[saxonhe] 01/01: Upstream release 9.5.1.1
Eugene Zhukov
eugene at moszumanska.debian.org
Wed Oct 19 19:25:51 UTC 2016
This is an automated email from the git hooks/post-receive script.
eugene pushed a commit to branch stretch
in repository saxonhe.
commit 54e48e025cf0261e29ee85a950e4965e9bb6d78c
Author: Eugene Zhukov <eugene at debian.org>
Date: Tue Oct 18 11:08:16 2016 +0000
Upstream release 9.5.1.1
---
sf/saxon/Configuration.java | 4387 +++++++++++++++++++
sf/saxon/Controller.java | 2659 ++++++++++++
sf/saxon/Filter.java | 458 ++
sf/saxon/IdentityTransformer.java | 67 +
sf/saxon/IdentityTransformerHandler.java | 123 +
sf/saxon/Platform.java | 195 +
sf/saxon/PreparedStylesheet.java | 746 ++++
sf/saxon/Query.java | 949 ++++
sf/saxon/TemplatesHandlerImpl.java | 163 +
sf/saxon/Transform.java | 1067 +++++
sf/saxon/TransformerFactoryImpl.java | 500 +++
sf/saxon/TransformerHandlerImpl.java | 167 +
sf/saxon/TypeCheckerEnvironment.java | 37 +
sf/saxon/Version.java | 140 +
sf/saxon/dom/AttrOverNodeInfo.java | 148 +
sf/saxon/dom/DOMAttributeMap.java | 230 +
sf/saxon/dom/DOMEnvelope.java | 227 +
sf/saxon/dom/DOMExceptionImpl.java | 64 +
sf/saxon/dom/DOMImplementationImpl.java | 101 +
sf/saxon/dom/DOMNodeList.java | 52 +
sf/saxon/dom/DOMNodeWrapper.java | 1247 ++++++
sf/saxon/dom/DOMObjectModel.java | 461 ++
sf/saxon/dom/DOMSender.java | 359 ++
sf/saxon/dom/DOMTransform.java | 68 +
sf/saxon/dom/DOMWriter.java | 317 ++
sf/saxon/dom/DocumentBuilderImpl.java | 303 ++
sf/saxon/dom/DocumentOverNodeInfo.java | 644 +++
sf/saxon/dom/DocumentWrapper.java | 316 ++
sf/saxon/dom/ElementOverNodeInfo.java | 346 ++
sf/saxon/dom/NodeOverNodeInfo.java | 732 ++++
sf/saxon/dom/PIOverNodeInfo.java | 49 +
sf/saxon/dom/TextOverAttrInfo.java | 86 +
sf/saxon/dom/TextOverNodeInfo.java | 233 +
sf/saxon/dom/TypeInfoImpl.java | 83 +
sf/saxon/dom/package.html | 61 +
sf/saxon/event/Builder.java | 258 ++
sf/saxon/event/BuilderMonitor.java | 39 +
sf/saxon/event/CommentStripper.java | 107 +
sf/saxon/event/ComplexContentOutputter.java | 599 +++
sf/saxon/event/ContentHandlerProxy.java | 647 +++
sf/saxon/event/ContentHandlerProxyLocator.java | 116 +
sf/saxon/event/CopyInformee.java | 29 +
.../event/CopyNamespaceSensitiveException.java | 24 +
sf/saxon/event/EventSource.java | 55 +
sf/saxon/event/FilterFactory.java | 22 +
sf/saxon/event/IDFilter.java | 186 +
sf/saxon/event/LocationCopier.java | 66 +
sf/saxon/event/LocationProvider.java | 53 +
sf/saxon/event/NamePoolConverter.java | 86 +
sf/saxon/event/NamespaceReducer.java | 281 ++
sf/saxon/event/NoOpenStartTagException.java | 89 +
sf/saxon/event/OnEmptyHandler.java | 122 +
sf/saxon/event/PIGrabber.java | 165 +
sf/saxon/event/PathMaintainer.java | 106 +
sf/saxon/event/PipelineConfiguration.java | 445 ++
sf/saxon/event/ProxyReceiver.java | 251 ++
sf/saxon/event/Receiver.java | 230 +
sf/saxon/event/ReceiverOptions.java | 133 +
sf/saxon/event/ReceivingContentHandler.java | 698 +++
sf/saxon/event/SaxonLocator.java | 20 +
sf/saxon/event/Sender.java | 570 +++
sf/saxon/event/SequenceCopier.java | 36 +
sf/saxon/event/SequenceOutputter.java | 171 +
sf/saxon/event/SequenceReceiver.java | 124 +
sf/saxon/event/SequenceWriter.java | 367 ++
sf/saxon/event/Sink.java | 175 +
sf/saxon/event/SourceLocationProvider.java | 20 +
sf/saxon/event/StartTagBuffer.java | 434 ++
sf/saxon/event/StreamWriterToReceiver.java | 583 +++
sf/saxon/event/Stripper.java | 203 +
sf/saxon/event/TeeOutputter.java | 264 ++
sf/saxon/event/TracingFilter.java | 230 +
sf/saxon/event/TransformerReceiver.java | 121 +
sf/saxon/event/Transmitter.java | 68 +
sf/saxon/event/TreeReceiver.java | 333 ++
sf/saxon/event/TypeCheckingFilter.java | 314 ++
sf/saxon/event/package.html | 67 +
sf/saxon/evpull/BlockEventIterator.java | 72 +
sf/saxon/evpull/BracketedDocumentIterator.java | 82 +
sf/saxon/evpull/BracketedElementIterator.java | 130 +
sf/saxon/evpull/ComplexContentProcessor.java | 275 ++
sf/saxon/evpull/Decomposer.java | 126 +
sf/saxon/evpull/EmptyEventIterator.java | 47 +
sf/saxon/evpull/EndDocumentEvent.java | 25 +
sf/saxon/evpull/EndElementEvent.java | 25 +
sf/saxon/evpull/EventAnnotationStripper.java | 74 +
sf/saxon/evpull/EventIterator.java | 34 +
sf/saxon/evpull/EventIteratorOverSequence.java | 52 +
sf/saxon/evpull/EventIteratorToReceiver.java | 106 +
sf/saxon/evpull/EventMappingFunction.java | 31 +
sf/saxon/evpull/EventMappingIterator.java | 57 +
sf/saxon/evpull/EventStackIterator.java | 86 +
sf/saxon/evpull/EventToStaxBridge.java | 619 +++
sf/saxon/evpull/NamespaceMaintainer.java | 200 +
sf/saxon/evpull/PullEvent.java | 22 +
sf/saxon/evpull/PullEventSource.java | 68 +
sf/saxon/evpull/PullEventTracer.java | 133 +
sf/saxon/evpull/SequenceComposer.java | 242 ++
sf/saxon/evpull/SingletonEventIterator.java | 56 +
sf/saxon/evpull/StartDocumentEvent.java | 24 +
sf/saxon/evpull/StartElementEvent.java | 339 ++
sf/saxon/evpull/StaxToEventBridge.java | 540 +++
sf/saxon/evpull/TracingEventIterator.java | 61 +
sf/saxon/evpull/package.html | 68 +
sf/saxon/expr/AdjacentTextNodeMerger.java | 272 ++
sf/saxon/expr/AnalyzeMappingFunction.java | 60 +
sf/saxon/expr/AndExpression.java | 144 +
sf/saxon/expr/ArithmeticExpression.java | 398 ++
sf/saxon/expr/ArithmeticExpression10.java | 349 ++
sf/saxon/expr/Assignation.java | 492 +++
sf/saxon/expr/AtomicSequenceConverter.java | 381 ++
sf/saxon/expr/Atomizer.java | 602 +++
sf/saxon/expr/AxisAtomizingIterator.java | 113 +
sf/saxon/expr/AxisExpression.java | 1073 +++++
sf/saxon/expr/BigRangeIterator.java | 109 +
sf/saxon/expr/BinaryExpression.java | 449 ++
sf/saxon/expr/Binding.java | 92 +
sf/saxon/expr/BindingReference.java | 42 +
sf/saxon/expr/BooleanExpression.java | 194 +
sf/saxon/expr/Calculator.java | 1116 +++++
sf/saxon/expr/Callable.java | 42 +
sf/saxon/expr/CardinalityChecker.java | 474 ++
sf/saxon/expr/CardinalityCheckingIterator.java | 138 +
sf/saxon/expr/CastExpression.java | 444 ++
sf/saxon/expr/CastToList.java | 298 ++
sf/saxon/expr/CastToUnion.java | 353 ++
sf/saxon/expr/CastableExpression.java | 269 ++
sf/saxon/expr/CastableToList.java | 262 ++
sf/saxon/expr/CastableToUnion.java | 273 ++
sf/saxon/expr/CastingExpression.java | 203 +
sf/saxon/expr/CollationMap.java | 120 +
sf/saxon/expr/CompareToIntegerConstant.java | 426 ++
sf/saxon/expr/ComparisonExpression.java | 46 +
sf/saxon/expr/Container.java | 58 +
sf/saxon/expr/ContextItemExpression.java | 338 ++
sf/saxon/expr/ContextMappingFunction.java | 37 +
sf/saxon/expr/ContextMappingIterator.java | 117 +
sf/saxon/expr/ContextSwitchingExpression.java | 30 +
sf/saxon/expr/CurrentItemExpression.java | 41 +
sf/saxon/expr/DifferenceEnumeration.java | 148 +
sf/saxon/expr/EagerLetExpression.java | 56 +
sf/saxon/expr/EarlyEvaluationContext.java | 413 ++
sf/saxon/expr/EmptyTextNodeRemover.java | 158 +
sf/saxon/expr/ErrorExpression.java | 154 +
sf/saxon/expr/ErrorIterator.java | 125 +
sf/saxon/expr/EveryItemMappingIterator.java | 84 +
sf/saxon/expr/Expression.java | 1409 ++++++
sf/saxon/expr/FilterExpression.java | 1366 ++++++
sf/saxon/expr/FilterIterator.java | 219 +
sf/saxon/expr/FirstItemExpression.java | 165 +
sf/saxon/expr/ForExpression.java | 841 ++++
sf/saxon/expr/FunctionCall.java | 459 ++
sf/saxon/expr/GeneralComparison.java | 878 ++++
sf/saxon/expr/GeneralComparison10.java | 442 ++
sf/saxon/expr/GeneralComparison20.java | 65 +
sf/saxon/expr/GroupVariableReference.java | 129 +
sf/saxon/expr/HomogeneityChecker.java | 129 +
sf/saxon/expr/IdentityComparison.java | 254 ++
sf/saxon/expr/InstanceOfExpression.java | 273 ++
sf/saxon/expr/IntegerRangeTest.java | 261 ++
sf/saxon/expr/IntersectionEnumeration.java | 123 +
sf/saxon/expr/IsLastExpression.java | 150 +
sf/saxon/expr/ItemChecker.java | 387 ++
sf/saxon/expr/ItemMappingFunction.java | 36 +
sf/saxon/expr/ItemMappingIterator.java | 152 +
sf/saxon/expr/ItemTypeCheckingFunction.java | 80 +
sf/saxon/expr/JPConverter.java | 723 ++++
sf/saxon/expr/LastItemExpression.java | 84 +
sf/saxon/expr/LastPositionFinder.java | 35 +
sf/saxon/expr/LetExpression.java | 735 ++++
sf/saxon/expr/Literal.java | 621 +++
sf/saxon/expr/LocalVariableReference.java | 135 +
sf/saxon/expr/MappingFunction.java | 36 +
sf/saxon/expr/MappingIterator.java | 130 +
sf/saxon/expr/MonoIterator.java | 76 +
sf/saxon/expr/MultiIterator.java | 80 +
sf/saxon/expr/Negatable.java | 32 +
sf/saxon/expr/NegateExpression.java | 143 +
sf/saxon/expr/OrExpression.java | 125 +
sf/saxon/expr/PJConverter.java | 823 ++++
sf/saxon/expr/PairIterator.java | 79 +
sf/saxon/expr/ParentNodeExpression.java | 167 +
sf/saxon/expr/PendingUpdateList.java | 48 +
sf/saxon/expr/PositionVariable.java | 125 +
sf/saxon/expr/QuantifiedExpression.java | 366 ++
sf/saxon/expr/RangeExpression.java | 204 +
sf/saxon/expr/RangeIterator.java | 141 +
sf/saxon/expr/ReverseRangeIterator.java | 109 +
sf/saxon/expr/RootExpression.java | 251 ++
sf/saxon/expr/SimpleExpression.java | 307 ++
sf/saxon/expr/SimpleStepExpression.java | 106 +
sf/saxon/expr/SingleItemFilter.java | 73 +
sf/saxon/expr/SingleNodeExpression.java | 152 +
sf/saxon/expr/SingletonAtomizer.java | 333 ++
sf/saxon/expr/SingletonIntersectExpression.java | 108 +
sf/saxon/expr/SlashExpression.java | 1136 +++++
sf/saxon/expr/StackFrame.java | 68 +
sf/saxon/expr/StatefulMappingFunction.java | 30 +
sf/saxon/expr/StaticContext.java | 241 ++
sf/saxon/expr/StaticProperty.java | 335 ++
sf/saxon/expr/StringLiteral.java | 53 +
sf/saxon/expr/StringTokenIterator.java | 101 +
sf/saxon/expr/SubExpressionInfo.java | 30 +
sf/saxon/expr/SubscriptExpression.java | 260 ++
sf/saxon/expr/SubsequenceIterator.java | 165 +
sf/saxon/expr/SuppliedParameterReference.java | 213 +
sf/saxon/expr/TailCallLoop.java | 199 +
sf/saxon/expr/TailExpression.java | 244 ++
sf/saxon/expr/TailIterator.java | 131 +
sf/saxon/expr/TreatExpression.java | 51 +
sf/saxon/expr/UnaryExpression.java | 242 ++
sf/saxon/expr/UnionEnumeration.java | 141 +
sf/saxon/expr/UntypedSequenceConverter.java | 236 +
sf/saxon/expr/UserFunctionCall.java | 648 +++
sf/saxon/expr/UserFunctionReference.java | 20 +
sf/saxon/expr/ValueComparison.java | 831 ++++
sf/saxon/expr/ValueTailIterator.java | 95 +
sf/saxon/expr/VariableReference.java | 600 +++
sf/saxon/expr/VennExpression.java | 683 +++
sf/saxon/expr/XPathContext.java | 307 ++
sf/saxon/expr/XPathContextMajor.java | 662 +++
sf/saxon/expr/XPathContextMinor.java | 501 +++
sf/saxon/expr/flwor/Clause.java | 166 +
sf/saxon/expr/flwor/ClauseInfo.java | 212 +
sf/saxon/expr/flwor/ExpressionProcessor.java | 21 +
sf/saxon/expr/flwor/FLWORExpression.java | 962 +++++
sf/saxon/expr/flwor/ForClause.java | 435 ++
sf/saxon/expr/flwor/ForClauseOuterPull.java | 84 +
sf/saxon/expr/flwor/ForClauseOuterPush.java | 69 +
sf/saxon/expr/flwor/ForClausePull.java | 75 +
sf/saxon/expr/flwor/ForClausePush.java | 59 +
sf/saxon/expr/flwor/LetClause.java | 196 +
sf/saxon/expr/flwor/LetClausePull.java | 57 +
sf/saxon/expr/flwor/LetClausePush.java | 47 +
sf/saxon/expr/flwor/LocalVariableBinding.java | 180 +
sf/saxon/expr/flwor/OrderByClause.java | 184 +
sf/saxon/expr/flwor/OrderByClausePull.java | 130 +
sf/saxon/expr/flwor/OrderByClausePush.java | 116 +
sf/saxon/expr/flwor/ReturnClauseIterator.java | 114 +
sf/saxon/expr/flwor/ReturnClausePush.java | 48 +
sf/saxon/expr/flwor/SingularityPull.java | 39 +
sf/saxon/expr/flwor/TraceClause.java | 117 +
sf/saxon/expr/flwor/TraceClausePull.java | 68 +
sf/saxon/expr/flwor/TraceClausePush.java | 59 +
sf/saxon/expr/flwor/Tuple.java | 26 +
sf/saxon/expr/flwor/TupleExpression.java | 177 +
sf/saxon/expr/flwor/TuplePull.java | 39 +
sf/saxon/expr/flwor/TuplePush.java | 38 +
sf/saxon/expr/flwor/WhereClause.java | 137 +
sf/saxon/expr/flwor/WhereClausePull.java | 57 +
sf/saxon/expr/flwor/WhereClausePush.java | 47 +
sf/saxon/expr/flwor/package.html | 37 +
sf/saxon/expr/instruct/AnalyzeString.java | 597 +++
sf/saxon/expr/instruct/ApplyImports.java | 356 ++
sf/saxon/expr/instruct/ApplyTemplates.java | 622 +++
sf/saxon/expr/instruct/AttributeCreator.java | 260 ++
sf/saxon/expr/instruct/AttributeSet.java | 155 +
sf/saxon/expr/instruct/Bindery.java | 320 ++
sf/saxon/expr/instruct/Block.java | 754 ++++
sf/saxon/expr/instruct/BlockIterator.java | 127 +
sf/saxon/expr/instruct/CallTemplate.java | 552 +++
sf/saxon/expr/instruct/Choose.java | 1092 +++++
sf/saxon/expr/instruct/Comment.java | 178 +
sf/saxon/expr/instruct/ComputedAttribute.java | 666 +++
sf/saxon/expr/instruct/ComputedElement.java | 558 +++
sf/saxon/expr/instruct/Copy.java | 647 +++
sf/saxon/expr/instruct/CopyOf.java | 844 ++++
sf/saxon/expr/instruct/Debugger.java | 24 +
sf/saxon/expr/instruct/Doctype.java | 369 ++
sf/saxon/expr/instruct/DocumentInstr.java | 417 ++
sf/saxon/expr/instruct/DummyNamespaceResolver.java | 67 +
sf/saxon/expr/instruct/ElementCreator.java | 569 +++
sf/saxon/expr/instruct/Executable.java | 807 ++++
sf/saxon/expr/instruct/FixedAttribute.java | 273 ++
sf/saxon/expr/instruct/FixedElement.java | 567 +++
sf/saxon/expr/instruct/ForEach.java | 565 +++
sf/saxon/expr/instruct/ForEachGroup.java | 1001 +++++
sf/saxon/expr/instruct/GeneralVariable.java | 563 +++
sf/saxon/expr/instruct/GlobalParam.java | 82 +
sf/saxon/expr/instruct/GlobalParameterSet.java | 150 +
sf/saxon/expr/instruct/GlobalVariable.java | 682 +++
sf/saxon/expr/instruct/ITemplateCall.java | 33 +
sf/saxon/expr/instruct/Instruction.java | 443 ++
sf/saxon/expr/instruct/InstructionDetails.java | 184 +
sf/saxon/expr/instruct/LocalParam.java | 230 +
sf/saxon/expr/instruct/LocalParamBlock.java | 227 +
sf/saxon/expr/instruct/LocalParamSetter.java | 321 ++
sf/saxon/expr/instruct/LocationMap.java | 98 +
sf/saxon/expr/instruct/Message.java | 386 ++
sf/saxon/expr/instruct/NamespaceConstructor.java | 259 ++
sf/saxon/expr/instruct/NextMatch.java | 219 +
sf/saxon/expr/instruct/NumberInstruction.java | 714 ++++
sf/saxon/expr/instruct/ParameterSet.java | 153 +
sf/saxon/expr/instruct/ParentNodeConstructor.java | 389 ++
sf/saxon/expr/instruct/Procedure.java | 148 +
.../instruct/ProcessRegexMatchInstruction.java | 93 +
sf/saxon/expr/instruct/ProcessingInstruction.java | 321 ++
sf/saxon/expr/instruct/ResultDocument.java | 1029 +++++
sf/saxon/expr/instruct/SavedNamespaceContext.java | 117 +
sf/saxon/expr/instruct/SimpleNodeConstructor.java | 315 ++
sf/saxon/expr/instruct/SlotManager.java | 99 +
sf/saxon/expr/instruct/TailCall.java | 32 +
sf/saxon/expr/instruct/TailCallReturner.java | 35 +
sf/saxon/expr/instruct/Template.java | 316 ++
sf/saxon/expr/instruct/TerminationException.java | 27 +
sf/saxon/expr/instruct/TraceExpression.java | 509 +++
sf/saxon/expr/instruct/UseAttributeSets.java | 250 ++
sf/saxon/expr/instruct/UserFunction.java | 481 +++
sf/saxon/expr/instruct/UserFunctionParameter.java | 179 +
sf/saxon/expr/instruct/ValidatingInstruction.java | 23 +
sf/saxon/expr/instruct/ValueOf.java | 327 ++
sf/saxon/expr/instruct/WithParam.java | 241 ++
sf/saxon/expr/instruct/package.html | 46 +
sf/saxon/expr/number/AbstractNumberer.java | 723 ++++
sf/saxon/expr/number/Alphanumeric.java | 227 +
sf/saxon/expr/number/IrregularGroupFormatter.java | 73 +
sf/saxon/expr/number/NamedTimeZone.java | 1011 +++++
sf/saxon/expr/number/NumberFormatter.java | 172 +
sf/saxon/expr/number/Numberer_en.java | 246 ++
sf/saxon/expr/number/NumericGroupFormatter.java | 49 +
sf/saxon/expr/number/RegularGroupFormatter.java | 67 +
sf/saxon/expr/number/package.html | 51 +
sf/saxon/expr/package.html | 67 +
sf/saxon/expr/parser/CodeInjector.java | 46 +
sf/saxon/expr/parser/ExpressionLocation.java | 193 +
sf/saxon/expr/parser/ExpressionParser.java | 3176 ++++++++++++++
sf/saxon/expr/parser/ExpressionTool.java | 1420 ++++++
sf/saxon/expr/parser/ExpressionVisitor.java | 403 ++
sf/saxon/expr/parser/Optimizer.java | 443 ++
sf/saxon/expr/parser/PathMap.java | 863 ++++
sf/saxon/expr/parser/PromotionOffer.java | 318 ++
sf/saxon/expr/parser/RoleLocator.java | 211 +
sf/saxon/expr/parser/Token.java | 803 ++++
sf/saxon/expr/parser/Tokenizer.java | 1116 +++++
sf/saxon/expr/parser/TypeChecker.java | 743 ++++
sf/saxon/expr/parser/package.html | 26 +
sf/saxon/expr/sort/AlphanumericCollator.java | 144 +
sf/saxon/expr/sort/AtomicComparer.java | 79 +
sf/saxon/expr/sort/AtomicSortComparer.java | 248 ++
sf/saxon/expr/sort/CalendarValueComparer.java | 96 +
sf/saxon/expr/sort/CaseFirstCollator.java | 113 +
sf/saxon/expr/sort/CodepointCollatingComparer.java | 124 +
sf/saxon/expr/sort/CodepointCollator.java | 179 +
sf/saxon/expr/sort/CollatingAtomicComparer.java | 126 +
.../expr/sort/ComparableAtomicValueComparer.java | 97 +
sf/saxon/expr/sort/ComparisonKey.java | 55 +
sf/saxon/expr/sort/ConditionalSorter.java | 305 ++
sf/saxon/expr/sort/DecimalSortComparer.java | 43 +
sf/saxon/expr/sort/DescendingComparer.java | 97 +
sf/saxon/expr/sort/DocumentOrderIterator.java | 127 +
sf/saxon/expr/sort/DocumentSorter.java | 180 +
sf/saxon/expr/sort/DoubleSortComparer.java | 118 +
sf/saxon/expr/sort/EmptyGreatestComparer.java | 126 +
sf/saxon/expr/sort/EmptyIntIterator.java | 52 +
sf/saxon/expr/sort/EqualityComparer.java | 93 +
sf/saxon/expr/sort/GenericAtomicComparer.java | 232 +
sf/saxon/expr/sort/GenericSorter.java | 500 +++
sf/saxon/expr/sort/GlobalOrderComparer.java | 49 +
sf/saxon/expr/sort/GroupAdjacentIterator.java | 252 ++
sf/saxon/expr/sort/GroupByIterator.java | 357 ++
sf/saxon/expr/sort/GroupEndingIterator.java | 66 +
sf/saxon/expr/sort/GroupIterator.java | 85 +
sf/saxon/expr/sort/GroupMatchingIterator.java | 140 +
sf/saxon/expr/sort/GroupStartingIterator.java | 62 +
sf/saxon/expr/sort/GroupToBeSorted.java | 30 +
sf/saxon/expr/sort/ItemOrderComparer.java | 31 +
sf/saxon/expr/sort/ItemToBeSorted.java | 23 +
sf/saxon/expr/sort/LRUCache.java | 89 +
sf/saxon/expr/sort/LocalOrderComparer.java | 42 +
sf/saxon/expr/sort/NumericComparer.java | 155 +
sf/saxon/expr/sort/NumericComparer11.java | 38 +
sf/saxon/expr/sort/ObjectToBeSorted.java | 29 +
sf/saxon/expr/sort/RuleBasedSubstringMatcher.java | 288 ++
sf/saxon/expr/sort/SimpleCollation.java | 93 +
sf/saxon/expr/sort/SortExpression.java | 609 +++
sf/saxon/expr/sort/SortKeyDefinition.java | 621 +++
sf/saxon/expr/sort/SortKeyEvaluator.java | 27 +
sf/saxon/expr/sort/Sortable.java | 35 +
sf/saxon/expr/sort/SortedGroupIterator.java | 166 +
sf/saxon/expr/sort/SortedIterator.java | 312 ++
sf/saxon/expr/sort/TextComparer.java | 110 +
sf/saxon/expr/sort/package.html | 36 +
sf/saxon/functions/Abs.java | 60 +
sf/saxon/functions/Adjust.java | 94 +
sf/saxon/functions/Aggregate.java | 60 +
sf/saxon/functions/Available.java | 43 +
sf/saxon/functions/Average.java | 167 +
sf/saxon/functions/BaseURI.java | 87 +
sf/saxon/functions/BooleanFn.java | 193 +
sf/saxon/functions/Ceiling.java | 75 +
sf/saxon/functions/CodepointEqual.java | 52 +
sf/saxon/functions/CodepointsToString.java | 99 +
sf/saxon/functions/CollatingFunction.java | 331 ++
sf/saxon/functions/Collection.java | 190 +
sf/saxon/functions/Compare.java | 108 +
sf/saxon/functions/CompileTimeFunction.java | 57 +
sf/saxon/functions/Component.java | 160 +
sf/saxon/functions/Concat.java | 160 +
sf/saxon/functions/ConstructorFunctionLibrary.java | 243 ++
sf/saxon/functions/Contains.java | 125 +
sf/saxon/functions/Count.java | 114 +
sf/saxon/functions/Current.java | 65 +
sf/saxon/functions/CurrentDateTime.java | 82 +
sf/saxon/functions/CurrentGroup.java | 76 +
sf/saxon/functions/CurrentGroupingKey.java | 82 +
sf/saxon/functions/Data.java | 78 +
sf/saxon/functions/DateTimeConstructor.java | 75 +
sf/saxon/functions/DeepEqual.java | 627 +++
sf/saxon/functions/DefaultCollation.java | 62 +
sf/saxon/functions/DistinctValues.java | 212 +
sf/saxon/functions/Doc.java | 256 ++
sf/saxon/functions/DocAvailable.java | 165 +
sf/saxon/functions/DocumentFn.java | 794 ++++
sf/saxon/functions/DocumentUriFn.java | 95 +
sf/saxon/functions/ElementAvailable.java | 115 +
sf/saxon/functions/Empty.java | 158 +
sf/saxon/functions/EndsWith.java | 135 +
sf/saxon/functions/Error.java | 151 +
sf/saxon/functions/EscapeURI.java | 260 ++
sf/saxon/functions/ExecutableFunctionLibrary.java | 186 +
sf/saxon/functions/Exists.java | 177 +
sf/saxon/functions/ExtensionFunctionFactory.java | 22 +
sf/saxon/functions/False.java | 38 +
sf/saxon/functions/Floor.java | 76 +
sf/saxon/functions/FormatDate.java | 791 ++++
sf/saxon/functions/FormatNumber.java | 895 ++++
sf/saxon/functions/FunctionAvailable.java | 165 +
sf/saxon/functions/FunctionLibrary.java | 109 +
sf/saxon/functions/FunctionLibraryList.java | 191 +
sf/saxon/functions/GenerateId.java | 98 +
sf/saxon/functions/Id.java | 283 ++
sf/saxon/functions/Idref.java | 228 +
sf/saxon/functions/InScopePrefixes.java | 96 +
sf/saxon/functions/IndexOf.java | 193 +
sf/saxon/functions/Insert.java | 153 +
sf/saxon/functions/IntegratedFunctionCall.java | 336 ++
sf/saxon/functions/IntegratedFunctionLibrary.java | 159 +
sf/saxon/functions/IsWholeNumber.java | 143 +
sf/saxon/functions/KeyFn.java | 385 ++
sf/saxon/functions/Lang.java | 160 +
sf/saxon/functions/Last.java | 132 +
sf/saxon/functions/LocalNameFn.java | 91 +
sf/saxon/functions/LowerCase.java | 57 +
sf/saxon/functions/Matches.java | 310 ++
sf/saxon/functions/Max.java | 40 +
sf/saxon/functions/Min.java | 40 +
sf/saxon/functions/Minimax.java | 464 ++
sf/saxon/functions/NameFn.java | 91 +
sf/saxon/functions/NamespaceForPrefix.java | 78 +
sf/saxon/functions/NamespaceUriFn.java | 91 +
sf/saxon/functions/Nilled.java | 107 +
sf/saxon/functions/NodeNameFn.java | 97 +
sf/saxon/functions/NormalizeSpace_0.java | 130 +
sf/saxon/functions/NormalizeSpace_1.java | 139 +
sf/saxon/functions/NormalizeUnicode.java | 104 +
sf/saxon/functions/NotFn.java | 144 +
sf/saxon/functions/NumberFn.java | 196 +
sf/saxon/functions/Position.java | 128 +
sf/saxon/functions/Put.java | 128 +
sf/saxon/functions/QNameFn.java | 143 +
sf/saxon/functions/RegexGroup.java | 83 +
sf/saxon/functions/Remove.java | 244 ++
sf/saxon/functions/Replace.java | 246 ++
sf/saxon/functions/ResolveQName.java | 80 +
sf/saxon/functions/ResolveURI.java | 322 ++
sf/saxon/functions/Reverse.java | 87 +
sf/saxon/functions/Root.java | 138 +
sf/saxon/functions/Round.java | 90 +
sf/saxon/functions/RoundHalfToEven.java | 100 +
sf/saxon/functions/StandardFunction.java | 895 ++++
sf/saxon/functions/StartsWith.java | 128 +
sf/saxon/functions/StaticBaseURI.java | 72 +
.../functions/StaticContextForSystemFunctions.java | 281 ++
sf/saxon/functions/StringFn.java | 200 +
sf/saxon/functions/StringJoin.java | 310 ++
sf/saxon/functions/StringLength.java | 192 +
sf/saxon/functions/StringToCodepoints.java | 64 +
sf/saxon/functions/Subsequence.java | 282 ++
sf/saxon/functions/Substring.java | 266 ++
sf/saxon/functions/SubstringAfter.java | 125 +
sf/saxon/functions/SubstringBefore.java | 130 +
sf/saxon/functions/Sum.java | 208 +
sf/saxon/functions/SystemFunctionCall.java | 472 ++
sf/saxon/functions/SystemFunctionLibrary.java | 239 ++
sf/saxon/functions/SystemProperty.java | 189 +
sf/saxon/functions/Tokenize.java | 208 +
sf/saxon/functions/Trace.java | 223 +
sf/saxon/functions/Translate.java | 192 +
sf/saxon/functions/TreatFn.java | 65 +
sf/saxon/functions/True.java | 38 +
sf/saxon/functions/TypeAvailable.java | 125 +
sf/saxon/functions/URIQueryParameters.java | 239 ++
sf/saxon/functions/Unordered.java | 66 +
sf/saxon/functions/UnparsedEntity.java | 156 +
sf/saxon/functions/UnparsedText.java | 306 ++
sf/saxon/functions/UnparsedTextAvailable.java | 70 +
sf/saxon/functions/UpperCase.java | 57 +
sf/saxon/functions/UriCollection.java | 150 +
sf/saxon/functions/VendorFunctionLibrary.java | 58 +
sf/saxon/functions/package.html | 50 +
sf/saxon/java/JavaCollationFactory.java | 216 +
sf/saxon/java/JavaPlatform.java | 265 ++
sf/saxon/java/package.html | 26 +
sf/saxon/lib/AugmentedSource.java | 523 +++
sf/saxon/lib/CollationURIResolver.java | 48 +
sf/saxon/lib/CollectionURIResolver.java | 61 +
sf/saxon/lib/ConversionRules.java | 642 +++
sf/saxon/lib/EnvironmentVariableResolver.java | 39 +
sf/saxon/lib/ExtensionFunctionCall.java | 225 +
sf/saxon/lib/ExtensionFunctionDefinition.java | 138 +
sf/saxon/lib/ExternalObjectModel.java | 117 +
sf/saxon/lib/FeatureKeys.java | 1570 +++++++
sf/saxon/lib/Initializer.java | 35 +
sf/saxon/lib/LocalizerFactory.java | 56 +
sf/saxon/lib/ModuleURIResolver.java | 75 +
sf/saxon/lib/NamespaceConstant.java | 401 ++
sf/saxon/lib/Numberer.java | 146 +
sf/saxon/lib/OutputURIResolver.java | 73 +
sf/saxon/lib/ParseOptions.java | 800 ++++
sf/saxon/lib/RelativeURIResolver.java | 98 +
sf/saxon/lib/SaxonOutputKeys.java | 543 +++
sf/saxon/lib/SchemaURIResolver.java | 59 +
sf/saxon/lib/SerializerFactory.java | 753 ++++
sf/saxon/lib/SourceResolver.java | 49 +
sf/saxon/lib/StAXResultHandler.java | 22 +
sf/saxon/lib/StandardCollationURIResolver.java | 91 +
sf/saxon/lib/StandardCollectionURIResolver.java | 506 +++
sf/saxon/lib/StandardEntityResolver.java | 640 +++
.../lib/StandardEnvironmentVariableResolver.java | 44 +
sf/saxon/lib/StandardErrorHandler.java | 157 +
sf/saxon/lib/StandardErrorListener.java | 651 +++
sf/saxon/lib/StandardModuleURIResolver.java | 174 +
sf/saxon/lib/StandardOutputResolver.java | 166 +
sf/saxon/lib/StandardURIChecker.java | 94 +
sf/saxon/lib/StandardURIResolver.java | 278 ++
sf/saxon/lib/StandardUnparsedTextResolver.java | 278 ++
sf/saxon/lib/StringCollator.java | 51 +
sf/saxon/lib/SubstringMatcher.java | 70 +
sf/saxon/lib/TraceListener.java | 97 +
sf/saxon/lib/URIChecker.java | 32 +
sf/saxon/lib/UnparsedTextURIResolver.java | 45 +
sf/saxon/lib/Validation.java | 112 +
sf/saxon/lib/ValidationStatisticsRecipient.java | 29 +
sf/saxon/lib/package.html | 31 +
sf/saxon/om/AbsolutePath.java | 236 +
sf/saxon/om/AbstractItem.java | 91 +
sf/saxon/om/AllElementsSpaceStrippingRule.java | 38 +
sf/saxon/om/AtomicArray.java | 257 ++
sf/saxon/om/AtomicSequence.java | 58 +
sf/saxon/om/AttributeCollection.java | 201 +
sf/saxon/om/AxisInfo.java | 348 ++
sf/saxon/om/Chain.java | 395 ++
sf/saxon/om/CodedName.java | 173 +
sf/saxon/om/CopyOptions.java | 41 +
sf/saxon/om/DocumentInfo.java | 104 +
sf/saxon/om/DocumentPool.java | 172 +
sf/saxon/om/DocumentURI.java | 81 +
sf/saxon/om/EmptyAtomicSequence.java | 109 +
sf/saxon/om/FingerprintedNode.java | 25 +
sf/saxon/om/FingerprintedQName.java | 135 +
sf/saxon/om/FunctionItem.java | 71 +
sf/saxon/om/GroundedValue.java | 89 +
sf/saxon/om/IdentityComparable.java | 18 +
sf/saxon/om/InscopeNamespaceResolver.java | 119 +
sf/saxon/om/Item.java | 74 +
sf/saxon/om/LazySequence.java | 38 +
sf/saxon/om/MemoSequence.java | 262 ++
sf/saxon/om/MutableDocumentInfo.java | 22 +
sf/saxon/om/MutableNodeInfo.java | 244 ++
sf/saxon/om/Name10Checker.java | 92 +
sf/saxon/om/Name11Checker.java | 75 +
sf/saxon/om/NameChecker.java | 258 ++
sf/saxon/om/NameOfNode.java | 176 +
sf/saxon/om/NamePool.java | 893 ++++
sf/saxon/om/NamespaceBinding.java | 127 +
sf/saxon/om/NamespaceException.java | 31 +
sf/saxon/om/NamespaceResolver.java | 42 +
sf/saxon/om/NoElementsSpaceStrippingRule.java | 38 +
sf/saxon/om/NoNamespaceName.java | 175 +
sf/saxon/om/NodeInfo.java | 595 +++
sf/saxon/om/NodeName.java | 114 +
sf/saxon/om/NotationSet.java | 23 +
sf/saxon/om/QNameException.java | 27 +
.../om/SelectedElementsSpaceStrippingRule.java | 230 +
sf/saxon/om/Sequence.java | 47 +
sf/saxon/om/SequenceIterator.java | 166 +
sf/saxon/om/SequenceTool.java | 313 ++
sf/saxon/om/SingletonSet.java | 55 +
sf/saxon/om/SpaceStrippingRule.java | 35 +
sf/saxon/om/StandardNames.java | 852 ++++
sf/saxon/om/StructuredQName.java | 414 ++
sf/saxon/om/StylesheetSpaceStrippingRule.java | 71 +
sf/saxon/om/TreeModel.java | 186 +
sf/saxon/om/package.html | 50 +
sf/saxon/package.html | 52 +
sf/saxon/pattern/AncestorQualifiedPattern.java | 363 ++
sf/saxon/pattern/AnchorPattern.java | 85 +
sf/saxon/pattern/AnyChildNodeTest.java | 128 +
sf/saxon/pattern/AnyNodeTest.java | 150 +
sf/saxon/pattern/BooleanExpressionPattern.java | 171 +
sf/saxon/pattern/CombinedNodeTest.java | 391 ++
sf/saxon/pattern/ConditionalPattern.java | 328 ++
sf/saxon/pattern/ContentTypeTest.java | 267 ++
sf/saxon/pattern/DocumentNodeTest.java | 162 +
sf/saxon/pattern/EmptySequenceTest.java | 167 +
sf/saxon/pattern/ExceptPattern.java | 97 +
sf/saxon/pattern/GeneralNodePattern.java | 301 ++
sf/saxon/pattern/GeneralPositionalPattern.java | 339 ++
sf/saxon/pattern/IdrefTest.java | 90 +
sf/saxon/pattern/IntersectPattern.java | 96 +
sf/saxon/pattern/ItemTypePattern.java | 140 +
sf/saxon/pattern/LocalNameTest.java | 163 +
sf/saxon/pattern/NameTest.java | 323 ++
sf/saxon/pattern/NamespaceTest.java | 183 +
sf/saxon/pattern/NodeKindTest.java | 272 ++
sf/saxon/pattern/NodeSetPattern.java | 220 +
sf/saxon/pattern/NodeTest.java | 271 ++
sf/saxon/pattern/Pattern.java | 490 +++
sf/saxon/pattern/PatternFinder.java | 40 +
sf/saxon/pattern/PatternMaker.java | 71 +
sf/saxon/pattern/PatternParser.java | 34 +
sf/saxon/pattern/PatternParser20.java | 250 ++
sf/saxon/pattern/PatternSponsor.java | 421 ++
sf/saxon/pattern/PatternThatSetsCurrent.java | 226 +
sf/saxon/pattern/PatternWithPredicate.java | 199 +
sf/saxon/pattern/QNameTest.java | 29 +
sf/saxon/pattern/SchemaNodeTest.java | 15 +
sf/saxon/pattern/SimplePositionalPattern.java | 298 ++
sf/saxon/pattern/UnionPattern.java | 99 +
sf/saxon/pattern/UnionQNameTest.java | 40 +
sf/saxon/pattern/VennPattern.java | 318 ++
sf/saxon/pattern/package.html | 51 +
sf/saxon/pull/NamespaceContextImpl.java | 107 +
sf/saxon/pull/PullConsumer.java | 45 +
sf/saxon/pull/PullFilter.java | 273 ++
sf/saxon/pull/PullProvider.java | 350 ++
sf/saxon/pull/PullPushCopier.java | 57 +
sf/saxon/pull/PullPushTee.java | 194 +
sf/saxon/pull/PullSource.java | 71 +
sf/saxon/pull/StaxBridge.java | 1055 +++++
sf/saxon/pull/UnparsedEntity.java | 102 +
sf/saxon/pull/package.html | 52 +
sf/saxon/query/Annotation.java | 67 +
sf/saxon/query/Declaration.java | 18 +
sf/saxon/query/DynamicQueryContext.java | 475 ++
sf/saxon/query/ImportedFunctionLibrary.java | 181 +
sf/saxon/query/LanguageFeature.java | 142 +
sf/saxon/query/QueryLibrary.java | 33 +
sf/saxon/query/QueryModule.java | 1876 ++++++++
sf/saxon/query/QueryParser.java | 4522 ++++++++++++++++++++
sf/saxon/query/QueryReader.java | 307 ++
sf/saxon/query/QueryResult.java | 272 ++
sf/saxon/query/SequenceWrapper.java | 292 ++
sf/saxon/query/StaticQueryContext.java | 1171 +++++
sf/saxon/query/UnboundFunctionLibrary.java | 210 +
sf/saxon/query/UndeclaredVariable.java | 38 +
sf/saxon/query/UpdateAgent.java | 33 +
sf/saxon/query/XQueryExpression.java | 979 +++++
sf/saxon/query/XQueryFunction.java | 696 +++
sf/saxon/query/XQueryFunctionBinder.java | 32 +
sf/saxon/query/XQueryFunctionLibrary.java | 375 ++
sf/saxon/query/package.html | 130 +
sf/saxon/regex/ARegexIterator.java | 349 ++
sf/saxon/regex/ARegularExpression.java | 129 +
sf/saxon/regex/ATokenIterator.java | 94 +
sf/saxon/regex/BMPString.java | 64 +
sf/saxon/regex/CaseVariants.java | 1879 ++++++++
sf/saxon/regex/Categories.java | 3435 +++++++++++++++
sf/saxon/regex/GeneralUnicodeString.java | 85 +
sf/saxon/regex/JRegexIterator.java | 343 ++
sf/saxon/regex/JTokenIterator.java | 92 +
sf/saxon/regex/JavaRegularExpression.java | 181 +
sf/saxon/regex/Operation.java | 741 ++++
sf/saxon/regex/RECompiler.java | 1513 +++++++
sf/saxon/regex/REFlags.java | 142 +
sf/saxon/regex/REMatcher.java | 837 ++++
sf/saxon/regex/REProgram.java | 276 ++
sf/saxon/regex/RESyntaxException.java | 58 +
sf/saxon/regex/RegexIterator.java | 83 +
sf/saxon/regex/RegularExpression.java | 70 +
sf/saxon/regex/UnicodeBlocks.java | 115 +
sf/saxon/regex/UnicodeString.java | 123 +
sf/saxon/regex/package.html | 35 +
sf/saxon/s9api/Axis.java | 49 +
sf/saxon/s9api/BuildingContentHandler.java | 33 +
sf/saxon/s9api/BuildingStreamWriter.java | 75 +
sf/saxon/s9api/BuildingStreamWriterImpl.java | 43 +
sf/saxon/s9api/ConstructedItemType.java | 101 +
sf/saxon/s9api/DOMDestination.java | 60 +
sf/saxon/s9api/Destination.java | 74 +
sf/saxon/s9api/DocumentBuilder.java | 524 +++
sf/saxon/s9api/ExtensionFunction.java | 55 +
sf/saxon/s9api/ItemType.java | 509 +++
sf/saxon/s9api/ItemTypeFactory.java | 524 +++
sf/saxon/s9api/MessageListener.java | 35 +
sf/saxon/s9api/MessageListenerProxy.java | 118 +
sf/saxon/s9api/OccurrenceIndicator.java | 91 +
sf/saxon/s9api/Processor.java | 556 +++
sf/saxon/s9api/QName.java | 471 ++
sf/saxon/s9api/SAXDestination.java | 66 +
sf/saxon/s9api/SaxonApiException.java | 72 +
sf/saxon/s9api/SaxonApiUncheckedException.java | 36 +
sf/saxon/s9api/SchemaManager.java | 170 +
sf/saxon/s9api/SchemaValidator.java | 361 ++
sf/saxon/s9api/SequenceType.java | 81 +
sf/saxon/s9api/Serializer.java | 620 +++
sf/saxon/s9api/TeeDestination.java | 65 +
sf/saxon/s9api/ValidationMode.java | 64 +
sf/saxon/s9api/WhitespaceStrippingPolicy.java | 77 +
sf/saxon/s9api/XPathCompiler.java | 647 +++
sf/saxon/s9api/XPathExecutable.java | 182 +
sf/saxon/s9api/XPathSelector.java | 247 ++
sf/saxon/s9api/XQueryCompiler.java | 585 +++
sf/saxon/s9api/XQueryEvaluator.java | 575 +++
sf/saxon/s9api/XQueryExecutable.java | 95 +
sf/saxon/s9api/XdmAtomicValue.java | 296 ++
sf/saxon/s9api/XdmDestination.java | 255 ++
sf/saxon/s9api/XdmEmptySequence.java | 52 +
sf/saxon/s9api/XdmExternalObject.java | 82 +
sf/saxon/s9api/XdmFunctionItem.java | 84 +
sf/saxon/s9api/XdmItem.java | 109 +
sf/saxon/s9api/XdmNode.java | 409 ++
sf/saxon/s9api/XdmNodeKind.java | 32 +
sf/saxon/s9api/XdmSequenceIterator.java | 113 +
sf/saxon/s9api/XdmValue.java | 215 +
sf/saxon/s9api/XsltCompiler.java | 350 ++
sf/saxon/s9api/XsltExecutable.java | 158 +
sf/saxon/s9api/XsltTransformer.java | 554 +++
sf/saxon/s9api/package.html | 60 +
sf/saxon/serialize/AttributeSorter.java | 157 +
sf/saxon/serialize/BinaryTextDecoder.java | 88 +
sf/saxon/serialize/CDATAFilter.java | 267 ++
sf/saxon/serialize/CharacterMap.java | 159 +
sf/saxon/serialize/CharacterMapExpander.java | 97 +
sf/saxon/serialize/CharacterMapIndex.java | 85 +
.../serialize/CharacterReferenceGenerator.java | 27 +
sf/saxon/serialize/Emitter.java | 378 ++
sf/saxon/serialize/HTML40Emitter.java | 93 +
sf/saxon/serialize/HTML50Emitter.java | 119 +
sf/saxon/serialize/HTMLEmitter.java | 424 ++
sf/saxon/serialize/HTMLIndenter.java | 305 ++
sf/saxon/serialize/HTMLTagHashSet.java | 69 +
sf/saxon/serialize/HTMLURIEscaper.java | 203 +
.../serialize/HexCharacterReferenceGenerator.java | 29 +
sf/saxon/serialize/ImplicitResultChecker.java | 107 +
sf/saxon/serialize/MessageEmitter.java | 40 +
sf/saxon/serialize/MessageWarner.java | 58 +
sf/saxon/serialize/MetaTagAdjuster.java | 248 ++
sf/saxon/serialize/TEXTEmitter.java | 136 +
sf/saxon/serialize/UTF8Writer.java | 407 ++
sf/saxon/serialize/UncommittedSerializer.java | 211 +
sf/saxon/serialize/UnicodeNormalizer.java | 72 +
sf/saxon/serialize/XHTML1Emitter.java | 63 +
sf/saxon/serialize/XHTMLURIEscaper.java | 142 +
sf/saxon/serialize/XML10ContentChecker.java | 177 +
sf/saxon/serialize/XMLEmitter.java | 860 ++++
sf/saxon/serialize/XMLIndenter.java | 351 ++
sf/saxon/serialize/charcode/ASCIICharacterSet.java | 31 +
sf/saxon/serialize/charcode/CharacterSet.java | 33 +
.../serialize/charcode/CharacterSetFactory.java | 135 +
.../serialize/charcode/ISO88591CharacterSet.java | 31 +
sf/saxon/serialize/charcode/JavaCharacterSet.java | 87 +
sf/saxon/serialize/charcode/UTF16CharacterSet.java | 129 +
sf/saxon/serialize/charcode/UTF8CharacterSet.java | 134 +
sf/saxon/serialize/charcode/XMLCharacterData.java | 773 ++++
sf/saxon/serialize/charcode/package.html | 50 +
sf/saxon/serialize/codenorm/Normalizer.java | 269 ++
sf/saxon/serialize/codenorm/NormalizerData.java | 147 +
.../serialize/codenorm/UnicodeDataGenerator.java | 657 +++
.../codenorm/UnicodeDataParserFromXML.java | 253 ++
sf/saxon/serialize/codenorm/package.html | 23 +
sf/saxon/serialize/package.html | 57 +
sf/saxon/stax/ReceiverToXMLStreamWriter.java | 167 +
sf/saxon/stax/StAXResultHandlerImpl.java | 31 +
sf/saxon/stax/XMLStreamWriterDestination.java | 40 +
sf/saxon/style/AbsentExtensionElement.java | 78 +
sf/saxon/style/AttributeValueTemplate.java | 198 +
sf/saxon/style/CollationDeclaration.java | 27 +
sf/saxon/style/DataElement.java | 21 +
sf/saxon/style/Declaration.java | 39 +
sf/saxon/style/ExpressionContext.java | 422 ++
sf/saxon/style/ExtensionInstruction.java | 35 +
sf/saxon/style/LiteralResultElement.java | 506 +++
sf/saxon/style/PrincipalStylesheetModule.java | 1130 +++++
sf/saxon/style/SourceBinding.java | 583 +++
sf/saxon/style/StyleElement.java | 2240 ++++++++++
sf/saxon/style/StyleNodeFactory.java | 452 ++
sf/saxon/style/StylesheetFunctionLibrary.java | 161 +
sf/saxon/style/StylesheetModule.java | 228 +
sf/saxon/style/StylesheetProcedure.java | 37 +
sf/saxon/style/UseWhenFilter.java | 431 ++
sf/saxon/style/UseWhenStaticContext.java | 292 ++
sf/saxon/style/XSLAnalyzeString.java | 226 +
sf/saxon/style/XSLApplyImports.java | 76 +
sf/saxon/style/XSLApplyTemplates.java | 208 +
sf/saxon/style/XSLAttribute.java | 300 ++
sf/saxon/style/XSLAttributeSet.java | 281 ++
sf/saxon/style/XSLCallTemplate.java | 266 ++
sf/saxon/style/XSLCharacterMap.java | 254 ++
sf/saxon/style/XSLChoose.java | 199 +
sf/saxon/style/XSLComment.java | 65 +
sf/saxon/style/XSLCopy.java | 198 +
sf/saxon/style/XSLCopyOf.java | 131 +
sf/saxon/style/XSLDecimalFormat.java | 164 +
sf/saxon/style/XSLDocument.java | 109 +
sf/saxon/style/XSLElement.java | 249 ++
sf/saxon/style/XSLFallback.java | 69 +
sf/saxon/style/XSLForEach.java | 151 +
sf/saxon/style/XSLForEachGroup.java | 401 ++
sf/saxon/style/XSLFunction.java | 521 +++
sf/saxon/style/XSLGeneralIncorporate.java | 249 ++
sf/saxon/style/XSLGeneralVariable.java | 111 +
sf/saxon/style/XSLGlobalParam.java | 125 +
sf/saxon/style/XSLGlobalVariable.java | 271 ++
sf/saxon/style/XSLIf.java | 146 +
sf/saxon/style/XSLImport.java | 25 +
sf/saxon/style/XSLImportSchema.java | 145 +
sf/saxon/style/XSLInclude.java | 23 +
sf/saxon/style/XSLKey.java | 311 ++
sf/saxon/style/XSLLeafNodeConstructor.java | 170 +
sf/saxon/style/XSLLocalParam.java | 215 +
sf/saxon/style/XSLLocalVariable.java | 88 +
sf/saxon/style/XSLMatchingSubstring.java | 61 +
sf/saxon/style/XSLMessage.java | 138 +
sf/saxon/style/XSLNamespace.java | 90 +
sf/saxon/style/XSLNamespaceAlias.java | 109 +
sf/saxon/style/XSLNextMatch.java | 97 +
sf/saxon/style/XSLNumber.java | 287 ++
sf/saxon/style/XSLOtherwise.java | 70 +
sf/saxon/style/XSLOutput.java | 428 ++
sf/saxon/style/XSLOutputCharacter.java | 93 +
sf/saxon/style/XSLPerformSort.java | 143 +
sf/saxon/style/XSLPreserveSpace.java | 136 +
sf/saxon/style/XSLProcessingInstruction.java | 52 +
sf/saxon/style/XSLResultDocument.java | 345 ++
sf/saxon/style/XSLSequence.java | 126 +
sf/saxon/style/XSLSort.java | 50 +
sf/saxon/style/XSLSortOrMergeKey.java | 254 ++
sf/saxon/style/XSLStylesheet.java | 312 ++
sf/saxon/style/XSLTStaticContext.java | 29 +
sf/saxon/style/XSLTemplate.java | 522 +++
sf/saxon/style/XSLText.java | 108 +
sf/saxon/style/XSLValueOf.java | 140 +
sf/saxon/style/XSLWhen.java | 76 +
sf/saxon/style/XSLWithParam.java | 83 +
sf/saxon/style/package.html | 48 +
sf/saxon/sxpath/AbstractStaticContext.java | 477 +++
sf/saxon/sxpath/DedicatedStaticContext.java | 68 +
sf/saxon/sxpath/IndependentContext.java | 463 ++
sf/saxon/sxpath/SimpleContainer.java | 151 +
sf/saxon/sxpath/XPathDynamicContext.java | 272 ++
sf/saxon/sxpath/XPathEvaluator.java | 227 +
sf/saxon/sxpath/XPathExpression.java | 322 ++
sf/saxon/sxpath/XPathStaticContext.java | 75 +
sf/saxon/sxpath/XPathVariable.java | 179 +
sf/saxon/sxpath/package.html | 32 +
sf/saxon/trace/AbstractTraceListener.java | 223 +
sf/saxon/trace/ContextStackFrame.java | 314 ++
sf/saxon/trace/ContextStackIterator.java | 153 +
sf/saxon/trace/ExpressionPresenter.java | 239 ++
sf/saxon/trace/InstructionInfo.java | 81 +
sf/saxon/trace/Location.java | 239 ++
sf/saxon/trace/TimingCodeInjector.java | 45 +
sf/saxon/trace/TimingTraceListener.java | 287 ++
sf/saxon/trace/TraceCodeInjector.java | 62 +
sf/saxon/trace/TraceEventMulticaster.java | 184 +
sf/saxon/trace/XQueryTraceListener.java | 118 +
sf/saxon/trace/XSLTTraceCodeInjector.java | 38 +
sf/saxon/trace/XSLTTraceListener.java | 64 +
sf/saxon/trace/package.html | 33 +
sf/saxon/trans/BuiltInRuleSet.java | 56 +
sf/saxon/trans/CommandLineOptions.java | 748 ++++
sf/saxon/trans/CompilerInfo.java | 427 ++
sf/saxon/trans/ConfigurationReader.java | 582 +++
sf/saxon/trans/DecimalFormatManager.java | 97 +
sf/saxon/trans/DecimalSymbols.java | 476 +++
sf/saxon/trans/DynamicLoader.java | 195 +
sf/saxon/trans/Err.java | 188 +
sf/saxon/trans/IAccumulatorManager.java | 26 +
sf/saxon/trans/KeyDefinition.java | 255 ++
sf/saxon/trans/KeyDefinitionSet.java | 130 +
sf/saxon/trans/KeyManager.java | 728 ++++
sf/saxon/trans/LicenseException.java | 36 +
sf/saxon/trans/Mode.java | 1139 +++++
sf/saxon/trans/NoDynamicContextException.java | 26 +
sf/saxon/trans/NonDelegatingURIResolver.java | 24 +
sf/saxon/trans/Rule.java | 167 +
sf/saxon/trans/RuleManager.java | 284 ++
sf/saxon/trans/RuleTarget.java | 25 +
sf/saxon/trans/SaxonErrorCode.java | 355 ++
sf/saxon/trans/ShallowSkipRuleSet.java | 97 +
sf/saxon/trans/TextOnlyCopyRuleSet.java | 108 +
sf/saxon/trans/Timer.java | 34 +
sf/saxon/trans/UncheckedXPathException.java | 29 +
sf/saxon/trans/XPathException.java | 413 ++
sf/saxon/trans/XmlCatalogResolver.java | 63 +
sf/saxon/trans/package.html | 29 +
sf/saxon/tree/NamespaceNode.java | 676 +++
.../tree/iter/AdjacentTextNodeMergingIterator.java | 93 +
sf/saxon/tree/iter/ArrayIterator.java | 252 ++
sf/saxon/tree/iter/AtomizingIterator.java | 119 +
sf/saxon/tree/iter/AxisIterator.java | 77 +
sf/saxon/tree/iter/AxisIteratorImpl.java | 114 +
sf/saxon/tree/iter/AxisIteratorOverSequence.java | 132 +
sf/saxon/tree/iter/EmptyAxisIterator.java | 63 +
sf/saxon/tree/iter/EmptyIterator.java | 180 +
sf/saxon/tree/iter/GroundedIterator.java | 32 +
sf/saxon/tree/iter/HomogeneityCheckerIterator.java | 99 +
sf/saxon/tree/iter/IteratorIterator.java | 83 +
sf/saxon/tree/iter/ListIterator.java | 126 +
sf/saxon/tree/iter/LookaheadIterator.java | 38 +
sf/saxon/tree/iter/LookaheadIteratorImpl.java | 71 +
sf/saxon/tree/iter/ManualIterator.java | 137 +
sf/saxon/tree/iter/NodeWrappingAxisIterator.java | 122 +
sf/saxon/tree/iter/NodeWrappingFunction.java | 24 +
sf/saxon/tree/iter/PrependIterator.java | 158 +
sf/saxon/tree/iter/ReverseArrayIterator.java | 117 +
sf/saxon/tree/iter/ReversibleIterator.java | 32 +
sf/saxon/tree/iter/SingleNodeIterator.java | 198 +
sf/saxon/tree/iter/SingletonIterator.java | 143 +
sf/saxon/tree/iter/TextLinesIterator.java | 143 +
sf/saxon/tree/iter/UnfailingIterator.java | 58 +
sf/saxon/tree/iter/UnparsedTextIterator.java | 57 +
sf/saxon/tree/iter/UntypedAtomizingIterator.java | 108 +
sf/saxon/tree/iter/package.html | 33 +
sf/saxon/tree/linked/AncestorEnumeration.java | 37 +
sf/saxon/tree/linked/AttributeEnumeration.java | 145 +
sf/saxon/tree/linked/AttributeImpl.java | 346 ++
sf/saxon/tree/linked/ChildEnumeration.java | 35 +
sf/saxon/tree/linked/CommentImpl.java | 66 +
sf/saxon/tree/linked/DocumentImpl.java | 701 +++
sf/saxon/tree/linked/ElementImpl.java | 837 ++++
sf/saxon/tree/linked/FollowingEnumeration.java | 49 +
.../tree/linked/FollowingSiblingEnumeration.java | 32 +
sf/saxon/tree/linked/LineNumberMap.java | 114 +
sf/saxon/tree/linked/LinkedBuilderMonitor.java | 105 +
sf/saxon/tree/linked/LinkedTreeBuilder.java | 438 ++
sf/saxon/tree/linked/NodeFactory.java | 70 +
sf/saxon/tree/linked/NodeImpl.java | 1042 +++++
sf/saxon/tree/linked/ParentNodeImpl.java | 476 +++
sf/saxon/tree/linked/PrecedingEnumeration.java | 55 +
.../linked/PrecedingOrAncestorEnumeration.java | 40 +
.../tree/linked/PrecedingSiblingEnumeration.java | 33 +
sf/saxon/tree/linked/ProcInstImpl.java | 118 +
sf/saxon/tree/linked/SystemIdMap.java | 72 +
sf/saxon/tree/linked/TextImpl.java | 78 +
sf/saxon/tree/linked/TreeEnumeration.java | 184 +
sf/saxon/tree/linked/package.html | 42 +
sf/saxon/tree/package.html | 27 +
sf/saxon/tree/tiny/AncestorEnumeration.java | 67 +
sf/saxon/tree/tiny/AppendableCharSequence.java | 29 +
sf/saxon/tree/tiny/AttributeEnumeration.java | 148 +
sf/saxon/tree/tiny/CharSlice.java | 228 +
sf/saxon/tree/tiny/CompressedWhitespace.java | 308 ++
sf/saxon/tree/tiny/DescendantEnumeration.java | 89 +
sf/saxon/tree/tiny/FollowingEnumeration.java | 102 +
sf/saxon/tree/tiny/LargeStringBuffer.java | 316 ++
sf/saxon/tree/tiny/PrecedingEnumeration.java | 90 +
.../tree/tiny/PrecedingSiblingEnumeration.java | 68 +
sf/saxon/tree/tiny/SiblingEnumeration.java | 287 ++
sf/saxon/tree/tiny/TinyAttributeCollection.java | 272 ++
sf/saxon/tree/tiny/TinyAttributeImpl.java | 289 ++
sf/saxon/tree/tiny/TinyBuilder.java | 486 +++
sf/saxon/tree/tiny/TinyBuilderCondensed.java | 105 +
sf/saxon/tree/tiny/TinyBuilderMonitor.java | 115 +
sf/saxon/tree/tiny/TinyCommentImpl.java | 71 +
sf/saxon/tree/tiny/TinyDocumentImpl.java | 447 ++
sf/saxon/tree/tiny/TinyElementImpl.java | 547 +++
sf/saxon/tree/tiny/TinyNodeImpl.java | 797 ++++
sf/saxon/tree/tiny/TinyParentNodeImpl.java | 109 +
sf/saxon/tree/tiny/TinyProcInstImpl.java | 97 +
sf/saxon/tree/tiny/TinyTextImpl.java | 97 +
sf/saxon/tree/tiny/TinyTree.java | 1507 +++++++
sf/saxon/tree/tiny/TinyTreeEventIterator.java | 191 +
sf/saxon/tree/tiny/WhitespaceTextImpl.java | 124 +
sf/saxon/tree/tiny/package.html | 83 +
sf/saxon/tree/util/AttributeCollectionImpl.java | 807 ++++
sf/saxon/tree/util/DiagnosticNamePool.java | 34 +
sf/saxon/tree/util/DocumentNumberAllocator.java | 33 +
sf/saxon/tree/util/FastStringBuffer.java | 559 +++
sf/saxon/tree/util/NamespaceIterator.java | 134 +
.../tree/util/NamespaceResolverWithDefault.java | 67 +
sf/saxon/tree/util/Navigator.java | 1462 +++++++
sf/saxon/tree/util/Orphan.java | 844 ++++
sf/saxon/tree/util/ProcInstParser.java | 147 +
sf/saxon/tree/util/SteppingNavigator.java | 344 ++
sf/saxon/tree/util/SteppingNode.java | 61 +
sf/saxon/tree/util/package.html | 31 +
sf/saxon/tree/wrapper/AbstractNodeWrapper.java | 644 +++
sf/saxon/tree/wrapper/AbstractVirtualNode.java | 477 +++
sf/saxon/tree/wrapper/SiblingCountingNode.java | 25 +
sf/saxon/tree/wrapper/SpaceStrippedDocument.java | 240 ++
sf/saxon/tree/wrapper/SpaceStrippedNode.java | 422 ++
sf/saxon/tree/wrapper/TypeStrippedDocument.java | 174 +
sf/saxon/tree/wrapper/TypeStrippedNode.java | 195 +
sf/saxon/tree/wrapper/VirtualCopy.java | 874 ++++
sf/saxon/tree/wrapper/VirtualDocumentCopy.java | 120 +
sf/saxon/tree/wrapper/VirtualNode.java | 47 +
sf/saxon/tree/wrapper/VirtualUntypedCopy.java | 174 +
sf/saxon/tree/wrapper/WrappingFunction.java | 29 +
sf/saxon/tree/wrapper/WrappingIterator.java | 136 +
sf/saxon/tree/wrapper/package.html | 35 +
sf/saxon/type/AnyFunctionType.java | 232 +
sf/saxon/type/AnyItemType.java | 141 +
sf/saxon/type/AnySimpleType.java | 434 ++
sf/saxon/type/AnyType.java | 593 +++
sf/saxon/type/AtomicType.java | 79 +
sf/saxon/type/BuiltInAtomicType.java | 1102 +++++
sf/saxon/type/BuiltInListType.java | 571 +++
sf/saxon/type/BuiltInType.java | 76 +
sf/saxon/type/ComplexType.java | 247 ++
sf/saxon/type/ConversionResult.java | 36 +
sf/saxon/type/Converter.java | 871 ++++
sf/saxon/type/ErrorType.java | 513 +++
sf/saxon/type/ExternalObjectType.java | 273 ++
sf/saxon/type/FunctionItemType.java | 65 +
sf/saxon/type/ISchemaCompiler.java | 15 +
sf/saxon/type/ItemType.java | 149 +
sf/saxon/type/ListType.java | 25 +
sf/saxon/type/PlainType.java | 29 +
sf/saxon/type/SchemaComponent.java | 97 +
sf/saxon/type/SchemaComponentVisitor.java | 20 +
sf/saxon/type/SchemaDeclaration.java | 54 +
sf/saxon/type/SchemaException.java | 68 +
sf/saxon/type/SchemaType.java | 296 ++
sf/saxon/type/SimpleType.java | 140 +
sf/saxon/type/StringConverter.java | 926 ++++
sf/saxon/type/StringToDouble.java | 158 +
sf/saxon/type/Type.java | 590 +++
sf/saxon/type/TypeHierarchy.java | 651 +++
sf/saxon/type/UnionType.java | 24 +
sf/saxon/type/UnresolvedReferenceException.java | 23 +
sf/saxon/type/Untyped.java | 604 +++
sf/saxon/type/ValidationException.java | 397 ++
sf/saxon/type/ValidationFailure.java | 293 ++
sf/saxon/type/ValidationParams.java | 29 +
sf/saxon/type/package.html | 44 +
sf/saxon/value/AnyURIValue.java | 159 +
sf/saxon/value/AtomicValue.java | 386 ++
sf/saxon/value/Base64BinaryValue.java | 339 ++
sf/saxon/value/BigIntegerValue.java | 575 +++
sf/saxon/value/BooleanValue.java | 287 ++
sf/saxon/value/CalendarValue.java | 312 ++
sf/saxon/value/Cardinality.java | 203 +
sf/saxon/value/Closure.java | 264 ++
sf/saxon/value/DateTimeValue.java | 1183 +++++
sf/saxon/value/DateValue.java | 544 +++
sf/saxon/value/DayTimeDurationValue.java | 447 ++
sf/saxon/value/DecimalValue.java | 579 +++
sf/saxon/value/DoubleValue.java | 493 +++
sf/saxon/value/DurationValue.java | 854 ++++
sf/saxon/value/EmptySequence.java | 187 +
sf/saxon/value/FloatValue.java | 342 ++
sf/saxon/value/FloatingPointConverter.java | 632 +++
sf/saxon/value/GDateValue.java | 438 ++
sf/saxon/value/GDayValue.java | 123 +
sf/saxon/value/GMonthDayValue.java | 125 +
sf/saxon/value/GMonthValue.java | 125 +
sf/saxon/value/GYearMonthValue.java | 132 +
sf/saxon/value/GYearValue.java | 129 +
sf/saxon/value/HexBinaryValue.java | 260 ++
sf/saxon/value/Int64Value.java | 669 +++
sf/saxon/value/IntegerRange.java | 225 +
sf/saxon/value/IntegerValue.java | 453 ++
sf/saxon/value/MemoClosure.java | 455 ++
sf/saxon/value/NotationValue.java | 164 +
sf/saxon/value/NumericValue.java | 255 ++
sf/saxon/value/ObjectValue.java | 162 +
sf/saxon/value/QNameValue.java | 207 +
sf/saxon/value/QualifiedNameValue.java | 195 +
sf/saxon/value/SequenceExtent.java | 459 ++
sf/saxon/value/SequenceType.java | 482 +++
sf/saxon/value/SingletonClosure.java | 123 +
sf/saxon/value/SingletonItem.java | 180 +
sf/saxon/value/StringToDouble11.java | 35 +
sf/saxon/value/StringValue.java | 490 +++
sf/saxon/value/TextFragmentValue.java | 1162 +++++
sf/saxon/value/TimeValue.java | 649 +++
sf/saxon/value/UntypedAtomicValue.java | 116 +
sf/saxon/value/Whitespace.java | 330 ++
sf/saxon/value/YearMonthDurationValue.java | 251 ++
sf/saxon/value/package.html | 51 +
sf/saxon/xpath/JAXPVariable.java | 155 +
sf/saxon/xpath/JAXPXPathStaticContext.java | 326 ++
sf/saxon/xpath/XPathEvaluator.java | 552 +++
sf/saxon/xpath/XPathExpressionImpl.java | 467 ++
sf/saxon/xpath/XPathFactoryImpl.java | 199 +
sf/saxon/xpath/XPathFunctionCall.java | 201 +
sf/saxon/xpath/XPathFunctionLibrary.java | 160 +
sf/saxon/xpath/package.html | 47 +
sf/saxon/xqj/Closable.java | 63 +
sf/saxon/xqj/ObjectConverter.java | 54 +
sf/saxon/xqj/SaxonDuration.java | 333 ++
sf/saxon/xqj/SaxonXMLGregorianCalendar.java | 759 ++++
sf/saxon/xqj/SaxonXQConnection.java | 207 +
sf/saxon/xqj/SaxonXQDataFactory.java | 1042 +++++
sf/saxon/xqj/SaxonXQDataSource.java | 539 +++
sf/saxon/xqj/SaxonXQDynamicContext.java | 270 ++
sf/saxon/xqj/SaxonXQExpression.java | 132 +
sf/saxon/xqj/SaxonXQForwardSequence.java | 359 ++
sf/saxon/xqj/SaxonXQItem.java | 327 ++
sf/saxon/xqj/SaxonXQItemAccessor.java | 28 +
sf/saxon/xqj/SaxonXQItemType.java | 260 ++
sf/saxon/xqj/SaxonXQMetaData.java | 163 +
sf/saxon/xqj/SaxonXQPreparedExpression.java | 197 +
sf/saxon/xqj/SaxonXQSequence.java | 426 ++
sf/saxon/xqj/SaxonXQSequenceType.java | 71 +
sf/saxon/xqj/SaxonXQStaticContext.java | 368 ++
sf/saxon/xqj/StandardObjectConverter.java | 284 ++
sf/saxon/xqj/package.html | 38 +
sf/saxon/z/AbstractIntSet.java | 109 +
sf/saxon/z/IntArraySet.java | 355 ++
sf/saxon/z/IntBlockSet.java | 129 +
sf/saxon/z/IntCheckingSet.java | 89 +
sf/saxon/z/IntComplementPredicate.java | 42 +
sf/saxon/z/IntComplementSet.java | 104 +
sf/saxon/z/IntEmptySet.java | 88 +
sf/saxon/z/IntExceptPredicate.java | 44 +
sf/saxon/z/IntHashMap.java | 427 ++
sf/saxon/z/IntHashSet.java | 367 ++
sf/saxon/z/IntIntersectionPredicate.java | 44 +
sf/saxon/z/IntIterator.java | 29 +
sf/saxon/z/IntPredicate.java | 22 +
sf/saxon/z/IntRangeSet.java | 425 ++
sf/saxon/z/IntSet.java | 119 +
sf/saxon/z/IntSetPredicate.java | 43 +
sf/saxon/z/IntSingletonSet.java | 109 +
sf/saxon/z/IntToIntArrayMap.java | 182 +
sf/saxon/z/IntToIntHashMap.java | 283 ++
sf/saxon/z/IntToIntMap.java | 81 +
sf/saxon/z/IntUnionPredicate.java | 43 +
sf/saxon/z/IntUniversalSet.java | 83 +
sf/saxon/z/IntValuePredicate.java | 40 +
sf/saxon/z/package.html | 30 +
1134 files changed, 288371 insertions(+)
diff --git a/sf/saxon/Configuration.java b/sf/saxon/Configuration.java
new file mode 100644
index 0000000..5fee409
--- /dev/null
+++ b/sf/saxon/Configuration.java
@@ -0,0 +1,4387 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import com.saxonica.config.DotNetPlatformPE;
+import com.saxonica.config.EnterpriseConfiguration;
+import com.saxonica.config.JavaPlatformPE;
+import com.saxonica.config.ProfessionalConfiguration;
+import net.sf.saxon.dotnet.DotNetPlatform;
+import net.sf.saxon.event.*;
+import net.sf.saxon.evpull.PullEventSource;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.number.Numberer_en;
+import net.sf.saxon.expr.parser.ExpressionParser;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.functions.FunctionLibraryList;
+import net.sf.saxon.functions.IntegratedFunctionLibrary;
+import net.sf.saxon.functions.VendorFunctionLibrary;
+import net.sf.saxon.java.JavaPlatform;
+import net.sf.saxon.lib.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.pattern.PatternParser20;
+import net.sf.saxon.pull.PullSource;
+import net.sf.saxon.query.QueryParser;
+import net.sf.saxon.query.StaticQueryContext;
+import net.sf.saxon.serialize.charcode.CharacterSetFactory;
+import net.sf.saxon.style.StyleNodeFactory;
+import net.sf.saxon.sxpath.IndependentContext;
+import net.sf.saxon.trace.TraceCodeInjector;
+import net.sf.saxon.trace.XSLTTraceCodeInjector;
+import net.sf.saxon.trans.*;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.util.DocumentNumberAllocator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import org.xml.sax.*;
+import org.xml.sax.ext.DefaultHandler2;
+import org.xml.sax.ext.LexicalHandler;
+
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+import java.io.*;
+import java.net.URL;
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+
+/**
+ * This class holds details of user-selected configuration options for a set of transformations
+ * and/or queries. When running XSLT, the preferred way of setting configuration options is via
+ * the JAXP TransformerFactory interface, but the Configuration object provides a finer
+ * level of control. As yet there is no standard API for XQuery, so the only way of setting
+ * Configuration information is to use the methods on this class directly.
+ * <p/>
+ * <p>As well as holding configuration settings, this class acts as a factory for classes
+ * providing service in particular areas: error handling, URI resolution, and the like. Some
+ * of these services are chosen on the basis of the current platform (Java or .NET), some vary
+ * depending whether the environment is schema-aware or not.</p>
+ * <p/>
+ * <p>The <code>Configuration</code> provides access to a {@link NamePool} which is used to manage
+ * all the names used in stylesheets, queries, schemas, and source and documents: the NamePool
+ * allocates integer codes to these names allowing efficient storage and comparison. Normally
+ * there will be a one-to-one relationship between a <code>NamePool</code> and a <code>Configuration</code>.
+ * It is possible, however, for several <code>Configuration</code> objects to share the same
+ * <code>NamePool</code>. Until Saxon 8.9, by default all <code>Configuration</code> objects
+ * shared a single <code>NamePool</code> unless configured otherwise; this changed in 8.9 so that
+ * the default is to allocate a new <code>NamePool</code> for each <code>Configuration</code>.</p>
+ * <p/>
+ * <p>The <code>Configuration</code> establishes the scope within which node identity is managed.
+ * Every document belongs to a <code>Configuration</code>, and every node has a distinct identity
+ * within that <code>Configuration</code>. In consequence, it is not possible for any query or
+ * transformation to manipulate multiple documents unless they all belong to the same
+ * <code>Configuration</code>.</p>
+ * <p/>
+ * <p>Saxon-EE has a subclass of the <code>Configuration</code> class which provides the additional
+ * services needed for schema-aware processing. The {@link com.saxonica.config.EnterpriseConfiguration}
+ * also holds a cache of loaded schema components used for compiling schema-aware transformations
+ * and queries, and for validating instance documents.</p>
+ * <p/>
+ * <p>Since Saxon 8.4, the JavaDoc documentation for Saxon attempts to identify interfaces
+ * that are considered stable, and will only be changed in a backwards-incompatible way
+ * if there is an overriding reason to do so. These interfaces and methods are labelled
+ * with the JavaDoc "since" tag. The value 8.n indicates a method in this category that
+ * was introduced in Saxon version 8.n: or in the case of 8.4, that was present in Saxon 8.4
+ * and possibly in earlier releases. (In some cases, these methods have been unchanged for
+ * a long time.) Methods without a "since" tag, although public, are provided for internal
+ * use or for use by advanced users, and are subject to change from one release to the next.
+ * The presence of a "since" tag on a class or interface indicates that there are one or more
+ * methods in the class that are considered stable; it does not mean that all methods are
+ * stable.
+ *
+ * @since 8.4
+ */
+
+
+public class Configuration implements Serializable, SourceResolver, NotationSet {
+
+ private static final Platform platform = makePlatform();
+ public static final Class<? extends Configuration> configurationClass = makeConfigurationClass();
+ public static final String softwareEdition = setSoftwareEdition();
+ private transient Object apiProcessor = null;
+ private transient CharacterSetFactory characterSetFactory;
+ private CollationMap collationMap = new CollationMap(this);
+ private CollationURIResolver collationResolver = new StandardCollationURIResolver();
+ protected CollectionURIResolver collectionResolver = new StandardCollectionURIResolver();
+ private EnvironmentVariableResolver environmentVariableResolver = new StandardEnvironmentVariableResolver();
+ private String defaultCollection = null;
+ private ParseOptions defaultParseOptions = new ParseOptions();
+ private transient StaticQueryContext defaultStaticQueryContext;
+
+ private CompilerInfo defaultXsltCompilerInfo = new CompilerInfo();
+
+
+ private DocumentNumberAllocator documentNumberAllocator = new DocumentNumberAllocator();
+ /*@Nullable*/
+ private transient Debugger debugger = null;
+ private String defaultLanguage = Locale.getDefault().getLanguage();
+
+
+ private String defaultCountry = Locale.getDefault().getCountry();
+ private Properties defaultSerializationProperties = new Properties();
+ private int domLevel = 3;
+ private transient DynamicLoader dynamicLoader = new DynamicLoader();
+
+ private Set<String> enabledProperties = new HashSet<String>();
+
+ private List<ExternalObjectModel> externalObjectModels = new ArrayList<ExternalObjectModel>(4);
+ private DocumentPool globalDocumentPool = new DocumentPool();
+ private int hostLanguage = XSLT;
+ private IntegratedFunctionLibrary integratedFunctionLibrary = new IntegratedFunctionLibrary();
+ private transient LocalizerFactory localizerFactory;
+ private ModuleURIResolver moduleURIResolver = null;
+ private NamePool namePool = new NamePool();
+
+ protected int optimizationLevel = Optimizer.FULL_OPTIMIZATION;
+ protected Optimizer optimizer = null;
+
+ private SchemaURIResolver schemaURIResolver = null;
+ private SerializerFactory serializerFactory = new SerializerFactory(this);
+ private volatile ConcurrentLinkedQueue<XMLReader> sourceParserPool = new ConcurrentLinkedQueue<XMLReader>();
+ private volatile ConcurrentLinkedQueue<XMLReader> styleParserPool = new ConcurrentLinkedQueue<XMLReader>();
+ private String sourceParserClass;
+ private transient SourceResolver sourceResolver = this;
+ private transient PrintStream standardErrorOutput = System.err;
+ private ModuleURIResolver standardModuleURIResolver = StandardModuleURIResolver.getInstance();
+ private String styleParserClass;
+ private StandardURIResolver systemURIResolver = new StandardURIResolver(this);
+
+ private transient XPathContext theConversionContext = null;
+ private ConversionRules theConversionRules = null;
+ private transient TraceListener traceListener = null;
+ private String traceListenerClass = null;
+ protected transient TypeHierarchy typeHierarchy;
+
+ private transient URIResolver uriResolver;
+ protected VendorFunctionLibrary vendorFunctionLibrary;
+ protected int xsdVersion = XSD10;
+ private int xmlVersion = XML10;
+
+
+ /**
+ * Constant indicating that the processor should take the recovery action
+ * when a recoverable error occurs, with no warning message.
+ */
+ public static final int RECOVER_SILENTLY = 0;
+ /**
+ * Constant indicating that the processor should produce a warning
+ * when a recoverable error occurs, and should then take the recovery
+ * action and continue.
+ */
+ public static final int RECOVER_WITH_WARNINGS = 1;
+ /**
+ * Constant indicating that when a recoverable error occurs, the
+ * processor should not attempt to take the defined recovery action,
+ * but should terminate with an error.
+ */
+ public static final int DO_NOT_RECOVER = 2;
+
+ /**
+ * Constant indicating the XML Version 1.0
+ */
+
+ public static final int XML10 = 10;
+
+ /**
+ * Constant indicating the XML Version 1.1
+ */
+
+ public static final int XML11 = 11;
+
+ /**
+ * Constant indicating that the host language is XSLT
+ */
+ public static final int XSLT = 50;
+
+ /**
+ * Constant indicating that the host language is XQuery
+ */
+ public static final int XQUERY = 51;
+
+ /**
+ * Constant indicating that the "host language" is XML Schema
+ */
+ public static final int XML_SCHEMA = 52;
+
+ /**
+ * Constant indicating that the host language is Java: that is, this is a free-standing
+ * Java application with no XSLT or XQuery content
+ */
+ public static final int JAVA_APPLICATION = 53;
+
+ /**
+ * Constant indicating that the host language is XPATH itself - that is, a free-standing XPath environment
+ */
+ public static final int XPATH = 54;
+
+ /**
+ * Language versions for XML Schema
+ */
+ public static final int XSD10 = 10;
+ public static final int XSD11 = 11;
+
+
+
+
+ /**
+ * Read a resource file issued with the Saxon product
+ *
+ * @param filename the filename of the file to be read
+ * @param messages List to be populated with messages in the event of failure
+ * @param loaders List to be populated with the ClassLoader that succeeded in loading the resource
+ * @return an InputStream for reading the file/resource
+ */
+
+ /*@Nullable*/
+ public static InputStream locateResource(String filename, List<String> messages, List<ClassLoader> loaders) {
+ ClassLoader loader = null;
+ try {
+ loader = Thread.currentThread().getContextClassLoader();
+ } catch (Exception err) {
+ messages.add("Failed to getContextClassLoader() - continuing\n");
+ }
+
+ InputStream in = null;
+
+ if (loader != null) {
+ in = loader.getResourceAsStream(filename);
+ if (in == null) {
+ messages.add("Cannot read " + filename + " file located using ClassLoader " +
+ loader + " - continuing\n");
+ }
+ }
+
+ if (in == null) {
+ loader = Configuration.class.getClassLoader();
+ if (loader != null) {
+ in = loader.getResourceAsStream(filename);
+ if (in == null) {
+ messages.add("Cannot read " + filename + " file located using ClassLoader " +
+ loader + " - continuing\n");
+ }
+ }
+ }
+
+ if (in == null) {
+ // Means we're in a very strange class-loading environment, things are getting desparate
+ URL url = ClassLoader.getSystemResource(filename);
+ if (url != null) {
+ try {
+ in = url.openStream();
+ } catch (IOException ioe) {
+ messages.add("IO error " + ioe.getMessage() +
+ " reading " + filename + " located using getSystemResource(): using defaults");
+ in = null;
+ }
+ }
+ }
+ loaders.add(loader);
+ return in;
+
+ }
+
+ /**
+ * Factory method to construct a Configuration object by reading a configuration file.
+ *
+ * @param source Source object containing the configuration file
+ * @return the resulting Configuration
+ * @throws net.sf.saxon.trans.XPathException if the configuration file cannot be read
+ * or is invalid
+ */
+
+ public static Configuration readConfiguration(Source source) throws XPathException {
+ Configuration tempConfig = newConfiguration();
+ return tempConfig.readConfigurationFile(source);
+ }
+
+ /**
+ * Read the configuration file an construct a new Configuration (the real one)
+ *
+ * @param source the source of the configuration file
+ * @return the Configuration that will be used for real work
+ * @throws XPathException if the configuration file cannot be read or is invalid
+ */
+
+ protected Configuration readConfigurationFile(Source source) throws XPathException {
+ return new ConfigurationReader().makeConfiguration(source);
+ }
+
+
+ /**
+ * Create a non-schema-aware configuration object with default settings for all options.
+ *
+ * @since 8.4
+ */
+
+ public Configuration() {
+ init();
+ }
+
+ /**
+ * Factory method to create a Configuration, of the class defined using conditional
+ * compilation tags when compiling this version of Saxon: that is,
+ * the type of Configuration appropriate to the edition of the software
+ * being used. This method does not check that the Configuration is licensed.
+ *
+ * @return a Configuration object of the class appropriate to the Saxon edition in use.
+ * @since 9.2
+ */
+
+ public static Configuration newConfiguration() {
+ //System.err.println("New configuration: " + configurationClass.getName());
+ try {
+ return configurationClass.newInstance();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw new RuntimeException("Cannot instantiate a Configuration", e);
+ }
+ }
+
+
+ protected void init() {
+ platform.initialize(this);
+ defaultXsltCompilerInfo.setURIResolver(getSystemURIResolver());
+ StandardEntityResolver resolver = new StandardEntityResolver();
+ resolver.setConfiguration(this);
+ defaultParseOptions.setEntityResolver(resolver);
+ internalSetBooleanProperty(FeatureKeys.PREFER_JAXP_PARSER, true);
+ internalSetBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS, true);
+ }
+
+ /**
+ * Static method to instantiate a professional or enterprise configuration.
+ * <p>This method fails if the specified configuration class cannot be loaded,
+ * but it does not check whether there is a license available.
+ *
+ * @param classLoader - the class loader to be used. If null, the context class loader for the current
+ * thread is used.
+ * @param className - the name of the configuration class. Defaults to
+ * "com.saxonica.config.ProfessionalConfiguration" if null is supplied. This allows an assembly
+ * qualified name to be supplied under .NET. The class, once instantiated, must be an instance
+ * of Configuration.
+ * @return the new ProfessionalConfiguration or EnterpriseConfiguration
+ * @throws RuntimeException if the required Saxon edition cannot be loaded
+ * @since 9.2 (renamed from makeSchemaAwareConfiguration)
+ */
+
+ public static Configuration makeLicensedConfiguration(ClassLoader classLoader, /*@Nullable*/ String className)
+ throws RuntimeException {
+ if (className == null) {
+ className = "com.saxonica.config.ProfessionalConfiguration";
+ }
+ try {
+ Class theClass;
+ ClassLoader loader = classLoader;
+ if (loader == null) {
+ try {
+ loader = Thread.currentThread().getContextClassLoader();
+ } catch (Exception err) {
+ System.err.println("Failed to getContextClassLoader() - continuing");
+ }
+ }
+ if (loader != null) {
+ try {
+ theClass = loader.loadClass(className);
+ } catch (Exception ex) {
+ theClass = Class.forName(className);
+ }
+ } else {
+ theClass = Class.forName(className);
+ }
+ return (Configuration) theClass.newInstance();
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Make an enterprise configuration
+ *
+ * @param loader the class loader (or null)
+ * @param className the name of the configuration class (or null)
+ * @return the same result as {@link #makeLicensedConfiguration}
+ * @deprecated since 9.2. Use {@link #makeLicensedConfiguration} instead.
+ */
+
+ public static Configuration makeSchemaAwareConfiguration(ClassLoader loader, String className) {
+ return makeLicensedConfiguration(loader, className);
+ }
+
+ /**
+ * Get the edition code identifying this configuration: "HE", "PE" or "EE"
+ * @return the code identifying the Saxon edition associated with this configuration
+ */
+
+ public String getEditionCode() {
+ return "HE";
+ }
+
+ /**
+ * Save the Processor object that owns this Configuration in the relevant API.
+ *
+ * @param processor This can be any object, but it is actually used to hold one of the
+ * following:
+ * <ul>
+ * <li>When using the Java s9api interface, the <code>net.sf.saxon.s9api.Processor</code></li>
+ * <li>When using the .NET interface, the <code>Saxon.Api.Processor</code></li>
+ * <li>When using the JAXP transformation interface, the JAXP <code>TransformerFactory</code></li>
+ * <li>When using the JAXP XPath interface, the JAXP <code>XPathFactory</code></li>
+ * <li>When using the JAXP Schema interface, the JAXP <code>SchemaFactory</code></li>
+ * <li>When using XQJ, the <code>XQDataSource</code></li>
+ * </ul>
+ * @since 9.2
+ */
+
+ public void setProcessor(Object processor) {
+ this.apiProcessor = processor;
+ }
+
+ /**
+ * Get the Processor object that created this Configuration in the relevant API.
+ * <p>The main purpose of this interface is to allow extension functions called from
+ * a stylesheet or query to get access to the originating processor. This is particularly
+ * useful when methods are static as there is then limited scope for passing data from the
+ * calling application to the code of the extension function.</p>
+ *
+ * @return the processor that was supplied to the {@link #setProcessor(Object)} method, or null
+ * if this method has not been called. In practice this property is used to hold one of the
+ * following:
+ * <ul>
+ * <li>When using the Java s9api interface, the <code>net.sf.saxon.s9api.Processor</code></li>
+ * <li>When using the .NET interface, the <code>Saxon.Api.Processor</code></li>
+ * <li>When using the JAXP transformation interface, the JAXP <code>TransformerFactory</code></li>
+ * <li>When using the JAXP XPath interface, the JAXP <code>XPathFactory</code></li>
+ * <li>When using the JAXP Schema interface, the JAXP <code>SchemaFactory</code></li>
+ * <li>When using XQJ, the <code>XQDataSource</code></li>
+ * </ul>
+ * @since 9.2
+ */
+
+ /*@Nullable*/
+ public Object getProcessor() {
+ return apiProcessor;
+ }
+
+ /**
+ * Get a message used to identify this product when a transformation is run using the -t option
+ *
+ * @return A string containing both the product name and the product
+ * version
+ * @since 8.4
+ */
+
+ public String getProductTitle() {
+ return "Saxon-" + getEditionCode() + " " + Version.getProductVersion() + platform.getPlatformSuffix() + " from Saxonica";
+ }
+
+ /**
+ * Check whether a particular feature is licensed, with a fatal error if it is not
+ *
+ * @param feature the feature in question, identified by a constant in class {@link net.sf.saxon.Configuration.LicenseFeature}
+ * @param name the name of the feature for use in diagnostics
+ * @throws LicenseException if the feature is not licensed. This is a RunTimeException, so it will normally be fatal.
+ */
+
+ public void checkLicensedFeature(int feature, String name) throws LicenseException {
+ String require = (feature == LicenseFeature.PROFESSIONAL_EDITION ? "PE" : "EE");
+ String message = "Requested feature (" + name + ") requires Saxon-" + require;
+ if (!softwareEdition.equals("HE")) {
+ message += ". You are using Saxon-" + softwareEdition + " software, but the Configuration is an instance of " +
+ getClass().getName() + "; to use this feature you need to create an instance of " +
+ (feature == LicenseFeature.PROFESSIONAL_EDITION ?
+ "com.saxonica.config.ProfessionalConfiguration" :
+ "com.saxonica.config.EnterpriseConfiguration");
+ }
+ throw new LicenseException(message, LicenseException.WRONG_CONFIGURATION);
+ }
+
+ /**
+ * Determine if a particular feature is licensed.
+ *
+ * @param feature the feature in question, identified by a constant in class {@link net.sf.saxon.Configuration.LicenseFeature}
+ * @return true if the feature is licensed, false if it is not.
+ */
+
+ public boolean isLicensedFeature(int feature) {
+ // changing this to true will do no good; it will cause Saxon to attempt to use the unavailable feature, rather than
+ // recovering from its absence.
+ return false;
+ }
+
+ /**
+ * Determine if the configuration is schema-aware, for the given host language
+ *
+ * @param language the required host language: XSLT, XQUERY, or XML_SCHEMA
+ * @return true if the configuration is schema-aware
+ * @since 8.4
+ * @deprecated since 9.2: use isLicensedFeature() instead
+ */
+
+ public boolean isSchemaAware(int language) {
+ return false;
+ // changing this to true will do no good!
+ }
+
+ /**
+ * Display a message about the license status
+ */
+
+ public void displayLicenseMessage() {
+ }
+
+ /**
+ * Get the host language used in this configuration. The typical values
+ * are XSLT and XQUERY. The values XML_SCHEMA and JAVA_APPLICATION may also
+ * be encountered.
+ * <p/>
+ * This method is problematic because it is possible to run multiple transformations
+ * or queries within the same configuration. The method is therefore best avoided.
+ * Instead, use {@link net.sf.saxon.expr.Container#getHostLanguage}.
+ * Internally its only use is in deciding (in Saxon-EE only) which error listener to
+ * use by default at compile time, and since the standard XSLT and XQuery listeners have
+ * no differences when used for static errors, the choice is immaterial.
+ *
+ * @return Configuration.XSLT or Configuration.XQUERY
+ */
+
+ public int getHostLanguage() {
+ return hostLanguage;
+ }
+
+ /**
+ * Set the host language used in this configuration. The possible values
+ * are XSLT and XQUERY.
+ *
+ * @param hostLanguage Configuration.XSLT or Configuration.XQUERY
+ */
+
+ public void setHostLanguage(int hostLanguage) {
+ this.hostLanguage = hostLanguage;
+ }
+
+ /**
+ * Get the Platform to be used for platform-dependent methods
+ *
+ * @return the platform to be used
+ */
+
+ public static Platform getPlatform() {
+ return platform;
+ }
+
+ /**
+ * Set the DynamicLoader to be used. By default an instance of {@link DynamicLoader} is used
+ * for all dynamic loading of Java classes. This method allows the actions of the standard
+ * DynamicLoader to be overridden
+ *
+ * @param dynamicLoader the DynamicLoader to be used by this Configuration
+ */
+
+ public void setDynamicLoader(DynamicLoader dynamicLoader) {
+ this.dynamicLoader = dynamicLoader;
+ }
+
+ /**
+ * Get the DynamicLoader used by this Configuration. By default the standard system-supplied
+ * dynamic loader is returned.
+ *
+ * @return the DynamicLoader in use - either a user-supplied DynamicLoader, or the standard one
+ * supplied by the system.
+ */
+
+ public DynamicLoader getDynamicLoader() {
+ return dynamicLoader;
+ }
+
+ /**
+ * Load a class using the class name provided.
+ * Note that the method does not check that the object is of the right class.
+ * <p/>
+ * This method is intended for internal use only. The call is delegated to the
+ * <code>DynamicLoader</code>, which may be overridden by a user-defined <code>DynamicLoader</code>.
+ *
+ * @param className A string containing the name of the
+ * class, for example "com.microstar.sax.LarkDriver"
+ * @param tracing true if diagnostic tracing is required
+ * @param classLoader The ClassLoader to be used to load the class, or null to
+ * use the ClassLoader selected by the DynamicLoader.
+ * @return an instance of the class named, or null if it is not
+ * loadable.
+ * @throws XPathException if the class cannot be loaded.
+ */
+
+ public Class getClass(String className, boolean tracing, /*@Nullable*/ ClassLoader classLoader) throws XPathException {
+ return dynamicLoader.getClass(className, (tracing ? standardErrorOutput : null), classLoader);
+ }
+
+ /**
+ * Instantiate a class using the class name provided.
+ * Note that the method does not check that the object is of the right class.
+ * <p/>
+ * This method is intended for internal use only. The call is delegated to the
+ * <code>DynamicLoader</code>, which may be overridden by a user-defined <code>DynamicLoader</code>.
+ * <p/>
+ * Diagnostic output is produced if the option "isTiming" is set (corresponding to the -t option on
+ * the command line).
+ *
+ * @param className A string containing the name of the
+ * class, for example "com.microstar.sax.LarkDriver"
+ * @param classLoader The ClassLoader to be used to load the class, or null to
+ * use the ClassLoader selected by the DynamicLoader.
+ * @return an instance of the class named, or null if it is not
+ * loadable.
+ * @throws XPathException if the class cannot be loaded.
+ */
+
+ public Object getInstance(String className, /*@Nullable*/ ClassLoader classLoader) throws XPathException {
+ return dynamicLoader.getInstance(className, (isTiming() ? standardErrorOutput : null), classLoader);
+ }
+
+ /**
+ * Get the URIResolver used in this configuration
+ *
+ * @return the URIResolver. If no URIResolver has been set explicitly, the
+ * default URIResolver is used.
+ * @since 8.4
+ */
+
+ public URIResolver getURIResolver() {
+ if (uriResolver == null) {
+ return systemURIResolver;
+ }
+ return uriResolver;
+ }
+
+ /**
+ * Set the URIResolver to be used in this configuration. This will be used to
+ * resolve the URIs used statically (e.g. by xsl:include) and also the URIs used
+ * dynamically by functions such as document() and doc(). Note that the URIResolver
+ * does not resolve the URI in the sense of RFC 2396 (which is also the sense in which
+ * the resolve-uri() function uses the term): rather it dereferences an absolute URI
+ * to obtain an actual resource, which is returned as a Source object.
+ *
+ * @param resolver The URIResolver to be used.
+ * @since 8.4
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ uriResolver = resolver;
+ if (resolver instanceof StandardURIResolver) {
+ ((StandardURIResolver) resolver).setConfiguration(this);
+ }
+ defaultXsltCompilerInfo.setURIResolver(resolver);
+ }
+
+ /**
+ * Set the URIResolver to a URI resolver that allows query parameters after the URI,
+ * and in the case of Saxon-EE, that inteprets the file extension .ptree
+ */
+
+ public void setParameterizedURIResolver() {
+ getSystemURIResolver().setRecognizeQueryParameters(true);
+ }
+
+ /**
+ * Get the system-defined URI Resolver. This is used when the user-defined URI resolver
+ * returns null as the result of the resolve() method
+ *
+ * @return the system-defined URI resolver
+ */
+
+ public StandardURIResolver getSystemURIResolver() {
+ return systemURIResolver;
+ }
+
+ /**
+ * Create an instance of a URIResolver with a specified class name.
+ * Note that this method does not register the URIResolver with this Configuration.
+ *
+ * @param className The fully-qualified name of the URIResolver class
+ * @return The newly created URIResolver
+ * @throws TransformerException if the requested class does not
+ * implement the javax.xml.transform.URIResolver interface
+ */
+ public URIResolver makeURIResolver(String className) throws TransformerException {
+ Object obj = dynamicLoader.getInstance(className, null);
+ if (obj instanceof StandardURIResolver) {
+ ((StandardURIResolver) obj).setConfiguration(this);
+ }
+ if (obj instanceof URIResolver) {
+ return (URIResolver) obj;
+ }
+ throw new XPathException("Class " + className + " is not a URIResolver");
+ }
+
+ /**
+ * Get the ErrorListener used in this configuration. If no ErrorListener
+ * has been supplied explicitly, the default ErrorListener is used.
+ *
+ * @return the ErrorListener.
+ * @since 8.4
+ */
+
+ public ErrorListener getErrorListener() {
+ ErrorListener listener = defaultParseOptions.getErrorListener();
+ if (listener == null) {
+ listener = new StandardErrorListener();
+ ((StandardErrorListener) listener).setErrorOutput(standardErrorOutput);
+ ((StandardErrorListener) listener).setRecoveryPolicy(defaultXsltCompilerInfo.getRecoveryPolicy());
+ defaultParseOptions.setErrorListener(listener);
+ }
+ return listener;
+ }
+
+ /**
+ * Set the ErrorListener to be used in this configuration. The ErrorListener
+ * is informed of all static and dynamic errors detected, and can decide whether
+ * run-time warnings are to be treated as fatal.
+ *
+ * @param listener the ErrorListener to be used
+ * @since 8.4
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ defaultParseOptions.setErrorListener(listener);
+ }
+
+
+ /**
+ * Report a fatal error
+ *
+ * @param err the exception to be reported
+ */
+
+ public void reportFatalError(XPathException err) {
+ if (!err.hasBeenReported()) {
+ try {
+ getErrorListener().fatalError(err);
+ } catch (TransformerException e) {
+ //
+ }
+ err.setHasBeenReported(true);
+ }
+ }
+
+ /**
+ * Set the standard error output to be used in all cases where no more specific destination
+ * is defined. This defaults to System.err.
+ *
+ * @param out the stream to be used for error output where no more specific destination
+ * has been supplied
+ * @since 9.3
+ */
+
+ public void setStandardErrorOutput(PrintStream out) {
+ standardErrorOutput = out;
+ }
+
+ /**
+ * Get the standard error output to be used in all cases where no more specific destination
+ * is defined. This defaults to System.err.
+ *
+ * @return the stream to be used for error output where no more specific destination
+ * has been supplied
+ * @since 9.3
+ */
+
+ public /*@NotNull*/ PrintStream getStandardErrorOutput() {
+ if (standardErrorOutput == null) {
+ standardErrorOutput = System.err;
+ }
+ return standardErrorOutput;
+ }
+
+ /**
+ * Set the XML version to be used by default for validating characters and names.
+ * Note that source documents specifying xml version="1.0" or "1.1" are accepted
+ * regardless of this setting. The effect of this switch is to change the validation
+ * rules for types such as Name and NCName, to change the meaning of \i and \c in
+ * regular expressions, and to determine whether the serializer allows XML 1.1 documents
+ * to be constructed.
+ *
+ * @param version one of the constants XML10 or XML11
+ * @since 8.6
+ */
+
+ public void setXMLVersion(int version) {
+ xmlVersion = version;
+ theConversionRules = null;
+ }
+
+ /**
+ * Get the XML version to be used by default for validating characters and names
+ *
+ * @return one of the constants {@link #XML10} or {@link #XML11}
+ * @since 8.6
+ */
+
+ public int getXMLVersion() {
+ return xmlVersion;
+ }
+
+ /**
+ * Get the parsing and document building options defined in this configuration
+ *
+ * @return the parsing and document building options. Note that any changes to this
+ * ParseOptions object will be reflected back in the Configuration; if changes are to be made
+ * locally, the caller should create a copy.
+ * @since 9.2
+ */
+
+ public ParseOptions getParseOptions() {
+ return defaultParseOptions;
+ }
+
+ /**
+ * Get a class that can be used to check names against the selected XML version
+ *
+ * @return a class that can be used for name checking
+ * @since 8.6
+ */
+
+ public NameChecker getNameChecker() {
+ return getConversionRules().getNameChecker();
+ }
+
+ /**
+ * Set the conversion rules to be used to convert between atomic types. By default,
+ * The rules depend on the versions of XML and XSD in use by the configuration.
+ *
+ * @param rules the conversion rules to be used
+ * @since 9.3
+ */
+
+ public void setConversionRules(/*@NotNull*/ ConversionRules rules) {
+ this.theConversionRules = rules;
+ }
+
+ /**
+ * Get the conversion rules used to convert between atomic types. By default, the rules depend on the versions
+ * of XML and XSD in use by the configuration
+ *
+ * @return the appropriate conversion rules
+ * @since 9.3
+ */
+
+ /*@NotNull*/
+ public ConversionRules getConversionRules() {
+ if (theConversionRules == null) {
+ synchronized (this) {
+ ConversionRules cv = new ConversionRules();
+ cv.setNameChecker(
+ xmlVersion == XML10 ?
+ Name10Checker.getInstance() :
+ Name11Checker.getInstance());
+ cv.setStringToDoubleConverter(
+ xsdVersion == XSD10 ?
+ StringToDouble.getInstance() :
+ StringToDouble11.getInstance());
+ cv.setNotationSet(this);
+ if (xsdVersion == XSD10) {
+ cv.setURIChecker(StandardURIChecker.getInstance());
+ // In XSD 1.1, there is no checking
+ }
+ cv.setAllowYearZero(xsdVersion != XSD10);
+ return (theConversionRules = cv);
+ }
+ } else {
+ return theConversionRules;
+ }
+ }
+
+ /**
+ * Get the version of XML Schema to be used
+ *
+ * @return {@link #XSD10} or {@link #XSD11}
+ * @since 9.2
+ */
+
+ public int getXsdVersion() {
+ return xsdVersion;
+ }
+
+ /**
+ * Get an XPathContext object with sufficient capability to perform comparisons and conversions
+ *
+ * @return a dynamic context for performing conversions
+ */
+
+ /*@NotNull*/
+ public XPathContext getConversionContext() {
+ if (theConversionContext == null) {
+ theConversionContext = new EarlyEvaluationContext(this, new CollationMap(this));
+ }
+ return theConversionContext;
+ }
+
+ /**
+ * Get the Tree Model used by this Configuration. This is either
+ * {@link Builder#LINKED_TREE}, {@link Builder#TINY_TREE}, or {@link Builder#TINY_TREE_CONDENSED}.
+ * The default is <code>Builder.TINY_TREE</code>.
+ *
+ * @return the selected Tree Model
+ * @since 8.4 (Condensed tinytree added in 9.2)
+ */
+
+ public int getTreeModel() {
+ return defaultParseOptions.getModel().getSymbolicValue();
+ }
+
+ /**
+ * Set the Tree Model used by this Configuration. This is either
+ * {@link Builder#LINKED_TREE} or {@link Builder#TINY_TREE}, or {@link Builder#TINY_TREE_CONDENSED}.
+ * The default is <code>Builder.TINY_TREE</code>.
+ *
+ * @param treeModel the integer constant representing the selected Tree Model
+ * @since 8.4 (Condensed tinytree added in 9.2)
+ */
+
+ public void setTreeModel(int treeModel) {
+ defaultParseOptions.setModel(TreeModel.getTreeModel(treeModel));
+ }
+
+ /**
+ * Determine whether source documents will maintain line numbers, for the
+ * benefit of the saxon:line-number() extension function as well as run-time
+ * tracing.
+ *
+ * @return true if line numbers are maintained in source documents
+ * @since 8.4
+ */
+
+ public boolean isLineNumbering() {
+ return defaultParseOptions.isLineNumbering();
+ }
+
+ /**
+ * Determine whether source documents will maintain line numbers, for the
+ * benefit of the saxon:line-number() extension function as well as run-time
+ * tracing.
+ *
+ * @param lineNumbering true if line numbers are maintained in source documents
+ * @since 8.4
+ */
+
+ public void setLineNumbering(boolean lineNumbering) {
+ defaultParseOptions.setLineNumbering(lineNumbering);
+ }
+
+ /**
+ * Set whether or not source documents (including stylesheets and schemas) are have
+ * XInclude processing applied to them, or not. Default is false.
+ *
+ * @param state true if XInclude elements are to be expanded, false if not
+ * @since 8.9
+ */
+
+ public void setXIncludeAware(boolean state) {
+ defaultParseOptions.setXIncludeAware(state);
+ }
+
+ /**
+ * Test whether or not source documents (including stylesheets and schemas) are to have
+ * XInclude processing applied to them, or not
+ *
+ * @return true if XInclude elements are to be expanded, false if not
+ * @since 8.9
+ */
+
+ public boolean isXIncludeAware() {
+ return defaultParseOptions.isXIncludeAware();
+ }
+
+ /**
+ * Get the TraceListener used for run-time tracing of instruction execution.
+ *
+ * @return the TraceListener that was set using {@link #setTraceListener} if set.
+ * Otherwise, returns null.
+ * @since 8.4. Modified in 9.1.
+ */
+
+ /*@Nullable*/
+ public TraceListener getTraceListener() {
+ return traceListener;
+ }
+
+
+ /**
+ * Get or create the TraceListener used for run-time tracing of instruction execution.
+ *
+ * @return If a TraceListener has been set using {@link #setTraceListener(net.sf.saxon.lib.TraceListener)},
+ * returns that TraceListener. Otherwise, if a TraceListener class has been set using
+ * {@link #setTraceListenerClass(String)}, returns a newly created instance of that class.
+ * Otherwise, returns null.
+ * @throws XPathException if the supplied TraceListenerClass cannot be instantiated as an instance
+ * of TraceListener
+ * @since 9.1.
+ */
+
+ /*@Nullable*/
+ public TraceListener makeTraceListener() throws XPathException {
+ if (traceListener != null) {
+ return traceListener;
+ } else if (traceListenerClass != null) {
+ try {
+ return makeTraceListener(traceListenerClass);
+ } catch (ClassCastException e) {
+ throw new XPathException(e);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set the TraceListener to be used for run-time tracing of instruction execution.
+ * <p/>
+ * <p>Note: this method should not be used if the Configuration is multithreading. In that situation,
+ * use {@link #setCompileWithTracing(boolean)} to force stylesheets and queries to be compiled
+ * with trace code enabled, and use {@link Controller#addTraceListener(net.sf.saxon.lib.TraceListener)} to
+ * supply a TraceListener at run time.</p>
+ *
+ * @param traceListener The TraceListener to be used. If null is supplied, any existing TraceListener is removed
+ * @since 8.4
+ */
+
+ public void setTraceListener(/*@Nullable*/ TraceListener traceListener) {
+ this.traceListener = traceListener;
+ setCompileWithTracing(traceListener != null);
+ internalSetBooleanProperty(FeatureKeys.ALLOW_MULTITHREADING, false);
+ }
+
+ /**
+ * Set the name of the trace listener class to be used for run-time tracing of instruction
+ * execution. A new instance of this class will be created for each query or transformation
+ * that requires tracing. The class must be an instance of {@link TraceListener}.
+ *
+ * @param className the name of the trace listener class. If null, any existing trace listener is
+ * removed from the configuration.
+ * @throws IllegalArgumentException if the class cannot be instantiated or does not implement
+ * TraceListener
+ * @since 9.1. Changed in 9.4 to allow null to be supplied.
+ */
+
+ public void setTraceListenerClass(/*@Nullable*/ String className) {
+ if (className == null) {
+ traceListenerClass = null;
+ setCompileWithTracing(false);
+ } else {
+ try {
+ makeTraceListener(className);
+ } catch (XPathException err) {
+ throw new IllegalArgumentException(className + ": " + err.getMessage());
+ }
+ this.traceListenerClass = className;
+ setCompileWithTracing(true);
+ }
+ }
+
+ /**
+ * Get the name of the trace listener class to be used for run-time tracing of instruction
+ * execution. A new instance of this class will be created for each query or transformation
+ * that requires tracing. The class must be an instance of {@link net.sf.saxon.lib.TraceListener}.
+ *
+ * @return the name of the trace listener class, or null if no trace listener class
+ * has been nominated.
+ * @since 9.1
+ */
+
+ /*@Nullable*/
+ public String getTraceListenerClass() {
+ return traceListenerClass;
+ }
+
+ /**
+ * Determine whether compile-time generation of trace code was requested
+ *
+ * @return true if compile-time generation of code was requested
+ * @since 8.8
+ */
+
+ public boolean isCompileWithTracing() {
+ return getBooleanProperty(FeatureKeys.COMPILE_WITH_TRACING);
+ }
+
+ /**
+ * Request compile-time generation of trace code (or not)
+ *
+ * @param trace true if compile-time generation of trace code is required
+ * @since 8.8
+ */
+
+ public void setCompileWithTracing(boolean trace) {
+ internalSetBooleanProperty(FeatureKeys.COMPILE_WITH_TRACING, trace);
+ if (defaultXsltCompilerInfo != null) {
+ if (trace) {
+ defaultXsltCompilerInfo.setCodeInjector(new XSLTTraceCodeInjector());
+ } else {
+ defaultXsltCompilerInfo.setCodeInjector(null);
+ }
+ }
+ if (defaultStaticQueryContext != null) {
+ if (trace) {
+ defaultStaticQueryContext.setCodeInjector(new TraceCodeInjector());
+ } else {
+ defaultStaticQueryContext.setCodeInjector(null);
+ }
+ }
+ }
+
+ /**
+ * Create an instance of a TraceListener with a specified class name
+ *
+ * @param className The fully qualified class name of the TraceListener to
+ * be constructed
+ * @return the newly constructed TraceListener
+ * @throws net.sf.saxon.trans.XPathException
+ * if the requested class does not
+ * implement the net.sf.saxon.trace.TraceListener interface
+ */
+
+ public TraceListener makeTraceListener(String className)
+ throws XPathException {
+ Object obj = dynamicLoader.getInstance(className, null);
+ if (obj instanceof TraceListener) {
+ return (TraceListener) obj;
+ }
+ throw new XPathException("Class " + className + " is not a TraceListener");
+ }
+
+ /**
+ * Register an extension function that is to be made available within any stylesheet, query,
+ * or XPath expression compiled under the control of this processor. This method
+ * registers an extension function implemented as an instance of
+ * {@link net.sf.saxon.lib.ExtensionFunctionDefinition}, using an arbitrary name and namespace.
+ * This supplements the ability to call arbitrary Java methods using a namespace and local name
+ * that are related to the Java class and method name.
+ *
+ * @param function the object that implements the extension function.
+ * @since 9.2
+ */
+
+ public void registerExtensionFunction(ExtensionFunctionDefinition function) {
+ integratedFunctionLibrary.registerFunction(function);
+ }
+
+ /**
+ * Get the IntegratedFunction library containing integrated extension functions
+ *
+ * @return the IntegratedFunctionLibrary
+ * @since 9.2
+ */
+
+ /*@NotNull*/
+ public IntegratedFunctionLibrary getIntegratedFunctionLibrary() {
+ return integratedFunctionLibrary;
+ }
+
+
+ /**
+ * Get the FunctionLibrary used to bind calls on Saxon-defined extension functions.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return the FunctionLibrary used for extension functions in the Saxon library.
+ */
+
+ public VendorFunctionLibrary getVendorFunctionLibrary() {
+ if (vendorFunctionLibrary == null) {
+ vendorFunctionLibrary = new VendorFunctionLibrary();
+ }
+ return vendorFunctionLibrary;
+ }
+
+ /**
+ * Add the registered extension binders to a function library.
+ * This method is intended primarily for internal use
+ *
+ * @param list the function library list
+ */
+
+ public void addExtensionBinders(FunctionLibraryList list) {
+ // no action in this class
+ }
+
+ /**
+ * Make a UserFunction object.
+ * This method is for internal use.
+ *
+ * @param memoFunction true if the function is to be a memo function, This option is ignored
+ * in Saxon-HE.
+ * @return a new UserFunction object
+ */
+
+ public UserFunction newUserFunction(boolean memoFunction) {
+ return new UserFunction();
+ }
+
+ /**
+ * Set a CollationURIResolver to be used to resolve collation URIs (that is,
+ * to take a URI identifying a collation, and return the corresponding collation).
+ * Note that Saxon attempts first to resolve a collation URI using the resolver
+ * registered with the Controller; if that returns null, it tries again using the
+ * resolver registered with the Configuration.
+ * <p/>
+ * Note that it is undefined whether collation URIs are resolved at compile time
+ * or at run-time. It is therefore inadvisable to change the CollationURIResolver after
+ * compiling a query or stylesheet and before running it.
+ *
+ * @param resolver the collation URI resolver to be used. This replaces any collation
+ * URI resolver previously registered.
+ * @since 8.5
+ */
+
+ public void setCollationURIResolver(CollationURIResolver resolver) {
+ collationResolver = resolver;
+ }
+
+ /**
+ * Get the collation URI resolver associated with this configuration. This will
+ * return the CollationURIResolver previously set using the {@link #setCollationURIResolver}
+ * method; if this has not been called, it returns the system-defined collation URI resolver
+ *
+ * @return the registered CollationURIResolver
+ * @since 8.5
+ */
+
+ public CollationURIResolver getCollationURIResolver() {
+ return collationResolver;
+ }
+
+ /**
+ * Get the collation map, which can be used to register named collations (and a default collation)
+ * at the Configuration level. Any collations registered in this collation map apply to all
+ * queries, stylesheets, and Xpath expressions compiled under this collation. The effective
+ * contents of the collation map are the contents at the time the query or stylesheet is compiled.
+ *
+ * @return the collation map
+ */
+
+ public CollationMap getCollationMap() {
+ return collationMap;
+ }
+
+ /**
+ * Set the default collection.
+ * <p/>
+ * <p>If no default collection URI is specified, then a request for the default collection
+ * is handled by calling the registered collection URI resolver with an argument of null.</p>
+ *
+ * @param uri the URI of the default collection. Calling the collection() function
+ * with no arguments is equivalent to calling collection() with this URI as an argument.
+ * The URI will be dereferenced by passing it to the registered CollectionURIResolver.
+ * If null is supplied, any existing default collection is removed.
+ * @since 9.2
+ */
+
+ public void setDefaultCollection(/*@Nullable*/ String uri) {
+ defaultCollection = uri;
+ }
+
+ /**
+ * Get the URI of the default collection. Returns null if no default collection URI has
+ * been registered.
+ *
+ * @return the default collection URI. This is dereferenced in the same way as a normal
+ * collection URI (via the CollectionURIResolver) to return a sequence of nodes
+ * @since 9.2
+ */
+
+ /*@Nullable*/
+ public String getDefaultCollection() {
+ return defaultCollection;
+ }
+
+ /**
+ * Set a CollectionURIResolver to be used to resolve collection URIs (that is,
+ * the URI supplied in a call to the collection() function).
+ * <p/>
+ * Collection URIs are always resolved at run-time, using the CollectionURIResolver
+ * in force at the time the collection() function is called.
+ *
+ * @param resolver the collection URI resolver to be used. This replaces any collection
+ * URI resolver previously registered. The value must not be null.
+ * @since 8.5
+ */
+
+ public void setCollectionURIResolver(/*@NotNull*/ CollectionURIResolver resolver) {
+ collectionResolver = resolver;
+ }
+
+ /**
+ * Get the collection URI resolver associated with this configuration. This will
+ * return the CollectionURIResolver previously set using the {@link #setCollectionURIResolver}
+ * method; if this has not been called, it returns the system-defined collection URI resolver
+ *
+ * @return the registered CollectionURIResolver
+ * @since 8.5
+ */
+
+ /*@NotNull*/
+ public CollectionURIResolver getCollectionURIResolver() {
+ return collectionResolver;
+ }
+
+ /**
+ * Set the localizer factory to be used
+ *
+ * @param factory the LocalizerFactory
+ * @since 9.2
+ */
+
+ public void setLocalizerFactory(LocalizerFactory factory) {
+ this.localizerFactory = factory;
+ }
+
+ /**
+ * Get the localizer factory in use
+ *
+ * @return the LocalizerFactory, if any. If none has been set, returns null.
+ * @since 9.2
+ */
+
+ public LocalizerFactory getLocalizerFactory() {
+ return localizerFactory;
+ }
+
+
+ /**
+ * Set the default language to be used for number and date formatting when no language is specified.
+ * If none is set explicitly, the default Locale for the Java Virtual Machine is used.
+ *
+ * @param language the default language to be used, as an ISO code for example "en" or "fr-CA"
+ * @since 9.2
+ */
+
+ public void setDefaultLanguage(String language) {
+ defaultLanguage = language;
+ }
+
+ /**
+ * Get the default language. Unless an explicit default is set, this will be the language
+ * of the default Locale for the Java Virtual Machine
+ *
+ * @return the default language
+ * @since 9.2
+ */
+
+ public String getDefaultLanguage() {
+ return defaultLanguage;
+ }
+
+ /**
+ * Set the default country to be used for number and date formatting when no country is specified.
+ * If none is set explicitly, the default Locale for the Java Virtual Machine is used.
+ *
+ * @param country the default country to be used, as an ISO code for example "US" or "GB"
+ * @since 9.2
+ */
+
+ public void setDefaultCountry(String country) {
+ defaultCountry = country;
+ }
+
+ /**
+ * Get the default country to be used for number and date formatting when no country is specified.
+ * If none is set explicitly, the default Locale for the Java Virtual Machine is used.
+ *
+ * @return the default country to be used, as an ISO code for example "US" or "GB"
+ * @since 9.2
+ */
+
+ public String getDefaultCountry() {
+ return defaultCountry;
+ }
+
+
+ /**
+ * Load a Numberer class for a given language and check it is OK.
+ * This method is provided primarily for internal use.
+ *
+ * @param language the language for which a Numberer is required. May be null,
+ * indicating default language
+ * @param country the country for which a Numberer is required. May be null,
+ * indicating default country
+ * @return a suitable numberer. If no specific numberer is available
+ * for the language, the default numberer (normally English) is used.
+ */
+
+ public Numberer makeNumberer(/*@Nullable*/ String language, /*@Nullable*/ String country) {
+ if (localizerFactory == null) {
+ return new Numberer_en();
+ } else {
+ Numberer numberer = localizerFactory.getNumberer(language, country);
+ if (numberer == null) {
+ numberer = new Numberer_en();
+ }
+ return numberer;
+ }
+ }
+
+
+ /**
+ * Set a user-defined ModuleURIResolver for resolving URIs used in "import module"
+ * declarations in an XQuery prolog.
+ * This acts as the default value for the ModuleURIResolver in the StaticQueryContext, and may be
+ * overridden by a more specific ModuleURIResolver nominated as part of the StaticQueryContext.
+ *
+ * @param resolver the URI resolver for XQuery modules. May be null, in which case any existing
+ * Module URI Resolver is removed from the configuration
+ */
+
+ public void setModuleURIResolver(/*@Nullable*/ ModuleURIResolver resolver) {
+ moduleURIResolver = resolver;
+ }
+
+ /**
+ * Create and register an instance of a ModuleURIResolver with a specified class name.
+ * This will be used for resolving URIs in XQuery "import module" declarations, unless
+ * a more specific ModuleURIResolver has been nominated as part of the StaticQueryContext.
+ *
+ * @param className The fully-qualified name of the LocationHintResolver class
+ * @throws TransformerException if the requested class does not
+ * implement the net.sf.saxon.LocationHintResolver interface
+ */
+ public void setModuleURIResolver(String className) throws TransformerException {
+ Object obj = dynamicLoader.getInstance(className, null);
+ if (obj instanceof ModuleURIResolver) {
+ setModuleURIResolver((ModuleURIResolver) obj);
+ } else {
+ throw new XPathException("Class " + className + " is not a ModuleURIResolver");
+ }
+ }
+
+ /**
+ * Get the user-defined ModuleURIResolver for resolving URIs used in "import module"
+ * declarations in the XQuery prolog; returns null if none has been explicitly set.
+ *
+ * @return the resolver for Module URIs
+ */
+
+ /*@Nullable*/
+ public ModuleURIResolver getModuleURIResolver() {
+ return moduleURIResolver;
+ }
+
+ /**
+ * Get the standard system-defined ModuleURIResolver for resolving URIs used in "import module"
+ * declarations in the XQuery prolog.
+ *
+ * @return the standard system-defined ModuleURIResolver for resolving URIs
+ */
+
+ public ModuleURIResolver getStandardModuleURIResolver() {
+ return standardModuleURIResolver;
+ }
+
+ /**
+ * Set a user-defined SchemaURIResolver for resolving URIs used in "import schema"
+ * declarations.
+ *
+ * @param resolver the URI resolver used for import schema declarations. May be null,
+ * in which case any existing URI resolver is removed from the Configuration.
+ */
+
+ public void setSchemaURIResolver(/*@Nullable*/ SchemaURIResolver resolver) {
+ schemaURIResolver = resolver;
+ }
+
+ /**
+ * Get the user-defined SchemaURIResolver for resolving URIs used in "import schema"
+ * declarations; if none has been explicitly set, returns null.
+ *
+ * @return the user-defined SchemaURIResolver for resolving URIs
+ */
+
+ /*@Nullable*/
+ public SchemaURIResolver getSchemaURIResolver() {
+ return schemaURIResolver;
+ }
+
+ /**
+ * Get the default options for XSLT compilation
+ *
+ * @return the default options for XSLT compilation. The CompilerInfo object will reflect any options
+ * set using other methods available for this Configuration object
+ */
+
+ public CompilerInfo getDefaultXsltCompilerInfo() {
+ return defaultXsltCompilerInfo;
+ }
+
+ /**
+ * Get the default options for XQuery compilation
+ * @return the default XQuery static context for this configuration
+ */
+
+ public StaticQueryContext getDefaultStaticQueryContext() {
+ if (defaultStaticQueryContext == null) {
+ defaultStaticQueryContext = new StaticQueryContext(this, true);
+ }
+ return defaultStaticQueryContext;
+ }
+
+ /**
+ * Determine how recoverable run-time errors are to be handled. This applies
+ * only if the standard ErrorListener is used.
+ *
+ * @return the current recovery policy. The options are {@link #RECOVER_SILENTLY},
+ * {@link #RECOVER_WITH_WARNINGS}, or {@link #DO_NOT_RECOVER}.
+ * @since 8.4
+ */
+
+ public int getRecoveryPolicy() {
+ return defaultXsltCompilerInfo.getRecoveryPolicy();
+ }
+
+ /**
+ * Determine how recoverable run-time errors are to be handled. This applies
+ * only if the standard ErrorListener is used. The recovery policy applies to
+ * errors classified in the XSLT 2.0 specification as recoverable dynamic errors,
+ * but only in those cases where Saxon provides a choice over how the error is handled:
+ * in some cases, Saxon makes the decision itself.
+ *
+ * @param recoveryPolicy the recovery policy to be used. The options are {@link #RECOVER_SILENTLY},
+ * {@link #RECOVER_WITH_WARNINGS}, or {@link #DO_NOT_RECOVER}.
+ * @since 8.4
+ */
+
+ public void setRecoveryPolicy(int recoveryPolicy) {
+ defaultXsltCompilerInfo.setRecoveryPolicy(recoveryPolicy);
+ }
+
+ /**
+ * Get the name of the class that will be instantiated to create a MessageEmitter,
+ * to process the output of xsl:message instructions in XSLT.
+ *
+ * @return the full class name of the message emitter class.
+ * @since 8.4
+ */
+
+ public String getMessageEmitterClass() {
+ return defaultXsltCompilerInfo.getMessageReceiverClassName();
+ }
+
+ /**
+ * Set the name of the class that will be instantiated to
+ * to process the output of xsl:message instructions in XSLT.
+ *
+ * @param messageReceiverClassName the full class name of the message receiver. This
+ * must implement net.sf.saxon.event.Receiver.
+ * @since 8.4
+ */
+
+ public void setMessageEmitterClass(String messageReceiverClassName) {
+ defaultXsltCompilerInfo.setMessageReceiverClassName(messageReceiverClassName);
+ }
+
+ /**
+ * Get the name of the class that will be instantiated to create an XML parser
+ * for parsing source documents (for example, documents loaded using the document()
+ * or doc() functions).
+ * <p/>
+ * This method is retained in Saxon for backwards compatibility, but the preferred way
+ * of choosing an XML parser is to use JAXP interfaces, for example by supplying a
+ * JAXP Source object initialized with an appropriate implementation of org.xml.sax.XMLReader.
+ *
+ * @return the fully qualified name of the XML parser class
+ */
+
+ public String getSourceParserClass() {
+ return sourceParserClass;
+ }
+
+ /**
+ * Set the name of the class that will be instantiated to create an XML parser
+ * for parsing source documents (for example, documents loaded using the document()
+ * or doc() functions).
+ * <p/>
+ * This method is retained in Saxon for backwards compatibility, but the preferred way
+ * of choosing an XML parser is to use JAXP interfaces, for example by supplying a
+ * JAXP Source object initialized with an appropriate implementation of org.xml.sax.XMLReader.
+ *
+ * @param sourceParserClass the fully qualified name of the XML parser class. This must implement
+ * the SAX2 XMLReader interface.
+ */
+
+ public void setSourceParserClass(String sourceParserClass) {
+ this.sourceParserClass = sourceParserClass;
+ }
+
+ /**
+ * Get the name of the class that will be instantiated to create an XML parser
+ * for parsing stylesheet modules.
+ * <p/>
+ * This method is retained in Saxon for backwards compatibility, but the preferred way
+ * of choosing an XML parser is to use JAXP interfaces, for example by supplying a
+ * JAXP Source object initialized with an appropriate implementation of org.xml.sax.XMLReader.
+ *
+ * @return the fully qualified name of the XML parser class
+ */
+
+ public String getStyleParserClass() {
+ return styleParserClass;
+ }
+
+ /**
+ * Set the name of the class that will be instantiated to create an XML parser
+ * for parsing stylesheet modules.
+ * <p/>
+ * This method is retained in Saxon for backwards compatibility, but the preferred way
+ * of choosing an XML parser is to use JAXP interfaces, for example by supplying a
+ * JAXP Source object initialized with an appropriate implementation of org.xml.sax.XMLReader.
+ *
+ * @param parser the fully qualified name of the XML parser class
+ */
+
+ public void setStyleParserClass(String parser) {
+ this.styleParserClass = parser;
+ }
+
+ /**
+ * Get the OutputURIResolver that will be used to resolve URIs used in the
+ * href attribute of the xsl:result-document instruction.
+ *
+ * @return the OutputURIResolver. If none has been supplied explicitly, the
+ * default OutputURIResolver is returned.
+ * @since 8.4
+ */
+
+ public OutputURIResolver getOutputURIResolver() {
+ return defaultXsltCompilerInfo.getOutputURIResolver();
+ }
+
+ /**
+ * Set the OutputURIResolver that will be used to resolve URIs used in the
+ * href attribute of the xsl:result-document instruction.
+ *
+ * @param outputURIResolver the OutputURIResolver to be used.
+ * @since 8.4
+ */
+
+ public void setOutputURIResolver(OutputURIResolver outputURIResolver) {
+ defaultXsltCompilerInfo.setOutputURIResolver(outputURIResolver);
+ }
+
+ /**
+ * Set a custom SerializerFactory. This will be used to create a serializer for a given
+ * set of output properties and result destination.
+ *
+ * @param factory a custom SerializerFactory
+ * @since 8.8
+ */
+
+ public void setSerializerFactory(SerializerFactory factory) {
+ serializerFactory = factory;
+ }
+
+ /**
+ * Get the SerializerFactory. This returns the standard built-in SerializerFactory, unless
+ * a custom SerializerFactory has been registered.
+ *
+ * @return the SerializerFactory in use
+ * @since 8.8
+ */
+
+ public SerializerFactory getSerializerFactory() {
+ return serializerFactory;
+ }
+
+ /**
+ * Get the CharacterSetFactory. Note: at present this cannot be changed.
+ *
+ * @return the CharacterSetFactory in use.
+ * @since 9.2
+ */
+
+ public CharacterSetFactory getCharacterSetFactory() {
+ if (characterSetFactory == null) {
+ characterSetFactory = new CharacterSetFactory();
+ }
+ return characterSetFactory;
+ }
+
+ /**
+ * Set the default serialization properties
+ *
+ * @param props the default properties
+ */
+
+ public void setDefaultSerializationProperties(Properties props) {
+ defaultSerializationProperties = props;
+ }
+
+ /**
+ * Get the default serialization properties
+ *
+ * @return the default properties
+ */
+
+ public Properties getDefaultSerializationProperties() {
+ return defaultSerializationProperties;
+ }
+
+ /**
+ * Process an xsl:result-document instruction. The Saxon-HE version of this method simply executes the instruction.
+ * The Saxon-EE version starts a new thread, and executes the instruction in that thread.
+ * @param instruction the instruction to be executed
+ * @param content the expression that defines the content of the new result document
+ * @param context the evaluation context
+ * @throws XPathException if any dynamic error occurs
+ */
+
+ public void processResultDocument(ResultDocument instruction, Expression content, XPathContext context) throws XPathException {
+ instruction.processInstruction(content, context);
+ }
+
+ /**
+ * Determine whether brief progress messages and timing information will be output
+ * to System.err.
+ * <p/>
+ * This method is provided largely for internal use. Progress messages are normally
+ * controlled directly from the command line interfaces, and are not normally used when
+ * driving Saxon from the Java API.
+ *
+ * @return true if these messages are to be output.
+ */
+
+ public boolean isTiming() {
+ return enabledProperties.contains(FeatureKeys.TIMING);
+ }
+
+ /**
+ * Determine whether brief progress messages and timing information will be output
+ * to System.err.
+ * <p/>
+ * This method is provided largely for internal use. Progress messages are normally
+ * controlled directly from the command line interfaces, and are not normally used when
+ *
+ * @param timing true if these messages are to be output.
+ */
+
+ public void setTiming(boolean timing) {
+ if (timing) {
+ enabledProperties.add(FeatureKeys.TIMING);
+ } else {
+ enabledProperties.remove(FeatureKeys.TIMING);
+ }
+ }
+
+ /**
+ * Determine whether a warning is to be output when running against a stylesheet labelled
+ * as version="1.0". The XSLT specification requires such a warning unless the user disables it.
+ *
+ * @return true if these messages are to be output.
+ * @since 8.4
+ */
+
+ public boolean isVersionWarning() {
+ return defaultXsltCompilerInfo.isVersionWarning();
+ }
+
+ /**
+ * Determine whether a warning is to be output when the version attribute of the stylesheet does
+ * not match the XSLT processor version. (In the case where the stylesheet version is "1.0",
+ * the XSLT specification requires such a warning unless the user disables it.)
+ *
+ * @param warn true if these warning messages are to be output.
+ * @since 8.4
+ */
+
+ public void setVersionWarning(boolean warn) {
+ defaultXsltCompilerInfo.setVersionWarning(warn);
+ }
+
+ /**
+ * Ask whether streamability extensions are allowed. Saxon allows some constructs
+ * to be streamed that are not guaranteed-streamable under the W3C streamability
+ * rules, provided that this option is enabled.
+ * @return true if streamability extensions are enabled
+ * @see {@link FeatureKeys#ALLOW_STREAMABILITY_EXTENSIONS}
+ */
+
+ public boolean isAllowStreamabilityExtensions() {
+ return enabledProperties.contains(FeatureKeys.ALLOW_STREAMABILITY_EXTENSIONS);
+ }
+
+ /**
+ * Determine whether the XML parser for source documents will be asked to perform
+ * validation of source documents
+ *
+ * @return true if DTD validation is requested.
+ * @since 8.4
+ */
+
+ public boolean isValidation() {
+ return defaultParseOptions.getDTDValidationMode() == Validation.STRICT ||
+ defaultParseOptions.getDTDValidationMode() == Validation.LAX;
+ }
+
+ /**
+ * Determine whether the XML parser for source documents will be asked to perform
+ * DTD validation of source documents
+ *
+ * @param validation true if DTD validation is to be requested.
+ * @since 8.4
+ */
+
+ public void setValidation(boolean validation) {
+ defaultParseOptions.setDTDValidationMode(validation ? Validation.STRICT : Validation.STRIP);
+ }
+
+ /**
+ * Create a document projector for a given path map. Document projection is available only
+ * in Saxon-EE, so the Saxon-B version of this method throws an exception
+ *
+ * @param map the path map used to control document projection
+ * @return a push filter that implements document projection
+ * @throws UnsupportedOperationException if this is not a schema-aware configuration, or
+ * if no Saxon-EE license is available
+ */
+
+ public FilterFactory makeDocumentProjector(PathMap.PathMapRoot map) {
+ throw new UnsupportedOperationException("Document projection requires Saxon-EE");
+ }
+
+ /**
+ * Ask whether source documents (supplied as a StreamSource or SAXSource)
+ * should be subjected to schema validation, and if so, in what validation mode
+ *
+ * @return the schema validation mode previously set using setSchemaValidationMode(),
+ * or the default mode {@link Validation#STRIP} otherwise.
+ */
+
+ public int getSchemaValidationMode() {
+ return defaultParseOptions.getSchemaValidationMode();
+ }
+
+ /**
+ * Say whether source documents (supplied as a StreamSource or SAXSource)
+ * should be subjected to schema validation, and if so, in what validation mode.
+ * This value may be overridden at the level of a Controller for an individual transformation or query.
+ *
+ * @param validationMode the validation (or construction) mode to be used for source documents.
+ * One of {@link Validation#STRIP}, {@link Validation#PRESERVE}, {@link Validation#STRICT},
+ * {@link Validation#LAX}
+ * @since 8.4
+ */
+
+ public void setSchemaValidationMode(int validationMode) {
+ switch (validationMode) {
+ case Validation.STRIP:
+ case Validation.PRESERVE:
+ break;
+ case Validation.LAX:
+ if (!isLicensedFeature(LicenseFeature.SCHEMA_VALIDATION)) {
+ // if schema processing isn't supported, then there's never a schema, so lax validation is a no-op.
+ validationMode = Validation.STRIP;
+ }
+ break;
+ case Validation.STRICT:
+ checkLicensedFeature(LicenseFeature.SCHEMA_VALIDATION, "strict validation");
+ break;
+ default:
+ throw new IllegalArgumentException("Unsupported validation mode " + validationMode);
+ }
+ defaultParseOptions.setSchemaValidationMode(validationMode);
+ }
+
+ /**
+ * Indicate whether schema validation failures on result documents are to be treated
+ * as fatal errors or as warnings. Note that all validation
+ * errors are reported to the error() method of the ErrorListener, and processing always
+ * continues except when that method chooses to throw an exception. At the end of the document,
+ * a fatal error is thrown if (a) there have been any validation errors, and (b) this option
+ * is not set.
+ *
+ * @param warn true if schema validation failures are to be treated as warnings; false if they
+ * are to be treated as fatal errors.
+ * @since 8.4
+ */
+
+ public void setValidationWarnings(boolean warn) {
+ defaultParseOptions.setContinueAfterValidationErrors(warn);
+ }
+
+ /**
+ * Determine whether schema validation failures on result documents are to be treated
+ * as fatal errors or as warnings. Note that all validation
+ * errors are reported to the error() method of the ErrorListener, and processing always
+ * continues except when that method chooses to throw an exception. At the end of the document,
+ * a fatal error is thrown if (a) there have been any validation errors, and (b) this option
+ * is not set.
+ *
+ * @return true if validation errors are to be treated as warnings (that is, the
+ * validation failure is reported but processing continues as normal); false
+ * if validation errors are fatal.
+ * @since 8.4
+ */
+
+ public boolean isValidationWarnings() {
+ return defaultParseOptions.isContinueAfterValidationErrors();
+ }
+
+ /**
+ * Indicate whether attributes that have a fixed or default value are to be expanded when
+ * generating a final result tree. By default (and for conformance with the W3C specifications)
+ * it is required that fixed and default values should be expanded. However, there are use cases
+ * for example when generating XHTML when this serves no useful purpose and merely bloats the output.
+ * <p/>
+ * <p>This option can be overridden at the level of a PipelineConfiguration</p>
+ *
+ * @param expand true if fixed and default values are to be expanded as required by the W3C
+ * specifications; false if this action is to be disabled. Note that this only affects the validation
+ * of final result trees; it is not possible to suppress expansion of fixed or default values on input
+ * documents, as this would make the type annotations on input nodes unsound.
+ * @since 9.0
+ */
+
+ public void setExpandAttributeDefaults(boolean expand) {
+ defaultParseOptions.setExpandAttributeDefaults(expand);
+ }
+
+ /**
+ * Determine whether elements and attributes that have a fixed or default value are to be expanded.
+ * This option applies both to DTD-defined attribute defaults and to schema-defined defaults for
+ * elements and attributes. If an XML parser is used that does not report whether defaults have
+ * been used, this option is ignored.
+ * <p/>
+ * * <p>This option can be overridden at the level of a PipelineConfiguration</p>
+ *
+ * @return true if elements and attributes that have a fixed or default value are to be expanded,
+ * false if defaults are not to be expanded. The default value is true. Note that the setting "false"
+ * is potentially non-conformant with the W3C specifications.
+ * @since 9.0
+ */
+
+ public boolean isExpandAttributeDefaults() {
+ return defaultParseOptions.isExpandAttributeDefaults();
+ }
+
+
+ /**
+ * Get the target namepool to be used for stylesheets/queries and for source documents.
+ *
+ * @return the target name pool. If no NamePoo has been specified explicitly, the
+ * default NamePool is returned.
+ * @since 8.4
+ */
+
+ public NamePool getNamePool() {
+ return namePool;
+ }
+
+ /**
+ * Set the NamePool to be used for stylesheets/queries and for source documents.
+ * <p/>
+ * <p> Using this method allows several Configurations to share the same NamePool. This
+ * was the normal default arrangement until Saxon 8.9, which changed the default so
+ * that each Configuration uses its own NamePool.</p>
+ * <p/>
+ * <p>Sharing a NamePool creates a potential bottleneck, since changes to the namepool are
+ * synchronized.</p>
+ *
+ * @param targetNamePool The NamePool to be used.
+ * @since 8.4
+ */
+
+ public void setNamePool(NamePool targetNamePool) {
+ namePool = targetNamePool;
+ }
+
+ /**
+ * Get the TypeHierarchy: a cache holding type information
+ *
+ * @return the type hierarchy cache
+ */
+
+ public TypeHierarchy getTypeHierarchy() {
+ if (typeHierarchy == null) {
+ typeHierarchy = new TypeHierarchy(this);
+ }
+ return typeHierarchy;
+ }
+
+ /**
+ * Get the document number allocator.
+ * <p/>
+ * The document number allocator is used to allocate a unique number to each document built under this
+ * configuration. The document number forms the basis of all tests for node identity; it is therefore essential
+ * that when two documents are accessed in the same XPath expression, they have distinct document numbers.
+ * Normally this is ensured by building them under the same Configuration. Using this method together with
+ * {@link #setDocumentNumberAllocator}, however, it is possible to have two different Configurations that share
+ * a single DocumentNumberAllocator
+ *
+ * @return the current DocumentNumberAllocator
+ * @since 9.0
+ */
+
+ public DocumentNumberAllocator getDocumentNumberAllocator() {
+ return documentNumberAllocator;
+ }
+
+ /**
+ * Set the document number allocator.
+ * <p/>
+ * The document number allocator is used to allocate a unique number to each document built under this
+ * configuration. The document number forms the basis of all tests for node identity; it is therefore essential
+ * that when two documents are accessed in the same XPath expression, they have distinct document numbers.
+ * Normally this is ensured by building them under the same Configuration. Using this method together with
+ * {@link #getDocumentNumberAllocator}, however, it is possible to have two different Configurations that share
+ * a single DocumentNumberAllocator</p>
+ * <p>This method is for advanced applications only. Misuse of the method can cause problems with node identity.
+ * The method should not be used except while initializing a Configuration, and it should be used only to
+ * arrange for two different configurations to share the same DocumentNumberAllocators. In this case they
+ * should also share the same NamePool.
+ *
+ * @param allocator the DocumentNumberAllocator to be used
+ * @since 9.0
+ */
+
+ public void setDocumentNumberAllocator(DocumentNumberAllocator allocator) {
+ documentNumberAllocator = allocator;
+ }
+
+ /**
+ * Determine whether two Configurations are compatible. When queries, transformations, and path expressions
+ * are run, all the Configurations used to build the documents and to compile the queries and stylesheets
+ * must be compatible. Two Configurations are compatible if they share the same NamePool and the same
+ * DocumentNumberAllocator.
+ *
+ * @param other the other Configuration to be compared with this one
+ * @return true if the two configurations are compatible
+ */
+
+ public boolean isCompatible(Configuration other) {
+ return namePool == other.namePool && documentNumberAllocator == other.documentNumberAllocator;
+ }
+
+ /**
+ * Get the global document pool. This is used for documents preloaded during query or stylesheet
+ * compilation. The user application can preload documents into the global pool, where they will be found
+ * if any query or stylesheet requests the specified document using the doc() or document() function.
+ *
+ * @return the global document pool
+ * @since 9.1
+ */
+
+ public DocumentPool getGlobalDocumentPool() {
+ return globalDocumentPool;
+ }
+
+ /**
+ * Determine whether whitespace-only text nodes are to be stripped unconditionally
+ * from source documents.
+ *
+ * @return true if all whitespace-only text nodes are stripped.
+ * @since 8.4
+ */
+
+ public boolean isStripsAllWhiteSpace() {
+ return defaultParseOptions.getStripSpace() == Whitespace.ALL;
+ }
+
+ /**
+ * Determine whether whitespace-only text nodes are to be stripped unconditionally
+ * from source documents.
+ *
+ * @param stripsAllWhiteSpace if all whitespace-only text nodes are to be stripped.
+ * @since 8.4
+ */
+
+ public void setStripsAllWhiteSpace(boolean stripsAllWhiteSpace) {
+ if (stripsAllWhiteSpace) {
+ defaultParseOptions.setStripSpace(Whitespace.ALL);
+ }
+ }
+
+ /**
+ * Set which kinds of whitespace-only text node should be stripped.
+ *
+ * @param kind the kind of whitespace-only text node that should be stripped when building
+ * a source tree. One of {@link Whitespace#NONE} (none), {@link Whitespace#ALL} (all),
+ * or {@link Whitespace#IGNORABLE} (element-content whitespace as defined in a DTD or schema)
+ */
+
+ public void setStripsWhiteSpace(int kind) {
+ defaultParseOptions.setStripSpace(kind);
+ }
+
+ /**
+ * Set which kinds of whitespace-only text node should be stripped.
+ *
+ * @return kind the kind of whitespace-only text node that should be stripped when building
+ * a source tree. One of {@link net.sf.saxon.value.Whitespace#NONE} (none), {@link Whitespace#ALL} (all),
+ * or {@link Whitespace#IGNORABLE} (element-content whitespace as defined in a DTD or schema)
+ */
+
+ public int getStripsWhiteSpace() {
+ return defaultParseOptions.getStripSpace();
+ }
+
+
+ /**
+ * Get a parser for source documents. The parser is allocated from a pool if any are available
+ * from the pool: the client should ideally return the parser to the pool after use, so that it
+ * can be reused.
+ * <p/>
+ * This method is intended primarily for internal use.
+ *
+ * @return a parser, in which the namespace properties must be set as follows:
+ * namespaces=true; namespace-prefixes=false. The DTD validation feature of the parser will be set
+ * on or off depending on the {@link #setValidation(boolean)} setting.
+ * @throws javax.xml.transform.TransformerFactoryConfigurationError if a failure occurs
+ * configuring the parser for use.
+ */
+
+ public XMLReader getSourceParser() throws TransformerFactoryConfigurationError {
+ if (sourceParserPool == null) {
+ sourceParserPool = new ConcurrentLinkedQueue<XMLReader>();
+ }
+ XMLReader parser = sourceParserPool.poll();
+ if (parser != null) {
+ return parser;
+ }
+
+ if (getSourceParserClass() != null) {
+ parser = makeParser(getSourceParserClass());
+ } else {
+ parser = loadParser();
+ }
+ if (isTiming()) {
+ reportParserDetails(parser);
+ }
+ try {
+ Sender.configureParser(parser);
+ } catch (XPathException err) {
+ throw new TransformerFactoryConfigurationError(err);
+ }
+ if (isValidation()) {
+ try {
+ parser.setFeature("http://xml.org/sax/features/validation", true);
+ } catch (SAXException err) {
+ throw new TransformerFactoryConfigurationError("The XML parser does not support validation");
+ }
+ }
+
+ return parser;
+ }
+
+ /**
+ * Report the parser details to the standard error output
+ *
+ * @param reader the parser
+ */
+
+ private void reportParserDetails(XMLReader reader) {
+ String name = reader.getClass().getName();
+// if (name.equals("com.sun.org.apache.xerces.internal.parsers.SAXParser")) {
+// name += " version " + com.sun.org.apache.xerces.internal.impl.Version.getVersion();
+// }
+ standardErrorOutput.println("Using parser " + name);
+ }
+
+ /**
+ * Return a source parser to the pool, for reuse
+ *
+ * @param parser The parser: the caller must not supply a parser that was obtained by any
+ * mechanism other than calling the getSourceParser() method.
+ * Must not be null.
+ */
+
+ public synchronized void reuseSourceParser(/*@NotNull*/ XMLReader parser) {
+ if (sourceParserPool == null) {
+ sourceParserPool = new ConcurrentLinkedQueue<XMLReader>();
+ }
+ try {
+ try {
+ // give things back to the garbage collecter
+ parser.setContentHandler(null);
+ if(parser.getEntityResolver() == defaultParseOptions.getEntityResolver()) {
+ parser.setEntityResolver(null);
+ }
+ parser.setDTDHandler(null);
+ parser.setErrorHandler(null);
+ // Unfortunately setting the lexical handler to null doesn't work on Xerces, because
+ // it tests "value instanceof LexicalHandler". So we set it to a lexical handler that
+ // holds no references
+ parser.setProperty("http://xml.org/sax/properties/lexical-handler", dummyLexicalHandler);
+ } catch (SAXNotRecognizedException err) {
+ //
+ } catch (SAXNotSupportedException err) {
+ //
+ }
+ sourceParserPool.offer(parser);
+ } catch (Exception e) {
+ // setting the callbacks on an XMLReader to null doesn't always work; some parsers throw a
+ // NullPointerException. If anything goes wrong, the simplest approach is to ignore the error
+ // and not attempt to reuse the parser.
+ }
+ }
+
+ /**
+ * Get a parser by instantiating the SAXParserFactory
+ *
+ * @return the parser (XMLReader)
+ */
+
+ private static XMLReader loadParser() {
+ return platform.loadParser();
+ }
+
+ /**
+ * Get the parser for stylesheet documents. This parser is also used for schema documents.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return an XML parser (a SAX2 parser) that can be used for stylesheets and schema documents
+ * @throws javax.xml.transform.TransformerFactoryConfigurationError if an error occurs
+ * configuring the parser
+ */
+
+ public synchronized XMLReader getStyleParser() throws TransformerFactoryConfigurationError {
+ if (styleParserPool == null) {
+ styleParserPool = new ConcurrentLinkedQueue<XMLReader>();
+ }
+ XMLReader parser = styleParserPool.poll();
+ if (parser != null) {
+ return parser;
+ }
+
+ if (getStyleParserClass() != null) {
+ parser = makeParser(getStyleParserClass());
+ } else {
+ parser = loadParser();
+ StandardEntityResolver resolver = new StandardEntityResolver();
+ resolver.setConfiguration(this);
+ parser.setEntityResolver(resolver);
+ }
+ try {
+ parser.setFeature("http://xml.org/sax/features/namespaces", true);
+ parser.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
+ } catch (SAXNotRecognizedException e) {
+ throw new TransformerFactoryConfigurationError(e);
+ } catch (SAXNotSupportedException e) {
+ throw new TransformerFactoryConfigurationError(e);
+ }
+ return parser;
+ }
+
+ private static LexicalHandler dummyLexicalHandler = new DefaultHandler2();
+
+ /**
+ * Return a stylesheet (or schema) parser to the pool, for reuse
+ *
+ * @param parser The parser: the caller must not supply a parser that was obtained by any
+ * mechanism other than calling the getStyleParser() method.
+ */
+
+ public synchronized void reuseStyleParser(XMLReader parser) {
+ if (styleParserPool == null) {
+ styleParserPool = new ConcurrentLinkedQueue<XMLReader>();
+ }
+ try {
+ try {
+ // give things back to the garbage collecter
+ parser.setContentHandler(null);
+ //parser.setEntityResolver(null);
+ parser.setDTDHandler(null);
+ parser.setErrorHandler(null);
+ // Unfortunately setting the lexical handler to null doesn't work on Xerces, because
+ // it tests "value instanceof LexicalHandler". Instead we set the lexical handler to one
+ // that holds no references
+ parser.setProperty("http://xml.org/sax/properties/lexical-handler", dummyLexicalHandler);
+ } catch (SAXNotRecognizedException err) {
+ //
+ } catch (SAXNotSupportedException err) {
+ //
+ }
+ styleParserPool.offer(parser);
+ } catch (Exception e) {
+ // setting the callbacks on an XMLReader to null doesn't always work; some parsers throw a
+ // NullPointerException. If anything goes wrong, the simplest approach is to ignore the error
+ // and not attempt to reuse the parser.
+ }
+ }
+
+ /**
+ * Simple interface to load a schema document
+ *
+ * @param absoluteURI the absolute URI of the location of the schema document
+ * @throws net.sf.saxon.type.SchemaException if the schema document at the given location cannot be read or is invalid
+ */
+
+ public void loadSchema(String absoluteURI) throws SchemaException {
+ readSchema(makePipelineConfiguration(), "", absoluteURI, null);
+ }
+
+ /**
+ * Read a schema from a given schema location
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param pipe the PipelineConfiguration
+ * @param baseURI the base URI of the instruction requesting the reading of the schema
+ * @param schemaLocation the location of the schema to be read
+ * @param expected The expected targetNamespace of the schema being read, or null if there is no expectation
+ * @return the target namespace of the schema; null if there is no expectation
+ * @throws UnsupportedOperationException when called in the non-schema-aware version of the product
+ * @throws net.sf.saxon.type.SchemaException if the schema cannot be read
+ */
+
+ /*@Nullable*/
+ public String readSchema(PipelineConfiguration pipe, String baseURI, String schemaLocation, /*@Nullable*/ String expected)
+ throws SchemaException {
+ needEnterpriseEdition();
+ return null;
+ }
+
+ /**
+ * Read schemas from a list of schema locations.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param pipe the pipeline configuration
+ * @param baseURI the base URI against which the schema locations are to be resolved
+ * @param schemaLocations the relative URIs specified as schema locations
+ * @param expected the namespace URI which is expected as the target namespace of the loaded schema
+ * @throws net.sf.saxon.type.SchemaException
+ * if an error occurs
+ */
+
+ public void readMultipleSchemas(PipelineConfiguration pipe, String baseURI, Collection<String> schemaLocations, String expected)
+ throws SchemaException {
+ needEnterpriseEdition();
+ }
+
+
+ /**
+ * Read an inline schema from a stylesheet.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param root the xs:schema element in the stylesheet
+ * @param expected the target namespace expected; null if there is no
+ * expectation.
+ * @param errorListener The destination for error messages. May be null, in which case
+ * the errorListener registered with this Configuration is used.
+ * @return the actual target namespace of the schema
+ * @throws net.sf.saxon.type.SchemaException if the schema cannot be processed
+ */
+
+ /*@Nullable*/
+ public String readInlineSchema(NodeInfo root, String expected, ErrorListener errorListener)
+ throws SchemaException {
+ needEnterpriseEdition();
+ return null;
+ }
+
+ /**
+ * Throw an error indicating that a request cannot be satisfied because it requires
+ * the schema-aware edition of Saxon
+ */
+
+ protected void needEnterpriseEdition() {
+ throw new UnsupportedOperationException(
+ "You need the Enterprise Edition of Saxon (with an EnterpriseConfiguration) for this operation");
+ }
+
+ /**
+ * Load a schema, which will be available for use by all subsequent operations using
+ * this Configuration. Any errors will be notified to the ErrorListener associated with
+ * this Configuration.
+ *
+ * @param schemaSource the JAXP Source object identifying the schema document to be loaded
+ * @throws SchemaException if the schema cannot be read or parsed or if it is invalid
+ * @throws UnsupportedOperationException if the configuration is not schema-aware
+ * @since 8.4
+ */
+
+ public void addSchemaSource(Source schemaSource) throws SchemaException {
+ addSchemaSource(schemaSource, getErrorListener());
+ }
+
+ /**
+ * Load a schema, which will be available for use by all subsequent operations using
+ * this EnterpriseConfiguration.
+ *
+ * @param schemaSource the JAXP Source object identifying the schema document to be loaded
+ * @param errorListener the ErrorListener to be notified of any errors in the schema.
+ * @throws SchemaException if the schema cannot be read or parsed or if it is invalid
+ */
+
+ public void addSchemaSource(Source schemaSource, ErrorListener errorListener) throws SchemaException {
+ needEnterpriseEdition();
+ }
+
+ /**
+ * Add a built-in schema for a given namespace. This is a no-op if the configuration is not schema-aware
+ *
+ * @param namespace the namespace. Currently built-in schemas are available for the XML and FN namespaces
+ */
+
+ public void addSchemaForBuiltInNamespace(String namespace) {
+ // no action
+ }
+
+ /**
+ * Determine whether the Configuration contains a cached schema for a given target namespace
+ *
+ * @param targetNamespace the target namespace of the schema being sought (supply "" for the
+ * unnamed namespace)
+ * @return true if the schema for this namespace is available, false if not.
+ */
+
+ public boolean isSchemaAvailable(String targetNamespace) {
+ return false;
+ }
+
+ /**
+ * Remove all schema components that have been loaded into this Configuration.
+ * This method must not be used if any processes (such as stylesheet or query compilations
+ * or executions) are currently active. In a multi-threaded environment, it is the user's
+ * responsibility to ensure that this method is not called unless it is safe to do so.
+ */
+
+ public void clearSchemaCache() {
+ // no-op except in Saxon-EE
+ }
+
+ /**
+ * Get the set of namespaces of imported schemas
+ *
+ * @return a Set whose members are the namespaces of all schemas in the schema cache, as
+ * String objects
+ */
+
+ public Set getImportedNamespaces() {
+ return Collections.EMPTY_SET;
+ }
+
+ /**
+ * Mark a schema namespace as being sealed. This is done when components from this namespace
+ * are first used for validating a source document or compiling a source document or query. Once
+ * a namespace has been sealed, it is not permitted to change the schema components in that namespace
+ * by redefining them, deriving new types by extension, or adding to their substitution groups.
+ *
+ * @param namespace the namespace URI of the components to be sealed
+ */
+
+ public void sealNamespace(String namespace) {
+ //
+ }
+
+ /**
+ * Get the set of saxon:param schema parameters declared in the schema held by this Configuration.
+ * @return the set of parameters. May return null if none have been declared.
+ */
+
+ public Collection<GlobalParam> getDeclaredSchemaParameters() {
+ return null;
+ }
+
+ /**
+ * Get the set of complex types that have been defined as extensions of a given type.
+ * Note that we do not seal the schema namespace, so this list is not necessarily final; we must
+ * assume that new extensions of built-in simple types can be added at any time
+ *
+ * @param type the type whose extensions are required
+ * @return an iterator over the types that are derived from the given type by extension
+ */
+
+ public Iterator<? extends SchemaType> getExtensionsOfType(SchemaType type) {
+ Set<SchemaType> e = Collections.emptySet();
+ return e.iterator();
+ }
+
+ /**
+ * Import a precompiled Schema Component Model from a given Source. The schema components derived from this schema
+ * document are added to the cache of schema components maintained by this SchemaManager
+ *
+ * @param source the XML file containing the schema component model, as generated by a previous call on
+ * {@link #exportComponents}
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void importComponents(Source source) throws XPathException {
+ needEnterpriseEdition();
+ }
+
+ /**
+ * Export a precompiled Schema Component Model containing all the components (except built-in components)
+ * that have been loaded into this Processor.
+ *
+ * @param out the destination to recieve the precompiled Schema Component Model in the form of an
+ * XML document
+ * @throws net.sf.saxon.trans.XPathException if a failure occurs
+ */
+
+ public void exportComponents(Receiver out) throws XPathException {
+ needEnterpriseEdition();
+ }
+
+ /**
+ * Get information about the schema in the form of a function item. Supports the extension function
+ * saxon:schema
+ * @return null for a non-schema-aware configuration
+ */
+
+ public FunctionItem getSchemaAsFunctionItem() {
+ return null;
+ }
+
+ /**
+ * Get information about the schema in the form of a function item. Supports the extension function
+ * saxon:schema
+ * @param kind the component kind, e.g. "element declaration"
+ * @param name the component name
+ * @return null for a non-schema-aware configuration
+ */
+
+ public FunctionItem getSchemaComponentAsFunctionItem(String kind, QNameValue name) throws XPathException {
+ return null;
+ }
+
+
+
+ /**
+ * Get a global element declaration.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param fingerprint the NamePool fingerprint of the name of the required
+ * element declaration
+ * @return the element declaration whose name matches the given
+ * fingerprint, or null if no element declaration with this name has
+ * been registered.
+ */
+
+ /*@Nullable*/
+ public SchemaDeclaration getElementDeclaration(int fingerprint) {
+ return null;
+ }
+
+ /**
+ * Get a global element declaration.
+ *
+ * @param qName the name of the required
+ * element declaration
+ * @return the element declaration whose name matches the given
+ * fingerprint, or null if no element declaration with this name has
+ * been registered.
+ */
+
+
+ /*@Nullable*/
+ public SchemaDeclaration getElementDeclaration(StructuredQName qName) {
+ return null;
+ }
+
+ /**
+ * Get a global attribute declaration.
+ * <p/>
+ * This method is intended for internal use
+ *
+ * @param fingerprint the namepool fingerprint of the required attribute
+ * declaration
+ * @return the attribute declaration whose name matches the given
+ * fingerprint, or null if no element declaration with this name has
+ * been registered.
+ */
+
+ /*@Nullable*/
+ public SchemaDeclaration getAttributeDeclaration(int fingerprint) {
+ return null;
+ }
+
+ /**
+ * Get the top-level schema type definition with a given fingerprint.
+ * <p/>
+ * This method is intended for internal use and for use by advanced
+ * applications. (The SchemaType object returned cannot yet be considered
+ * a stable API, and may be superseded when a JAXP API for schema information
+ * is defined.)
+ *
+ * @param fingerprint the fingerprint of the schema type
+ * @return the schema type , or null if there is none
+ * with this name.
+ */
+
+ /*@Nullable*/
+ public SchemaType getSchemaType(int fingerprint) {
+ if (fingerprint < 1023) {
+ return BuiltInType.getSchemaType(fingerprint);
+ }
+ return null;
+ }
+
+ /**
+ * Ask whether a given notation has been declared in the schema
+ *
+ * @param uri the targetNamespace of the notation
+ * @param local the local part of the notation name
+ * @return true if the notation has been declared, false if not
+ * @since 9.3
+ */
+
+ public boolean isDeclaredNotation(String uri, String local) {
+ return false;
+ }
+
+ /**
+ * Get the external object type corresponding to a fingerprint if it is indeed an external object
+ * type, otherwise return null
+ *
+ * @param fingerprint the name of the type
+ * @return the external object type it the name is in the JAVA_TYPE namespace, otherwise null.
+ */
+
+ /*@Nullable*/
+ protected ExternalObjectType getExternalObjectType(int fingerprint) {
+ if (getNamePool().getURI(fingerprint).equals(NamespaceConstant.JAVA_TYPE)) {
+ try {
+ Class namedClass = dynamicLoader.getClass(getNamePool().getLocalName(fingerprint), null, null);
+ if (namedClass == null) {
+ return null;
+ }
+ return new ExternalObjectType(namedClass, this);
+ } catch (XPathException err) {
+ return null;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check that a type is validly derived from another type, following the rules for the Schema Component
+ * Constraint "Is Type Derivation OK (Simple)" (3.14.6) or "Is Type Derivation OK (Complex)" (3.4.6) as
+ * appropriate.
+ *
+ * @param derived the derived type
+ * @param base the base type; the algorithm tests whether derivation from this type is permitted
+ * @param block the derivations that are blocked by the relevant element declaration
+ * @throws SchemaException if the derivation is not allowed
+ */
+
+ public void checkTypeDerivationIsOK(SchemaType derived, SchemaType base, int block)
+ throws SchemaException {
+ // no action. Although the method can be used to check built-in types, it is never
+ // needed in the non-schema-aware product
+ }
+
+ /**
+ * Get a document-level validator to add to a Receiver pipeline.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ *
+ * @param receiver The receiver to which events should be sent after validation
+ * @param systemId the base URI of the document being validated
+ * @param validationOptions Supplies options relevant to XSD validation
+ * @return A Receiver to which events can be sent for validation
+ */
+
+ public Receiver getDocumentValidator(Receiver receiver,
+ String systemId,
+ ParseOptions validationOptions) {
+ // non-schema-aware version
+ return receiver;
+ }
+
+ /**
+ * Get a Receiver that can be used to validate an element, and that passes the validated
+ * element on to a target receiver. If validation is not supported, the returned receiver
+ * will be the target receiver.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param receiver the target receiver tp receive the validated element
+ * @param validationOptions options affecting the way XSD validation is done
+ * @param locationId current location in the stylesheet or query
+ * @return The target receiver, indicating that with this configuration, no validation
+ * is performed.
+ * @throws net.sf.saxon.trans.XPathException if a validator for the element cannot be created
+ */
+
+ public SequenceReceiver getElementValidator(SequenceReceiver receiver,
+ ParseOptions validationOptions,
+ int locationId)
+ throws XPathException {
+ return receiver;
+ }
+
+ /**
+ * Validate an attribute value.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ *
+ * @param nameCode the name of the attribute
+ * @param value the value of the attribute as a string
+ * @param validation STRICT or LAX
+ * @return the type annotation to apply to the attribute node
+ * @throws ValidationException if the value is invalid
+ */
+
+ public SimpleType validateAttribute(int nameCode, CharSequence value, int validation)
+ throws ValidationException {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+
+ /**
+ * Add to a pipeline a receiver that strips all type annotations. This
+ * has a null implementation in the Saxon-B product, because type annotations
+ * can never arise.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param destination the Receiver that events will be written to after whitespace stripping
+ * @return the Receiver to which events should be sent for stripping
+ */
+
+ public Receiver getAnnotationStripper(Receiver destination) {
+ return destination;
+ }
+
+ /**
+ * Create a new SAX XMLReader object using the class name provided. <br>
+ * <p/>
+ * The named class must exist and must implement the
+ * org.xml.sax.XMLReader or Parser interface. <br>
+ * <p/>
+ * This method returns an instance of the parser named.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param className A string containing the name of the
+ * SAX parser class, for example "com.microstar.sax.LarkDriver"
+ * @return an instance of the Parser class named, or null if it is not
+ * loadable or is not a Parser.
+ * @throws javax.xml.transform.TransformerFactoryConfigurationError if a failure
+ * occurs configuring the parser of this class
+ */
+ public XMLReader makeParser(String className)
+ throws TransformerFactoryConfigurationError {
+ Object obj;
+ try {
+ obj = dynamicLoader.getInstance(className, null);
+ } catch (XPathException err) {
+ throw new TransformerFactoryConfigurationError(err);
+ }
+ if (obj instanceof XMLReader) {
+ return (XMLReader) obj;
+ }
+ throw new TransformerFactoryConfigurationError("Class " + className +
+ " is not a SAX2 XMLReader");
+ }
+
+ /**
+ * Make an expression Parser for a specified version of XPath or XQuery
+ *
+ * @param language set to "XP" (XPath) or "XQ" (XQuery) or "PATTERN" (XSLT Patterns)
+ * @param updating indicates whether or not XQuery update syntax may be used. Note that XQuery Update
+ * is supported only in Saxon-EE
+ * @param languageVersion the required version (e.g "1.0", "3.0")
+ * @return the QueryParser
+ * @throws UnsupportedOperationException if a parser that supports update syntax is requested on Saxon-B
+ */
+
+ public ExpressionParser newExpressionParser(String language, boolean updating, DecimalValue languageVersion) {
+ if ("XQ".equals(language)) {
+ if (updating) {
+ throw new UnsupportedOperationException("XQuery Update is supported only in Saxon-EE");
+ } else if (DecimalValue.THREE.equals(languageVersion) || DecimalValue.ONE_POINT_ONE.equals(languageVersion)) {
+ throw new UnsupportedOperationException("XQuery 3.0 extensions are supported only in Saxon-PE");
+ } else if (DecimalValue.ONE.equals(languageVersion)) {
+ return new QueryParser();
+ } else {
+ throw new IllegalArgumentException("Unknown XQuery version " + languageVersion);
+ }
+ } else if ("XP".equals(language)) {
+ if (DecimalValue.THREE.equals(languageVersion)) {
+ throw new UnsupportedOperationException("XPath 3.0 extensions are supported only in Saxon-PE");
+ } else if (DecimalValue.TWO.equals(languageVersion)) {
+ return new ExpressionParser();
+ } else {
+ throw new IllegalArgumentException("Unknown XPath version " + languageVersion);
+ }
+ } else if ("PATTERN".equals(language)) {
+ if (DecimalValue.THREE.equals(languageVersion)) {
+ throw new UnsupportedOperationException("XSLT 3.0 extensions are supported only in Saxon-PE");
+ } else {
+ return new PatternParser20();
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown expression language " + language);
+ }
+ }
+
+ /**
+ * Make an OuterForExpression (Saxon-PE and Saxon-EE only)
+ * @return the new expression
+ */
+
+ public Expression makeOuterForExpression() {
+ throw new UnsupportedOperationException("'allowing empty' requires Saxon-PE or -EE");
+ }
+
+
+
+ /**
+ * Get a locale given a language code in XML format.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param lang the language code
+ * @return the Java locale
+ */
+
+ public static Locale getLocale(String lang) {
+ int hyphen = lang.indexOf("-");
+ String language, country;
+ if (hyphen < 1) {
+ language = lang;
+ country = "";
+ } else {
+ language = lang.substring(1, hyphen);
+ country = lang.substring(hyphen + 1);
+ }
+ return new Locale(language, country);
+ }
+
+ /**
+ * Set the debugger to be used.
+ * <p/>
+ * This method is provided for advanced users only, and is subject to change.
+ *
+ * @param debugger the debugger to be used, or null if no debugger is to be used
+ */
+
+ public void setDebugger(/*@Nullable*/ Debugger debugger) {
+ this.debugger = debugger;
+ }
+
+ /**
+ * Get the debugger in use. This will be null if no debugger has been registered.
+ * <p/>
+ * This method is provided for advanced users only, and is subject to change.
+ *
+ * @return the debugger in use, or null if none is in use
+ */
+
+ /*@Nullable*/
+ public Debugger getDebugger() {
+ return debugger;
+ }
+
+ /**
+ * Factory method to create a SlotManager.
+ * <p/>
+ * This method is provided for advanced users only, and is subject to change.
+ *
+ * @return a SlotManager (which is a skeletal stack frame representing the mapping of variable
+ * names to slots on the stack frame)
+ */
+
+ public SlotManager makeSlotManager() {
+ if (debugger == null) {
+ return new SlotManager();
+ } else {
+ return debugger.makeSlotManager();
+ }
+ }
+
+ /**
+ * Create a streaming transformer
+ *
+ * @param context the initial XPath context
+ * @param mode the initial mode, which must be a streaming mode
+ * @return a Receiver to which the streamed input document will be pushed
+ * @throws XPathException if a streaming transformer cannot be created (which
+ * is always the case in Saxon-HE and Saxon-PE)
+ */
+
+ /*@NotNull*/
+ public Receiver makeStreamingTransformer(XPathContext context, Mode mode) throws XPathException {
+ throw new XPathException("Streaming is only available in Saxon-EE");
+ }
+
+ /**
+ * Factory method to get an Optimizer.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return the optimizer used in this configuration, which is created if necessary
+ */
+
+ /*@NotNull*/
+ public Optimizer obtainOptimizer() {
+ if (optimizer == null) {
+ optimizer = new Optimizer(this);
+ optimizer.setOptimizationLevel(optimizationLevel);
+ assert optimizer != null;
+ return optimizer;
+ } else {
+ return optimizer;
+ }
+ }
+
+ /**
+ * Make a Closure, given the expected reference count
+ *
+ *
+ * @param expression the expression to be evaluated
+ * @param ref the (nominal) number of times the value of the expression is required
+ * @param context the XPath dynamic evaluation context
+ * @return the constructed Closure
+ * @throws XPathException if a failure occurs constructing the Closure
+ */
+
+ public Sequence makeClosure(Expression expression, int ref, XPathContext context) throws XPathException {
+ Sequence seq = new Closure();
+ if (ref > 1) {
+ seq = new MemoClosure();
+ }
+ return seq;
+ }
+
+ /**
+ * Make a SequenceExtent, given the expected reference count
+ *
+ * @param expression the expression to be evaluated
+ * @param ref the (nominal) number of times the value of the expression is required
+ * @param context the XPath dynamic evaluation context
+ * @return the constructed SequenceExtent
+ * @throws XPathException if evaluation of the expression fails
+ */
+
+ public Sequence makeSequenceExtent(Expression expression, int ref, XPathContext context) throws XPathException {
+ return SequenceExtent.makeSequenceExtent(expression.iterate(context));
+ }
+
+
+ /**
+ * Factory method to get the StyleNodeFactory, used for constructing elements
+ * in a stylesheet document
+ *
+ * @return the StyleNodeFactory used in this Configuration
+ */
+
+ public StyleNodeFactory makeStyleNodeFactory() {
+ return new StyleNodeFactory(this);
+ }
+
+ /**
+ * Register an external object model with this Configuration.
+ *
+ * @param model The external object model.
+ * This can either be one of the system-supplied external
+ * object models for JDOM, XOM, or DOM, or a user-supplied external object model.
+ * <p/>
+ * This method is intended for advanced users only, and is subject to change.
+ */
+
+ public void registerExternalObjectModel(ExternalObjectModel model) {
+ if (externalObjectModels == null) {
+ externalObjectModels = new ArrayList<ExternalObjectModel>(4);
+ }
+ if (!externalObjectModels.contains(model)) {
+ externalObjectModels.add(model);
+ }
+ }
+
+ /**
+ * Get the external object model with a given URI, if registered
+ *
+ * @param uri the identifying URI of the required external object model
+ * @return the requested external object model if available, or null otherwise
+ */
+
+ /*@Nullable*/
+ public ExternalObjectModel getExternalObjectModel(String uri) {
+ for (ExternalObjectModel model : externalObjectModels) {
+ if (model.getIdentifyingURI().equals(uri)) {
+ return model;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the external object model that recognizes a particular class of node, if available
+ *
+ * @param nodeClass the class of the Node object in the external object model
+ * @return the requested external object model if available, or null otherwise
+ */
+
+ /*@Nullable*/
+ public ExternalObjectModel getExternalObjectModel(Class nodeClass) {
+ for (ExternalObjectModel model : externalObjectModels) {
+ PJConverter converter = model.getPJConverter(nodeClass);
+ if (converter != null) {
+ return model;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all the registered external object models.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return a list of external object models supported. The members of the list are of
+ * type {@link ExternalObjectModel}
+ */
+
+ public List<ExternalObjectModel> getExternalObjectModels() {
+ return externalObjectModels;
+ }
+
+ /**
+ * Get a NodeInfo corresponding to a DOM or other external Node,
+ * either by wrapping or unwrapping the external Node.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param source A Source representing the wrapped or unwrapped external Node. This will typically
+ * be a DOMSource, but it may be a similar Source recognized by some other registered external
+ * object model.
+ * @return If the Source is a DOMSource and the underlying node is a wrapper around a Saxon NodeInfo,
+ * returns the wrapped Saxon NodeInfo. If the Source is a DOMSource and the undelying node is not such a wrapper,
+ * returns a new Saxon NodeInfo that wraps the DOM Node. If the Source is any other kind of source, it
+ * is offered to each registered external object model for similar treatment. The result is the
+ * NodeInfo object obtained by wrapping or unwrapping the supplied external node.
+ * @throws IllegalArgumentException if the source object is not of a recognized class. This method does
+ * <emph>not</emph> call the registered {@link SourceResolver to resolve the Source}.
+ */
+
+ public NodeInfo unravel(Source source) {
+ List<ExternalObjectModel> externalObjectModels = getExternalObjectModels();
+ for (ExternalObjectModel model : externalObjectModels) {
+ NodeInfo node = model.unravel(source, this);
+ if (node != null) {
+ if (node.getConfiguration() != this) {
+ throw new IllegalArgumentException("Externally supplied Node belongs to the wrong Configuration");
+ }
+ return node;
+ }
+ }
+ if (source instanceof NodeInfo) {
+ if (((NodeInfo) source).getConfiguration() != this) {
+ throw new IllegalArgumentException("Externally supplied NodeInfo belongs to the wrong Configuration");
+ }
+ return (NodeInfo) source;
+ }
+
+ throw new IllegalArgumentException("A source of class " +
+ source.getClass() + " is not recognized by any registered object model");
+
+ }
+
+ /**
+ * Set the level of DOM interface to be used
+ *
+ * @param level the DOM level. Must be 2 or 3. By default Saxon assumes that DOM level 3 is available;
+ * this parameter can be set to the value 2 to indicate that Saxon should not use methods unless they
+ * are available in DOM level 2. From Saxon 9.2, this switch remains available, but the use of
+ * DOM level 2 is untested and unsupported.
+ */
+
+ public void setDOMLevel(int level) {
+ if (!(level == 2 || level == 3)) {
+ throw new IllegalArgumentException("DOM Level must be 2 or 3");
+ }
+ domLevel = level;
+ }
+
+ /**
+ * Get the level of DOM interface to be used
+ *
+ * @return the DOM level. Always 2 or 3.
+ */
+
+ public int getDOMLevel() {
+ return domLevel;
+ }
+
+ /**
+ * Get a new StaticQueryContext (which is also the factory class for creating a query parser)
+ *
+ * @return a new StaticQueryContext
+ */
+
+ public StaticQueryContext newStaticQueryContext() {
+ return new StaticQueryContext(this, false);
+ }
+
+ /**
+ * Get a new Pending Update List
+ *
+ * @return the new Pending Update List
+ * @throws UnsupportedOperationException if called when using Saxon-B
+ */
+
+ public PendingUpdateList newPendingUpdateList() {
+ throw new UnsupportedOperationException("XQuery update is supported only in Saxon-EE");
+ }
+
+ /**
+ * Make a PipelineConfiguration from the properties of this Configuration
+ *
+ * @return a new PipelineConfiguration
+ * @since 8.4
+ */
+
+ public PipelineConfiguration makePipelineConfiguration() {
+ PipelineConfiguration pipe = new PipelineConfiguration(this);
+ pipe.setURIResolver(getURIResolver());
+ pipe.setSchemaURIResolver(getSchemaURIResolver());
+ pipe.setHostLanguage(getHostLanguage());
+ pipe.setParseOptions(new ParseOptions(defaultParseOptions));
+ return pipe;
+ }
+
+ /**
+ * Get the configuration, given the context. This is provided as a static method to make it accessible
+ * as an extension function.
+ *
+ * @param context the XPath dynamic context
+ * @return the Saxon Configuration for a given XPath dynamic context
+ */
+
+ public static Configuration getConfiguration(XPathContext context) {
+ return context.getConfiguration();
+ }
+
+ /**
+ * Get the type of function items (values representing a function that can be
+ * used as an argument to higher-order functions)
+ */
+
+// public AtomicType getFunctionItemType() {
+// throw new UnsupportedOperationException("Higher-order functions require Saxon-EE");
+// }
+
+ /**
+ * Supply a SourceResolver. This is used for handling unknown implementations of the
+ * {@link javax.xml.transform.Source} interface: a user-supplied SourceResolver can handle
+ * such Source objects and translate them to a kind of Source that Saxon understands.
+ *
+ * @param resolver the source resolver.
+ */
+
+ public void setSourceResolver(SourceResolver resolver) {
+ sourceResolver = resolver;
+ }
+
+ /**
+ * Get the current SourceResolver. If none has been supplied, a system-defined SourceResolver
+ * is returned.
+ *
+ * @return the current SourceResolver
+ */
+
+ public SourceResolver getSourceResolver() {
+ return sourceResolver;
+ }
+
+ /**
+ * Resolve a Source.
+ *
+ * @param source A source object, typically the source supplied as the first
+ * argument to {@link javax.xml.transform.Transformer#transform(javax.xml.transform.Source, javax.xml.transform.Result)}
+ * or similar methods.
+ * @param config The Configuration. This provides the SourceResolver with access to
+ * configuration information; it also allows the SourceResolver to invoke the
+ * resolveSource() method on the Configuration object as a fallback implementation.
+ * @return a source object that Saxon knows how to process. This must be an instance of one
+ * of the classes StreamSource, SAXSource, DOMSource, {@link net.sf.saxon.lib.AugmentedSource},
+ * {@link net.sf.saxon.om.NodeInfo},
+ * or {@link net.sf.saxon.pull.PullSource}. Return null if the Source object is not
+ * recognized
+ * @throws XPathException if the Source object is recognized but cannot be processed
+ */
+
+ /*@Nullable*/
+ public Source resolveSource(Source source, Configuration config) throws XPathException {
+ if (source instanceof AugmentedSource) {
+ return source;
+ }
+ if (source instanceof StreamSource) {
+ return source;
+ }
+ if (source instanceof SAXSource) {
+ return source;
+ }
+ if (source instanceof DOMSource) {
+ return source;
+ }
+ if (source instanceof NodeInfo) {
+ return source;
+ }
+ if (source instanceof PullSource) {
+ return source;
+ }
+ if (source instanceof PullEventSource) {
+ return source;
+ }
+ return null;
+ }
+
+ /**
+ * Build a document tree, using options set on this Configuration and on the supplied source
+ * object. Options set on the source object override options set in the Configuration. The Source
+ * object must be one of the kinds of source recognized by Saxon, or a source that can be resolved
+ * using the registered {@link SourceResolver}.
+ *
+ * @param source the Source to be used. This may be an {@link AugmentedSource}, allowing options
+ * to be specified for the way in which this document will be built. If an AugmentedSource
+ * is supplied then options set in the AugmentedSource take precendence over options
+ * set in the Configuration.
+ * <p>From Saxon 9.2, this method always creates a new tree, it never wraps or returns
+ * an existing tree.</p>
+ * @return the document node of the constructed document
+ * @throws XPathException if any errors occur during document parsing or validation. Detailed
+ * errors occurring during schema validation will be written to the ErrorListener associated
+ * with the AugmentedSource, if supplied, or with the Configuration otherwise.
+ * @since 8.9. Modified in 9.0 to avoid copying a supplied document where this is not
+ * necessary. Modified in 9.2 so that this interface always constructs a new tree; it never
+ * wraps an existing document, even if an AugmentedSource that requests wrapping is supplied.
+ */
+
+ public DocumentInfo buildDocument(/*@Nullable*/ Source source) throws XPathException {
+
+ if (source == null) {
+ throw new NullPointerException("source");
+ }
+
+ // Resolve user-defined implementations of Source
+ Source src2 = resolveSource(source, this); // TODO the called buildDocument() does this again redundantly
+ if (src2 == null) {
+ throw new XPathException("Unknown source class " + source.getClass().getName());
+ }
+ source = src2;
+
+ ParseOptions options;
+ Source underlyingSource = source;
+ if (source instanceof AugmentedSource) {
+ options = ((AugmentedSource) source).getParseOptions();
+ underlyingSource = ((AugmentedSource) source).getContainedSource();
+ } else {
+ options = new ParseOptions();
+ }
+
+ source = underlyingSource;
+ return buildDocument(source, options);
+ }
+
+ /**
+ * Build a document, using specified options for parsing and building. This method always
+ * constructs a new tree, it never wraps an existing document (regardless of anything in
+ * the parseOptions)
+ *
+ * @param source the source of the document to be constructed. If this is an
+ * AugmentedSource, then any parser options contained in the AugmentedSource take precedence
+ * over options specified in the parseOptions argument.
+ * @param parseOptions options for parsing and constructing the document. Any options that
+ * are not explicitly set in parseOptions default first to the values supplied in the source
+ * argument if it is an AugmentedSource, and then to the values set in this Configuration.
+ * The supplied parseOptions object is not modified.
+ * @return the document node of the constructed document
+ * @throws XPathException if parsing fails, or if the Source represents a node other than
+ * a document node
+ * @since 9.2
+ */
+
+ public DocumentInfo buildDocument(/*@Nullable*/ Source source, ParseOptions parseOptions) throws XPathException {
+
+ if (source == null) {
+ throw new NullPointerException("source");
+ }
+
+ boolean finallyClose = false;
+ try {
+ ParseOptions options = new ParseOptions(parseOptions);
+
+ // Resolve user-defined implementations of Source
+ Source src2 = resolveSource(source, this);
+ if (src2 == null) {
+ throw new XPathException("Unknown source class " + source.getClass().getName());
+ }
+ source = src2;
+
+ if (source instanceof AugmentedSource) {
+ options.merge(((AugmentedSource) source).getParseOptions());
+ }
+
+ options.applyDefaults(this);
+ finallyClose = options.isPleaseCloseAfterUse();
+
+ // Create an appropriate Builder
+
+ TreeModel treeModel = options.getModel();
+
+ // Decide whether line numbering is in use
+
+ boolean lineNumbering = options.isLineNumbering();
+
+ PipelineConfiguration pipe = makePipelineConfiguration();
+ Builder builder = treeModel.makeBuilder(pipe);
+ builder.setTiming(isTiming());
+ builder.setLineNumbering(lineNumbering);
+ builder.setPipelineConfiguration(pipe);
+ Sender.send(source, new NamespaceReducer(builder), options);
+
+ // Get the constructed document
+
+ NodeInfo newdoc = builder.getCurrentRoot();
+ if (!(newdoc instanceof DocumentInfo)) {
+ throw new XPathException("Source object represents a node other than a document node");
+ }
+
+ // Reset the builder, detaching it from the constructed document
+
+ builder.reset();
+
+ // Return the constructed document
+
+ return (DocumentInfo) newdoc;
+
+ } finally {
+ // If requested, close the input stream
+ if (finallyClose) {
+ ParseOptions.close(source);
+ }
+ }
+
+ }
+
+
+ /**
+ * Load a named output emitter or SAX2 ContentHandler and check it is OK.
+ *
+ * @param clarkName the QName of the user-supplied ContentHandler (requested as a prefixed
+ * value of the method attribute in xsl:output, or anywhere that serialization parameters
+ * are allowed), encoded in Clark format as {uri}local
+ * @param props the properties to be used in the case of a dynamically-loaded ContentHandler.
+ * @return a Receiver (despite the name, it is not required to be an Emitter)
+ * @throws net.sf.saxon.trans.XPathException if a failure occurs creating the Emitter
+ */
+
+ public Receiver makeEmitter(String clarkName, Properties props) throws XPathException {
+ int brace = clarkName.indexOf('}');
+ String localName = clarkName.substring(brace + 1);
+ int colon = localName.indexOf(':');
+ String className = localName.substring(colon + 1);
+ Object handler;
+ try {
+ handler = dynamicLoader.getInstance(className, null);
+ } catch (XPathException e) {
+ throw new XPathException("Cannot create user-supplied output method. " + e.getMessage(),
+ SaxonErrorCode.SXCH0004);
+ }
+
+ if (handler instanceof Receiver) {
+ return (Receiver) handler;
+ } else if (handler instanceof ContentHandler) {
+ ContentHandlerProxy emitter = new ContentHandlerProxy();
+ emitter.setUnderlyingContentHandler((ContentHandler) handler);
+ emitter.setOutputProperties(props);
+ return emitter;
+ } else {
+ throw new XPathException("Output method " + className +
+ " is neither a Receiver nor a SAX2 ContentHandler");
+ }
+
+ }
+
+ /**
+ * Make an "unconstructed" (that is, lazily-constructed) element node
+ *
+ * @param instr the instruction that creates the element
+ * @param context the dynamic evaluation context
+ * @return the lazily constructed element node
+ * @throws net.sf.saxon.trans.XPathException if an error occurs, for example
+ * if called in Saxon-HE
+ */
+
+ public NodeInfo makeUnconstructedElement(ElementCreator instr, XPathContext context)
+ throws XPathException {
+ throw new XPathException("Lazy element construction requires Saxon-PE");
+ }
+
+ /**
+ * Make an "unconstructed" (that is, lazily-constructed) document node
+ *
+ * @param instr the instruction that creates the document node
+ * @param context the dynamic evaluation context
+ * @return the lazily constructed document node
+ * @throws net.sf.saxon.trans.XPathException
+ * in Saxon-HE
+ */
+
+ public DocumentInfo makeUnconstructedDocument(DocumentInstr instr, XPathContext context)
+ throws XPathException {
+ throw new XPathException("Lazy document construction requires Saxon-PE");
+ }
+
+ /**
+ * Get the sibling position of a node: specifically, count how many preceding siblings
+ * of a node satisfy the nodetest. This method is included here so it can be subclassed
+ * to handle streamed nodes.
+ * @param node the starting node, which is assumed to satisfy the node test
+ * @param nodeTest the node test
+ * @return the number of preceding siblings that satisfy the node test, plus one, unless the
+ * number exceeds max, in which case return some number greater than or equal to max.
+ */
+
+ public int getSiblingPosition(NodeInfo node, NodeTest nodeTest, int max) throws XPathException {
+ AxisIterator prev = node.iterateAxis(AxisInfo.PRECEDING_SIBLING, nodeTest);
+ int count = 1;
+ while (true) {
+ NodeInfo n = prev.next();
+ if (n == null) {
+ return count;
+ }
+ if (++count > max) {
+ return count;
+ }
+ }
+ }
+
+
+ /**
+ * Set a property of the configuration. This method underpins the setAttribute() method of the
+ * TransformerFactory implementation, and is provided
+ * to enable setting of Configuration properties using URIs without instantiating a TransformerFactory:
+ * specifically, this may be useful when running XQuery, and it is also used by the Validator API
+ *
+ * @param name the URI identifying the property to be set. See the class {@link FeatureKeys} for
+ * constants representing the property names that can be set.
+ * @param value the value of the property. Note that boolean values may be supplied either as a Boolean,
+ * or as one of the strings "0", "1", "true", "false", "yes", "no", "on", or "off".
+ * @throws IllegalArgumentException if the property name is not recognized or if the value is not
+ * a valid value for the named property
+ */
+
+ public void setConfigurationProperty(String name, Object value) {
+
+ if (name.equals(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
+ internalSetBooleanProperty(name, value);
+
+ } else if (name.equals(FeatureKeys.ALLOW_MULTITHREADING)) {
+ internalSetBooleanProperty(name, value);
+
+ } else if (name.equals(FeatureKeys.COLLATION_URI_RESOLVER)) {
+ if (!(value instanceof CollationURIResolver)) {
+ throw new IllegalArgumentException(
+ "COLLATION_URI_RESOLVER value must be an instance of net.sf.saxon.lib.CollationURIResolver");
+ }
+ setCollationURIResolver((CollationURIResolver) value);
+
+ } else if (name.equals(FeatureKeys.COLLATION_URI_RESOLVER_CLASS)) {
+ setCollationURIResolver(
+ (CollationURIResolver) instantiateClassName(name, value, CollationURIResolver.class));
+
+ } else if (name.equals(FeatureKeys.COLLECTION_URI_RESOLVER)) {
+ if (!(value instanceof CollectionURIResolver)) {
+ throw new IllegalArgumentException(
+ "COLLECTION_URI_RESOLVER value must be an instance of net.sf.saxon.lib.CollectionURIResolver");
+ }
+ setCollectionURIResolver((CollectionURIResolver) value);
+
+ } else if (name.equals(FeatureKeys.COLLECTION_URI_RESOLVER_CLASS)) {
+ setCollectionURIResolver(
+ (CollectionURIResolver) instantiateClassName(name, value, CollectionURIResolver.class));
+
+ } else if (name.equals(FeatureKeys.COMPILE_WITH_TRACING)) {
+ boolean b = requireBoolean("COMPILE_WITH_TRACING", value);
+ setCompileWithTracing(b);
+
+ } else if (name.equals(FeatureKeys.DEFAULT_COLLATION)) {
+ getCollationMap().setDefaultCollationName(value.toString());
+
+ } else if (name.equals(FeatureKeys.DEFAULT_COLLECTION)) {
+ setDefaultCollection(value.toString());
+
+ } else if (name.equals(FeatureKeys.DEFAULT_COUNTRY)) {
+ setDefaultCountry(value.toString());
+
+ } else if (name.equals(FeatureKeys.DEFAULT_LANGUAGE)) {
+ setDefaultLanguage(value.toString());
+
+ } else if (name.equals(FeatureKeys.DTD_VALIDATION)) {
+ boolean b = requireBoolean(name, value);
+ setValidation(b);
+
+ } else if (name.equals(FeatureKeys.DTD_VALIDATION_RECOVERABLE)) {
+ boolean b = requireBoolean(name, value);
+ if (b) {
+ defaultParseOptions.setDTDValidationMode(Validation.LAX);
+ } else {
+ defaultParseOptions.setDTDValidationMode(isValidation() ? Validation.STRICT : Validation.SKIP);
+ }
+
+ } else if (name.equals(FeatureKeys.ENTITY_RESOLVER_CLASS)) {
+ if ("".equals(value)) {
+ defaultParseOptions.setEntityResolver(null);
+ } else {
+ defaultParseOptions.setEntityResolver(
+ (EntityResolver) instantiateClassName(name, value, EntityResolver.class));
+ }
+
+ } else if (name.equals(FeatureKeys.ENVIRONMENT_VARIABLE_RESOLVER)) {
+ if (!(value instanceof EnvironmentVariableResolver)) {
+ throw new IllegalArgumentException(
+ "ENVIRONMENT_VARIABLE_RESOLVER value must be an instance of net.sf.saxon.lib.EnvironmentVariableResolver");
+ }
+ environmentVariableResolver = (EnvironmentVariableResolver)value;
+
+ } else if (name.equals(FeatureKeys.ENVIRONMENT_VARIABLE_RESOLVER_CLASS)) {
+ environmentVariableResolver =
+ (EnvironmentVariableResolver)instantiateClassName(name, value, EnvironmentVariableResolver.class);
+
+ } else if (name.equals(FeatureKeys.ERROR_LISTENER_CLASS)) {
+ setErrorListener((ErrorListener) instantiateClassName(name, value, ErrorListener.class));
+
+ } else if (name.equals(FeatureKeys.EXPAND_ATTRIBUTE_DEFAULTS)) {
+ boolean b = requireBoolean(name, value);
+ setExpandAttributeDefaults(b);
+
+ } else if (name.equals(FeatureKeys.IGNORE_SAX_SOURCE_PARSER)) {
+ internalSetBooleanProperty(name, value);
+
+ } else if (name.equals(FeatureKeys.LAZY_CONSTRUCTION_MODE)) {
+ internalSetBooleanProperty(name, value);
+
+ } else if (name.equals(FeatureKeys.LINE_NUMBERING)) {
+ boolean b = requireBoolean(name, value);
+ setLineNumbering(b);
+
+ } else if (name.equals(FeatureKeys.MESSAGE_EMITTER_CLASS)) {
+ if (!(value instanceof String)) {
+ throw new IllegalArgumentException("MESSAGE_EMITTER_CLASS class must be a String");
+ }
+ setMessageEmitterClass((String) value);
+
+ } else if (name.equals(FeatureKeys.MODULE_URI_RESOLVER)) {
+ if (!(value instanceof ModuleURIResolver)) {
+ throw new IllegalArgumentException(
+ "MODULE_URI_RESOLVER value must be an instance of net.sf.saxon.lib.ModuleURIResolver");
+ }
+ setModuleURIResolver((ModuleURIResolver) value);
+
+ } else if (name.equals(FeatureKeys.MODULE_URI_RESOLVER_CLASS)) {
+ setModuleURIResolver(
+ (ModuleURIResolver) instantiateClassName(name, value, ModuleURIResolver.class));
+
+ } else if (name.equals(FeatureKeys.NAME_POOL)) {
+ if (!(value instanceof NamePool)) {
+ throw new IllegalArgumentException("NAME_POOL value must be an instance of net.sf.saxon.om.NamePool");
+ }
+ setNamePool((NamePool) value);
+
+ } else if (name.equals(FeatureKeys.OPTIMIZATION_LEVEL)) {
+ String s = requireString(name, value);
+ try {
+ optimizationLevel = Integer.parseInt(s);
+ if (optimizationLevel < Optimizer.NO_OPTIMIZATION || optimizationLevel > Optimizer.FULL_OPTIMIZATION) {
+ throw new IllegalArgumentException("OPTIMIZATION_LEVEL must be in the range " +
+ Optimizer.NO_OPTIMIZATION + " to " + Optimizer.FULL_OPTIMIZATION);
+ }
+ if (optimizer != null) {
+ optimizer.setOptimizationLevel(optimizationLevel);
+ }
+ if (optimizationLevel < 10) {
+ internalSetBooleanProperty(FeatureKeys.GENERATE_BYTE_CODE, false);
+ }
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("OPTIMIZATION_LEVEL value must be a number represented as a string");
+ }
+
+ } else if (name.equals(FeatureKeys.OUTPUT_URI_RESOLVER)) {
+ if (!(value instanceof OutputURIResolver)) {
+ throw new IllegalArgumentException(
+ "OUTPUT_URI_RESOLVER value must be an instance of net.sf.saxon.lib.OutputURIResolver");
+ }
+ setOutputURIResolver((OutputURIResolver) value);
+
+ } else if (name.equals(FeatureKeys.OUTPUT_URI_RESOLVER_CLASS)) {
+ setOutputURIResolver(
+ (OutputURIResolver) instantiateClassName(name, value, OutputURIResolver.class));
+
+ } else if (name.equals(FeatureKeys.PRE_EVALUATE_DOC_FUNCTION)) {
+ internalSetBooleanProperty(name, value);
+
+ } else if (name.equals(FeatureKeys.PREFER_JAXP_PARSER)) {
+ internalSetBooleanProperty(name, value);
+
+ } else if (name.equals(FeatureKeys.RECOGNIZE_URI_QUERY_PARAMETERS)) {
+ boolean b = requireBoolean(name, value);
+ getSystemURIResolver().setRecognizeQueryParameters(b);
+
+ } else if (name.equals(FeatureKeys.RECOVERY_POLICY)) {
+ if (!(value instanceof Integer)) {
+ throw new IllegalArgumentException("RECOVERY_POLICY value must be an Integer");
+ }
+ setRecoveryPolicy((Integer) value);
+
+ } else if (name.equals(FeatureKeys.RECOVERY_POLICY_NAME)) {
+ if (!(value instanceof String)) {
+ throw new IllegalArgumentException("RECOVERY_POLICY_NAME value must be a String");
+ }
+ int rval;
+ if (value.equals("recoverSilently")) {
+ rval = RECOVER_SILENTLY;
+ } else if (value.equals("recoverWithWarnings")) {
+ rval = RECOVER_WITH_WARNINGS;
+ } else if (value.equals("doNotRecover")) {
+ rval = DO_NOT_RECOVER;
+ } else {
+ throw new IllegalArgumentException(
+ "Unrecognized value of RECOVERY_POLICY_NAME = '" + value +
+ "': must be 'recoverSilently', 'recoverWithWarnings', or 'doNotRecover'");
+ }
+ setRecoveryPolicy(rval);
+
+ } else if (name.equals(FeatureKeys.SERIALIZER_FACTORY_CLASS)) {
+ setSerializerFactory(
+ (SerializerFactory) instantiateClassName(name, value, OutputURIResolver.class));
+
+ } else if (name.equals(FeatureKeys.SCHEMA_URI_RESOLVER)) {
+ if (!(value instanceof SchemaURIResolver)) {
+ throw new IllegalArgumentException(
+ "SCHEMA_URI_RESOLVER value must be an instance of net.sf.saxon.lib.SchemaURIResolver");
+ }
+ setSchemaURIResolver((SchemaURIResolver) value);
+
+ } else if (name.equals(FeatureKeys.SCHEMA_URI_RESOLVER_CLASS)) {
+ setSchemaURIResolver(
+ (SchemaURIResolver) instantiateClassName(name, value, SchemaURIResolver.class));
+
+
+ } else if (name.equals(FeatureKeys.SCHEMA_VALIDATION)) {
+ if (!(value instanceof Integer)) {
+ throw new IllegalArgumentException("SCHEMA_VALIDATION must be an integer");
+ }
+ setSchemaValidationMode((Integer) value);
+
+ } else if (name.equals(FeatureKeys.SCHEMA_VALIDATION_MODE)) {
+ if (!(value instanceof String)) {
+ throw new IllegalArgumentException("SCHEMA_VALIDATION_MODE must be a string");
+ }
+ setSchemaValidationMode(Validation.getCode((String) value));
+
+ } else if (name.equals(FeatureKeys.SOURCE_PARSER_CLASS)) {
+ if (!(value instanceof String)) {
+ throw new IllegalArgumentException("SOURCE_PARSER_CLASS class must be a String");
+ }
+ setSourceParserClass((String) value);
+
+ } else if (name.equals(FeatureKeys.SOURCE_RESOLVER_CLASS)) {
+ setSourceResolver(
+ (SourceResolver) instantiateClassName(name, value, SourceResolver.class));
+
+ } else if (name.equals(FeatureKeys.STANDARD_ERROR_OUTPUT_FILE)) {
+ // Note, this property is write-only
+ try {
+ boolean append = true;
+ boolean autoFlush = true;
+ setStandardErrorOutput(
+ new PrintStream(new FileOutputStream(((String) value), append), autoFlush));
+ } catch (FileNotFoundException fnf) {
+ throw new IllegalArgumentException(fnf);
+ }
+
+ } else if (name.equals(FeatureKeys.STRIP_WHITESPACE)) {
+ String s = requireString(name, value);
+ int ival;
+ if (s.equals("all")) {
+ ival = Whitespace.ALL;
+ } else if (s.equals("none")) {
+ ival = Whitespace.NONE;
+ } else if (s.equals("ignorable")) {
+ ival = Whitespace.IGNORABLE;
+ } else {
+ throw new IllegalArgumentException(
+ "Unrecognized value STRIP_WHITESPACE = '" + value +
+ "': must be 'all', 'none', or 'ignorable'");
+ }
+ setStripsWhiteSpace(ival);
+
+
+ } else if (name.equals(FeatureKeys.STYLE_PARSER_CLASS)) {
+ setStyleParserClass(requireString(name, value));
+
+ } else if (name.equals(FeatureKeys.TIMING)) {
+ setTiming(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.TRACE_EXTERNAL_FUNCTIONS)) {
+ internalSetBooleanProperty(FeatureKeys.TRACE_EXTERNAL_FUNCTIONS, value);
+
+ } else if (name.equals(FeatureKeys.TRACE_OPTIMIZER_DECISIONS)) {
+ internalSetBooleanProperty(name, value);
+
+ } else if (name.equals(FeatureKeys.TRACE_LISTENER)) {
+ if (!(value instanceof TraceListener)) {
+ throw new IllegalArgumentException("TRACE_LISTENER is of wrong class");
+ }
+ setTraceListener((TraceListener) value);
+
+ } else if (name.equals(FeatureKeys.TRACE_LISTENER_CLASS)) {
+ setTraceListenerClass(requireString(name, value));
+
+ } else if (name.equals(FeatureKeys.TREE_MODEL)) {
+ if (!(value instanceof Integer)) {
+ throw new IllegalArgumentException("Tree model must be an Integer");
+ }
+ setTreeModel((Integer) value);
+
+ } else if (name.equals(FeatureKeys.TREE_MODEL_NAME)) {
+ String s = requireString(name, value);
+ if (s.equals("tinyTree")) {
+ setTreeModel(Builder.TINY_TREE);
+ } else if (s.equals("tinyTreeCondensed")) {
+ setTreeModel(Builder.TINY_TREE_CONDENSED);
+ } else if (s.equals("linkedTree")) {
+ setTreeModel(Builder.LINKED_TREE);
+ } else if (s.equals("jdom")) {
+ setTreeModel(Builder.JDOM_TREE);
+ } else if (s.equals("jdom2")) {
+ setTreeModel(Builder.JDOM2_TREE);
+ } else {
+ throw new IllegalArgumentException(
+ "Unrecognized value TREE_MODEL_NAME = '" + value +
+ "': must be linkedTree|tinyTree|tinyTreeCondensed");
+ }
+ } else if (name.equals(FeatureKeys.URI_RESOLVER_CLASS)) {
+ setURIResolver(
+ (URIResolver) instantiateClassName(name, value, URIResolver.class));
+
+ } else if (name.equals(FeatureKeys.USE_PI_DISABLE_OUTPUT_ESCAPING)) {
+ internalSetBooleanProperty(name, value);
+
+ } else if (name.equals(FeatureKeys.USE_TYPED_VALUE_CACHE)) {
+ internalSetBooleanProperty(name, value);
+
+ } else if (name.equals(FeatureKeys.USE_XSI_SCHEMA_LOCATION)) {
+ defaultParseOptions.setUseXsiSchemaLocation(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.VALIDATION_COMMENTS)) {
+ defaultParseOptions.setAddCommentsAfterValidationErrors(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.VALIDATION_WARNINGS)) {
+ setValidationWarnings(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.VERSION_WARNING)) {
+ setVersionWarning(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.XINCLUDE)) {
+ setXIncludeAware(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.XQUERY_ALLOW_UPDATE)) {
+ getDefaultStaticQueryContext().setUpdatingEnabled(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.XQUERY_CONSTRUCTION_MODE)) {
+ getDefaultStaticQueryContext().setConstructionMode(Validation.getCode(value.toString()));
+
+ } else if (name.equals(FeatureKeys.XQUERY_DEFAULT_ELEMENT_NAMESPACE)) {
+ getDefaultStaticQueryContext().setDefaultElementNamespace(value.toString());
+
+ } else if (name.equals(FeatureKeys.XQUERY_DEFAULT_FUNCTION_NAMESPACE)) {
+ getDefaultStaticQueryContext().setDefaultFunctionNamespace(value.toString());
+
+ } else if (name.equals(FeatureKeys.XQUERY_EMPTY_LEAST)) {
+ getDefaultStaticQueryContext().setEmptyLeast(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.XQUERY_INHERIT_NAMESPACES)) {
+ getDefaultStaticQueryContext().setInheritNamespaces(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.XQUERY_PRESERVE_BOUNDARY_SPACE)) {
+ getDefaultStaticQueryContext().setPreserveBoundarySpace(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.XQUERY_PRESERVE_NAMESPACES)) {
+ getDefaultStaticQueryContext().setPreserveNamespaces(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.XQUERY_REQUIRED_CONTEXT_ITEM_TYPE)) {
+ ExpressionParser parser = new ExpressionParser();
+ parser.setLanguage(ExpressionParser.SEQUENCE_TYPE, DecimalValue.THREE);
+ try {
+ SequenceType type = parser.parseSequenceType(value.toString(), new IndependentContext(this));
+ // TODO: disallow occurrence indicator
+ getDefaultStaticQueryContext().setRequiredContextItemType(type.getPrimaryType());
+ } catch (XPathException err) {
+ throw new IllegalArgumentException(err);
+ }
+
+ } else if (name.equals(FeatureKeys.XQUERY_SCHEMA_AWARE)) {
+ getDefaultStaticQueryContext().setSchemaAware(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.XQUERY_STATIC_ERROR_LISTENER_CLASS)) {
+ getDefaultStaticQueryContext().setErrorListener(
+ (ErrorListener) instantiateClassName(name, value, ErrorListener.class));
+
+ } else if (name.equals(FeatureKeys.XQUERY_VERSION)) {
+ if ("1.1".equals(value)) {
+ value = "3.0";
+ }
+ DecimalValue version;
+ try {
+ version = (DecimalValue) DecimalValue.makeDecimalValue(value.toString(), true);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("XQuery version");
+ }
+ getDefaultStaticQueryContext().setLanguageVersion(version);
+
+ } else if (name.equals(FeatureKeys.XML_VERSION) || name.equals("http://saxon.sf.bet/feature/xml-version")) {
+ // spelling mistake retained for backwards compatibility with 8.9 and earlier
+ String s = requireString(name, value);
+ if (!(s.equals("1.0") || s.equals("1.1"))) {
+ throw new IllegalArgumentException(
+ "XML_VERSION value must be \"1.0\" or \"1.1\" as a String");
+
+ }
+ setXMLVersion((s.equals("1.0") ? XML10 : XML11));
+
+ } else if (name.equals(FeatureKeys.XSD_VERSION)) {
+ String s = requireString(name, value);
+ if (!(s.equals("1.0") || s.equals("1.1"))) {
+ throw new IllegalArgumentException(
+ "XSD_VERSION value must be \"1.0\" or \"1.1\" as a String");
+
+ }
+ xsdVersion = ((value.equals("1.0") ? XSD10 : XSD11));
+ theConversionRules = null;
+ } else if (name.equals(FeatureKeys.XSLT_INITIAL_MODE)) {
+ String s = requireString(name, value);
+ getDefaultXsltCompilerInfo().setDefaultInitialMode(StructuredQName.fromClarkName(s));
+
+ } else if (name.equals(FeatureKeys.XSLT_INITIAL_TEMPLATE)) {
+ String s = requireString(name, value);
+ getDefaultXsltCompilerInfo().setDefaultInitialTemplate(StructuredQName.fromClarkName(s));
+
+ } else if (name.equals(FeatureKeys.XSLT_SCHEMA_AWARE)) {
+ getDefaultXsltCompilerInfo().setSchemaAware(requireBoolean(name, value));
+
+ } else if (name.equals(FeatureKeys.XSLT_STATIC_ERROR_LISTENER_CLASS)) {
+ getDefaultXsltCompilerInfo().setErrorListener(
+ (ErrorListener) instantiateClassName(name, value, ErrorListener.class));
+
+ } else if (name.equals(FeatureKeys.XSLT_STATIC_URI_RESOLVER_CLASS)) {
+ getDefaultXsltCompilerInfo().setURIResolver(
+ (URIResolver) instantiateClassName(name, value, URIResolver.class));
+
+ } else if (name.equals(FeatureKeys.XSLT_VERSION)) {
+ try {
+ if ("2.1".equals(value)) {
+ value = "3.0";
+ }
+ ConversionResult vn = DecimalValue.makeDecimalValue(requireString(name, value), true);
+ DecimalValue dv = (DecimalValue) vn.asAtomic();
+ getDefaultXsltCompilerInfo().setXsltVersion(dv);
+ } catch (ValidationException e) {
+ throw new IllegalArgumentException("XSLT version must be a decimal number");
+ }
+
+ } else {
+ throw new IllegalArgumentException("Unknown configuration option " + name);
+ }
+ }
+
+ /**
+ * Validate a property value where the required type is boolean
+ *
+ * @param propertyName the name of the property
+ * @param value the supplied value of the property. This may be either a java.lang.Boolean, or a string
+ * taking one of the values on|off, true|false, yes|no, or 1|0 (suited to the conventions of different
+ * configuration APIs that end up calling this method)
+ * @return the value as a boolean
+ * @throws IllegalArgumentException if the supplied value cannot be validated as a recognized boolean value
+ */
+
+ protected boolean requireBoolean(String propertyName, Object value) {
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ } else if (value instanceof String) {
+ if ("true".equals(value) || "on".equals(value) || "yes".equals(value) || "1".equals(value)) {
+ return true;
+ } else if ("false".equals(value) || "off".equals(value) || "no".equals(value) || "0".equals(value)) {
+ return false;
+ } else {
+ throw new IllegalArgumentException(propertyName + " must be 'true' or 'false' (or on|off, yes|no, 1|0)");
+ }
+ } else {
+ throw new IllegalArgumentException(propertyName + " must be a boolean (or a string representing a boolean)");
+ }
+ }
+
+ /**
+ * Set a boolean property value, without checking that it is a recognized property name
+ * @param propertyName the name of the property to be set
+ * @param value a representation of the boolean value.
+ * This may be either a java.lang.Boolean, or a string
+ * taking one of the values on|off, true|false, yes|no, or 1|0 (suited to the conventions of different
+ * configuration APIs that end up calling this method)
+ */
+
+ protected void internalSetBooleanProperty(String propertyName, Object value) {
+ boolean b = requireBoolean(propertyName, value);
+ if (b) {
+ enabledProperties.add(propertyName);
+ } else {
+ enabledProperties.remove(propertyName);
+ }
+ }
+
+ /**
+ * Get a boolean property of the configuration
+ *
+ * @param propertyName the name of the required property. See the class {@link FeatureKeys} for
+ * constants representing the property names that can be requested. This class only recognizes
+ * properties whose type is boolean.
+ * @return the value of the property. In the case of an unrecognized property name, the value returned is
+ * false: no error is thrown.
+ */
+
+ public boolean getBooleanProperty(String propertyName) {
+ return enabledProperties.contains(propertyName);
+ }
+
+ /**
+ * Set a boolean property of the configuration
+ *
+ * @param propertyName the name of the required property. See the class {@link FeatureKeys} for
+ * constants representing the property names that can be requested. This class only recognizes
+ * properties whose type is boolean.
+ * @param value the value of the property.
+ * @throws IllegalArgumentException if the property name is not recognized (as a property whose expected
+ * value is boolean)
+ */
+
+ public void setBooleanProperty(String propertyName, boolean value) {
+ setConfigurationProperty(propertyName, value);
+ }
+
+ protected String requireString(String propertyName, Object value) {
+ if (value instanceof String) {
+ return ((String) value);
+ } else {
+ throw new IllegalArgumentException("The value of " + propertyName + " must be a string");
+ }
+ }
+
+
+ protected Object instantiateClassName(String propertyName, Object value, Class requiredClass) {
+ if (!(value instanceof String)) {
+ throw new IllegalArgumentException(
+ propertyName + " must be a String");
+ }
+ try {
+ Object obj = getInstance((String) value, null);
+ if (!requiredClass.isAssignableFrom(obj.getClass())) {
+ throw new IllegalArgumentException("Error in " + propertyName +
+ ": Class " + value + " does not implement " + requiredClass.getName());
+ }
+ return obj;
+ } catch (XPathException err) {
+ throw new IllegalArgumentException(
+ "Cannot use " + value + " as the value of " + propertyName + ". " + err.getMessage());
+ }
+ }
+
+ /**
+ * Get a property of the configuration
+ *
+ * @param name the name of the required property. See the class {@link FeatureKeys} for
+ * constants representing the property names that can be requested.
+ * @return the value of the property. Note that boolean values are returned as a Boolean,
+ * even if the value was supplied as a string (for example "true" or "on").
+ * @throws IllegalArgumentException thrown if the property is not one that Saxon recognizes.
+ */
+
+ /*@NotNull*/
+ public Object getConfigurationProperty(String name) {
+ if (name.equals(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.ALLOW_MULTITHREADING)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.COLLATION_URI_RESOLVER)) {
+ return getCollationURIResolver();
+
+ } else if (name.equals(FeatureKeys.COLLATION_URI_RESOLVER_CLASS)) {
+ return getCollationURIResolver().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.COLLECTION_URI_RESOLVER)) {
+ return getCollectionURIResolver();
+
+ } else if (name.equals(FeatureKeys.COLLECTION_URI_RESOLVER_CLASS)) {
+ return getCollectionURIResolver().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.GENERATE_BYTE_CODE)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.COMPILE_WITH_TRACING)) {
+ return isCompileWithTracing();
+
+ } else if (name.equals(FeatureKeys.DEFAULT_COLLATION)) {
+ return getCollationMap().getDefaultCollationName();
+
+ } else if (name.equals(FeatureKeys.DEFAULT_COLLECTION)) {
+ return getDefaultCollection();
+
+ } else if (name.equals(FeatureKeys.DEFAULT_COUNTRY)) {
+ return getDefaultCountry();
+
+ } else if (name.equals(FeatureKeys.DEFAULT_LANGUAGE)) {
+ return getDefaultLanguage();
+
+ } else if (name.equals(FeatureKeys.DTD_VALIDATION)) {
+ return isValidation();
+
+ } else if (name.equals(FeatureKeys.DTD_VALIDATION_RECOVERABLE)) {
+ return defaultParseOptions.getDTDValidationMode() == Validation.LAX;
+
+ } else if (name.equals(FeatureKeys.ERROR_LISTENER_CLASS)) {
+ return getErrorListener().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.ENTITY_RESOLVER_CLASS)) {
+ EntityResolver er = defaultParseOptions.getEntityResolver();
+ if (er == null) {
+ return "";
+ } else {
+ return er.getClass().getName();
+ }
+
+ } else if (name.equals(FeatureKeys.ENVIRONMENT_VARIABLE_RESOLVER)) {
+ return environmentVariableResolver;
+
+ } else if (name.equals(FeatureKeys.ENVIRONMENT_VARIABLE_RESOLVER_CLASS)) {
+ return environmentVariableResolver.getClass().getName();
+
+ } else if (name.equals(FeatureKeys.EXPAND_ATTRIBUTE_DEFAULTS)) {
+ return isExpandAttributeDefaults();
+
+ } else if (name.equals(FeatureKeys.IGNORE_SAX_SOURCE_PARSER)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.LAZY_CONSTRUCTION_MODE)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.LINE_NUMBERING)) {
+ return isLineNumbering();
+
+ } else if (name.equals(FeatureKeys.MESSAGE_EMITTER_CLASS)) {
+ return getMessageEmitterClass();
+
+ } else if (name.equals(FeatureKeys.MODULE_URI_RESOLVER)) {
+ return getModuleURIResolver();
+
+ } else if (name.equals(FeatureKeys.MODULE_URI_RESOLVER_CLASS)) {
+ return getModuleURIResolver().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.NAME_POOL)) {
+ return getNamePool();
+
+ } else if (name.equals(FeatureKeys.OPTIMIZATION_LEVEL)) {
+ return "" + optimizationLevel;
+
+ } else if (name.equals(FeatureKeys.OUTPUT_URI_RESOLVER)) {
+ return getOutputURIResolver();
+
+ } else if (name.equals(FeatureKeys.OUTPUT_URI_RESOLVER_CLASS)) {
+ return getOutputURIResolver().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.PRE_EVALUATE_DOC_FUNCTION)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.PREFER_JAXP_PARSER)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.RECOGNIZE_URI_QUERY_PARAMETERS)) {
+ return getSystemURIResolver().queryParametersAreRecognized();
+
+ } else if (name.equals(FeatureKeys.RECOVERY_POLICY)) {
+ return getRecoveryPolicy();
+
+ } else if (name.equals(FeatureKeys.RECOVERY_POLICY_NAME)) {
+ switch (getRecoveryPolicy()) {
+ case RECOVER_SILENTLY:
+ return "recoverSilently";
+ case RECOVER_WITH_WARNINGS:
+ return "recoverWithWarnings";
+ case DO_NOT_RECOVER:
+ return "doNotRecover";
+ default:
+ throw new IllegalStateException();
+ }
+
+ } else if (name.equals(FeatureKeys.SCHEMA_URI_RESOLVER)) {
+ return getSchemaURIResolver();
+
+ } else if (name.equals(FeatureKeys.SCHEMA_URI_RESOLVER_CLASS)) {
+ return getSchemaURIResolver().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.SCHEMA_VALIDATION)) {
+ return getSchemaValidationMode();
+
+ } else if (name.equals(FeatureKeys.SCHEMA_VALIDATION_MODE)) {
+ return Validation.toString(getSchemaValidationMode());
+
+ } else if (name.equals(FeatureKeys.SERIALIZER_FACTORY_CLASS)) {
+ return getSerializerFactory().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.SOURCE_PARSER_CLASS)) {
+ return getSourceParserClass();
+
+ } else if (name.equals(FeatureKeys.SOURCE_RESOLVER_CLASS)) {
+ return getSourceResolver().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.STRIP_WHITESPACE)) {
+ int s = getStripsWhiteSpace();
+ if (s == Whitespace.ALL) {
+ return "all";
+ } else if (s == Whitespace.IGNORABLE) {
+ return "ignorable";
+ } else {
+ return "none";
+ }
+
+ } else if (name.equals(FeatureKeys.STYLE_PARSER_CLASS)) {
+ return getStyleParserClass();
+
+ } else if (name.equals(FeatureKeys.TIMING)) {
+ return isTiming();
+
+ } else if (name.equals(FeatureKeys.TRACE_LISTENER)) {
+ return traceListener;
+
+ } else if (name.equals(FeatureKeys.TRACE_LISTENER_CLASS)) {
+ return traceListenerClass;
+
+ } else if (name.equals(FeatureKeys.TRACE_EXTERNAL_FUNCTIONS)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.TRACE_OPTIMIZER_DECISIONS)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.TREE_MODEL)) {
+ return getTreeModel();
+
+ } else if (name.equals(FeatureKeys.TREE_MODEL_NAME)) {
+ switch (getTreeModel()) {
+ case Builder.TINY_TREE:
+ default:
+ return "tinyTree";
+ case Builder.TINY_TREE_CONDENSED:
+ return "tinyTreeCondensed";
+ case Builder.LINKED_TREE:
+ return "linkedTree";
+ }
+
+ } else if (name.equals(FeatureKeys.URI_RESOLVER_CLASS)) {
+ return getURIResolver().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.USE_PI_DISABLE_OUTPUT_ESCAPING)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.USE_TYPED_VALUE_CACHE)) {
+ return getBooleanProperty(name);
+
+ } else if (name.equals(FeatureKeys.USE_XSI_SCHEMA_LOCATION)) {
+ return defaultParseOptions.isUseXsiSchemaLocation();
+
+ } else if (name.equals(FeatureKeys.VALIDATION_COMMENTS)) {
+ return defaultParseOptions.isAddCommentsAfterValidationErrors();
+
+ } else if (name.equals(FeatureKeys.VALIDATION_WARNINGS)) {
+ return isValidationWarnings();
+
+ } else if (name.equals(FeatureKeys.VERSION_WARNING)) {
+ return isVersionWarning();
+
+ } else if (name.equals(FeatureKeys.XINCLUDE)) {
+ return isXIncludeAware();
+
+ } else if (name.equals(FeatureKeys.XML_VERSION)) {
+ // Spelling mistake retained for backwards compatibility with 8.9 and earlier
+ return (getXMLVersion() == XML10 ? "1.0" : "1.1");
+
+ } else if (name.equals(FeatureKeys.XQUERY_ALLOW_UPDATE)) {
+ return getDefaultStaticQueryContext().isUpdatingEnabled();
+
+ } else if (name.equals(FeatureKeys.XQUERY_CONSTRUCTION_MODE)) {
+ return getDefaultStaticQueryContext().getConstructionMode();
+
+ } else if (name.equals(FeatureKeys.XQUERY_DEFAULT_ELEMENT_NAMESPACE)) {
+ return getDefaultStaticQueryContext().getDefaultElementNamespace();
+
+ } else if (name.equals(FeatureKeys.XQUERY_DEFAULT_FUNCTION_NAMESPACE)) {
+ return getDefaultStaticQueryContext().getDefaultFunctionNamespace();
+
+ } else if (name.equals(FeatureKeys.XQUERY_EMPTY_LEAST)) {
+ return getDefaultStaticQueryContext().isEmptyLeast();
+
+ } else if (name.equals(FeatureKeys.XQUERY_INHERIT_NAMESPACES)) {
+ return getDefaultStaticQueryContext().isInheritNamespaces();
+
+ } else if (name.equals(FeatureKeys.XQUERY_PRESERVE_BOUNDARY_SPACE)) {
+ return getDefaultStaticQueryContext().isPreserveBoundarySpace();
+
+ } else if (name.equals(FeatureKeys.XQUERY_PRESERVE_NAMESPACES)) {
+ return getDefaultStaticQueryContext().isPreserveNamespaces();
+
+ } else if (name.equals(FeatureKeys.XQUERY_REQUIRED_CONTEXT_ITEM_TYPE)) {
+ return getDefaultStaticQueryContext().getRequiredContextItemType();
+
+ } else if (name.equals(FeatureKeys.XQUERY_SCHEMA_AWARE)) {
+ return getDefaultStaticQueryContext().isSchemaAware();
+
+ } else if (name.equals(FeatureKeys.XQUERY_STATIC_ERROR_LISTENER_CLASS)) {
+ return getDefaultStaticQueryContext().getErrorListener().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.XQUERY_VERSION)) {
+ return getDefaultStaticQueryContext().getLanguageVersion();
+
+ } else if (name.equals(FeatureKeys.XSD_VERSION)) {
+ return (xsdVersion == XSD10 ? "1.0" : "1.1");
+
+ } else if (name.equals(FeatureKeys.XSLT_INITIAL_MODE)) {
+ return getDefaultXsltCompilerInfo().getDefaultInitialMode().getClarkName();
+
+ } else if (name.equals(FeatureKeys.XSLT_INITIAL_TEMPLATE)) {
+ return getDefaultXsltCompilerInfo().getDefaultInitialTemplate().getClarkName();
+
+ } else if (name.equals(FeatureKeys.XSLT_SCHEMA_AWARE)) {
+ return defaultXsltCompilerInfo.isSchemaAware();
+
+ } else if (name.equals(FeatureKeys.XSLT_STATIC_ERROR_LISTENER_CLASS)) {
+ return getDefaultXsltCompilerInfo().getErrorListener().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.XSLT_STATIC_URI_RESOLVER_CLASS)) {
+ return getDefaultXsltCompilerInfo().getURIResolver().getClass().getName();
+
+ } else if (name.equals(FeatureKeys.XSLT_VERSION)) {
+ return getDefaultXsltCompilerInfo().getXsltVersion().toString();
+
+ } else {
+ throw new IllegalArgumentException("Unknown attribute " + name);
+ }
+ }
+
+
+
+ /**
+ * Ask whether bytecode should be generated. The default setting
+ * is true in Saxon Enterprise Edition and false in all other cases. Setting the option to
+ * true has no effect if Saxon-EE is not available (but if it is set to true, this method will
+ * return true). Setting the option to false in Saxon-EE
+ * is permitted if for some reason bytecode generation is to be suppressed (one possible reason
+ * is to improve compilation performance at the expense of evaluation performance).
+ * @param hostLanguage one of XSLT or XQUERY
+ * @return true if the option is switched on
+ */
+
+ public boolean isGenerateByteCode(int hostLanguage) {
+ return getBooleanProperty(FeatureKeys.GENERATE_BYTE_CODE) &&
+ isLicensedFeature(hostLanguage == XSLT ?
+ LicenseFeature.ENTERPRISE_XSLT :
+ LicenseFeature.ENTERPRISE_XQUERY);
+ }
+
+ /**
+ * Called by the garbage collector on an object when garbage collection
+ * determines that there are no more references to the object. This implementation
+ * closes the error output file if one has been allocated.
+ */
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ if (standardErrorOutput != System.err) {
+ standardErrorOutput.close();
+ }
+ }
+
+ /**
+ * This class contains constants representing features of the software that may or may
+ * not be licensed. (Note, this list is at a finer-grained level than the actual
+ * purchasing options.)
+ */
+
+ public static class LicenseFeature {
+ public static final int SCHEMA_VALIDATION = 1;
+ public static final int ENTERPRISE_XSLT = 2;
+ public static final int ENTERPRISE_XQUERY = 4;
+ public static final int PROFESSIONAL_EDITION = 8;
+ }
+
+ /**
+ * Make use of Preprocessor directives to create
+ * a Platform class depending on the platform choice
+ *
+ * @return an Platform class
+ */
+ public static Platform makePlatform(){
+ Platform platformChoice;
+//#if DOTNET==true
+ //#if EE==true || PE==true
+ platformChoice = new DotNetPlatformPE();
+ //#else
+ platformChoice = new DotNetPlatform();
+ //#endif
+//#else
+ platformChoice = new JavaPlatform();
+//#endif
+//#if PE==true && DOTNET != true
+ platformChoice = new JavaPlatformPE();
+//#endif
+//#if EE==true && DOTNET != true
+ platformChoice = new JavaPlatformPE();
+//#endif
+
+ return platformChoice;
+ }
+
+ public static Class<? extends Configuration> makeConfigurationClass() {
+ Class<? extends Configuration> configClass;
+ configClass = Configuration.class;
+//#if PE==true
+ configClass = ProfessionalConfiguration.class;
+//#endif
+//#if EE==true
+ configClass = EnterpriseConfiguration.class;
+//#endif
+ return configClass;
+ }
+
+
+ public static String setSoftwareEdition() {
+ String softwareEditionTemp = "HE";
+//#if EE==true
+ softwareEditionTemp = "EE";
+//#endif
+
+//#if PE==true
+ softwareEditionTemp = "PE";
+//#endif
+ return softwareEditionTemp;
+ }
+}
+
diff --git a/sf/saxon/Controller.java b/sf/saxon/Controller.java
new file mode 100644
index 0000000..0d9b760
--- /dev/null
+++ b/sf/saxon/Controller.java
@@ -0,0 +1,2659 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.*;
+import net.sf.saxon.expr.ErrorExpression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.functions.EscapeURI;
+import net.sf.saxon.lib.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.serialize.Emitter;
+import net.sf.saxon.serialize.ImplicitResultChecker;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trace.TraceEventMulticaster;
+import net.sf.saxon.trans.*;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.wrapper.SpaceStrippedDocument;
+import net.sf.saxon.tree.wrapper.TypeStrippedDocument;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.DateTimeValue;
+import net.sf.saxon.value.SingletonClosure;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import java.io.*;
+import java.util.*;
+
+/**
+ * The Controller is Saxon's implementation of the JAXP Transformer class, and represents
+ * an executing instance of a transformation or query. Multiple concurrent executions of
+ * the same transformation or query will use different Controller instances. This class is
+ * therefore not thread-safe.
+ * <p/>
+ * The Controller is serially reusable, as required by JAXP: when one transformation or query
+ * is finished, it can be used to run another. However, there is no advantage in doing this
+ * rather than allocating a new Controller each time.
+ * <p/>
+ * The Controller can also be used when running Java applications that use neither XSLT nor
+ * XQuery. A dummy Controller is created when running free-standing XPath expressions.
+ * <p/>
+ * The Controller holds those parts of the dynamic context that do not vary during the course
+ * of a transformation or query, or that do not change once their value has been computed.
+ * This also includes those parts of the static context that are required at run-time.
+ * <p/>
+ * Wherever possible XSLT applications should use the JAXP Transformer class directly,
+ * rather than relying on Saxon-specific methods in the Controller. However, some
+ * features are currently available only through this class. This applies especially
+ * to new features specific to XSLT 2.0, since the JAXP interface still supports
+ * only XSLT 1.0. Such methods may be superseded in the future by JAXP methods.
+ * <p/>
+ * Many methods on the Controller are designed for internal use and should not be
+ * considered stable. From release 8.4 onwards, those methods that are considered sufficiently
+ * stable to constitute path of the Saxon public API are labelled with the JavaDoc tag "since":
+ * the value indicates the release at which the method was added to the public API.
+ *
+ * @author Michael H. Kay
+ * @since 8.4
+ */
+
+public class Controller extends Transformer {
+
+ private Configuration config;
+ /*@Nullable*/ private Item initialContextItem;
+ /*@Nullable*/ private Item contextForGlobalVariables;
+ private Bindery bindery; // holds values of global and local variables
+ private NamePool namePool;
+ private String messageReceiverClassName;
+ /*@Nullable*/ private Receiver messageReceiver;
+ private RuleManager ruleManager;
+ /*@Nullable*/ private Properties localOutputProperties;
+ /*@Nullable*/ private GlobalParameterSet parameters;
+ private PreparedStylesheet preparedStylesheet;
+ /*@Nullable*/ private TraceListener traceListener;
+ private boolean tracingPaused;
+ private PrintStream traceFunctionDestination;
+ private URIResolver standardURIResolver;
+ private URIResolver userURIResolver;
+ /*@Nullable*/ private Result principalResult;
+ /*@Nullable*/ private String principalResultURI;
+ /*@Nullable*/ private String cookedPrincipalResultURI;
+ private boolean thereHasBeenAnExplicitResultDocument;
+ /*@Nullable*/ private OutputURIResolver outputURIResolver;
+ private UnparsedTextURIResolver unparsedTextResolver;
+ /*@Nullable*/ private SchemaURIResolver schemaURIResolver;
+ /*@Nullable*/ private CollectionURIResolver collectionURIResolver;
+ /*@Nullable*/ private String defaultCollectionURI;
+ private ErrorListener errorListener;
+ private int recoveryPolicy;
+ private Executable executable;
+ private TreeModel treeModel = TreeModel.TINY_TREE;
+ /*@Nullable*/ private Template initialTemplate = null;
+ /*@Nullable*/ private HashSet<DocumentURI> allOutputDestinations;
+ private DocumentPool sourceDocumentPool;
+ /*@Nullable*/ private SequenceOutputter reusableSequenceOutputter = null;
+ private HashMap<String, Object> userDataTable;
+ /*@Nullable*/ private DateTimeValue currentDateTime;
+ private boolean dateTimePreset = false;
+ /*@Nullable*/ private StructuredQName initialMode = null;
+ /*@Nullable*/ private NodeInfo lastRememberedNode = null;
+ private int lastRememberedNumber = -1;
+ /*@Nullable*/ private ClassLoader classLoader;
+ /*@Nullable*/ private PathMap pathMap = null;
+ private int validationMode;
+ private boolean inUse = false;
+ private boolean stripSourceTrees = true;
+// private XPathException errFound = null;
+// private List<Thread> asyncTaskList = new ArrayList<Thread>(5);
+
+ public final static String ANONYMOUS_PRINCIPAL_OUTPUT_URI = "dummy:/anonymous/principal/result";
+
+
+
+ /**
+ * Create a Controller and initialise variables. Note: XSLT applications should
+ * create the Controller by using the JAXP newTransformer() method, or in S9API
+ * by using XsltExecutable.load()
+ *
+ * @param config The Configuration used by this Controller
+ */
+
+ public Controller(Configuration config) {
+ this.config = config;
+ // create a dummy executable
+ executable = new Executable(config);
+ executable.setHostLanguage(config.getHostLanguage(), false);
+ sourceDocumentPool = new DocumentPool();
+ reset();
+ }
+
+ /**
+ * Create a Controller and initialise variables.
+ *
+ * @param config The Configuration used by this Controller
+ * @param executable The executable used by this Controller
+ */
+
+ public Controller(Configuration config, Executable executable) {
+ this.config = config;
+ this.executable = executable;
+ sourceDocumentPool = new DocumentPool();
+ reset();
+ }
+
+ /**
+ * <p>Reset this <code>Transformer</code> to its original configuration.</p>
+ * <p/>
+ * <p><code>Transformer</code> is reset to the same state as when it was created with
+ * {@link javax.xml.transform.TransformerFactory#newTransformer()},
+ * {@link javax.xml.transform.TransformerFactory#newTransformer(javax.xml.transform.Source source)} or
+ * {@link javax.xml.transform.Templates#newTransformer()}.
+ * <code>reset()</code> is designed to allow the reuse of existing <code>Transformer</code>s
+ * thus saving resources associated with the creation of new <code>Transformer</code>s.</p>
+ * <p>
+ * <i>The above is from the JAXP specification. With Saxon, it's unlikely that reusing a Transformer will
+ * give any performance benefits over creating a new one. The one case where it might be beneficial is
+ * to reuse the document pool (the set of documents that have been loaded using the doc() or document()
+ * functions). Therefore, this method does not clear the document pool. If you want to clear the document
+ * pool, call the method {@link #clearDocumentPool} as well.</i>
+ * <p/>
+ * <p>The reset <code>Transformer</code> is not guaranteed to have the same {@link javax.xml.transform.URIResolver}
+ * or {@link javax.xml.transform.ErrorListener} <code>Object</code>s, e.g. {@link Object#equals(Object obj)}.
+ * It is guaranteed to have a functionally equal <code>URIResolver</code>
+ * and <code>ErrorListener</code>.</p>
+ *
+ * @since 1.5
+ */
+
+ public void reset() {
+ bindery = new Bindery();
+ namePool = config.getNamePool();
+ standardURIResolver = config.getSystemURIResolver();
+ userURIResolver = config.getURIResolver();
+ outputURIResolver = config.getOutputURIResolver();
+ schemaURIResolver = config.getSchemaURIResolver();
+ unparsedTextResolver = new StandardUnparsedTextResolver();
+ errorListener = config.getErrorListener();
+ recoveryPolicy = config.getRecoveryPolicy();
+ validationMode = config.getSchemaValidationMode();
+ if (errorListener instanceof StandardErrorListener) {
+ // if using a standard error listener, make a fresh one
+ // for each transformation, because it is stateful - and also because the
+ // host language is now known (a Configuration can serve multiple host languages)
+ PrintStream ps = ((StandardErrorListener) errorListener).getErrorOutput();
+ errorListener = ((StandardErrorListener) errorListener).makeAnother(executable.getHostLanguage());
+ ((StandardErrorListener) errorListener).setErrorOutput(ps);
+ ((StandardErrorListener) errorListener).setRecoveryPolicy(recoveryPolicy);
+ }
+
+ traceListener = null;
+ traceFunctionDestination = config.getStandardErrorOutput();
+ TraceListener tracer;
+ try {
+ tracer = config.makeTraceListener();
+ } catch (XPathException err) {
+ throw new IllegalStateException(err.getMessage());
+ }
+ if (tracer != null) {
+ addTraceListener(tracer);
+ }
+
+ setModel(config.getParseOptions().getModel());
+
+ contextForGlobalVariables = null;
+ messageReceiver = null;
+ localOutputProperties = null;
+ parameters = null;
+ currentDateTime = null;
+ dateTimePreset = false;
+ initialContextItem = null;
+ initialMode = null;
+ initialTemplate = null;
+ classLoader = null;
+ clearPerTransformationData();
+ }
+
+ /**
+ * Reset variables that need to be reset for each transformation if the controller
+ * is serially reused
+ */
+
+ private void clearPerTransformationData() {
+ userDataTable = new HashMap<String, Object>(20);
+ principalResult = null;
+ //principalResultURI = null;
+ allOutputDestinations = null;
+ thereHasBeenAnExplicitResultDocument = false;
+ lastRememberedNode = null;
+ lastRememberedNumber = -1;
+ tracingPaused = false;
+ }
+
+ /**
+ * Get the Configuration associated with this Controller. The Configuration holds
+ * settings that potentially apply globally to many different queries and transformations.
+ *
+ * @return the Configuration object
+ * @since 8.4
+ */
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Set the initial mode for the transformation.
+ * <p/>
+ * XSLT 2.0 allows a transformation to be started in a mode other than the default mode.
+ * The transformation then starts by looking for the template rule in this mode that best
+ * matches the initial context node.
+ * <p/>
+ * This method may eventually be superseded by a standard JAXP method.
+ *
+ * @param expandedModeName the name of the initial mode. The mode is
+ * supplied as an expanded QName, that is "localname" if there is no
+ * namespace, or "{uri}localname" otherwise. If the value is null or zero-length,
+ * the initial mode is reset to the unnamed default mode.
+ * @since 8.4
+ */
+
+ public void setInitialMode(/*@Nullable*/ String expandedModeName) {
+ if (expandedModeName == null || expandedModeName.length() == 0) {
+ initialMode = null;
+ } else {
+ initialMode = StructuredQName.fromClarkName(expandedModeName);
+ }
+ }
+
+ /**
+ * Get the name of the initial mode for the transformation
+ *
+ * @return the name of the initial mode, as a name in Clark format; null indicates
+ * that the initial mode is the unnamed mode.
+ */
+
+ public String getInitialModeName() {
+ return initialMode.getClarkName();
+ }
+
+ /**
+ * Get the initial mode for the transformation
+ *
+ * @return the initial mode. This will be the default/unnamed mode if no specific mode
+ * has been requested
+ */
+
+ /*@NotNull*/
+ public Mode getInitialMode() {
+ return preparedStylesheet.getRuleManager().getMode(initialMode, false);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Methods for managing output destinations and formatting
+ ////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Set the output properties for the transformation. These
+ * properties will override properties set in the templates
+ * with xsl:output.
+ * <p/>
+ * As well as the properties defined in the JAXP OutputKeys class,
+ * Saxon defines an additional set of properties in {@link SaxonOutputKeys}.
+ * These fall into two categories: Constants representing serialization
+ * properties defined in XSLT 2.0 (which are not yet supported by JAXP),
+ * and constants supporting Saxon extensions to the set of serialization
+ * properties.
+ *
+ * @param properties the output properties to be used for the
+ * transformation. If the value is null, the properties are reset to
+ * be the properties of the Templates object (that is, for XSLT 2.0,
+ * the properties set in the unnamed xsl:output object).
+ * @throws IllegalArgumentException if any of the properties are invalid (other than
+ * properties in a user-defined namespace)
+ * @see SaxonOutputKeys
+ * @since 8.4
+ */
+
+ public void setOutputProperties(/*@Nullable*/ Properties properties) {
+ if (properties == null) {
+ localOutputProperties = null;
+ } else {
+ Enumeration keys = properties.propertyNames();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ setOutputProperty(key, properties.getProperty(key));
+ }
+ }
+ }
+
+ /**
+ * Get the output properties for the transformation.
+ * <p/>
+ * As well as the properties defined in the JAXP OutputKeys class,
+ * Saxon defines an additional set of properties in {@link SaxonOutputKeys}.
+ * These fall into two categories: Constants representing serialization
+ * properties defined in XSLT 2.0 (which are not yet supported by JAXP),
+ * and constants supporting Saxon extensions to the set of serialization
+ * properties.
+ *
+ * @return the output properties being used for the transformation,
+ * including properties defined in the stylesheet for the unnamed
+ * output format
+ * @see SaxonOutputKeys
+ * @since 8.4
+ */
+
+ public Properties getOutputProperties() {
+ if (localOutputProperties == null) {
+ if (executable == null) {
+ return new Properties();
+ } else {
+ localOutputProperties = new Properties(executable.getDefaultOutputProperties());
+ }
+ }
+
+ // Make a copy, so that modifications to the returned properties object have no effect (even on the
+ // local output properties)
+
+ Properties newProps = new Properties();
+ Enumeration keys = localOutputProperties.propertyNames();
+ while (keys.hasMoreElements()) {
+ String key = (String) keys.nextElement();
+ newProps.setProperty(key, localOutputProperties.getProperty(key));
+ }
+ return newProps;
+ }
+
+ /**
+ * Set an output property for the transformation.
+ * <p/>
+ * <p>As well as the properties defined in the JAXP OutputKeys class,
+ * Saxon defines an additional set of properties in {@link SaxonOutputKeys}.
+ * These fall into two categories: Constants representing serialization
+ * properties defined in XSLT 2.0 (which are not yet supported by JAXP),
+ * and constants supporting Saxon extensions to the set of serialization
+ * properties.</p>
+ * <p/>
+ * <p>As an extension to the JAXP specification, supplying the value "" (a zero-length
+ * string) for any parameter has the effect of cancelling any value defined in the
+ * stylesheet or query; for example setting doctype-system or doctype-public to ""
+ * causes the serializer to be run with these properties taken as absent. This rule
+ * does not apply to parameters where "" is a meaningful value, for example cdata-section-elements.</p>
+ *
+ * @param name the name of the property
+ * @param value the value of the property
+ * @throws IllegalArgumentException if the property is invalid (except for
+ * properties in a user-defined namespace)
+ * @see SaxonOutputKeys
+ * @since 8.4
+ */
+
+ public void setOutputProperty(String name, String value) {
+ if (localOutputProperties == null) {
+ localOutputProperties = getOutputProperties();
+ }
+ try {
+ SaxonOutputKeys.checkOutputProperty(name, value, getConfiguration());
+ } catch (XPathException err) {
+ throw new IllegalArgumentException(err.getMessage());
+ }
+ localOutputProperties.setProperty(name, value);
+ }
+
+ /**
+ * Get the value of an output property.
+ * <p/>
+ * As well as the properties defined in the JAXP OutputKeys class,
+ * Saxon defines an additional set of properties in {@link net.sf.saxon.lib.SaxonOutputKeys}.
+ * These fall into two categories: Constants representing serialization
+ * properties defined in XSLT 2.0 (which are not yet supported by JAXP),
+ * and constants supporting Saxon extensions to the set of serialization
+ * properties.
+ *
+ * @param name the name of the requested property
+ * @return the value of the requested property
+ * @see SaxonOutputKeys
+ * @since 8.4
+ */
+
+ /*@Nullable*/
+ public String getOutputProperty(String name) {
+ try {
+ SaxonOutputKeys.checkOutputProperty(name, null, getConfiguration());
+ } catch (XPathException err) {
+ throw new IllegalArgumentException(err.getMessage());
+ }
+ if (localOutputProperties == null) {
+ if (executable == null) {
+ return null;
+ } else {
+ localOutputProperties = executable.getDefaultOutputProperties();
+ }
+ }
+ return localOutputProperties.getProperty(name);
+ }
+
+ /**
+ * Set the base output URI.
+ * <p/>
+ * <p>This defaults to the system ID of the Result object for the principal output
+ * of the transformation if this is known; if it is not known, it defaults
+ * to the current directory.</p>
+ * <p/>
+ * <p> The base output URI is used for resolving relative URIs in the <code>href</code> attribute
+ * of the <code>xsl:result-document</code> instruction.</p>
+ *
+ * @param uri the base output URI
+ * @since 8.4
+ */
+
+ public void setBaseOutputURI(String uri) {
+ principalResultURI = uri;
+ }
+
+ /**
+ * Get the base output URI.
+ * <p/>
+ * <p>This returns the value set using the {@link #setBaseOutputURI} method. If no value has been set
+ * explicitly, then the method returns null if called before the transformation, or the computed
+ * default base output URI if called after the transformation.</p>
+ * <p/>
+ * <p> The base output URI is used for resolving relative URIs in the <code>href</code> attribute
+ * of the <code>xsl:result-document</code> instruction.</p>
+ *
+ * @return the base output URI
+ * @since 8.4
+ */
+
+ /*@Nullable*/
+ public String getBaseOutputURI() {
+ return principalResultURI;
+ }
+
+ /**
+ * Get the base output URI after processing. The processing consists of (a) defaulting
+ * to the current user directory if no base URI is available and if the stylesheet is trusted,
+ * and (b) applying IRI-to-URI escaping
+ *
+ * @return the base output URI after processing.
+ */
+
+ /*@Nullable*/
+ public String getCookedBaseOutputURI() {
+ if (cookedPrincipalResultURI == null) {
+ String base = getBaseOutputURI();
+ if (base == null && config.getBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
+ // if calling external functions is allowed, then the stylesheet is trusted, so
+ // we allow it to write to files relative to the current directory
+ base = new File(System.getProperty("user.dir")).toURI().toString();
+ }
+ if (base != null) {
+ base = EscapeURI.iriToUri(base).toString();
+ }
+ cookedPrincipalResultURI = base;
+ }
+ return cookedPrincipalResultURI;
+ }
+
+ /**
+ * Get the principal result destination.
+ * <p>This method is intended for internal use only. It is typically called by Saxon during the course
+ * of a transformation, to discover the result that was supplied in the transform() call.</p>
+ *
+ * @return the Result object supplied as the principal result destination.
+ */
+
+ /*@Nullable*/
+ public Result getPrincipalResult() {
+ return principalResult;
+ }
+
+ /**
+ * Check that an output destination has not been used before, optionally adding
+ * this URI to the set of URIs that have been used.
+ *
+ * @param uri the URI to be used as the output destination
+ * @return true if the URI is available for use; false if it has already been used.
+ * <p/>
+ * This method is intended for internal use only.
+ */
+
+ public synchronized boolean checkUniqueOutputDestination(/*@Nullable*/ DocumentURI uri) {
+ if (uri == null) {
+ return true; // happens when writing say to an anonymous StringWriter
+ }
+ if (allOutputDestinations == null) {
+ allOutputDestinations = new HashSet<DocumentURI>(20);
+ }
+
+ return !allOutputDestinations.contains(uri);
+ }
+
+ /**
+ * Add a URI to the set of output destinations that cannot be written to, either because
+ * they have already been written to, or because they have been read
+ *
+ * @param uri A URI that is not available as an output destination
+ */
+
+ public void addUnavailableOutputDestination(DocumentURI uri) {
+ if (allOutputDestinations == null) {
+ allOutputDestinations = new HashSet<DocumentURI>(20);
+ }
+ allOutputDestinations.add(uri);
+ }
+
+ /**
+ * Remove a URI from the set of output destinations that cannot be written to or read from.
+ * Used to support saxon:discard-document()
+ *
+ * @param uri A URI that is being made available as an output destination
+ */
+
+ public void removeUnavailableOutputDestination(DocumentURI uri) {
+ if (allOutputDestinations != null) {
+ allOutputDestinations.remove(uri);
+ }
+ }
+
+
+ /**
+ * Determine whether an output URI is available for use. This method is intended
+ * for use by applications, via an extension function.
+ *
+ * @param uri A uri that the application is proposing to use in the href attribute of
+ * xsl:result-document: if this function returns false, then the xsl:result-document
+ * call will fail saying the URI has already been used.
+ * @return true if the URI is available for use. Note that this function is not "stable":
+ * it may return different results for the same URI at different points in the transformation.
+ */
+
+ public boolean isUnusedOutputDestination(DocumentURI uri) {
+ return allOutputDestinations == null || !allOutputDestinations.contains(uri);
+ }
+
+ /**
+ * Check whether an XSLT implicit result tree can be written. This is allowed only if no xsl:result-document
+ * has been written for the principal output URI
+ */
+
+ public void checkImplicitResultTree() throws XPathException {
+ String implicitURI = principalResultURI;
+ if (implicitURI == null) {
+ implicitURI = ANONYMOUS_PRINCIPAL_OUTPUT_URI;
+ }
+ //if (principalResultURI != null) {
+
+ DocumentURI documentURI = new DocumentURI(implicitURI);
+ synchronized (getDocumentPool()){
+ if (!checkUniqueOutputDestination(documentURI)) {
+ XPathException err = new XPathException(
+ "Cannot write an implicit result document if an explicit result document has been written to the same URI: " +
+ (implicitURI.equals(ANONYMOUS_PRINCIPAL_OUTPUT_URI) ?
+ "(no URI supplied)" : implicitURI));
+ err.setErrorCode("XTDE1490");
+ throw err;
+ } else {
+ addUnavailableOutputDestination(documentURI);
+ }
+ }
+ //}
+ }
+
+ /**
+ * Set that an explicit result tree has been written using xsl:result-document
+ */
+
+ public void setThereHasBeenAnExplicitResultDocument() {
+ thereHasBeenAnExplicitResultDocument = true;
+ }
+
+ /**
+ * Test whether an explicit result tree has been written using xsl:result-document
+ *
+ * @return true if the transformation has evaluated an xsl:result-document instruction
+ */
+
+ public boolean hasThereBeenAnExplicitResultDocument() {
+ return thereHasBeenAnExplicitResultDocument;
+ }
+
+ /**
+ * Allocate a SequenceOutputter for a new output destination. Reuse the existing one
+ * if it is available for reuse (this is designed to ensure that the TinyTree structure
+ * is also reused, creating a forest of trees all sharing the same data structure)
+ *
+ * @param size the estimated size of the output sequence
+ * @return SequenceOutputter the allocated SequenceOutputter
+ */
+
+ /*@NotNull*/
+ public synchronized SequenceOutputter allocateSequenceOutputter(int size) {
+ PipelineConfiguration pipe = makePipelineConfiguration();
+ SequenceOutputter so = reusableSequenceOutputter;
+ if (so != null) {
+ so.setPipelineConfiguration(pipe);
+ so.setSystemId(null); // Added 10.8.2009 - seems right, but doesn't solve EvaluateNodeTest problem
+ reusableSequenceOutputter = null;
+ return so;
+ } else {
+ return new SequenceOutputter(pipe, this, size);
+ }
+ }
+
+ /**
+ * Accept a SequenceOutputter that is now available for reuse
+ *
+ * @param out the SequenceOutputter that is available for reuse
+ */
+
+ public void reuseSequenceOutputter(SequenceOutputter out) {
+ reusableSequenceOutputter = out;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Set the initial named template to be used as the entry point.
+ * <p/>
+ * XSLT 2.0 allows a transformation to start by executing a named template, rather than
+ * by matching an initial context node in a source document. This method may eventually
+ * be superseded by a standard JAXP method once JAXP supports XSLT 2.0.
+ * <p/>
+ * Note that any parameters supplied using {@link #setParameter} are used as the values
+ * of global stylesheet parameters. There is no way to supply values for local parameters
+ * of the initial template.
+ *
+ * @param expandedName The expanded name of the template in {uri}local format, or null
+ * or a zero-length string to indicate that there should be no initial template.
+ * @throws XPathException if there is no named template with this name
+ * @since 8.4
+ */
+
+ public void setInitialTemplate(/*@Nullable*/ String expandedName) throws XPathException {
+ if (expandedName == null || expandedName.length() == 0) {
+ initialTemplate = null;
+ return;
+ }
+ StructuredQName qName = StructuredQName.fromClarkName(expandedName);
+ Template t = ((PreparedStylesheet) getExecutable()).getNamedTemplate(qName);
+ if (t == null) {
+ XPathException err = new XPathException("The requested initial template, with expanded name "
+ + expandedName + ", does not exist");
+ err.setErrorCode("XTDE0040");
+ reportFatalError(err);
+ throw err;
+ } else if (t.hasRequiredParams()) {
+ XPathException err = new XPathException("The named template "
+ + expandedName
+ + " has required parameters, so cannot be used as the entry point");
+ err.setErrorCode("XTDE0060");
+ reportFatalError(err);
+ throw err;
+ } else {
+ initialTemplate = t;
+ }
+ }
+
+ /**
+ * Get the initial template
+ *
+ * @return the name of the initial template, as an expanded name in Clark format if set, or null otherwise
+ * @since 8.7
+ */
+
+ /*@Nullable*/
+ public String getInitialTemplate() {
+ if (initialTemplate == null) {
+ return null;
+ } else {
+ return initialTemplate.getTemplateName().getClarkName();
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Make a PipelineConfiguration based on the properties of this Controller.
+ * <p/>
+ * This interface is intended primarily for internal use, although it may be necessary
+ * for applications to call it directly if they construct pull or push pipelines
+ *
+ * @return a newly constructed PipelineConfiguration holding a reference to this
+ * Controller as well as other configuration information.
+ */
+
+ /*@NotNull*/
+ public PipelineConfiguration makePipelineConfiguration() {
+ PipelineConfiguration pipe = new PipelineConfiguration(getConfiguration());
+ pipe.setURIResolver(userURIResolver == null ? standardURIResolver : userURIResolver);
+ pipe.setSchemaURIResolver(schemaURIResolver);
+ pipe.setExpandAttributeDefaults(getConfiguration().isExpandAttributeDefaults());
+ pipe.setParseOptions(new ParseOptions(config.getParseOptions()));
+ pipe.setErrorListener(getErrorListener());
+ pipe.setController(this);
+ final Executable executable = getExecutable();
+ if (executable != null) {
+ // can be null for an IdentityTransformer
+ pipe.setLocationProvider(executable.getLocationMap());
+ pipe.setHostLanguage(executable.getHostLanguage());
+ }
+ return pipe;
+ }
+
+ /**
+ * Make a Receiver to be used for xsl:message output.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return The newly constructed message Emitter
+ * @throws XPathException if any dynamic error occurs; in
+ * particular, if the registered MessageEmitter class is not an
+ * Emitter
+ */
+
+ /*@NotNull*/
+ private Receiver makeMessageReceiver() throws XPathException {
+ Object messageReceiver = config.getInstance(messageReceiverClassName, getClassLoader());
+ if (!(messageReceiver instanceof Receiver)) {
+ throw new XPathException(messageReceiverClassName + " is not a Receiver");
+ }
+ ((Receiver) messageReceiver).setPipelineConfiguration(makePipelineConfiguration());
+ setMessageEmitter((Receiver) messageReceiver);
+ return (Receiver) messageReceiver;
+ }
+
+ /**
+ * Set the Receiver to be used for xsl:message output.
+ * <p>
+ * Recent versions of the JAXP interface specify that by default the
+ * output of xsl:message is sent to the registered ErrorListener. Saxon
+ * does not implement this convention. Instead, the output is sent
+ * to a default message emitter, which is a slightly customised implementation
+ * of the standard Saxon Emitter interface.</p>
+ * <p>
+ * This interface can be used to change the way in which Saxon outputs
+ * xsl:message output.</p>
+ * <p>
+ * It is not necessary to use this interface in order to change the destination
+ * to which messages are written: that can be achieved by obtaining the standard
+ * message emitter and calling its {@link Emitter#setWriter} method.</p>
+ * <p>
+ * Although any <code>Receiver</code> can be supplied as the destination for messages,
+ * applications may find it convenient to implement a subclass of {@link net.sf.saxon.event.SequenceWriter},
+ * in which only the abstract <code>write()</code> method is implemented. This will have the effect that the
+ * <code>write()</code> method is called to output each message as it is generated, with the <code>Item</code>
+ * that is passed to the <code>write()</code> method being the document node at the root of an XML document
+ * containing the contents of the message.
+ * <p>
+ * This method is intended for use by advanced applications. The Receiver interface
+ * itself is subject to change in new Saxon releases.</p>
+ * <p>
+ * The supplied Receiver will have its open() method called once at the start of
+ * the transformation, and its close() method will be called once at the end of the
+ * transformation. Each individual call of an xsl:message instruction is wrapped by
+ * calls of startDocument() and endDocument(). If terminate="yes" is specified on the
+ * xsl:message call, the properties argument of the startDocument() call will be set
+ * to the value {@link ReceiverOptions#TERMINATE}.</p>
+ *
+ * @param receiver The receiver to receive xsl:message output.
+ * @since 8.4; changed in 8.9 to supply a Receiver rather than an Emitter
+ */
+
+ public void setMessageEmitter(/*@NotNull*/ Receiver receiver) {
+ messageReceiver = receiver;
+ receiver.setPipelineConfiguration(makePipelineConfiguration());
+ if (receiver instanceof Emitter && ((Emitter) receiver).getOutputProperties() == null) {
+ try {
+ Properties props = new Properties();
+ props.setProperty(OutputKeys.METHOD, "xml");
+ props.setProperty(OutputKeys.INDENT, "yes");
+ props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ ((Emitter) receiver).setOutputProperties(props);
+ } catch (XPathException e) {
+ // no action
+ }
+ }
+ }
+
+ /**
+ * Get the Receiver used for xsl:message output. This returns the emitter
+ * previously supplied to the {@link #setMessageEmitter} method, or the
+ * default message emitter otherwise.
+ *
+ * @return the Receiver being used for xsl:message output
+ * @since 8.4; changed in 8.9 to return a Receiver rather than an Emitter
+ */
+
+ /*@Nullable*/
+ public Receiver getMessageEmitter() {
+ return messageReceiver;
+ }
+
+ /**
+ * Set the policy for handling recoverable XSLT errors.
+ * <p/>
+ * <p>Since 9.3 this call has no effect unless the error listener in use is a {@link StandardErrorListener}
+ * or a subclass thereof. Calling this method then results in a call to the StandardErrorListener
+ * to set the recovery policy, and the action that is taken on calls of the various methods
+ * error(), fatalError(), and warning() is then the responsibility of the ErrorListener itself.</p>
+ * <p/>
+ * <p>Since 9.2 the policy for handling the most common recoverable error, namely the ambiguous template
+ * match that arises when a node matches more than one match pattern, is a compile-time rather than run-time
+ * setting, and can be controlled using {@link CompilerInfo#setRecoveryPolicy(int)} </p>
+ *
+ * @param policy the recovery policy to be used. The options are {@link Configuration#RECOVER_SILENTLY},
+ * {@link Configuration#RECOVER_WITH_WARNINGS}, or {@link Configuration#DO_NOT_RECOVER}.
+ * @since 8.7.1
+ */
+
+ public void setRecoveryPolicy(int policy) {
+ recoveryPolicy = policy;
+ if (errorListener instanceof StandardErrorListener) {
+ ((StandardErrorListener) errorListener).setRecoveryPolicy(policy);
+ }
+ }
+
+ /**
+ * Get the policy for handling recoverable errors
+ *
+ * @return the current policy. If none has been set with this Controller, the value registered with the
+ * Configuration is returned.
+ * @since 8.7.1
+ */
+
+ public int getRecoveryPolicy() {
+ return recoveryPolicy;
+ }
+
+ /**
+ * Set the error listener.
+ *
+ * @param listener the ErrorListener to be used
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ errorListener = listener;
+ }
+
+ /**
+ * Get the error listener.
+ *
+ * @return the ErrorListener in use
+ */
+
+ public ErrorListener getErrorListener() {
+ return errorListener;
+ }
+
+ /**
+ * Report a recoverable error. This is an XSLT concept: by default, such an error results in a warning
+ * message, and processing continues. In XQuery, however, there are no recoverable errors so a fatal
+ * error is reported.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @param err An exception holding information about the error
+ * @throws XPathException if the error listener decides not to
+ * recover from the error
+ */
+
+ public void recoverableError(XPathException err) throws XPathException {
+ if (executable.getHostLanguage() == Configuration.XQUERY || recoveryPolicy == Configuration.DO_NOT_RECOVER) {
+ throw err;
+ } else {
+ try {
+ errorListener.error(err);
+ } catch (TransformerException e) {
+ XPathException de = XPathException.makeXPathException(e);
+ de.setHasBeenReported(true);
+ throw de;
+ }
+ }
+ }
+
+ /**
+ * Report a fatal error
+ *
+ * @param err the error to be reported
+ */
+
+ public void reportFatalError(XPathException err) {
+ if (!err.hasBeenReported()) {
+ try {
+ getErrorListener().fatalError(err);
+ } catch (TransformerException e) {
+ //
+ }
+ err.setHasBeenReported(true);
+ }
+ }
+
+ /**
+ * Report a warning
+ * @param message the warning message
+ */
+
+ public void warning(String message, String errorCode) {
+ try {
+ getErrorListener().warning(new XPathException(message, errorCode));
+ } catch (TransformerException e) {
+ //
+ }
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Methods for managing the various runtime control objects
+ /////////////////////////////////////////////////////////////////////////////////////////
+
+
+ /**
+ * Get the Executable object.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return the Executable (which represents the compiled stylesheet)
+ */
+
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Get the document pool. This is used only for source documents, not for stylesheet modules.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return the source document pool
+ */
+
+ public DocumentPool getDocumentPool() {
+ return sourceDocumentPool;
+ }
+
+ /**
+ * Clear the document pool.
+ * This is sometimes useful when re-using the same Transformer
+ * for a sequence of transformations, but it isn't done automatically, because when
+ * the transformations use common look-up documents, the caching is beneficial.
+ */
+
+ public void clearDocumentPool() {
+ sourceDocumentPool.discardIndexes(getKeyManager());
+ sourceDocumentPool = new DocumentPool();
+ }
+
+ /**
+ * Set the initial context item, when running XQuery.
+ * <p/>
+ * When a transformation is invoked using the {@link #transform} method, the
+ * initial context node is set automatically. This method is useful in XQuery,
+ * to define an initial context node for evaluating global variables, and also
+ * in XSLT 2.0, when the transformation is started by invoking a named template.
+ * <p/>
+ * <p>When an initial context item is set, it also becomes the context item used for
+ * evaluating global variables. The two context items can only be different when the
+ * {@link #transform} method is used to transform a document starting at a node other
+ * than the root.</p>
+ * <p/>
+ * <p>In XQuery, the two context items are always
+ * the same; in XSLT, the context node for evaluating global variables is the root of the
+ * tree containing the initial context item.</p>
+ *
+ * @param item The initial context item.
+ * @since 8.7
+ */
+
+ public void setInitialContextItem(Item item) {
+ initialContextItem = item;
+ contextForGlobalVariables = item;
+ }
+
+ /**
+ * Get the current bindery.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return the Bindery (in which values of all variables are held)
+ */
+
+ public Bindery getBindery() {
+ return bindery;
+ }
+
+ /**
+ * Get the initial context item. This returns the item (often a document node)
+ * previously supplied to the {@link #setInitialContextItem} method, or the
+ * initial context node set implicitly using methods such as {@link #transform}.
+ *
+ * @return the initial context item. Note that in XSLT this must be a node, but in
+ * XQuery it may also be an atomic value.
+ * @since 8.7
+ */
+
+ /*@Nullable*/
+ public Item getInitialContextItem() {
+ return initialContextItem;
+ }
+
+ /**
+ * Get the item used as the context for evaluating global variables. In XQuery this
+ * is the same as the initial context item; in XSLT it is the root of the tree containing
+ * the initial context node.
+ *
+ * @return the context item for evaluating global variables, or null if there is none
+ * @since 8.7
+ */
+
+ /*@Nullable*/
+ public Item getContextForGlobalVariables() {
+ return contextForGlobalVariables;
+ // See bug 5224, which points out that the rules for XQuery 1.0 weren't clearly defined
+ }
+
+ /**
+ * Set an object that will be used to resolve URIs used in
+ * document(), etc.
+ *
+ * @param resolver An object that implements the URIResolver interface, or
+ * null.
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ userURIResolver = resolver;
+ if (resolver instanceof StandardURIResolver) {
+ ((StandardURIResolver) resolver).setConfiguration(getConfiguration());
+ }
+ }
+
+ /**
+ * Get the URI resolver.
+ * <p/>
+ * <p><i>This method changed in Saxon 8.5, to conform to the JAXP specification. If there
+ * is no user-specified URIResolver, it now returns null; previously it returned the system
+ * default URIResolver.</i></p>
+ *
+ * @return the user-supplied URI resolver if there is one, or null otherwise.
+ */
+
+ public URIResolver getURIResolver() {
+ return userURIResolver;
+ }
+
+ /**
+ * Get the fallback URI resolver. This is the URIResolver that Saxon uses when
+ * the user-supplied URI resolver returns null.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return the the system-defined URIResolver
+ */
+
+ public URIResolver getStandardURIResolver() {
+ return standardURIResolver;
+ }
+
+ /**
+ * Set the URI resolver for secondary output documents.
+ * <p/>
+ * XSLT 2.0 introduces the <code>xsl:result-document</code instruction,
+ * allowing a transformation to have multiple result documents. JAXP does
+ * not yet support this capability. This method allows an OutputURIResolver
+ * to be specified that takes responsibility for deciding the destination
+ * (and, if it wishes, the serialization properties) of secondary output files.
+ * <p/>
+ * In Saxon 9.5, because xsl:result-document is now multi-threaded, the
+ * supplied resolver is cloned each time a new result document is created.
+ * The cloned resolved is therefore able to maintain information about
+ * the specific result document for use when its close() method is called,
+ * without worrying about thread safety.
+ *
+ * @param resolver An object that implements the OutputURIResolver
+ * interface, or null.
+ * @since 8.4
+ */
+
+ public void setOutputURIResolver(/*@Nullable*/ OutputURIResolver resolver) {
+ if (resolver == null) {
+ outputURIResolver = config.getOutputURIResolver();
+ } else {
+ outputURIResolver = resolver;
+ }
+ }
+
+ /**
+ * Get the output URI resolver.
+ *
+ * @return the user-supplied URI resolver if there is one, or the
+ * system-defined one otherwise.
+ * @see #setOutputURIResolver
+ * @since 8.4
+ */
+
+ /*@Nullable*/
+ public OutputURIResolver getOutputURIResolver() {
+ return outputURIResolver;
+ }
+
+ /**
+ * Set an UnparsedTextURIResolver to be used to resolve URIs passed to the XSLT
+ * unparsed-text() function.
+ *
+ * @param resolver the unparsed text URI resolver to be used. This replaces any unparsed text
+ * URI resolver previously registered.
+ * @since 8.9
+ */
+
+ public void setUnparsedTextURIResolver(UnparsedTextURIResolver resolver) {
+ unparsedTextResolver = resolver;
+ }
+
+ /**
+ * Get the URI resolver for the unparsed-text() function. This will
+ * return the UnparsedTextURIResolver previously set using the {@link #setUnparsedTextURIResolver}
+ * method.
+ *
+ * @return the registered UnparsedTextURIResolver
+ * @since 8.9
+ */
+
+ public UnparsedTextURIResolver getUnparsedTextURIResolver() {
+ return unparsedTextResolver;
+ }
+
+ /**
+ * Set the SchemaURIResolver used for resolving references to schema
+ * documents. Defaults to the SchemaURIResolver registered with the
+ * Configuration
+ *
+ * @param resolver the resolver for references to schema documents
+ */
+
+ public void setSchemaURIResolver(SchemaURIResolver resolver) {
+ schemaURIResolver = resolver;
+ }
+
+ /**
+ * Get the SchemaURIResolver used for resolving references to schema
+ * documents. If none has been set on the Controller, returns the
+ * SchemaURIResolver registered with the Configuration
+ *
+ * @return the resolver for references to schema documents
+ */
+
+ /*@Nullable*/
+ public SchemaURIResolver getSchemaURIResolver() {
+ return schemaURIResolver;
+ }
+
+ /**
+ * Set the CollectionURIResolver used for resolving collection URIs.
+ * Defaults to the CollectionURIResolver registered with the Configuration
+ *
+ * @param resolver the resolver for references to collections
+ * @since 9.4
+ */
+
+ public void setCollectionURIResolver(CollectionURIResolver resolver) {
+ collectionURIResolver = resolver;
+ }
+
+ /**
+ * Get the CollectionURIResolver used for resolving references to collections.
+ * If none has been set on the Controller, returns the
+ * CollectionURIResolver registered with the Configuration
+ *
+ * @return the resolver for references to collections
+ * @since 9.4
+ */
+
+ /*@NotNull*/
+ public CollectionURIResolver getCollectionURIResolver() {
+ return (collectionURIResolver == null ? getConfiguration().getCollectionURIResolver() : collectionURIResolver);
+ }
+
+ /**
+ * Set the name of the default collection. Defaults to the default collection
+ * name registered with the Configuration.
+ *
+ * @param uri the collection URI of the default collection. May be null, to cause
+ * fallback to the collection name registered with the Configuration. The name will be passed
+ * to the collection URI resolver to identify the documents in the collection, unless
+ * the name is <code>http://saxon.sf.net/collection/empty</code> which always refers
+ * to the empty collection.
+ * @since 9.4
+ */
+
+ public void setDefaultCollection(String uri) {
+ defaultCollectionURI = uri;
+ }
+
+ /**
+ * Get the name of the default collection. Defaults to the default collection
+ * name registered with the Configuration.
+ *
+ * @return the collection URI of the default collection. If no value has been
+ * set explicitly, the collection URI registered with the Configuration is returned
+ * @since 9.4
+ */
+
+ public String getDefaultCollection() {
+ return (defaultCollectionURI == null ? getConfiguration().getDefaultCollection() : defaultCollectionURI);
+ }
+
+
+ /**
+ * Ask whether source documents loaded using the doc(), document(), and collection()
+ * functions, or supplied as a StreamSource or SAXSource to the transform() or addParameter() method
+ * should be subjected to schema validation
+ *
+ * @return the schema validation mode previously set using setSchemaValidationMode(),
+ * or the default mode {@link Validation#STRIP} otherwise.
+ */
+
+ public int getSchemaValidationMode() {
+ return validationMode;
+ }
+
+ /**
+ * Say whether source documents loaded using the doc(), document(), and collection()
+ * functions, or supplied as a StreamSource or SAXSource to the transform() or addParameter() method,
+ * should be subjected to schema validation. The default value is taken
+ * from the corresponding property of the Configuration.
+ *
+ * @param validationMode the validation (or construction) mode to be used for source documents.
+ * One of {@link Validation#STRIP}, {@link Validation#PRESERVE}, {@link Validation#STRICT},
+ * {@link Validation#LAX}
+ * @since 9.2
+ */
+
+ public void setSchemaValidationMode(int validationMode) {
+ this.validationMode = validationMode;
+ }
+
+
+ /**
+ * Get the KeyManager.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return the KeyManager, which holds details of all key declarations
+ */
+
+ public KeyManager getKeyManager() {
+ return executable.getKeyManager();
+ }
+
+ /**
+ * Get the name pool in use. The name pool is responsible for mapping QNames used in source
+ * documents and compiled stylesheets and queries into numeric codes. All source documents
+ * used by a given transformation or query must use the same name pool as the compiled stylesheet
+ * or query.
+ *
+ * @return the name pool in use
+ * @since 8.4
+ */
+
+ public NamePool getNamePool() {
+ return namePool;
+ }
+
+ /**
+ * Set the tree data model to use. This affects all source documents subsequently constructed using a
+ * Builder obtained from this Controller. This includes a document built from a StreamSource or
+ * SAXSource supplied as a parameter to the {@link #transform} method.
+ *
+ * @param model the required tree model: {@link Builder#LINKED_TREE},
+ * {@link Builder#TINY_TREE}, or {@link Builder#TINY_TREE_CONDENSED}
+ * @see net.sf.saxon.event.Builder
+ * @since 8.4 (Condensed tinytree added in 9.2)
+ * @deprecated since 9.2: use {@link #setModel}
+ */
+
+ public void setTreeModel(int model) {
+ treeModel = TreeModel.getTreeModel(model);
+ }
+
+ /**
+ * Get the tree data model to use. This affects all source documents subsequently constructed using a
+ * Builder obtained from this Controller. This includes a document built from a StreamSource or
+ * SAXSource supplied as a parameter to the {@link #transform} method.
+ *
+ * @return model the tree model: {@link Builder#LINKED_TREE},
+ * {@link Builder#TINY_TREE}, or {@link Builder#TINY_TREE_CONDENSED}
+ * @see net.sf.saxon.event.Builder
+ * @since 9.1 (Condensed tinytree added in 9.2)
+ * @deprecated since 9.2: use {@link #getModel}
+ */
+
+ public int getTreeModel() {
+ return treeModel.getSymbolicValue();
+ }
+
+ /**
+ * Set the tree model to use. Default is the tiny tree
+ *
+ * @param model typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link net.sf.saxon.om.TreeModel#TINY_TREE_CONDENSED}, or {@link net.sf.saxon.om.TreeModel#LINKED_TREE}.
+ * It is also possible to use a user-defined tree model.
+ * @since 9.2
+ */
+
+ public void setModel(TreeModel model) {
+ treeModel = model;
+ }
+
+ /**
+ * Get the tree model that will be used.
+ *
+ * @return typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link TreeModel#TINY_TREE_CONDENSED}, or {@link TreeModel#LINKED_TREE}.
+ * It is also possible to use a user-defined tree model.
+ * @since 9.2
+ */
+
+ public TreeModel getModel() {
+ return treeModel;
+ }
+
+
+ /**
+ * Make a builder for the selected tree model.
+ *
+ * @return an instance of the Builder for the chosen tree model
+ * @since 8.4
+ */
+
+ public Builder makeBuilder() {
+ Builder b = treeModel.makeBuilder(makePipelineConfiguration());
+ b.setTiming(config.isTiming());
+ b.setLineNumbering(config.isLineNumbering());
+ return b;
+ }
+
+ /**
+ * Say whether the transformation should perform whitespace stripping as defined
+ * by the xsl:strip-space and xsl:preserve-space declarations in the stylesheet
+ * in the case where a source tree is supplied to the transformation as a tree
+ * (typically a DOMSource, or a Saxon NodeInfo).
+ * The default is true. It is legitimate to suppress whitespace
+ * stripping if the client knows that all unnecessary whitespace has already been removed
+ * from the tree before it is processed. Note that this option applies to all source
+ * documents for which whitespace-stripping is normally applied, that is, both the
+ * principal source documents, and documents read using the doc(), document(), and
+ * collection() functions. It does not apply to source documents that are supplied
+ * in the form of a SAXSource or StreamSource, for which whitespace is stripped
+ * during the process of tree construction.
+ * <p>Generally, stripping whitespace speeds up the transformation if it is done
+ * while building the source tree, but slows it down if it is applied to a tree that
+ * has already been built. So if the same source tree is used as input to a number
+ * of transformations, it is better to strip the whitespace once at the time of
+ * tree construction, rather than doing it on-the-fly during each transformation.</p>
+ *
+ * @param strip true if whitespace is to be stripped from supplied source trees
+ * as defined by xsl:strip-space; false to suppress whitespace stripping
+ * @since 9.3
+ */
+
+ public void setStripSourceTrees(boolean strip) {
+ stripSourceTrees = strip;
+ }
+
+ /**
+ * Ask whether the transformation will perform whitespace stripping for supplied source trees as defined
+ * by the xsl:strip-space and xsl:preserve-space declarations in the stylesheet.
+ *
+ * @return true unless whitespace stripping has been suppressed using
+ * {@link #setStripSourceTrees(boolean)}.
+ * @since 9.3
+ */
+
+ public boolean isStripSourceTree() {
+ return stripSourceTrees;
+ }
+
+ /**
+ * Make a Stripper configured to implement the whitespace stripping rules.
+ * In the case of XSLT the whitespace stripping rules are normally defined
+ * by <code>xsl:strip-space</code> and <code>xsl:preserve-space</code elements
+ * in the stylesheet. Alternatively, stripping of all whitespace text nodes
+ * may be defined at the level of the Configuration, using the method
+ * {@link Configuration#setStripsAllWhiteSpace(boolean)}.
+ *
+ * @param next the Receiver to which the events filtered by this stripper are
+ * to be sent (often a Builder). May be null if the stripper is not being used for filtering
+ * into a Builder or other Receiver.
+ * @return the required Stripper. A Stripper may be used in two ways. It acts as
+ * a filter applied to an event stream, that can be used to remove the events
+ * representing whitespace text nodes before they reach a Builder. Alternatively,
+ * it can be used to define a view of an existing tree in which the whitespace
+ * text nodes are dynamically skipped while navigating the XPath axes.
+ * @since 8.4 - Generalized in 8.5 to accept any Receiver as an argument
+ */
+
+ public Stripper makeStripper(/*@Nullable*/ Receiver next) {
+ if (next == null) {
+ next = new Sink(makePipelineConfiguration());
+ }
+ return new Stripper(getSpaceStrippingRule(), next);
+ }
+
+ public SpaceStrippingRule getSpaceStrippingRule() {
+ if (config.isStripsAllWhiteSpace()) {
+ return AllElementsSpaceStrippingRule.getInstance();
+ } else {
+ if (executable == null) {
+ return NoElementsSpaceStrippingRule.getInstance();
+ } else {
+ return executable.getStripperRules();
+ }
+ }
+ }
+
+ /**
+ * Add a document to the document pool, and check that it is suitable for use in this query or
+ * transformation. This check rejects the document if document has been validated (and thus carries
+ * type annotations) but the query or transformation is not schema-aware.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @param doc the root node of the document to be added. Must not be null.
+ * @param uri the document-URI property of this document. If non-null, the document is registered
+ * in the document pool with this as its document URI.
+ */
+ public void registerDocument(/*@Nullable*/ DocumentInfo doc, /*@Nullable*/ DocumentURI uri) throws XPathException {
+ if (doc == null) {
+ throw new NullPointerException("null");
+ }
+ if (!getExecutable().isSchemaAware() && !Untyped.getInstance().equals(doc.getSchemaType())) {
+ String task = (getExecutable().getHostLanguage() == Configuration.XSLT ? "transformation" : "query");
+ throw new XPathException("The " + task + " is not schema-aware, so the source document must be untyped");
+ }
+ if (uri != null) {
+ sourceDocumentPool.add(doc, uri);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Methods for registering and retrieving handlers for template rules
+ ////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Set the RuleManager, used to manage template rules for each mode.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @param r the Rule Manager
+ */
+ public void setRuleManager(RuleManager r) {
+ ruleManager = r;
+ }
+
+ /**
+ * Get the Rule Manager.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @return the Rule Manager, used to hold details of template rules for
+ * all modes
+ */
+ public RuleManager getRuleManager() {
+ return ruleManager;
+ }
+
+ /////////////////////////////////////////////////////////////////////////
+ // Methods for tracing
+ /////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Set a TraceListener, replacing any existing TraceListener
+ * <p>This method has no effect unless the stylesheet or query was compiled
+ * with tracing enabled.</p>
+ *
+ * @param listener the TraceListener to be set. May be null, in which case
+ * trace events will not be reported
+ * @since 9.2
+ */
+
+ public void setTraceListener(TraceListener listener) {
+ this.traceListener = listener;
+ }
+
+ /**
+ * Get the TraceListener. By default, there is no TraceListener, and this
+ * method returns null. A TraceListener may be added using the method
+ * {@link #addTraceListener}. If more than one TraceListener has been added,
+ * this method will return a composite TraceListener. Because the form
+ * this takes is implementation-dependent, this method is not part of the
+ * stable Saxon public API.
+ *
+ * @return the TraceListener used for XSLT or XQuery instruction tracing, or null if absent.
+ */
+ /*@Nullable*/
+ public TraceListener getTraceListener() {
+ return traceListener;
+ }
+
+ /**
+ * Test whether instruction execution is being traced. This will be true
+ * if (a) at least one TraceListener has been registered using the
+ * {@link #addTraceListener} method, and (b) tracing has not been temporarily
+ * paused using the {@link #pauseTracing} method.
+ *
+ * @return true if tracing is active, false otherwise
+ * @since 8.4
+ */
+
+ public final boolean isTracing() {
+ return traceListener != null && !tracingPaused;
+ }
+
+ /**
+ * Pause or resume tracing. While tracing is paused, trace events are not sent to any
+ * of the registered TraceListeners.
+ *
+ * @param pause true if tracing is to pause; false if it is to resume
+ * @since 8.4
+ */
+ public final void pauseTracing(boolean pause) {
+ tracingPaused = pause;
+ }
+
+ /**
+ * Adds the specified trace listener to receive trace events from
+ * this instance. Note that although TraceListeners can be added
+ * or removed dynamically, this has no effect unless the stylesheet
+ * or query has been compiled with tracing enabled. This is achieved
+ * by calling {@link Configuration#setTraceListener} or by setting
+ * the attribute {@link net.sf.saxon.lib.FeatureKeys#TRACE_LISTENER} on the
+ * TransformerFactory. Conversely, if this property has been set in the
+ * Configuration or TransformerFactory, the TraceListener will automatically
+ * be added to every Controller that uses that Configuration.
+ *
+ * @param trace the trace listener. If null is supplied, the call has no effect.
+ * @since 8.4
+ */
+
+ public void addTraceListener(/*@Nullable*/ TraceListener trace) {
+ if (trace != null) {
+ traceListener = TraceEventMulticaster.add(traceListener, trace);
+ }
+ }
+
+ /**
+ * Removes the specified trace listener so that the listener will no longer
+ * receive trace events.
+ *
+ * @param trace the trace listener.
+ * @since 8.4
+ */
+
+ public void removeTraceListener(TraceListener trace) {
+ traceListener = TraceEventMulticaster.remove(traceListener, trace);
+ }
+
+ /**
+ * Set the destination for output from the fn:trace() function.
+ * By default, the destination is System.err. If a TraceListener is in use,
+ * this is ignored, and the trace() output is sent to the TraceListener.
+ *
+ * @param stream the PrintStream to which trace output will be sent. If set to
+ * null, trace output is suppressed entirely. It is the caller's responsibility
+ * to close the stream after use.
+ * @since 9.1
+ */
+
+ public void setTraceFunctionDestination(PrintStream stream) {
+ traceFunctionDestination = stream;
+ }
+
+ /**
+ * Get the destination for output from the fn:trace() function.
+ *
+ * @return the PrintStream to which trace output will be sent. If no explicitly
+ * destination has been set, returns System.err. If the destination has been set
+ * to null to suppress trace output, returns null.
+ * @since 9.1
+ */
+
+ public PrintStream getTraceFunctionDestination() {
+ return traceFunctionDestination;
+ }
+
+ /**
+ * Associate this Controller with a compiled stylesheet.
+ * <p/>
+ * This method is intended for internal use only.
+ *
+ * @param sheet the compiled stylesheet
+ */
+
+ public void setPreparedStylesheet(PreparedStylesheet sheet) {
+ preparedStylesheet = sheet;
+ executable = sheet;
+ messageReceiverClassName = sheet.getCompilerInfo().getMessageReceiverClassName();
+ outputURIResolver = sheet.getCompilerInfo().getOutputURIResolver();
+ recoveryPolicy = sheet.getCompilerInfo().getRecoveryPolicy();
+ //setOutputProperties(sheet.getOutputProperties());
+ // above line deleted for bug 490964 - may have side-effects
+ }
+
+ /**
+ *
+ */
+
+ /**
+ * Associate this Controller with an Executable. This method is used by the XQuery
+ * processor. The Executable object is overkill in this case - the only thing it
+ * currently holds are copies of the collation table.
+ * <p/>
+ * This method is intended for internal use only
+ *
+ * @param exec the Executable
+ */
+
+ public void setExecutable(Executable exec) {
+ executable = exec;
+ }
+
+ /**
+ * Initialize the controller ready for a new transformation. This method should not normally be called by
+ * users (it is done automatically when transform() is invoked). However, it is available as a low-level API
+ * especially for use with XQuery.
+ */
+
+ private void initializeController() throws XPathException {
+ if (preparedStylesheet != null) {
+ setRuleManager(preparedStylesheet.getRuleManager());
+ }
+ //setDecimalFormatManager(executable.getDecimalFormatManager());
+
+ if (traceListener != null) {
+ traceListener.open(this);
+ }
+
+ // get a new bindery, to clear out any variables from previous runs
+
+ bindery = new Bindery();
+ executable.initializeBindery(bindery);
+
+ // if parameters were supplied, set them up
+
+ defineGlobalParameters();
+ }
+
+ /**
+ * Register the global parameters of the transformation or query. This should be called after a sequence
+ * of calls on {@link #setParameter}. It checks that all required parameters have been supplied, and places
+ * the values of the parameters in the Bindery to make them available for use during the query or
+ * transformation.
+ * <p/>
+ * This method is intended for internal use only
+ * @throws XPathException if a required parameter is missing
+ */
+
+ public void defineGlobalParameters() throws XPathException {
+ executable.checkAllRequiredParamsArePresent(parameters);
+ bindery.defineGlobalParameters(parameters);
+ }
+
+ /**
+ * Allocate space in the bindery for global variables.
+ * <p>For internal use only.</p>
+ *
+ * @param numberOfVariables the number of global variables for which space is required
+ */
+
+ public void allocateGlobalVariables(int numberOfVariables) {
+ SlotManager map = executable.getGlobalVariableMap();
+ map.setNumberOfVariables(numberOfVariables);
+ bindery.allocateGlobals(map);
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // Allow user data to be associated with nodes on a tree
+ /////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Get user data associated with a key. To retrieve user data, two objects are required:
+ * an arbitrary object that may be regarded as the container of the data (originally, and
+ * typically still, a node in a tree), and a name. The name serves to distingush data objects
+ * associated with the same node by different client applications.
+ * <p/>
+ * This method is intended primarily for internal use, though it may also be
+ * used by advanced applications.
+ *
+ * @param key an object acting as a key for this user data value. This must be equal
+ * (in the sense of the equals() method) to the key supplied when the data value was
+ * registered using {@link #setUserData}.
+ * @param name the name of the required property
+ * @return the value of the required property
+ */
+
+ public Object getUserData(Object key, String name) {
+ String keyValue = key.hashCode() + " " + name;
+ // System.err.println("getUserData " + name + " on object returning " + userDataTable.get(key));
+ return userDataTable.get(keyValue);
+ }
+
+ /**
+ * Set user data associated with a key. To store user data, two objects are required:
+ * an arbitrary object that may be regarded as the container of the data (originally, and
+ * typically still, a node in a tree), and a name. The name serves to distingush data objects
+ * associated with the same node by different client applications.
+ * <p/>
+ * This method is intended primarily for internal use, though it may also be
+ * used by advanced applications.
+ *
+ * @param key an object acting as a key for this user data value. This can be any object, for example
+ * a node or a string. If data for the given object and name already exists, it is overwritten.
+ * @param name the name of the required property
+ * @param data the value of the required property. If null is supplied, any existing entry
+ * for the key is removed.
+ */
+
+ public void setUserData(Object key, String name, /*@Nullable*/ Object data) {
+ // System.err.println("setUserData " + name + " on object to " + data);
+ String keyVal = key.hashCode() + " " + name;
+ if (data == null) {
+ userDataTable.remove(keyVal);
+ } else {
+ userDataTable.put(keyVal, data);
+ }
+ }
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // implement the javax.xml.transform.Transformer methods
+ /////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Perform a transformation from a Source document to a Result document.
+ *
+ * @param source The input for the source tree. May be null if and only if an
+ * initial template has been supplied.
+ * @param result The destination for the result tree.
+ * @throws XPathException if the transformation fails. As a
+ * special case, the method throws a TerminationException (a subclass
+ * of XPathException) if the transformation was terminated using
+ * xsl:message terminate="yes".
+ */
+
+ public void transform(Source source, Result result) throws TransformerException {
+ if (inUse) {
+ throw new IllegalStateException(
+ "The Transformer is being used recursively or concurrently. This is not permitted.");
+ }
+ clearPerTransformationData();
+ if (preparedStylesheet == null) {
+ throw new XPathException("Stylesheet has not been prepared");
+ }
+
+ if (!dateTimePreset) {
+ currentDateTime = null; // reset at start of each transformation
+ }
+
+ if (source instanceof SAXSource && config.getBooleanProperty(FeatureKeys.IGNORE_SAX_SOURCE_PARSER)) {
+ // This option is provided to allow the parser set by applications such as Ant to be overridden by
+ // the parser requested using FeatureKeys.SOURCE_PARSER
+ ((SAXSource)source).setXMLReader(null);
+ }
+
+ boolean close = false;
+ try {
+ NodeInfo startNode = null;
+ boolean wrap = true;
+ boolean streaming = false;
+ int validationMode = getSchemaValidationMode();
+ Source underSource = source;
+ if (source instanceof AugmentedSource) {
+ Boolean localWrap = ((AugmentedSource) source).getWrapDocument();
+ if (localWrap != null) {
+ wrap = localWrap.booleanValue();
+ }
+ close = ((AugmentedSource) source).isPleaseCloseAfterUse();
+ int localValidate = ((AugmentedSource) source).getSchemaValidation();
+ if (localValidate != Validation.DEFAULT) {
+ validationMode = localValidate;
+ }
+ if (validationMode == Validation.STRICT || validationMode == Validation.LAX) {
+ // If validation of a DOMSource or NodeInfo is requested, we must copy it, we can't wrap it
+ wrap = false;
+ }
+ underSource = ((AugmentedSource) source).getContainedSource();
+ }
+ Source s2 = config.getSourceResolver().resolveSource(underSource, config);
+ if (s2 != null) {
+ underSource = s2;
+ }
+ if (wrap && (underSource instanceof NodeInfo || underSource instanceof DOMSource)) {
+ startNode = prepareInputTree(underSource);
+ String uri = underSource.getSystemId();
+ DocumentInfo root = startNode.getDocumentRoot();
+ if (root != null) {
+ registerDocument(startNode.getDocumentRoot(), (uri == null ? null : new DocumentURI(uri)));
+ }
+
+ } else if (source == null) {
+ if (initialTemplate == null) {
+ throw new XPathException("Either a source document or an initial template must be specified");
+ }
+
+ } else {
+
+ Mode mode = preparedStylesheet.getRuleManager().getMode(initialMode, false);
+ if (mode == null || (initialMode != null && mode.isEmpty())) {
+ throw new XPathException("Requested initial mode " +
+ (initialMode == null ? "" : initialMode.getDisplayName()) +
+ " does not exist", "XTDE0045");
+ }
+ if (mode.isStreamable()) {
+ if (source instanceof StreamSource || source instanceof SAXSource || source instanceof Transmitter) {
+ streaming = true;
+ transformStream(source, mode, result);
+ } else {
+ throw new XPathException("Requested initial mode " +
+ (initialMode == null ? "" : initialMode.getDisplayName()) +
+ " is streamable: must supply a SAXSource or StreamSource", "SXST0061");
+ }
+ } else {
+ // The input is a SAXSource or StreamSource or AugmentedSource, or
+ // a DOMSource with wrap=no: build the document tree
+
+ Builder sourceBuilder = makeBuilder();
+ //Sender sender = new Sender(sourceBuilder.getPipelineConfiguration());
+ Receiver r = sourceBuilder;
+ if (config.isStripsAllWhiteSpace() || executable.stripsWhitespace() ||
+ validationMode == Validation.STRICT || validationMode == Validation.LAX) {
+ r = makeStripper(sourceBuilder);
+ }
+ if (executable.stripsInputTypeAnnotations()) {
+ r = config.getAnnotationStripper(r);
+ }
+ r.setPipelineConfiguration(sourceBuilder.getPipelineConfiguration());
+ Sender.send(source, r, null);
+ if (close) {
+ ((AugmentedSource) source).close();
+ }
+ DocumentInfo doc = (DocumentInfo) sourceBuilder.getCurrentRoot();
+ sourceBuilder.reset();
+ if (source.getSystemId() != null) {
+ registerDocument(doc, new DocumentURI(source.getSystemId()));
+ }
+ startNode = doc;
+ }
+ }
+ if (!streaming) {
+ transformDocument(startNode, result);
+ }
+
+ } catch (TerminationException err) {
+ //System.err.println("Processing terminated using xsl:message");
+ if (!err.hasBeenReported()) {
+ reportFatalError(err);
+ }
+ throw err;
+ } catch (XPathException err) {
+ Throwable cause = err.getException();
+ if (cause != null && cause instanceof SAXParseException) {
+ // This generally means the error was already reported.
+ // But if a RuntimeException occurs in Saxon during a callback from
+ // the Crimson parser, Crimson wraps this in a SAXParseException without
+ // reporting it further.
+ SAXParseException spe = (SAXParseException) cause;
+ cause = spe.getException();
+ if (cause instanceof RuntimeException) {
+ reportFatalError(err);
+ }
+ } else {
+ reportFatalError(err);
+ }
+ throw err;
+ } catch (NullPointerException err) {
+ err.printStackTrace();
+ throw err;
+ } finally {
+ inUse = false;
+ if (close && source instanceof AugmentedSource) {
+ ((AugmentedSource) source).close();
+ }
+ principalResultURI = null;
+ }
+ }
+
+ /**
+ * Prepare an input tree for processing. This is used when either the initial
+ * input, or a Source returned by the document() function, is a NodeInfo or a
+ * DOMSource. The preparation consists of wrapping a DOM document inside a wrapper
+ * that implements the NodeInfo interface, and/or adding a space-stripping wrapper
+ * if the stylesheet strips whitespace nodes, and/or adding a type-stripping wrapper
+ * if the stylesheet strips input type annotations.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param source the input tree. Must be either a DOMSource or a NodeInfo
+ * @return the NodeInfo representing the input node, suitably wrapped.
+ */
+
+ public NodeInfo prepareInputTree(Source source) {
+ NodeInfo start = getConfiguration().unravel(source);
+ if (stripSourceTrees && executable.stripsWhitespace()) {
+ DocumentInfo docInfo = start.getDocumentRoot();
+ SpaceStrippedDocument strippedDoc = new SpaceStrippedDocument(docInfo, getSpaceStrippingRule());
+ start = strippedDoc.wrap(start);
+ }
+ if (executable.stripsInputTypeAnnotations()) {
+ DocumentInfo docInfo = start.getDocumentRoot();
+ if (!Untyped.getInstance().equals(docInfo.getSchemaType())) {
+ TypeStrippedDocument strippedDoc = new TypeStrippedDocument(docInfo);
+ start = strippedDoc.wrap(start);
+ }
+ }
+ return start;
+ }
+
+ /**
+ * Transform a source XML document supplied as a tree. <br>
+ * <p/>
+ * This method is intended for internal use. External applications should use
+ * the {@link #transform} method, which is part of the JAXP interface. Note that
+ * <code>NodeInfo</code> implements the JAXP <code>Source</code> interface, so
+ * it may be supplied directly to the transform() method.
+ *
+ * @param startNode A Node that identifies the source document to be
+ * transformed and the node where the transformation should start.
+ * May be null if the transformation is to start using an initial template.
+ * @param result The output destination
+ * @throws XPathException if any dynamic error occurs
+ */
+
+ public void transformDocument(/*@Nullable*/ NodeInfo startNode, /*@NotNull*/ Result result)
+ throws TransformerException {
+ // System.err.println("*** TransformDocument");
+ if (executable == null) {
+ throw new XPathException("Stylesheet has not been compiled");
+ }
+
+ openMessageEmitter();
+
+ // Determine whether we need to close the output stream at the end. We
+ // do this if the Result object is a StreamResult and is supplied as a
+ // system ID, not as a Writer or OutputStream
+
+ boolean mustClose = (result instanceof StreamResult &&
+ ((StreamResult) result).getOutputStream() == null);
+
+ principalResult = result;
+ if (principalResultURI == null) {
+ principalResultURI = result.getSystemId();
+ }
+
+ XPathContextMajor initialContext = newXPathContext();
+ initialContext.createThreadManager();
+ initialContext.setOriginatingConstructType(Location.CONTROLLER);
+
+ if (startNode != null) {
+
+ initialContextItem = startNode;
+ contextForGlobalVariables = startNode.getRoot();
+
+ if (startNode.getConfiguration() == null) {
+ // must be a non-standard document implementation
+ throw new TransformerException("The supplied source document must be associated with a Configuration");
+ }
+
+ if (!startNode.getConfiguration().isCompatible(preparedStylesheet.getConfiguration())) {
+ throw new XPathException(
+ "Source document and stylesheet must use the same or compatible Configurations",
+ SaxonErrorCode.SXXP0004);
+ }
+ if (startNode instanceof DocumentInfo && ((DocumentInfo) startNode).isTyped() && !preparedStylesheet.isSchemaAware()) {
+ throw new XPathException("Cannot use a schema-validated source document unless the stylesheet is schema-aware");
+ }
+ SequenceIterator currentIter = SingletonIterator.makeIterator(startNode);
+ if (initialTemplate != null) {
+ currentIter.next();
+ }
+ initialContext.setCurrentIterator(currentIter);
+ }
+
+ initializeController();
+
+ // In tracing/debugging mode, evaluate all the global variables first
+ if (traceListener != null) {
+ preEvaluateGlobals(initialContext);
+ }
+
+ result = openResult(result, initialContext);
+
+ // Process the source document by applying template rules to the initial context node
+
+ if (initialTemplate == null) {
+ initialContextItem = startNode;
+ Mode mode = getRuleManager().getMode(initialMode, false);
+ if (mode == null || (initialMode != null && mode.isEmpty())) {
+ throw new XPathException("Requested initial mode " +
+ (initialMode == null ? "" : initialMode.getDisplayName()) +
+ " does not exist", "XTDE0045");
+ }
+ if (mode.isStreamable()) {
+ throw new XPathException("Requested initial mode " +
+ (initialMode == null ? "" : initialMode.getDisplayName()) +
+ " is streamable: must supply a StreamSource or SAXSource");
+ }
+ if (startNode instanceof DocumentInfo) {
+ NodeInfo topElement = startNode.iterateAxis(AxisInfo.CHILD, NodeKindTest.ELEMENT).next();
+ if (topElement != null) {
+ String uri = topElement.getURI();
+ Set<String> explicitNamespaces = mode.getExplicitNamespaces(namePool);
+ if (!explicitNamespaces.isEmpty() && !explicitNamespaces.contains(uri)) {
+ if (explicitNamespaces.size() == 1 && explicitNamespaces.contains("")) {
+ warning("The source document is in namespace " + uri +
+ ", but all the template rules match elements in no namespace", SaxonErrorCode.SXXP0005);
+ } else if (uri.equals("")) {
+ warning("The source document is in no namespace" +
+ ", but the template rules all expect elements in a namespace", SaxonErrorCode.SXXP0005);
+ } else {
+ warning("The source document is in namespace " + uri +
+ ", but none of the template rules match elements in this namespace", SaxonErrorCode.SXXP0005);
+ }
+ }
+ }
+ }
+ initialContext.setCurrentMode(mode);
+ TailCall tc = mode.applyTemplates(null, null, initialContext, 0);
+ while (tc != null) {
+ tc = tc.processLeavingTail();
+ }
+ } else {
+ Template t = initialTemplate;
+ XPathContextMajor c2 = initialContext.newContext();
+ initialContext.setOriginatingConstructType(Location.CONTROLLER);
+ c2.openStackFrame(t.getStackFrameMap());
+ c2.setLocalParameters(new ParameterSet());
+ c2.setTunnelParameters(new ParameterSet());
+
+ TailCall tc = t.expand(c2);
+ while (tc != null) {
+ tc = tc.processLeavingTail();
+ }
+ }
+
+ if (traceListener != null) {
+ traceListener.close();
+ }
+
+ initialContext.notifyChildThreads();
+
+ closeMessageEmitter();
+ closeResult(result, mustClose, initialContext);
+ }
+
+ /**
+ * Transform a source XML document supplied as a stream, in streaming mode. <br>
+ * <p/>
+ * This method is intended for internal use. External applications should use
+ * the {@link #transform} method, which is part of the JAXP interface. Note that
+ * <code>NodeInfo</code> implements the JAXP <code>Source</code> interface, so
+ * it may be supplied directly to the transform() method.
+ *
+ * @param source the principal input document, supplied as a
+ * {@link SAXSource}, {@link StreamSource}, or {@link Transmitter}
+ * @param mode the initial mode, which must be a streaming mode
+ * @param result The output destination
+ * @throws XPathException if any dynamic error occurs
+ */
+
+ public void transformStream(Source source, Mode mode, Result result)
+ throws TransformerException {
+ // System.err.println("*** TransformDocument");
+ if (executable == null) {
+ throw new XPathException("Stylesheet has not been compiled");
+ }
+
+ openMessageEmitter();
+
+ // Determine whether we need to close the output stream at the end. We
+ // do this if the Result object is a StreamResult and is supplied as a
+ // system ID, not as a Writer or OutputStream
+
+ boolean mustClose = (result instanceof StreamResult &&
+ ((StreamResult) result).getOutputStream() == null);
+
+ principalResult = result;
+ if (principalResultURI == null) {
+ principalResultURI = result.getSystemId();
+ }
+
+ XPathContextMajor initialContext = newXPathContext();
+ initialContext.setOriginatingConstructType(Location.CONTROLLER);
+
+ initialContextItem = null;
+ contextForGlobalVariables = null;
+ initializeController();
+ result = openResult(result, initialContext);
+
+ // Process the source document by applying template rules to the initial context node
+
+ if (!mode.isStreamable()) {
+ throw new XPathException("mode supplied to transformStream() must be streamable");
+ }
+ Receiver despatcher = config.makeStreamingTransformer(initialContext, mode);
+ if (despatcher == null) {
+ throw new TransformerException("Streaming requires Saxon-EE");
+ }
+ if (config.isStripsAllWhiteSpace() || executable.stripsWhitespace()) {
+ despatcher = makeStripper(despatcher);
+ }
+ Sender.send(source, despatcher, null);
+
+ if (traceListener != null) {
+ traceListener.close();
+ }
+
+ closeResult(result, mustClose, initialContext);
+ closeMessageEmitter();
+
+ }
+
+ /**
+ * Get a receiver to which the input to this transformation can be supplied
+ * as a stream of events, causing the transformation to be executed in streaming mode. <br>
+ * <p/>
+ * This method is intended for internal use. External applications should use
+ * the {@link #transform} method, which is part of the JAXP interface. Note that
+ * <code>NodeInfo</code> implements the JAXP <code>Source</code> interface, so
+ * it may be supplied directly to the transform() method.
+ * <p/>
+ * {@link SAXSource}, {@link StreamSource}, or {@link Transmitter}
+ *
+ * @param mode the initial mode, which must be a streaming mode
+ * @param result The output destination
+ * @return a receiver to which events can be streamed
+ * @throws XPathException if any dynamic error occurs
+ */
+
+ /*@Nullable*/
+ public Receiver getStreamingReceiver(Mode mode, Result result)
+ throws TransformerException {
+ // System.err.println("*** TransformDocument");
+ if (executable == null) {
+ throw new XPathException("Stylesheet has not been compiled");
+ }
+
+ openMessageEmitter();
+
+ // Determine whether we need to close the output stream at the end. We
+ // do this if the Result object is a StreamResult and is supplied as a
+ // system ID, not as a Writer or OutputStream
+
+ final boolean mustClose = (result instanceof StreamResult &&
+ ((StreamResult) result).getOutputStream() == null);
+
+ principalResult = result;
+ if (principalResultURI == null) {
+ principalResultURI = result.getSystemId();
+ }
+
+ final XPathContextMajor initialContext = newXPathContext();
+ initialContext.setOriginatingConstructType(Location.CONTROLLER);
+
+ initialContextItem = null;
+ contextForGlobalVariables = null;
+ initializeController();
+ final Result result2 = openResult(result, initialContext);
+
+ // Process the source document by applying template rules to the initial context node
+
+ if (!mode.isStreamable()) {
+ throw new XPathException("mode supplied to getStreamingReceiver() must be streamable");
+ }
+ Receiver despatcher = config.makeStreamingTransformer(initialContext, mode);
+ if (despatcher == null) {
+ throw new TransformerException("Streaming requires Saxon-EE");
+ }
+ if (config.isStripsAllWhiteSpace() || executable.stripsWhitespace()) {
+ despatcher = makeStripper(despatcher);
+ }
+ despatcher.setPipelineConfiguration(makePipelineConfiguration());
+
+ return new ProxyReceiver(despatcher) {
+ public void close() throws XPathException {
+ if (traceListener != null) {
+ traceListener.close();
+ }
+ closeResult(result2, mustClose, initialContext);
+ closeMessageEmitter();
+ }
+ };
+
+ }
+
+
+ private void closeMessageEmitter() throws XPathException {
+ getMessageEmitter().close();
+ }
+
+ private void closeResult(Result result, boolean mustClose, XPathContextMajor initialContext) throws XPathException {
+ Receiver out = initialContext.getReceiver();
+// if (out instanceof ComplexContentOutputter && ((ComplexContentOutputter) out).contentHasBeenWritten()) {
+// if (principalResultURI != null) {
+// DocumentURI documentKey = new DocumentURI(principalResultURI);
+// synchronized (getDocumentPool()) {
+// if (!checkUniqueOutputDestination(documentKey)) {
+// XPathException err = new XPathException(
+// "Cannot write more than one result document to the same URI, or write to a URI that has been read: " +
+// result.getSystemId());
+// err.setErrorCode("XTDE1490");
+// throw err;
+// } else {
+// addUnavailableOutputDestination(documentKey);
+// }
+// }
+// }
+// }
+
+ out.endDocument();
+ out.close();
+
+ if (mustClose && result instanceof StreamResult) {
+ OutputStream os = ((StreamResult) result).getOutputStream();
+ if (os != null) {
+ try {
+ os.close();
+ } catch (IOException err) {
+ throw new XPathException(err);
+ }
+ }
+ }
+ }
+
+ private Result openResult(Result result, XPathContextMajor initialContext) throws TransformerException {
+ Properties xslOutputProps;
+ if (localOutputProperties == null) {
+ xslOutputProps = executable.getDefaultOutputProperties();
+ } else {
+ xslOutputProps = localOutputProperties;
+ }
+
+ // deal with stylesheet chaining
+ String nextInChain = xslOutputProps.getProperty(SaxonOutputKeys.NEXT_IN_CHAIN);
+ if (nextInChain != null && nextInChain.length() > 0) {
+ SerializerFactory factory = getConfiguration().getSerializerFactory();
+ String baseURI = xslOutputProps.getProperty(SaxonOutputKeys.NEXT_IN_CHAIN_BASE_URI);
+ result = factory.prepareNextStylesheet(this, nextInChain, baseURI, result);
+ }
+
+ SerializerFactory sf = getConfiguration().getSerializerFactory();
+ PipelineConfiguration pipe = makePipelineConfiguration();
+ pipe.setHostLanguage(Configuration.XSLT);
+ Receiver receiver = sf.getReceiver(result, pipe, xslOutputProps);
+
+ // if this is the implicit XSLT result document, and if the executable is capable
+ // of creating a secondary result document, then add a filter to check the first write
+
+ boolean openNow = false;
+ if (getExecutable().createsSecondaryResult()) {
+ receiver = new ImplicitResultChecker(receiver, this);
+ receiver.setPipelineConfiguration(pipe);
+ } else {
+ openNow = true;
+ }
+
+ initialContext.changeOutputDestination(receiver, null);
+
+ if (openNow) {
+ Receiver out = initialContext.getReceiver();
+ out.open();
+ out.startDocument(0);
+ }
+ return result;
+ }
+
+ private void openMessageEmitter() throws XPathException {
+ if (getMessageEmitter() == null) {
+ Receiver me = makeMessageReceiver();
+ setMessageEmitter(me);
+ if (me instanceof Emitter && ((Emitter) me).getWriter() == null) {
+ try {
+ ((Emitter) me).setWriter(new OutputStreamWriter(getConfiguration().getStandardErrorOutput()));
+ } catch (Exception err) {
+ // This has been known to fail on .NET because the default encoding set for the
+ // .NET environment is not supported by the Java class library. So we'll try again
+ try {
+ ((Emitter) me).setWriter(new OutputStreamWriter(getConfiguration().getStandardErrorOutput(), "utf8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new XPathException(e);
+ }
+ }
+ }
+ }
+ getMessageEmitter().open();
+ }
+
+ /**
+ * Pre-evaluate global variables (when debugging/tracing).
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @param context the dynamic context for evaluating the global variables
+ * @throws XPathException - should not happen.
+ */
+
+ public void preEvaluateGlobals(XPathContext context) throws XPathException {
+ HashMap<StructuredQName, GlobalVariable> vars = getExecutable().getCompiledGlobalVariables();
+ if (vars != null) {
+ for (GlobalVariable var : vars.values()) {
+ try {
+ var.evaluateVariable(context);
+ } catch (XPathException err) {
+ // Don't report an exception unless the variable is actually evaluated
+ SingletonClosure closure = new SingletonClosure(new ErrorExpression(err), context);
+ getBindery().setGlobalVariable(var, closure);
+ }
+ }
+ }
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // Handle parameters to the transformation
+ //////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Set all parameters for the transformation
+ * @params the parameter values to be used
+ */
+
+ public void setGlobalParameterSet(GlobalParameterSet params) {
+ parameters = params;
+ }
+
+ /**
+ * Set a parameter for the transformation.
+ * <p/>
+ * The following table shows some of the classes that are supported
+ * by this method. (Others may also be supported, but continued support is
+ * not guaranteed.) Each entry in the table shows first the Java class of the
+ * supplied object, and then the type of the resulting XPath value.
+ * <p/>
+ * <table>
+ * <thead>
+ * <tr><th>Java Class</th><th>XPath 2.0 type</th></tr>
+ * </thead>
+ * <tbody>
+ * <tr><td>String</td><td>xs:string</td></tr>
+ * <tr><td>Boolean</td><td>xs:boolean</td></tr>
+ * <tr><td>Integer</td><td>xs:integer</td></tr>
+ * <tr><td>Long</td><td>xs:integer</td></tr>
+ * <tr><td>Double</td><td>xs:double</td></tr>
+ * <tr><td>Float</td><td>xs:float</td></tr>
+ * <tr><td>BigDecimal</td><td>xs:decimal</td></tr>
+ * <tr><td>BigInteger</td><td>xs:integer</td></tr>
+ * <tr><td>Date</td><td>xs:dateTime</td></tr>
+ * <tr><td>Array or List of any of the above</td><td>sequence of the above</td></tr>
+ * <tr><td>null</td><td>empty sequence</td></tr>
+ * </tbody></table>
+ * <p/>
+ * A node may be supplied as a <code>NodeInfo</code> object, a sequence of nodes
+ * as an array or List of <code>NodeInfo</code> objects.
+ * <p/>
+ * In addition, any object that implements the Saxon {@link net.sf.saxon.om.Sequence} interface
+ * may be supplied, and will be used without conversion.
+ * <p/>
+ * A node belong to an external object model (such as DOM, JDOM, or XOM) may be supplied provided (a)
+ * that the external object model is registered with the Configuration, and (b) that the node is part
+ * of a document tree that has been registered in the document pool.
+ *
+ * @param expandedName The name of the parameter in {uri}local format
+ * @param value The value object. This must follow the rules above.
+ * Other formats in addition to those listed above may be accepted.
+ * @since 8.4
+ */
+
+ public void setParameter(/*@Nullable*/ String expandedName, Object value) {
+
+ if (expandedName == null) {
+ throw new NullPointerException("Transformer.setParameter() - expandedName is null");
+ }
+
+ if (parameters == null) {
+ parameters = new GlobalParameterSet();
+ }
+
+ parameters.put(StructuredQName.fromClarkName(expandedName), value);
+
+ }
+
+ /**
+ * Supply a parameter using Saxon-specific representations of the name and value
+ *
+ * @param qName The structured representation of the parameter name
+ * @param value The value of the parameter, or null to remove a previously set value
+ */
+
+ public void setParameter(StructuredQName qName, Sequence value) {
+ if (parameters == null) {
+ parameters = new GlobalParameterSet();
+ }
+ parameters.put(qName, value);
+ }
+
+ /**
+ * Reset the parameters to a null list.
+ */
+
+ public void clearParameters() {
+ parameters = null;
+ }
+
+ /**
+ * Get a parameter to the transformation. This returns the value of a parameter
+ * that has been previously set using the {@link #setParameter} method. The value
+ * is returned exactly as supplied, that is, before any conversion to an XPath value.
+ *
+ * @param expandedName the name of the required parameter, in
+ * "{uri}local-name" format
+ * @return the value of the parameter, if it exists, or null otherwise
+ */
+
+ /*@Nullable*/
+ public Object getParameter(String expandedName) {
+ if (parameters == null) {
+ return null;
+ }
+ return parameters.get(StructuredQName.fromClarkName(expandedName));
+ }
+
+ /**
+ * Get an iterator over the names of global parameters that have been defined
+ *
+ * @return an Iterator whose items are strings in the form of Clark names, that is {uri}local
+ */
+
+ public Iterator iterateParameters() {
+ if (parameters == null) {
+ return Collections.EMPTY_LIST.iterator();
+ }
+ int k = parameters.getNumberOfKeys();
+ List list = new ArrayList(k);
+ Collection keys = parameters.getKeys();
+ for (Iterator it = keys.iterator(); it.hasNext(); ) {
+ StructuredQName qName = (StructuredQName) it.next();
+ String clarkName = qName.getClarkName();
+ list.add(clarkName);
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Set the current date and time for this query or transformation.
+ * This method is provided primarily for testing purposes, to allow tests to be run with
+ * a fixed date and time. The supplied date/time must include a timezone, which is used
+ * as the implicit timezone.
+ * <p/>
+ * <p>Note that comparisons of date/time values currently use the implicit timezone
+ * taken from the system clock, not from the value supplied here.</p>
+ *
+ * @param dateTime the date/time value to be used as the current date and time
+ * @throws IllegalStateException if a current date/time has already been
+ * established by calling getCurrentDateTime(), or by a previous call on setCurrentDateTime()
+ * @throws net.sf.saxon.trans.XPathException
+ * if the supplied dateTime contains no timezone
+ */
+
+ public void setCurrentDateTime(/*@NotNull*/ DateTimeValue dateTime) throws XPathException {
+ if (currentDateTime == null) {
+ if (dateTime.getComponent(Component.TIMEZONE) == null) {
+ throw new XPathException("No timezone is present in supplied value of current date/time");
+ }
+ currentDateTime = dateTime;
+ dateTimePreset = true;
+ } else {
+ throw new IllegalStateException(
+ "Current date and time can only be set once, and cannot subsequently be changed");
+ }
+ }
+
+ /**
+ * Get the current date and time for this query or transformation.
+ * All calls during one transformation return the same answer.
+ *
+ * @return Get the current date and time. This will deliver the same value
+ * for repeated calls within the same transformation
+ */
+
+ /*@Nullable*/
+ public DateTimeValue getCurrentDateTime() {
+ if (currentDateTime == null) {
+ currentDateTime = new DateTimeValue(new GregorianCalendar(), true);
+ }
+ return currentDateTime;
+ }
+
+ /**
+ * Get the implicit timezone for this query or transformation
+ *
+ * @return the implicit timezone as an offset in minutes
+ */
+
+ public int getImplicitTimezone() {
+ return getCurrentDateTime().getTimezoneInMinutes();
+ }
+
+ /////////////////////////////////////////
+ // Methods for handling dynamic context
+ /////////////////////////////////////////
+
+ /**
+ * Make an XPathContext object for expression evaluation.
+ * <p/>
+ * This method is intended for internal use.
+ *
+ * @return the new XPathContext
+ */
+
+ public XPathContextMajor newXPathContext() {
+ return new XPathContextMajor(this);
+ }
+
+ /**
+ * Set the last remembered node, for node numbering purposes.
+ * <p/>
+ * This method is strictly for internal use only.
+ *
+ * @param node the node in question
+ * @param number the number of this node
+ */
+
+ public void setRememberedNumber(NodeInfo node, int number) {
+ lastRememberedNode = node;
+ lastRememberedNumber = number;
+ }
+
+ /**
+ * Get the number of a node if it is the last remembered one.
+ * <p/>
+ * This method is strictly for internal use only.
+ *
+ * @param node the node for which remembered information is required
+ * @return the number of this node if known, else -1.
+ */
+
+ public int getRememberedNumber(NodeInfo node) {
+ if (lastRememberedNode == node) {
+ return lastRememberedNumber;
+ }
+ return -1;
+ }
+
+ /**
+ * Indicate whether document projection should be used, and supply the PathMap used to control it.
+ * Note: this is available only under Saxon-EE.
+ *
+ * @param pathMap a path map to be used for projecting source documents
+ */
+
+ public void setUseDocumentProjection(PathMap pathMap) {
+ this.pathMap = pathMap;
+ }
+
+ /**
+ * Get the path map used for document projection, if any.
+ *
+ * @return the path map to be used for document projection, if one has been supplied; otherwise null
+ */
+
+ /*@Nullable*/
+ public PathMap getPathMapForDocumentProjection() {
+ return pathMap;
+ }
+
+ /**
+ * Set a ClassLoader to be used when loading external classes. Examples of classes that are
+ * loaded include SAX parsers, localization modules for formatting numbers and dates,
+ * extension functions, external object models. In an environment such as Eclipse that uses
+ * its own ClassLoader, this ClassLoader should be nominated to ensure that any class loaded
+ * by Saxon is identical to a class of the same name loaded by the external environment.
+ * <p/>
+ * This method is for application use, but is experimental and subject to change.
+ *
+ * @param loader the ClassLoader to be used.
+ */
+
+ public void setClassLoader(ClassLoader loader) {
+ classLoader = loader;
+ }
+
+ /**
+ * Get the ClassLoader supplied using the method {@link #setClassLoader}.
+ * If none has been supplied, return null.
+ * <p/>
+ * This method is for application use, but is experimental and subject to change.
+ *
+ * @return the ClassLoader in use.
+ */
+
+ /*@Nullable*/
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+}
+
diff --git a/sf/saxon/Filter.java b/sf/saxon/Filter.java
new file mode 100644
index 0000000..3ac431a
--- /dev/null
+++ b/sf/saxon/Filter.java
@@ -0,0 +1,458 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.ContentHandlerProxy;
+import org.xml.sax.*;
+import org.xml.sax.ext.LexicalHandler;
+
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.SAXSource;
+import java.io.IOException;
+
+
+
+/**
+ * <B>Filter</B> is an XMLFilter (a SAX2 filter) that performs a transformation
+ * taking a SAX stream as input and producing a SAX stream as output.
+ * @author Michael H. Kay
+ */
+
+public class Filter implements XMLFilter {
+
+ private Controller controller;
+ private XMLReader parser;
+ private ContentHandler contentHandler; // destination for output of this filter
+ private LexicalHandler lexicalHandler; // destination for output of this filter
+
+
+
+ /**
+ * Create a Filter and initialise variables. The constructor is protected, because
+ * the Filter should be created using newXMLFilter() in the SAXTransformerFactory
+ * class
+ */
+
+ protected Filter(Controller controller) {
+ this.controller = controller;
+ }
+
+
+ //////////////////////////////////////////////////////////////////
+ // Implement XMLFilter interface methods
+ //////////////////////////////////////////////////////////////////
+
+ /**
+ * Set the parent reader.
+ *
+ * <p>This method allows the application to link the filter to
+ * a parent reader (which may be another filter). The argument
+ * may not be null.</p>
+ *
+ * @param parent The parent reader (the supplier of SAX events).
+ */
+
+ public void setParent (XMLReader parent) {
+ parser = parent;
+ }
+
+ /**
+ * Get the parent reader.
+ *
+ * <p>This method allows the application to query the parent
+ * reader (which may be another filter). It is generally a
+ * bad idea to perform any operations on the parent reader
+ * directly: they should all pass through this filter.</p>
+ *
+ * @return The parent filter, or null if none has been set.
+ */
+
+ public XMLReader getParent() {
+ return parser;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // implement XMLReader interface methods
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Look up the value of a feature.
+ *
+ * <p>The feature name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a feature name but
+ * to be unable to return its value; this is especially true
+ * in the case of an adapter for a SAX1 Parser, which has
+ * no way of knowing whether the underlying parser is
+ * performing validation or expanding external entities.</p>
+ *
+ * <p>All XMLReaders are required to recognize the
+ * http://xml.org/sax/features/namespaces and the
+ * http://xml.org/sax/features/namespace-prefixes feature names.</p>
+ *
+ * @param name The feature name, which is a fully-qualified URI.
+ * @return The current state of the feature (true or false).
+ * @exception org.xml.sax.SAXNotRecognizedException When the
+ * XMLReader does not recognize the feature name.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the feature name but
+ * cannot determine its value at this time.
+ * @see #setFeature
+ */
+
+ public boolean getFeature (String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException {
+ if (name.equals("http://xml.org/sax/features/namespaces")) {
+ return true;
+ } else if (name.equals("http://xml.org/sax/features/namespace-prefixes")) {
+ return false;
+ } else {
+ throw new SAXNotRecognizedException(name);
+ }
+ }
+
+
+ /**
+ * Set the state of a feature.
+ *
+ * <p>The feature name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a feature name but
+ * to be unable to set its value</p>
+ *
+ * <p>All XMLReaders are required to support setting
+ * http://xml.org/sax/features/namespaces to true and
+ * http://xml.org/sax/features/namespace-prefixes to false.</p>
+ *
+ * <p>Some feature values may be immutable or mutable only
+ * in specific contexts, such as before, during, or after
+ * a parse.</p>
+ *
+ * @param name The feature name, which is a fully-qualified URI.
+ * @param value The requested state of the feature (true or false).
+ * @exception org.xml.sax.SAXNotRecognizedException When the
+ * XMLReader does not recognize the feature name.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the feature name but
+ * cannot set the requested value.
+ * @see #getFeature
+ */
+
+ public void setFeature (String name, boolean value)
+ throws SAXNotRecognizedException, SAXNotSupportedException {
+ if (name.equals("http://xml.org/sax/features/namespaces")) {
+ if (!value) {
+ throw new SAXNotSupportedException(name);
+ }
+ } else if (name.equals("http://xml.org/sax/features/namespace-prefixes")) {
+ if (value) {
+ throw new SAXNotSupportedException(name);
+ }
+ } else {
+ throw new SAXNotRecognizedException(name);
+ }
+ }
+
+ /**
+ * Look up the value of a property.
+ *
+ * <p>The property name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a property name but
+ * to be unable to return its state.</p>
+ *
+ * <p>XMLReaders are not required to recognize any specific
+ * property names, though an initial core set is documented for
+ * SAX2.</p>
+ *
+ * <p>Some property values may be available only in specific
+ * contexts, such as before, during, or after a parse.</p>
+ *
+ * <p>Implementors are free (and encouraged) to invent their own properties,
+ * using names built on their own URIs.</p>
+ *
+ * @param name The property name, which is a fully-qualified URI.
+ * @return The current value of the property.
+ * @exception org.xml.sax.SAXNotRecognizedException When the
+ * XMLReader does not recognize the property name.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the property name but
+ * cannot determine its value at this time.
+ * @see #setProperty
+ */
+
+ public Object getProperty (String name)
+ throws SAXNotRecognizedException, SAXNotSupportedException {
+ if (name.equals("http://xml.org/sax/properties/lexical-handler")) {
+ return lexicalHandler;
+ } else {
+ throw new SAXNotRecognizedException(name);
+ }
+ }
+
+
+ /**
+ * Set the value of a property.
+ *
+ * <p>The property name is any fully-qualified URI. It is
+ * possible for an XMLReader to recognize a property name but
+ * to be unable to set its value.</p>
+ *
+ * <p>XMLReaders are not required to recognize setting
+ * any specific property names, though a core set is provided with
+ * SAX2.</p>
+ *
+ * <p>Some property values may be immutable or mutable only
+ * in specific contexts, such as before, during, or after
+ * a parse.</p>
+ *
+ * <p>This method is also the standard mechanism for setting
+ * extended handlers.</p>
+ *
+ * @param name The property name, which is a fully-qualified URI.
+ * @param value The requested value for the property.
+ * @exception org.xml.sax.SAXNotRecognizedException When the
+ * XMLReader does not recognize the property name.
+ * @exception org.xml.sax.SAXNotSupportedException When the
+ * XMLReader recognizes the property name but
+ * cannot set the requested value.
+ */
+
+ public void setProperty (String name, Object value)
+ throws SAXNotRecognizedException, SAXNotSupportedException {
+ if (name.equals("http://xml.org/sax/properties/lexical-handler")) {
+ if (value instanceof LexicalHandler) {
+ lexicalHandler = (LexicalHandler)value;
+ } else {
+ throw new SAXNotSupportedException(
+ "Lexical Handler must be instance of org.xml.sax.ext.LexicalHandler");
+ }
+ } else {
+ throw new SAXNotRecognizedException(name);
+ }
+ }
+
+ /**
+ * Register a content handler to receive the output of the transformation
+ * filter. If the content handler is also a LexicalHandler, and if no LexicalHandler
+ * is separately registered, the ContentHandler will also act as the LexicalHandler
+ */
+
+ public void setContentHandler(ContentHandler handler) {
+ contentHandler = handler;
+ if (handler instanceof LexicalHandler && lexicalHandler==null) {
+ lexicalHandler = (LexicalHandler)handler;
+ }
+ }
+
+ /**
+ * Get the ContentHandler registered using setContentHandler()
+ */
+
+ public ContentHandler getContentHandler() {
+ return contentHandler;
+ }
+
+
+ /**
+ * Allow an application to register an entity resolver.
+ *
+ * <p>If the application does not register an entity resolver,
+ * the XMLReader will perform its own default resolution.</p>
+ *
+ * <p>Applications may register a new or different resolver in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * resolver immediately.</p>
+ *
+ * @param resolver The entity resolver.
+ * @exception java.lang.NullPointerException If the resolver
+ * argument is null.
+ * @see #getEntityResolver
+ */
+
+ public void setEntityResolver (EntityResolver resolver) {
+ // XSLT output does not use entities, so the resolver is never used
+ }
+
+
+ /**
+ * Return the current entity resolver.
+ *
+ * @return Always null, since no entity resolver is used even if one
+ * is supplied.
+ * @see #setEntityResolver
+ */
+
+ /*@Nullable*/ public EntityResolver getEntityResolver () {
+ return null;
+ }
+
+
+ /**
+ * Allow an application to register a DTD event handler.
+ *
+ * <p>If the application does not register a DTD handler, all DTD
+ * events reported by the SAX parser will be silently ignored.</p>
+ *
+ * <p>Applications may register a new or different handler in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * handler immediately.</p>
+ *
+ * @param handler The DTD handler.
+ * @exception java.lang.NullPointerException If the handler
+ * argument is null.
+ * @see #getDTDHandler
+ */
+
+ public void setDTDHandler (DTDHandler handler) {
+ // XSLT output does not include a DTD
+ }
+
+
+ /**
+ * Return the current DTD handler.
+ *
+ * @return Always null, since no DTD handler is used even if one has been
+ * supplied.
+ * @see #setDTDHandler
+ */
+
+ /*@Nullable*/ public DTDHandler getDTDHandler () {
+ return null;
+ }
+
+
+
+ /**
+ * Allow an application to register an error event handler.
+ *
+ * <p>If the application does not register an error handler, all
+ * error events reported by the SAX parser will be silently
+ * ignored; however, normal processing may not continue. It is
+ * highly recommended that all SAX applications implement an
+ * error handler to avoid unexpected bugs.</p>
+ *
+ * <p>Applications may register a new or different handler in the
+ * middle of a parse, and the SAX parser must begin using the new
+ * handler immediately.</p>
+ *
+ * @param handler The error handler.
+ * @exception java.lang.NullPointerException If the handler
+ * argument is null.
+ * @see #getErrorHandler
+ */
+
+ public void setErrorHandler (ErrorHandler handler) {
+ // No effect
+ }
+
+ /**
+ * Return the current error handler.
+ *
+ * @return The current error handler, or null if none
+ * has been registered.
+ * @see #setErrorHandler
+ */
+ /*@Nullable*/ public ErrorHandler getErrorHandler () {
+ return null;
+ }
+
+ /**
+ * Parse an XML document - In the context of a Transformer, this means
+ * perform a transformation. The method is equivalent to transform().
+ *
+ * @param input The input source (the XML document to be transformed)
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @exception java.io.IOException An IO exception from the parser,
+ * possibly from a byte stream or character stream
+ * supplied by the application.
+ * @see org.xml.sax.InputSource
+ * @see #parse(java.lang.String)
+ * @see #setEntityResolver
+ * @see #setDTDHandler
+ * @see #setContentHandler
+ * @see #setErrorHandler
+ */
+
+ public void parse (InputSource input) throws IOException, SAXException {
+ if (parser==null) {
+ try {
+ parser = Configuration.getPlatform().loadParser();
+ } catch (Exception err) {
+ throw new SAXException(err);
+ }
+ }
+ SAXSource source = new SAXSource();
+ source.setInputSource(input);
+ source.setXMLReader(parser);
+ ContentHandlerProxy result = new ContentHandlerProxy();
+ result.setPipelineConfiguration(controller.makePipelineConfiguration());
+ result.setUnderlyingContentHandler(contentHandler);
+
+ if (lexicalHandler!=null) {
+ result.setLexicalHandler(lexicalHandler);
+ }
+ try {
+ //result.open();
+ result.setOutputProperties(controller.getOutputProperties());
+ controller.transform(source, result);
+ } catch (TransformerException err) {
+ Throwable cause = err.getException();
+ if (cause != null && cause instanceof SAXException) {
+ throw (SAXException)cause;
+ } else if (cause != null && cause instanceof IOException) {
+ throw (IOException)cause;
+ } else {
+ throw new SAXException(err);
+ }
+ }
+
+
+ }
+
+ /**
+ * Parse (that is, transform) an XML document given a system identifier (URI).
+ *
+ * <p>This method is a shortcut for the common case of reading a
+ * document from a system identifier. It is the exact
+ * equivalent of the following:</p>
+ *
+ * <pre>
+ * parse(new InputSource(systemId));
+ * </pre>
+ *
+ * <p>If the system identifier is a URL, it must be fully resolved
+ * by the application before it is passed to the parser.</p>
+ *
+ * @param systemId The system identifier (URI).
+ * @exception org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @exception java.io.IOException An IO exception from the parser,
+ * possibly from a byte stream or character stream
+ * supplied by the application.
+ * @see #parse(org.xml.sax.InputSource)
+ */
+
+ public void parse (String systemId) throws IOException, SAXException {
+ InputSource input = new InputSource(systemId);
+ parse(input);
+ }
+
+
+ /**
+ * Get the underlying Transformer. This is a Saxon-specific method that allows the
+ * user to set parameters on the transformation, set a URIResolver or ErrorListener, etc.
+ * New in Saxon 7.2
+ */
+
+ public Transformer getTransformer() {
+ return controller;
+ }
+
+
+}
+
diff --git a/sf/saxon/IdentityTransformer.java b/sf/saxon/IdentityTransformer.java
new file mode 100644
index 0000000..98b799f
--- /dev/null
+++ b/sf/saxon/IdentityTransformer.java
@@ -0,0 +1,67 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.NamespaceReducer;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.Sender;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.SerializerFactory;
+import net.sf.saxon.trans.XPathException;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+
+class IdentityTransformer extends Controller {
+
+ protected IdentityTransformer(Configuration config) {
+ super(config);
+ }
+
+ /**
+ * Perform identify transformation from Source to Result
+ */
+
+ public void transform(Source source, Result result)
+ throws TransformerException {
+ try {
+ if (getConfiguration().isLicensedFeature(Configuration.LicenseFeature.SCHEMA_VALIDATION)) {
+ getExecutable().setSchemaAware(true);
+ }
+ PipelineConfiguration pipe = makePipelineConfiguration();
+ SerializerFactory sf = getConfiguration().getSerializerFactory();
+ Receiver receiver = sf.getReceiver(
+ result, pipe, getOutputProperties());
+ NamespaceReducer reducer = new NamespaceReducer(receiver);
+ ParseOptions options = pipe.getParseOptions();
+ options.setContinueAfterValidationErrors(true);
+ Sender.send(source, reducer, options);
+ } catch (XPathException err) {
+ Throwable cause = err.getException();
+ if (cause != null && cause instanceof SAXParseException) {
+ // This generally means the error was already reported.
+ // But if a RuntimeException occurs in Saxon during a callback from
+ // the Crimson parser, Crimson wraps this in a SAXParseException without
+ // reporting it further.
+ SAXParseException spe = (SAXParseException)cause;
+ cause = spe.getException();
+ if (cause instanceof RuntimeException) {
+ reportFatalError(err);
+ }
+ } else {
+ reportFatalError(err);
+ }
+ throw err;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/IdentityTransformerHandler.java b/sf/saxon/IdentityTransformerHandler.java
new file mode 100644
index 0000000..ccc46cd
--- /dev/null
+++ b/sf/saxon/IdentityTransformerHandler.java
@@ -0,0 +1,123 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceivingContentHandler;
+import net.sf.saxon.event.Stripper;
+import net.sf.saxon.lib.SerializerFactory;
+import net.sf.saxon.om.AllElementsSpaceStrippingRule;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.SAXException;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import java.util.Properties;
+
+/**
+ * <b>IdentityTransformerHandler</b> implements the javax.xml.transform.sax.TransformerHandler
+ * interface. It acts as a ContentHandler and LexicalHandler which receives a stream of
+ * SAX events representing an input document, and performs an identity transformation passing
+ * these events to a Result
+ * @author Michael H. Kay
+ */
+
+public class IdentityTransformerHandler extends ReceivingContentHandler implements TransformerHandler {
+
+ /*@Nullable*/ private Result result;
+ private String systemId;
+ private Controller controller;
+
+ /**
+ * Create a IdentityTransformerHandler and initialise variables. The constructor is protected, because
+ * the Filter should be created using newTransformerHandler() in the SAXTransformerFactory
+ * class
+ * @param controller the Controller for this transformation
+ */
+
+ protected IdentityTransformerHandler(Controller controller) {
+ this.controller = controller;
+ setPipelineConfiguration(controller.makePipelineConfiguration());
+
+ }
+
+ /**
+ * Get the Transformer used for this transformation
+ */
+
+ public Transformer getTransformer() {
+ return controller;
+ }
+
+ /**
+ * Set the SystemId of the document
+ */
+
+ public void setSystemId(String url) {
+ systemId = url;
+ }
+
+ /**
+ * Get the systemId of the document
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Set the output destination of the transformation
+ */
+
+ public void setResult(/*@Nullable*/ Result result) {
+ if (result==null) {
+ throw new IllegalArgumentException("Result must not be null");
+ }
+ this.result = result;
+ }
+
+ /**
+ * Get the output destination of the transformation
+ * @return the output destination
+ */
+
+ /*@Nullable*/ public Result getResult() {
+ return result;
+ }
+
+ /**
+ * Override the behaviour of startDocument() in ReceivingContentHandler
+ */
+
+ public void startDocument() throws SAXException {
+ if (result==null) {
+ result = new StreamResult(System.out);
+ }
+ try {
+ Properties props = controller.getOutputProperties();
+ PipelineConfiguration pipe = controller.makePipelineConfiguration();
+ Configuration config = getConfiguration();
+ SerializerFactory sf = config.getSerializerFactory();
+ Receiver out = sf.getReceiver(result, pipe, props);
+ setPipelineConfiguration(pipe);
+ if (config.getStripsWhiteSpace() == Whitespace.ALL) {
+ out = new Stripper(AllElementsSpaceStrippingRule.getInstance(), out);
+ }
+ setReceiver(out);
+ } catch (XPathException err) {
+ throw new SAXException(err);
+ }
+ super.startDocument();
+ }
+
+}
+
diff --git a/sf/saxon/Platform.java b/sf/saxon/Platform.java
new file mode 100644
index 0000000..f911776
--- /dev/null
+++ b/sf/saxon/Platform.java
@@ -0,0 +1,195 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.regex.RegularExpression;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import org.xml.sax.XMLReader;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * This interface provides access to methods whose implementation depends on the chosen platform
+ * (typically Java or .NET)
+ */
+public interface Platform extends Serializable {
+
+ /**
+ * Perform platform-specific initialization of the configuration
+ * @param config the Saxon Configuration
+ */
+
+ public void initialize(Configuration config);
+
+ /**
+ * Return true if this is the Java platform
+ * @return true if this is the Java platform
+ */
+
+ public boolean isJava();
+
+ /**
+ * Return true if this is the .NET platform
+ * @return true if this is the .NET platform
+ */
+
+ public boolean isDotNet();
+
+ /**
+ * Get the platform version
+ * @return the version of the platform, for example "Java version 1.5.09"
+ */
+
+ public String getPlatformVersion();
+
+ /**
+ * Get a suffix letter to add to the Saxon version number to identify the platform
+ * @return "J" for Java, "N" for .NET
+ */
+
+ public String getPlatformSuffix();
+
+ /**
+ * Get a parser by instantiating the SAXParserFactory
+ *
+ * @return the parser (XMLReader)
+ */
+
+ public XMLReader loadParser();
+
+ /**
+ * Convert a StreamSource to either a SAXSource or a PullSource, depending on the native
+ * parser of the selected platform
+ * @param pipe the pipeline Configuration
+ * @param input the supplied StreamSource
+ * @param validation required validation mode, for example Validation.STRICT
+ * @param dtdValidation true if DTD-based input validation is required
+ * @param stripspace option for whitespace-stripping (ALL, NONE, or IGNORABLE)
+ * @return the PullSource or SAXSource, initialized with a suitable parser, or the original
+ * input Source, if now special handling is required or possible
+ */
+
+ public Source getParserSource(PipelineConfiguration pipe, StreamSource input,
+ int validation, boolean dtdValidation, int stripspace);
+
+ /**
+ * Obtain a collation with a given set of properties. The set of properties is extensible
+ * and variable across platforms. Common properties with example values include lang=en-GB,
+ * strength=primary, case-order=upper-first, ignore-modifiers=yes, alphanumeric=yes.
+ * Properties that are not supported are generally ignored; however some errors, such as
+ * failing to load a requested class, are fatal.
+ * @param config the configuration object
+ * @param props the desired properties of the collation
+ * @param uri the collation URI
+ * @return a collation with these properties
+ * @throws XPathException if a fatal error occurs
+ */
+
+ /*@Nullable*/ public StringCollator makeCollation(Configuration config, Properties props, String uri) throws XPathException;
+
+ /**
+ * Given a collation, determine whether it is capable of returning collation keys.
+ * The essential property of collation keys
+ * is that if two values are equal under the collation, then the collation keys are
+ * equal under the equals() method.
+ * @param collation the collation being examined, provided as a Comparator
+ * @return true if this collation can supply collation keys
+ */
+
+ public boolean canReturnCollationKeys(StringCollator collation);
+
+ /**
+ * Given a collation, get a collation key. The essential property of collation keys
+ * is that if two values are equal under the collation, then the collation keys are
+ * equal under the equals() method.
+ * @param namedCollation the collation in use
+ * @param value the string whose collation key is required
+ * @return a representation of the collation key, such that two collation keys are
+ * equal() if and only if the string values they represent are equal under the specified collation.
+ * @throws ClassCastException if the collation is not one that is capable of supplying
+ * collation keys (this should have been checked in advance)
+ */
+
+ public Object getCollationKey(SimpleCollation namedCollation, String value);
+
+ /**
+ * Compile a regular expression
+ *
+ * @param regex the regular expression as a string
+ * @param flags the value of the flags attribute
+ * @param hostLanguage one of "XSD", XP20" or "XP30"
+ * @param warnings if non-null, any warnings from the regular expression compiler will be added to this list.
+ * If null, the warnings are ignored.
+ * @return the compiled regular expression
+ * @throws XPathException if the regular expression or the flags are invalid
+ */
+
+ public RegularExpression compileRegularExpression(CharSequence regex, String flags, String hostLanguage, List<String> warnings)
+ throws XPathException;
+
+ /**
+ * Get a SchemaType representing a wrapped external (Java or .NET) object
+ * @param config the Saxon Configuration
+ * @param uri the namespace URI of the schema type
+ * @param localName the local name of the schema type
+ * @return the SchemaType object representing this type
+ */
+
+ public SchemaType getExternalObjectType(Configuration config, String uri, String localName);
+
+ /**
+ * Return the name of the directory in which the software is installed (if available)
+ * @return the name of the directory in which Saxon is installaed, if available, or null otherwise
+ * @param edition The edition of the software that is loaded ("HE", "PE", or "EE")
+ * @param config the Saxon configuration
+ */
+
+ public String getInstallationDirectory(String edition, Configuration config);
+
+ /**
+ * Register all the external object models that are provided as standard
+ * with the relevant edition of Saxon for this Configuration
+ *
+ * @since 9.3
+ */
+
+ public void registerAllBuiltInObjectModels(Configuration config);
+
+ /**
+ * Set the default XML parser to be loaded by the SAXParserFactory on this platform.
+ * Needed because the Apache catalog resolver uses the SAXParserFactory to instantiate
+ * a parser, and if not customized this causes a failure on the .NET platform.
+ *
+ * @since 9.4
+ */
+
+ public void setDefaultSAXParserFactory();
+
+ /**
+ * Return the class loader required to load the bytecode generated classes
+ * @return the class loader object
+ * @param definedClassName The generated class name
+ * @param classFile The bytecode of the generated class
+ * @param config The cThe saxon configuration
+ * @param thisClass The class object generated
+ *
+ * @since 9.4
+ *
+ * */
+
+ public ClassLoader getClassLoaderForGeneratedClass(final String definedClassName, final byte[] classFile, Configuration config, Class thisClass);
+}
+
diff --git a/sf/saxon/PreparedStylesheet.java b/sf/saxon/PreparedStylesheet.java
new file mode 100644
index 0000000..9c3d7f4
--- /dev/null
+++ b/sf/saxon/PreparedStylesheet.java
@@ -0,0 +1,746 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.GlobalParameterSet;
+import net.sf.saxon.expr.instruct.Template;
+import net.sf.saxon.expr.instruct.UserFunction;
+import net.sf.saxon.functions.ExecutableFunctionLibrary;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.functions.FunctionLibraryList;
+import net.sf.saxon.lib.AugmentedSource;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.om.StylesheetSpaceStrippingRule;
+import net.sf.saxon.style.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.*;
+import net.sf.saxon.tree.linked.DocumentImpl;
+import net.sf.saxon.tree.linked.LinkedTreeBuilder;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import javax.xml.transform.*;
+import javax.xml.transform.sax.SAXSource;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * This <B>PreparedStylesheet</B> class represents a Stylesheet that has been
+ * prepared for execution (or "compiled").
+ * <p/>
+ * Note that the PreparedStylesheet object does not contain a reference to the source stylesheet
+ * tree (rooted at an XSLStyleSheet object). This allows the source tree to be garbage-collected
+ * when it is no longer required.
+ */
+
+public class PreparedStylesheet extends Executable implements Templates, Serializable {
+
+ private CompilerInfo compilerInfo;
+ private transient StyleNodeFactory nodeFactory;
+ private int errorCount = 0;
+ private HashMap<URI, PreparedStylesheet> nextStylesheetCache;
+ // cache for stylesheets named as "saxon:next-in-chain"
+
+ // definitions of decimal formats
+ private DecimalFormatManager decimalFormatManager;
+
+ // definitions of template rules (XSLT only)
+ private RuleManager ruleManager;
+
+ // index of named templates.
+ private HashMap<StructuredQName, Template> namedTemplateTable;
+
+ // a boolean flag indicating whether the stylesheet makes any use of tunnel parameters
+ private boolean usesTunnel = false;
+
+ // manager class for accumulator rules (XSLT 3.0 only)
+ private IAccumulatorManager accumulatorManager = null;
+
+ // manager list of variables coming from xsl:param and xsl:variable relating to statis attribute
+ private GlobalParameterSet variableList = new GlobalParameterSet();
+
+
+
+ /**
+ * Constructor - deliberately protected
+ * @param config The Configuration set up by the TransformerFactory
+ * @param info Compilation options
+ */
+
+ protected PreparedStylesheet(Configuration config, CompilerInfo info) {
+ super(config);
+ nodeFactory = config.makeStyleNodeFactory();
+ nodeFactory.setXsltProcessorVersion(info.getXsltVersion());
+ nodeFactory.makeAccumulatorManager(this);
+ RuleManager rm = new RuleManager();
+ rm.setRecoveryPolicy(info.getRecoveryPolicy());
+ setRuleManager(rm);
+ setHostLanguage(Configuration.XSLT, info.getXsltVersion().equals(DecimalValue.THREE));
+ setCollationMap(info.getCollationMap());
+ setSchemaAware(info.isSchemaAware());
+ compilerInfo = info;
+ if (compilerInfo.getErrorListener() == null) {
+ compilerInfo.setErrorListener(config.getErrorListener());
+ }
+ }
+
+ /**
+ * Factory method to make a PreparedStylesheet
+ * @param source the source of this principal stylesheet module
+ * @param config the Saxon configuration
+ * @param info compile-time options for this stylesheet compilation
+ * @return the prepared stylesheet
+ * @throws javax.xml.transform.TransformerConfigurationException if there is a static error in the stylesheet
+ */
+
+ public static PreparedStylesheet compile(Source source, Configuration config, CompilerInfo info)
+ throws TransformerConfigurationException {
+ PreparedStylesheet pss = new PreparedStylesheet(config, info);
+ pss.prepare(source);
+ return pss;
+ }
+
+ /**
+ * Make a Transformer from this Templates object.
+ * @return the new Transformer (always a Controller)
+ * @see net.sf.saxon.Controller
+ */
+
+ public Transformer newTransformer() {
+ Controller c = new Controller(getConfiguration(), this);
+ c.setPreparedStylesheet(this);
+ c.setRecoveryPolicy(compilerInfo.getRecoveryPolicy());
+ if (compilerInfo.getDefaultInitialTemplate() != null) {
+ try {
+ c.setInitialTemplate(compilerInfo.getDefaultInitialTemplate().getClarkName());
+ } catch (XPathException err) {
+ // ignore error if there is no template with this name
+ }
+
+ }
+ if (compilerInfo.getDefaultInitialMode() != null) {
+ c.setInitialMode(compilerInfo.getDefaultInitialMode().getClarkName());
+ }
+
+
+ return c;
+ }
+
+ /**
+ * Set the configuration in which this stylesheet is compiled.
+ * Intended for internal use.
+ * @param config the configuration to be used.
+ */
+
+ public void setConfiguration(Configuration config) {
+ super.setConfiguration(config);
+ this.compilerInfo = config.getDefaultXsltCompilerInfo();
+ }
+
+ /**
+ * Get the StyleNodeFactory in use. The StyleNodeFactory determines which subclass of StyleElement
+ * to use for each element node in the stylesheet tree.
+ * @return the StyleNodeFactory
+ */
+
+ public StyleNodeFactory getStyleNodeFactory() {
+ return nodeFactory;
+ }
+
+ /**
+ * Set the DecimalFormatManager which handles decimal-format definitions
+ * @param dfm the DecimalFormatManager containing the named xsl:decimal-format definitions
+ */
+
+ public void setDecimalFormatManager(DecimalFormatManager dfm) {
+ decimalFormatManager = dfm;
+ }
+
+ /**
+ * Get the DecimalFormatManager which handles decimal-format definitions
+ * @return the DecimalFormatManager containing the named xsl:decimal-format definitions
+ */
+
+ public DecimalFormatManager getDecimalFormatManager() {
+ if (decimalFormatManager == null) {
+ decimalFormatManager = new DecimalFormatManager();
+ }
+ return decimalFormatManager;
+ }
+
+ /**
+ * Get the class that manages accumulator functions
+ * @return the class that manages accumulators. Always null in Saxon-HE
+ */
+
+ public IAccumulatorManager getAccumulatorManager() {
+ return accumulatorManager;
+ }
+
+ /**
+ * Set the class that manages accumulator functions
+ * @param accumulatorManager the manager of accumulator functions
+ */
+
+ public void setAccumulatorManager(IAccumulatorManager accumulatorManager) {
+ this.accumulatorManager = accumulatorManager;
+ }
+
+ /**
+ * Say that the stylesheet uses tunnel parameters. (This information is used by the bytecode generator,
+ * which avoids generating code to pass tunnel parameters on every apply-templates call if there are no
+ * tunnel parameters anywhere in the stylesheet).
+ */
+
+ public void setUsesTunnelParameters() {
+ usesTunnel = true;
+ }
+
+ /**
+ * Ask whether the stylesheet uses tunnel parameters. (Called by the bytecode generator).
+ * @return true if the stylesheet uses tunnel parameters.
+ */
+
+ public boolean usesTunnelParameters() {
+ return usesTunnel;
+ }
+
+
+ /**
+ * Prepare a stylesheet from a Source document
+ * @param styleSource the source document containing the stylesheet
+ * @throws TransformerConfigurationException
+ * if compilation of the
+ * stylesheet fails for any reason
+ */
+
+ protected void prepare(Source styleSource) throws TransformerConfigurationException {
+ DocumentImpl doc;
+ try {
+ doc = loadStylesheetModule(styleSource);
+ setStylesheetDocument(doc);
+ } catch (XPathException e) {
+ try {
+ compilerInfo.getErrorListener().fatalError(e);
+ } catch (TransformerException e2) {
+ // ignore an exception thrown by the error handler
+ }
+ if (errorCount == 0) {
+ errorCount++;
+ }
+ }
+
+ if (errorCount > 0) {
+ throw new TransformerConfigurationException(
+ "Failed to compile stylesheet. " +
+ errorCount +
+ (errorCount == 1 ? " error " : " errors ") +
+ "detected.");
+ }
+ }
+
+ /**
+ * Build the tree representation of a stylesheet module
+ * @param styleSource the source of the module
+ * @return the root Document node of the tree containing the stylesheet
+ * module
+ * @throws XPathException if XML parsing or tree
+ * construction fails
+ */
+ public DocumentImpl loadStylesheetModule(Source styleSource)
+ throws XPathException {
+
+ StyleNodeFactory nodeFactory = getStyleNodeFactory();
+
+ PipelineConfiguration pipe = getConfiguration().makePipelineConfiguration();
+ LinkedTreeBuilder styleBuilder = new LinkedTreeBuilder(pipe);
+ pipe.setURIResolver(compilerInfo.getURIResolver());
+ styleBuilder.setSystemId(styleSource.getSystemId());
+ styleBuilder.setNodeFactory(nodeFactory);
+ styleBuilder.setLineNumbering(true);
+
+ UseWhenFilter useWhenFilter = new UseWhenFilter(this, styleBuilder);
+ StartTagBuffer startTagBuffer = new StartTagBuffer(useWhenFilter);
+ useWhenFilter.setStartTagBuffer(startTagBuffer);
+
+ StylesheetSpaceStrippingRule rule = new StylesheetSpaceStrippingRule(getConfiguration().getNamePool());
+ Stripper styleStripper = new Stripper(rule, startTagBuffer);
+ CommentStripper commentStripper = new CommentStripper(styleStripper);
+
+ // build the stylesheet document
+
+ DocumentImpl doc;
+
+ ParseOptions options;
+ if (styleSource instanceof AugmentedSource) {
+ options = ((AugmentedSource)styleSource).getParseOptions();
+ styleSource = ((AugmentedSource)styleSource).getContainedSource();
+ } else {
+ options = new ParseOptions();
+ }
+ options.setSchemaValidationMode(Validation.STRIP);
+ options.setDTDValidationMode(Validation.STRIP);
+ options.setLineNumbering(true);
+ options.setStripSpace(Whitespace.NONE);
+ try {
+ if (options.getXMLReader() == null && Configuration.getPlatform().isJava()) {
+ XMLReader styleParser = getConfiguration().getStyleParser();
+ options.setXMLReader(styleParser);
+ Sender.send(styleSource, commentStripper, options);
+ getConfiguration().reuseStyleParser(styleParser);
+ } else {
+ Sender.send(styleSource, commentStripper, options);
+ }
+ doc = (DocumentImpl)styleBuilder.getCurrentRoot();
+ styleBuilder.reset();
+ return doc;
+
+ } finally {
+ if (options.isPleaseCloseAfterUse()) {
+ ParseOptions.close(styleSource);
+ }
+ }
+ }
+
+
+ /**
+ * Create a PreparedStylesheet from a supplied DocumentInfo
+ * Note: the document must have been built using the StyleNodeFactory
+ * @param doc the document containing the stylesheet module
+ * @throws XPathException if the document supplied
+ * is not a stylesheet
+ */
+
+ protected void setStylesheetDocument(DocumentImpl doc)
+ throws XPathException {
+
+ DocumentImpl styleDoc = doc;
+
+ // If top-level node is a literal result element, stitch it into a skeleton stylesheet
+
+ StyleElement topnode = (StyleElement)styleDoc.getDocumentElement();
+ if (topnode instanceof LiteralResultElement) {
+ styleDoc = ((LiteralResultElement)topnode).makeStylesheet(this);
+ }
+
+ if (!(styleDoc.getDocumentElement() instanceof XSLStylesheet)) {
+ throw new XPathException(
+ "Outermost element of stylesheet is not xsl:stylesheet or xsl:transform or literal result element");
+ }
+
+ XSLStylesheet top = (XSLStylesheet)styleDoc.getDocumentElement();
+ if (compilerInfo.isVersionWarning() &&
+ top.getEffectiveVersion().compareTo(getStyleNodeFactory().getXsltProcessorVersion()) != 0) {
+ try {
+ TransformerException w = new TransformerException(
+ "Running an XSLT " + top.getEffectiveVersion() + " stylesheet with an XSLT " +
+ getStyleNodeFactory().getXsltProcessorVersion() + " processor");
+ w.setLocator(topnode);
+ getConfiguration().getErrorListener().warning(w);
+ } catch (TransformerException e) {
+ throw XPathException.makeXPathException(e);
+ }
+ }
+
+ if (getStyleNodeFactory().getXsltProcessorVersion().compareTo(DecimalValue.TWO) > 0) {
+ // The condition is checked again later in non-open code, but we can give a better error message here
+ getConfiguration().checkLicensedFeature(Configuration.LicenseFeature.PROFESSIONAL_EDITION, "XSLT 3.0");
+ }
+
+
+ PrincipalStylesheetModule psm = new PrincipalStylesheetModule(top, 0);
+ psm.setPreparedStylesheet(this);
+ psm.setVersion(top.getAttributeValue("version"));
+ psm.createFunctionLibrary(compilerInfo);
+
+ // Preprocess the stylesheet, performing validation and preparing template definitions
+
+ //executable.setLocationMap(psm.getLocationMap());
+ top.setPrincipalStylesheetModule(psm);
+ try {
+ psm.preprocess();
+ } catch (XPathException e) {
+ Throwable e2 = e.getException();
+ if (e2 instanceof XPathException) {
+ try {
+ compilerInfo.getErrorListener().fatalError((XPathException)e2);
+ } catch (TransformerException e3) {
+ // ignore an error thrown by the ErrorListener
+ }
+ }
+ throw e;
+ }
+
+ // Compile the stylesheet, retaining the resulting executable
+
+ psm.compileStylesheet();
+ }
+
+ /**
+ * Set the RuleManager that handles template rules
+ *
+ * @param rm the RuleManager containing details of all the template rules
+ */
+
+ public void setRuleManager(RuleManager rm) {
+ ruleManager = rm;
+ }
+
+ /**
+ * Get the RuleManager which handles template rules
+ *
+ * @return the RuleManager registered with setRuleManager
+ */
+
+ public RuleManager getRuleManager() {
+ return ruleManager;
+ }
+
+ /**
+ * Get the named template with a given name.
+ *
+ * @param qName The template name
+ * @return The template (of highest import precedence) with this name if there is one;
+ * null if none is found.
+ */
+
+ /*@Nullable*/ public Template getNamedTemplate(StructuredQName qName) {
+ if (namedTemplateTable == null) {
+ return null;
+ }
+ return namedTemplateTable.get(qName);
+ }
+
+ /**
+ * Register the named template with a given name
+ * @param templateName the name of a named XSLT template
+ * @param template the template
+ */
+
+ public void putNamedTemplate(StructuredQName templateName, Template template) {
+ if (namedTemplateTable == null) {
+ namedTemplateTable = new HashMap<StructuredQName, Template>(32);
+ }
+ namedTemplateTable.put(templateName, template);
+ }
+
+ /**
+ * Iterate over all the named templates defined in this Executable
+ * @return an iterator, the items returned being of class {@link net.sf.saxon.expr.instruct.Template}
+ */
+
+ public Iterator<Template> iterateNamedTemplates() {
+ if (namedTemplateTable == null) {
+ List<Template> list = Collections.emptyList();
+ return list.iterator();
+ } else {
+ return namedTemplateTable.values().iterator();
+ }
+ }
+
+ /**
+ * Explain the expression tree for named templates in a stylesheet
+ * @param presenter destination for the explanatory output
+ */
+
+ public void explainNamedTemplates(ExpressionPresenter presenter) {
+ presenter.startElement("namedTemplates");
+ if (namedTemplateTable != null) {
+ for (Template t : namedTemplateTable.values()) {
+ presenter.startElement("template");
+ presenter.emitAttribute("name", t.getTemplateName().getDisplayName());
+ presenter.emitAttribute("line", t.getLineNumber() + "");
+ presenter.emitAttribute("module", t.getSystemId());
+ if (t.getBody() != null) {
+ t.getBody().explain(presenter);
+ }
+ presenter.endElement();
+ }
+ }
+ presenter.endElement();
+ }
+
+
+ /**
+ * Determine whether trace hooks are included in the compiled code.
+ * @return true if trace hooks are included, false if not.
+ * @since 8.9
+ */
+
+ public boolean isCompileWithTracing() {
+ return compilerInfo.isCompileWithTracing();
+ }
+
+
+ /**
+ * Get the properties for xsl:output. JAXP method. The object returned will
+ * be a clone of the internal values, and thus it can be mutated
+ * without mutating the Templates object, and then handed in to
+ * the process method.
+ * <p>In Saxon, the properties object is a new, empty, Properties object that is
+ * backed by the live properties to supply default values for missing properties.
+ * This means that the property values must be read using the getProperty() method.
+ * Calling the get() method on the underlying Hashtable will return null.</p>
+ * <p>In Saxon 8.x, this method gets the output properties for the unnamed output
+ * format in the stylesheet.</p>
+ * @return A Properties object reflecting the output properties defined
+ * for the default (unnamed) output format in the stylesheet. It may
+ * be mutated and supplied to the setOutputProperties() method of the
+ * Transformer, without affecting other transformations that use the
+ * same stylesheet.
+ * @see javax.xml.transform.Transformer#setOutputProperties
+ */
+
+
+ public Properties getOutputProperties() {
+ Properties details = getDefaultOutputProperties();
+ return new Properties(details);
+ }
+
+ /**
+ * Report a compile time error. This calls the errorListener to output details
+ * of the error, and increments an error count.
+ * @param err the exception containing details of the error
+ * @throws TransformerException if the ErrorListener decides that the
+ * error should be reported
+ */
+
+ public void reportError(TransformerException err) throws TransformerException {
+ if (err instanceof XPathException) {
+ if (!((XPathException)err).hasBeenReported()) {
+ errorCount++;
+ try {
+ compilerInfo.getErrorListener().fatalError(err);
+ ((XPathException)err).setHasBeenReported(true);
+ } catch (Exception err2) {
+ // ignore secondary error
+ }
+ } else if (errorCount == 0) {
+ errorCount++;
+ }
+ } else {
+ errorCount++;
+ compilerInfo.getErrorListener().fatalError(err);
+ }
+ }
+
+ /**
+ * Get the number of errors reported so far
+ * @return the number of errors reported
+ */
+
+ public int getErrorCount() {
+ return errorCount;
+ }
+
+ /**
+ * Report a compile time warning. This calls the errorListener to output details
+ * of the warning.
+ * @param err an exception holding details of the warning condition to be
+ * reported
+ */
+
+ public void reportWarning(TransformerException err) {
+ try {
+ compilerInfo.getErrorListener().warning(err);
+ } catch (TransformerException err2) {
+ // ignore secondary error
+ }
+ }
+
+ /**
+ * Get a "next in chain" stylesheet. This method is intended for internal use.
+ * @param href the relative URI of the next-in-chain stylesheet
+ * @param baseURI the baseURI against which this relativeURI is to be resolved
+ * @return the cached stylesheet if present in the cache, or null if not
+ */
+
+ /*@Nullable*/ public PreparedStylesheet getCachedStylesheet(String href, String baseURI) {
+ URI abs = null;
+ try {
+ abs = new URI(baseURI).resolve(href);
+ } catch (URISyntaxException err) {
+ //
+ }
+ PreparedStylesheet result = null;
+ if (abs != null && nextStylesheetCache != null) {
+ result = nextStylesheetCache.get(abs);
+ }
+ return result;
+ }
+
+ /**
+ * Save a "next in chain" stylesheet in compiled form, so that it can be reused repeatedly.
+ * This method is intended for internal use.
+ * @param href the relative URI of the stylesheet
+ * @param baseURI the base URI against which the relative URI is resolved
+ * @param pss the prepared stylesheet object to be cached
+ */
+
+ public void putCachedStylesheet(String href, String baseURI, PreparedStylesheet pss) {
+ URI abs = null;
+ try {
+ abs = new URI(baseURI).resolve(href);
+ } catch (URISyntaxException err) {
+ //
+ }
+ if (abs != null) {
+ if (nextStylesheetCache == null) {
+ nextStylesheetCache = new HashMap<URI, PreparedStylesheet>(4);
+ }
+ nextStylesheetCache.put(abs, pss);
+ }
+ }
+
+ /**
+ * Get the CompilerInfo containing details of XSLT compilation options
+ * @return the CompilerInfo containing compilation options
+ * @since 9.2
+ */
+
+ public CompilerInfo getCompilerInfo() {
+ return compilerInfo;
+ }
+
+ /**
+ * Produce an XML representation of the compiled and optimized stylesheet
+ * @param presenter defines the destination and format of the output
+ */
+
+ public void explain(ExpressionPresenter presenter) {
+ presenter.startElement("stylesheet");
+ getKeyManager().explainKeys(presenter);
+ explainGlobalVariables(presenter);
+ ruleManager.explainTemplateRules(presenter);
+ explainNamedTemplates(presenter);
+ FunctionLibraryList libList = getFunctionLibrary();
+ List<FunctionLibrary> libraryList = libList.getLibraryList();
+ presenter.startElement("functions");
+ for (FunctionLibrary lib : libraryList) {
+ if (lib instanceof ExecutableFunctionLibrary) {
+ for (Iterator f = ((ExecutableFunctionLibrary) lib).iterateFunctions(); f.hasNext(); ) {
+ UserFunction func = (UserFunction) f.next();
+ presenter.startElement("function");
+ presenter.emitAttribute("name", func.getFunctionName().getDisplayName());
+ presenter.emitAttribute("line", func.getLineNumber() + "");
+ presenter.emitAttribute("module", func.getSystemId());
+ func.getBody().explain(presenter);
+ presenter.endElement();
+ }
+ }
+ }
+ presenter.endElement();
+ presenter.endElement();
+ }
+
+ /**
+ * Get the stylesheet specification(s) associated
+ * via the xml-stylesheet processing instruction (see
+ * http://www.w3.org/TR/xml-stylesheet/) with the document
+ * document specified in the source parameter, and that match
+ * the given criteria. Note that it is possible to return several
+ * stylesheets, in which case they are applied as if they were
+ * a list of imports or cascades.
+ * @param config The Saxon Configuration
+ * @param source The XML source document.
+ * @param media The media attribute to be matched. May be null, in which
+ * case the prefered templates will be used (i.e. alternate = no).
+ * @param title The value of the title attribute to match. May be null.
+ * @param charset The value of the charset attribute to match. May be null.
+ * @return A Source object suitable for passing to the TransformerFactory.
+ * @throws TransformerConfigurationException
+ * if any problems occur
+ */
+
+
+ public static Source getAssociatedStylesheet(
+ Configuration config, Source source, String media, String title, String charset)
+ throws TransformerConfigurationException {
+ PIGrabber grabber = new PIGrabber(new Sink(config.makePipelineConfiguration()));
+ grabber.setFactory(config);
+ grabber.setCriteria(media, title);
+ grabber.setBaseURI(source.getSystemId());
+ grabber.setURIResolver(config.getURIResolver());
+
+ try {
+ Sender.send(source, grabber, null);
+ // this parse will be aborted when the first start tag is found
+ } catch (XPathException err) {
+ if (grabber.isTerminated()) {
+ // do nothing
+ } else {
+ throw new TransformerConfigurationException(
+ "Failed while looking for xml-stylesheet PI", err);
+ }
+ }
+
+ try {
+ Source[] sources = grabber.getAssociatedStylesheets();
+ if (sources == null) {
+ throw new TransformerConfigurationException(
+ "No matching <?xml-stylesheet?> processing instruction found");
+ }
+ return compositeStylesheet(config, source.getSystemId(), sources);
+ } catch (TransformerException err) {
+ if (err instanceof TransformerConfigurationException) {
+ throw (TransformerConfigurationException)err;
+ } else {
+ throw new TransformerConfigurationException(err);
+ }
+ }
+ }
+
+ /**
+ * Process a series of stylesheet inputs, treating them in import or cascade
+ * order. This is mainly for support of the getAssociatedStylesheets
+ * method, but may be useful for other purposes.
+ * @param config the Saxon configuration
+ * @param baseURI the base URI to be used for the synthesized composite stylesheet
+ * @param sources An array of Source objects representing individual stylesheets.
+ * @return A Source object representing a composite stylesheet.
+ * @throws javax.xml.transform.TransformerConfigurationException if there is a static error
+ * in the stylesheet
+ */
+
+ private static Source compositeStylesheet(Configuration config, String baseURI, Source[] sources)
+ throws TransformerConfigurationException {
+
+ if (sources.length == 1) {
+ return sources[0];
+ } else if (sources.length == 0) {
+ throw new TransformerConfigurationException(
+ "No stylesheets were supplied");
+ }
+
+ // create a new top-level stylesheet that imports all the others
+
+ StringBuilder sb = new StringBuilder(250);
+ sb.append("<xsl:stylesheet version='1.0' ");
+ sb.append(" xmlns:xsl='" + NamespaceConstant.XSLT + "'>");
+ for (Source source : sources) {
+ sb.append("<xsl:import href='").append(source.getSystemId()).append("'/>");
+ }
+ sb.append("</xsl:stylesheet>");
+ InputSource composite = new InputSource();
+ composite.setSystemId(baseURI);
+ composite.setCharacterStream(new StringReader(sb.toString()));
+ return new SAXSource(config.getSourceParser(), composite);
+ }
+
+}
+
diff --git a/sf/saxon/Query.java b/sf/saxon/Query.java
new file mode 100644
index 0000000..cb9e106
--- /dev/null
+++ b/sf/saxon/Query.java
@@ -0,0 +1,949 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.instruct.GlobalParameterSet;
+import net.sf.saxon.expr.instruct.TerminationException;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.ModuleURIResolver;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.DocumentPool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.query.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.TimingCodeInjector;
+import net.sf.saxon.trace.TimingTraceListener;
+import net.sf.saxon.trace.XQueryTraceListener;
+import net.sf.saxon.trans.CommandLineOptions;
+import net.sf.saxon.trans.LicenseException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaException;
+import net.sf.saxon.type.ValidationException;
+import net.sf.saxon.value.DecimalValue;
+import org.xml.sax.InputSource;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * This <B>Query</B> class provides a command-line interface to the Saxon XQuery processor.<p>
+ * <p/>
+ * The XQuery syntax supported conforms to the W3C XQuery 1.0 drafts.
+ *
+ * @author Michael H. Kay
+ */
+
+public class Query {
+
+ protected Configuration config;
+ protected Properties outputProperties = new Properties();
+ protected boolean showTime = false;
+ protected int repeat = 1;
+ /*@Nullable*/ protected String sourceFileName = null;
+ /*@Nullable*/ protected String queryFileName = null;
+ protected boolean useURLs = false;
+ /*@Nullable*/ protected String outputFileName = null;
+ /*@Nullable*/ protected String moduleURIResolverClass = null;
+ /*@Nullable*/ protected String uriResolverClass = null;
+ protected boolean explain = false;
+ protected boolean wrap = false;
+ protected boolean pullMode = false;
+ protected boolean projection = false;
+ /*@Nullable*/ protected DecimalValue languageVersion = null;
+ protected boolean updating = false;
+ protected boolean writeback = false;
+ protected boolean backup = true;
+ /*@Nullable*/ protected String explainOutputFileName = null;
+ //private PrintStream traceDestination = System.err;
+ private boolean closeTraceDestination = false;
+ private boolean allowExit = true;
+
+ /**
+ * Get the configuration in use
+ *
+ * @return the configuration
+ */
+
+ protected Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Main program, can be used directly from the command line.
+ * <p>The format is:</P>
+ * <p>java net.sf.saxon.Query [options] <I>query-file</I> ><I>output-file</I></P>
+ * <p>followed by any number of parameters in the form {keyword=value}... which can be
+ * referenced from within the query.</p>
+ * <p>This program executes the query in query-file.</p>
+ *
+ * @param args List of arguments supplied on operating system command line
+ * @throws Exception Indicates that a compile-time or
+ * run-time error occurred
+ */
+
+ public static void main(String args[])
+ throws Exception {
+ // the real work is delegated to another routine so that it can be used in a subclass
+ (new Query()).doQuery(args, "java net.sf.saxon.Query");
+ }
+
+ /**
+ * Set the options that are recognized on the command line. This method can be overridden in a subclass
+ * to define additional command line options.
+ * @param options the CommandLineOptions in which the recognized options are to be registered.
+ */
+
+ public void setPermittedOptions(CommandLineOptions options) {
+ options.addRecognizedOption("backup", CommandLineOptions.TYPE_BOOLEAN,
+ "Save updated documents before overwriting");
+ options.addRecognizedOption("catalog", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use specified catalog file to resolve URIs");
+ options.addRecognizedOption("config", (CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED),
+ "Use specified configuration file");
+ options.addRecognizedOption("cr", CommandLineOptions.TYPE_CLASSNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use specified collection URI resolver class");
+ options.addRecognizedOption("dtd", CommandLineOptions.TYPE_ENUMERATION,
+ "Validate using DTD");
+ options.setPermittedValues("dtd", new String[]{"on","off","recover"}, "on");
+ options.addRecognizedOption("expand", CommandLineOptions.TYPE_BOOLEAN,
+ "Expand attribute defaults from DTD or Schema");
+ options.addRecognizedOption("explain", CommandLineOptions.TYPE_FILENAME,
+ "Display compiled expression tree and optimization decisions");
+ options.addRecognizedOption("ext", CommandLineOptions.TYPE_BOOLEAN,
+ "Allow calls to Java extension functions and xsl:result-document");
+ options.addRecognizedOption("init", CommandLineOptions.TYPE_CLASSNAME,
+ "User-supplied net.sf.saxon.lib.Initializer class to initialize the Saxon Configuration");
+ options.addRecognizedOption("l", CommandLineOptions.TYPE_BOOLEAN,
+ "Maintain line numbers for source documents");
+ options.addRecognizedOption("mr", CommandLineOptions.TYPE_CLASSNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use named ModuleURIResolver class");
+ options.addRecognizedOption("now", CommandLineOptions.TYPE_DATETIME | CommandLineOptions.VALUE_REQUIRED,
+ "Run with specified current date/time");
+ options.addRecognizedOption("o", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use specified file for primary output");
+ options.addRecognizedOption("opt", CommandLineOptions.TYPE_INTEGER | CommandLineOptions.VALUE_REQUIRED,
+ "Use optimization level 0..10");
+ options.addRecognizedOption("outval", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Action when validation of output file fails");
+ options.setPermittedValues("outval", new String[]{"recover","fatal"}, null);
+ options.addRecognizedOption("p", CommandLineOptions.TYPE_BOOLEAN,
+ "Recognize query parameters in URI passed to doc()");
+ options.addRecognizedOption("pipe", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Execute internally in push or pull mode");
+ options.setPermittedValues("pipe", new String[]{"push","pull"}, null);
+ options.addRecognizedOption("projection", CommandLineOptions.TYPE_BOOLEAN,
+ "Use source document projection");
+ options.addRecognizedOption("q", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "Query filename");
+ options.addRecognizedOption("qs", CommandLineOptions.TYPE_STRING | CommandLineOptions.VALUE_REQUIRED,
+ "Query string (usually in quotes)");
+ options.addRecognizedOption("quit", CommandLineOptions.TYPE_BOOLEAN | CommandLineOptions.VALUE_REQUIRED,
+ "Quit JVM if query fails");
+ options.addRecognizedOption("qversion", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Indicate whether XQuery version 3.0 is supported");
+ options.setPermittedValues("qversion", new String[]{"1.0","1.1","3.0"}, null);
+ options.addRecognizedOption("r", CommandLineOptions.TYPE_CLASSNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use named URIResolver class");
+ options.addRecognizedOption("repeat", CommandLineOptions.TYPE_INTEGER | CommandLineOptions.VALUE_REQUIRED,
+ "Run N times for performance measurement");
+ options.addRecognizedOption("s", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "Source file for primary input");
+ options.addRecognizedOption("sa", CommandLineOptions.TYPE_BOOLEAN,
+ "Run in schema-aware mode");
+ options.addRecognizedOption("strip", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Handling of whitespace text nodes in source documents");
+ options.setPermittedValues("strip", new String[]{"none","all","ignorable"}, null);
+ options.addRecognizedOption("t", CommandLineOptions.TYPE_BOOLEAN,
+ "Display version and timing information");
+ options.addRecognizedOption("T", CommandLineOptions.TYPE_CLASSNAME,
+ "Use named TraceListener class, or standard TraceListener");
+ options.addRecognizedOption("TJ", CommandLineOptions.TYPE_BOOLEAN,
+ "Debug binding and execution of extension functions");
+ options.setPermittedValues("TJ", new String[]{"on","off"}, "on");
+ options.addRecognizedOption("tree", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Use specified tree model for source documents");
+ options.addRecognizedOption("TP", CommandLineOptions.TYPE_FILENAME,
+ "Use profiling trace listener, with specified output file");
+ options.addRecognizedOption("traceout", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "File for output of trace() and -T output");
+ options.setPermittedValues("tree", new String[]{"linked","tiny","tinyc"}, null);
+ options.addRecognizedOption("u", CommandLineOptions.TYPE_BOOLEAN,
+ "Interpret filename arguments as URIs");
+ options.setPermittedValues("u", new String[]{"on","off"}, "on");
+ options.addRecognizedOption("update", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Enable or disable XQuery updates, or enable the syntax but discard the updates");
+ options.setPermittedValues("update", new String[]{"on","off","discard"}, null);
+ options.addRecognizedOption("val", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Apply validation to source documents");
+ options.setPermittedValues("val", new String[]{"strict","lax"}, "strict");
+ options.addRecognizedOption("wrap", CommandLineOptions.TYPE_BOOLEAN,
+ "Wrap result sequence in XML elements");
+ options.addRecognizedOption("x", CommandLineOptions.TYPE_CLASSNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use named XMLReader class for parsing source documents");
+ options.addRecognizedOption("xi", CommandLineOptions.TYPE_BOOLEAN,
+ "Expand XInclude directives in source documents");
+ options.addRecognizedOption("xmlversion", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Indicate whether XML 1.1 is supported");
+ options.setPermittedValues("xmlversion", new String[]{"1.0","1.1"}, null);
+ options.addRecognizedOption("xsd", CommandLineOptions.TYPE_FILENAME_LIST | CommandLineOptions.VALUE_REQUIRED,
+ "List of schema documents to be preloaded");
+ options.addRecognizedOption("xsdversion", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Indicate whether XSD 1.1 is supported");
+ options.setPermittedValues("xsdversion", new String[]{"1.0","1.1"}, null);
+ options.addRecognizedOption("xsiloc", CommandLineOptions.TYPE_BOOLEAN,
+ "Load schemas named in xsi:schemaLocation (default on)");
+ options.addRecognizedOption("?", CommandLineOptions.VALUE_PROHIBITED,
+ "Display command line help text");
+
+ }
+
+
+ /**
+ * Support method for main program. This support method can also be invoked from subclasses
+ * that support the same command line interface
+ *
+ * @param args the command-line arguments
+ * @param command name of the class, to be used in error messages
+ */
+
+ protected void doQuery(String args[], String command) {
+
+ CommandLineOptions options = new CommandLineOptions();
+ setPermittedOptions(options);
+ try {
+ options.setActualOptions(args);
+ } catch (XPathException err) {
+ quit(err.getMessage(), 2);
+ }
+
+
+ boolean schemaAware = false;
+ String configFile = options.getOptionValue("config");
+ if (configFile != null) {
+ try {
+ config = Configuration.readConfiguration((new StreamSource(configFile)));
+ schemaAware = config.isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY);
+ } catch (XPathException e) {
+ quit(e.getMessage(), 2);
+ }
+ }
+
+ if (config == null && !schemaAware) {
+ schemaAware = options.testIfSchemaAware();
+ }
+
+ if (config == null) {
+ config = Configuration.newConfiguration();
+ }
+
+ config.setHostLanguage(Configuration.XQUERY);
+
+ StaticQueryContext staticEnv;
+ DynamicQueryContext dynamicEnv = new DynamicQueryContext(config);
+
+ // Check the command-line arguments.
+
+ try {
+ parseOptions(options, command, dynamicEnv);
+ staticEnv = config.newStaticQueryContext();
+ staticEnv.setSchemaAware(schemaAware);
+
+ if (languageVersion != null) {
+ staticEnv.setLanguageVersion(languageVersion);
+ }
+ if (updating) {
+ staticEnv.setUpdatingEnabled(true);
+ }
+ if (config.getTraceListener() != null) {
+ staticEnv.setCompileWithTracing(true);
+ }
+
+ if (moduleURIResolverClass != null) {
+ Object mr = config.getInstance(moduleURIResolverClass, null);
+ if (!(mr instanceof ModuleURIResolver)) {
+ badUsage(moduleURIResolverClass + " is not a ModuleURIResolver");
+ }
+ staticEnv.setModuleURIResolver((ModuleURIResolver)mr);
+ }
+
+ if (uriResolverClass != null) {
+ config.setURIResolver(config.makeURIResolver(uriResolverClass));
+ dynamicEnv.setURIResolver(config.makeURIResolver(uriResolverClass));
+ }
+
+ config.displayLicenseMessage();
+ if (schemaAware && !config.isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY)) {
+ if ("EE".equals(config.getEditionCode())) {
+ quit("Installed license does not allow schema-aware query", 2);
+ } else {
+ quit("Schema-aware query requires Saxon Enterprise Edition", 2);
+ }
+ }
+ if(languageVersion.equals(DecimalValue.THREE) && !config.isLicensedFeature(Configuration.LicenseFeature.PROFESSIONAL_EDITION)) {
+ if ("HE".equals(config.getEditionCode())) {
+ quit("XQuery version 3.0 requires Saxon-PE or Saxon-EE", 2);
+ } else {
+ quit("XQuery version 3.0 requires a license", 2);
+ }
+ }
+ if (pullMode) {
+ //config.setLazyConstructionMode(true);
+ }
+
+ if (explain) {
+ config.setBooleanProperty(FeatureKeys.TRACE_OPTIMIZER_DECISIONS, true);
+ }
+
+ Source sourceInput = null;
+
+ if (sourceFileName != null) {
+ sourceInput = processSourceFile(sourceFileName, useURLs);
+ }
+
+ long startTime = System.nanoTime();
+ if (showTime) {
+ System.err.println("Analyzing query from " + queryFileName);
+ }
+
+ // Compile the query
+
+ XQueryExpression exp;
+ try {
+ exp = compileQuery(staticEnv, queryFileName, useURLs);
+
+ if (showTime) {
+ long endTime = System.nanoTime();
+ System.err.println("Analysis time: " + ((endTime - startTime)/1e6) + " milliseconds");
+ startTime = endTime;
+ }
+
+ } catch (XPathException err) {
+ int line = -1;
+ String module = null;
+ if (err.getLocator() != null) {
+ line = err.getLocator().getLineNumber();
+ module = err.getLocator().getSystemId();
+ }
+ if (err.hasBeenReported()) {
+ quit("Static error(s) in query", 2);
+ } else {
+ if (line == -1) {
+ System.err.println("Static error in query: " + err.getMessage());
+ } else {
+ System.err.println("Static error at line " + line + " of " + module + ':');
+ System.err.println(err.getMessage());
+ }
+ }
+ exp = null;
+ if (allowExit) {
+ System.exit(2);
+ } else {
+ throw new RuntimeException(err);
+ }
+ }
+
+ if (explain) {
+ explain(exp);
+ }
+
+ // Load the source file (applying document projection if requested)
+
+ exp.setAllowDocumentProjection(projection);
+ processSource(sourceInput, exp, dynamicEnv);
+
+ // Run the query (repeatedly, if the -repeat option was set)
+
+ startTime = System.nanoTime();
+ long totalTime = 0;
+ int r;
+ for (r = 0; r < repeat; r++) { // repeat is for internal testing/timing
+ try {
+ OutputStream destination;
+ if (outputFileName != null) {
+ File outputFile = new File(outputFileName);
+ if (outputFile.isDirectory()) {
+ quit("Output is a directory", 2);
+ }
+ if (!outputFile.exists()) {
+ File directory = outputFile.getParentFile();
+ if (directory != null && !directory.exists()) {
+ directory.mkdirs();
+ }
+ outputFile.createNewFile();
+ }
+ destination = new FileOutputStream(outputFile);
+ } else {
+ destination = System.out;
+ }
+
+ runQuery(exp, dynamicEnv, destination, outputProperties);
+ } catch (TerminationException err) {
+ throw err;
+ } catch (XPathException err) {
+ if (err.hasBeenReported()) {
+ //err.printStackTrace();
+ throw new XPathException("Run-time errors were reported");
+ } else {
+ throw err;
+ }
+ }
+
+ if (showTime) {
+ long endTime = System.nanoTime();
+ if (r >= 3) {
+ totalTime += (endTime - startTime);
+ }
+ if (repeat < 100) {
+ System.err.println("Execution time: " + CommandLineOptions.showExecutionTimeNano(endTime - startTime));
+ System.err.println("Memory used: " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
+ } else if (totalTime > 1000000000000L) {
+ // quit after 1000 seconds
+ break;
+ }
+ startTime = endTime;
+ }
+ }
+
+ if (repeat > 3) {
+ System.err.println("Average execution time: " +
+ CommandLineOptions.showExecutionTimeNano((totalTime / (r - 3))));
+ }
+
+ } catch (TerminationException err) {
+ quit(err.getMessage(), 1);
+ } catch (XPathException err) {
+ quit("Query processing failed: " + err.getMessage(), 2);
+ } catch (TransformerFactoryConfigurationError err) {
+ err.printStackTrace();
+ quit("Query processing failed", 2);
+ } catch (SchemaException err) {
+ quit("Schema processing failed: " + err.getMessage(), 2);
+ } catch (LicenseException err) {
+ quit("Query processing failed: " + err.getMessage(), 2);
+ } catch (Exception err2) {
+ err2.printStackTrace();
+ quit("Fatal error during query: " + err2.getClass().getName() + ": " +
+ (err2.getMessage() == null ? " (no message)" : err2.getMessage()), 2);
+ }
+ }
+
+ /**
+ * Parse the options supplied on the command line
+ * @param options the command line arguments
+ * @param command the name of the command that was used (for diagnostics only)
+ * @param dynamicEnv the XQuery dynamic context
+ * @throws TransformerException if failures occur. Note, the method may also invoke System.exit().
+ */
+
+ protected void parseOptions(CommandLineOptions options, String command, DynamicQueryContext dynamicEnv)
+ throws TransformerException {
+
+ // Apply those options which simply update the Configuration
+
+ options.applyToConfiguration(config);
+
+ // Apply options that are processed locally
+
+ allowExit = !"off".equals(options.getOptionValue("quit"));
+
+ dynamicEnv.setURIResolver(config.getURIResolver());
+
+ backup = "on".equals(options.getOptionValue("backup"));
+ explainOutputFileName = options.getOptionValue("explain");
+ explain = (explainOutputFileName != null);
+ moduleURIResolverClass = options.getOptionValue("mr");
+ outputFileName = options.getOptionValue("o");
+
+ String value = options.getOptionValue("p");
+ if ("on".equals(value)) {
+ config.setParameterizedURIResolver();
+ useURLs = true;
+ }
+
+ pullMode = "pull".equals(options.getOptionValue("pipe"));
+ projection = "on".equals(options.getOptionValue("projection"));
+
+
+ value = options.getOptionValue("q");
+ if (value != null) {
+ queryFileName = value;
+ }
+
+ value = options.getOptionValue("qs");
+ if (value != null) {
+ queryFileName = "{" + value + "}";
+ }
+
+ try {
+ String qv = options.getOptionValue("qversion");
+ if (qv == null) {
+ qv = "1.0";
+ }
+ if ("1.1".equals(qv)) {
+ qv = "3.0";
+ }
+ languageVersion = (DecimalValue)DecimalValue.makeDecimalValue(qv, true).asAtomic();
+ } catch (ValidationException err) {
+ badUsage("XQuery version " + options.getOptionValue("qversion") + " must be numeric");
+ }
+
+ value = options.getOptionValue("repeat");
+ if (value != null) {
+ repeat = Integer.parseInt(value);
+ }
+
+ sourceFileName = options.getOptionValue("s");
+
+ value = options.getOptionValue("t");
+ if ("on".equals(value)) {
+ System.err.println(config.getProductTitle());
+ System.err.println(Configuration.getPlatform().getPlatformVersion());
+ config.setTiming(true);
+ showTime = true;
+ }
+
+ value = options.getOptionValue("traceout");
+ if (value != null) {
+ if (value.equals("#err")) {
+ // no action, this is the default
+ } else if (value.equals("#out")) {
+ dynamicEnv.setTraceFunctionDestination(System.out);
+ } else if (value.equals("#null")) {
+ dynamicEnv.setTraceFunctionDestination(null);
+ } else {
+ try {
+ dynamicEnv.setTraceFunctionDestination(
+ new PrintStream(new FileOutputStream(new File(value))));
+ closeTraceDestination = true;
+ } catch (FileNotFoundException e) {
+ badUsage("Trace output file " + value + " cannot be created");
+ }
+ }
+ }
+
+ value = options.getOptionValue("T");
+ if (value != null) {
+ if ("".equals(value)) {
+ config.setTraceListener(new XQueryTraceListener());
+ } else {
+ config.setTraceListenerClass(value);
+ }
+ config.setLineNumbering(true);
+ }
+
+ value = options.getOptionValue("TP");
+ if (value != null) {
+ TimingTraceListener listener = new TimingTraceListener();
+ config.setTraceListener(listener);
+ config.setLineNumbering(true);
+ config.getDefaultStaticQueryContext().setCodeInjector(new TimingCodeInjector());
+ if (value.length() > 0) {
+ try {
+ listener.setOutputDestination(
+ new PrintStream(new FileOutputStream(new File(value))));
+ } catch (FileNotFoundException e) {
+ badUsage("Trace output file " + value + " cannot be created");
+ }
+ }
+ }
+
+ value = options.getOptionValue("u");
+ if (value != null) {
+ useURLs = "on".equals(value);
+ }
+
+ value = options.getOptionValue("update");
+ if (value != null) {
+ if (!"off".equals(value)) {
+ updating = true;
+ }
+ writeback = !("discard".equals(value));
+ }
+
+ wrap = "on".equals(options.getOptionValue("wrap"));
+
+ value = options.getOptionValue("x");
+ if (value != null) {
+ config.setSourceParserClass(value);
+ }
+
+ String additionalSchemas = options.getOptionValue("xsd");
+
+ value = options.getOptionValue("?");
+ if (value != null) {
+ badUsage("");
+ }
+
+ // Apply options defined locally in a subclass
+
+ applyLocalOptions(options, config);
+
+ // Apply positional options
+
+ List<String> positional = options.getPositionalOptions();
+ int currentPositionalOption = 0;
+
+ if (queryFileName == null) {
+ if (positional.size() == currentPositionalOption) {
+ badUsage("No query file name");
+ }
+ queryFileName = positional.get(currentPositionalOption++);
+ }
+
+ if (currentPositionalOption < positional.size()) {
+ badUsage("Unrecognized option: " + positional.get(currentPositionalOption));
+ }
+
+ GlobalParameterSet params = new GlobalParameterSet();
+ options.setParams(config, params, outputProperties);
+ dynamicEnv.setParameters(params);
+
+ if (additionalSchemas != null) {
+ CommandLineOptions.loadAdditionalSchemas(config, additionalSchemas);
+ }
+ }
+
+ /**
+ * Customisation hook: apply options defined locally in a subclass
+ * @param options the CommandLineOptions
+ * @param config the Saxon Configuration
+ */
+
+ protected void applyLocalOptions(CommandLineOptions options, Configuration config) {
+ // no action: provided for subclasses to override
+ }
+
+ /*@Nullable*/ protected Source processSourceFile(String sourceFileName, boolean useURLs) throws TransformerException {
+ //DOM code for comparison
+// long start = new Date().getTime();
+// try {
+// DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+// factory.setNamespaceAware(true);
+// factory.setIgnoringElementContentWhitespace(true);
+// DocumentBuilder builder = factory.newDocumentBuilder();
+// org.w3c.dom.Document doc = builder.parse(new File(sourceFileName));
+// DocumentWrapper dom = new DocumentWrapper(doc, sourceFileName, getConfiguration());
+// long end = new Date().getTime();
+// System.err.println("Build time " + (end - start) + "ms");
+// return dom;
+// } catch (ParserConfigurationException e) {
+// e.printStackTrace();
+// } catch (SAXException e) {
+// e.printStackTrace();
+// } catch (IOException e) {
+// e.printStackTrace();
+// }
+// return null;
+ Source sourceInput;
+ if (useURLs || sourceFileName.startsWith("http:") || sourceFileName.startsWith("file:")) {
+ sourceInput = config.getURIResolver().resolve(sourceFileName, null);
+ if (sourceInput == null) {
+ sourceInput = config.getSystemURIResolver().resolve(sourceFileName, null);
+ }
+ } else if (sourceFileName.equals("-")) {
+ // take input from stdin
+ sourceInput = new StreamSource(System.in);
+ } else {
+ File sourceFile = new File(sourceFileName);
+ if (!sourceFile.exists()) {
+ quit("Source file " + sourceFile + " does not exist", 2);
+ }
+
+ if (Configuration.getPlatform().isJava()) {
+ InputSource eis = new InputSource(sourceFile.toURI().toString());
+ sourceInput = new SAXSource(eis);
+ } else {
+ sourceInput = new StreamSource(sourceFile.toURI().toString());
+ }
+ }
+ return sourceInput;
+ }
+
+ /**
+ * Compile the query
+ *
+ * @param staticEnv the static query context
+ * @param queryFileName the filename holding the query (or "-" for the standard input)
+ * @param useURLs true if the filename is in the form of a URI
+ * @return the compiled query
+ * @throws XPathException if query compilation fails
+ * @throws IOException if the query cannot be read
+ */
+
+ /*@Nullable*/ protected XQueryExpression compileQuery(StaticQueryContext staticEnv, String queryFileName, boolean useURLs)
+ throws XPathException, IOException {
+ XQueryExpression exp;
+ if (queryFileName.equals("-")) {
+ Reader queryReader = new InputStreamReader(System.in);
+ exp = staticEnv.compileQuery(queryReader);
+ } else if (queryFileName.startsWith("{") && queryFileName.endsWith("}")) {
+ // query is inline on the command line
+ String q = queryFileName.substring(1, queryFileName.length() - 1);
+ exp = staticEnv.compileQuery(q);
+ } else if (useURLs || queryFileName.startsWith("http:") || queryFileName.startsWith("file:")) {
+ ModuleURIResolver resolver = staticEnv.getModuleURIResolver();
+ boolean isStandardResolver = false;
+ if (resolver == null) {
+ resolver = staticEnv.getConfiguration().getStandardModuleURIResolver();
+ isStandardResolver = true;
+ }
+ while (true) {
+ String[] locations = {queryFileName};
+ Source[] sources;
+ try {
+ sources = resolver.resolve(null, null, locations);
+ } catch (Exception e) {
+ if (e instanceof XPathException) {
+ throw (XPathException)e;
+ } else {
+ XPathException err = new XPathException("Exception in ModuleURIResolver: ", e);
+ err.setErrorCode("XQST0059");
+ throw err;
+ }
+ }
+ if (sources == null) {
+ if (isStandardResolver) {
+ // this should not happen
+ quit("System problem: standard ModuleURIResolver returned null", 4);
+ } else {
+ resolver = staticEnv.getConfiguration().getStandardModuleURIResolver();
+ isStandardResolver = true;
+ }
+ } else {
+ if (sources.length != 1 || !(sources[0] instanceof StreamSource)) {
+ quit("Module URI Resolver must return a single StreamSource", 2);
+ }
+ String queryText = QueryReader.readSourceQuery((StreamSource)sources[0], config.getNameChecker());
+ exp = staticEnv.compileQuery(queryText);
+ break;
+ }
+ }
+ } else {
+ InputStream queryStream = null;
+ try {
+ queryStream = new FileInputStream(queryFileName);
+ staticEnv.setBaseURI(new File(queryFileName).toURI().toString());
+ exp = staticEnv.compileQuery(queryStream, null);
+ } finally {
+ if(queryStream!=null){
+ queryStream.close();
+ }
+ }
+ }
+ return exp;
+ }
+
+ /**
+ * Explain the results of query compilation
+ *
+ * @param exp the compiled expression
+ * @throws FileNotFoundException if the destination for the explanation doesn't exist
+ * @throws XPathException if other failures occur
+ */
+
+ protected void explain(XQueryExpression exp) throws FileNotFoundException, XPathException {
+ OutputStream explainOutput;
+ if (explainOutputFileName == null || "".equals(explainOutputFileName)) {
+ explainOutput = System.err;
+ } else {
+ explainOutput = new FileOutputStream(new File(explainOutputFileName));
+ }
+ Properties props = ExpressionPresenter.makeDefaultProperties();
+ Receiver diag = config.getSerializerFactory().getReceiver(
+ new StreamResult(explainOutput),
+ config.makePipelineConfiguration(),
+ props);
+ ExpressionPresenter expressionPresenter = new ExpressionPresenter(config, diag);
+ exp.explain(expressionPresenter);
+ }
+
+ /**
+ * Process the supplied source file
+ *
+ * @param sourceInput the supplied source
+ * @param exp the compiled XQuery expression
+ * @param dynamicEnv the dynamic query context
+ * @throws XPathException if processing fails
+ */
+
+ protected void processSource(/*@Nullable*/ Source sourceInput, XQueryExpression exp, DynamicQueryContext dynamicEnv) throws XPathException {
+ if (sourceInput != null) {
+ ParseOptions options = new ParseOptions(config.getParseOptions());
+ if (showTime) {
+ System.err.println("Processing " + sourceInput.getSystemId());
+ }
+ if (!exp.usesContextItem()) {
+ System.err.println("Source document ignored - query does not access the context item");
+ sourceInput = null;
+
+ } else if (projection) {
+ PathMap map = exp.getPathMap();
+ PathMap.PathMapRoot contextRoot = map.getContextDocumentRoot();
+ if (explain) {
+ System.err.println("DOCUMENT PROJECTION: PATH MAP");
+ map.diagnosticDump(System.err);
+ }
+ if (contextRoot != null) {
+ if (contextRoot.hasUnknownDependencies()) {
+ System.err.println("Document projection for the context document is not possible, " +
+ "because the query uses paths that defy analysis");
+ } else {
+ options.addFilter(config.makeDocumentProjector(contextRoot));
+ }
+ } else {
+ System.err.println("Source document supplied, but query does not access the context item");
+ }
+ }
+ if (sourceInput != null) {
+ DocumentInfo doc = config.buildDocument(sourceInput, options);
+ dynamicEnv.setContextItem(doc);
+ }
+ }
+ }
+
+ /**
+ * Run the query
+ *
+ * @param exp the compiled query expression
+ * @param dynamicEnv the dynamic query context
+ * @param destination the destination for serialized results
+ * @param outputProps serialization properties defining the output format
+ * @throws XPathException if the query fails
+ * @throws IOException if input or output fails
+ */
+ protected void runQuery(XQueryExpression exp, DynamicQueryContext dynamicEnv,
+ OutputStream destination, final Properties outputProps)
+ throws XPathException, IOException {
+ if (exp.getExpression().isUpdatingExpression() && updating) {
+ if (outputProps.getProperty(OutputKeys.METHOD) == null) {
+ outputProps.setProperty(OutputKeys.METHOD, "xml");
+ }
+ if (writeback) {
+ final List<XPathException> errors = new ArrayList<XPathException>(3);
+ UpdateAgent agent = new UpdateAgent() {
+ public void update(NodeInfo node, Controller controller) throws XPathException {
+ try {
+ DocumentPool pool = controller.getDocumentPool();
+ String documentURI = pool.getDocumentURI(node);
+ if (documentURI != null) {
+ QueryResult.rewriteToDisk(node, outputProps, backup, (showTime ? System.err : null));
+ } else if (showTime) {
+ System.err.println("Updated document discarded because it was not read using doc()");
+ }
+ } catch (XPathException err) {
+ System.err.println(err.getMessage());
+ errors.add(err);
+ }
+ }
+ };
+ exp.runUpdate(dynamicEnv, agent);
+
+ if (!errors.isEmpty()) {
+ throw errors.get(0);
+ }
+ } else {
+ Set affectedDocuments = exp.runUpdate(dynamicEnv);
+ if (affectedDocuments.contains(dynamicEnv.getContextItem())) {
+ QueryResult.serialize((NodeInfo)dynamicEnv.getContextItem(),
+ new StreamResult(destination),
+ outputProps);
+ }
+ }
+ } else if (wrap && !pullMode) {
+ SequenceIterator results = exp.iterator(dynamicEnv);
+ DocumentInfo resultDoc = QueryResult.wrap(results, config);
+ QueryResult.serialize(resultDoc,
+ new StreamResult(destination),
+ outputProps);
+ destination.close();
+ } else if (pullMode) {
+ if (wrap) {
+ outputProps.setProperty(SaxonOutputKeys.WRAP, "yes");
+ }
+ //outputProps.setProperty(OutputKeys.METHOD, "xml");
+ //outputProps.setProperty(OutputKeys.INDENT, "yes");
+ exp.pull(dynamicEnv, new StreamResult(destination), outputProps);
+ } else {
+ exp.run(dynamicEnv, new StreamResult(destination), outputProps);
+ }
+ if (closeTraceDestination) {
+ dynamicEnv.getTraceFunctionDestination().close();
+ }
+ }
+
+ /**
+ * Exit with a message
+ *
+ * @param message The message to be output
+ * @param code The result code to be returned to the operating
+ * system shell
+ */
+
+ protected void quit(String message, int code) {
+ System.err.println(message);
+ if (allowExit) {
+ System.exit(code);
+ } else {
+ throw new RuntimeException(message);
+ }
+ }
+
+// public void setPOption(Configuration config) {
+// config.getSystemURIResolver().setRecognizeQueryParameters(true);
+// }
+
+ /**
+ * Report incorrect usage of the command line, with a list of the options and arguments that are available
+ *
+ * @param message The error message
+ */
+ protected void badUsage(String message) {
+ if (!"".equals(message)) {
+ System.err.println(message);
+ }
+ if (!showTime) {
+ System.err.println(config.getProductTitle());
+ }
+ System.err.println("Usage: see http://www.saxonica.com/documentation/html/using-xquery/commandline.html");
+ System.err.println("Format: " + getClass().getName() + " options params");
+ CommandLineOptions options = new CommandLineOptions();
+ setPermittedOptions(options);
+ System.err.println("Options available:" + options.displayPermittedOptions());
+ System.err.println("Use -XYZ:? for details of option XYZ");
+ System.err.println("Params: ");
+ System.err.println(" param=value Set query string parameter");
+ System.err.println(" +param=filename Set query document parameter");
+ System.err.println(" ?param=expression Set query parameter using XPath");
+ System.err.println(" !param=value Set serialization parameter");
+ if (allowExit) {
+ if ("".equals(message)) {
+ System.exit(0);
+ } else {
+ System.exit(2);
+ }
+ } else {
+ throw new RuntimeException(message);
+ }
+ }
+}
+
diff --git a/sf/saxon/TemplatesHandlerImpl.java b/sf/saxon/TemplatesHandlerImpl.java
new file mode 100644
index 0000000..b6292c4
--- /dev/null
+++ b/sf/saxon/TemplatesHandlerImpl.java
@@ -0,0 +1,163 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.CommentStripper;
+import net.sf.saxon.event.ReceivingContentHandler;
+import net.sf.saxon.event.StartTagBuffer;
+import net.sf.saxon.event.Stripper;
+import net.sf.saxon.om.StylesheetSpaceStrippingRule;
+import net.sf.saxon.style.StyleNodeFactory;
+import net.sf.saxon.style.UseWhenFilter;
+import net.sf.saxon.trans.CompilerInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.linked.DocumentImpl;
+import net.sf.saxon.tree.linked.LinkedTreeBuilder;
+import org.xml.sax.Locator;
+
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.TemplatesHandler;
+
+
+/**
+ * <b>TemplatesHandlerImpl</b> implements the javax.xml.transform.sax.TemplatesHandler
+ * interface. It acts as a ContentHandler which receives a stream of
+ * SAX events representing a stylesheet, and returns a Templates object that
+ * represents the compiled form of this stylesheet.
+ * @author Michael H. Kay
+ */
+
+public class TemplatesHandlerImpl extends ReceivingContentHandler implements TemplatesHandler {
+
+ private LinkedTreeBuilder builder;
+ private StyleNodeFactory nodeFactory;
+ private Templates templates;
+ private String systemId;
+
+ /**
+ * Create a TemplatesHandlerImpl and initialise variables. The constructor is protected, because
+ * the Filter should be created using newTemplatesHandler() in the SAXTransformerFactory
+ * class
+ * @param config the Saxon configuration
+ */
+
+ protected TemplatesHandlerImpl(Configuration config) {
+
+ setPipelineConfiguration(config.makePipelineConfiguration());
+
+ nodeFactory = config.makeStyleNodeFactory();
+
+ builder = new LinkedTreeBuilder(getPipelineConfiguration());
+ builder.setNodeFactory(nodeFactory);
+ builder.setLineNumbering(true);
+
+ PreparedStylesheet sheet = new PreparedStylesheet(config, config.getDefaultXsltCompilerInfo());
+
+ UseWhenFilter useWhenFilter = new UseWhenFilter(sheet, builder);
+
+ StartTagBuffer startTagBuffer = new StartTagBuffer(useWhenFilter);
+ useWhenFilter.setStartTagBuffer(startTagBuffer);
+
+ StylesheetSpaceStrippingRule rule = new StylesheetSpaceStrippingRule(config.getNamePool());
+ Stripper styleStripper = new Stripper(rule, startTagBuffer);
+ CommentStripper commentStripper = new CommentStripper(styleStripper);
+ setReceiver(commentStripper);
+
+ }
+
+ /**
+ * Get the Templates object to be used for a transformation
+ */
+
+ /*@Nullable*/ public Templates getTemplates() {
+ if (templates==null) {
+ DocumentImpl doc = (DocumentImpl)builder.getCurrentRoot();
+ builder.reset();
+ if (doc==null) {
+ return null;
+ }
+
+ final Configuration config = getConfiguration();
+ CompilerInfo info = new CompilerInfo(config.getDefaultXsltCompilerInfo());
+ info.setXsltVersion(nodeFactory.getXsltProcessorVersion());
+ PreparedStylesheet sheet = new PreparedStylesheet(config, info);
+
+ try {
+ sheet.setStylesheetDocument(doc);
+ templates = sheet;
+ } catch (XPathException tce) {
+ if (!tce.hasBeenReported()) {
+ try {
+ info.getErrorListener().fatalError(tce);
+ } catch (TransformerException e2) {
+ //
+ }
+ }
+ // don't know why we aren't allowed to just throw it!
+ throw new IllegalStateException(tce.getMessage());
+ }
+ }
+
+ return templates;
+ }
+
+ /**
+ * Set the SystemId of the document. Note that if this method is called, any locator supplied
+ * to the setDocumentLocator() method is ignored. This also means that no line number information
+ * will be available.
+ * @param url the system ID (base URI) of the stylesheet document, which will be used in any error
+ * reporting and also for resolving relative URIs in xsl:include and xsl:import. It will also form
+ * the static base URI in the static context of XPath expressions.
+ */
+
+ public void setSystemId(String url) {
+ systemId = url;
+ builder.setSystemId(url);
+ super.setDocumentLocator(new Locator() {
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+ public String getSystemId() {
+ return systemId;
+ }
+ });
+ }
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void setDocumentLocator (final Locator locator) {
+ // If the user has called setSystemId(), we use that system ID in preference to this one,
+ // which probably comes from the XML parser possibly via some chain of SAX filters
+ if (systemId == null) {
+ super.setDocumentLocator(locator);
+ }
+ }
+
+ /**
+ * Get the systemId of the document
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+
+}
+
diff --git a/sf/saxon/Transform.java b/sf/saxon/Transform.java
new file mode 100644
index 0000000..5287ee7
--- /dev/null
+++ b/sf/saxon/Transform.java
@@ -0,0 +1,1067 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.instruct.GlobalParameterSet;
+import net.sf.saxon.expr.instruct.TerminationException;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.trace.AbstractTraceListener;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.TimingCodeInjector;
+import net.sf.saxon.trace.TimingTraceListener;
+import net.sf.saxon.trans.CommandLineOptions;
+import net.sf.saxon.trans.CompilerInfo;
+import net.sf.saxon.trans.LicenseException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.DateTimeValue;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import javax.xml.transform.*;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * This <b>Transform</b> class is the command-line entry point to the Saxon XSLT Processor.
+ *
+ * <p>It is possible to subclass this class to provide a customized command line interface. In writing such
+ * a subclass:</p>
+ *
+ * <ul>
+ * <li>The {@link #main} method should instantiate the class and call the {@link #doTransform} method, passing the
+ * argument list. The argument list can be augmented or modified if required: for example, by adding a -config
+ * argument to cause the configuration to be initialized from a configuration file.</li>
+ * <li>The {@link #setFactoryConfiguration} method can be implemented
+ */
+
+public class Transform {
+
+ protected Configuration config;
+ protected CompilerInfo compilerInfo;
+ protected boolean useURLs = false;
+ protected boolean showTime = false;
+ protected int repeat = 1;
+ /*@Nullable*/ String sourceParserName = null;
+ boolean schemaAware = false;
+ boolean allowExit = true;
+
+ /**
+ * Main program, can be used directly from the command line.
+ * <p>The format is:</P>
+ * <p>java net.sf.saxon.Transform [options] <I>source-file</I> <I>style-file</I> ><I>output-file</I></P>
+ * <p>followed by any number of parameters in the form {keyword=value}... which can be
+ * referenced from within the stylesheet.</p>
+ * <p>This program applies the XSL style sheet in style-file to the source XML document in source-file.</p>
+ *
+ * @param args List of arguments supplied on operating system command line
+ * @throws java.lang.Exception Indicates that a compile-time or
+ * run-time error occurred
+ */
+
+ public static void main(String args[])
+ throws java.lang.Exception {
+ // the real work is delegated to another routine so that it can be used in a subclass
+ (new Transform()).doTransform(args, "java net.sf.saxon.Transform");
+ }
+
+ /**
+ * Set the options that are recognized on the command line. This method can be overridden in a subclass
+ * to define additional command line options.
+ * @param options the CommandLineOptions in which the recognized options are to be registered.
+ */
+
+ public void setPermittedOptions(CommandLineOptions options) {
+ options.addRecognizedOption("a", CommandLineOptions.TYPE_BOOLEAN,
+ "Use <?xml-stylesheet?> processing instruction to identify stylesheet");
+ options.addRecognizedOption("catalog", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use specified catalog file to resolve URIs");
+ options.addRecognizedOption("config", (CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED),
+ "Use specified configuration file");
+ options.addRecognizedOption("cr", CommandLineOptions.TYPE_CLASSNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use specified collection URI resolver class");
+ options.addRecognizedOption("dtd", CommandLineOptions.TYPE_ENUMERATION,
+ "Validate using DTD");
+ options.setPermittedValues("dtd", new String[]{"on","off","recover"}, "on");
+ options.addRecognizedOption("expand", CommandLineOptions.TYPE_BOOLEAN,
+ "Expand attribute defaults from DTD or Schema");
+ options.addRecognizedOption("explain", CommandLineOptions.TYPE_FILENAME,
+ "Display compiled expression tree and optimization decisions");
+ options.addRecognizedOption("ext", CommandLineOptions.TYPE_BOOLEAN,
+ "Allow calls to Java extension functions and xsl:result-document");
+ options.addRecognizedOption("im", CommandLineOptions.TYPE_QNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Name of initial mode");
+ options.addRecognizedOption("init", CommandLineOptions.TYPE_CLASSNAME,
+ "User-supplied net.sf.saxon.lib.Initializer class to initialize the Saxon Configuration");
+ options.addRecognizedOption("it", CommandLineOptions.TYPE_QNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Name of initial template");
+ options.addRecognizedOption("l", CommandLineOptions.TYPE_BOOLEAN,
+ "Maintain line numbers for source documents");
+ options.addRecognizedOption("m", CommandLineOptions.TYPE_CLASSNAME,
+ "Use named class to handle xsl:message output");
+ options.addRecognizedOption("now", CommandLineOptions.TYPE_DATETIME | CommandLineOptions.VALUE_REQUIRED,
+ "Run with specified current date/time");
+ options.addRecognizedOption("o", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use specified file for primary output");
+ options.addRecognizedOption("opt", CommandLineOptions.TYPE_INTEGER | CommandLineOptions.VALUE_REQUIRED,
+ "Use optimization level 0..10");
+ options.addRecognizedOption("or", CommandLineOptions.TYPE_CLASSNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use named OutputURIResolver class");
+ options.addRecognizedOption("outval", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Action when validation of output file fails");
+ options.setPermittedValues("outval", new String[]{"recover","fatal"}, null);
+ options.addRecognizedOption("p", CommandLineOptions.TYPE_BOOLEAN,
+ "Recognize query parameters in URI passed to doc()");
+ options.addRecognizedOption("quit", CommandLineOptions.TYPE_BOOLEAN | CommandLineOptions.VALUE_REQUIRED,
+ "Quit JVM if transformation fails");
+ options.addRecognizedOption("r", CommandLineOptions.TYPE_CLASSNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use named URIResolver class");
+ options.addRecognizedOption("repeat", CommandLineOptions.TYPE_INTEGER | CommandLineOptions.VALUE_REQUIRED,
+ "Run N times for performance measurement");
+ options.addRecognizedOption("s", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "Source file for primary input");
+ options.addRecognizedOption("sa", CommandLineOptions.TYPE_BOOLEAN,
+ "Run in schema-aware mode");
+ options.addRecognizedOption("strip", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Handling of whitespace text nodes in source documents");
+ options.setPermittedValues("strip", new String[]{"none","all","ignorable"}, null);
+ options.addRecognizedOption("t", CommandLineOptions.TYPE_BOOLEAN,
+ "Display version and timing information, and names of output files");
+ options.addRecognizedOption("T", CommandLineOptions.TYPE_CLASSNAME,
+ "Use named TraceListener class, or standard TraceListener");
+ options.addRecognizedOption("TJ", CommandLineOptions.TYPE_BOOLEAN,
+ "Debug binding and execution of extension functions");
+ options.setPermittedValues("TJ", new String[]{"on","off"}, "on");
+ options.addRecognizedOption("TP", CommandLineOptions.TYPE_FILENAME,
+ "Use profiling trace listener, with specified output file");
+ options.addRecognizedOption("threads", CommandLineOptions.TYPE_INTEGER | CommandLineOptions.VALUE_REQUIRED,
+ "Run stylesheet on directory of files divided in N threads");
+ options.addRecognizedOption("tree", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Use specified tree model for source documents");
+ options.setPermittedValues("tree", new String[]{"linked","tiny","tinyc"}, null);
+ options.addRecognizedOption("traceout", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "File for output of trace() and -T output");
+ options.addRecognizedOption("u", CommandLineOptions.TYPE_BOOLEAN,
+ "Interpret filename arguments as URIs");
+ options.setPermittedValues("u", new String[]{"on","off"}, "on");
+ options.addRecognizedOption("val", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Apply validation to source documents");
+ options.setPermittedValues("val", new String[]{"strict","lax"}, "strict");
+ options.addRecognizedOption("versionmsg", CommandLineOptions.TYPE_BOOLEAN,
+ "Output warning when stylesheet specifies version='1.0");
+ options.addRecognizedOption("warnings", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Handling of recoverable dynamic errors");
+ options.setPermittedValues("warnings", new String[]{"silent","recover","fatal"}, null);
+ options.addRecognizedOption("x", CommandLineOptions.TYPE_CLASSNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use named XMLReader class for parsing source documents");
+ options.addRecognizedOption("xi", CommandLineOptions.TYPE_BOOLEAN,
+ "Expand XInclude directives in source documents");
+ options.addRecognizedOption("xmlversion", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Indicate whether XML 1.1 is supported");
+ options.setPermittedValues("xmlversion", new String[]{"1.0","1.1"}, null);
+ options.addRecognizedOption("xsd", CommandLineOptions.TYPE_FILENAME_LIST | CommandLineOptions.VALUE_REQUIRED,
+ "List of schema documents to be preloaded");
+ options.addRecognizedOption("xsdversion", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Indicate whether XSD 1.1 is supported");
+ options.setPermittedValues("xsdversion", new String[]{"1.0","1.1"}, null);
+ options.addRecognizedOption("xsiloc", CommandLineOptions.TYPE_BOOLEAN,
+ "Load schemas named in xsi:schemaLocation (default on)");
+ options.addRecognizedOption("xsl", CommandLineOptions.TYPE_FILENAME | CommandLineOptions.VALUE_REQUIRED,
+ "Stylesheet file");
+ options.addRecognizedOption("xsltversion", CommandLineOptions.TYPE_ENUMERATION | CommandLineOptions.VALUE_REQUIRED,
+ "Indicate whether the XSLT processor should support XSLT 2.0 or XSLT 3.0");
+ options.setPermittedValues("xsltversion", new String[]{"0.0","2.0","2.1","3.0"}, null);
+ options.addRecognizedOption("y", CommandLineOptions.TYPE_CLASSNAME | CommandLineOptions.VALUE_REQUIRED,
+ "Use named XMLReader class for parsing stylesheet and schema documents");
+ options.addRecognizedOption("?", CommandLineOptions.VALUE_PROHIBITED,
+ "Display command line help text");
+
+ }
+
+
+ class TransformThread extends Thread{
+ private File outputDir;
+ private Templates sheet;
+ private CommandLineOptions options;
+ private PrintStream traceDestination;
+ private List<Source> sources;
+ private int start;
+
+ TransformThread(int i, Templates st, List<Source> s, File out, CommandLineOptions opt, PrintStream trace){
+ start = i;
+ sheet = st;
+ sources = s;
+ options = opt;
+ outputDir = out;
+ traceDestination = trace;
+ }
+
+ public long getStart(){
+ return start;
+ }
+
+ public void run() {
+ try{
+ processDirectory(sources, sheet, outputDir, options, traceDestination);
+ }catch(Exception err){
+ err.printStackTrace();
+ }
+ }
+
+ }
+
+
+ /**
+ * Support method for main program. This support method can also be invoked from subclasses
+ * that support the same command line interface
+ *
+ * @param args the command-line arguments
+ * @param command the form of the command as written by the user. Not used, retained for backwards compatibility
+ */
+
+ public void doTransform(String args[], String command) {
+
+
+ String sourceFileName = null;
+ String styleFileName = null;
+ File outputFile = null;
+ String outputFileName = null;
+ boolean useAssociatedStylesheet;
+ boolean wholeDirectory = false;
+ boolean dtdValidation = false;
+ String styleParserName = null;
+ boolean explain = false;
+ String explainOutputFileName = null;
+ String additionalSchemas = null;
+ PrintStream traceDestination;
+ TraceListener traceListener = null;
+ boolean closeTraceDestination = false;
+ TransformThread [] th = null;
+ int threadCount = 0;
+
+ CommandLineOptions options = new CommandLineOptions();
+ setPermittedOptions(options);
+ try {
+ options.setActualOptions(args);
+ } catch (XPathException err) {
+ quit(err.getMessage(), 2);
+ }
+
+
+ schemaAware = false;
+ String configFile = options.getOptionValue("config");
+ if (configFile != null) {
+ try {
+ config = Configuration.readConfiguration((new StreamSource(configFile)));
+ initializeConfiguration(config);
+ schemaAware = config.isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XSLT);
+ compilerInfo = config.getDefaultXsltCompilerInfo();
+ } catch (XPathException e) {
+ quit(e.getMessage(), 2);
+ }
+ }
+
+ if (config == null && !schemaAware) {
+ schemaAware = options.testIfSchemaAware();
+ }
+
+ if (config == null) {
+ config = Configuration.newConfiguration();
+ initializeConfiguration(config);
+ config.setVersionWarning(true); // unless suppressed by command line options
+ try {
+ setFactoryConfiguration(schemaAware, null);
+ compilerInfo = config.getDefaultXsltCompilerInfo();
+ if (schemaAware) {
+ config.checkLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XSLT, "schema-aware XSLT");
+ compilerInfo.setSchemaAware(true);
+ } else {
+ compilerInfo.setSchemaAware(false);
+ }
+ } catch (Exception err) {
+ err.printStackTrace();
+ quit(err.getMessage(), 2);
+ }
+ }
+
+ // Process the named options
+
+ try {
+
+ // Apply those options which simply update the Configuration
+
+ options.applyToConfiguration(config);
+
+ // Apply options that are processed locally
+
+ allowExit = !"off".equals(options.getOptionValue("quit"));
+
+ useAssociatedStylesheet = "on".equals(options.getOptionValue("a"));
+
+ String value = options.getOptionValue("explain");
+ if (value != null) {
+ explain = true;
+ config.setBooleanProperty(FeatureKeys.TRACE_OPTIMIZER_DECISIONS, true);
+ if (!"".equals(value)) {
+ explainOutputFileName = value;
+ }
+ }
+
+ value = options.getOptionValue("o");
+ if (value != null) {
+ outputFileName = value;
+ }
+
+ value = options.getOptionValue("p");
+ if ("on".equals(value)) {
+ config.setParameterizedURIResolver();
+ useURLs = true;
+ }
+
+ value = options.getOptionValue("repeat");
+ if (value != null) {
+ try {
+ repeat = Integer.parseInt(value);
+ } catch (NumberFormatException err) {
+ badUsage("Bad number after -repeat");
+ }
+ }
+
+ value = options.getOptionValue("s");
+ if (value != null) {
+ sourceFileName = value;
+ }
+
+ value = options.getOptionValue("threads");
+ if (value != null) {
+ threadCount = Integer.parseInt(value);
+ }
+
+ value = options.getOptionValue("t");
+ if (value != null) {
+ System.err.println(config.getProductTitle());
+ System.err.println(Configuration.getPlatform().getPlatformVersion());
+ config.setBooleanProperty(FeatureKeys.TIMING, true);
+ showTime = true;
+ }
+
+ value = options.getOptionValue("T");
+ if (value != null) {
+ if ("".equals(value)) {
+ traceListener = new net.sf.saxon.trace.XSLTTraceListener();
+ } else {
+ traceListener = config.makeTraceListener(value);
+ }
+ config.setConfigurationProperty(FeatureKeys.TRACE_LISTENER, traceListener);
+ config.setBooleanProperty(FeatureKeys.LINE_NUMBERING, true);
+ }
+
+ value = options.getOptionValue("TP");
+ if (value != null) {
+ traceListener = new TimingTraceListener();
+ config.setConfigurationProperty(FeatureKeys.TRACE_LISTENER, traceListener);
+ config.setBooleanProperty(FeatureKeys.LINE_NUMBERING, true);
+ config.getDefaultXsltCompilerInfo().setCodeInjector(new TimingCodeInjector());
+ if (value.length() > 0) {
+ traceListener.setOutputDestination(
+ new PrintStream(new FileOutputStream(new File(value))));
+ }
+ }
+
+ value = options.getOptionValue("traceout");
+ if (value == null) {
+ traceDestination = config.getStandardErrorOutput();
+ } else {
+ if (value.equals("#err")) {
+ traceDestination = System.err;
+ } else if (value.equals("#out")) {
+ traceDestination = System.out;
+ } else if (value.equals("#null")) {
+ traceDestination = null;
+ } else {
+ traceDestination = new PrintStream(new FileOutputStream(new File(value)));
+ closeTraceDestination = true;
+ }
+ }
+
+ value = options.getOptionValue("u");
+ if (value != null) {
+ useURLs = "on".equals(value);
+ }
+
+ value = options.getOptionValue("x");
+ if (value != null) {
+ sourceParserName = value;
+ config.setConfigurationProperty(FeatureKeys.SOURCE_PARSER_CLASS, sourceParserName);
+ }
+
+ value = options.getOptionValue("xsd");
+ if (value != null) {
+ additionalSchemas = value;
+ }
+
+ value = options.getOptionValue("xsdversion");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.XSD_VERSION, value);
+ }
+
+ value = options.getOptionValue("xsl");
+ if (value != null) {
+ styleFileName = value;
+ }
+
+ value = options.getOptionValue("y");
+ if (value != null) {
+ styleParserName = value;
+ config.setConfigurationProperty(FeatureKeys.STYLE_PARSER_CLASS, value);
+ }
+
+ value = options.getOptionValue("?");
+ if (value != null) {
+ badUsage("");
+ }
+
+ compilerInfo.setRecoveryPolicy(config.getRecoveryPolicy());
+
+ // Apply options defined locally in a subclass
+
+ applyLocalOptions(options, config);
+
+ // Check consistency of selected options
+
+ if (options.getOptionValue("it") != null && useAssociatedStylesheet) {
+ badUsage("-it and -a options cannot be used together");
+ }
+
+ List<String> positional = options.getPositionalOptions();
+ int currentPositionalOption = 0;
+
+ if (options.getOptionValue("it") == null && sourceFileName == null) {
+ if (positional.size() == currentPositionalOption) {
+ badUsage("No source file name");
+ }
+ sourceFileName = positional.get(currentPositionalOption++);
+ }
+
+ if (!useAssociatedStylesheet && styleFileName == null) {
+ if (positional.size() == currentPositionalOption) {
+ badUsage("No stylesheet file name");
+ }
+ styleFileName = positional.get(currentPositionalOption++);
+ }
+
+ if (currentPositionalOption < positional.size()) {
+ badUsage("Unrecognized option: " + positional.get(currentPositionalOption));
+ }
+
+ config.displayLicenseMessage();
+ if (schemaAware) {
+ config.checkLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XSLT, "schema-aware XSLT");
+ }
+
+ if (traceListener instanceof AbstractTraceListener && traceDestination != null) {
+ traceListener.setOutputDestination(traceDestination);
+ }
+
+ if (additionalSchemas != null) {
+ CommandLineOptions.loadAdditionalSchemas(config, additionalSchemas);
+ }
+
+ List<Source> sources = new ArrayList<Source>();
+ if (sourceFileName != null) {
+ boolean useSAXSource = sourceParserName != null || dtdValidation;
+ wholeDirectory = CommandLineOptions.loadDocuments(sourceFileName, useURLs, config, useSAXSource, sources);
+ sources = preprocess(sources);
+ if (wholeDirectory) {
+ if (outputFileName == null) {
+ quit("To process a directory, -o must be specified", 2);
+ } else if (outputFileName.equals(sourceFileName)) {
+ quit("Output directory must be different from input", 2);
+ } else {
+ outputFile = new File(outputFileName);
+ if (!outputFile.isDirectory()) {
+ quit("Input is a directory, but output is not", 2);
+ }
+ }
+ }
+ }
+
+ if (outputFileName != null && !wholeDirectory) {
+ outputFile = new File(outputFileName);
+ if (outputFile.isDirectory()) {
+ quit("Output is a directory, but input is not", 2);
+ }
+ }
+
+ if (useAssociatedStylesheet) {
+ if (wholeDirectory) {
+ processDirectoryAssoc(sources, outputFile, options, traceDestination);
+ } else {
+ processFileAssoc(sources.get(0), null, outputFile, options,
+ traceDestination);
+ }
+ } else {
+
+ long startTime = (new Date()).getTime();
+
+ PreparedStylesheet sheet = null;
+
+
+ Source styleSource;
+ XMLReader styleParser = null;
+ if (useURLs || styleFileName.startsWith("http:")
+ || styleFileName.startsWith("file:")) {
+ styleSource = config.getURIResolver().resolve(styleFileName, null);
+ if (styleSource == null) {
+ styleSource = config.getSystemURIResolver().resolve(styleFileName, null);
+ }
+ } else if (styleFileName.equals("-")) {
+ // take input from stdin
+ if (styleParserName == null) {
+ styleSource = new StreamSource(System.in);
+ } else if (Configuration.getPlatform().isJava()) {
+ styleParser = config.getStyleParser();
+ styleSource = new SAXSource(styleParser, new InputSource(System.in));
+ } else {
+ styleSource = new StreamSource(System.in);
+ }
+ } else {
+ File sheetFile = new File(styleFileName);
+ if (!sheetFile.exists()) {
+ quit("Stylesheet file " + sheetFile + " does not exist", 2);
+ }
+ if (styleParserName == null) {
+ styleSource = new StreamSource(sheetFile.toURI().toString());
+ } else {
+ InputSource eis = new InputSource(sheetFile.toURI().toString());
+ styleParser = config.getStyleParser();
+ styleSource = new SAXSource(styleParser, eis);
+ }
+ }
+
+ if (styleSource == null) {
+ quit("URIResolver for stylesheet file must return a Source", 2);
+ }
+ for (int j=0; j<repeat; j++) {
+ // Repeat loop is to get reliable performance data
+ options.applyStaticParams(config, compilerInfo);
+ sheet = PreparedStylesheet.compile(styleSource, config, compilerInfo);
+ if (styleParser != null) {
+ config.reuseStyleParser(styleParser);
+ // pointless, because the Configuration won't be used again; but we want to set a good example
+ }
+ if (showTime) {
+ long endTime = now();
+ System.err.println("Stylesheet compilation time: " + (endTime - startTime) + " milliseconds");
+ startTime = endTime;
+ }
+ }
+
+ if (explain) {
+
+ OutputStream explainOutput;
+ if (explainOutputFileName == null) {
+ explainOutput = config.getStandardErrorOutput();
+ } else {
+ explainOutput = new FileOutputStream(new File(explainOutputFileName));
+ }
+ Properties props = ExpressionPresenter.makeDefaultProperties();
+ Receiver diag = config.getSerializerFactory().getReceiver(
+ new StreamResult(explainOutput),
+ config.makePipelineConfiguration(),
+ props);
+ ExpressionPresenter expressionPresenter = new ExpressionPresenter(config, diag);
+ sheet.explain(expressionPresenter);
+ expressionPresenter.close();
+ }
+
+ if (wholeDirectory) {
+ if((threadCount > 0) && (sources.size() > 1)){
+ if (threadCount > sources.size()) {
+ threadCount = sources.size();
+ }
+
+ //calculate sources per thread
+ int sourcesPerThread = (int)Math.floor(sources.size()/threadCount);
+
+ //split remainder of sources amongst rem threads
+ int rem = sources.size() % threadCount;
+ th = new TransformThread[threadCount];
+ // long elapsedTime = System.nanoTime();
+ for(int i= 0, j = 0, z = 0; i < sources.size(); j++, i += sourcesPerThread + z){
+ z = (j < rem ? 1 : 0); //split remainder of sources amongst rem threads
+ th[j] = new TransformThread(i, sheet, sources.subList(i, i + sourcesPerThread + z), outputFile, options, traceDestination);
+ th[j].start();
+ }
+ for (TransformThread aTh : th) {
+ aTh.join();
+ }
+ // elapsedTime = System.nanoTime() - elapsedTime;
+ // System.out.println("Total Elapsed Time: "+elapsedTime/1e6 + "ms");
+
+
+ }else{
+ processDirectory(sources, sheet, outputFile, options, traceDestination);
+ }
+ } else {
+ Source source = (sources == null || sources.isEmpty() ? null : sources.get(0));
+ processFile(source, sheet, outputFile, options, traceDestination);
+ }
+ if (closeTraceDestination) {
+ traceDestination.close();
+ }
+ }
+ } catch (TerminationException err) {
+ quit(err.getMessage(), 1);
+ } catch (TransformerConfigurationException err) {
+ //err.printStackTrace();
+ quit(err.getMessage(), 2);
+ } catch (TransformerException err) {
+ //err.printStackTrace();
+ quit("Transformation failed: " + err.getMessage(), 2);
+ } catch (TransformerFactoryConfigurationError err) {
+ //err.printStackTrace();
+ quit("Transformation failed: " + err.getMessage(), 2);
+ } catch (LicenseException err) {
+ quit("Transformation failed: " + err.getMessage(), 2);
+ } catch (Exception err2) {
+ err2.printStackTrace();
+ quit("Fatal error during transformation: " + err2.getClass().getName() + ": " +
+ (err2.getMessage() == null ? " (no message)" : err2.getMessage()), 2);
+ }
+ }
+
+ /**
+ * Customisation hook called immediately after the Configuration
+ * object is instantiated. The intended purpose of this hook is to allow
+ * a subclass to supply an OEM license key programmatically, but it can also
+ * be used for other initialization of the Configuration. This method is
+ * called before analyzing the command line options, so configuration settings
+ * made at this stage may be overridden when the command line options are processed.
+ * However, if a configuration file is used, the settings defined in the configuration
+ * file will have been applied.
+ * @param config the Configuration object
+ */
+
+ protected void initializeConfiguration(Configuration config) {
+ // no action: provided for subclasses to override
+ }
+
+ /**
+ * Customisation hook called immediately after the Configuration
+ * object is instantiated. This hook is retained for backwards
+ * compatibility but it is recommended to implement {@link #initializeConfiguration}
+ * in preference. This method is called after {@link #initializeConfiguration},
+ * but only if the configuration was not created using a configuration file.
+ * The default implementation does nothing.
+ * @param schemaAware True if the transformation is to be schema-aware
+ * @param className Always null.
+ * @throws LicenseException can be thrown if there is no valid license available
+ */
+
+ public void setFactoryConfiguration(boolean schemaAware, String className) throws LicenseException {
+ // no action
+ }
+
+ /**
+ * Customisation hook: apply options defined locally in a subclass. This method
+ * allows a subclass to recognize and implement command line options that are not recognized
+ * by the superclass. To prevent Saxon rejecting such options as errors, the method
+ * {@link #setPermittedOptions} must be overridden in the subclass to add details of
+ * options recognized in the subclass.
+ * @param options the CommandLineOptions. This will contain details of all the options
+ * that were specified on the command line. Those that are recognized by the standard Saxon
+ * command line interface will already have been processed; other options can now be processed
+ * by the subclass.
+ * @param config the Saxon Configuration
+ */
+
+ protected void applyLocalOptions(CommandLineOptions options, Configuration config) {
+ // no action: provided for subclasses to override
+ }
+
+
+ /**
+ * Preprocess the list of sources. This method exists so that it can be
+ * overridden in a subclass
+ * @param sources the list of Source objects
+ * @return a revised list of Source objects
+ */
+
+ public List<Source> preprocess(List<Source> sources) throws XPathException {
+ return sources;
+ }
+
+ /**
+ * Get the configuration.
+ * @return the Saxon configuration
+ */
+
+ protected Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Exit with a message
+ *
+ * @param message The message to be output
+ * @param code The result code to be returned to the operating
+ * system shell
+ */
+
+ protected void quit(String message, int code) {
+ System.err.println(message);
+ if (allowExit) {
+ System.exit(code);
+ } else {
+ throw new RuntimeException(message);
+ }
+ }
+
+ /**
+ * Process each file in the source directory using its own associated stylesheet
+ *
+ * @param sources The sources in the directory to be processed
+ * @param outputDir The directory in which output files are to be
+ * created
+ * @param options Command line options
+ * @param traceDestination output destination for fn:trace() calls
+ * @throws Exception when any error occurs during a transformation
+ */
+
+ private void processDirectoryAssoc(List sources, File outputDir,
+ CommandLineOptions options, PrintStream traceDestination)
+ throws Exception {
+
+ int failures = 0;
+ for (int f = 0; f < sources.size(); f++) {
+ Source source = (Source)sources.get(f);
+ String localName = getLocalFileName(source);
+ try {
+ processFileAssoc(source, localName, outputDir, options, traceDestination);
+ } catch (XPathException err) {
+ failures++;
+ System.err.println("While processing " + localName +
+ ": " + err.getMessage() + '\n');
+ }
+ }
+ if (failures > 0) {
+ throw new XPathException(failures + " transformation" +
+ (failures == 1 ? "" : "s") + " failed");
+ }
+ }
+
+ /**
+ * Make an output file in the output directory, with filename extension derived from the
+ * media-type produced by the stylesheet
+ *
+ * @param directory The directory in which the file is to be created
+ * @param localName The local name of the file within the
+ * directory, excluding the file type suffix
+ * @param sheet The Templates object identifying the stylesheet -
+ * used to determine the output method, and hence the suffix to be
+ * used for the filename
+ * @return The newly created file
+ */
+
+ private File makeOutputFile(File directory, String localName, Templates sheet) {
+ String mediaType = sheet.getOutputProperties().getProperty(OutputKeys.MEDIA_TYPE);
+ String suffix = ".xml";
+ if ("text/html".equals(mediaType)) {
+ suffix = ".html";
+ } else if ("text/plain".equals(mediaType)) {
+ suffix = ".txt";
+ }
+ String prefix = localName;
+ if (localName.endsWith(".xml") || localName.endsWith(".XML")) {
+ prefix = localName.substring(0, localName.length() - 4);
+ }
+ return new File(directory, prefix + suffix);
+ }
+
+
+ /**
+ * Process a single source file using its associated stylesheet(s)
+ *
+ * @param sourceInput Identifies the source file to be transformed
+ * @param localName The local name of the file within the
+ * directory, excluding the file type suffix
+ * @param outputFile The output file to contain the results of the
+ * transformation
+ * @param options Command line options
+ * @param traceDestination Destination for trace output
+ * @throws XPathException If the transformation fails
+ */
+
+ private void processFileAssoc(Source sourceInput, String localName, File outputFile,
+ CommandLineOptions options, PrintStream traceDestination)
+ throws TransformerException {
+ if (showTime) {
+ System.err.println("Processing " + sourceInput.getSystemId() + " using associated stylesheet");
+ }
+ long startTime = now();
+
+ Source style = PreparedStylesheet.getAssociatedStylesheet(config, sourceInput, null, null, null);
+ Templates sheet = PreparedStylesheet.compile(style, config, compilerInfo);
+
+ if (showTime) {
+ System.err.println("Prepared associated stylesheet " + style.getSystemId());
+ }
+
+ Controller controller = newController(sheet, options, traceDestination);
+
+ File outFile = outputFile;
+
+ if (outFile != null && outFile.isDirectory()) {
+ outFile = makeOutputFile(outFile, localName, sheet);
+ }
+
+ StreamResult result =
+ (outFile == null ? new StreamResult(System.out) : new StreamResult(outFile.toURI().toString()));
+
+ try {
+ controller.transform(sourceInput, result);
+ } catch (TerminationException err) {
+ throw err;
+ } catch (XPathException err) {
+ // The error message will already have been displayed; don't do it twice
+ if (!err.hasBeenReported()) {
+ err.printStackTrace();
+ }
+ throw new XPathException("Run-time errors were reported");
+ }
+
+ if (showTime) {
+ long endTime = now();
+ System.err.println("Execution time: " + CommandLineOptions.showExecutionTime(endTime - startTime));
+ }
+ }
+
+ /**
+ * Create a new Controller. This method is protected so it can be overridden in a subclass, allowing additional
+ * options to be set on the Controller
+ * @param sheet The Templates object representing the compiled stylesheet
+ * @param options The commmand line options
+ * @param traceDestination destination for trace output
+ * @return the newly constructed Controller to be used for the transformation
+ * @throws TransformerException if any error occurs
+ */
+
+ protected Controller newController(
+ Templates sheet, CommandLineOptions options, PrintStream traceDestination) throws TransformerException {
+ Controller controller = (Controller)sheet.newTransformer();
+ GlobalParameterSet params = new GlobalParameterSet();
+ Properties outputProperties = new Properties();
+ options.setParams(config, params, outputProperties);
+ controller.setGlobalParameterSet(params);
+ controller.setTraceFunctionDestination(traceDestination);
+ controller.setOutputProperties(outputProperties);
+ String initialMode = options.getOptionValue("im");
+ if (initialMode != null) {
+ controller.setInitialMode(initialMode);
+ }
+ String initialTemplate = options.getOptionValue("it");
+ if (initialTemplate != null) {
+ controller.setInitialTemplate(initialTemplate);
+ }
+ String now = options.getOptionValue("now");
+ if (now != null) {
+ DateTimeValue currentDateTime = (DateTimeValue)DateTimeValue.makeDateTimeValue(now, config.getConversionRules()).asAtomic();
+ controller.setCurrentDateTime(currentDateTime);
+ }
+ return controller;
+ }
+
+ /**
+ * Get current time in milliseconds
+ * @return the current time in milliseconds since 1970
+ */
+
+ private static long now() {
+ return System.currentTimeMillis();
+ }
+
+ /**
+ * Process each file in the source directory using the same supplied stylesheet
+ *
+ * @param sources The sources in the directory to be processed
+ * @param sheet The Templates object identifying the stylesheet
+ * @param outputDir The directory in which output files are to be
+ * created
+ * @param options Command line options
+ * @param traceDestination Destination for output from fn:trace() calls
+ * @throws XPathException when any error occurs during a
+ * transformation
+ */
+
+ private void processDirectory(List<Source> sources, Templates sheet, File outputDir, CommandLineOptions options,
+ PrintStream traceDestination)
+ throws TransformerException {
+ int failures = 0;
+ for (Source source : sources) {
+ String localName = getLocalFileName(source);
+ try {
+ File outputFile = makeOutputFile(outputDir, localName, sheet);
+ processFile(source, sheet, outputFile, options, traceDestination);
+ } catch (XPathException err) {
+ failures++;
+ System.err.println("While processing " + localName + ": " + err.getMessage() + '\n');
+ }
+ }
+ if (failures > 0) {
+ throw new XPathException(failures + " transformation" +
+ (failures == 1 ? "" : "s") + " failed");
+ }
+ }
+
+ private static String getLocalFileName(Source source) {
+ try {
+ String path = new URI(source.getSystemId()).getPath();
+ while (true) {
+ int sep = path.indexOf('/');
+ if (sep < 0) {
+ return path;
+ } else {
+ path = path.substring(sep + 1);
+ }
+ }
+ } catch (URISyntaxException err) {
+ throw new IllegalArgumentException(err.getMessage());
+ }
+ }
+
+ /**
+ * Process a single file using a supplied stylesheet
+ *
+ * @param source The source XML document to be transformed (maybe null if an initial template
+ * is specified)
+ * @param sheet The Templates object identifying the stylesheet
+ * @param outputFile The output file to contain the results of the
+ * transformation
+ * @param options The command line options
+ * @param traceDestination Destination for output from fn:trace() function
+ * @throws net.sf.saxon.trans.XPathException
+ * If the transformation fails
+ */
+
+ private void processFile(/*@Nullable*/ Source source, Templates sheet, /*@Nullable*/ File outputFile, CommandLineOptions options,
+ PrintStream traceDestination)
+ throws TransformerException {
+
+ long totalTime = 0;
+ int runs = 0;
+ for (int r = 0; r < repeat; r++) { // repeat is for internal testing/timing
+ if (showTime) {
+ String msg = "Processing ";
+ if (source != null) {
+ msg += source.getSystemId();
+ } else {
+ msg += " (no source document)";
+ }
+ String initialMode = options.getOptionValue("im");
+ if (initialMode != null) {
+ msg += " initial mode = " + initialMode;
+ }
+ String initialTemplate = options.getOptionValue("it");
+ if (initialTemplate != null) {
+ msg += " initial template = " + initialTemplate;
+ }
+ System.err.println(msg);
+ }
+ long startTime = now();
+ runs++;
+ Controller controller = newController(sheet, options, traceDestination);
+
+ Result result =
+ (outputFile == null ?
+ new StreamResult(System.out) :
+ new StreamResult(outputFile.toURI().toString()));
+
+ try {
+ controller.transform(source, result);
+ } catch (TerminationException err) {
+ throw err;
+ } catch (XPathException err) {
+ // The message will already have been displayed; don't do it twice
+ if (!err.hasBeenReported()) {
+ err.printStackTrace();
+ }
+ throw new XPathException("Run-time errors were reported");
+ }
+
+ long endTime = now();
+ totalTime += (endTime - startTime);
+ if (showTime) {
+ System.err.println("Execution time: " + CommandLineOptions.showExecutionTime(endTime - startTime));
+ System.err.println("Memory used: " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
+ config.getNamePool().statistics();
+ if (repeat > 1) {
+ System.err.println("-------------------------------");
+ Runtime.getRuntime().gc();
+ }
+ }
+ if (repeat == 999999 && totalTime > 60000) {
+ break;
+ }
+ }
+ if (repeat > 1) {
+ System.err.println("*** Average execution time over " + runs + " runs: " + (totalTime / runs) + "ms");
+ }
+ }
+
+
+ /**
+ * Report incorrect usage of the command line, with a list of the options and arguments that are available
+ *
+ * @param message The error message
+ */
+ protected void badUsage(String message) {
+ if (!"".equals(message)) {
+ System.err.println(message);
+ }
+ if (!showTime) {
+ System.err.println(config.getProductTitle());
+ }
+ System.err.println("Usage: see http://saxonica.com/documentation/html/using-xsl/commandline.html");
+ System.err.println("Format: " + getClass().getName() + " options params");
+ CommandLineOptions options = new CommandLineOptions();
+ setPermittedOptions(options);
+ System.err.println("Options available:" + options.displayPermittedOptions());
+ System.err.println("Use -XYZ:? for details of option XYZ");
+ System.err.println("Params: ");
+ System.err.println(" param=value Set stylesheet string parameter");
+ System.err.println(" +param=filename Set stylesheet document parameter");
+ System.err.println(" ?param=expression Set stylesheet parameter using XPath");
+ System.err.println(" !param=value Set serialization parameter");
+ if (allowExit) {
+ if ("".equals(message)) {
+ System.exit(0);
+ } else {
+ System.exit(2);
+ }
+ } else {
+ throw new RuntimeException(message);
+ }
+ }
+
+
+
+}
+
diff --git a/sf/saxon/TransformerFactoryImpl.java b/sf/saxon/TransformerFactoryImpl.java
new file mode 100644
index 0000000..726c090
--- /dev/null
+++ b/sf/saxon/TransformerFactoryImpl.java
@@ -0,0 +1,500 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.trans.CompilerInfo;
+import net.sf.saxon.trans.ConfigurationReader;
+import net.sf.saxon.trans.XPathException;
+import org.xml.sax.XMLFilter;
+
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.sax.*;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import java.io.File;
+
+
+/**
+ * A TransformerFactoryImpl instance can be used to create Transformer and Template
+ * objects.
+ *
+ * <p>The system property that determines which Factory implementation
+ * to create is named "javax.xml.transform.TransformerFactory". This
+ * property names a concrete subclass of the TransformerFactory abstract
+ * class. If the property is not defined, a platform default is be used.</p>
+ *
+ * <p>This implementation class implements the abstract methods on both the
+ * javax.xml.transform.TransformerFactory and javax.xml.transform.sax.SAXTransformerFactory
+ * classes.
+ */
+
+public class TransformerFactoryImpl extends SAXTransformerFactory {
+
+ private Configuration config;
+
+ /**
+ * Default constructor.
+ */
+ public TransformerFactoryImpl() {
+ config = Configuration.newConfiguration();
+ config.setProcessor(this);
+ }
+
+ /**
+ * Construct a TransformerFactory using an existing Configuration.
+ * @param config the Saxon configuration
+ */
+ public TransformerFactoryImpl(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Set the configuration. This can also be done using the JAXP method
+ * setAttribute, with the attribute name {@link FeatureKeys#CONFIGURATION}
+ * @param config the Saxon configuration
+ */
+
+ public void setConfiguration(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Get the configuration. This can also be done using the JAXP method
+ * getAttribute, with the attribute name {@link FeatureKeys#CONFIGURATION}
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Process the Source into a Transformer object.
+ * The Transformer object must not be used in multiple threads running concurrently.
+ * Different TransformerFactories can be used concurrently by different
+ * threads.
+ *
+ * @param source An object that holds a URI, input stream, etc. of the stylesheet
+ * used to perform the transformation.
+ *
+ * @return A Transformer object that may be used to perform a transformation
+ * in a single thread, never null.
+ *
+ * @exception TransformerConfigurationException May throw this during the parse
+ * when it is constructing the Templates object and fails.
+ */
+
+ public Transformer newTransformer(Source source)
+ throws TransformerConfigurationException {
+ Templates templates = newTemplates(source);
+ return templates.newTransformer();
+ }
+
+ /**
+ * Create a new Transformer object that performs a copy
+ * of the source to the result.
+ *
+ * @return A Transformer object that may be used to perform a transformation
+ * in a single thread, never null.
+ *
+ * @exception TransformerConfigurationException May throw this during
+ * the parse when it is constructing the
+ * Templates object and fails.
+ */
+
+ public Transformer newTransformer()
+ throws TransformerConfigurationException {
+
+ return new IdentityTransformer(config);
+ }
+
+
+ /**
+ * Process the Source into a Templates object, which is a
+ * a compiled representation of the source. This Templates object
+ * may then be used concurrently across multiple threads. Creating
+ * a Templates object allows the TransformerFactory to do detailed
+ * performance optimization of transformation instructions, without
+ * penalizing runtime transformation.
+ *
+ * @param source An object that holds a URL, input stream, etc.
+ *
+ * @return A Templates object capable of being used for transformation purposes,
+ * never null.
+ *
+ * @exception TransformerConfigurationException May throw this during the parse when it
+ * is constructing the Templates object and fails.
+ */
+
+ public Templates newTemplates(Source source)
+ throws TransformerConfigurationException {
+
+ CompilerInfo info = config.getDefaultXsltCompilerInfo();
+ PreparedStylesheet pss = new PreparedStylesheet(config, info);
+ pss.prepare(source);
+ return pss;
+ }
+
+ /**
+ * Process the Source into a Templates object, which is a
+ * a compiled representation of the source. This Templates object
+ * may then be used concurrently across multiple threads. Creating
+ * a Templates object allows the TransformerFactory to do detailed
+ * performance optimization of transformation instructions, without
+ * penalizing runtime transformation.
+ *
+ * @param source An object that holds a URL, input stream, etc.
+ * @param info compile-time options for this stylesheet compilation
+ *
+ * @return A Templates object capable of being used for transformation purposes,
+ * never null.
+ *
+ * @exception TransformerConfigurationException May throw this during the parse when it
+ * is constructing the Templates object and fails.
+ */
+
+ public Templates newTemplates(Source source, CompilerInfo info)
+ throws TransformerConfigurationException {
+
+ PreparedStylesheet pss = new PreparedStylesheet(config, info);
+ pss.prepare(source);
+ return pss;
+ }
+
+
+ /**
+ * Get the stylesheet specification(s) associated
+ * via the xml-stylesheet processing instruction (see
+ * http://www.w3.org/TR/xml-stylesheet/) with the document
+ * document specified in the source parameter, and that match
+ * the given criteria. Note that it is possible to return several
+ * stylesheets, in which case they are applied as if they were
+ * a list of imports or cascades.
+ *
+ * @param source The XML source document.
+ * @param media The media attribute to be matched. May be null, in which
+ * case the prefered templates will be used (i.e. alternate = no).
+ * @param title The value of the title attribute to match. May be null.
+ * @param charset The value of the charset attribute to match. May be null.
+ *
+ * @return A Source object suitable for passing to the TransformerFactory.
+ *
+ * @throws TransformerConfigurationException if any problems occur
+ */
+
+ public Source getAssociatedStylesheet(
+ Source source, String media, String title, String charset)
+ throws TransformerConfigurationException {
+
+
+ return PreparedStylesheet.getAssociatedStylesheet(config, source, media, title, charset);
+ }
+
+
+ /**
+ * Set an object that is used by default during the transformation
+ * to resolve URIs used in xsl:import, or xsl:include.
+ *
+ * @param resolver An object that implements the URIResolver interface,
+ * or null.
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ config.setURIResolver(resolver);
+ }
+
+ /**
+ * Get the object that is used by default during the transformation
+ * to resolve URIs used in document(), xsl:import, or xsl:include.
+ *
+ * @return The URIResolver that was set with setURIResolver.
+ */
+
+ public URIResolver getURIResolver() {
+ return config.getURIResolver();
+ }
+
+ //======= CONFIGURATION METHODS =======
+
+ private static final String FEATURE_SECURE_PROCESSING =
+ javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING;
+ //"http://javax.xml.XMLConstants/feature/secure-processing";
+ // Note JDK 1.5 dependency
+
+
+ /**
+ * Look up the value of a feature.
+ *
+ * <p>The feature name is any absolute URI.</p>
+ * @param name The feature name, which is an absolute URI.
+ * @return The current state of the feature (true or false).
+ */
+
+ public boolean getFeature(String name) {
+ if (name.equals(SAXSource.FEATURE)) return true;
+ if (name.equals(SAXResult.FEATURE)) return true;
+ if (name.equals(DOMSource.FEATURE)) return true;
+ if (name.equals(DOMResult.FEATURE)) return true;
+ if (name.equals(StreamSource.FEATURE)) return true;
+ if (name.equals(StreamResult.FEATURE)) return true;
+ if (name.equals(SAXTransformerFactory.FEATURE)) return true;
+ if (name.equals(SAXTransformerFactory.FEATURE_XMLFILTER)) return true;
+ if (name.equals(FEATURE_SECURE_PROCESSING)) {
+ return !config.getBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS);
+ }
+ try {
+ Object val = config.getConfigurationProperty(name);
+ return (val instanceof Boolean && ((Boolean)val).booleanValue());
+ } catch (IllegalArgumentException err) {
+ return false;
+ }
+ }
+
+ /**
+ * Allows the user to set specific attributes on the underlying
+ * implementation. An attribute in this context is defined to
+ * be an option that the implementation provides.
+ *
+ * @param name The name of the attribute. This must be one of the constants
+ * defined in class {@link net.sf.saxon.lib.FeatureKeys}.
+ * @param value The value of the attribute.
+ * @throws IllegalArgumentException thrown if Saxon
+ * doesn't recognize the attribute.
+ * @see net.sf.saxon.lib.FeatureKeys
+ */
+
+ public void setAttribute(String name, Object value) throws IllegalArgumentException {
+ if (name.equals(FeatureKeys.CONFIGURATION_FILE)) {
+ ConfigurationReader reader = new ConfigurationReader();
+ try {
+ setConfiguration(reader.makeConfiguration(new StreamSource(new File((String)(value)))));
+ } catch (XPathException err) {
+ throw new IllegalArgumentException(err);
+ }
+ } else if (name.equals(FeatureKeys.CONFIGURATION)) {
+ config = (Configuration)value;
+ } else {
+ config.setConfigurationProperty(name, value);
+ }
+ }
+
+ /**
+ * Allows the user to retrieve specific attributes on the underlying
+ * implementation.
+ * @param name The name of the attribute. This must be one of the constants
+ * defined in class {@link net.sf.saxon.lib.FeatureKeys}.
+ * @return value The value of the attribute.
+ * @throws IllegalArgumentException thrown if the underlying
+ * implementation doesn't recognize the attribute.
+ */
+ /*@Nullable*/ public Object getAttribute(String name) throws IllegalArgumentException{
+ if (name.equals(FeatureKeys.CONFIGURATION)) {
+ return config;
+ } else {
+ return config.getConfigurationProperty(name);
+ }
+ }
+
+ /**
+ * Set the error event listener for the TransformerFactory, which
+ * is used for the processing of transformation instructions,
+ * and not for the transformation itself.
+ *
+ * @param listener The new error listener.
+ * @throws IllegalArgumentException if listener is null.
+ */
+
+ public void setErrorListener(ErrorListener listener)
+ throws IllegalArgumentException {
+ config.setErrorListener(listener);
+ }
+
+ /**
+ * Get the error event handler for the TransformerFactory.
+ * @return The current error listener, which should never be null.
+ */
+ public ErrorListener getErrorListener() {
+ return config.getErrorListener();
+ }
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Methods defined in class javax.xml.transform.sax.SAXTransformerFactory
+ ///////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Get a TransformerHandler object that can process SAX
+ * ContentHandler events into a Result, based on the transformation
+ * instructions specified by the argument.
+ *
+ * @param src The Source of the transformation instructions.
+ *
+ * @return TransformerHandler ready to transform SAX events.
+ *
+ * @throws TransformerConfigurationException If for some reason the
+ * TransformerHandler can not be created.
+ */
+
+ public TransformerHandler newTransformerHandler(Source src)
+ throws TransformerConfigurationException {
+ Templates tmpl = newTemplates(src);
+ return newTransformerHandler(tmpl);
+ }
+
+ /**
+ * Get a TransformerHandler object that can process SAX
+ * ContentHandler events into a Result, based on the Templates argument.
+ *
+ * @param templates The compiled transformation instructions.
+ *
+ * @return TransformerHandler ready to transform SAX events.
+ *
+ * @throws TransformerConfigurationException If for some reason the
+ * TransformerHandler can not be created.
+ */
+
+ public TransformerHandler newTransformerHandler(Templates templates)
+ throws TransformerConfigurationException {
+ if (!(templates instanceof PreparedStylesheet)) {
+ throw new TransformerConfigurationException("Templates object was not created by Saxon");
+ }
+ Controller controller = (Controller)templates.newTransformer();
+ return new TransformerHandlerImpl(controller);
+ }
+
+ /**
+ * Get a TransformerHandler object that can process SAX
+ * ContentHandler events into a Result. The transformation
+ * is defined as an identity (or copy) transformation, for example
+ * to copy a series of SAX parse events into a DOM tree.
+ *
+ * @return A non-null reference to a TransformerHandler, that may
+ * be used as a ContentHandler for SAX parse events.
+ *
+ * @throws TransformerConfigurationException If for some reason the
+ * TransformerHandler cannot be created.
+ */
+
+ public TransformerHandler newTransformerHandler()
+ throws TransformerConfigurationException {
+ Controller controller = new IdentityTransformer(config);
+ return new IdentityTransformerHandler(controller);
+ }
+
+ /**
+ * Get a TemplatesHandler object that can process SAX
+ * ContentHandler events into a Templates object.
+ *
+ * @return A non-null reference to a TransformerHandler, that may
+ * be used as a ContentHandler for SAX parse events.
+ *
+ * @throws TransformerConfigurationException If for some reason the
+ * TemplatesHandler cannot be created.
+ */
+
+ public TemplatesHandler newTemplatesHandler()
+ throws TransformerConfigurationException {
+ return new TemplatesHandlerImpl(config);
+ }
+
+ /**
+ * Create an XMLFilter that uses the given Source as the
+ * transformation instructions.
+ *
+ * @param src The Source of the transformation instructions.
+ *
+ * @return An XMLFilter object, or null if this feature is not supported.
+ *
+ * @throws TransformerConfigurationException If for some reason the
+ * XMLFilter cannot be created.
+ */
+
+ public XMLFilter newXMLFilter(Source src)
+ throws TransformerConfigurationException {
+ Templates tmpl = newTemplates(src);
+ return newXMLFilter(tmpl);
+ }
+
+ /**
+ * Create an XMLFilter, based on the Templates argument..
+ *
+ * @param templates The compiled transformation instructions.
+ *
+ * @return An XMLFilter object, or null if this feature is not supported.
+ *
+ * @throws TransformerConfigurationException If for some reason the
+ * XMLFilter cannot be created.
+ */
+
+ public XMLFilter newXMLFilter(Templates templates)
+ throws TransformerConfigurationException {
+ if (!(templates instanceof PreparedStylesheet)) {
+ throw new TransformerConfigurationException("Supplied Templates object was not created using Saxon");
+ }
+ Controller controller = (Controller)templates.newTransformer();
+ return new Filter(controller);
+ }
+
+ /**
+ * <p>Set a feature for this <code>TransformerFactory</code> and <code>Transformer</code>s
+ * or <code>Template</code>s created by this factory.</p>
+ * <p/>
+ * <p/>
+ * Feature names are fully qualified {@link java.net.URI}s.
+ * Implementations may define their own features.
+ * An {@link javax.xml.transform.TransformerConfigurationException} is thrown if this <code>TransformerFactory</code> or the
+ * <code>Transformer</code>s or <code>Template</code>s it creates cannot support the feature.
+ * It is possible for an <code>TransformerFactory</code> to expose a feature value but be unable to change its state.
+ * </p>
+ * <p/>
+ * <p>All implementations are required to support the FEATURE_SECURE_PROCESSING feature.
+ * When the feature is:</p>
+ * <ul>
+ * <li>
+ * <code>true</code>: the implementation will limit XML processing to conform to implementation limits
+ * and behave in a secure fashion as defined by the implementation.
+ * Examples include resolving user defined style sheets and functions.
+ * If XML processing is limited for security reasons, it will be reported via a call to the registered
+ * {@link javax.xml.transform.ErrorListener#fatalError(javax.xml.transform.TransformerException exception)}.
+ * See {@link #setErrorListener(javax.xml.transform.ErrorListener listener)}. In the Saxon implementation,
+ * this option causes calls on extension functions and extensions instructions to be disabled, and also
+ * disables the use of xsl:result-document to write to secondary output destinations.
+ * </li>
+ * <li>
+ * <code>false</code>: the implementation will processing XML according to the XML specifications without
+ * regard to possible implementation limits.
+ * </li>
+ * </ul>
+ *
+ * @param name Feature name.
+ * @param value Is feature state <code>true</code> or <code>false</code>.
+ * @throws javax.xml.transform.TransformerConfigurationException
+ * if this <code>TransformerFactory</code>
+ * or the <code>Transformer</code>s or <code>Template</code>s it creates cannot support this feature.
+ * @throws NullPointerException If the <code>name</code> parameter is null.
+ */
+
+ public void setFeature(String name, boolean value) throws TransformerConfigurationException {
+ if (name.equals(FEATURE_SECURE_PROCESSING)) {
+ config.setBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS, !value);
+ } else {
+ try {
+ config.setBooleanProperty(name, value);
+ } catch (IllegalArgumentException err) {
+ throw new TransformerConfigurationException("Unsupported TransformerFactory feature: " + name);
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/TransformerHandlerImpl.java b/sf/saxon/TransformerHandlerImpl.java
new file mode 100644
index 0000000..be7f6c9
--- /dev/null
+++ b/sf/saxon/TransformerHandlerImpl.java
@@ -0,0 +1,167 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceivingContentHandler;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.SAXException;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.TransformerHandler;
+
+
+/**
+ * <b>TransformerHandlerImpl</b> implements the javax.xml.transform.sax.TransformerHandler
+ * interface. It acts as a ContentHandler and LexicalHandler which receives a stream of
+ * SAX events representing an input document, and performs a transformation treating this
+ * SAX stream as the source document of the transformation.
+ * @author Michael H. Kay
+ */
+
+public class TransformerHandlerImpl extends ReceivingContentHandler implements TransformerHandler {
+
+ Controller controller;
+ Builder builder;
+ Receiver receiver;
+ /*@Nullable*/ Result result;
+ String systemId;
+ boolean started = false;
+
+ /**
+ * Create a TransformerHandlerImpl and initialise variables. The constructor is protected, because
+ * the Filter should be created using newTransformerHandler() in the SAXTransformerFactory
+ * class
+ * @param controller the Controller to be used
+ */
+
+ protected TransformerHandlerImpl(Controller controller) {
+ this.controller = controller;
+ Configuration config = controller.getConfiguration();
+ int validation = controller.getSchemaValidationMode();
+ builder = controller.makeBuilder();
+ PipelineConfiguration pipe = builder.getPipelineConfiguration();
+ ParseOptions options = pipe.getParseOptions();
+ options.setCheckEntityReferences(true);
+ setPipelineConfiguration(pipe);
+ receiver = controller.makeStripper(builder);
+ if (controller.getExecutable().stripsInputTypeAnnotations()) {
+ receiver = config.getAnnotationStripper(receiver);
+ }
+ if (validation != Validation.PRESERVE) {
+ options.setSchemaValidationMode(validation);
+ options.setStripSpace(Whitespace.NONE);
+ receiver = config.getDocumentValidator(receiver, getSystemId(), options);
+ }
+ setReceiver(receiver);
+ }
+
+ /**
+ * Start of a new document. The TransformerHandler is not serially reusable, so this method
+ * must only be called once.
+ * @throws SAXException only if an overriding subclass throws this exception
+ * @throws UnsupportedOperationException if an attempt is made to reuse the TransformerHandler by calling
+ * startDocument() more than once.
+ */
+
+ public void startDocument () throws SAXException {
+ if (started) {
+ throw new UnsupportedOperationException(
+ "The TransformerHandler is not serially reusable. The startDocument() method must be called once only.");
+ }
+ started = true;
+ super.startDocument();
+ }
+
+ /**
+ * Get the Transformer used for this transformation
+ */
+
+ public Transformer getTransformer() {
+ return controller;
+ }
+
+ /**
+ * Set the SystemId of the document. Note that in reporting location information, Saxon gives
+ * priority to the system Id reported by the SAX Parser in the Locator passed to the
+ * {@link #setDocumentLocator(org.xml.sax.Locator)} method. The SystemId passed to this method
+ * is used as the base URI for resolving relative references.
+ * @param url the systemId of the source document
+ */
+
+ public void setSystemId(String url) {
+ systemId = url;
+ receiver.setSystemId(url);
+ }
+
+ /**
+ * Get the systemId of the document. This will be the systemId obtained from the Locator passed to the
+ * {@link #setDocumentLocator(org.xml.sax.Locator)} method if available, otherwise the SystemId passed
+ * to the {@link #setSystemId(String)} method.
+ */
+
+ public String getSystemId() {
+ return systemId;
+// String s = super.getSystemId();
+// return (s == null ? systemId : s);
+ }
+
+
+ /**
+ * Set the output destination of the transformation
+ */
+
+ public void setResult(/*@Nullable*/ Result result) {
+ if (result==null) {
+ throw new IllegalArgumentException("Result must not be null");
+ }
+ this.result = result;
+ }
+
+ /**
+ * Get the output destination of the transformation
+ * @return the output destination
+ */
+
+ /*@Nullable*/ public Result getResult() {
+ return result;
+ }
+
+ /**
+ * Override the behaviour of endDocument() in ReceivingContentHandler, so that it fires off
+ * the transformation of the constructed document
+ */
+
+ public void endDocument() throws SAXException {
+ super.endDocument();
+ DocumentInfo doc = (DocumentInfo)builder.getCurrentRoot();
+ builder.reset();
+ if (doc==null) {
+ throw new SAXException("No source document has been built");
+ }
+
+ try {
+ controller.transformDocument(doc, result);
+ } catch (TransformerException err) {
+ if (err instanceof XPathException) {
+ controller.reportFatalError((XPathException)err);
+ }
+ throw new SAXException(err);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/TypeCheckerEnvironment.java b/sf/saxon/TypeCheckerEnvironment.java
new file mode 100644
index 0000000..66ada91
--- /dev/null
+++ b/sf/saxon/TypeCheckerEnvironment.java
@@ -0,0 +1,37 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+import net.sf.saxon.expr.CollationMap;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * An abstraction of ExpressionVisitor that provides the minimal set of services needed by
+ * the TypeChecker; used to allow run-time type-checking of higher order function arguments without
+ * retaining the whole static context
+ */
+public interface TypeCheckerEnvironment {
+
+ public Configuration getConfiguration();
+
+ public void issueWarning(String message, SourceLocator locator);
+
+ public CollationMap getCollationMap();
+
+ public XPathContext makeDynamicContext();
+
+ public Expression simplify(Expression exp) throws XPathException;
+
+ public Expression typeCheck(Expression exp, ExpressionVisitor.ContextItemType contextItemType) throws XPathException;
+}
+
diff --git a/sf/saxon/Version.java b/sf/saxon/Version.java
new file mode 100644
index 0000000..e7dc534
--- /dev/null
+++ b/sf/saxon/Version.java
@@ -0,0 +1,140 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon;
+
+/**
+ * The Version class holds the SAXON version information.
+ */
+
+public final class Version {
+
+ private static final int[] STRUCTURED_VERSION = {9,5,1,1};
+ private static final String VERSION = "9.5.1.1";
+ private static final String BUILD = "041714"; //mmddhh
+ private static final String RELEASE_DATE = "2013-06-19";
+ private static final String MAJOR_RELEASE_DATE = "2013-04-17";
+
+ private Version() {
+ // class is never instantiated
+ }
+
+ /**
+ * Return the name of this product. Supports the XSLT 2.0 system property xsl:product-name
+ * @return the string "SAXON"
+ */
+
+
+ public static String getProductName() {
+ return "SAXON";
+ }
+
+ /**
+ * Return the name of the product vendor.
+ * @return the string "Saxonica"
+ */
+
+ public static String getProductVendor() {
+ return "Saxonica";
+ }
+
+ /**
+ * Get the version number of the schema-aware version of the product
+ * @param config the Saxon configuration
+ * @return the version number of this version of Saxon, as a string
+ */
+
+ public static String getProductVariantAndVersion(Configuration config) {
+ String edition = config.getEditionCode();
+ if (edition.equals("PE") || edition.equals("EE")) {
+ if (!config.isLicensedFeature(Configuration.LicenseFeature.PROFESSIONAL_EDITION)) {
+ edition += " (unlicensed)";
+ }
+ }
+ return edition + " " + getProductVersion();
+ }
+
+ /**
+ * Get the user-visible version number of this version of the product
+ * @return the version number of this version of Saxon, as a string: for example "9.0.1"
+ */
+
+ public static String getProductVersion() {
+ return VERSION;
+ }
+
+ /**
+ * Get the four components of the structured version number. This is used in the .NET product
+ * to locate an assembly in the dynamic assembly cache: the assumption is that the third
+ * and fourth components represent implementation changes rather than interface changes
+ * @return the four components of the version number, as an array: for example {9, 0, 1, 1}
+ */
+
+ public static int[] getStructuredVersionNumber() {
+ return STRUCTURED_VERSION;
+ }
+
+ /**
+ * Get the issue date of this version of the product. This will be the release date of the
+ * latest maintenance release
+ * @return the release date, as an ISO 8601 string
+ */
+
+ public static String getReleaseDate() {
+ return RELEASE_DATE;
+ }
+
+ /**
+ * Get the issue date of the most recent major release of the product, that is, a release offering
+ * new functionality rather than just bug fixes (typically, a release in which the first two digits
+ * of the version number change, for example 9.2 to 9.3).
+ * @return the release date, as an ISO 8601 string
+ */
+
+ public static String getMajorReleaseDate() {
+ return MAJOR_RELEASE_DATE;
+ }
+
+
+ /**
+ * Get the version of the XSLT specification that this product supports
+ * @return the string 2.0
+ */
+
+ public static String getXSLVersionString() {
+ // TODO: not satisfactory if the user requested a 3.0 processor
+ return "2.0";
+ }
+
+ /**
+ * Get a message used to identify this product when a transformation is run using the -t option
+ * @return A string containing both the product name and the product
+ * version
+ */
+
+ public static String getProductTitle() {
+ return getProductName() + ' ' + getProductVersion() + " from Saxonica";
+ }
+
+ /**
+ * Return a web site address containing information about the product. Supports the XSLT system property xsl:vendor-url
+ * @return the string "http://saxon.sf.net/"
+ */
+
+ public static String getWebSiteAddress() {
+ return "http://www.saxonica.com/";
+ }
+
+ /**
+ * Invoking net.sf.saxon.Version from the command line outputs the build number
+ * @param args not used
+ */
+ public static void main(String[] args) {
+ System.err.println(getProductTitle() + " (build " + BUILD + ')');
+ }
+}
+
diff --git a/sf/saxon/dom/AttrOverNodeInfo.java b/sf/saxon/dom/AttrOverNodeInfo.java
new file mode 100644
index 0000000..1a4847b
--- /dev/null
+++ b/sf/saxon/dom/AttrOverNodeInfo.java
@@ -0,0 +1,148 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import org.w3c.dom.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is an implementation of the DOM Attr class that wraps a Saxon NodeInfo
+ * representation of an attribute or namespace node.
+ */
+
+public class AttrOverNodeInfo extends NodeOverNodeInfo implements Attr {
+
+ /**
+ * Get the name of an attribute node (the lexical QName) (DOM method)
+ */
+
+ public String getName() {
+ if (node.getNodeKind() == Type.NAMESPACE) {
+ String local = node.getLocalPart();
+ if (local.equals("")) {
+ return "xmlns";
+ } else {
+ return "xmlns:" + local;
+ }
+ }
+ return node.getDisplayName();
+ }
+
+ /**
+ * Return the character value of an attribute node (DOM method)
+ * @return the attribute value
+ */
+
+ public String getValue() {
+ return node.getStringValue();
+ }
+
+ /**
+ * Determine whether the node has any children.
+ * @return <code>true</code>: a DOM Attribute has a text node as a child.
+ */
+
+ public boolean hasChildNodes() {
+ return true;
+ }
+
+ /**
+ * Get first child
+ * @return the first child node of this node. In this model an attribute node always has a single text
+ * node as its child.
+ */
+
+ public Node getFirstChild() {
+ return new TextOverAttrInfo(this);
+ }
+
+ /**
+ * Get last child
+ *
+ * @return last child of this node, or null if it has no children
+ */
+
+ public Node getLastChild() {
+ return getFirstChild();
+ }
+
+ /**
+ * Return a <code>NodeList</code> that contains all children of this node. If
+ * there are no children, this is a <code>NodeList</code> containing no
+ * nodes.
+ */
+
+ public NodeList getChildNodes() {
+ List<Node> list = new ArrayList<Node>(1);
+ list.add(getFirstChild());
+ return new DOMNodeList(list);
+ }
+
+ /**
+ * If this attribute was explicitly given a value in the original
+ * document, this is <code>true</code> ; otherwise, it is
+ * <code>false</code>. (DOM method)
+ * @return Always true in this implementation.
+ */
+
+ public boolean getSpecified() {
+ return true;
+ }
+
+ /**
+ * Set the value of an attribute node. (DOM method).
+ * Always fails (because tree is readonly)
+ */
+
+ public void setValue(String value) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Determine whether this (attribute) node is an ID. This method is introduced
+ * in DOM Level 3.
+ */
+
+ public boolean isId() {
+ return node.isId();
+ }
+
+
+ /**
+ * The <code>Element</code> node this attribute is attached to or
+ * <code>null</code> if this attribute is not in use.
+ * @since DOM Level 2
+ */
+
+ public Element getOwnerElement() {
+ if (node.getNodeKind() == Type.ATTRIBUTE || node.getNodeKind() == Type.NAMESPACE) {
+ return (Element)wrap(node.getParent());
+ } else {
+ throw new UnsupportedOperationException(
+ "This method is defined only on attribute and namespace nodes");
+ }
+ }
+ /**
+ * Get the schema type information for this node. Returns null for an untyped node.
+ */
+
+ /*@Nullable*/ public TypeInfo getSchemaTypeInfo() {
+ SchemaType type = node.getSchemaType();
+ if (type == null || BuiltInAtomicType.UNTYPED_ATOMIC.equals(type)) {
+ return null;
+ }
+ return new TypeInfoImpl(node.getConfiguration(), type);
+ }
+
+}
+
diff --git a/sf/saxon/dom/DOMAttributeMap.java b/sf/saxon/dom/DOMAttributeMap.java
new file mode 100644
index 0000000..ba175f9
--- /dev/null
+++ b/sf/saxon/dom/DOMAttributeMap.java
@@ -0,0 +1,230 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.tree.NamespaceNode;
+import net.sf.saxon.tree.iter.AxisIterator;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+/**
+ * Implementation of DOM NamedNodeMap used to represent the attributes of an element, for use when
+ * Saxon element and attribute nodes are accessed using the DOM API.
+ *
+ * <p>Note that namespaces are treated as attributes.</p>
+ *
+ */
+
+class DOMAttributeMap implements NamedNodeMap {
+
+ private NodeInfo parent;
+ private int numberOfNamespaces = -1;
+
+ /**
+ * Construct an AttributeMap for a given element node
+ * @param parent the element node owning the attributes
+ */
+
+ public DOMAttributeMap(NodeInfo parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Get named attribute (DOM NamedNodeMap method)
+ */
+
+ public Node getNamedItem(String name) {
+ if (name.equals("xmlns")) {
+ NamespaceBinding[] nsarray = parent.getDeclaredNamespaces(null);
+ for (int i=0; i<nsarray.length; i++) {
+ if (nsarray[i] == null) {
+ return null;
+ } else if (((nsarray[i].getPrefix().length()==0))) {
+ NamespaceNode nn =
+ new NamespaceNode(parent, nsarray[i], i+1);
+ return NodeOverNodeInfo.wrap(nn);
+ }
+ }
+ return null;
+ } else if (name.startsWith("xmlns:")) {
+ String prefix = name.substring(6);
+ if (prefix.equals("xml")) {
+ NamespaceNode nn =
+ new NamespaceNode(parent, NamespaceBinding.XML, 0);
+ return NodeOverNodeInfo.wrap(nn);
+ }
+ NamespaceBinding[] buffer = new NamespaceBinding[8];
+ NamespaceBinding[] nsarray = parent.getDeclaredNamespaces(buffer);
+ for (int i=0; i<nsarray.length; i++) {
+ if (nsarray[i] == null) {
+ return null;
+ } else if (prefix.equals(nsarray[i].getPrefix())) {
+ NamespaceNode nn =
+ new NamespaceNode(parent, nsarray[i], i+1);
+ return NodeOverNodeInfo.wrap(nn);
+ }
+ }
+ return null;
+ } else {
+ AxisIterator atts = parent.iterateAxis(AxisInfo.ATTRIBUTE);
+ while (true) {
+ NodeInfo att = atts.next();
+ if (att == null) {
+ return null;
+ }
+ if (name.equals(att.getDisplayName())) {
+ return NodeOverNodeInfo.wrap(att);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get n'th attribute (DOM NamedNodeMap method).
+ * In this implementation we number the attributes as follows:
+ * 0 - the xmlns:xml namespace declaration
+ * 1-n further namespace declarations
+ * n+1... "real" attribute declarations
+ */
+
+ public Node item(int index) {
+ if (index<0) {
+ return null;
+ }
+ if (index == 0) {
+ NamespaceNode nn =
+ new NamespaceNode(parent, NamespaceBinding.XML, 0);
+ return NodeOverNodeInfo.wrap(nn);
+ }
+ int nscount = getNumberOfNamespaces();
+ if (index < nscount) {
+ NamespaceBinding[] buffer = new NamespaceBinding[8];
+ NamespaceBinding[] nsList = parent.getDeclaredNamespaces(buffer);
+ NamespaceBinding nscode = nsList[index-1];
+ NamespaceNode nn =
+ new NamespaceNode(parent, nscode, index);
+ return NodeOverNodeInfo.wrap(nn);
+ }
+ int pos = 0;
+ int attNr = (index - nscount);
+ AxisIterator atts = parent.iterateAxis(AxisInfo.ATTRIBUTE);
+ while (true) {
+ NodeInfo att = atts.next();
+ if (att == null) {
+ return null;
+ }
+ if (pos==attNr) {
+ return NodeOverNodeInfo.wrap(att);
+ }
+ pos++;
+ }
+ }
+
+ /**
+ * Get the number of declared namespaces
+ * @return the number of namespaces declared on the parent element
+ */
+
+ private int getNumberOfNamespaces() {
+ if (numberOfNamespaces == -1) {
+ NamespaceBinding[] buffer = new NamespaceBinding[8];
+ NamespaceBinding[] nsList = parent.getDeclaredNamespaces(buffer);
+ int count = nsList.length;
+ for (int i=0; i<count; i++) {
+ if (nsList[i] == null) {
+ count = i;
+ break;
+ }
+ }
+ numberOfNamespaces = count+1; //+1 for the XML namespace
+ }
+ return numberOfNamespaces;
+ }
+
+ /**
+ * Get number of attributes and namespaces (DOM NamedNodeMap method).
+ */
+
+ public int getLength() {
+ int length = 0;
+ AxisIterator atts = parent.iterateAxis(AxisInfo.ATTRIBUTE);
+ while (atts.next() != null) {
+ length++;
+ }
+ return getNumberOfNamespaces() + length;
+ }
+
+ /**
+ * Get named attribute (DOM NamedNodeMap method)
+ */
+
+ public Node getNamedItemNS(String uri, String localName) {
+ if (uri==null) {
+ uri = "";
+ }
+ if (NamespaceConstant.XMLNS.equals(uri)) {
+ return getNamedItem("xmlns:" + localName);
+ }
+ if (uri.equals("") && localName.equals("xmlns")) {
+ return getNamedItem("xmlns");
+ }
+ AxisIterator atts = parent.iterateAxis(AxisInfo.ATTRIBUTE);
+ while (true) {
+ NodeInfo att = atts.next();
+ if (att == null) {
+ return null;
+ }
+ if (uri.equals(att.getURI()) && localName.equals(att.getLocalPart())) {
+ return NodeOverNodeInfo.wrap(att);
+ }
+ }
+ }
+
+ /**
+ * Set named attribute (DOM NamedNodeMap method: always fails)
+ */
+
+ public Node setNamedItem(Node arg) throws DOMException {
+ NodeOverNodeInfo.disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Remove named attribute (DOM NamedNodeMap method: always fails)
+ */
+
+ public Node removeNamedItem(String name) throws DOMException {
+ NodeOverNodeInfo.disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Set named attribute (DOM NamedNodeMap method: always fails)
+ */
+
+ public Node setNamedItemNS(Node arg) throws DOMException {
+ NodeOverNodeInfo.disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Remove named attribute (DOM NamedNodeMap method: always fails)
+ */
+
+ /*@Nullable*/ public Node removeNamedItemNS(String uri, String localName) throws DOMException {
+ NodeOverNodeInfo.disallowUpdate();
+ return null;
+ }
+
+}
+
diff --git a/sf/saxon/dom/DOMEnvelope.java b/sf/saxon/dom/DOMEnvelope.java
new file mode 100644
index 0000000..9c435b6
--- /dev/null
+++ b/sf/saxon/dom/DOMEnvelope.java
@@ -0,0 +1,227 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.Sender;
+import net.sf.saxon.expr.JPConverter;
+import net.sf.saxon.expr.PJConverter;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.ExternalObjectModel;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.SequenceExtent;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.xpath.XPathConstants;
+import java.io.Serializable;
+
+/**
+ * DOMEnvelope is an object model representation in which DOM interfaces are wrapped around
+ * Saxon NodeInfo nodes: that is, it implements the DOM on top of a Saxon tree implementation
+ * such as the tiny tree or linked tree.
+ */
+
+public class DOMEnvelope implements ExternalObjectModel, Serializable {
+
+ private static DOMEnvelope THE_INSTANCE = new DOMEnvelope();
+
+ /**
+ * Get the singular instance (this class is stateless)
+ * @return the singular instance
+ */
+
+ public static DOMEnvelope getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * Public constructor. (Use the singular instance in preference to creating a new one).
+ */
+
+ public DOMEnvelope() {}
+
+ /**
+ * Get the URI of the external object model as used in the JAXP factory interfaces for obtaining
+ * an XPath implementation
+ */
+
+ public String getIdentifyingURI() {
+ return XPathConstants.DOM_OBJECT_MODEL;
+ }
+
+ public PJConverter getPJConverter(Class targetClass) {
+ if (NodeOverNodeInfo.class.isAssignableFrom(targetClass)) {
+ return new PJConverter() {
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ return DOMObjectModel.convertXPathValueToObject(value, targetClass);
+ }
+ };
+ } else if (NodeList.class.isAssignableFrom(targetClass)) {
+ return new PJConverter() {
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ return DOMObjectModel.convertXPathValueToObject(value, targetClass);
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+
+ public JPConverter getJPConverter(Class sourceClass, Configuration config) {
+ if (NodeOverNodeInfo.class.isAssignableFrom(sourceClass)) {
+ return new JPConverter() {
+ /*@Nullable*/ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return convertObjectToXPathValue(object);
+ }
+ public ItemType getItemType() {
+ return AnyNodeTest.getInstance();
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get a converter that converts a sequence of XPath nodes to this model's representation
+ * of a node list.
+ * @param node an example of the kind of node used in this model
+ * @return if the model does not recognize this node as one of its own, return null. Otherwise
+ * return a PJConverter that takes a list of XPath nodes (represented as NodeInfo objects) and
+ * returns a collection of nodes in this object model
+ */
+
+ public PJConverter getNodeListCreator(Object node) {
+ //return getPJConverter(NodeList.class);
+ return null;
+ }
+
+ /**
+ * Test whether this object model recognizes a given node as one of its own
+ * @param object the object to be tested
+ * @return true if the supplied object is a node in this object model
+ */
+
+ public boolean isRecognizedNode(Object object) {
+ return object instanceof NodeOverNodeInfo;
+ }
+
+ /**
+ * Test whether this object model recognizes a given class as representing a
+ * node in that object model. This method will generally be called at compile time.
+ *
+ * @param nodeClass A class that possibly represents nodes
+ * @return true if the class is used to represent nodes in this object model
+ */
+
+ public boolean isRecognizedNodeClass(Class nodeClass) {
+ return NodeOverNodeInfo.class.isAssignableFrom(nodeClass);
+ }
+
+ /**
+ * Test whether this object model recognizes a particular kind of JAXP Result object,
+ * and if it does, return a Receiver that builds an instance of this data model from
+ * a sequence of events. If the Result is not recognised, return null.
+ * <p>
+ * This implementation always returns null: it is not possible to construct an instance
+ * of this object model implementation directly as the result of a JAXP transformation.
+ */
+
+ public Receiver getDocumentBuilder(Result result) throws XPathException {
+ return null;
+ }
+
+ /**
+ * Test whether this object model recognizes a particular kind of JAXP Source object,
+ * and if it does, send the contents of the document to a supplied Receiver, and return true.
+ * Otherwise, return false.
+ * <p>
+ * This implementation returns true only if the source is a DOMSource whose contained node is a
+ * a "NodeOverNodeInfo".
+ */
+
+ public boolean sendSource(Source source, Receiver receiver) throws XPathException {
+ if (source instanceof DOMSource) {
+ Node startNode = ((DOMSource)source).getNode();
+ if (startNode instanceof NodeOverNodeInfo) {
+ NodeInfo base = ((NodeOverNodeInfo)startNode).getUnderlyingNodeInfo();
+ Sender.send(base, receiver, null);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Wrap or unwrap a node using this object model to return the corresponding Saxon node. If the supplied
+ * source does not belong to this object model, return null
+ */
+
+ public NodeInfo unravel(Source source, Configuration config) {
+
+ if (source instanceof DOMSource) {
+ Node dsnode = ((DOMSource)source).getNode();
+ if (dsnode instanceof NodeOverNodeInfo) {
+ // Supplied source is a DOM Node wrapping a Saxon node: unwrap it
+ return ((NodeOverNodeInfo)dsnode).getUnderlyingNodeInfo();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Convert a Java object to an XPath value. If the supplied object is recognized as a representation
+ * of a value using this object model, the object model should convert the value to an XPath value
+ * and return this as the result. If not, it should return null. If the object is recognized but cannot
+ * be converted, an exception should be thrown
+ * @param object the object to be converted
+ * @return the value after conversion
+ * @throws net.sf.saxon.trans.XPathException if conversion fails
+ */
+
+ private Sequence convertObjectToXPathValue(Object object) throws XPathException {
+ if (object instanceof NodeList) {
+ // NodeList needs great care, because Xerces element nodes implement the NodeList interface,
+ // with the actual list being the children of the node in question. So we only recognize a
+ // NodeList here if it is non-empty, and if all the nodes within it are NodeOverNodeInfo objects.
+ NodeList list = ((NodeList)object);
+ final int len = list.getLength();
+ if (len == 0) {
+ return null;
+ }
+ NodeInfo[] nodes = new NodeInfo[len];
+ for (int i=0; i<len; i++) {
+ if (list.item(i) instanceof NodeOverNodeInfo) {
+ nodes[i] = ((NodeOverNodeInfo)list.item(i)).getUnderlyingNodeInfo();
+ } else {
+ return null;
+ }
+ }
+ return new SequenceExtent<NodeInfo>(nodes);
+
+ // Note, we accept the nodes in the order returned by the function; there
+ // is no requirement that this should be document order.
+ } else if (object instanceof NodeOverNodeInfo) {
+ return ((NodeOverNodeInfo)object).getUnderlyingNodeInfo();
+ } else {
+ return null;
+ }
+ }
+
+}
+
+
diff --git a/sf/saxon/dom/DOMExceptionImpl.java b/sf/saxon/dom/DOMExceptionImpl.java
new file mode 100644
index 0000000..6c47a29
--- /dev/null
+++ b/sf/saxon/dom/DOMExceptionImpl.java
@@ -0,0 +1,64 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+import org.w3c.dom.DOMException;
+
+/**
+ * DOM operations only raise exceptions in "exceptional" circumstances,
+ * i.e., when an operation is impossible to perform (either for logical
+ * reasons, because data is lost, or because the implementation has become
+ * unstable). In general, DOM methods return specific error values in ordinary
+ * processing situations, such as out-of-bound errors when using
+ * <code>NodeList</code> .
+ * <p> Implementations may raise other exceptions under other circumstances.
+ * For example, implementations may raise an implementation-dependent
+ * exception if a <code>null</code> argument is passed.
+ * <p>See also the <a href='http://www.w3.org/TR/2000/CR-DOM-Level-2-20000510'>Document Object Model (DOM) Level 2 Specification</a>.
+ */
+public class DOMExceptionImpl extends DOMException {
+
+ public DOMExceptionImpl (short code, String message) {
+ super(code, message);
+ //this.code = code;
+ }
+ public short code;
+ // ExceptionCode
+// public static final short INDEX_SIZE_ERR = 1;
+// public static final short DOMSTRING_SIZE_ERR = 2;
+// public static final short HIERARCHY_REQUEST_ERR = 3;
+// public static final short WRONG_DOCUMENT_ERR = 4;
+// public static final short INVALID_CHARACTER_ERR = 5;
+// public static final short NO_DATA_ALLOWED_ERR = 6;
+// public static final short NO_MODIFICATION_ALLOWED_ERR = 7;
+// public static final short NOT_FOUND_ERR = 8;
+// public static final short NOT_SUPPORTED_ERR = 9;
+// public static final short INUSE_ATTRIBUTE_ERR = 10;
+ /**
+ * @since DOM Level 2
+ */
+ public static final short INVALID_STATE_ERR = 11;
+ /**
+ * @since DOM Level 2
+ */
+ public static final short SYNTAX_ERR = 12;
+ /**
+ * @since DOM Level 2
+ */
+ public static final short INVALID_MODIFICATION_ERR = 13;
+ /**
+ * @since DOM Level 2
+ */
+ public static final short NAMESPACE_ERR = 14;
+ /**
+ * @since DOM Level 2
+ */
+ public static final short INVALID_ACCESS_ERR = 15;
+
+}
+
+
diff --git a/sf/saxon/dom/DOMImplementationImpl.java b/sf/saxon/dom/DOMImplementationImpl.java
new file mode 100644
index 0000000..eaf9eb9
--- /dev/null
+++ b/sf/saxon/dom/DOMImplementationImpl.java
@@ -0,0 +1,101 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+
+/**
+ * A simple implementation of the DOMImplementation interface, for use when accessing
+ * Saxon tree structure using the DOM API.
+*/
+
+class DOMImplementationImpl implements DOMImplementation {
+
+ /**
+ * Test if the DOM implementation implements a specific feature.
+ * @param feature The name of the feature to test (case-insensitive).
+ * @param version This is the version number of the feature to test.
+ * @return <code>true</code> if the feature is implemented in the
+ * specified version, <code>false</code> otherwise. This implementation
+ * returns true if the feature is "XML" or "Core" and the version is null,
+ * "", "3.0", "2.0", or "1.0".
+ */
+
+public boolean hasFeature(String feature, String version) {
+ return (feature.equalsIgnoreCase("XML") || feature.equalsIgnoreCase("Core")) &&
+ (version == null || version.length() == 0 ||
+ version.equals("3.0") || version.equals("2.0") || version.equals("1.0"));
+}
+
+ /**
+ * This method returns a specialized object which implements the
+ * specialized APIs of the specified feature and version, as specified
+ * in .
+ * @param feature The name of the feature requested.
+ * @param version This is the version number of the feature to test.
+ * @return Always returns null in this implementation
+ * @since DOM Level 3
+ */
+
+public Object getFeature(String feature,
+ String version) {
+ return null;
+}
+
+
+/**
+ * Creates an empty <code>DocumentType</code> node.
+ * @param qualifiedName The qualified name of the document type to be
+ * created.
+ * @param publicId The external subset public identifier.
+ * @param systemId The external subset system identifier.
+ * @return A new <code>DocumentType</code> node with
+ * <code>Node.ownerDocument</code> set to <code>null</code> .
+ * @exception org.w3c.dom.DOMException
+ * INVALID_CHARACTER_ERR: Raised if the specified qualified name
+ * contains an illegal character.
+ * <br> NAMESPACE_ERR: Raised if the <code>qualifiedName</code> is
+ * malformed.
+ * @since DOM Level 2
+ */
+
+public DocumentType createDocumentType(String qualifiedName,
+ String publicId,
+ String systemId)
+ throws DOMException
+{
+ NodeOverNodeInfo.disallowUpdate();
+ return null;
+}
+
+/**
+ * Creates an XML <code>Document</code> object of the specified type with
+ * its document element.
+ * @param namespaceURI The namespace URI of the document element to
+ * create.
+ * @param qualifiedName The qualified name of the document element to be
+ * created.
+ * @param doctype The type of document to be created or <code>null</code>.
+ * @return A new <code>Document</code> object.
+ * @exception org.w3c.dom.DOMException
+ * @since DOM Level 2
+ */
+/*@Nullable*/ public Document createDocument(String namespaceURI,
+ String qualifiedName,
+ DocumentType doctype)
+ throws DOMException
+{
+ NodeOverNodeInfo.disallowUpdate();
+ return null;
+}
+
+}
+
diff --git a/sf/saxon/dom/DOMNodeList.java b/sf/saxon/dom/DOMNodeList.java
new file mode 100644
index 0000000..3de46d5
--- /dev/null
+++ b/sf/saxon/dom/DOMNodeList.java
@@ -0,0 +1,52 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import org.w3c.dom.Node;
+
+import java.util.List;
+
+/**
+* This class wraps a list of nodes as a DOM NodeList
+*/
+
+public final class DOMNodeList implements org.w3c.dom.NodeList {
+ private List<Node> sequence;
+
+ /**
+ * Construct an node list that wraps a supplied list of DOM Nodes.
+ * @param extent the list of nodes to be wrapped
+ */
+
+ public DOMNodeList(List<Node> extent) {
+ sequence = extent;
+ }
+
+ /**
+ * return the number of nodes in the list (DOM method)
+ */
+
+ public int getLength() {
+ return sequence.size();
+ }
+
+ /**
+ * Return the n'th item in the list (DOM method)
+ * @throws java.lang.ClassCastException if the item is not a DOM Node
+ */
+
+ /*@Nullable*/ public Node item(int index) {
+ if (index < 0 || index >= sequence.size()) {
+ return null;
+ } else {
+ return sequence.get(index);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/dom/DOMNodeWrapper.java b/sf/saxon/dom/DOMNodeWrapper.java
new file mode 100644
index 0000000..9d1feeb
--- /dev/null
+++ b/sf/saxon/dom/DOMNodeWrapper.java
@@ -0,0 +1,1247 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorImpl;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.tree.util.SteppingNavigator;
+import net.sf.saxon.tree.util.SteppingNode;
+import net.sf.saxon.tree.wrapper.AbstractNodeWrapper;
+import net.sf.saxon.tree.wrapper.SiblingCountingNode;
+import net.sf.saxon.type.Type;
+import org.w3c.dom.*;
+
+import java.util.ArrayList;
+
+
+/**
+ * A node in the XML parse tree representing an XML element, character content, or attribute.<P>
+ * This is the implementation of the NodeInfo interface used as a wrapper for DOM nodes.
+ *
+ * <p>Because the DOM is not thread-safe even when reading, and because Saxon-EE can spawn multiple
+ * threads that access the same input tree, all methods that invoke DOM methods are synchronized
+ * on the DocumentWrapper object. (This still relies on the user not allocating two DocumentWrappers
+ * around the same DOM).</p>
+ */
+
+ at SuppressWarnings({"SynchronizeOnNonFinalField"})
+public class DOMNodeWrapper extends AbstractNodeWrapper implements SiblingCountingNode, SteppingNode {
+
+ protected Node node;
+ private int namecode = -1;
+ protected short nodeKind;
+ private DOMNodeWrapper parent; // null means unknown
+ protected DocumentWrapper docWrapper; // effectively final
+ protected int index; // -1 means unknown
+ protected int span = 1; // the number of adjacent text nodes wrapped by this NodeWrapper.
+ // If span>1, node will always be the first of a sequence of adjacent text nodes
+ private NamespaceBinding[] localNamespaces = null;
+
+ /**
+ * This constructor is protected: nodes should be created using the makeWrapper
+ * factory method
+ *
+ * @param node The DOM node to be wrapped
+ * @param docWrapper The wrapper for the Document node at the root of the DOM tree. Never null
+ * except in the case where we are creating the DocumentWrapper itself (which is a subclass).
+ * @param parent The DOMNodeWrapper that wraps the parent of this node. May be null if unknown.
+ * @param index Position of this node among its siblings, 0-based. May be -1 if unknown.
+ */
+ protected DOMNodeWrapper(Node node, DocumentWrapper docWrapper, /*@Nullable*/ DOMNodeWrapper parent, int index) {
+ this.node = node;
+ this.parent = parent;
+ this.index = index;
+ this.docWrapper = docWrapper;
+ }
+
+ /**
+ * Factory method to wrap a DOM node with a wrapper that implements the Saxon
+ * NodeInfo interface.
+ *
+ * @param node The DOM node
+ * @param docWrapper The wrapper for the containing Document node
+ * @return The new wrapper for the supplied node
+ * @throws NullPointerException if the node or the document wrapper are null
+ */
+ protected DOMNodeWrapper makeWrapper(Node node, DocumentWrapper docWrapper) {
+ if (node == null) {
+ throw new NullPointerException("NodeWrapper#makeWrapper: Node must not be null");
+ }
+ if (docWrapper == null) {
+ throw new NullPointerException("NodeWrapper#makeWrapper: DocumentWrapper must not be null");
+ }
+ return makeWrapper(node, docWrapper, null, -1);
+ }
+
+ /**
+ * Factory method to wrap a DOM node with a wrapper that implements the Saxon
+ * NodeInfo interface.
+ *
+ * @param node The DOM node
+ * @param docWrapper The wrapper for the containing Document node *
+ * @param parent The wrapper for the parent of the JDOM node
+ * @param index The position of this node relative to its siblings
+ * @return The new wrapper for the supplied node
+ */
+
+ protected DOMNodeWrapper makeWrapper(Node node, DocumentWrapper docWrapper,
+ /*@Nullable*/ DOMNodeWrapper parent, int index) {
+ DOMNodeWrapper wrapper;
+ switch (node.getNodeType()) {
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ return docWrapper;
+ case Node.ELEMENT_NODE:
+ wrapper = new DOMNodeWrapper(node, docWrapper, parent, index);
+ wrapper.nodeKind = Type.ELEMENT;
+ break;
+ case Node.ATTRIBUTE_NODE:
+ wrapper = new DOMNodeWrapper(node, docWrapper, parent, index);
+ wrapper.nodeKind = Type.ATTRIBUTE;
+ break;
+ case Node.TEXT_NODE:
+ wrapper = new DOMNodeWrapper(node, docWrapper, parent, index);
+ wrapper.nodeKind = Type.TEXT;
+ break;
+ case Node.CDATA_SECTION_NODE:
+ wrapper = new DOMNodeWrapper(node, docWrapper, parent, index);
+ wrapper.nodeKind = Type.TEXT;
+ break;
+ case Node.COMMENT_NODE:
+ wrapper = new DOMNodeWrapper(node, docWrapper, parent, index);
+ wrapper.nodeKind = Type.COMMENT;
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ wrapper = new DOMNodeWrapper(node, docWrapper, parent, index);
+ wrapper.nodeKind = Type.PROCESSING_INSTRUCTION;
+ break;
+ case Node.ENTITY_REFERENCE_NODE:
+ throw new IllegalStateException("DOM contains entity reference nodes, which Saxon does not support. " +
+ "The DOM should be built using the expandEntityReferences() option");
+ default:
+ throw new IllegalArgumentException("Unsupported node type in DOM! " + node.getNodeType() + " instance " + node.toString());
+ }
+ return wrapper;
+ }
+
+ /**
+ * Get the underlying DOM node, to implement the VirtualNode interface
+ */
+
+ public Object getUnderlyingNode() {
+ return node;
+ }
+
+ /**
+ * Return the kind of node.
+ *
+ * @return one of the values Node.ELEMENT, Node.TEXT, Node.ATTRIBUTE, etc.
+ */
+
+ public int getNodeKind() {
+ return nodeKind;
+ }
+
+ /**
+ * Determine whether this is the same node as another node. <br />
+ * Note: a.isSameNodeInfo(b) if and only if generateId(a)==generateId(b)
+ *
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(NodeInfo other) {
+ if (!(other instanceof DOMNodeWrapper)) {
+ return false;
+ }
+ if (docWrapper.domLevel3) {
+ synchronized (docWrapper) {
+ return node.isSameNode(((DOMNodeWrapper) other).node);
+ }
+ } else {
+ DOMNodeWrapper ow = (DOMNodeWrapper) other;
+ return getNodeKind() == ow.getNodeKind() &&
+ getNameCode() == ow.getNameCode() && // redundant, but gives a quick exit
+ getSiblingPosition() == ow.getSiblingPosition() &&
+ getParent().isSameNodeInfo(ow.getParent());
+ }
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ *
+ * @param other The other node, whose position is to be compared with this node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public int compareOrder(NodeInfo other) {
+ // Use the DOM Level-3 compareDocumentPosition() method
+ if (other instanceof DOMNodeWrapper && docWrapper.domLevel3) {
+ if (isSameNodeInfo(other)) {
+ return 0;
+ }
+ try {
+ synchronized (docWrapper) {
+ short relationship = node.compareDocumentPosition(((DOMNodeWrapper) other).node);
+ if ((relationship &
+ (Node.DOCUMENT_POSITION_PRECEDING | Node.DOCUMENT_POSITION_CONTAINS)) != 0) {
+ return +1;
+ } else if ((relationship &
+ (Node.DOCUMENT_POSITION_FOLLOWING | Node.DOCUMENT_POSITION_CONTAINED_BY)) != 0) {
+ return -1;
+ }
+ }
+ // otherwise use fallback implementation (e.g. nodes in different documents)
+ } catch (DOMException e) {
+ // can happen if nodes are from different DOM implementations.
+ // use fallback implementation
+ }
+ }
+
+ if (other instanceof SiblingCountingNode) {
+ return Navigator.compareOrder(this, (SiblingCountingNode) other);
+ } else {
+ // it's presumably a Namespace Node
+ return -other.compareOrder(this);
+ }
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+// Use the DOM Level-3 compareDocumentPosition() method
+ if (other instanceof DOMNodeWrapper && docWrapper.domLevel3) {
+ if (isSameNodeInfo(other)) {
+ return AxisInfo.SELF;
+ }
+ try {
+ synchronized (docWrapper) {
+ short relationship = node.compareDocumentPosition(((DOMNodeWrapper) other).node);
+ if ((relationship & Node.DOCUMENT_POSITION_PRECEDING) != 0) {
+ return AxisInfo.FOLLOWING;
+ } else if ((relationship & Node.DOCUMENT_POSITION_FOLLOWING) != 0) {
+ return AxisInfo.PRECEDING;
+ } else if ((relationship & Node.DOCUMENT_POSITION_CONTAINS) != 0) {
+ return AxisInfo.ANCESTOR;
+ } else if ((relationship & Node.DOCUMENT_POSITION_CONTAINED_BY) != 0) {
+ return AxisInfo.DESCENDANT;
+ }
+ }
+ // otherwise use fallback implementation (e.g. nodes in different documents)
+ } catch (DOMException e) {
+ //
+ }
+ }
+ return Navigator.comparePosition(this, other);
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ synchronized (docWrapper) {
+ switch (nodeKind) {
+ case Type.DOCUMENT:
+ case Type.ELEMENT:
+ NodeList children1 = node.getChildNodes();
+ StringBuffer sb1 = new StringBuffer(16);
+ expandStringValue(children1, sb1);
+ return sb1;
+
+ case Type.ATTRIBUTE:
+ return emptyIfNull(((Attr) node).getValue());
+
+ case Type.TEXT:
+ if (span == 1) {
+ return emptyIfNull(node.getNodeValue());
+ } else {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ Node textNode = node;
+ for (int i = 0; i < span; i++) {
+ fsb.append(emptyIfNull(textNode.getNodeValue()));
+ textNode = textNode.getNextSibling();
+ }
+ return fsb.condense();
+ }
+
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ return emptyIfNull(node.getNodeValue());
+
+ default:
+ return "";
+ }
+ }
+ }
+
+ /**
+ * Treat a node value of null as an empty string.
+ *
+ * @param s the node value
+ * @return a zero-length string if s is null, otherwise s
+ */
+
+ private static String emptyIfNull(String s) {
+ return (s == null ? "" : s);
+ }
+
+ private static void expandStringValue(NodeList list, StringBuffer sb) {
+ final int len = list.getLength();
+ for (int i = 0; i < len; i++) {
+ Node child = list.item(i);
+ switch (child.getNodeType()) {
+ case Node.ELEMENT_NODE:
+ expandStringValue(child.getChildNodes(), sb);
+ break;
+ case Node.COMMENT_NODE:
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ break;
+ default:
+ sb.append(child.getNodeValue());
+ }
+ }
+ }
+
+ /**
+ * Get name code. The name code is a coded form of the node name: two nodes
+ * with the same name code have the same namespace URI, the same local name,
+ * and the same prefix. By masking the name code with &0xfffff, you get a
+ * fingerprint: two nodes with the same fingerprint have the same local name
+ * and namespace URI.
+ *
+ * @see NamePool#allocate allocate
+ */
+
+ public int getNameCode() {
+ if (namecode != -1) {
+ // this is a memo function
+ return namecode;
+ }
+ int nodeKind = getNodeKind();
+ if (nodeKind == Type.ELEMENT || nodeKind == Type.ATTRIBUTE) {
+ String prefix = getPrefix();
+ if (prefix == null) {
+ prefix = "";
+ }
+ namecode = docWrapper.getNamePool().allocate(prefix, getURI(), getLocalPart());
+ return namecode;
+ } else if (nodeKind == Type.PROCESSING_INSTRUCTION) {
+ namecode = docWrapper.getNamePool().allocate("", "", getLocalPart());
+ return namecode;
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Get the local part of the name of this node. This is the name after the ":" if any.
+ *
+ * @return the local part of the name. For an unnamed node, returns null, except for
+ * un unnamed namespace node, which returns "".
+ */
+
+ public String getLocalPart() {
+ synchronized (docWrapper) {
+ switch (getNodeKind()) {
+ case Type.ELEMENT:
+ case Type.ATTRIBUTE:
+ return getLocalName(node);
+ case Type.PROCESSING_INSTRUCTION:
+ return node.getNodeName();
+ default:
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Get the local name of a DOM element or attribute node.
+ * @param node the DOM element or attribute node
+ * @return the local name as defined in XDM
+ */
+
+ public static String getLocalName(Node node) {
+ String s = node.getLocalName();
+ if (s == null) {
+ // true if the node was created using a DOM level 1 method
+ String n = node.getNodeName();
+ int colon = n.indexOf(':');
+ if (colon >= 0) {
+ return n.substring(colon + 1);
+ }
+ return n;
+ } else {
+ return s;
+ }
+ }
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ *
+ * @return The URI of the namespace of this node. For an unnamed node,
+ * or for a node with an empty prefix, return an empty
+ * string.
+ */
+
+ public String getURI() {
+ synchronized (docWrapper) {
+ if (nodeKind == Type.ELEMENT) {
+ return getElementURI((Element) node);
+ } else if (nodeKind == Type.ATTRIBUTE) {
+ return getAttributeURI((Attr) node);
+ }
+ return "";
+ }
+ }
+
+ public static String getElementURI(Element element) {
+
+ // The DOM methods getPrefix() and getNamespaceURI() do not always
+ // return the prefix and the URI; they both return null, unless the
+ // prefix and URI have been explicitly set in the node by using DOM
+ // level 2 interfaces. There's no obvious way of deciding whether
+ // an element whose name has no prefix is in the default namespace,
+ // other than searching for a default namespace declaration. So we have to
+ // be prepared to search.
+
+ // If getPrefix() and getNamespaceURI() are non-null, however,
+ // we can use the values.
+
+ String uri = element.getNamespaceURI();
+ if (uri != null) {
+ return uri;
+ }
+
+ // Otherwise we have to work it out the hard way...
+
+ String displayName = element.getNodeName();
+ int colon = displayName.indexOf(':');
+ String attName = (colon < 0 ? "xmlns" : "xmlns:" + displayName.substring(0, colon));
+
+ if (attName.equals("xmlns:xml")) {
+ return NamespaceConstant.XML;
+ }
+
+ Node node = element;
+ do {
+ if (((Element)node).hasAttribute(attName)) {
+ return ((Element) node).getAttribute(attName);
+ }
+ node = node.getParentNode();
+ } while (node.getNodeType() == Node.ELEMENT_NODE);
+
+ return "";
+
+ }
+
+
+ public static String getAttributeURI(Attr attr) {
+
+ String uri = attr.getNamespaceURI();
+ if (uri != null) {
+ return uri;
+ }
+
+ // Otherwise we have to work it out the hard way...
+
+ String displayName = attr.getNodeName();
+ int colon = displayName.indexOf(':');
+ if (colon < 0) {
+ return "";
+ }
+ String attName = "xmlns:" + displayName.substring(0, colon);
+
+ if (attName.equals("xmlns:xml")) {
+ return NamespaceConstant.XML;
+ }
+
+ Node node = attr.getOwnerElement();
+ do {
+ String attVal = ((Element) node).getAttribute(attName);
+ if (attVal != null) {
+ return attVal;
+ }
+
+
+ node = node.getParentNode();
+ } while (node.getNodeType() == Node.ELEMENT_NODE);
+
+ return "";
+
+ }
+
+ /**
+ * Get the prefix of the name of the node. This is defined only for elements and attributes.
+ * If the node has no prefix, or for other kinds of node, return a zero-length string.
+ *
+ * @return The prefix of the name of the node.
+ */
+
+ public String getPrefix() {
+ synchronized (docWrapper) {
+ int kind = getNodeKind();
+ if (kind == Type.ELEMENT || kind == Type.ATTRIBUTE) {
+ String name = node.getNodeName();
+ int colon = name.indexOf(':');
+ if (colon < 0) {
+ return "";
+ } else {
+ return name.substring(0, colon);
+ }
+ }
+ return "";
+ }
+ }
+
+ /**
+ * Get the display name of this node. For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ *
+ * @return The display name of this node.
+ * For a node with no name, return an empty string.
+ */
+
+ public String getDisplayName() {
+ switch (nodeKind) {
+ case Type.ELEMENT:
+ case Type.ATTRIBUTE:
+ case Type.PROCESSING_INSTRUCTION:
+ synchronized (docWrapper) {
+ return node.getNodeName();
+ }
+ default:
+ return "";
+
+ }
+ }
+
+ /**
+ * Get the NodeInfo object representing the parent of this node
+ */
+
+ public DOMNodeWrapper getParent() {
+ if (parent == null) {
+ synchronized (docWrapper) {
+ switch (getNodeKind()) {
+ case Type.ATTRIBUTE:
+ parent = makeWrapper(((Attr) node).getOwnerElement(), docWrapper);
+ break;
+ default:
+ Node p = node.getParentNode();
+ if (p == null) {
+ return null;
+ } else {
+ parent = makeWrapper(p, docWrapper);
+ }
+ }
+ }
+ }
+ return parent;
+ }
+
+ /**
+ * Get the index position of this node among its siblings (starting from 0).
+ * In the case of a text node that maps to several adjacent siblings in the DOM,
+ * the numbering actually refers to the position of the underlying DOM nodes;
+ * thus the sibling position for the text node is that of the first DOM node
+ * to which it relates, and the numbering of subsequent XPath nodes is not necessarily
+ * consecutive.
+ *
+ * <p>Despite the name, this method also returns a meaningful result for attribute
+ * nodes; it returns the position of the attribute among the attributes of its
+ * parent element, when they are listed in document order.</p>
+ */
+
+ public int getSiblingPosition() {
+ if (index == -1) {
+ synchronized (docWrapper) {
+ switch (nodeKind) {
+ case Type.ELEMENT:
+ case Type.TEXT:
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ int ix = 0;
+ Node start = node;
+ while (true) {
+ start = start.getPreviousSibling();
+ if (start == null) {
+ index = ix;
+ return ix;
+ }
+ ix++;
+ }
+ case Type.ATTRIBUTE:
+ ix = 0;
+ int fp = getFingerprint();
+ AxisIterator iter = parent.iterateAxis(AxisInfo.ATTRIBUTE);
+ while (true) {
+ NodeInfo n = iter.next();
+ if (n == null || n.getFingerprint() == fp) {
+ index = ix;
+ return ix;
+ }
+ ix++;
+ }
+
+ case Type.NAMESPACE:
+ ix = 0;
+ fp = getFingerprint();
+ iter = parent.iterateAxis(AxisInfo.NAMESPACE);
+ while (true) {
+ NodeInfo n = iter.next();
+ if (n == null || n.getFingerprint() == fp) {
+ index = ix;
+ return ix;
+ }
+ ix++;
+ }
+ default:
+ index = 0;
+ return index;
+ }
+ }
+ }
+ return index;
+ }
+
+ @Override
+ protected AxisIterator<NodeInfo> iterateAttributes(NodeTest nodeTest) {
+ AxisIterator<NodeInfo> iter = new AttributeEnumeration(this);
+ if (nodeTest != AnyNodeTest.getInstance()) {
+ iter = new Navigator.AxisFilter(iter, nodeTest);
+ }
+ return iter;
+ }
+
+ @Override
+ protected AxisIterator<NodeInfo> iterateChildren(NodeTest nodeTest) {
+ boolean elementOnly = nodeTest.getNodeKindMask() == 1 << Type.ELEMENT;
+ AxisIterator<NodeInfo> iter = new Navigator.EmptyTextFilter(
+ new ChildEnumeration(this, true, true, elementOnly));
+ if (nodeTest != AnyNodeTest.getInstance()) {
+ iter = new Navigator.AxisFilter(iter, nodeTest);
+ }
+ return iter;
+ }
+
+ @Override
+ protected AxisIterator<NodeInfo> iterateSiblings(NodeTest nodeTest, boolean forwards) {
+ boolean elementOnly = nodeTest.getNodeKindMask() == 1 << Type.ELEMENT;
+ AxisIterator<NodeInfo> iter = new Navigator.EmptyTextFilter(
+ new ChildEnumeration(this, false, forwards, elementOnly));
+ if (nodeTest != AnyNodeTest.getInstance()) {
+ iter = new Navigator.AxisFilter(iter, nodeTest);
+ }
+ return iter;
+ }
+
+ @Override
+ protected AxisIterator<NodeInfo> iterateDescendants(NodeTest nodeTest, boolean includeSelf) {
+ return new SteppingNavigator.DescendantAxisIterator(this, includeSelf, nodeTest);
+ }
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) {
+ NameTest test = new NameTest(Type.ATTRIBUTE, uri, local, getNamePool());
+ AxisIterator iterator = iterateAxis(AxisInfo.ATTRIBUTE, test);
+ NodeInfo attribute = iterator.next();
+ if (attribute == null) {
+ return null;
+ } else {
+ return attribute.getStringValue();
+ }
+ }
+
+ /**
+ * Get the root node - always a document node with this tree implementation
+ *
+ * @return the NodeInfo representing the containing document
+ */
+
+ public NodeInfo getRoot() {
+ return docWrapper;
+ }
+
+ /**
+ * Get the root (document) node
+ *
+ * @return the DocumentInfo representing the containing document
+ */
+
+ public DocumentInfo getDocumentRoot() {
+ return docWrapper;
+ }
+
+ /**
+ * Determine whether the node has any children. <br />
+ * Note: the result is equivalent to <br />
+ * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
+ */
+
+ public boolean hasChildNodes() {
+ // An attribute node has child text nodes
+ synchronized (docWrapper) {
+ return node.getNodeType() != Node.ATTRIBUTE_NODE && node.hasChildNodes();
+ }
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node.
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ *
+ * @param buffer a buffer to contain a string that uniquely identifies this node, across all
+ * documents
+ */
+
+ public void generateId(FastStringBuffer buffer) {
+ Navigator.appendSequentialKey(this, buffer, true);
+ }
+
+ /**
+ * Get the document number of the document containing this node. For a free-standing
+ * orphan node, just return the hashcode.
+ */
+
+ public long getDocumentNumber() {
+ return getDocumentRoot().getDocumentNumber();
+ }
+
+ /**
+ * Copy this node to a given outputter (deep copy)
+ */
+
+ public void copy(Receiver out, int copyOptions, int locationId) throws XPathException {
+ Navigator.copy(this, out, copyOptions, locationId);
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ synchronized (docWrapper) {
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ if (localNamespaces != null) {
+ return localNamespaces;
+ }
+ Element elem = (Element) node;
+ NamedNodeMap atts = elem.getAttributes();
+
+ if (atts == null) {
+ localNamespaces = NamespaceBinding.EMPTY_ARRAY;
+ return NamespaceBinding.EMPTY_ARRAY;
+ }
+ int count = 0;
+ final int attsLen = atts.getLength();
+ for (int i = 0; i < attsLen; i++) {
+ Attr att = (Attr) atts.item(i);
+ String attName = att.getName();
+ if (attName.equals("xmlns")) {
+ count++;
+ } else if (attName.startsWith("xmlns:")) {
+ count++;
+ }
+ }
+ if (count == 0) {
+ localNamespaces = NamespaceBinding.EMPTY_ARRAY;
+ return NamespaceBinding.EMPTY_ARRAY;
+ } else {
+ NamespaceBinding[] result = (buffer == null || count > buffer.length ? new NamespaceBinding[count] : buffer);
+ int n = 0;
+ for (int i = 0; i < attsLen; i++) {
+ Attr att = (Attr) atts.item(i);
+ String attName = att.getName();
+ if (attName.equals("xmlns")) {
+ String prefix = "";
+ String uri = att.getValue();
+ result[n++] = new NamespaceBinding(prefix, uri);
+ } else if (attName.startsWith("xmlns:")) {
+ String prefix = attName.substring(6);
+ String uri = att.getValue();
+ result[n++] = new NamespaceBinding(prefix, uri);
+ }
+ }
+ if (count < result.length) {
+ result[count] = null;
+ }
+ localNamespaces = new NamespaceBinding[result.length];
+ System.arraycopy(result, 0, localNamespaces, 0, result.length);
+ return result;
+ }
+ } else {
+ return null;
+ }
+ }
+ }
+
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ synchronized (docWrapper) {
+ return (node instanceof Attr) && ((Attr) node).isId();
+ }
+ }
+
+ public DOMNodeWrapper getNextSibling() {
+ synchronized (docWrapper) {
+ Node currNode = node.getNextSibling();
+ if (currNode != null) {
+ if (currNode.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
+ currNode = currNode.getNextSibling();
+ }
+ return makeWrapper(currNode, docWrapper);
+ }
+ return null;
+ }
+ }
+
+
+ public DOMNodeWrapper getFirstChild() {
+ synchronized (docWrapper) {
+ Node currNode = node.getFirstChild();
+ if (currNode != null) {
+ if (currNode.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
+ currNode = currNode.getNextSibling();
+ }
+ return makeWrapper(currNode, docWrapper);
+ }
+ return null;
+ }
+ }
+
+ public DOMNodeWrapper getPreviousSibling() {
+ synchronized (docWrapper) {
+ Node currNode = node.getPreviousSibling();
+ if (currNode != null) {
+ if (currNode.getNodeType() == Node.DOCUMENT_TYPE_NODE) {
+ return null;
+ }
+ return makeWrapper(currNode, docWrapper);
+ }
+ return null;
+ }
+ }
+
+ public SteppingNode getSuccessorElement(SteppingNode anchor, String uri, String local) {
+ synchronized (docWrapper) {
+ Node stop = (anchor == null ? null : ((DOMNodeWrapper) anchor).node);
+ Node next = node;
+ do {
+ next = getSuccessorNode(next, stop);
+ } while (next != null &&
+ !(next.getNodeType() == Node.ELEMENT_NODE &&
+ (local == null || local.equals(getLocalName(next))) &&
+ (uri == null || uri.equals(getElementURI((Element) next)))));
+ if (next == null) {
+ return null;
+ } else {
+ return makeWrapper(next, docWrapper);
+ }
+ }
+ }
+
+ /**
+ * Get the following DOM node in an iteration of a subtree
+ *
+ * @param start the start DOM node
+ * @param anchor the DOM node marking the root of the subtree within which navigation takes place (may be null)
+ * @return the next DOM node in document order after the start node, excluding attributes and namespaces
+ */
+
+ private static Node getSuccessorNode(Node start, Node anchor) {
+ if (start.hasChildNodes()) {
+ return start.getFirstChild();
+ }
+ if ((anchor != null && start.isSameNode(anchor))) {
+ return null;
+ }
+ Node p = start;
+ while (true) {
+ Node s = p.getNextSibling();
+ if (s != null) {
+ return s;
+ }
+ p = p.getParentNode();
+ if (p == null || (anchor != null && p.isSameNode(anchor))) {
+ return null;
+ }
+ }
+ }
+
+ private final class AttributeEnumeration implements AxisIterator<NodeInfo>, LookaheadIterator<NodeInfo> {
+
+ private ArrayList<Node> attList = new ArrayList<Node>(10);
+ private int ix = 0;
+ private DOMNodeWrapper start;
+ private DOMNodeWrapper current;
+
+ public AttributeEnumeration(DOMNodeWrapper start) {
+ synchronized (start.docWrapper) {
+ this.start = start;
+ NamedNodeMap atts = start.node.getAttributes();
+ if (atts != null) {
+ final int attsLen = atts.getLength();
+ for (int i = 0; i < attsLen; i++) {
+ String name = atts.item(i).getNodeName();
+ if (!(name.startsWith("xmlns") &&
+ (name.length() == 5 || name.charAt(5) == ':'))) {
+ attList.add(atts.item(i));
+ }
+ }
+ }
+ ix = 0;
+ }
+ }
+
+ public boolean hasNext() {
+ return ix < attList.size();
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return (next() != null);
+ }
+
+ public NodeInfo next() {
+ if (ix >= attList.size()) {
+ return null;
+ }
+ current = start.makeWrapper(
+ attList.get(ix), docWrapper, start, ix);
+ ix++;
+ return current;
+ }
+
+ public NodeInfo current() {
+ return current;
+ }
+
+ public int position() {
+ return ix + 1;
+ }
+
+ public void close() {
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ return current.iterateAxis(axis, test);
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ return current.atomize();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ return current.getStringValueCS();
+ }
+
+
+ /*@NotNull*/
+ public AxisIterator<NodeInfo> getAnother() {
+ return new AttributeEnumeration(start);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+ }
+
+
+ /**
+ * The class ChildEnumeration handles not only the child axis, but also the
+ * following-sibling and preceding-sibling axes. It can also iterate the children
+ * of the start node in reverse order, something that is needed to support the
+ * preceding and preceding-or-ancestor axes (the latter being used by xsl:number)
+ */
+
+ private final class ChildEnumeration extends AxisIteratorImpl implements LookaheadIterator<NodeInfo> {
+
+ private DOMNodeWrapper start;
+ private DOMNodeWrapper commonParent;
+ private boolean downwards; // iterate children of start node (not siblings)
+ private boolean forwards; // iterate in document order (not reverse order)
+ private boolean elementsOnly;
+ NodeList childNodes;
+ private int childNodesLength;
+ private int ix; // index of the current DOM node within childNodes;
+ // in the case of adjacent text nodes, index of the first in the group
+ private int currentSpan; // number of DOM nodes mapping to the current XPath node
+
+ /**
+ * Create an iterator over the children or siblings of a given node
+ *
+ * @param start the start node for the iteration
+ * @param downwards if true, iterate over the children of the start node; if false, iterate
+ * over the following or preceding siblings
+ * @param forwards if true, iterate in forwards document order; if false, iterate in reverse
+ * document order
+ * @param elementsOnly if true, retrieve element nodes only; if false, retrieve all nodes
+ */
+ public ChildEnumeration(DOMNodeWrapper start,
+ boolean downwards, boolean forwards, boolean elementsOnly) {
+ synchronized (start.docWrapper) {
+ this.start = start;
+ this.downwards = downwards;
+ this.forwards = forwards;
+ this.elementsOnly = elementsOnly;
+ position = 0;
+ currentSpan = 1;
+
+ if (downwards) {
+ commonParent = start;
+ } else {
+ commonParent = start.getParent();
+ }
+
+ childNodes = commonParent.node.getChildNodes();
+ childNodesLength = childNodes.getLength();
+ if (downwards) {
+ currentSpan = 1;
+ if (forwards) {
+ ix = -1; // just before first
+ } else {
+ ix = childNodesLength; // just after last
+ }
+ } else {
+ ix = start.getSiblingPosition(); // at current node
+ currentSpan = start.span;
+ }
+ }
+ }
+
+ /**
+ * Starting with ix positioned at a node, which in the last in a span, calculate the length
+ * of the span, that is the number of DOM nodes mapped to this XPath node.
+ *
+ * @return the number of nodes spanned
+ */
+
+ private int skipPrecedingTextNodes() {
+ int count = 0;
+ while (ix >= count) {
+ Node node = childNodes.item(ix - count);
+ short kind = node.getNodeType();
+ if (kind == Node.TEXT_NODE || kind == Node.CDATA_SECTION_NODE) {
+ count++;
+ } else {
+ break;
+ }
+ }
+ return (count == 0 ? 1 : count);
+ }
+
+ /**
+ * Starting with ix positioned at a node, which in the first in a span, calculate the length
+ * of the span, that is the number of DOM nodes mapped to this XPath node.
+ *
+ * @return the number of nodes spanned
+ */
+
+ private int skipFollowingTextNodes() {
+ int count = 0;
+ int pos = ix;
+ final int len = childNodesLength;
+ while (pos < len) {
+ Node node = childNodes.item(pos);
+ short kind = node.getNodeType();
+ if (kind == Node.TEXT_NODE || kind == Node.CDATA_SECTION_NODE) {
+ pos++;
+ count++;
+ } else {
+ break;
+ }
+ }
+ return (count == 0 ? 1 : count);
+ }
+
+ public boolean hasNext() {
+ if (forwards) {
+ return ix + currentSpan < childNodesLength;
+ } else {
+ return ix > 0;
+ }
+ }
+
+ /*@Nullable*/
+ public NodeInfo next() {
+ while (true) {
+ if (forwards) {
+ ix += currentSpan;
+ if (ix >= childNodesLength) {
+ position = -1;
+ return null;
+ } else {
+ currentSpan = skipFollowingTextNodes();
+ Node currentDomNode = childNodes.item(ix);
+ switch (currentDomNode.getNodeType()) {
+ case Node.DOCUMENT_TYPE_NODE:
+ continue;
+ case Node.ELEMENT_NODE:
+ break;
+ default:
+ if (elementsOnly) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ DOMNodeWrapper wrapper = makeWrapper(currentDomNode, docWrapper, commonParent, ix);
+ wrapper.span = currentSpan;
+ position++;
+ return current = wrapper;
+ }
+ } else {
+ ix--;
+ if (ix < 0) {
+ position = -1;
+ return null;
+ } else {
+ currentSpan = skipPrecedingTextNodes();
+ ix -= (currentSpan - 1);
+ Node currentDomNode = childNodes.item(ix);
+ switch (currentDomNode.getNodeType()) {
+ case Node.DOCUMENT_TYPE_NODE:
+ continue;
+ case Node.ELEMENT_NODE:
+ break;
+ default:
+ if (elementsOnly) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ DOMNodeWrapper wrapper = makeWrapper(currentDomNode, docWrapper, commonParent, ix);
+ wrapper.span = currentSpan;
+ position++;
+ return current = wrapper;
+ }
+ }
+ }
+ }
+
+ /*@NotNull*/
+ public AxisIterator<NodeInfo> getAnother() {
+ return new ChildEnumeration(start, downwards, forwards, elementsOnly);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+
+ } // end of class ChildEnumeration
+
+
+}
+
diff --git a/sf/saxon/dom/DOMObjectModel.java b/sf/saxon/dom/DOMObjectModel.java
new file mode 100644
index 0000000..3490822
--- /dev/null
+++ b/sf/saxon/dom/DOMObjectModel.java
@@ -0,0 +1,461 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.Sender;
+import net.sf.saxon.expr.JPConverter;
+import net.sf.saxon.expr.PJConverter;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.ExternalObjectModel;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.wrapper.VirtualNode;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.SequenceExtent;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.xpath.XPathConstants;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * This interface must be implemented by any third-party object model that can
+ * be wrapped with a wrapper that implements the Saxon Object Model (the NodeInfo interface).
+ * This implementation of the interface supports wrapping of DOM Documents.
+ */
+
+public class DOMObjectModel extends TreeModel implements ExternalObjectModel, Serializable {
+
+ private static DOMObjectModel THE_INSTANCE = new DOMObjectModel();
+ private static DocumentBuilderFactory factory = null;
+
+ /**
+ * Get a reusable instance instance of this class.
+ * <p>Note, this is not actually a singleton instance; the class also has a public constructor,
+ * which is needed to support the automatic loading of object models into the Configuration.</p>
+ * @return the singleton instance
+ */
+
+ public static DOMObjectModel getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * Create an instance of the DOMObjectModel class.
+ * <p>When possible, use the getInstance() method in preference, as the instance is then reusable.</p>
+ */
+
+ public DOMObjectModel() {
+ }
+
+ /**
+ * Get the URI of the external object model as used in the JAXP factory interfaces for obtaining
+ * an XPath implementation
+ */
+
+ public String getIdentifyingURI() {
+ return XPathConstants.DOM_OBJECT_MODEL;
+ }
+
+ public String getName() {
+ return "DOM";
+ }
+
+ /**
+ * Get a converter from XPath values to values in the external object model
+ * @param targetClass the required class of the result of the conversion. If this class represents
+ * a node or list of nodes in the external object model, the method should return a converter that takes
+ * a native node or sequence of nodes as input and returns a node or sequence of nodes in the
+ * external object model representation. Otherwise, it should return null.
+ * @return a converter, if the targetClass is recognized as belonging to this object model;
+ * otherwise null
+ */
+
+ public PJConverter getPJConverter(Class targetClass) {
+ if (Node.class.isAssignableFrom(targetClass) && !(NodeOverNodeInfo.class.isAssignableFrom(targetClass))) {
+ return new PJConverter() {
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ return convertXPathValueToObject(value, targetClass);
+ }
+ };
+ } else if (NodeList.class == targetClass) {
+ return new PJConverter() {
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ return convertXPathValueToObject(value, targetClass);
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+
+ //public NodeSet ns;
+ public JPConverter getJPConverter(Class sourceClass, Configuration config) {
+ if (Node.class.isAssignableFrom(sourceClass) && !(NodeOverNodeInfo.class.isAssignableFrom(sourceClass))) {
+ return new JPConverter() {
+ public Sequence convert(Object obj, XPathContext context) throws XPathException {
+ return wrapOrUnwrapNode((Node)obj, context.getConfiguration());
+ }
+
+ public ItemType getItemType() {
+ return AnyNodeTest.getInstance();
+ }
+ };
+ } else if (NodeList.class.isAssignableFrom(sourceClass)) {
+ return new JPConverter() {
+ public Sequence convert(Object obj, XPathContext context) throws XPathException {
+ Configuration config = context.getConfiguration();
+ NodeList list = ((NodeList)obj);
+ final int len = list.getLength();
+ NodeInfo[] nodes = new NodeInfo[len];
+ for (int i = 0; i < len; i++) {
+ nodes[i] = wrapOrUnwrapNode(list.item(i), config);
+ }
+ return new SequenceExtent(nodes);
+ }
+
+ public ItemType getItemType() {
+ return AnyNodeTest.getInstance();
+ }
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ };
+ } else if (DOMSource.class == sourceClass) {
+ return new JPConverter() {
+ public Sequence convert(Object obj, XPathContext context) throws XPathException {
+ return unravel((DOMSource)obj, context.getConfiguration());
+ }
+
+ public ItemType getItemType() {
+ return AnyNodeTest.getInstance();
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get a converter that converts a sequence of XPath nodes to this model's representation
+ * of a node list.
+ * @param node an example of the kind of node used in this model
+ * @return if the model does not recognize this node as one of its own, return null. Otherwise
+ * return a PJConverter that takes a list of XPath nodes (represented as NodeInfo objects) and
+ * returns a collection of nodes in this object model
+ */
+
+ public PJConverter getNodeListCreator(Object node) {
+ if (node == null ||
+ node instanceof Node ||
+ node instanceof DOMSource ||
+ (node instanceof VirtualNode && ((VirtualNode)node).getRealNode() instanceof Node)) {
+ return new PJConverter() {
+ /*@Nullable*/ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ return convertXPathValueToObject(value, NodeList.class);
+ }
+ };
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Test whether this object model recognizes a particular kind of JAXP Result object,
+ * and if it does, return a Receiver that builds an instance of this data model from
+ * a sequence of events. If the Result is not recognised, return null.
+ */
+
+ public Receiver getDocumentBuilder(Result result) throws XPathException {
+ if (result instanceof DOMResult) {
+ DOMWriter emitter = new DOMWriter();
+ Node root = ((DOMResult)result).getNode();
+ if (root instanceof NodeOverNodeInfo && !(((NodeOverNodeInfo)root).getUnderlyingNodeInfo() instanceof MutableNodeInfo)) {
+ throw new XPathException("Supplied DOMResult is a non-mutable Saxon implementation");
+ }
+ // JDK 1.5 adds a nextSibling() property to identify the insertion point among the siblings
+ Node nextSibling = ((DOMResult)result).getNextSibling();
+ if (root == null) {
+ try {
+ if (factory == null) {
+ factory = DocumentBuilderFactory.newInstance();
+ }
+ DocumentBuilder docBuilder = factory.newDocumentBuilder();
+ Document out = docBuilder.newDocument();
+ ((DOMResult)result).setNode(out);
+ emitter.setNode(out);
+ } catch (ParserConfigurationException e) {
+ throw new XPathException(e);
+ }
+ } else {
+ emitter.setNode(root);
+ emitter.setNextSibling(nextSibling);
+ }
+ return emitter;
+ }
+ return null;
+ }
+
+ @Override
+ public Builder makeBuilder(PipelineConfiguration pipe) {
+ DOMWriter dw = new DOMWriter();
+ dw.setPipelineConfiguration(pipe);
+ return dw;
+ }
+
+ /**
+ * Test whether this object model recognizes a particular kind of JAXP Source object,
+ * and if it does, send the contents of the document to a supplied Receiver, and return true.
+ * Otherwise, return false
+ */
+
+ public boolean sendSource(Source source, Receiver receiver) throws XPathException {
+ if (source instanceof DOMSource) {
+ Node startNode = ((DOMSource)source).getNode();
+ DOMSender driver = new DOMSender(startNode, receiver);
+ driver.setSystemId(source.getSystemId());
+ driver.send();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Wrap a DOM node using this object model to return the corresponding Saxon node.
+ * @param node the DOM node to be wrapped
+ * @param config the Saxon Configuration
+ * @return the wrapped DOM node
+ * @since 9.2
+ */
+
+ public NodeInfo wrap(Node node, Configuration config) {
+ // Supplied source is an ordinary DOM Node: wrap it
+ Document dom;
+ if (node.getNodeType() == Node.DOCUMENT_NODE) {
+ dom = (Document)node;
+ } else {
+ dom = node.getOwnerDocument();
+ }
+ DocumentWrapper docWrapper = new DocumentWrapper(dom, node.getBaseURI(), config);
+ return docWrapper.wrap(node);
+ }
+
+ /**
+ * Copy a DOM node to create a node in a different tree model
+ * @param node the DOM node to be copied
+ * @param model the target tree model
+ * @param config the Saxon Configuration
+ * @return the copied node
+ * @throws net.sf.saxon.trans.XPathException if the operation fails
+ * @since 9.2
+ */
+
+ public NodeInfo copy(Node node, TreeModel model, Configuration config) throws XPathException {
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ Builder builder = model.makeBuilder(pipe);
+ Sender.send(new DOMSource(node), builder, null);
+ return builder.getCurrentRoot();
+ }
+
+ /**
+ * Wrap or unwrap a node using this object model to return the corresponding Saxon node. If the supplied
+ * source does not belong to this object model, return null
+ */
+
+ public NodeInfo unravel(Source source, Configuration config) {
+
+ if (source instanceof DOMSource) {
+ Node dsnode = ((DOMSource)source).getNode();
+ if (!(dsnode instanceof NodeOverNodeInfo)) {
+ // Supplied source is an ordinary DOM Node: wrap it
+ Document dom;
+ if (dsnode.getNodeType() == Node.DOCUMENT_NODE) {
+ dom = (Document)dsnode;
+ } else {
+ dom = dsnode.getOwnerDocument();
+ }
+ DocumentWrapper docWrapper = new DocumentWrapper(dom, source.getSystemId(), config);
+ return docWrapper.wrap(dsnode);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Wrap a DOM Node as a NodeInfo, unless it already wraps a NodeInfo, inwhich case unwrap it
+ * @param node the node to be wrapped
+ * @param config the Saxon Configuration. This must be the same Configuration, or one that is compatible with,
+ * the Configuration used to compile and execute the query or stylesheet.
+ * @return the new wrapper node
+ * @throws net.sf.saxon.trans.XPathException if the operation fails
+ */
+
+ private NodeInfo wrapOrUnwrapNode(Node node, Configuration config) throws XPathException {
+ if (node instanceof NodeOverNodeInfo) {
+ return ((NodeOverNodeInfo)node).getUnderlyingNodeInfo();
+ } else {
+ DocumentInfo doc = wrapDocument(node, "", config);
+ return wrapNode(doc, node);
+ }
+ }
+
+ /**
+ * Convert an XPath value to an object in this object model. If the supplied value can be converted
+ * to an object in this model, of the specified class, then the conversion should be done and the
+ * resulting object returned. If the value cannot be converted, the method should return null. Note
+ * that the supplied class might be a List, in which case the method should inspect the contents of the
+ * Value to see whether they belong to this object model.
+ * @param value the XPath value to be converted
+ * @param target the class of object required
+ * @throws XPathException if the target class is explicitly associated with this object model, but the
+ * supplied value cannot be converted to the appropriate class
+ * @return the result of the conversion
+ */
+
+ public static <T extends Item> Object convertXPathValueToObject(Sequence value, Class target) throws XPathException {
+ // We accept the object if (a) the target class is Node, Node[], or NodeList,
+ // or (b) the supplied object is a node, or sequence of nodes, that wrap DOM nodes,
+ // provided that the target class is Object or a collection class
+ boolean requireDOM =
+ (Node.class.isAssignableFrom(target) || (target == NodeList.class) ||
+ (target.isArray() && Node.class.isAssignableFrom(target.getComponentType())));
+
+ // Note: we allow the declared type of the method argument to be a subclass of Node. If the actual
+ // node supplied is the wrong kind of node, this will result in a Java exception.
+
+ boolean allowDOM =
+ (target == Object.class || target.isAssignableFrom(ArrayList.class) ||
+ target.isAssignableFrom(HashSet.class) ||
+ (target.isArray() && target.getComponentType() == Object.class));
+ if (!(requireDOM || allowDOM)) {
+ return null;
+ }
+ List<Node> nodes = new ArrayList<Node>(20);
+
+ SequenceIterator<T> iter = (SequenceIterator<T>)value.iterate();
+ while (true) {
+ T item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (item instanceof VirtualNode) {
+ Object o = ((VirtualNode)item).getRealNode();
+ if (o instanceof Node) {
+ nodes.add((Node)o);
+ } else {
+ if (requireDOM) {
+ throw new XPathException(
+ "Cannot convert XPath value to Java object: required class is " + target.getName() +
+ "; supplied value has type " + Type.displayTypeName(item));
+ }
+ }
+ } else if (requireDOM) {
+ if (item instanceof NodeInfo) {
+ nodes.add(NodeOverNodeInfo.wrap((NodeInfo)item));
+ } else {
+ throw new XPathException(
+ "Cannot convert XPath value to Java object: required class is " + target.getName() +
+ "; supplied value has type " + Type.displayTypeName(item));
+ }
+ } else {
+ return null; // DOM Nodes are not actually required; let someone else try the conversion
+ }
+ }
+
+ if (nodes.isEmpty() && !requireDOM) {
+ return null; // empty sequence supplied - try a different mapping
+ }
+ if (Node.class.isAssignableFrom(target)) {
+ if (nodes.size() != 1) {
+ throw new XPathException("Cannot convert XPath value to Java object: requires a single DOM Node" +
+ "but supplied value contains " + nodes.size() + " nodes");
+ }
+ return nodes.get(0);
+ // could fail if the node is of the wrong kind
+ } else if (target == NodeList.class) {
+ return new DOMNodeList(nodes);
+ } else if (target.isArray() && target.getComponentType() == Node.class) {
+ return nodes.toArray(new Node[nodes.size()]);
+ } else if (target.isAssignableFrom(ArrayList.class)) {
+ return nodes;
+ } else if (target.isAssignableFrom(HashSet.class)) {
+ return new HashSet<Node>(nodes);
+ } else {
+ // after all this work, give up
+ return null;
+ }
+ }
+
+ /**
+ * Wrap a document node in the external object model in a document wrapper that implements
+ * the Saxon DocumentInfo interface. (However, if the supplied object is a wrapper for a Saxon
+ * NodeInfo object, then we <i>unwrap</i> it.
+ * @param node a node (any node) in the third party document
+ * @param baseURI the base URI of the node (supply "" if unknown)
+ * @param config the Saxon configuration (which among other things provides access to the NamePool)
+ * @return the wrapper, which must implement DocumentInfo
+ */
+
+ private DocumentInfo wrapDocument(Object node, String baseURI, Configuration config) {
+ if (node instanceof DocumentOverNodeInfo) {
+ return (DocumentInfo)((DocumentOverNodeInfo)node).getUnderlyingNodeInfo();
+ }
+ if (node instanceof NodeOverNodeInfo) {
+ return ((NodeOverNodeInfo)node).getUnderlyingNodeInfo().getDocumentRoot();
+ }
+ if (node instanceof org.w3c.dom.Node) {
+ if (((Node)node).getNodeType() == Node.DOCUMENT_NODE) {
+ Document doc = (org.w3c.dom.Document)node;
+ return new DocumentWrapper(doc, baseURI, config);
+ } else if (((Node)node).getNodeType() == Node.DOCUMENT_FRAGMENT_NODE) {
+ DocumentFragment doc = (org.w3c.dom.DocumentFragment)node;
+ return new DocumentWrapper(doc, baseURI, config);
+ } else {
+ Document doc = ((org.w3c.dom.Node)node).getOwnerDocument();
+ return new DocumentWrapper(doc, baseURI, config);
+ }
+ }
+ throw new IllegalArgumentException("Unknown node class " + node.getClass());
+ }
+
+ /**
+ * Wrap a node within the external object model in a node wrapper that implements the Saxon
+ * VirtualNode interface (which is an extension of NodeInfo)
+ * @param document the document wrapper, as a DocumentInfo object
+ * @param node the node to be wrapped. This must be a node within the document wrapped by the
+ * DocumentInfo provided in the first argument
+ * @return the wrapper for the node, as an instance of VirtualNode
+ */
+
+ private NodeInfo wrapNode(DocumentInfo document, Object node) {
+ return ((DocumentWrapper)document).wrap((Node)node);
+ }
+
+
+}
+
diff --git a/sf/saxon/dom/DOMSender.java b/sf/saxon/dom/DOMSender.java
new file mode 100644
index 0000000..692c167
--- /dev/null
+++ b/sf/saxon/dom/DOMSender.java
@@ -0,0 +1,359 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.SaxonLocator;
+import net.sf.saxon.event.SourceLocationProvider;
+import net.sf.saxon.om.FingerprintedQName;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.Untyped;
+import org.w3c.dom.*;
+import org.xml.sax.helpers.NamespaceSupport;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+* DOMSender.java: pseudo-SAX driver for a DOM source document.
+* This class takes an existing
+* DOM Document and walks around it in a depth-first traversal,
+* calling a Receiver to process the nodes as it does so
+*/
+
+public class DOMSender implements SaxonLocator, SourceLocationProvider {
+ /*@NotNull*/ private Receiver receiver;
+ /*@NotNull*/ protected Node root;
+ private NamespaceSupport nsSupport = new NamespaceSupport();
+ private String[] parts = new String[3];
+ private String[] elparts = new String[3];
+ private HashMap<String, String> nsDeclarations = new HashMap<String, String>(10);
+ protected String systemId;
+
+ /**
+ * Create a DOMSender that will send events representing the nodes in a tree
+ * to a nominated receiver
+ * @param startNode the root node of the tree to be send. Usually a document or element node.
+ * @param receiver the object to be notified of the resulting events. The supplied Receiver must
+ * be initialized with a PipelineConfiguration.The PipelineConfiguration
+ * of the Receiver will be modified to set this DOMSender as its LocationProvider.
+ */
+
+ public DOMSender(Node startNode, Receiver receiver) {
+ if (startNode == null) {
+ throw new NullPointerException("startNode");
+ }
+ if (receiver == null) {
+ throw new NullPointerException("receiver");
+ }
+ this.root = startNode;
+ this.receiver = receiver;
+ }
+
+ /**
+ * Set the systemId of the source document (which will also be
+ * used for the destination)
+ * @param systemId the systemId of the source document
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Walk a tree (traversing the nodes depth first).
+ * @throws IllegalStateException if the
+ * start node is of a node kind other than document, document fragment, element, text,
+ * comment, or processing instruction (for example, if it is an attribute node).
+ * @throws net.sf.saxon.trans.XPathException On any error in the document
+ */
+
+ public void send() throws XPathException {
+ receiver.setSystemId(systemId);
+ receiver.getPipelineConfiguration().setLocationProvider(this);
+
+ receiver.open();
+ switch (root.getNodeType()) {
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ receiver.startDocument(0);
+ walkNode(root);
+ receiver.endDocument();
+ break;
+ case Node.ELEMENT_NODE:
+ sendElement((Element)root);
+ break;
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ receiver.characters(((CharacterData)root).getData(), 0, 0);
+ break;
+ case Node.COMMENT_NODE:
+ receiver.comment(((Comment)root).getData(), 0, 0);
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ receiver.processingInstruction(
+ ((ProcessingInstruction)root).getTarget(),
+ ((ProcessingInstruction)root).getData(), 0, 0);
+ break;
+ default:
+ throw new IllegalStateException("DOMSender: unsupported kind of start node (" + root.getNodeType() + ")");
+ }
+ receiver.close();
+ }
+
+ /**
+ * Walk a tree starting from a particular element node. This has to make
+ * sure that all the namespace declarations in scope for the element are
+ * treated as if they were namespace declarations on the element itself.
+ * @param startNode the start element node from which the walk will start
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ private void sendElement(Element startNode) throws XPathException {
+ Element node = startNode;
+ boolean hasNamespaceDeclarations = gatherNamespaces(node, false);
+ while (true) {
+ gatherNamespaces(node, true);
+ Node parent = node.getParentNode();
+ if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) {
+ node = (Element)parent;
+ } else {
+ break;
+ }
+ }
+ outputElement(startNode, hasNamespaceDeclarations);
+ }
+
+ /**
+ * Walk an element of a document (traversing the children depth first)
+ * @param node The DOM Element object to walk
+ * @exception net.sf.saxon.trans.XPathException On any error in the document
+ *
+ */
+
+ private void walkNode (Node node) throws XPathException {
+ if (node.hasChildNodes()) {
+ NodeList nit = node.getChildNodes();
+ final int len = nit.getLength();
+ for (int i=0; i<len; i++) {
+ Node child = nit.item(i);
+ switch (child.getNodeType()) {
+ case Node.DOCUMENT_NODE:
+ case Node.DOCUMENT_FRAGMENT_NODE:
+ break; // should not happen
+ case Node.ELEMENT_NODE:
+ Element element = (Element)child;
+ boolean hasNamespaces = gatherNamespaces(element, false);
+ outputElement(element, hasNamespaces);
+ nsSupport.popContext();
+ break;
+ case Node.ATTRIBUTE_NODE: // have already dealt with attributes
+ break;
+ case Node.PROCESSING_INSTRUCTION_NODE:
+ receiver.processingInstruction(
+ ((ProcessingInstruction)child).getTarget(),
+ ((ProcessingInstruction)child).getData(),
+ 0, 0);
+ break;
+ case Node.COMMENT_NODE: {
+ String text = ((Comment)child).getData();
+ if (text!=null) {
+ receiver.comment(text, 0, 0);
+ }
+ break;
+ }
+ case Node.TEXT_NODE:
+ case Node.CDATA_SECTION_NODE: {
+ String text = ((CharacterData)child).getData();
+ if (text!=null) {
+ receiver.characters(text, 0, 0);
+ }
+ break;
+ }
+ case Node.ENTITY_REFERENCE_NODE:
+ walkNode(child);
+ break;
+ default:
+ break; // should not happen
+ }
+ }
+ }
+
+ }
+
+ private void outputElement(Element element, boolean hasNamespaceDeclarations) throws XPathException {
+ String[] elparts2 = nsSupport.processName(element.getTagName(), elparts, false);
+ if (elparts2==null) {
+ throw new XPathException("Undeclared namespace in " + element.getTagName());
+ }
+ String uri = elparts2[0];
+ String local = elparts2[1];
+ String prefix = NameChecker.getPrefix(elparts2[2]);
+
+ receiver.startElement(new FingerprintedQName(prefix, uri, local), Untyped.getInstance(), 0, 0);
+ for (Map.Entry<String, String> decl : nsDeclarations.entrySet()) {
+ receiver.namespace(new NamespaceBinding(decl.getKey(), decl.getValue()), 0);
+ }
+
+ NamedNodeMap atts = element.getAttributes();
+ if (atts != null) {
+ final int len = atts.getLength();
+ for (int a2=0; a2<len; a2++) {
+ Attr att = (Attr)atts.item(a2);
+ String attname = att.getName();
+ if (hasNamespaceDeclarations && (attname.equals("xmlns") || attname.startsWith("xmlns:"))) {
+ // do nothing: namespace declarations have already been processed
+ } else {
+ //System.err.println("Processing attribute " + attname);
+ String[] parts2 = nsSupport.processName(attname, parts, true);
+ if (parts2==null) {
+ throw new XPathException("Undeclared namespace in " + attname);
+ }
+ String atturi = parts2[0];
+ String attlocal = parts2[1];
+ String attprefix = NameChecker.getPrefix(parts2[2]);
+
+ // Note, DOM gives no guarantee that the prefix and URI are actually consistent. For example,
+ // it's possible programmatically to construct attribute nodes that have a namespace URI but
+ // no prefix. We don't attempt to deal with such situations: garbage in, garbage out.
+
+ NodeName attCode = new FingerprintedQName(attprefix, atturi, attlocal);
+
+ receiver.attribute(attCode, BuiltInAtomicType.UNTYPED_ATOMIC, att.getValue(), 0, 0);
+ }
+ }
+ }
+ receiver.startContent();
+
+ walkNode(element);
+
+ receiver.endElement();
+ }
+
+ /**
+ * Collect all the namespace attributes in scope for a given element. The namespace
+ * declaration attributes are added to the nsDeclarations map (which records namespaces
+ * declared for this element only), and are stacked on the stack maintated by the nsSupport
+ * object.
+ * @param element The element whose namespace declarations are required
+ * @param cumulative If true, the namespace declarations on this element are added to the
+ * current context, without creating a new context. If false, a new namespace context is
+ * created.
+ * @return true if any of the attributes on this element are namespace declarations (this information
+ * is used to avoid repeated checking while processing the attributes as attributes).
+ */
+
+ private boolean gatherNamespaces(Element element, boolean cumulative) {
+
+ boolean hasNamespaceDeclarations = false;
+ if (!cumulative) {
+ nsSupport.pushContext();
+ nsDeclarations.clear();
+ }
+
+ // we can't rely on namespace declaration attributes being present -
+ // there may be undeclared namespace prefixes. So we
+ // declare all namespaces encountered, to be on the safe side.
+
+ try {
+ String prefix = element.getPrefix();
+ String uri = element.getNamespaceURI();
+ if (prefix==null) prefix="";
+ if (uri==null) uri="";
+ //System.err.println("Implicit Namespace: " + prefix + "=" + uri);
+ if (nsDeclarations.get(prefix)==null) {
+ nsSupport.declarePrefix(prefix, uri);
+ nsDeclarations.put(prefix, uri);
+ }
+ } catch (Throwable err) {
+ // it must be a level 1 DOM
+ }
+
+ NamedNodeMap atts = element.getAttributes();
+
+ // Apparently the Oracle DOM returns null if there are no attributes:
+ if (atts == null) {
+ return false;
+ }
+ int alen = atts.getLength();
+ for (int a1=0; a1<alen; a1++) {
+ Attr att = (Attr)atts.item(a1);
+ String attname = att.getName();
+ boolean possibleNamespace = attname.startsWith("xmlns");
+ if (possibleNamespace && attname.equals("xmlns")) {
+ //System.err.println("Default namespace: " + att.getValue());
+ hasNamespaceDeclarations = true;
+ String uri = att.getValue();
+ if (nsDeclarations.get("")==null || (!cumulative && !nsDeclarations.get("").equals(uri))) {
+ nsSupport.declarePrefix("", uri);
+ nsDeclarations.put("", uri);
+ }
+ } else if (possibleNamespace && attname.startsWith("xmlns:")) {
+ //System.err.println("Namespace: " + attname.substring(6) + "=" + att.getValue());
+ hasNamespaceDeclarations = true;
+ String prefix = attname.substring(6);
+ if (nsDeclarations.get(prefix)==null) {
+ String uri = att.getValue();
+ nsSupport.declarePrefix(prefix, uri);
+ nsDeclarations.put(prefix, uri);
+ }
+ } else if (attname.indexOf(':')>=0) {
+ try {
+ String prefix = att.getPrefix();
+ String uri = att.getNamespaceURI();
+ //System.err.println("Implicit Namespace: " + prefix + "=" + uri);
+ if (nsDeclarations.get(prefix)==null) {
+ nsSupport.declarePrefix(prefix, uri);
+ nsDeclarations.put(prefix, uri);
+ }
+ } catch (Throwable err) {
+ // it must be a level 1 DOM
+ }
+ }
+ }
+ return hasNamespaceDeclarations;
+ }
+
+ // Implement the SAX Locator interface. This is needed to pass the base URI of nodes
+ // to the receiver. We don't attempt to preserve the original base URI of each individual
+ // node as it is copied, only the base URI of the document as a whole.
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+ public String getPublicId() {
+ return null;
+ }
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+}
+
diff --git a/sf/saxon/dom/DOMTransform.java b/sf/saxon/dom/DOMTransform.java
new file mode 100644
index 0000000..6d8da51
--- /dev/null
+++ b/sf/saxon/dom/DOMTransform.java
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.Transform;
+import net.sf.saxon.trans.XPathException;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * Variant of command line net.sf.saxon.Transform do build the source document
+ * in DOM and then proceed with the transformation. This class is provided largely for
+ * testing purposes.
+ */
+
+public class DOMTransform extends Transform {
+
+ public List<Source> preprocess(List<Source> sources) throws XPathException {
+ try {
+ ArrayList<Source> domSources = new ArrayList(sources.size());
+ for (Object source : sources) {
+ StreamSource src = (StreamSource) source;
+ InputSource ins = new InputSource(src.getSystemId());
+
+ // The following statement, if uncommented, forces use of the Xerces DOM.
+ // This system property can also be set from the command line using the -D option
+
+ System.setProperty("javax.xml.parser.DocumentBuilderFactory",
+ "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setNamespaceAware(true);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document doc = builder.parse(ins);
+ DocumentWrapper dom = new DocumentWrapper(doc, src.getSystemId(), getConfiguration());
+ domSources.add(dom);
+ }
+ return domSources;
+ } catch (ParserConfigurationException e) {
+ throw new XPathException(e);
+ } catch (SAXException e) {
+ throw new XPathException(e);
+ } catch (IOException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public static void main(String[] args) {
+ new DOMTransform().doTransform(args, "DOMTransform");
+ }
+}
+
diff --git a/sf/saxon/dom/DOMWriter.java b/sf/saxon/dom/DOMWriter.java
new file mode 100644
index 0000000..c9fea2e
--- /dev/null
+++ b/sf/saxon/dom/DOMWriter.java
@@ -0,0 +1,317 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.value.Whitespace;
+import org.w3c.dom.*;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+
+/**
+ * DOMWriter is a Receiver that attaches the result tree to a specified Node in the DOM Document
+ */
+
+public class DOMWriter extends Builder {
+
+ private PipelineConfiguration pipe;
+ private Node currentNode;
+ /*@Nullable*/ private Document document;
+ private Node nextSibling;
+ private int level = 0;
+ private boolean canNormalize = true;
+ private String systemId;
+
+ /**
+ * Set the pipelineConfiguration
+ */
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipe) {
+ this.pipe = pipe;
+ config = pipe.getConfiguration();
+ }
+
+ /**
+ * Get the pipeline configuration used for this document
+ */
+
+ /*@NotNull*/
+ public PipelineConfiguration getPipelineConfiguration() {
+ return pipe;
+ }
+
+ /**
+ * Set the System ID of the destination tree
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Notify an unparsed entity URI.
+ *
+ * @param name The name of the unparsed entity
+ * @param systemID The system identifier of the unparsed entity
+ * @param publicID The public identifier of the unparsed entity
+ */
+
+ public void setUnparsedEntity(String name, String systemID, String publicID) throws XPathException {
+ // no-op
+ }
+
+ /**
+ * Get the system identifier that was set with setSystemId.
+ *
+ * @return The system identifier that was set with setSystemId,
+ * or null if setSystemId was not called.
+ */
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Start of the document.
+ */
+
+ public void open () {}
+
+ /**
+ * End of the document.
+ */
+
+ public void close () {}
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ if (document == null) {
+ try {
+ document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+ currentNode = document;
+ } catch (ParserConfigurationException err) {
+ throw new XPathException(err);
+ }
+ }
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {}
+
+ /**
+ * Start of an element.
+ */
+
+ public void startElement (NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ String qname = nameCode.getDisplayName();
+ String uri = nameCode.getURI();
+ try {
+ Element element = document.createElementNS(("".equals(uri) ? null : uri), qname);
+ if (nextSibling != null && level == 0) {
+ currentNode.insertBefore(element, nextSibling);
+ } else {
+ currentNode.appendChild(element);
+ }
+ currentNode = element;
+ } catch (DOMException err) {
+ throw new XPathException(err);
+ }
+ level++;
+ }
+
+ public void namespace (NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ try {
+ String prefix = namespaceBinding.getPrefix();
+ String uri = namespaceBinding.getURI();
+ Element element = (Element)currentNode;
+ if (!(uri.equals(NamespaceConstant.XML))) {
+ if (prefix.length() == 0) {
+ element.setAttributeNS(NamespaceConstant.XMLNS, "xmlns", uri);
+ } else {
+ element.setAttributeNS(NamespaceConstant.XMLNS, "xmlns:" + prefix, uri);
+
+ }
+ }
+ } catch (DOMException err) {
+ throw new XPathException(err);
+ }
+ }
+
+ public void attribute (NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ String qname = nameCode.getDisplayName();
+ String uri = nameCode.getURI();
+ try {
+ Element element = (Element)currentNode;
+ element.setAttributeNS(("".equals(uri) ? null : uri), qname, value.toString());
+ // The following code assumes JDK 1.5 or JAXP 1.3
+ if (nameCode.equals(StandardNames.XML_ID_NAME) ||
+ (properties & ReceiverOptions.IS_ID) != 0 ||
+ nameCode.isInNamespace(NamespaceConstant.XML) && nameCode.getLocalPart().equals("id")) {
+ String localName = nameCode.getLocalPart();
+ element.setIdAttributeNS(("".equals(uri) ? null : uri), localName, true);
+ }
+ } catch (DOMException err) {
+ throw new XPathException(err);
+ }
+ }
+
+ public void startContent() throws XPathException {}
+
+ /**
+ * End of an element.
+ */
+
+ public void endElement () throws XPathException {
+ if (canNormalize) {
+ try {
+ currentNode.normalize();
+ } catch (Throwable err) {
+ canNormalize = false;
+ } // in case it's a Level 1 DOM
+ }
+
+ currentNode = currentNode.getParentNode();
+ level--;
+ }
+
+
+ /**
+ * Character data.
+ */
+
+ public void characters (CharSequence chars, int locationId, int properties) throws XPathException
+ {
+ if (level == 0 && nextSibling == null && Whitespace.isWhite(chars)) {
+ return; // no action for top-level whitespace
+ }
+ try {
+ Text text = document.createTextNode(chars.toString());
+ if (nextSibling != null && level == 0) {
+ currentNode.insertBefore(text, nextSibling);
+ } else {
+ currentNode.appendChild(text);
+ }
+ } catch (DOMException err) {
+ throw new XPathException(err);
+ }
+ }
+
+
+ /**
+ * Handle a processing instruction.
+ */
+
+ public void processingInstruction (String target, CharSequence data, int locationId, int properties)
+ throws XPathException
+ {
+ try {
+ ProcessingInstruction pi =
+ document.createProcessingInstruction(target, data.toString());
+ if (nextSibling != null && level == 0) {
+ currentNode.insertBefore(pi, nextSibling);
+ } else {
+ currentNode.appendChild(pi);
+ }
+ } catch (DOMException err) {
+ throw new XPathException(err);
+ }
+ }
+
+ /**
+ * Handle a comment.
+ */
+
+ public void comment (CharSequence chars, int locationId, int properties) throws XPathException
+ {
+ try {
+ Comment comment = document.createComment(chars.toString());
+ if (nextSibling != null && level == 0) {
+ currentNode.insertBefore(comment, nextSibling);
+ } else {
+ currentNode.appendChild(comment);
+ }
+ } catch (DOMException err) {
+ throw new XPathException(err);
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return false;
+ }
+
+ /**
+ * Set the attachment point for the new subtree
+ * @param node the node to which the new subtree will be attached
+ */
+
+ public void setNode (Node node) {
+ if (node == null) {
+ return;
+ }
+ currentNode = node;
+ if (node.getNodeType() == Node.DOCUMENT_NODE) {
+ document = (Document)node;
+ } else {
+ document = currentNode.getOwnerDocument();
+ if (document == null) {
+ // which might be because currentNode() is a parentless ElementOverNodeInfo.
+ // we create a DocumentOverNodeInfo, which is immutable, and will cause the DOMWriter to fail
+ document = new DocumentOverNodeInfo();
+ }
+ }
+ }
+
+ /**
+ * Set next sibling
+ * @param nextSibling the node, which must be a child of the attachment point, before which the new subtree
+ * will be created. If this is null the new subtree will be added after any existing children of the
+ * attachment point.
+ */
+
+ public void setNextSibling(Node nextSibling) {
+ this.nextSibling = nextSibling;
+ }
+
+ /**
+ * Get the current root node. This will normally be a document node, but if the root of the tree
+ * is an element node, it can be an element.
+ * @return the root of the tree that is currently being built, or that has been most recently built
+ * using this builder
+ */
+
+ @Override
+ public NodeInfo getCurrentRoot() {
+ return new DocumentWrapper(document, systemId, config);
+ }
+}
+
diff --git a/sf/saxon/dom/DocumentBuilderImpl.java b/sf/saxon/dom/DocumentBuilderImpl.java
new file mode 100644
index 0000000..8811a02
--- /dev/null
+++ b/sf/saxon/dom/DocumentBuilderImpl.java
@@ -0,0 +1,303 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.Sender;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.TinyBuilder;
+import net.sf.saxon.tree.tiny.TinyDocumentImpl;
+import org.w3c.dom.DOMImplementation;
+import org.w3c.dom.Document;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.transform.sax.SAXSource;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * This class implements the JAXP DocumentBuilder interface, allowing a Saxon TinyTree to be
+ * constructed using standard JAXP parsing interfaces. The returned DOM document node is a wrapper
+ * over the Saxon TinyTree structure. Note that although this wrapper
+ * implements the DOM interfaces, it is read-only, and all attempts to update it will throw
+ * an exception. No schema or DTD validation is carried out on the document.
+ */
+
+public class DocumentBuilderImpl extends DocumentBuilder {
+
+ private Configuration config;
+ private ParseOptions parseOptions = new ParseOptions();
+
+ /**
+ * Set the Saxon Configuration to be used by the document builder.
+ * This non-JAXP method must be called if the resulting document is to be used
+ * within a Saxon query or transformation. If no Configuration is supplied,
+ * Saxon creates a Configuration on the first call to the {@link #parse} method,
+ * and subsequent calls reuse the same Configuration.
+ *
+ * <p>As an alternative to calling this method, a Configuration can be supplied by calling
+ * <code>setAttribute(FeatureKeys.CONFIGURATION, config)</code> on the <code>DocumentBuilderFactory</code>
+ * object, where <code>config</code> can be obtained by calling
+ * <code>getAttribute(FeatureKeys.CONFIGURATION)</code> on the <code>TransformerFactory</code>.</p>
+ *
+ * @param config the Saxon configuration
+ * @since Saxon 8.8
+ */
+
+ public void setConfiguration(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Get the Saxon Configuration to be used by the document builder. This is
+ * a non-JAXP method.
+ * @return the Configuration previously supplied to {@link #setConfiguration},
+ * or the Configuration created automatically by Saxon on the first call to the
+ * {@link #parse} method, or a newly constructed Configuration if no Configuration has been supplied and
+ * the {@link #parse} method has not been called.
+ *
+ * @since Saxon 8.8
+ */
+
+ public Configuration getConfiguration() {
+ if (config == null) {
+ config = new Configuration();
+ }
+ return config;
+ }
+
+ /**
+ * Indicates whether or not this document builder is configured to
+ * understand namespaces.
+ *
+ * @return true if this document builder is configured to understand
+ * namespaces. This implementation always returns true.
+ */
+
+ public boolean isNamespaceAware() {
+ return true;
+ }
+
+ /**
+ * Determine whether the document builder should perform DTD validation
+ * @param state set to true to request DTD validation
+ */
+
+ public void setValidating(boolean state) {
+ parseOptions.setDTDValidationMode(state ? Validation.STRICT : Validation.SKIP);
+ }
+
+ /**
+ * Indicates whether or not this document builder is configured to
+ * validate XML documents against a DTD.
+ *
+ * @return true if this parser is configured to validate
+ * XML documents against a DTD; false otherwise.
+ */
+
+ public boolean isValidating() {
+ return parseOptions.getDTDValidationMode() == Validation.STRICT;
+ }
+
+ /**
+ * Create a new Document Node.
+ * @throws UnsupportedOperationException (always). The only way to build a document using this DocumentBuilder
+ * implementation is by using the parse() method.
+ */
+
+ public Document newDocument() {
+ throw new UnsupportedOperationException("The only way to build a document using this DocumentBuilder is with the parse() method");
+ }
+
+ /**
+ * Parse the content of the given input source as an XML document
+ * and return a new DOM {@link Document} object.
+ *
+ * <p>Note: for this document to be usable as part of a Saxon query or transformation,
+ * the document should be built within the {@link Configuration} in which that query
+ * or transformation is running. This can be achieved using the non-JAXP
+ * {@link #setConfiguration} method.
+ *
+ * @param in InputSource containing the content to be parsed. Note that if
+ * an EntityResolver or ErrorHandler has been supplied, then the XMLReader contained
+ * in this InputSource will be modified to register this EntityResolver or ErrorHandler,
+ * replacing any that was previously registered.
+ *
+ * @exception SAXException If any parse errors occur.
+ * @return A new DOM Document object.
+ *
+ * @deprecated since 9.3. The DOM created by this class is a DOM wrapper around
+ * (typically) a Saxon Tiny Tree. There is little point in constructing such
+ * an object. If the tree is primarily for use by Saxon XSLT and XQuery, it is
+ * better to construct a Tiny Tree directly and omit the DOM wrapper; if it is
+ * primarily for other DOM applications, it is better to construct a fully-
+ * functional DOM using (for example) Apache Xerces.
+ */
+
+ public Document parse(InputSource in) throws SAXException {
+ try {
+ if (config == null) {
+ config = new Configuration();
+ }
+ Builder builder = new TinyBuilder(config.makePipelineConfiguration());
+ SAXSource source = new SAXSource(in);
+ source.setSystemId(in.getSystemId());
+ Sender.send(source, builder, parseOptions);
+ TinyDocumentImpl doc = (TinyDocumentImpl)builder.getCurrentRoot();
+ builder.reset();
+ return (Document)DocumentOverNodeInfo.wrap(doc);
+ } catch (XPathException err) {
+ throw new SAXException(err);
+ }
+ }
+
+ /**
+ * Parse the content of the given file as an XML document
+ * and return a new DOM {@link Document} object.
+ * An <code>IllegalArgumentException</code> is thrown if the
+ * <code>File</code> is <code>null</code> null.
+ *
+ * <p><i>This implementation differs from the parent implementation
+ * by using a correct algorithm for filename-to-uri conversion.<i></p>
+ *
+ * @param f The file containing the XML to parse.
+ * @exception java.io.IOException If any IO errors occur.
+ * @exception SAXException If any parse errors occur.
+ * @return A new DOM Document object.
+ */
+
+ public Document parse(/*@Nullable*/ File f) throws SAXException, IOException {
+ if (f == null) {
+ throw new IllegalArgumentException("File cannot be null");
+ }
+
+ String uri = f.toURI().toString();
+ InputSource in = new InputSource(uri);
+ return parse(in);
+ }
+
+
+ /**
+ * Specify the {@link EntityResolver} to be used to resolve
+ * entities present in the XML document to be parsed.
+ *
+ * @param er The <code>EntityResolver</code> to be used to resolve entities
+ * present in the XML document to be parsed.
+ */
+
+ public void setEntityResolver(EntityResolver er) {
+ parseOptions.setEntityResolver(er);
+ }
+
+ /**
+ * Specify the {@link ErrorHandler} to be used by the parser.
+ * @param eh The <code>ErrorHandler</code> to be used by the parser.
+ */
+
+
+ public void setErrorHandler(ErrorHandler eh) {
+ parseOptions.setErrorHandler(eh);
+ }
+
+ /**
+ * Obtain an instance of a {@link DOMImplementation} object.
+ *
+ * @return A new instance of a <code>DOMImplementation</code>.
+ */
+
+ public DOMImplementation getDOMImplementation() {
+ return newDocument().getImplementation();
+ }
+
+ /**
+ * <p>Set state of XInclude processing.</p>
+ * <p/>
+ * <p>If XInclude markup is found in the document instance, should it be
+ * processed as specified in <a href="http://www.w3.org/TR/xinclude/">
+ * XML Inclusions (XInclude) Version 1.0</a>.</p>
+ * <p/>
+ * <p>XInclude processing defaults to <code>false</code>.</p>
+ *
+ * @param state Set XInclude processing to <code>true</code> or
+ * <code>false</code>
+ */
+ public void setXIncludeAware(boolean state) {
+ parseOptions.setXIncludeAware(state);
+ }
+
+
+ /**
+ * <p>Get the XInclude processing mode for this parser.</p>
+ *
+ * @return the return value of
+ * the {@link javax.xml.parsers.DocumentBuilderFactory#isXIncludeAware()}
+ * when this parser was created from factory.
+ * @throws UnsupportedOperationException For backward compatibility, when implementations for
+ * earlier versions of JAXP is used, this exception will be
+ * thrown.
+ * @see javax.xml.parsers.DocumentBuilderFactory#setXIncludeAware(boolean)
+ * @since JAXP 1.5, Saxon 8.9
+ */
+ public boolean isXIncludeAware() {
+ return parseOptions.isXIncludeAware();
+ }
+
+ /**
+ * Set the space-stripping action to be applied to the source document
+ * @param stripAction one of {@link net.sf.saxon.value.Whitespace#IGNORABLE},
+ * {@link net.sf.saxon.value.Whitespace#ALL}, or {@link net.sf.saxon.value.Whitespace#NONE}
+ * @since 8.9
+ */
+
+ public void setStripSpace(int stripAction) {
+ parseOptions.setStripSpace(stripAction);
+ }
+
+ /**
+ * Get the space-stripping action to be applied to the source document
+ * @return one of {@link net.sf.saxon.value.Whitespace#IGNORABLE},
+ * {@link net.sf.saxon.value.Whitespace#ALL}, or {@link net.sf.saxon.value.Whitespace#NONE}
+ * @since 8.9
+ */
+
+ public int getStripSpace() {
+ return parseOptions.getStripSpace();
+ }
+
+ /**
+ * Set the XML parsing options to be used
+ * @param options the XML parsing options. Options set using this method will override any options previously set
+ * using other methods; options subsequently set using other methods will modify the parseOptions
+ * object supplied using this method
+ * @since 9.3
+ */
+
+ public void setParseOptions(ParseOptions options) {
+ this.parseOptions = options;
+ }
+
+ /**
+ * Get the XML parsing options that have been set using setParseOptions and other setter methods
+ * @return the XML parsing options to be used
+ * @since 9.3
+ */
+
+ public ParseOptions getParseOptions() {
+ return parseOptions;
+ }
+
+}
+
+
diff --git a/sf/saxon/dom/DocumentOverNodeInfo.java b/sf/saxon/dom/DocumentOverNodeInfo.java
new file mode 100644
index 0000000..7fafdfd
--- /dev/null
+++ b/sf/saxon/dom/DocumentOverNodeInfo.java
@@ -0,0 +1,644 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.Type;
+import org.w3c.dom.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is an implementation of the DOM Document class that wraps a Saxon DocumentInfo
+ * representation of a document node.
+ */
+
+public class DocumentOverNodeInfo extends NodeOverNodeInfo implements Document {
+
+ /**
+ * Get the Document Type Declaration (see <code>DocumentType</code> )
+ * associated with this document. For HTML documents as well as XML
+ * documents without a document type declaration this returns
+ * <code>null</code>. DOM method.
+ * @return null: The Saxon tree model does not include the document type
+ * information.
+ */
+
+ public DocumentType getDoctype() {
+ return null;
+ }
+
+ /**
+ * Get a <code>DOMImplementation</code> object that handles this document.
+ * A DOM application may use objects from multiple implementations.
+ * DOM method.
+ */
+
+ public DOMImplementation getImplementation() {
+ return new DOMImplementationImpl();
+ }
+
+ /**
+ * Creates an element of the type specified. DOM method: always fails,
+ * because the Saxon tree is not updateable.
+ */
+
+ public Element createElement(String tagName) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Creates an empty <code>DocumentFragment</code> object.
+ * @return A new <code>DocumentFragment</code> .
+ * DOM method: returns null, because the Saxon tree is not updateable.
+ */
+
+ public DocumentFragment createDocumentFragment() {
+ return null;
+ }
+
+ /**
+ * Create a <code>Text</code> node given the specified string.
+ * DOM method: returns null, because the Saxon tree is not updateable.
+ * @param data The data for the node.
+ * @return The new <code>Text</code> object.
+ */
+
+ public Text createTextNode(String data) {
+ return null;
+ }
+
+ /**
+ * Create a <code>Comment</code> node given the specified string.
+ * DOM method: returns null, because the Saxon tree is not updateable.
+ * @param data The data for the node.
+ * @return The new <code>Comment</code> object.
+ */
+ public Comment createComment(String data) {
+ return null;
+ }
+
+ /**
+ * Create a <code>CDATASection</code> node whose value is the specified
+ * string.
+ * DOM method: always fails, because the Saxon tree is not updateable.
+ * @param data The data for the <code>CDATASection</code> contents.
+ * @return The new <code>CDATASection</code> object.
+ * @exception org.w3c.dom.DOMException
+ * NOT_SUPPORTED_ERR: Raised if this document is an HTML document.
+ */
+
+ public CDATASection createCDATASection(String data) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Create a <code>ProcessingInstruction</code> node given the specified
+ * name and data strings.
+ * DOM method: returns null, because the Saxon tree is not updateable.
+ * @param target The target part of the processing instruction.
+ * @param data The data for the node.
+ * @return The new <code>ProcessingInstruction</code> object.
+ * @exception org.w3c.dom.DOMException
+ * INVALID_CHARACTER_ERR: Raised if the specified target contains an
+ * illegal character.
+ * <br> NOT_SUPPORTED_ERR: Raised if this document is an HTML document.
+ */
+
+ public ProcessingInstruction createProcessingInstruction(String target,
+ String data)
+ throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Create an <code>Attr</code> of the given name.
+ * DOM method: always fails, because the Saxon tree is not updateable.
+ * @param name The name of the attribute.
+ * @return A new <code>Attr</code> object with the <code>nodeName</code>
+ * attribute set to <code>name</code> , and <code>localName</code> ,
+ * <code>prefix</code> , and <code>namespaceURI</code> set to
+ * <code>null</code> .
+ * @exception org.w3c.dom.DOMException
+ * INVALID_CHARACTER_ERR: Raised if the specified name contains an
+ * illegal character.
+ */
+
+ public Attr createAttribute(String name) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Create an <code>EntityReference</code> object.
+ * DOM method: returns null, because the Saxon tree is not updateable.
+ * @param name The name of the entity to reference.
+ * @return The new <code>EntityReference</code> object.
+ * @exception org.w3c.dom.DOMException
+ * INVALID_CHARACTER_ERR: Raised if the specified name contains an
+ * illegal character.
+ * <br> NOT_SUPPORTED_ERR: Raised if this document is an HTML document.
+ */
+
+ public EntityReference createEntityReference(String name) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Return a <code>NodeList</code> of all the <code>Elements</code> with
+ * a given tag name in the order in which they are encountered in a
+ * preorder traversal of the <code>Document</code> tree.
+ * @param tagname The name of the tag to match on. The special value "*"
+ * matches all tags.
+ * @return A new <code>NodeList</code> object containing all the matched
+ * <code>Elements</code> .
+ */
+
+ public NodeList getElementsByTagName(String tagname) {
+ return getElementsByTagName(node, tagname);
+ }
+
+ /**
+ * Get the outermost element of a document.
+ * @return the Element for the outermost element of the document. If the document is
+ * not well-formed, this returns the first element child of the root if there is one, otherwise
+ * null.
+ */
+
+ public Element getDocumentElement() {
+ NodeInfo root = node.getDocumentRoot();
+ if (root==null) {
+ return null;
+ }
+ AxisIterator children =
+ root.iterateAxis(AxisInfo.CHILD, NodeKindTest.ELEMENT);
+ return (Element)wrap(children.next());
+ }
+
+ protected static NodeList getElementsByTagName(NodeInfo node, String tagname) {
+ AxisIterator allElements = node.iterateAxis(AxisInfo.DESCENDANT);
+ List<Node> nodes = new ArrayList<Node>(100);
+ while(true) {
+ NodeInfo next = allElements.next();
+ if (next == null) {
+ break;
+ }
+ if (next.getNodeKind()==Type.ELEMENT) {
+ if (tagname.equals("*") || tagname.equals(next.getDisplayName())) {
+ nodes.add(NodeOverNodeInfo.wrap(next));
+ }
+ }
+ }
+ return new DOMNodeList(nodes);
+ }
+
+
+ /**
+ * Import a node from another document to this document.
+ * DOM method: always fails, because the Saxon tree is not updateable.
+ * @exception org.w3c.dom.DOMException
+ * @since DOM Level 2
+ */
+
+ public Node importNode(Node importedNode, boolean deep) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Create an element of the given qualified name and namespace URI.
+ * HTML-only DOM implementations do not need to implement this method.
+ * DOM method: always fails, because the Saxon tree is not updateable.
+ * @param namespaceURI The namespace URI of the element to create.
+ * @param qualifiedName The qualified name of the element type to
+ * instantiate.
+ * @return A new <code>Element</code> object
+ * @exception org.w3c.dom.DOMException
+ */
+
+ public Element createElementNS(String namespaceURI,
+ String qualifiedName)
+ throws DOMException
+ {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Create an attribute of the given qualified name and namespace URI.
+ * HTML-only DOM implementations do not need to implement this method.
+ * DOM method: returns null, because the Saxon tree is not updateable.
+ * @param namespaceURI The namespace URI of the attribute to create.
+ * @param qualifiedName The qualified name of the attribute to
+ * instantiate.
+ * @return A new <code>Attr</code> object.
+ * @exception org.w3c.dom.DOMException
+ */
+
+ public Attr createAttributeNS(String namespaceURI,
+ String qualifiedName)
+ throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Return a <code>NodeList</code> of all the <code>Elements</code> with
+ * a given local name and namespace URI in the order in which they are
+ * encountered in a preorder traversal of the <code>Document</code> tree.
+ * DOM method.
+ * @param namespaceURI The namespace URI of the elements to match on.
+ * The special value "*" matches all namespaces. The value null matches
+ * elements not in any namespace
+ * @param localName The local name of the elements to match on. The
+ * special value "*" matches all local names.
+ * @return A new <code>NodeList</code> object containing all the matched
+ * <code>Elements</code> .
+ * @since DOM Level 2
+ */
+
+ public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
+ return getElementsByTagNameNS(node, namespaceURI, localName);
+ }
+
+ public static NodeList getElementsByTagNameNS(NodeInfo node, String namespaceURI, String localName) {
+ String ns = (namespaceURI==null ? "" : namespaceURI);
+ AxisIterator allElements = node.iterateAxis(AxisInfo.DESCENDANT);
+ List<Node> nodes = new ArrayList<Node>(100);
+ while(true) {
+ NodeInfo next = allElements.next();
+ if (next == null) {
+ break;
+ }
+ if (next.getNodeKind()==Type.ELEMENT) {
+ if ((ns.equals("*") || ns.equals(next.getURI())) &&
+ (localName.equals("*") || localName.equals(next.getLocalPart()))) {
+ nodes.add(NodeOverNodeInfo.wrap(next));
+ }
+ }
+ }
+ return new DOMNodeList(nodes);
+ }
+
+ /**
+ * Return the <code>Element</code> whose <code>ID</code> is given by
+ * <code>elementId</code> . If no such element exists, returns
+ * <code>null</code> . Behavior is not defined if more than one element
+ * has this <code>ID</code> . The DOM implementation must have
+ * information that says which attributes are of type ID. Attributes with
+ * the name "ID" are not of type ID unless so defined. Implementations
+ * that do not know whether attributes are of type ID or not are expected
+ * to return <code>null</code> .
+ * @param elementId The unique <code>id</code> value for an element.
+ * @return The matching element, or null if there is none.
+ * @since DOM Level 2
+ */
+
+ public Element getElementById(String elementId) {
+ // Defined on Document node; but we support it on any node.
+ DocumentInfo doc = node.getDocumentRoot();
+ if (doc == null) {
+ return null;
+ }
+ return (Element)wrap(doc.selectID(elementId, false));
+ }
+
+ /**
+ * An attribute specifying the encoding used for this document at the time
+ * of the parsing. This is <code>null</code> when it is not known, such
+ * as when the <code>Document</code> was created in memory.
+ *
+ * @since DOM Level 3
+ */
+ public String getInputEncoding() {
+ return null;
+ }
+
+ /**
+ * An attribute specifying, as part of the
+ * <a href='http://www.w3.org/TR/2004/REC-xml-20040204#NT-XMLDecl'>XML declaration</a>,
+ * the encoding of this document. This is <code>null</code> when
+ * unspecified or when it is not known, such as when the
+ * <code>Document</code> was created in memory.
+ *
+ * @since DOM Level 3
+ */
+ public String getXmlEncoding() {
+ return null;
+ }
+
+ /**
+ * An attribute specifying, as part of the
+ * <a href='http://www.w3.org/TR/2004/REC-xml-20040204#NT-XMLDecl'>XML declaration</a>,
+ * whether this document is standalone. This is <code>false</code> when
+ * unspecified.
+ * <p ><b>Note:</b> No verification is done on the value when setting
+ * this attribute. Applications should use
+ * <code>Document.normalizeDocument()</code> with the "validate"
+ * parameter to verify if the value matches the <a href='http://www.w3.org/TR/2004/REC-xml-20040204#sec-rmd'>validity
+ * constraint for standalone document declaration</a> as defined in [<a href='http://www.w3.org/TR/2004/REC-xml-20040204'>XML 1.0</a>].
+ *
+ * @since DOM Level 3
+ */
+ public boolean getXmlStandalone() {
+ return false;
+ }
+
+ /**
+ * An attribute specifying, as part of the <a href='http://www.w3.org/TR/2004/REC-xml-20040204#NT-XMLDecl'>XML declaration</a>, whether this document is standalone. This is <code>false</code> when
+ * unspecified.
+ * <p ><b>Note:</b> No verification is done on the value when setting
+ * this attribute. Applications should use
+ * <code>Document.normalizeDocument()</code> with the "validate"
+ * parameter to verify if the value matches the <a href='http://www.w3.org/TR/2004/REC-xml-20040204#sec-rmd'>validity
+ * constraint for standalone document declaration</a> as defined in [<a href='http://www.w3.org/TR/2004/REC-xml-20040204'>XML 1.0</a>].
+ *
+ * @throws org.w3c.dom.DOMException NOT_SUPPORTED_ERR: Raised if this document does not support the
+ * "XML" feature.
+ * @since DOM Level 3
+ */
+ public void setXmlStandalone(boolean xmlStandalone) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * An attribute specifying, as part of the <a href='http://www.w3.org/TR/2004/REC-xml-20040204#NT-XMLDecl'>XML declaration</a>, the version number of this document. If there is no declaration and if
+ * this document supports the "XML" feature, the value is
+ * <code>"1.0"</code>. If this document does not support the "XML"
+ * feature, the value is always <code>null</code>. Changing this
+ * attribute will affect methods that check for invalid characters in
+ * XML names. Application should invoke
+ * <code>Document.normalizeDocument()</code> in order to check for
+ * invalid characters in the <code>Node</code>s that are already part of
+ * this <code>Document</code>.
+ * <br> DOM applications may use the
+ * <code>DOMImplementation.hasFeature(feature, version)</code> method
+ * with parameter values "XMLVersion" and "1.0" (respectively) to
+ * determine if an implementation supports [<a href='http://www.w3.org/TR/2004/REC-xml-20040204'>XML 1.0</a>]. DOM
+ * applications may use the same method with parameter values
+ * "XMLVersion" and "1.1" (respectively) to determine if an
+ * implementation supports [<a href='http://www.w3.org/TR/2004/REC-xml11-20040204/'>XML 1.1</a>]. In both
+ * cases, in order to support XML, an implementation must also support
+ * the "XML" feature defined in this specification. <code>Document</code>
+ * objects supporting a version of the "XMLVersion" feature must not
+ * raise a <code>NOT_SUPPORTED_ERR</code> exception for the same version
+ * number when using <code>Document.xmlVersion</code>.
+ *
+ * @since DOM Level 3
+ */
+ public String getXmlVersion() {
+ return "1.0";
+ }
+
+ /**
+ * An attribute specifying, as part of the <a href='http://www.w3.org/TR/2004/REC-xml-20040204#NT-XMLDecl'>XML declaration</a>, the version number of this document. If there is no declaration and if
+ * this document supports the "XML" feature, the value is
+ * <code>"1.0"</code>. If this document does not support the "XML"
+ * feature, the value is always <code>null</code>. Changing this
+ * attribute will affect methods that check for invalid characters in
+ * XML names. Application should invoke
+ * <code>Document.normalizeDocument()</code> in order to check for
+ * invalid characters in the <code>Node</code>s that are already part of
+ * this <code>Document</code>.
+ * <br> DOM applications may use the
+ * <code>DOMImplementation.hasFeature(feature, version)</code> method
+ * with parameter values "XMLVersion" and "1.0" (respectively) to
+ * determine if an implementation supports [<a href='http://www.w3.org/TR/2004/REC-xml-20040204'>XML 1.0</a>]. DOM
+ * applications may use the same method with parameter values
+ * "XMLVersion" and "1.1" (respectively) to determine if an
+ * implementation supports [<a href='http://www.w3.org/TR/2004/REC-xml11-20040204/'>XML 1.1</a>]. In both
+ * cases, in order to support XML, an implementation must also support
+ * the "XML" feature defined in this specification. <code>Document</code>
+ * objects supporting a version of the "XMLVersion" feature must not
+ * raise a <code>NOT_SUPPORTED_ERR</code> exception for the same version
+ * number when using <code>Document.xmlVersion</code>.
+ *
+ * @throws org.w3c.dom.DOMException NOT_SUPPORTED_ERR: Raised if the version is set to a value that is
+ * not supported by this <code>Document</code> or if this document
+ * does not support the "XML" feature.
+ * @since DOM Level 3
+ */
+ public void setXmlVersion(String xmlVersion) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * An attribute specifying whether error checking is enforced or not. When
+ * set to <code>false</code>, the implementation is free to not test
+ * every possible error case normally defined on DOM operations, and not
+ * raise any <code>DOMException</code> on DOM operations or report
+ * errors while using <code>Document.normalizeDocument()</code>. In case
+ * of error, the behavior is undefined. This attribute is
+ * <code>true</code> by default.
+ *
+ * @since DOM Level 3
+ */
+ public boolean getStrictErrorChecking() {
+ return false;
+ }
+
+ /**
+ * An attribute specifying whether error checking is enforced or not. When
+ * set to <code>false</code>, the implementation is free to not test
+ * every possible error case normally defined on DOM operations, and not
+ * raise any <code>DOMException</code> on DOM operations or report
+ * errors while using <code>Document.normalizeDocument()</code>. In case
+ * of error, the behavior is undefined. This attribute is
+ * <code>true</code> by default.
+ *
+ * @since DOM Level 3
+ */
+ public void setStrictErrorChecking(boolean strictErrorChecking) {
+ //no-op
+ }
+
+ /**
+ * The location of the document or <code>null</code> if undefined or if
+ * the <code>Document</code> was created using
+ * <code>DOMImplementation.createDocument</code>. No lexical checking is
+ * performed when setting this attribute; this could result in a
+ * <code>null</code> value returned when using <code>Node.baseURI</code>
+ * .
+ * <br> Beware that when the <code>Document</code> supports the feature
+ * "HTML" [<a href='http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109'>DOM Level 2 HTML</a>]
+ * , the href attribute of the HTML BASE element takes precedence over
+ * this attribute when computing <code>Node.baseURI</code>.
+ *
+ * @since DOM Level 3
+ */
+ public String getDocumentURI() {
+ return node.getSystemId();
+ }
+
+ /**
+ * The location of the document or <code>null</code> if undefined or if
+ * the <code>Document</code> was created using
+ * <code>DOMImplementation.createDocument</code>. No lexical checking is
+ * performed when setting this attribute; this could result in a
+ * <code>null</code> value returned when using <code>Node.baseURI</code>
+ * .
+ * <br> Beware that when the <code>Document</code> supports the feature
+ * "HTML" [<a href='http://www.w3.org/TR/2003/REC-DOM-Level-2-HTML-20030109'>DOM Level 2 HTML</a>]
+ * , the href attribute of the HTML BASE element takes precedence over
+ * this attribute when computing <code>Node.baseURI</code>.
+ *
+ * @since DOM Level 3
+ */
+ public void setDocumentURI(String documentURI) {
+ disallowUpdate();
+ }
+
+ /**
+ * Attempts to adopt a node from another document to this document. If
+ * supported, it changes the <code>ownerDocument</code> of the source
+ * node, its children, as well as the attached attribute nodes if there
+ * are any. If the source node has a parent it is first removed from the
+ * child list of its parent. This effectively allows moving a subtree
+ * from one document to another (unlike <code>importNode()</code> which
+ * create a copy of the source node instead of moving it). When it
+ * fails, applications should use <code>Document.importNode()</code>
+ * instead. Note that if the adopted node is already part of this
+ * document (i.e. the source and target document are the same), this
+ * method still has the effect of removing the source node from the
+ * child list of its parent, if any. The following list describes the
+ * specifics for each type of node.
+ * <dl>
+ * <dt>ATTRIBUTE_NODE</dt>
+ * <dd>The
+ * <code>ownerElement</code> attribute is set to <code>null</code> and
+ * the <code>specified</code> flag is set to <code>true</code> on the
+ * adopted <code>Attr</code>. The descendants of the source
+ * <code>Attr</code> are recursively adopted.</dd>
+ * <dt>DOCUMENT_FRAGMENT_NODE</dt>
+ * <dd>The
+ * descendants of the source node are recursively adopted.</dd>
+ * <dt>DOCUMENT_NODE</dt>
+ * <dd>
+ * <code>Document</code> nodes cannot be adopted.</dd>
+ * <dt>DOCUMENT_TYPE_NODE</dt>
+ * <dd>
+ * <code>DocumentType</code> nodes cannot be adopted.</dd>
+ * <dt>ELEMENT_NODE</dt>
+ * <dd><em>Specified</em> attribute nodes of the source element are adopted. Default attributes
+ * are discarded, though if the document being adopted into defines
+ * default attributes for this element name, those are assigned. The
+ * descendants of the source element are recursively adopted.</dd>
+ * <dt>ENTITY_NODE</dt>
+ * <dd>
+ * <code>Entity</code> nodes cannot be adopted.</dd>
+ * <dt>ENTITY_REFERENCE_NODE</dt>
+ * <dd>Only
+ * the <code>EntityReference</code> node itself is adopted, the
+ * descendants are discarded, since the source and destination documents
+ * might have defined the entity differently. If the document being
+ * imported into provides a definition for this entity name, its value
+ * is assigned.</dd>
+ * <dt>NOTATION_NODE</dt>
+ * <dd><code>Notation</code> nodes cannot be
+ * adopted.</dd>
+ * <dt>PROCESSING_INSTRUCTION_NODE, TEXT_NODE, CDATA_SECTION_NODE,
+ * COMMENT_NODE</dt>
+ * <dd>These nodes can all be adopted. No specifics.</dd>
+ * </dl>
+ * <p ><b>Note:</b> Since it does not create new nodes unlike the
+ * <code>Document.importNode()</code> method, this method does not raise
+ * an <code>INVALID_CHARACTER_ERR</code> exception, and applications
+ * should use the <code>Document.normalizeDocument()</code> method to
+ * check if an imported name is not an XML name according to the XML
+ * version in use.
+ *
+ * @param source The node to move into this document.
+ * @return The adopted node, or <code>null</code> if this operation
+ * fails, such as when the source node comes from a different
+ * implementation.
+ * @throws org.w3c.dom.DOMException NOT_SUPPORTED_ERR: Raised if the source node is of type
+ * <code>DOCUMENT</code>, <code>DOCUMENT_TYPE</code>.
+ * <br>NO_MODIFICATION_ALLOWED_ERR: Raised when the source node is
+ * readonly.
+ * @since DOM Level 3
+ */
+ public Node adoptNode(Node source) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * The configuration used when <code>Document.normalizeDocument()</code>
+ * is invoked.
+ *
+ * @since DOM Level 3
+ */
+ public DOMConfiguration getDomConfig() {
+ return null;
+ }
+
+ /**
+ * This method acts as if the document was going through a save and load
+ * cycle, putting the document in a "normal" form. As a consequence,
+ * this method updates the replacement tree of
+ * <code>EntityReference</code> nodes and normalizes <code>Text</code>
+ * nodes, as defined in the method <code>Node.normalize()</code>.
+ * <br> Otherwise, the actual result depends on the features being set on
+ * the <code>Document.domConfig</code> object and governing what
+ * operations actually take place. Noticeably this method could also
+ * make the document namespace well-formed according to the algorithm
+ * described in , check the character normalization, remove the
+ * <code>CDATASection</code> nodes, etc. See
+ * <code>DOMConfiguration</code> for details.
+ * <pre>// Keep in the document
+ * the information defined // in the XML Information Set (Java example)
+ * DOMConfiguration docConfig = myDocument.getDomConfig();
+ * docConfig.setParameter("infoset", Boolean.TRUE);
+ * myDocument.normalizeDocument();</pre>
+ * <p/>
+ * <br>Mutation events, when supported, are generated to reflect the
+ * changes occurring on the document.
+ * <br> If errors occur during the invocation of this method, such as an
+ * attempt to update a read-only node or a <code>Node.nodeName</code>
+ * contains an invalid character according to the XML version in use,
+ * errors or warnings (<code>DOMError.SEVERITY_ERROR</code> or
+ * <code>DOMError.SEVERITY_WARNING</code>) will be reported using the
+ * <code>DOMErrorHandler</code> object associated with the "error-handler
+ * " parameter. Note this method might also report fatal errors (
+ * <code>DOMError.SEVERITY_FATAL_ERROR</code>) if an implementation
+ * cannot recover from an error.
+ *
+ * @since DOM Level 3
+ */
+ public void normalizeDocument() {
+ disallowUpdate();
+ }
+
+ /**
+ * Rename an existing node of type <code>ELEMENT_NODE</code> or
+ * <code>ATTRIBUTE_NODE</code>. Not supported in this implementation
+ *
+ * @param n The node to rename.
+ * @param namespaceURI The new namespace URI.
+ * @param qualifiedName The new qualified name.
+ * @return The renamed node. This is either the specified node or the new
+ * node that was created to replace the specified node.
+ * @throws org.w3c.dom.DOMException
+ */
+ /*@Nullable*/ public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+
+}
+
diff --git a/sf/saxon/dom/DocumentWrapper.java b/sf/saxon/dom/DocumentWrapper.java
new file mode 100644
index 0000000..fda877e
--- /dev/null
+++ b/sf/saxon/dom/DocumentWrapper.java
@@ -0,0 +1,316 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+import org.w3c.dom.*;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * The document node of a tree implemented as a wrapper around a DOM Document.
+ *
+ * <p>Because the DOM is not thread-safe even when reading, and because Saxon-EE can spawn multiple
+ * threads that access the same input tree, all methods that invoke DOM methods are synchronized
+ * on the DocumentWrapper object. (This still relies on the user not allocating two DocumentWrappers
+ * around the same DOM).</p>
+ */
+
+public class DocumentWrapper extends DOMNodeWrapper implements DocumentInfo {
+
+ protected Configuration config;
+ protected String baseURI;
+ protected long documentNumber;
+ protected boolean domLevel3;
+ private HashMap<String, Object> userData;
+
+ /**
+ * Wrap a DOM Document or DocumentFragment node
+ * @param doc a DOM Document or DocumentFragment node
+ * @param baseURI the base URI of the document
+ * @param config the Saxon configuration
+ */
+
+ public DocumentWrapper(Node doc, String baseURI, Configuration config) {
+ super(doc, null, null, 0);
+ if (doc.getNodeType() != Node.DOCUMENT_NODE && doc.getNodeType() != Node.DOCUMENT_FRAGMENT_NODE) {
+ throw new IllegalArgumentException("Node must be a DOM Document or DocumentFragment");
+ }
+ node = doc;
+ nodeKind = Type.DOCUMENT;
+ this.baseURI = baseURI;
+ docWrapper = this;
+ domLevel3 = config.getDOMLevel() == 3;
+ if (config.getExternalObjectModel(doc.getClass()) == null) {
+ throw new IllegalArgumentException(
+ "Node class " + doc.getClass().getName() + " is not recognized in this Saxon configuration");
+ }
+ setConfiguration(config);
+ }
+
+ /**
+ * Create a wrapper for a node in this document
+ *
+ * @param node the DOM node to be wrapped. This must be a node within the document wrapped by this
+ * DocumentWrapper
+ * @throws IllegalArgumentException if the node is not a descendant of the Document node wrapped by
+ * this DocumentWrapper
+ * @return the wrapped node
+ */
+
+ public DOMNodeWrapper wrap(Node node) {
+ if (node == this.node) {
+ return this;
+ }
+ Document doc = node.getOwnerDocument();
+ if (doc == this.node || (domLevel3 && doc != null && doc.isSameNode(this.node))) {
+ return makeWrapper(node, this);
+ } else {
+ throw new IllegalArgumentException(
+ "DocumentWrapper#wrap: supplied node does not belong to the wrapped DOM document");
+ }
+ }
+
+
+ @Override
+ public String getBaseURI() {
+ return baseURI;
+ }
+
+ /**
+ * Get the System ID for the node.
+ *
+ * @return the System Identifier of the entity in the source document containing the node,
+ * or null if not known. Note this is not the same as the base URI: the base URI can be
+ * modified by xml:base, but the system ID cannot.
+ */
+ @Override
+ public String getSystemId() {
+ return baseURI;
+ }
+
+ /**
+ * Set the Configuration that contains this document
+ * @param config the Saxon configuration
+ */
+
+ public void setConfiguration(Configuration config) {
+ this.config = config;
+ documentNumber = config.getDocumentNumberAllocator().allocateDocumentNumber();
+ }
+
+ /**
+ * Get the configuration previously set using setConfiguration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the name pool used for the names in this document
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ /**
+ * Ask whether the document contains any nodes whose type annotation is anything other than
+ * UNTYPED
+ *
+ * @return true if the document contains elements whose type is other than UNTYPED
+ */
+ public boolean isTyped() {
+ return false;
+ }
+
+ /**
+ * Get the unique document number
+ */
+
+ public long getDocumentNumber() {
+ return documentNumber;
+ }
+
+ /**
+ * Get the element with a given ID, if any
+ *
+ * @param id the required ID value
+ * @param getParent true if the parent of the element having the given ID value is required
+ * @return a NodeInfo representing the element with the given ID, or null if there
+ * is no such element. This implementation does not necessarily conform to the
+ * rule that if an invalid document contains two elements with the same ID, the one
+ * that comes last should be returned.
+ */
+
+ public synchronized NodeInfo selectID(String id, boolean getParent) {
+ if (node instanceof Document) {
+ Node el = ((Document)node).getElementById(id);
+ if (el == null) {
+ return null;
+ }
+ return wrap(el);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Determine whether this is the same node as another node. <br />
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ *
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public synchronized boolean isSameNodeInfo(NodeInfo other) {
+ return other instanceof DocumentWrapper && node == ((DocumentWrapper)other).node;
+ }
+
+ /**
+ * Get the list of unparsed entities defined in this document
+ * @return an Iterator, whose items are of type String, containing the names of all
+ * unparsed entities defined in this document. If there are no unparsed entities or if the
+ * information is not available then an empty iterator is returned
+ * @since 9.1 (implemented for this subclass since 9.2)
+ */
+
+ public synchronized Iterator<String> getUnparsedEntityNames() {
+ DocumentType docType = ((Document)node).getDoctype();
+ if (docType == null) {
+ List<String> ls = Collections.emptyList();
+ return ls.iterator();
+ }
+ NamedNodeMap map = docType.getEntities();
+ if (map == null) {
+ List<String> ls = Collections.emptyList();
+ return ls.iterator();
+ }
+ List<String> names = new ArrayList<String>(map.getLength());
+ for (int i=0; i<map.getLength(); i++) {
+ Entity e = (Entity)map.item(i);
+ if (e.getNotationName() != null) {
+ // it is an unparsed entity
+ names.add(e.getLocalName());
+ }
+ }
+ return names.iterator();
+ }
+
+ /**
+ * Get the unparsed entity with a given name
+ *
+ * @param name the name of the entity
+ * @return if the entity exists, return an array of two Strings, the first
+ * holding the system ID of the entity (as an absolute URI if possible),
+ * the second holding the public ID if there is one, or null if not.
+ * If the entity does not exist, the method returns null.
+ * Applications should be written on the assumption that this array may
+ * be extended in the future to provide additional information.
+ * @since 8.4 (implemented for this subclass since 9.2)
+ */
+
+ public synchronized String[] getUnparsedEntity(String name) {
+ DocumentType docType = ((Document)node).getDoctype();
+ if (docType == null) {
+ return null;
+ }
+ NamedNodeMap map = docType.getEntities();
+ if (map == null) {
+ return null;
+ }
+ Entity entity = (Entity)map.getNamedItem(name);
+ if (entity == null || entity.getNotationName() == null) {
+ // In the first case, no entity found. In the second case, it's a parsed entity.
+ return null;
+ }
+ String systemId = entity.getSystemId();
+ try {
+ URI systemIdURI = new URI(systemId);
+ if (!systemIdURI.isAbsolute()) {
+ systemIdURI = new URI(getBaseURI()).resolve(systemIdURI);
+ systemId = systemIdURI.toString();
+ }
+ } catch (URISyntaxException err) {
+ // invalid URI: no action - return the "URI" as written
+ }
+ return new String[]{systemId, entity.getPublicId()};
+ }
+
+ /**
+ * Get the type annotation. Always XS_UNTYPED.
+ */
+
+ public int getTypeAnnotation() {
+ return StandardNames.XS_UNTYPED;
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ public SchemaType getSchemaType() {
+ return Untyped.getInstance();
+ }
+
+ /**
+ * Set user data on the document node. The user data can be retrieved subsequently
+ * using {@link #getUserData}
+ * @param key A string giving the name of the property to be set. Clients are responsible
+ * for choosing a key that is likely to be unique. Must not be null. Keys used internally
+ * by Saxon are prefixed "saxon:".
+ * @param value The value to be set for the property. May be null, which effectively
+ * removes the existing value for the property.
+ */
+
+ public synchronized void setUserData(String key, Object value) {
+ if (userData == null) {
+ userData = new HashMap<String, Object>(4);
+ }
+ if (value == null) {
+ userData.remove(key);
+ } else {
+ userData.put(key, value);
+ }
+ }
+
+ /**
+ * Get user data held in the document node. This retrieves properties previously set using
+ * {@link #setUserData}
+ * @param key A string giving the name of the property to be retrieved.
+ * @return the value of the property, or null if the property has not been defined.
+ */
+
+ /*@Nullable*/ public synchronized Object getUserData(String key) {
+ if (userData == null) {
+ return null;
+ } else {
+ return userData.get(key);
+ }
+ }
+}
+
diff --git a/sf/saxon/dom/ElementOverNodeInfo.java b/sf/saxon/dom/ElementOverNodeInfo.java
new file mode 100644
index 0000000..f4db0a6
--- /dev/null
+++ b/sf/saxon/dom/ElementOverNodeInfo.java
@@ -0,0 +1,346 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+import org.w3c.dom.*;
+
+/**
+ * This class is an implementation of the DOM Element class that wraps a Saxon NodeInfo
+ * representation of an element node.
+ * <p>
+ * The class provides read-only access to the tree; methods that request updates all fail
+ * with an UnsupportedOperationException.
+ * <p>
+ * Note that contrary to the DOM specification, this implementation does not expose namespace
+ * declarations as attributes.
+ */
+
+public class ElementOverNodeInfo extends NodeOverNodeInfo implements Element {
+
+ /**
+ * The name of the element (DOM interface).
+ */
+
+ public String getTagName() {
+ return node.getDisplayName();
+ }
+
+ /**
+ * Returns a <code>NodeList</code> of all descendant <code>Elements</code>
+ * with a given tag name, in document order.
+ *
+ * @param name The name of the tag to match on. The special value "*"
+ * matches all tags.
+ * @return A list of matching <code>Element</code> nodes.
+ */
+ public NodeList getElementsByTagName(String name) {
+ return DocumentOverNodeInfo.getElementsByTagName(node, name);
+ }
+
+ /**
+ * Returns a <code>NodeList</code> of all the descendant
+ * <code>Elements</code> with a given local name and namespace URI in
+ * document order.
+ *
+ * @param namespaceURI The namespace URI of the elements to match on. The
+ * special value "*" matches all namespaces.
+ * @param localName The local name of the elements to match on. The
+ * special value "*" matches all local names.
+ * @return A new <code>NodeList</code> object containing all the matched
+ * <code>Elements</code>.
+ * @throws org.w3c.dom.DOMException NOT_SUPPORTED_ERR: May be raised if the implementation does not
+ * support the feature <code>"XML"</code> and the language exposed
+ * through the Document does not support XML Namespaces (such as [<a href='http://www.w3.org/TR/1999/REC-html401-19991224/'>HTML 4.01</a>]).
+ * @since DOM Level 2
+ */
+ public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException {
+ return DocumentOverNodeInfo.getElementsByTagNameNS(node, namespaceURI, localName);
+ }
+
+ /**
+ * Retrieves an attribute value by name.
+ * This implementation does not expose namespace nodes as attributes.
+ * @param name The QName of the attribute to retrieve.
+ * @return The <code>Attr</code> value as a string, or the empty string if
+ * that attribute does not have a specified or default value.
+ */
+
+ public String getAttribute(String name) {
+ AxisIterator atts = node.iterateAxis(AxisInfo.ATTRIBUTE);
+ while (true) {
+ NodeInfo att = atts.next();
+ if (att == null) {
+ return "";
+ }
+ if (att.getDisplayName().equals(name)) {
+ String val = att.getStringValue();
+ if (val==null) return "";
+ return val;
+ }
+ }
+ }
+
+ /**
+ * Retrieves an attribute node by name.
+ * This implementation does not expose namespace nodes as attributes.
+ * <br> To retrieve an attribute node by qualified name and namespace URI,
+ * use the <code>getAttributeNodeNS</code> method.
+ * @param name The name (<code>nodeName</code> ) of the attribute to
+ * retrieve.
+ * @return The <code>Attr</code> node with the specified name (
+ * <code>nodeName</code> ) or <code>null</code> if there is no such
+ * attribute.
+ */
+
+ public Attr getAttributeNode(String name) {
+ AxisIterator atts = node.iterateAxis(AxisInfo.ATTRIBUTE);
+ while (true) {
+ NodeInfo att = atts.next();
+ if (att == null) {
+ return null;
+ }
+ if (att.getDisplayName().equals(name)) {
+ return (Attr)att;
+ }
+ }
+ }
+
+ /**
+ * Adds a new attribute node. Always fails
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ */
+
+ public Attr setAttributeNode(Attr newAttr) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Removes the specified attribute. Always fails
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ */
+
+ public void removeAttribute(String oldAttr) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Removes the specified attribute node. Always fails
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ */
+
+ public Attr removeAttributeNode(Attr oldAttr) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+
+ /**
+ * Retrieves an attribute value by local name and namespace URI.
+ * This implementation does not expose namespace nodes as attributes.
+ * @param namespaceURI The namespace URI of the attribute to retrieve.
+ * @param localName The local name of the attribute to retrieve.
+ * @return The <code>Attr</code> value as a string, or the empty string if
+ * that attribute does not have a specified or default value.
+ * @since DOM Level 2
+ */
+
+ public String getAttributeNS(String namespaceURI, String localName) {
+ String val = Navigator.getAttributeValue(node, (namespaceURI==null ? "" : namespaceURI), localName);
+ if (val==null) return "";
+ return val;
+ }
+
+ /**
+ * Adds a new attribute. Always fails
+ *
+ * @param name The name of the attribute to create or alter.
+ * @param value Value to set in string form.
+ * @throws org.w3c.dom.DOMException INVALID_CHARACTER_ERR: Raised if the specified name is not an XML
+ * name according to the XML version in use specified in the
+ * <code>Document.xmlVersion</code> attribute.
+ * <br>NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ */
+ public void setAttribute(String name, String value) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Adds a new attribute. Always fails.
+ * @param namespaceURI The namespace URI of the attribute to create or
+ * alter.
+ * @param qualifiedName The qualified name of the attribute to create or
+ * alter.
+ * @param value The value to set in string form.
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ */
+
+ public void setAttributeNS(String namespaceURI,
+ String qualifiedName,
+ String value)
+ throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Removes an attribute by local name and namespace URI. Always fails
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ * @since DOM Level 2
+ */
+
+ public void removeAttributeNS(String namespaceURI,
+ String localName)
+ throws DOMException{
+ disallowUpdate();
+ }
+
+ /**
+ * Retrieves an <code>Attr</code> node by local name and namespace URI.
+ * This implementation does not expose namespace nodes as attributes.
+ * @param namespaceURI The namespace URI of the attribute to retrieve.
+ * @param localName The local name of the attribute to retrieve.
+ * @return The <code>Attr</code> node with the specified attribute local
+ * name and namespace URI or <code>null</code> if there is no such
+ * attribute.
+ * @since DOM Level 2
+ */
+
+ public Attr getAttributeNodeNS(String namespaceURI, String localName) {
+ NamePool pool = node.getNamePool();
+ int fingerprint = pool.getFingerprint((namespaceURI==null ? "" : namespaceURI), localName);
+ if (fingerprint==-1) return null;
+ NameTest test = new NameTest(Type.ATTRIBUTE, fingerprint, pool);
+ AxisIterator atts = node.iterateAxis(AxisInfo.ATTRIBUTE, test);
+ return (Attr)wrap(atts.next());
+ }
+
+ /**
+ * Add a new attribute. Always fails.
+ * @param newAttr The <code>Attr</code> node to add to the attribute list.
+ * @return If the <code>newAttr</code> attribute replaces an existing
+ * attribute with the same local name and namespace URI , the
+ * replaced <code>Attr</code> node is returned, otherwise
+ * <code>null</code> is returned.
+ * @exception org.w3c.dom.DOMException
+ * <br> NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ * @since DOM Level 2
+ */
+
+ public Attr setAttributeNodeNS(Attr newAttr)
+ throws DOMException{
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Returns <code>true</code> when an attribute with a given name is
+ * specified on this element or has a default value, <code>false</code>
+ * otherwise.
+ * This implementation does not expose namespace nodes as attributes.
+ * @param name The name of the attribute to look for.
+ * @return <code>true</code> if an attribute with the given name is
+ * specified on this element or has a default value, <code>false</code>
+ * otherwise.
+ * @since DOM Level 2
+ */
+
+ public boolean hasAttribute(String name) {
+ AxisIterator atts = node.iterateAxis(AxisInfo.ATTRIBUTE);
+ while (true) {
+ NodeInfo att = atts.next();
+ if (att == null) {
+ return false;
+ }
+ if (att.getDisplayName().equals(name)) {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Returns <code>true</code> when an attribute with a given local name
+ * and namespace URI is specified on this element or has a default value,
+ * <code>false</code> otherwise.
+ * This implementation does not expose namespace nodes as attributes.
+ * @param namespaceURI The namespace URI of the attribute to look for.
+ * @param localName The local name of the attribute to look for.
+ * @return <code>true</code> if an attribute with the given local name and
+ * namespace URI is specified or has a default value on this element,
+ * <code>false</code> otherwise.
+ * @since DOM Level 2
+ */
+
+ public boolean hasAttributeNS(String namespaceURI, String localName) {
+ return (Navigator.getAttributeValue(node, (namespaceURI==null ? "" : namespaceURI), localName) != null);
+ }
+
+ /**
+ * Mark an attribute as an ID. Always fails.
+ * @throws DOMException
+ */
+
+ public void setIdAttribute(String name,
+ boolean isId)
+ throws DOMException{
+ disallowUpdate();
+ }
+
+ /**
+ * Mark an attribute as an ID. Always fails.
+ * @throws DOMException
+ */
+
+ public void setIdAttributeNS(String namespaceURI,
+ String localName,
+ boolean isId)
+ throws DOMException{
+ disallowUpdate();
+ }
+
+ /**
+ * Mark an attribute as an ID. Always fails.
+ * @throws DOMException
+ */
+
+ public void setIdAttributeNode(Attr idAttr,
+ boolean isId)
+ throws DOMException{
+ disallowUpdate();
+ }
+
+
+ /**
+ * Get the schema type information for this node.
+ * @return the type information. Returns null for an untyped node.
+ */
+
+ /*@Nullable*/ public TypeInfo getSchemaTypeInfo() {
+ SchemaType type = node.getSchemaType();
+ if (type == null || Untyped.getInstance().equals(type) || BuiltInAtomicType.UNTYPED_ATOMIC.equals(type)) {
+ return null;
+ }
+ return new TypeInfoImpl(node.getConfiguration(), type);
+ }
+
+}
+
diff --git a/sf/saxon/dom/NodeOverNodeInfo.java b/sf/saxon/dom/NodeOverNodeInfo.java
new file mode 100644
index 0000000..8db5511
--- /dev/null
+++ b/sf/saxon/dom/NodeOverNodeInfo.java
@@ -0,0 +1,732 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.functions.DeepEqual;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.Type;
+import org.w3c.dom.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * This class implements the DOM Node interface as a wrapper around a Saxon NodeInfo object.
+ * <p>
+ * The class provides read-only access to the tree; methods that request updates all fail
+ * with an UnsupportedOperationException.
+ */
+
+public abstract class NodeOverNodeInfo implements Node {
+
+ protected NodeInfo node;
+
+ /**
+ * Get the Saxon NodeInfo object representing this node
+ * @return the Saxon NodeInfo object
+ */
+
+ /*@Nullable*/ public NodeInfo getUnderlyingNodeInfo() {
+ return node;
+ }
+
+ /**
+ * Factory method to construct a DOM node that wraps an underlying Saxon NodeInfo
+ * @param node the Saxon NodeInfo object
+ * @return the DOM wrapper node
+ */
+
+ public static NodeOverNodeInfo wrap(NodeInfo node) {
+ NodeOverNodeInfo n;
+ if (node == null) {
+ return null;
+ }
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT:
+ n = new DocumentOverNodeInfo();
+ break;
+ case Type.ELEMENT:
+ n = new ElementOverNodeInfo();
+ break;
+ case Type.ATTRIBUTE:
+ n = new AttrOverNodeInfo();
+ break;
+ case Type.TEXT:
+ case Type.COMMENT:
+ n = new TextOverNodeInfo();
+ break;
+ case Type.PROCESSING_INSTRUCTION:
+ n = new PIOverNodeInfo();
+ break;
+ case Type.NAMESPACE:
+ n = new AttrOverNodeInfo();
+ break;
+ default:
+ return null;
+ }
+ n.node = node;
+ return n;
+ }
+
+
+ /**
+ * Determine whether this is the same node as another node. DOM Level 3 method.
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public final boolean isSameNode(Node other) {
+ return other instanceof NodeOverNodeInfo &&
+ node.isSameNodeInfo(((NodeOverNodeInfo)other).node);
+ }
+
+ /**
+ * The equals() method returns true for two Node objects that represent the same
+ * conceptual DOM Node. This is a concession to the Xalan IdentityTransformer, which relies
+ * on equals() for DOM Nodes having this behaviour, even though it is not defined in the
+ * specification
+ * @param obj the object to be compared
+ * @return if this node and obj represent the same conceptual DOM node. That is, return
+ * true if isSameNode((Node)obj) returns true
+ */
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof Node && isSameNode((Node)obj);
+ }
+
+ /**
+ * Return a hashCode
+ * @return a hashCode such that two wrappers over the same underlying node have the same hashCode.
+ */
+
+ @Override
+ public int hashCode() {
+ return node.hashCode();
+ }
+
+ /**
+ * Get the base URI for the node. Default implementation for child nodes gets
+ * the base URI of the parent node.
+ */
+
+ public String getBaseURI() {
+ return node.getBaseURI();
+ }
+
+ /**
+ * Get the name of this node, following the DOM rules
+ * @return The name of the node. For an element this is the element name, for an attribute
+ * it is the attribute name, as a lexical QName. Other node types return conventional names such
+ * as "#text" or "#comment"
+ */
+
+ public String getNodeName() {
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT:
+ return "#document";
+ case Type.ELEMENT:
+ return node.getDisplayName();
+ case Type.ATTRIBUTE:
+ return node.getDisplayName();
+ case Type.TEXT:
+ return "#text";
+ case Type.COMMENT:
+ return "#comment";
+ case Type.PROCESSING_INSTRUCTION:
+ return node.getLocalPart();
+ case Type.NAMESPACE:
+ if (node.getLocalPart().length() == 0) {
+ return "xmlns";
+ } else {
+ return "xmlns:" + node.getLocalPart();
+ }
+ default:
+ return "#unknown";
+ }
+ }
+
+ /**
+ * Get the local name of this node, following the DOM rules
+ * @return The local name of the node. For an element this is the local part of the element name,
+ * for an attribute it is the local part of the attribute name. Other node types return null.
+ */
+
+ public String getLocalName() {
+ switch (node.getNodeKind()) {
+ case Type.ELEMENT:
+ case Type.ATTRIBUTE:
+ return node.getLocalPart();
+ case Type.DOCUMENT:
+ case Type.TEXT:
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ return null;
+ case Type.NAMESPACE:
+ if (node.getLocalPart().length() == 0) {
+ return "xmlns";
+ } else {
+ return node.getLocalPart();
+ }
+ default:
+ return null;
+ }
+ }
+
+
+ /**
+ * Determine whether the node has any children.
+ * @return <code>true</code> if this node has any attributes,
+ * <code>false</code> otherwise.
+ */
+
+ public boolean hasChildNodes() {
+ return node.iterateAxis(AxisInfo.CHILD).next() != null;
+ }
+
+ /**
+ * Returns whether this node has any attributes. We treat the declaration of the XML namespace
+ * as being present on every element, and since namespace declarations are treated as attributes,
+ * every element has at least one attribute. This method therefore returns true.
+ * @return <code>true</code> if this node has any attributes,
+ * <code>false</code> otherwise.
+ * @since DOM Level 2
+ */
+
+ public boolean hasAttributes() {
+ return true;
+ }
+
+ /**
+ * Get the type of this node (node kind, in XPath terminology).
+ * Note, the numbers assigned to node kinds
+ * in Saxon (see {@link Type}) are the same as those assigned in the DOM
+ */
+
+ public short getNodeType() {
+ short kind = (short)node.getNodeKind();
+ if (kind == Type.NAMESPACE) {
+ return Type.ATTRIBUTE;
+ } else {
+ return kind;
+ }
+ }
+
+ /**
+ * Find the parent node of this node.
+ * @return The Node object describing the containing element or root node.
+ */
+
+ public Node getParentNode() {
+ return wrap(node.getParent());
+ }
+
+ /**
+ * Get the previous sibling of the node
+ * @return The previous sibling node. Returns null if the current node is the first
+ * child of its parent.
+ */
+
+ public Node getPreviousSibling() {
+ return wrap(node.iterateAxis(AxisInfo.PRECEDING_SIBLING).next());
+ }
+
+ /**
+ * Get next sibling node
+ * @return The next sibling node. Returns null if the current node is the last
+ * child of its parent.
+ */
+
+ public Node getNextSibling() {
+ return wrap(node.iterateAxis(AxisInfo.FOLLOWING_SIBLING).next());
+ }
+
+ /**
+ * Get first child
+ * @return the first child node of this node, or null if it has no children
+ */
+
+ public Node getFirstChild() {
+ return wrap(node.iterateAxis(AxisInfo.CHILD).next());
+ }
+
+ /**
+ * Get last child
+ * @return last child of this node, or null if it has no children
+ */
+
+ public Node getLastChild() {
+ AxisIterator children = node.iterateAxis(AxisInfo.CHILD);
+ NodeInfo last = null;
+ while (true) {
+ NodeInfo next = children.next();
+ if (next == null) {
+ return wrap(last);
+ } else {
+ last = next;
+ }
+ }
+ }
+
+ /**
+ * Get the node value (as defined in the DOM).
+ * This is not generally the same as the XPath string-value: in particular, the
+ * node value of an element node is null.
+ */
+
+ public String getNodeValue() {
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT:
+ case Type.ELEMENT:
+ return null;
+ case Type.ATTRIBUTE:
+ case Type.TEXT:
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ case Type.NAMESPACE:
+ return node.getStringValue();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Set the node value. Always fails
+ */
+
+ public void setNodeValue(String nodeValue) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Return a <code>NodeList</code> that contains all children of this node. If
+ * there are no children, this is a <code>NodeList</code> containing no
+ * nodes.
+ */
+
+ public NodeList getChildNodes() {
+ try {
+ List<Node> nodes = new ArrayList<Node>(10);
+ SequenceIterator iter = node.iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo node = (NodeInfo)iter.next();
+ if (node == null) break;
+ nodes.add(NodeOverNodeInfo.wrap(node));
+ }
+ return new DOMNodeList(nodes);
+ } catch (XPathException err) {
+ return null;
+ // can't happen
+ }
+ }
+
+ /**
+ * Return a <code>NamedNodeMap</code> containing the attributes of this node (if
+ * it is an <code>Element</code>) or <code>null</code> otherwise. Note that this
+ * implementation changed in Saxon 8.8 to treat namespace declarations as attributes.
+ */
+
+ public NamedNodeMap getAttributes() {
+ if (node.getNodeKind()==Type.ELEMENT) {
+ return new DOMAttributeMap(node);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return the <code>Document</code> object associated with this node.
+ */
+
+ public Document getOwnerDocument() {
+ return (Document)wrap(node.getDocumentRoot());
+ }
+
+ /**
+ * Insert the node <code>newChild</code> before the existing child node
+ * <code>refChild</code>. Always fails.
+ * @param newChild The node to insert.
+ * @param refChild The reference node, i.e., the node before which the
+ * new node must be inserted.
+ * @return The node being inserted.
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Always raised.
+ */
+
+ public Node insertBefore(Node newChild,
+ Node refChild)
+ throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Replace the child node <code>oldChild</code> with
+ * <code>newChild</code> in the list of children, and returns the
+ * <code>oldChild</code> node. Always fails.
+ * @param newChild The new node to put in the child list.
+ * @param oldChild The node being replaced in the list.
+ * @return The node replaced.
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Always raised.
+ */
+
+ public Node replaceChild(Node newChild,
+ Node oldChild)
+ throws DOMException{
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Remove the child node indicated by <code>oldChild</code> from the
+ * list of children, and returns it. Always fails.
+ * @param oldChild The node being removed.
+ * @return The node removed.
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Always raised.
+ */
+
+ public Node removeChild(Node oldChild) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Adds the node <code>newChild</code> to the end of the list of children
+ * of this node. Always fails.
+ * @param newChild The node to add.
+ * @return The node added.
+ * @exception org.w3c.dom.DOMException
+ * <br> NO_MODIFICATION_ALLOWED_ERR: Always raised.
+ */
+
+ public Node appendChild(Node newChild) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Returns a duplicate of this node, i.e., serves as a generic copy
+ * constructor for nodes. Always fails.
+ * @param deep If <code>true</code> , recursively clone the subtree under
+ * the specified node; if <code>false</code> , clone only the node
+ * itself (and its attributes, if it is an <code>Element</code> ).
+ * @return The duplicate node.
+ */
+
+ public Node cloneNode(boolean deep) {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Puts all <code>Text</code> nodes in the full depth of the sub-tree
+ * underneath this <code>Node</code>, including attribute nodes, into a
+ * "normal" form where only structure (e.g., elements, comments,
+ * processing instructions, CDATA sections, and entity references)
+ * separates <code>Text</code> nodes, i.e., there are neither adjacent
+ * <code>Text</code> nodes nor empty <code>Text</code> nodes.
+ * @since DOM Level 2
+ */
+
+ public void normalize() {
+ // null operation; nodes are always normalized
+ }
+
+ /**
+ * Tests whether the DOM implementation implements a specific feature and
+ * that feature is supported by this node.
+ * @param feature The name of the feature to test. This is the same name
+ * which can be passed to the method <code>hasFeature</code> on
+ * <code>DOMImplementation</code> .
+ * @param version This is the version number of the feature to test. In
+ * Level 2, version 1, this is the string "2.0". If the version is not
+ * specified, supporting any version of the feature will cause the
+ * method to return <code>true</code> .
+ * @return Returns <code>true</code> if the specified feature is supported
+ * on this node, <code>false</code> otherwise.
+ * @since DOM Level 2
+ */
+
+ public boolean isSupported(String feature,
+ String version) {
+ return (feature.equalsIgnoreCase("XML") || feature.equalsIgnoreCase("Core")) &&
+ (version == null || version.length() == 0 ||
+ version.equals("3.0") || version.equals("2.0") || version.equals("1.0"));
+ }
+
+ /**
+ * The namespace URI of this node, or <code>null</code> if it is
+ * unspecified.
+ * <br> This is not a computed value that is the result of a namespace
+ * lookup based on an examination of the namespace declarations in scope.
+ * It is merely the namespace URI given at creation time.
+ * <br> For nodes of any type other than <code>ELEMENT_NODE</code> and
+ * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1
+ * method, such as <code>createElement</code> from the
+ * <code>Document</code> interface, this is always <code>null</code> .
+ * Per the Namespaces in XML Specification an attribute does not
+ * inherit its namespace from the element it is attached to. If an
+ * attribute is not explicitly given a namespace, it simply has no
+ * namespace.
+ * @since DOM Level 2
+ */
+
+ public String getNamespaceURI() {
+ if (node.getNodeKind() == Type.NAMESPACE) {
+ return NamespaceConstant.XMLNS;
+ }
+ String uri = node.getURI();
+ return ("".equals(uri) ? null : uri);
+ }
+
+ /**
+ * The namespace prefix of this node, or <code>null</code> if it is
+ * unspecified.
+ * <br>For nodes of any type other than <code>ELEMENT_NODE</code> and
+ * <code>ATTRIBUTE_NODE</code> and nodes created with a DOM Level 1
+ * method, such as <code>createElement</code> from the
+ * <code>Document</code> interface, this is always <code>null</code>.
+ *
+ * @since DOM Level 2
+ */
+
+ public String getPrefix() {
+ if (node.getNodeKind() == Type.NAMESPACE) {
+ if (node.getLocalPart().length() == 0) {
+ return null;
+ } else {
+ return "xmlns";
+ }
+ }
+ String p = node.getNamePool().getPrefix(node.getNameCode());
+ return ("".equals(p) ? null : p);
+ }
+
+ /**
+ * Set the namespace prefix of this node. Always fails.
+ */
+
+ public void setPrefix(String prefix)
+ throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Compare the position of the (other) node in document order with the reference node (this node).
+ * DOM Level 3 method.
+ * @param other the other node.
+ * @return Returns how the node is positioned relatively to the reference
+ * node.
+ * @throws org.w3c.dom.DOMException
+ */
+
+ public short compareDocumentPosition(Node other) throws DOMException {
+ final short DOCUMENT_POSITION_DISCONNECTED = 0x01;
+ final short DOCUMENT_POSITION_PRECEDING = 0x02;
+ final short DOCUMENT_POSITION_FOLLOWING = 0x04;
+ final short DOCUMENT_POSITION_CONTAINS = 0x08;
+ final short DOCUMENT_POSITION_CONTAINED_BY = 0x10;
+ if (!(other instanceof NodeOverNodeInfo)) {
+ return DOCUMENT_POSITION_DISCONNECTED;
+ }
+ int c = node.compareOrder(((NodeOverNodeInfo)other).node);
+ if (c==0) {
+ return (short)0;
+ } else if (c==-1) {
+ short result = DOCUMENT_POSITION_FOLLOWING;
+ short d = compareDocumentPosition(other.getParentNode());
+ if (d==0 || (d&DOCUMENT_POSITION_CONTAINED_BY) != 0) {
+ result |= DOCUMENT_POSITION_CONTAINED_BY;
+ }
+ return result;
+ } else if (c==+1) {
+ short result = DOCUMENT_POSITION_PRECEDING;
+ short d = getParentNode().compareDocumentPosition(other);
+ if (d==0 || (d&DOCUMENT_POSITION_CONTAINS) != 0) {
+ result |= DOCUMENT_POSITION_CONTAINS;
+ }
+ return result;
+ } else {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Get the text content of a node. This is a DOM Level 3 method. The definition
+ * is the same as the definition of the string value of a node in XPath, except
+ * in the case of document nodes.
+ * @return the string value of the node, or null in the case of document nodes.
+ * @throws org.w3c.dom.DOMException
+ */
+
+ public String getTextContent() throws DOMException {
+ if (node.getNodeKind() == Type.DOCUMENT) {
+ return null;
+ } else {
+ return node.getStringValue();
+ }
+ }
+
+ /**
+ * Set the text content of a node. Always fails.
+ * @param textContent the new text content of the node
+ * @throws org.w3c.dom.DOMException
+ */
+
+ public void setTextContent(String textContent) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Get the (first) prefix assigned to a specified namespace URI, or null
+ * if the namespace is not in scope. DOM Level 3 method.
+ * @param namespaceURI the namespace whose prefix is required
+ * @return the corresponding prefix, if there is one, or null if not.
+ */
+
+ public String lookupPrefix(String namespaceURI){
+ if (node.getNodeKind() == Type.DOCUMENT) {
+ return null;
+ } else if (node.getNodeKind() == Type.ELEMENT) {
+ AxisIterator iter = node.iterateAxis(AxisInfo.NAMESPACE);
+ while (true) {
+ NodeInfo ns = iter.next();
+ if (ns==null) {
+ return null;
+ }
+ if (ns.getStringValue().equals(namespaceURI)) {
+ return ns.getLocalPart();
+ }
+ }
+ } else {
+ return getParentNode().lookupPrefix(namespaceURI);
+ }
+ }
+
+ /**
+ * Test whether a particular namespace is the default namespace.
+ * DOM Level 3 method.
+ * @param namespaceURI the namespace to be tested
+ * @return true if this is the default namespace
+ */
+
+ public boolean isDefaultNamespace(String namespaceURI) {
+ return namespaceURI.equals(lookupNamespaceURI(""));
+ }
+
+ /**
+ * Find the URI corresponding to a given in-scope prefix
+ * @param prefix The namespace prefix whose namespace URI is required.
+ * @return the corresponding namespace URI, or null if the prefix is
+ * not declared.
+ */
+
+ public String lookupNamespaceURI(String prefix) {
+ if (node.getNodeKind() == Type.DOCUMENT) {
+ return null;
+ } else if (node.getNodeKind() == Type.ELEMENT) {
+ AxisIterator iter = node.iterateAxis(AxisInfo.NAMESPACE);
+ while (true) {
+ NodeInfo ns = iter.next();
+ if (ns==null) {
+ return null;
+ }
+ if (ns.getLocalPart().equals(prefix)) {
+ return ns.getStringValue();
+ }
+ }
+ } else {
+ return getParentNode().lookupNamespaceURI(prefix);
+ }
+ }
+
+ /**
+ * Compare whether two nodes have the same content. This is a DOM Level 3 method.
+ * @param arg The node to be compared. This must wrap a Saxon NodeInfo.
+ * @return true if the two nodes are deep-equal.
+ */
+
+ public boolean isEqualNode(Node arg) {
+ if (!(arg instanceof NodeOverNodeInfo)) {
+ throw new IllegalArgumentException("Other Node must wrap a Saxon NodeInfo");
+ }
+ try {
+ XPathContext context = node.getConfiguration().getConversionContext();
+ return DeepEqual.deepEquals(
+ SingletonIterator.makeIterator(node),
+ SingletonIterator.makeIterator(((NodeOverNodeInfo)arg).node),
+ new GenericAtomicComparer(CodepointCollator.getInstance(),
+ context),
+ context,
+ DeepEqual.INCLUDE_PREFIXES |
+ DeepEqual.INCLUDE_COMMENTS |
+ DeepEqual.COMPARE_STRING_VALUES |
+ DeepEqual.INCLUDE_PROCESSING_INSTRUCTIONS);
+ } catch (XPathException err) {
+ // can't happen
+ return false;
+ }
+ }
+
+ /**
+ * Get a feature of this node. DOM Level 3 method, always returns null.
+ * @param feature the required feature
+ * @param version the version of the required feature
+ * @return the value of the feature. Always null in this implementation
+ */
+
+ public Object getFeature(String feature, String version) {
+ return null;
+ }
+
+ /**
+ * Set user data. Always throws UnsupportedOperationException in this implementation
+ * @param key name of the user data
+ * @param data value of the user data
+ * @param handler handler for the user data
+ * @return This implementation always throws an exception
+ */
+
+ public Object setUserData(String key, Object data, UserDataHandler handler) {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Get user data associated with this node. DOM Level 3 method, always returns
+ * null in this implementation
+ * @param key identifies the user data required
+ * @return always null in this implementation
+ */
+ public Object getUserData(String key) {
+ return null;
+ }
+
+ /**
+ * Internal method used to indicate that update operations are not allowed
+ * @throws org.w3c.dom.DOMException always, to indicate that update is not supported in this DOM implementation
+ */
+
+ protected static void disallowUpdate() throws DOMException {
+ throw new UnsupportedOperationException("The Saxon DOM implementation cannot be updated");
+ }
+
+}
+
diff --git a/sf/saxon/dom/PIOverNodeInfo.java b/sf/saxon/dom/PIOverNodeInfo.java
new file mode 100644
index 0000000..84576ad
--- /dev/null
+++ b/sf/saxon/dom/PIOverNodeInfo.java
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import org.w3c.dom.DOMException;
+import org.w3c.dom.ProcessingInstruction;
+
+/**
+ * This class is an implementation of the DOM ProcessingInstruction interface that wraps a Saxon NodeInfo
+ * representation of a text or comment node.
+ */
+
+public class PIOverNodeInfo extends NodeOverNodeInfo implements ProcessingInstruction {
+
+ /**
+ * The target of this processing instruction. XML defines this as being
+ * the first token following the markup that begins the processing
+ * instruction.
+ */
+ public String getTarget() {
+ return node.getLocalPart();
+ }
+
+ /**
+ * The content of this processing instruction. This is from the first non
+ * white space character after the target to the character immediately
+ * preceding the <code>?></code>.
+ */
+ public String getData() {
+ return node.getStringValue();
+ }
+
+ /**
+ * The content of this processing instruction. This is from the first non
+ * white space character after the target to the character immediately
+ * preceding the <code>?></code>.
+ *
+ * @throws org.w3c.dom.DOMException NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+ */
+ public void setData(String data) throws DOMException {
+ disallowUpdate();
+ }
+}
+
diff --git a/sf/saxon/dom/TextOverAttrInfo.java b/sf/saxon/dom/TextOverAttrInfo.java
new file mode 100644
index 0000000..beeee47
--- /dev/null
+++ b/sf/saxon/dom/TextOverAttrInfo.java
@@ -0,0 +1,86 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.type.Type;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Node;
+
+/**
+ * This class represents a DOM text node that is the child of a DOM attribute node. The DOM attribute node
+ * will be a wrapper over a Saxon attribute node or namespace node.
+ */
+public class TextOverAttrInfo extends TextOverNodeInfo {
+
+ private AttrOverNodeInfo attr;
+
+ public TextOverAttrInfo(AttrOverNodeInfo attr) {
+ this.attr = attr;
+ this.node = attr.getUnderlyingNodeInfo();
+ }
+
+ /**
+ * Returns whether this text node contains <a href='http://www.w3.org/TR/2004/REC-xml-infoset-20040204#infoitem.character'>
+ * element content whitespace</a>, often abusively called "ignorable whitespace". The text node is
+ * determined to contain whitespace in element content during the load
+ * of the document or if validation occurs while using
+ * <code>Document.normalizeDocument()</code>.
+ *
+ * @since DOM Level 3
+ */
+ public boolean isElementContentWhitespace() {
+ return false;
+ }
+
+ /**
+ * Get the type of this node (node kind, in XPath terminology).
+ * Note, the numbers assigned to node kinds
+ * in Saxon (see {@link net.sf.saxon.type.Type}) are the same as those assigned in the DOM
+ */
+
+ public short getNodeType() {
+ return Type.TEXT;
+ }
+
+ /**
+ * Compare the position of the (other) node in document order with the reference node (this node).
+ * DOM Level 3 method.
+ *
+ * @param other the other node.
+ * @return Returns how the node is positioned relatively to the reference
+ * node.
+ * @throws org.w3c.dom.DOMException
+ */
+
+ public short compareDocumentPosition(Node other) throws DOMException {
+ final short DOCUMENT_POSITION_FOLLOWING = 0x04;
+ if (other instanceof TextOverAttrInfo) {
+ if (node.isSameNodeInfo(((TextOverAttrInfo)other).node)) {
+ return 0;
+ } else {
+ return attr.compareDocumentPosition(((TextOverAttrInfo)other).attr);
+ }
+ } else if (other instanceof AttrOverNodeInfo) {
+ if (node.isSameNodeInfo(((AttrOverNodeInfo)other).getUnderlyingNodeInfo())) {
+ return DOCUMENT_POSITION_FOLLOWING;
+ }
+ }
+ return attr.compareDocumentPosition(other);
+ }
+
+ /**
+ * Find the parent node of this node.
+ *
+ * @return The Node object describing the containing element or root node.
+ */
+
+ public Node getParentNode() {
+ return attr;
+ }
+}
+
diff --git a/sf/saxon/dom/TextOverNodeInfo.java b/sf/saxon/dom/TextOverNodeInfo.java
new file mode 100644
index 0000000..b2eb4de
--- /dev/null
+++ b/sf/saxon/dom/TextOverNodeInfo.java
@@ -0,0 +1,233 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.type.ComplexType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Whitespace;
+import org.w3c.dom.Comment;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Text;
+
+/**
+ * This class is an implementation of the DOM Text and Comment interfaces that wraps a Saxon NodeInfo
+ * representation of a text or comment node.
+ */
+
+public class TextOverNodeInfo extends NodeOverNodeInfo implements Text, Comment {
+
+
+ /**
+ * Get the character data of a Text or Comment node.
+ * DOM method.
+ */
+
+ public String getData() {
+ return node.getStringValue();
+ }
+
+ /**
+ * Set the character data of a Text or Comment node.
+ * DOM method: always fails, Saxon tree is immutable.
+ */
+
+ public void setData(String data) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Get the length of a Text or Comment node.
+ * DOM method.
+ */
+
+ public int getLength() {
+ return node.getStringValue().length();
+ }
+
+ /**
+ * Extract a range of data from a Text or Comment node. DOM method.
+ * @param offset Start offset of substring to extract.
+ * @param count The number of 16-bit units to extract.
+ * @return The specified substring. If the sum of <code>offset</code> and
+ * <code>count</code> exceeds the <code>length</code> , then all 16-bit
+ * units to the end of the data are returned.
+ * @exception org.w3c.dom.DOMException
+ * INDEX_SIZE_ERR: Raised if the specified <code>offset</code> is
+ * negative or greater than the number of 16-bit units in
+ * <code>data</code> , or if the specified <code>count</code> is
+ * negative.
+ */
+
+ public String substringData(int offset, int count) throws DOMException {
+ try {
+ return node.getStringValue().substring(offset, offset+count);
+ } catch (IndexOutOfBoundsException err2) {
+ throw new DOMExceptionImpl(DOMException.INDEX_SIZE_ERR,
+ "substringData: index out of bounds");
+ }
+ }
+
+ /**
+ * Append the string to the end of the character data of the node.
+ * DOM method: always fails.
+ * @param arg The <code>DOMString</code> to append.
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ */
+
+ public void appendData(String arg) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Insert a string at the specified character offset.
+ * DOM method: always fails.
+ * @param offset The character offset at which to insert.
+ * @param arg The <code>DOMString</code> to insert.
+ * @exception org.w3c.dom.DOMException
+ */
+
+ public void insertData(int offset, String arg) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Remove a range of 16-bit units from the node.
+ * DOM method: always fails.
+ * @param offset The offset from which to start removing.
+ * @param count The number of 16-bit units to delete.
+ * @exception org.w3c.dom.DOMException
+ */
+
+ public void deleteData(int offset, int count) throws DOMException {
+ disallowUpdate();
+ }
+
+ /**
+ * Replace the characters starting at the specified 16-bit unit offset
+ * with the specified string. DOM method: always fails.
+ * @param offset The offset from which to start replacing.
+ * @param count The number of 16-bit units to replace.
+ * @param arg The <code>DOMString</code> with which the range must be
+ * replaced.
+ * @exception org.w3c.dom.DOMException
+ * NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ */
+
+ public void replaceData(int offset,
+ int count,
+ String arg) throws DOMException {
+ disallowUpdate();
+ }
+
+
+ /**
+ * Break this node into two nodes at the specified offset,
+ * keeping both in the tree as siblings. DOM method, always fails.
+ * @param offset The 16-bit unit offset at which to split, starting from 0.
+ * @return The new node, of the same type as this node.
+ * @exception org.w3c.dom.DOMException
+ */
+
+ public Text splitText(int offset) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Replaces the text of the current node and all logically-adjacent text
+ * nodes with the specified text. All logically-adjacent text nodes are
+ * removed including the current node unless it was the recipient of the
+ * replacement text.
+ * <br>This method returns the node which received the replacement text.
+ * The returned node is:
+ * <ul>
+ * <li><code>null</code>, when the replacement text is
+ * the empty string;
+ * </li>
+ * <li>the current node, except when the current node is
+ * read-only;
+ * </li>
+ * <li> a new <code>Text</code> node of the same type (
+ * <code>Text</code> or <code>CDATASection</code>) as the current node
+ * inserted at the location of the replacement.
+ * </li>
+ * </ul>
+ * <br>For instance, in the above example calling
+ * <code>replaceWholeText</code> on the <code>Text</code> node that
+ * contains "bar" with "yo" in argument results in the following:
+ * <br>Where the nodes to be removed are read-only descendants of an
+ * <code>EntityReference</code>, the <code>EntityReference</code> must
+ * be removed instead of the read-only nodes. If any
+ * <code>EntityReference</code> to be removed has descendants that are
+ * not <code>EntityReference</code>, <code>Text</code>, or
+ * <code>CDATASection</code> nodes, the <code>replaceWholeText</code>
+ * method must fail before performing any modification of the document,
+ * raising a <code>DOMException</code> with the code
+ * <code>NO_MODIFICATION_ALLOWED_ERR</code>.
+ * <br>For instance, in the example below calling
+ * <code>replaceWholeText</code> on the <code>Text</code> node that
+ * contains "bar" fails, because the <code>EntityReference</code> node
+ * "ent" contains an <code>Element</code> node which cannot be removed.
+ *
+ * @param content The content of the replacing <code>Text</code> node.
+ * @return The <code>Text</code> node created with the specified content.
+ * @throws org.w3c.dom.DOMException NO_MODIFICATION_ALLOWED_ERR: Raised if one of the <code>Text</code>
+ * nodes being replaced is readonly.
+ * @since DOM Level 3
+ */
+ /*@Nullable*/ public Text replaceWholeText(String content) throws DOMException {
+ disallowUpdate();
+ return null;
+ }
+
+ /**
+ * Returns whether this text node contains <a href='http://www.w3.org/TR/2004/REC-xml-infoset-20040204#infoitem.character'>
+ * element content whitespace</a>, often abusively called "ignorable whitespace". The text node is
+ * determined to contain whitespace in element content during the load
+ * of the document or if validation occurs while using
+ * <code>Document.normalizeDocument()</code>.
+ *
+ * @since DOM Level 3
+ */
+ public boolean isElementContentWhitespace() {
+ if (node.getNodeKind() != Type.TEXT) {
+ throw new UnsupportedOperationException("Method is defined only on text nodes");
+ }
+ if (!Whitespace.isWhite(node.getStringValue())) {
+ return false;
+ }
+ NodeInfo parent = node.getParent();
+ if (parent == null) {
+ return false;
+ }
+ SchemaType type = parent.getSchemaType();
+ return type.isComplexType() && !((ComplexType) type).isMixedContent();
+ }
+
+ /**
+ * Returns all text of <code>Text</code> nodes logically-adjacent text
+ * nodes to this node, concatenated in document order.
+ * <br>For instance, in the example below <code>wholeText</code> on the
+ * <code>Text</code> node that contains "bar" returns "barfoo", while on
+ * the <code>Text</code> node that contains "foo" it returns "barfoo".
+ *
+ * @since DOM Level 3
+ */
+ public String getWholeText() {
+ if (node.getNodeKind() != Type.TEXT) {
+ throw new UnsupportedOperationException("Method is defined only on text nodes");
+ }
+ return node.getStringValue();
+ }
+
+
+}
+
diff --git a/sf/saxon/dom/TypeInfoImpl.java b/sf/saxon/dom/TypeInfoImpl.java
new file mode 100644
index 0000000..4279ef6
--- /dev/null
+++ b/sf/saxon/dom/TypeInfoImpl.java
@@ -0,0 +1,83 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.dom;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.type.AnyType;
+import net.sf.saxon.type.SchemaType;
+import org.w3c.dom.TypeInfo;
+
+/**
+ * This class implements the DOM TypeInfo interface as a wrapper over the Saxon SchemaType
+ * interface.
+ */
+
+public class TypeInfoImpl implements TypeInfo {
+
+ private Configuration config;
+ private SchemaType schemaType;
+
+ /**
+ * Construct a TypeInfo based on a SchemaType
+ */
+
+ public TypeInfoImpl(Configuration config, SchemaType type) {
+ this.config = config;
+ this.schemaType = type;
+ }
+
+ /**
+ * Get the local name of the type (a system-allocated name if anonymous). Needed to implement the
+ * DOM level 3 TypeInfo interface.
+ */
+
+ public String getTypeName() {
+ return config.getNamePool().getLocalName(schemaType.getNameCode());
+ }
+
+ /**
+ * Get the namespace name of the type (a system-allocated name if anonymous). Needed to implement the
+ * DOM level 3 TypeInfo interface.
+ */
+
+ public String getTypeNamespace() {
+ return config.getNamePool().getURI(schemaType.getNameCode());
+ }
+
+ /**
+ * This method returns true if there is a derivation between the reference type definition, that is the TypeInfo
+ * on which the method is being called, and the other type definition, that is the one passed as parameters.
+ * This method implements the DOM Level 3 TypeInfo interface. It must be called only on a valid type.
+ * @param typeNamespaceArg the namespace of the "other" type
+ * @param typeNameArg the local name of the "other" type
+ * @param derivationMethod the derivation method: zero or more of {@link SchemaType#DERIVATION_RESTRICTION},
+ * {@link SchemaType#DERIVATION_EXTENSION}, {@link SchemaType#DERIVATION_LIST}, or {@link SchemaType#DERIVATION_UNION}.
+ * Zero means derived by any possible route.
+ */
+
+ public boolean isDerivedFrom(String typeNamespaceArg,
+ String typeNameArg,
+ int derivationMethod) throws IllegalStateException {
+ SchemaType base = schemaType.getBaseType();
+ int fingerprint = config.getNamePool().allocate("", typeNamespaceArg, typeNameArg);
+ if (derivationMethod==0 || (derivationMethod & schemaType.getDerivationMethod()) != 0) {
+ if (base.getFingerprint() == fingerprint) {
+ return true;
+ } else if (base instanceof AnyType) {
+ return false;
+ } else {
+ return new TypeInfoImpl(config, base).isDerivedFrom(typeNamespaceArg, typeNameArg, derivationMethod);
+ }
+ }
+ return false;
+ // Note: if derivationMethod is RESTRICTION, this interpretation requires every step to be derived
+ // by restriction. An alternative interpretation is that at least one step must be derived by restriction.
+ }
+
+}
+
diff --git a/sf/saxon/dom/package.html b/sf/saxon/dom/package.html
new file mode 100644
index 0000000..ec7f06c
--- /dev/null
+++ b/sf/saxon/dom/package.html
@@ -0,0 +1,61 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.dom</title>
+</head>
+
+<body>
+
+<p>This package provides glue classes that enable Saxon to process a source
+document supplied as a DOM tree in the form of a DOMSource object; it also provides
+classes that present a DOM view of Saxon's native tree structures.</p>
+
+<p>The native Saxon tree structures (the linked tree and tiny tree) do not
+implement DOM interfaces directly. However, Saxon supports the DOM at two levels:</p>
+
+<ul>
+<li><p>The input to a transformation or query may be supplied in the form of
+a <code>DOMSource</code> (which contains a DOM document). Saxon is capable of either performing the
+query or transformation on the DOM <i>in situ</i>, by wrapping the DOM nodes in
+a layer that make them appear to be Saxon nodes, or of converting the DOM to Saxon's native tree
+implementation. </p></li>
+<li><p>It is possible for a transformation or query to call extension functions
+that use DOM interfaces to access a Saxon tree. If the Saxon tree is in fact a wrapper
+around the DOM, then extension functions will be presented with the underlying
+DOM nodes. In other cases, Saxon adds a wrapper to the native Saxon nodes to make
+them implement the DOM interfaces.</p>
+<p>Note that Saxon's tree structures are immutable. Updating interfaces
+in the DOM API are therefore not supported.</p></li></ul>
+
+<p>The classes {@link net.sf.saxon.dom.NodeWrapper} and {@link net.sf.saxon.dom.DocumentWrapper} implement the Saxon interfaces
+{@link net.sf.saxon.om.NodeInfo} and {@link net.sf.saxon.om.DocumentInfo} on top of an underlying DOM
+ <code>Node</code> or <code>Document</code> object respectively. This enables XPath expressions to be executed directly against
+the DOM.</p>
+
+<p>The classes {@link net.sf.saxon.dom.NodeOverNodeInfo},
+ {@link net.sf.saxon.dom.DocumentOverNodeInfo}, and the like do the converse:
+they provide a DOM wrapper over a native Saxon node.</p>
+
+<p><b>Note that using the DOM with Saxon is considerably less efficient than using
+Saxon's native tree implementations, the TinyTree and the LinkedTree.
+The DOM should be used only where there is some good reason, e.g. where other parts
+of the application have to use DOM interfaces.</b></p>
+
+<p>Saxon doesn't stop you modifying the contents of the DOM in the course of a
+transformation (for example, from an extension function, or in a different thread)
+but the consequences of doing so are unpredictable.</p>
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+July 2010</i></p>
+</body>
+</html>
diff --git a/sf/saxon/event/Builder.java b/sf/saxon/event/Builder.java
new file mode 100644
index 0000000..f13b974
--- /dev/null
+++ b/sf/saxon/event/Builder.java
@@ -0,0 +1,258 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.TinyDocumentImpl;
+
+import java.util.Date;
+
+/**
+ * The abstract Builder class is responsible for taking a stream of SAX events
+ * and constructing a Document tree. There is one concrete subclass for each
+ * tree implementation.
+ * @author Michael H. Kay
+ */
+
+public abstract class Builder implements Receiver {
+ /**
+ * Constant denoting a request for the default tree model
+ */
+ public static final int UNSPECIFIED_TREE_MODEL = -1;
+ /**
+ * Constant denoting the "linked tree" in which each node is represented as an object
+ */
+ public static final int LINKED_TREE = 0;
+ /**
+ * Alternative constant denoting the "linked tree" in which each node is represented as an object
+ * Retained for backwards compatibility
+ */
+ public static final int STANDARD_TREE = 0;
+ /**
+ * Constant denoting the "tiny tree" in which the tree is represented internally using arrays of integers
+ */
+ public static final int TINY_TREE = 1;
+ /**
+ * Constant denoting the "tiny tree condensed", a variant of the tiny tree in which text and attribute nodes
+ * sharing the same string value use shared storage for the value.
+ */
+ public static final int TINY_TREE_CONDENSED = 2;
+
+ public static final int JDOM_TREE = 3;
+ public static final int JDOM2_TREE = 4;
+ public static final int AXIOM_TREE = 5;
+
+ /*@NotNull*/ protected PipelineConfiguration pipe;
+ /*@NotNull*/ protected Configuration config;
+ /*@NotNull*/ protected NamePool namePool;
+ /*@Nullable*/ protected String systemId;
+ /*@Nullable*/ protected String baseURI;
+ /*@Nullable*/ protected NodeInfo currentRoot;
+ protected boolean lineNumbering = false;
+
+ protected boolean started = false;
+ protected boolean timing = false;
+ protected boolean open = false;
+
+ private long startTime;
+
+ /**
+ * Create a Builder and initialise variables
+ */
+
+ public Builder() {
+ }
+
+ public Builder(PipelineConfiguration pipe) {
+ this.pipe = pipe;
+ config = pipe.getConfiguration();
+ lineNumbering = config.isLineNumbering() && (pipe.getLocationProvider() != null);
+ namePool = config.getNamePool();
+ }
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipe) {
+ //System.err.println("Builder#setPipelineConfiguration pipe = " + pipe);
+// if (pipe == null) {
+// new NullPointerException("pipe not initialized").printStackTrace();
+// }
+ this.pipe = pipe;
+ config = pipe.getConfiguration();
+ lineNumbering = (lineNumbering || config.isLineNumbering()) && (pipe.getLocationProvider() != null);
+ namePool = config.getNamePool();
+ }
+
+ /*@NotNull*/
+ public PipelineConfiguration getPipelineConfiguration () {
+ return pipe;
+ }
+
+ /**
+ * Get the Configuration
+ * @return the Saxon configuration
+ */
+
+ /*@NotNull*/
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get a builder monitor for this builder. This must be called immediately after opening the builder,
+ * and all events to the builder must thenceforth be sent via the BuilderMonitor.
+ * @return a new BuilderMonitor appropriate to this kind of Builder; or null if the Builder does
+ * not provide this service. The default implementation returns null.
+ */
+
+ /*@Nullable*/ public BuilderMonitor getBuilderMonitor() {
+ return null;
+ }
+
+ /**
+ * The SystemId is equivalent to the document-uri property defined in the XDM data model.
+ * It should be set only in the case of a document that is potentially retrievable via this URI.
+ * This means it should not be set in the case of a temporary tree constructed in the course of
+ * executing a query or transformation.
+ * @param systemId the SystemId, that is, the document-uri.
+ */
+
+ public void setSystemId(/*@Nullable*/ String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * The SystemId is equivalent to the document-uri property defined in the XDM data model.
+ * It should be set only in the case of a document that is potentially retrievable via this URI.
+ * This means the value will be null in the case of a temporary tree constructed in the course of
+ * executing a query or transformation.
+ * @return the SystemId, that is, the document-uri.
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Set the base URI of the document node of the tree being constructed by this builder
+ * @param baseURI the base URI
+ */
+
+ public void setBaseURI(/*@Nullable*/ String baseURI) {
+ this.baseURI = baseURI;
+ }
+
+ /**
+ * Get the base URI of the document node of the tree being constructed by this builder
+ * @return the base URI
+ */
+
+ /*@Nullable*/ public String getBaseURI() {
+ return baseURI;
+ }
+
+
+
+ /////////////////////////////////////////////////////////////////////////
+ // Methods setting and getting options for building the tree
+ /////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Set line numbering on or off
+ * @param lineNumbering set to true if line numbers are to be maintained for nodes in the tree being
+ * constructed.
+ */
+
+ public void setLineNumbering(boolean lineNumbering) {
+ this.lineNumbering = lineNumbering;
+ }
+
+ /**
+ * Set timing option on or off
+ * @param on set to true to turn timing on. This causes the builder to display statistical information
+ * about the tree that is constructed. It corresponds to the command line -t option
+ */
+
+ public void setTiming(boolean on) {
+ timing = on;
+ }
+
+ /**
+ * Get timing option
+ * @return true if timing information has been requested
+ */
+
+ public boolean isTiming() {
+ return timing;
+ }
+
+ public void open() {
+ if (timing && !open) {
+ getConfiguration().getStandardErrorOutput().println(
+ "Building tree for " + getSystemId() + " using " + getClass());
+ startTime = (new Date()).getTime();
+ }
+ open = true;
+ }
+
+ public void close() throws XPathException {
+ if (timing && open) {
+ long endTime = (new Date()).getTime();
+ getConfiguration().getStandardErrorOutput().println(
+ "Tree built in " + (endTime - startTime) + " milliseconds");
+ if (currentRoot instanceof TinyDocumentImpl) {
+ ((TinyDocumentImpl)currentRoot).showSize();
+ }
+ startTime = endTime;
+ }
+ open = false;
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return true;
+ }
+
+ /**
+ * Get the current root node. This will normally be a document node, but if the root of the tree
+ * is an element node, it can be an element.
+ * @return the root of the tree that is currently being built, or that has been most recently built
+ * using this builder
+ */
+
+ /*@Nullable*/ public NodeInfo getCurrentRoot() {
+ return currentRoot;
+ }
+
+ /**
+ * Reset the builder to its initial state. The most important effect of calling this
+ * method (implemented in subclasses) is to release any links to the constructed document
+ * tree, allowing the memory occupied by the tree to released by the garbage collector even
+ * if the Builder is still in memory. This can happen because the Builder is referenced from a
+ * parser in the Configuration's parser pool.
+ */
+
+ public void reset() {
+ systemId = null;
+ baseURI = null;
+ currentRoot = null;
+ lineNumbering = false;
+ started = false;
+ timing = false;
+ open = false;
+ }
+
+}
+
diff --git a/sf/saxon/event/BuilderMonitor.java b/sf/saxon/event/BuilderMonitor.java
new file mode 100644
index 0000000..c4ac710
--- /dev/null
+++ b/sf/saxon/event/BuilderMonitor.java
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.NodeInfo;
+
+/**
+ * A BuilderMonitor can be inserted into a pipeline immediately in front of a Builder. During tree construction,
+ * the method markNextNode() can be called to request that the next node to be created is treated specially by
+ * remembering the current position on the tree; on completion of the tree construction, the method getMarkedNode()
+ * can be called to return the NodeInfo that was created immediately after calling markNextNode().
+ */
+public abstract class BuilderMonitor extends ProxyReceiver {
+
+ public BuilderMonitor(Receiver next) {
+ super(next);
+ }
+
+ /**
+ * Indicate that the next node to be created will be of a given type, and request the monitor to remember
+ * the identity of this node.
+ * @param nodeKind the kind of node that will be created next
+ */
+
+ public abstract void markNextNode(int nodeKind);
+
+ /**
+ * On completion of tree building, get the node that was marked using markNextNode().
+ * @return the marked node, or null if none was marked
+ */
+
+ public abstract NodeInfo getMarkedNode();
+}
+
diff --git a/sf/saxon/event/CommentStripper.java b/sf/saxon/event/CommentStripper.java
new file mode 100644
index 0000000..aa91816
--- /dev/null
+++ b/sf/saxon/event/CommentStripper.java
@@ -0,0 +1,107 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CompressedWhitespace;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.SchemaType;
+
+/**
+ * The CommentStripper class is a filter that removes all comments and processing instructions.
+ * It also concatenates text nodes that are split by comments and PIs. This follows the rules for
+ * processing stylesheets; it is also used for removing comments and PIs from the tree seen
+ * by XPath expressions used to process XSD 1.1 assertions
+ * @author Michael H. Kay
+ */
+
+
+public class CommentStripper extends ProxyReceiver {
+
+ /*@Nullable*/ private CompressedWhitespace savedWhitespace = null;
+ private FastStringBuffer buffer = new FastStringBuffer(FastStringBuffer.MEDIUM);
+
+ /**
+ * Default constructor for use in subclasses
+ * @param next the next receiver in the pipeline
+ */
+
+ public CommentStripper(Receiver next) {
+ super(next);
+ }
+
+ public void startElement (NodeName nameCode, SchemaType typeCode, int locationId, int properties)
+ throws XPathException {
+ flush();
+ nextReceiver.startElement(nameCode, typeCode, locationId, properties);
+ }
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void endElement () throws XPathException {
+ flush();
+ nextReceiver.endElement();
+ }
+
+ /**
+ * Handle a text node. Because we're often handling stylesheets on this path, whitespace text
+ * nodes will often be stripped but we can't strip them immediately because of the case
+ * [element] [!-- comment --]text[/element], where the space before the comment is considered
+ * significant. But it's worth going to some effort to avoid uncompressing the whitespace in the
+ * more common case, so that it can easily be detected and stripped downstream.
+ */
+
+ public void characters (CharSequence chars, int locationId, int properties) throws XPathException {
+ if (chars instanceof CompressedWhitespace) {
+ if (buffer.length() == 0 && savedWhitespace == null) {
+ savedWhitespace = (CompressedWhitespace)chars;
+ } else {
+ ((CompressedWhitespace)chars).uncompress(buffer);
+ }
+ } else {
+ if (savedWhitespace != null) {
+ savedWhitespace.uncompress(buffer);
+ savedWhitespace = null;
+ }
+ buffer.append(chars);
+ }
+
+ }
+
+ /**
+ * Remove comments
+ */
+
+ public void comment (CharSequence chars, int locationId, int properties) {}
+
+ /**
+ * Remove processing instructions
+ */
+
+ public void processingInstruction(String name, CharSequence data, int locationId, int properties) {}
+
+ /**
+ * Flush the character buffer
+ * @throws net.sf.saxon.trans.XPathException if a failure occurs writing the output
+ */
+
+ private void flush() throws XPathException {
+ if (buffer.length() > 0) {
+ nextReceiver.characters(buffer, 0, 0);
+ } else if (savedWhitespace != null) {
+ nextReceiver.characters(savedWhitespace, 0, 0);
+ }
+ savedWhitespace = null;
+ buffer.setLength(0);
+ }
+
+}
+
diff --git a/sf/saxon/event/ComplexContentOutputter.java b/sf/saxon/event/ComplexContentOutputter.java
new file mode 100644
index 0000000..8bf3aa8
--- /dev/null
+++ b/sf/saxon/event/ComplexContentOutputter.java
@@ -0,0 +1,599 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.ObjectValue;
+
+/**
+ * This class is used for generating complex content, that is, the content of an
+ * element or document node. It enforces the rules on the order of events within
+ * complex content (attributes and namespaces must come first), and it implements
+ * part of the namespace fixup rules, in particular, it ensures that there is a
+ * namespace node for the namespace used in the element name and in each attribute
+ * name.
+ *
+ * <p>The same ComplexContentOutputter may be used for generating an entire XML
+ * document; it is not necessary to create a new outputter for each element node.</p>
+ *
+ * @author Michael H. Kay
+ */
+
+public final class ComplexContentOutputter extends SequenceReceiver {
+
+ private Receiver nextReceiver;
+ // the next receiver in the output pipeline
+
+ private int pendingStartTagDepth = -2;
+ // -2 means we are at the top level, or immediately within a document node
+ // -1 means we are in the content of an element node whose start tag is complete
+ private NodeName pendingStartTag = null;
+ private int level = -1; // records the number of startDocument or startElement events
+ // that have not yet been closed. Note that startDocument and startElement
+ // events may be arbitrarily nested; startDocument and endDocument
+ // are ignored unless they occur at the outermost level, except that they
+ // still change the level number
+ private boolean[] currentLevelIsDocument = new boolean[20];
+ private Boolean elementIsInNullNamespace;
+ private NodeName[] pendingAttCode = new NodeName[20];
+ private SimpleType[] pendingAttType = new SimpleType[20];
+ private String[] pendingAttValue = new String[20];
+ private int[] pendingAttLocation = new int[20];
+ private int[] pendingAttProp = new int[20];
+ private int pendingAttListSize = 0;
+
+ private NamespaceBinding[] pendingNSList = new NamespaceBinding[20];
+ private int pendingNSListSize = 0;
+
+ private SchemaType currentSimpleType = null; // any other value means we are currently writing an
+ // element of a particular simple type
+
+ private int startElementProperties;
+ private int startElementLocationId = -1;
+ private boolean declaresDefaultNamespace;
+ private int hostLanguage = Configuration.XSLT;
+ private boolean started = false;
+
+ /**
+ * Create a ComplexContentOutputter
+ * @param pipe the pipeline configuration
+ */
+
+ public ComplexContentOutputter(/*@NotNull*/ PipelineConfiguration pipe) {
+ super(pipe);
+ //System.err.println("ComplexContentOutputter init");
+ }
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipe) {
+ if (pipelineConfiguration != pipe) {
+ pipelineConfiguration = pipe;
+ if (nextReceiver != null) {
+ nextReceiver.setPipelineConfiguration(pipe);
+ }
+ }
+ }
+
+ /**
+ * Set the host language
+ * @param language the host language, for example {@link Configuration#XQUERY}
+ */
+
+ public void setHostLanguage(int language) {
+ hostLanguage = language;
+ }
+
+ /**
+ * Set the receiver (to handle the next stage in the pipeline) directly
+ * @param receiver the receiver to handle the next stage in the pipeline
+ */
+
+ public void setReceiver(Receiver receiver) {
+ this.nextReceiver = receiver;
+ }
+
+ /**
+ * Start the output process
+ */
+
+ public void open() throws XPathException {
+ nextReceiver.open();
+ previousAtomic = false;
+ }
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ level++;
+ if (level == 0) {
+ nextReceiver.startDocument(properties);
+ } else if (pendingStartTagDepth >= 0) {
+ startContent();
+ pendingStartTagDepth = -2;
+ }
+ previousAtomic = false;
+ if (currentLevelIsDocument.length < level+1) {
+ boolean[] b2 = new boolean[level*2];
+ System.arraycopy(currentLevelIsDocument, 0, b2, 0, level);
+ currentLevelIsDocument = b2;
+ }
+ currentLevelIsDocument[level] = true;
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ if (level == 0) {
+ nextReceiver.endDocument();
+ }
+ previousAtomic = false;
+ level--;
+ }
+
+
+
+ /**
+ * Produce text content output. <BR>
+ * Special characters are escaped using XML/HTML conventions if the output format
+ * requires it.
+ * @param s The String to be output
+ * @exception XPathException for any failure
+ */
+
+ public void characters(CharSequence s, int locationId, int properties) throws XPathException {
+ previousAtomic = false;
+ if (s==null) return;
+ int len = s.length();
+ if (len==0) return;
+ if (pendingStartTagDepth >= 0) {
+ startContent();
+ }
+ nextReceiver.characters(s, locationId, properties);
+ }
+
+ /**
+ * Output an element start tag. <br>
+ * The actual output of the tag is deferred until all attributes have been output
+ * using attribute().
+ * @param elemName The element name
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ //System.err.println("CCO " + this + "StartElement " + nameCode);
+ level++;
+ started = true;
+ if (pendingStartTagDepth >= 0) {
+ startContent();
+ }
+ startElementProperties = properties;
+ startElementLocationId = locationId;
+ pendingAttListSize = 0;
+ pendingNSListSize = 0;
+ pendingStartTag = elemName;
+ pendingStartTagDepth = 1;
+ elementIsInNullNamespace = null; // meaning not yet computed
+ declaresDefaultNamespace = false;
+ currentSimpleType = typeCode;
+ previousAtomic = false;
+ if (currentLevelIsDocument.length < level+1) {
+ boolean[] b2 = new boolean[level*2];
+ System.arraycopy(currentLevelIsDocument, 0, b2, 0, level);
+ currentLevelIsDocument = b2;
+ }
+ currentLevelIsDocument[level] = false;
+ }
+
+
+ /**
+ * Output a namespace declaration. <br>
+ * This is added to a list of pending namespaces for the current start tag.
+ * If there is already another declaration of the same prefix, this one is
+ * ignored, unless the REJECT_DUPLICATES flag is set, in which case this is an error.
+ * Note that unlike SAX2 startPrefixMapping(), this call is made AFTER writing the start tag.
+ * @param nsBinding The namespace binding
+ * @throws XPathException if there is no start tag to write to (created using writeStartTag),
+ * or if character content has been written since the start tag was written.
+ */
+
+ public void namespace(NamespaceBinding nsBinding, int properties)
+ throws XPathException {
+
+ // System.err.println("Write namespace prefix=" + (nscode>>16) + " uri=" + (nscode&0xffff));
+ if (pendingStartTagDepth < 0) {
+ LocationProvider lp = getPipelineConfiguration().getLocationProvider();
+ throw NoOpenStartTagException.makeNoOpenStartTagException(
+ Type.NAMESPACE,
+ nsBinding.getPrefix(),
+ hostLanguage,
+ pendingStartTagDepth == -2,
+ getPipelineConfiguration().isSerializing(),
+ lp,
+ startElementLocationId);
+ }
+
+ // elimination of namespaces already present on an outer element of the
+ // result tree is done by the NamespaceReducer.
+
+ // Handle declarations whose prefix is duplicated for this element.
+
+ boolean rejectDuplicates = (properties & ReceiverOptions.REJECT_DUPLICATES) != 0;
+
+ for (int i=0; i<pendingNSListSize; i++) {
+ if (nsBinding.equals(pendingNSList[i])) {
+ // same prefix and URI: ignore this duplicate
+ return;
+ }
+ if (nsBinding.getPrefix().equals(pendingNSList[i].getPrefix())) {
+ if (pendingNSList[i].isDefaultUndeclaration() || nsBinding.isDefaultUndeclaration()) {
+ // xmlns="" overridden by xmlns="abc"
+ pendingNSList[i] = nsBinding;
+ } else if (rejectDuplicates) {
+ String prefix = nsBinding.getPrefix();
+ String uri1 = nsBinding.getURI();
+ String uri2 = pendingNSList[i].getURI();
+ XPathException err = new XPathException("Cannot create two namespace nodes with the same prefix mapped to different URIs (prefix=" +
+ (prefix.length() == 0 ? "\"\"" : prefix) + ", URI=" +
+ (uri1.length() == 0 ? "\"\"" : uri1) + ", URI=" +
+ (uri2.length() == 0 ? "\"\"" : uri2) + ")");
+ err.setErrorCode(hostLanguage == Configuration.XSLT ? "XTDE0430" : "XQDY0102");
+ throw err;
+ } else {
+ // same prefix, do a quick exit
+ return;
+ }
+ }
+ }
+
+ // It is an error to output a namespace node for the default namespace if the element
+ // itself is in the null namespace, as the resulting element could not be serialized
+
+ if ((nsBinding.getPrefix().length()==0) && (nsBinding.getURI().length()!=0)) {
+ declaresDefaultNamespace = true;
+ if (elementIsInNullNamespace == null) {
+ elementIsInNullNamespace = Boolean.valueOf(pendingStartTag.isInNamespace(""));
+ }
+ if (elementIsInNullNamespace) {
+ XPathException err = new XPathException("Cannot output a namespace node for the default namespace when the element is in no namespace");
+ err.setErrorCode("XTDE0440");
+ throw err;
+ }
+ }
+
+ // if it's not a duplicate namespace, add it to the list for this start tag
+
+ if (pendingNSListSize+1 > pendingNSList.length) {
+ NamespaceBinding[] newlist = new NamespaceBinding[pendingNSListSize * 2];
+ System.arraycopy(pendingNSList, 0, newlist, 0, pendingNSListSize);
+ pendingNSList = newlist;
+ }
+ pendingNSList[pendingNSListSize++] = nsBinding;
+ previousAtomic = false;
+ }
+
+
+ /**
+ * Output an attribute value. <br>
+ * This is added to a list of pending attributes for the current start tag, overwriting
+ * any previous attribute with the same name. <br>
+ * This method should NOT be used to output namespace declarations.<br>
+ *
+ *
+ * @param attName The name of the attribute
+ * @param value The value of the attribute
+ * @param properties Bit fields containing properties of the attribute to be written
+ * @throws XPathException if there is no start tag to write to (created using writeStartTag),
+ * or if character content has been written since the start tag was written.
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ //System.err.println("Write attribute " + nameCode + "=" + value + " to Outputter " + this);
+ if (pendingStartTagDepth < 0) {
+ // The complexity here is in identifying the right error message and error code
+
+ LocationProvider lp = getPipelineConfiguration().getLocationProvider();
+ XPathException err = NoOpenStartTagException.makeNoOpenStartTagException(
+ Type.ATTRIBUTE,
+ attName.getDisplayName(),
+ hostLanguage,
+ level < 0 || currentLevelIsDocument[level],
+ getPipelineConfiguration().isSerializing(),
+ lp,
+ startElementLocationId);
+ if (lp != null) {
+ err.setLocator(new ExpressionLocation(lp, locationId));
+ }
+ throw err;
+ }
+
+ // if this is a duplicate attribute, overwrite the original, unless
+ // the REJECT_DUPLICATES option is set.
+
+ for (int a=0; a<pendingAttListSize; a++) {
+ if ((pendingAttCode[a].equals(attName))) {
+ if (hostLanguage == Configuration.XSLT) {
+ pendingAttType[a] = typeCode;
+ pendingAttValue[a] = value.toString();
+ // we have to copy the CharSequence, because some kinds of CharSequence are mutable.
+ pendingAttLocation[a] = locationId;
+ pendingAttProp[a] = properties;
+ return;
+ } else {
+ XPathException err = new XPathException("Cannot create an element having two attributes with the same name: " +
+ Err.wrap(attName.getDisplayName(), Err.ATTRIBUTE));
+ err.setErrorCode("XQDY0025");
+ throw err;
+ }
+ }
+ }
+
+ // for top-level attributes (attributes whose parent element is not being copied),
+ // check that the type annotation is not namespace-sensitive (because the namespace context might
+ // be different, and we don't do namespace fixup for prefixes in content: see bug 4151
+
+ if (level==0 && !typeCode.equals(BuiltInAtomicType.UNTYPED_ATOMIC) /**/ && currentLevelIsDocument[0] /**/) {
+ // commenting-out in line above done MHK 22 Jul 2011 to pass test Constr-cont-nsmode-8
+ // reverted 2011-07-27 to pass tests in qischema family
+ if (typeCode.isNamespaceSensitive()) {
+ XPathException err = new XPathException("Cannot copy attributes whose type is namespace-sensitive (QName or NOTATION): " +
+ Err.wrap(attName.getDisplayName(), Err.ATTRIBUTE));
+ err.setErrorCode((hostLanguage == Configuration.XSLT ? "XTTE0950" : "XQTY0086"));
+ throw err;
+ }
+ }
+
+ // otherwise, add this one to the list
+
+ if (pendingAttListSize >= pendingAttCode.length) {
+ NodeName[] attCode2 = new NodeName[pendingAttListSize*2];
+ SimpleType[] attType2 = new SimpleType[pendingAttListSize*2];
+ String[] attValue2 = new String[pendingAttListSize*2];
+ int[] attLoc2 = new int[pendingAttListSize*2];
+ int[] attProp2 = new int[pendingAttListSize*2];
+ System.arraycopy(pendingAttCode, 0, attCode2, 0, pendingAttListSize);
+ System.arraycopy(pendingAttType, 0, attType2, 0, pendingAttListSize);
+ System.arraycopy(pendingAttValue, 0, attValue2, 0, pendingAttListSize);
+ System.arraycopy(pendingAttLocation, 0, attLoc2, 0, pendingAttListSize);
+ System.arraycopy(pendingAttProp, 0, attProp2, 0, pendingAttListSize);
+ pendingAttCode = attCode2;
+ pendingAttType = attType2;
+ pendingAttValue = attValue2;
+ pendingAttLocation = attLoc2;
+ pendingAttProp = attProp2;
+ }
+
+ pendingAttCode[pendingAttListSize] = attName;
+ pendingAttType[pendingAttListSize] = typeCode;
+ pendingAttValue[pendingAttListSize] = value.toString();
+ pendingAttLocation[pendingAttListSize] = locationId;
+ pendingAttProp[pendingAttListSize] = properties;
+ pendingAttListSize++;
+ previousAtomic = false;
+ }
+
+ /**
+ * Check that the prefix for an element or attribute is acceptable, allocating a substitute
+ * prefix if not. The prefix is acceptable unless a namespace declaration has been
+ * written that assignes this prefix to a different namespace URI. This method
+ * also checks that the element or attribute namespace has been declared, and declares it
+ * if not.
+ * @param nodeName the proposed name, including proposed prefix
+ * @param seq sequence number, used for generating a substitute prefix when necessary
+ * @return a nameCode to use in place of the proposed nameCode (or the original nameCode
+ * if no change is needed)
+ * @throws net.sf.saxon.trans.XPathException if an error occurs writing the new
+ * namespace node
+ */
+
+ private NodeName checkProposedPrefix(NodeName nodeName, int seq) throws XPathException {
+ NamespaceBinding binding = nodeName.getNamespaceBinding();
+ String nsprefix = binding.getPrefix();
+
+ for (int i=0; i<pendingNSListSize; i++) {
+ if (nsprefix.equals(pendingNSList[i].getPrefix())) {
+ // same prefix
+ if (binding.getURI().equals((pendingNSList[i].getURI()))) {
+ // same URI
+ return nodeName; // all is well
+ } else {
+ String prefix = getSubstitutePrefix(binding, seq);
+ NodeName newName = new FingerprintedQName(prefix, nodeName.getURI(), nodeName.getLocalPart());
+ namespace(newName.getNamespaceBinding(), 0);
+ return newName;
+ }
+ }
+ }
+ // no declaration of this prefix: declare it now
+ namespace(binding, 0);
+ return nodeName;
+ }
+
+ /**
+ * It is possible for a single output element to use the same prefix to refer to different
+ * namespaces. In this case we have to generate an alternative prefix for uniqueness. The
+ * one we generate is based on the sequential position of the element/attribute: this is
+ * designed to ensure both uniqueness (with a high probability) and repeatability
+ * @param nscode the proposed namespace code
+ * @param seq sequence number for use in the substitute prefix
+ * @return a prefix to use in place of the one originally proposed
+ */
+
+ private String getSubstitutePrefix(NamespaceBinding nscode, int seq) {
+ return nscode.getPrefix() + '_' + seq;
+ }
+
+ /**
+ * Output an element end tag.
+ */
+
+ public void endElement() throws XPathException {
+ //System.err.println("Write end tag " + this + " : " + name);
+ if (pendingStartTagDepth >= 0) {
+ startContent();
+ } else {
+ pendingStartTagDepth = -2;
+ pendingStartTag = null;
+ }
+
+ // write the end tag
+
+ nextReceiver.endElement();
+ level--;
+ previousAtomic = false;
+ }
+
+ /**
+ * Write a comment
+ */
+
+ public void comment(CharSequence comment, int locationId, int properties) throws XPathException {
+ if (pendingStartTagDepth >= 0) {
+ startContent();
+ }
+ nextReceiver.comment(comment, locationId, properties);
+ previousAtomic = false;
+ }
+
+ /**
+ * Write a processing instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (pendingStartTagDepth >= 0) {
+ startContent();
+ }
+ nextReceiver.processingInstruction(target, data, locationId, properties);
+ previousAtomic = false;
+ }
+
+ /**
+ * Append an arbitrary item (node or atomic value) to the output
+ * @param item the item to be appended
+ * @param locationId the location of the calling instruction, for diagnostics
+ * @param copyNamespaces if the item is an element node, this indicates whether its namespaces
+ * need to be copied. Values are {@link net.sf.saxon.om.NodeInfo#ALL_NAMESPACES},
+ * {@link net.sf.saxon.om.NodeInfo#LOCAL_NAMESPACES}, {@link net.sf.saxon.om.NodeInfo#NO_NAMESPACES}
+ */
+
+ public void append(/*@Nullable*/ Item item, int locationId, int copyNamespaces) throws XPathException {
+ if (item == null) {
+ //return;
+ } else if (item instanceof AtomicValue || item instanceof ObjectValue) {
+ if (previousAtomic) {
+ characters(" ", locationId, 0);
+ }
+ characters(item.getStringValueCS(), locationId, 0);
+ previousAtomic = true;
+ } else if (item instanceof FunctionItem) {
+ throw new XPathException("Cannot add a function item to a node tree");
+ } else if (((NodeInfo)item).getNodeKind() == Type.DOCUMENT) {
+ startDocument(0);
+ SequenceIterator iter = ((NodeInfo)item).iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ Item it = iter.next();
+ if (it == null) break;
+ append(it, locationId, copyNamespaces);
+ }
+ endDocument();
+ previousAtomic = false;
+ } else {
+ int copyOptions = CopyOptions.TYPE_ANNOTATIONS;
+ if (copyNamespaces == NodeInfo.LOCAL_NAMESPACES) {
+ copyOptions |= CopyOptions.LOCAL_NAMESPACES;
+ } else if (copyNamespaces == NodeInfo.ALL_NAMESPACES) {
+ copyOptions |= CopyOptions.ALL_NAMESPACES;
+ }
+ ((NodeInfo)item).copy(this, copyOptions, locationId);
+ previousAtomic = false;
+ }
+ }
+
+
+ /**
+ * Close the output
+ */
+
+ public void close() throws XPathException {
+ // System.err.println("Close " + this + " using emitter " + emitter.getClass());
+ nextReceiver.close();
+ previousAtomic = false;
+ }
+
+ /**
+ * Flush out a pending start tag
+ */
+
+ public void startContent() throws XPathException {
+
+ if (pendingStartTagDepth < 0) {
+ // this can happen if the method is called from outside,
+ // e.g. from a SequenceOutputter earlier in the pipeline
+ return;
+ }
+
+ started = true;
+ int props = startElementProperties;
+ NodeName elcode = pendingStartTag;
+ if (declaresDefaultNamespace || pendingStartTag.getPrefix().length()!=0) {
+ // skip this check if the element is unprefixed and no xmlns="abc" declaration has been encountered
+ elcode = checkProposedPrefix(pendingStartTag, 0);
+ props = startElementProperties | ReceiverOptions.NAMESPACE_OK;
+ }
+ nextReceiver.startElement(elcode, currentSimpleType, startElementLocationId, props);
+
+ for (int a=0; a<pendingAttListSize; a++) {
+ NodeName attcode = pendingAttCode[a];
+ if (!attcode.isInNamespace("")) { // non-null prefix
+ attcode = checkProposedPrefix(attcode, a+1);
+ pendingAttCode[a] = attcode;
+ }
+ }
+
+ for (int n=0; n<pendingNSListSize; n++) {
+ nextReceiver.namespace(pendingNSList[n], 0);
+ }
+
+ for (int a=0; a<pendingAttListSize; a++) {
+ nextReceiver.attribute( pendingAttCode[a],
+ pendingAttType[a],
+ pendingAttValue[a],
+ pendingAttLocation[a],
+ pendingAttProp[a]);
+ }
+
+ nextReceiver.startContent();
+
+ pendingAttListSize = 0;
+ pendingNSListSize = 0;
+ pendingStartTagDepth = -1;
+ previousAtomic = false;
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return nextReceiver.usesTypeAnnotations();
+ }
+}
+
diff --git a/sf/saxon/event/ContentHandlerProxy.java b/sf/saxon/event/ContentHandlerProxy.java
new file mode 100644
index 0000000..71b55a0
--- /dev/null
+++ b/sf/saxon/event/ContentHandlerProxy.java
@@ -0,0 +1,647 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.type.SchemaException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.ext.LexicalHandler;
+
+import javax.xml.transform.Result;
+import java.io.PrintStream;
+import java.util.Properties;
+import java.util.Stack;
+
+/**
+ * A ContentHandlerProxy is a Receiver that converts events into the form expected by an
+ * underlying SAX2 ContentHandler. Relevant events (notably comments) can also be
+ * fed to a LexicalHandler.
+ * <p/>
+ * Note that in general the output passed to a Receiver
+ * corresponds to an External General Parsed Entity. A SAX2 ContentHandler only expects
+ * to deal with well-formed XML documents, so we only pass it the contents of the first
+ * element encountered, unless the saxon:require-well-formed output property is set to "no".
+ * </p><p>
+ * This ContentHandlerProxy provides no access to type information. For a ContentHandler that
+ * makes type information available, see {@link com.saxonica.jaxp.TypedContentHandler}
+ * <p></p>
+ * The ContentHandlerProxy can also be nominated as a TraceListener, to receive notification
+ * of trace events. This will be done automatically if the option setTraceListener(
+ */
+
+public class ContentHandlerProxy implements Receiver {
+ private PipelineConfiguration pipe;
+ private String systemId;
+ protected ContentHandler handler;
+ protected LexicalHandler lexicalHandler;
+ private LocationProvider locationProvider;
+ private int depth = 0;
+ private boolean requireWellFormed = false;
+ private boolean undeclareNamespaces = false;
+ private Stack elementStack = new Stack();
+ private Stack namespaceStack = new Stack();
+ private ContentHandlerProxyTraceListener traceListener;
+ protected AttributeCollectionImpl pendingAttributes;
+ private NodeName pendingElement = null;
+ private long currentLocationId;
+
+ // MARKER is a value added to the namespace stack at the start of an element, so that we know how
+ // far to unwind the stack on an end-element event.
+
+ private static final String MARKER = "##";
+
+ /**
+ * Set the underlying content handler. This call is mandatory before using this Receiver.
+ * If the content handler is an instance of {@link LexicalHandler}, then it will also receive
+ * notification of lexical events such as comments.
+ * @param handler the SAX content handler to which all events will be directed
+ */
+
+ public void setUnderlyingContentHandler(ContentHandler handler) {
+ this.handler = handler;
+ if (handler instanceof LexicalHandler) {
+ lexicalHandler = (LexicalHandler)handler;
+ }
+ }
+
+ /**
+ * Get the underlying content handler
+ * @return the SAX content handler to which all events are being directed
+ */
+
+ public ContentHandler getUnderlyingContentHandler() {
+ return handler;
+ }
+
+ /**
+ * Set the Lexical Handler to be used. If called, this must be called AFTER
+ * setUnderlyingContentHandler()
+ * @param handler the SAX lexical handler to which lexical events (such as comments) will
+ * be notified.
+ */
+
+ public void setLexicalHandler(LexicalHandler handler) {
+ lexicalHandler = handler;
+ }
+
+ /**
+ * Set the pipeline configuration
+ * @param pipe the pipeline configuration
+ */
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipe) {
+ this.pipe = pipe;
+ locationProvider = pipe.getLocationProvider();
+ }
+
+ /**
+ * Get the pipeline configuration
+ */
+
+ /*@NotNull*/
+ public PipelineConfiguration getPipelineConfiguration() {
+ return pipe;
+ }
+
+ /**
+ * Get the Saxon configuration
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return pipe.getConfiguration();
+ }
+
+ /**
+ * Set the System ID of the destination tree
+ * @param systemId the system ID (effectively the base URI)
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the System ID of the destination tree
+ * @return the system ID (effectively the base URI)
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Get the associated TraceListener that receives notification of trace events
+ * @return the trace listener. If there is no existing trace listener, then a new one
+ * will be created.
+ */
+
+ public ContentHandlerProxyTraceListener getTraceListener() {
+ if (traceListener == null) {
+ traceListener = new ContentHandlerProxyTraceListener();
+ }
+ return traceListener;
+ }
+
+ /**
+ * Get the location provider
+ * @return the location provider, used to map location ids to actual URIs and line numbers
+ */
+
+ public LocationProvider getLocationProvider() {
+ return locationProvider;
+ }
+
+ /**
+ * Get the current location identifier
+ * @return the location identifier of the most recent event. This can be translated to real
+ * location information by passing it to the location provider.
+ */
+
+ public long getCurrentLocationId() {
+ return currentLocationId;
+ }
+
+ /**
+ * Notify an unparsed entity URI. This implementation does nothing: the event is ignored.
+ *
+ * @param name The name of the unparsed entity
+ * @param systemID The system identifier of the unparsed entity
+ * @param publicID The public identifier of the unparsed entity
+ */
+
+ public void setUnparsedEntity(String name, String systemID, String publicID) throws XPathException {
+ // no-op
+ }
+
+ /**
+ * Set the output details.
+ * @param details the serialization properties. The only values used by this implementation are
+ * {@link SaxonOutputKeys#REQUIRE_WELL_FORMED} and {@link net.sf.saxon.lib.SaxonOutputKeys#UNDECLARE_PREFIXES}.
+ */
+
+ public void setOutputProperties(Properties details) throws XPathException {
+ String prop = details.getProperty(SaxonOutputKeys.REQUIRE_WELL_FORMED);
+ if (prop != null) {
+ requireWellFormed = prop.equals("yes");
+ }
+ prop = details.getProperty(SaxonOutputKeys.UNDECLARE_PREFIXES);
+ if (prop != null) {
+ undeclareNamespaces = prop.equals("yes");
+ }
+ }
+
+ /**
+ * Ask whether the content handler can handle a stream of events that is merely
+ * well-balanced, or whether it can only handle a well-formed sequence.
+ * @return true if the content handler requires the event stream to represent a well-formed
+ * XML document (containing exactly one top-level element node and no top-level text nodes)
+ */
+
+ public boolean isRequireWellFormed() {
+ return requireWellFormed;
+ }
+
+ /**
+ * Set whether the content handler can handle a stream of events that is merely
+ * well-balanced, or whether it can only handle a well-formed sequence. The default is false.
+ * @param wellFormed set to true if the content handler requires the event stream to represent a well-formed
+ * XML document (containing exactly one top-level element node and no top-level text nodes). Otherwise,
+ * multiple top-level elements and text nodes are allowed, as in the XDM model.
+ */
+
+ public void setRequireWellFormed(boolean wellFormed) {
+ requireWellFormed = wellFormed;
+ }
+
+ /**
+ * Ask whether namespace undeclaration events (for a non-null prefix) should be notified.
+ * The default is no, because some ContentHandlers (e.g. JDOM) can't cope with them.
+ *
+ * @return true if namespace undeclarations (xmlns:p="") are to be output
+ */
+
+ public boolean isUndeclareNamespaces() {
+ return undeclareNamespaces;
+ }
+
+ /**
+ * Set whether namespace undeclaration events (for a non-null prefix) should be notified.
+ * The default is no, because some ContentHandlers (e.g. JDOM) can't cope with them.
+ *
+ * @param undeclareNamespaces true if namespace undeclarations (xmlns:p="") are to be output
+ */
+
+ public void setUndeclareNamespaces(boolean undeclareNamespaces) {
+ this.undeclareNamespaces = undeclareNamespaces;
+ }
+
+ /**
+ * Notify the start of the event stream
+ */
+
+ public void open() throws XPathException {
+ pendingAttributes = new AttributeCollectionImpl(getPipelineConfiguration().getConfiguration());
+ if (handler == null) {
+ throw new IllegalStateException("ContentHandlerProxy.open(): no underlying handler provided");
+ }
+ try {
+ locationProvider = getPipelineConfiguration().getLocationProvider();
+ pendingAttributes.setLocationProvider(locationProvider);
+ Locator locator = new ContentHandlerProxyLocator(this);
+ handler.setDocumentLocator(locator);
+ handler.startDocument();
+ } catch (SAXException err) {
+ handleSAXException(err);
+ }
+ depth = 0;
+ }
+
+ /**
+ * Notify the end of the event stream
+ */
+
+ public void close() throws XPathException {
+ try {
+ handler.endDocument();
+ } catch (SAXException err) {
+ handleSAXException(err);
+ }
+ }
+
+ /**
+ * Notify the start of the document.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ }
+
+ /**
+ * Notify the end of the document
+ */
+
+ public void endDocument() throws XPathException {
+ }
+
+ /**
+ * Notify the start of an element
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ depth++;
+ if (depth <= 0 && requireWellFormed) {
+ notifyNotWellFormed();
+ }
+ pendingElement = nameCode;
+ currentLocationId = locationId;
+ namespaceStack.push(MARKER);
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element.
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ String prefix = namespaceBinding.getPrefix();
+ if (prefix.equals("xml")) {
+ return;
+ }
+ String uri = namespaceBinding.getURI();
+ if ((!undeclareNamespaces) && uri.length()==0 && prefix.length()!=0) {
+ // This is a namespace undeclaration, but the ContentHandler doesn't want to know about undeclarations
+ return;
+ }
+ try {
+ handler.startPrefixMapping(prefix, uri);
+ namespaceStack.push(prefix);
+ } catch (SAXException err) {
+ handleSAXException(err);
+ }
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children.
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ int index = pendingAttributes.findByNodeName(nameCode);
+ if (index < 0) {
+ pendingAttributes.addAttribute(nameCode, typeCode, value.toString(), locationId, properties);
+ } else {
+ pendingAttributes.setAttribute(index, nameCode, typeCode, value.toString(), locationId, properties);
+ }
+ }
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+
+ public void startContent() throws XPathException {
+ try {
+ if (depth > 0 || !requireWellFormed) {
+ String uri = pendingElement.getURI();
+ String localName = pendingElement.getLocalPart();
+ String qname = pendingElement.getDisplayName();
+
+ handler.startElement(uri,
+ localName,
+ qname,
+ pendingAttributes);
+
+ elementStack.push(uri);
+ elementStack.push(localName);
+ elementStack.push(qname);
+
+ pendingAttributes.clear();
+ pendingElement = null;
+ }
+ } catch (SAXException err) {
+ handleSAXException(err);
+
+ }
+ }
+
+
+ /**
+ * End of element
+ */
+
+ public void endElement() throws XPathException {
+ if (depth > 0) {
+ try {
+ String qname = (String)elementStack.pop();
+ String localName = (String)elementStack.pop();
+ String uri = (String)elementStack.pop();
+ handler.endElement(uri, localName, qname);
+ } catch (SAXException err) {
+ handleSAXException(err);
+ }
+ }
+
+ while (true) {
+ String prefix = (String)namespaceStack.pop();
+ if (prefix.equals(MARKER)) {
+ break;
+ }
+ try {
+ handler.endPrefixMapping(prefix);
+ } catch (SAXException err) {
+ handleSAXException(err);
+ }
+ }
+ depth--;
+ // if this was the outermost element, and well formed output is required
+ // then no further elements will be processed
+ if (requireWellFormed && depth <= 0) {
+ depth = Integer.MIN_VALUE; // crude but effective
+ }
+
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ currentLocationId = locationId;
+ boolean disable = ((properties & ReceiverOptions.DISABLE_ESCAPING) != 0);
+ if (disable) {
+ setEscaping(false);
+ }
+ try {
+ if (depth <= 0 && requireWellFormed) {
+ if (Whitespace.isWhite(chars)) {
+ // ignore top-level white space
+ } else {
+ notifyNotWellFormed();
+ }
+ } else {
+ handler.characters(chars.toString().toCharArray(), 0, chars.length());
+ }
+ } catch (SAXException err) {
+ handleSAXException(err);
+ }
+ if (disable) {
+ setEscaping(true);
+ }
+ }
+
+ /**
+ * The following function is called when it is found that the output is not a well-formed document.
+ * Unless the ContentHandler accepts "balanced content", this is a fatal error.
+ */
+
+ protected void notifyNotWellFormed() throws XPathException {
+ XPathException err = new XPathException("The result tree cannot be supplied to the ContentHandler because it is not well-formed XML");
+ err.setErrorCode(SaxonErrorCode.SXCH0002);
+ throw err;
+ }
+
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties)
+ throws XPathException {
+ currentLocationId = locationId;
+ try {
+ handler.processingInstruction(target, data.toString());
+ } catch (SAXException err) {
+ handleSAXException(err);
+ }
+ }
+
+ /**
+ * Output a comment. Passes it on to the ContentHandler provided that the ContentHandler
+ * is also a SAX2 LexicalHandler.
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties)
+ throws XPathException {
+ currentLocationId = locationId;
+ try {
+ if (lexicalHandler != null) {
+ lexicalHandler.comment(chars.toString().toCharArray(), 0, chars.length());
+ }
+ } catch (SAXException err) {
+ handleSAXException(err);
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return false;
+ }
+
+ /**
+ * Switch escaping on or off. This is called when the XSLT disable-output-escaping attribute
+ * is used to switch escaping on or off. It is not called for other sections of output (e.g.
+ * element names) where escaping is inappropriate. The action, as defined in JAXP 1.1, is
+ * to notify the request to the Content Handler using a processing instruction.
+ * @param escaping true if escaping is to be switched on, false to switch it off
+ */
+
+ private void setEscaping(boolean escaping) {
+ try {
+ handler.processingInstruction(
+ (escaping ? Result.PI_ENABLE_OUTPUT_ESCAPING : PI_DISABLE_OUTPUT_ESCAPING),
+ "");
+ } catch (SAXException err) {
+ throw new AssertionError(err);
+ }
+ }
+
+
+ /**
+ * Handle a SAXException thrown by the ContentHandler
+ * @param err the exception to be handler
+ * @throws XPathException always
+ */
+
+ private void handleSAXException(SAXException err) throws XPathException {
+ Exception nested = err.getException();
+ if (nested instanceof XPathException) {
+ throw (XPathException)nested;
+ } else if (nested instanceof SchemaException) {
+ throw new XPathException(nested);
+ } else {
+ XPathException de = new XPathException(err);
+ de.setErrorCode(SaxonErrorCode.SXCH0003);
+ throw de;
+ }
+ }
+
+ /**
+ * Create a TraceListener that will collect information about the current
+ * location in the source document. This is used to provide information
+ * to the receiving application for diagnostic purposes.
+ */
+
+ public static class ContentHandlerProxyTraceListener implements TraceListener {
+
+ private Stack contextItemStack;
+
+ public void setOutputDestination(PrintStream stream) {
+ // no action
+ }
+
+ /**
+ * Get the context item stack
+ * @return the context item stack
+ */
+
+ /*@Nullable*/ public Stack getContextItemStack() {
+ return contextItemStack;
+ }
+
+ /**
+ * Method called at the start of execution, that is, when the run-time transformation starts
+ */
+
+ public void open(Controller controller) {
+ contextItemStack = new Stack();
+ }
+
+ /**
+ * Method called at the end of execution, that is, when the run-time execution ends
+ */
+
+ public void close() {
+ contextItemStack = null;
+ }
+
+ /**
+ * Method that is called when an instruction in the stylesheet gets processed.
+ *
+ * @param instruction gives information about the instruction being
+ * executed, and about the context in which it is executed. This object is mutable,
+ * so if information from the InstructionInfo is to be retained, it must be copied.
+ */
+
+ public void enter(InstructionInfo instruction, XPathContext context) {
+ // do nothing
+ }
+
+ /**
+ * Method that is called after processing an instruction of the stylesheet,
+ * that is, after any child instructions have been processed.
+ *
+ * @param instruction gives the same information that was supplied to the
+ * enter method, though it is not necessarily the same object. Note that the
+ * line number of the instruction is that of the start tag in the source stylesheet,
+ * not the line number of the end tag.
+ */
+
+ public void leave(InstructionInfo instruction) {
+ // do nothing
+ }
+
+ /**
+ * Method that is called by an instruction that changes the current item
+ * in the source document: that is, xsl:for-each, xsl:apply-templates, xsl:for-each-group.
+ * The method is called after the enter method for the relevant instruction, and is called
+ * once for each item processed.
+ *
+ * @param currentItem the new current item. Item objects are not mutable; it is safe to retain
+ * a reference to the Item for later use.
+ */
+
+ public void startCurrentItem(Item currentItem) {
+ if (contextItemStack == null) {
+ contextItemStack = new Stack();
+ }
+ contextItemStack.push(currentItem);
+ }
+
+ /**
+ * Method that is called when an instruction has finished processing a new current item
+ * and is ready to select a new current item or revert to the previous current item.
+ * The method will be called before the leave() method for the instruction that made this
+ * item current.
+ *
+ * @param currentItem the item that was current, whose processing is now complete. This will represent
+ * the same underlying item as the corresponding startCurrentItem() call, though it will
+ * not necessarily be the same actual object.
+ */
+
+ public void endCurrentItem(Item currentItem) {
+ contextItemStack.pop();
+ }
+ }
+
+}
+
diff --git a/sf/saxon/event/ContentHandlerProxyLocator.java b/sf/saxon/event/ContentHandlerProxyLocator.java
new file mode 100644
index 0000000..7961a84
--- /dev/null
+++ b/sf/saxon/event/ContentHandlerProxyLocator.java
@@ -0,0 +1,116 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import org.xml.sax.Locator;
+
+import java.util.Stack;
+
+/**
+ * Implementation of Locator, used to supply location information to the ContentHandler.
+ *
+ * <p>When the ContentHandler is used to receive the results of a query or stylesheet,
+ * the information supplied by the standard methods such as {@link #getSystemId} and
+ * {@link #getLineNumber} relates to the position of the expression/instruction in the stylesheet
+ * or query that caused the relevant nodes to be output.</p>
+ *
+ * <p>If the ContentHandler is used in other contexts, for example as the destination of an
+ * IdentityTransformer, the information reflects the position in the source document.</p>
+ *
+ * <p>If the output property <code>saxon:supply-source-locator</code> was set to the
+ * value "yes" (which in turn requires that tracing was enabled at compile time), the Locator will
+ * also contain information about the current location in the source document (specifically, the position
+ * of the context node). This will not always be 100% accurate, since there is some buffering of events
+ * in the output pipeline: it reflects the context node at the time the event reaches the ContentHandler,
+ * which may not be the same as the context node at the time the relevant instruction was executed;
+ * however, it still provides some information that may be useful for diagnostics.</p>
+ */
+
+public class ContentHandlerProxyLocator implements Locator {
+
+ private ContentHandlerProxy parent = null;
+
+ /**
+ * Create the Locator for a ContentHandlerProxy
+ * @param parent the ContentHandlerProxy
+ */
+
+ public ContentHandlerProxyLocator(ContentHandlerProxy parent) {
+ this.parent = parent;
+ }
+
+ // previously ContentHandlerProxy implemented Locator directly. However, this caused a clash
+ // over the semantics of getSystemId(), which is inherited from both Receiver and Locator.
+ // The getSystemId() method in the Receiver interface identifies the URI of the document represented
+ // by the event stream. The getSystemId() method in this class typically returns the URI of the stylesheet
+ // or query module that generated the event.
+
+ /**
+ * Get the Public ID
+ * @return null (always)
+ */
+
+ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Get the System ID
+ * @return the system ID giving the location in the query or stylesheet of the most recent event notified
+ */
+
+ public String getSystemId() {
+ final LocationProvider locationProvider = parent.getLocationProvider();
+ if (locationProvider == null) {
+ return null;
+ } else {
+ return locationProvider.getSystemId(parent.getCurrentLocationId());
+ }
+ }
+
+ /**
+ * Get the line number
+ * @return the line number giving the location of the most recent event notified
+ */
+
+ public int getLineNumber() {
+ final LocationProvider locationProvider = parent.getLocationProvider();
+ if (locationProvider == null) {
+ return -1;
+ } else {
+ return locationProvider.getLineNumber(parent.getCurrentLocationId());
+ }
+ }
+
+ /**
+ * Get the column number
+ * @return -1 (always)
+ */
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Get the current item stack. This is a Stack, whose members are objects of class
+ * {@link net.sf.saxon.om.Item}. The top item in the stack is the context node or atomic value; items
+ * further down the stack represent previous context node or atomic value
+ * @return the stack of context items
+ */
+
+ /*@Nullable*/ public Stack getContextItemStack() {
+ final ContentHandlerProxy.ContentHandlerProxyTraceListener traceListener = parent.getTraceListener();
+ if (traceListener == null) {
+ return null;
+ } else {
+ return traceListener.getContextItemStack();
+ }
+ }
+
+}
+
diff --git a/sf/saxon/event/CopyInformee.java b/sf/saxon/event/CopyInformee.java
new file mode 100644
index 0000000..2828637
--- /dev/null
+++ b/sf/saxon/event/CopyInformee.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.NodeInfo;
+
+/**
+ * A CopyInformee is an agent that receives extra information while a tree is being copied. Specifically,
+ * each time an element node is copied to the receiver, before calling the startElement() method, the copying
+ * code will first call notifyElementNode(), giving the informee extra information about the element currently
+ * being copied.
+ */
+public interface CopyInformee {
+
+ /**
+ * Provide information about the node being copied. This method is called immediately before
+ * the startElement call for the element node in question.
+ * @param element the node being copied, which must be an element node
+ * @return int a locationId to be used when referring to this element in the pipeline
+ */
+
+ public int notifyElementNode(NodeInfo element);
+}
+
diff --git a/sf/saxon/event/CopyNamespaceSensitiveException.java b/sf/saxon/event/CopyNamespaceSensitiveException.java
new file mode 100644
index 0000000..e9571ac
--- /dev/null
+++ b/sf/saxon/event/CopyNamespaceSensitiveException.java
@@ -0,0 +1,24 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Exception indicating that an attempt was made to copy namespace-sensitive content
+ * without copying its associated namespaces
+ */
+
+public class CopyNamespaceSensitiveException extends XPathException {
+
+ public CopyNamespaceSensitiveException(String message) {
+ super(message);
+ }
+
+}
+
diff --git a/sf/saxon/event/EventSource.java b/sf/saxon/event/EventSource.java
new file mode 100644
index 0000000..bb26557
--- /dev/null
+++ b/sf/saxon/event/EventSource.java
@@ -0,0 +1,55 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.Source;
+
+/**
+ * An implementation of the JAXP Source class that supplies a document in the form of a stream
+ * of push events sent to a Receiver
+ * @since 9.1
+ */
+public abstract class EventSource implements Source {
+
+ private String systemId;
+
+ /**
+ * Set the system identifier for this Source.
+ * <p/>
+ * <p>The system identifier is optional if the source does not
+ * get its data from a URL, but it may still be useful to provide one.
+ * The application can use a system identifier, for example, to resolve
+ * relative URIs and to include in error messages and warnings.</p>
+ * @param systemId The system identifier as a URL string.
+ */
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the system identifier that was set with setSystemId.
+ * @return The system identifier that was set with setSystemId, or null
+ * if setSystemId was not called.
+ */
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Supply events to a Receiver.
+ * @param out the Receiver to which events will be sent. It is the caller's responsibility
+ * to initialize the receiver with a PipelineConfiguration, and to call the open() and close()
+ * methods on the receiver before and after calling this send() method.
+ * @throws net.sf.saxon.trans.XPathException if any error occurs
+ */
+
+ public abstract void send(Receiver out) throws XPathException;
+}
+
diff --git a/sf/saxon/event/FilterFactory.java b/sf/saxon/event/FilterFactory.java
new file mode 100644
index 0000000..af244b4
--- /dev/null
+++ b/sf/saxon/event/FilterFactory.java
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+/**
+ * Factory class to create a ProxyReceiver which filters events on a push pipeline
+ */
+public interface FilterFactory {
+
+ /**
+ * Make a ProxyReceiver to filter events on a push pipeline
+ * @param next the next receiver in the pipeline
+ * @return a ProxyReceiver initialized to send events to the next receiver in the pipeine
+ */
+
+ public ProxyReceiver makeFilter(Receiver next);
+}
diff --git a/sf/saxon/event/IDFilter.java b/sf/saxon/event/IDFilter.java
new file mode 100644
index 0000000..5882378
--- /dev/null
+++ b/sf/saxon/event/IDFilter.java
@@ -0,0 +1,186 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+import java.util.HashSet;
+
+
+/**
+* IDFilter is a ProxyReceiver that extracts the subtree of a document rooted at the
+* element with a given ID value. Namespace declarations outside this subtree are
+* treated as if they were present on the identified element.
+*/
+
+public class IDFilter extends StartTagBuffer {
+
+ private String requiredId;
+ private int activeDepth = 0;
+ private boolean matched = false;
+ private HashSet<SimpleType> nonIDs;
+
+ public IDFilter (Receiver next, String id) {
+ // System.err.println("IDFilter, looking for " + id);
+ super(next);
+ this.requiredId = id;
+ }
+
+ /**
+ * startElement
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ matched = false;
+ if (activeDepth>0) {
+ activeDepth++;
+ }
+ super.startElement(nameCode, typeCode, locationId, properties); // this remembers the details
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param attName The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined, inter alia:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * <dd>IS_ID</dd> <dt>Attribute is an ID</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ super.attribute(attName, typeCode, value, locationId, properties);
+ if ((attName.equals(StandardNames.XML_ID_NAME)) || ((properties &ReceiverOptions.IS_ID) != 0)) {
+ if (value.toString().equals(requiredId)) {
+ matched = true;
+ }
+ }
+ }
+
+ /**
+ * startContent: Test if a matching ID attribute was found; if so, start outputting.
+ */
+
+ public void startContent() throws XPathException {
+ if (activeDepth>0) {
+ super.startContent();
+ } else if (matched) {
+ activeDepth = 1;
+ super.startContent();
+ }
+ }
+
+ protected void declareNamespacesForStartElement() throws XPathException {
+ if (activeDepth == 1) {
+ declareAllNamespaces();
+ } else {
+ super.declareNamespacesForStartElement();
+ }
+ }
+
+ /**
+ * endElement:
+ */
+
+ public void endElement() throws XPathException {
+ if (activeDepth > 0) {
+ nextReceiver.endElement();
+ activeDepth--;
+ } else {
+ undeclareNamespacesForElement();
+ }
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (activeDepth > 0) {
+ super.characters(chars, locationId, properties);
+ }
+ }
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (activeDepth > 0) {
+ super.processingInstruction(target, data, locationId, properties);
+ }
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (activeDepth > 0) {
+ super.comment(chars, locationId, properties);
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return true;
+ }
+
+ /**
+ * Test whether a type annotation code represents the type xs:ID or one of its subtypes
+ * @param typeCode the fingerprint of the type name
+ * @return true if the type is an ID type; false if it is not (or if the type code does not
+ * resolve to a known type)
+ */
+
+ private boolean isIDCode(SimpleType typeCode) {
+ if (typeCode == BuiltInAtomicType.ID) {
+ return true;
+ }
+ if (typeCode instanceof BuiltInAtomicType) {
+ return false; // No other built-in type is an ID
+ }
+
+ if (nonIDs == null) {
+ nonIDs = new HashSet<SimpleType>(20);
+ }
+ if (nonIDs.contains(typeCode)) {
+ return false;
+ }
+ if (typeCode.isAtomicType()) {
+ if (getConfiguration().getTypeHierarchy().isSubType((AtomicType)typeCode, BuiltInAtomicType.ID)) {
+ return true;
+ } else {
+ nonIDs.add(typeCode);
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/event/LocationCopier.java b/sf/saxon/event/LocationCopier.java
new file mode 100644
index 0000000..6293628
--- /dev/null
+++ b/sf/saxon/event/LocationCopier.java
@@ -0,0 +1,66 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.NodeInfo;
+
+/**
+ * A Receiver that can be inserted into an event pipeline to copy location information.
+ * The class acts as a LocationProvider, so it supports getSystemId() and getLineNumber() methods;
+ * the location returned can vary for each node, and is set by the class generating the events.
+ * The class is used when it is necessary to copy a subtree along with its location information;
+ * for example, when copying an inline schema within a stylesheet to a separate schema document.
+ *
+ * <p><i>Note: prior to 9.2, the LocationCopier was a ProxyReceiver that passed all events on the
+ * pipeline unchanged. It no longer does this, instead it is found as the LocationProvider on a
+ * pipeline, but does not itself see the pipeline events.</i></p>
+ */
+
+public class LocationCopier implements CopyInformee, SourceLocationProvider {
+
+ private String systemId;
+ private int lineNumber;
+ private boolean wholeDocument;
+
+ public LocationCopier(boolean wholeDocument) {
+ this.wholeDocument = wholeDocument;
+ }
+
+ /**
+ * Provide information about the node being copied. This method is called immediately before
+ * the startElement call for the element node in question.
+ *
+ * @param element the node being copied, which must be an element node
+ */
+
+ public int notifyElementNode(NodeInfo element) {
+ systemId = (wholeDocument ? element.getSystemId() : element.getBaseURI());
+ // The logic behind this is that if we are copying the whole document, we will be copying all
+ // the relevant xml:base attributes; so retaining the systemId values is sufficient to enable
+ // the base URIs of the nodes to be preserved. But if we only copy an element (for example
+ // an xsl:import-schema element - see test schema091 - then its base URI might be affected
+ // by xml:base attributes that aren't being copied. Ideally we would have two separate properties,
+ // but XDM doesn't work that way.
+ lineNumber = element.getLineNumber();
+ return 0;
+ }
+
+ public String getSystemId(long locationId) {
+ return systemId;
+ }
+
+ public int getLineNumber(long locationId) {
+ return lineNumber;
+ }
+
+ public int getColumnNumber(long locationId) {
+ return -1;
+ }
+
+}
+
diff --git a/sf/saxon/event/LocationProvider.java b/sf/saxon/event/LocationProvider.java
new file mode 100644
index 0000000..98a6236
--- /dev/null
+++ b/sf/saxon/event/LocationProvider.java
@@ -0,0 +1,53 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+
+/**
+ * LocationProvider: this interface represents an object that
+ * provides the location of elements in a source document or instructions in a stylesheet
+ * or query. A locationProvider may be passed down the Receiver pipeline as part of the
+ * PipelineConfiguration object; on the input pipeline, this will be a {@link SaxonLocator} object,
+ * on the output pipeline, it will be a {@link net.sf.saxon.expr.instruct.LocationMap}
+ * <p/>
+ * A LocationProvider that represents locations in the source document from which the events
+ * are derived (as distinct from locations in a query or stylesheet of the instructions causing the
+ * events) will also implement the marker interface {@link net.sf.saxon.event.SourceLocationProvider}
+ */
+
+public interface LocationProvider {
+
+ /**
+ * Get the URI of the document, entity, or module containing a particular location
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the URI of the document, XML entity or module. For a SourceLocationProvider this will
+ * be the URI of the document or entity (the URI that would be the base URI if there were no
+ * xml:base attributes). In other cases it may identify the query or stylesheet module currently
+ * being executed.
+ */
+
+ public String getSystemId(long locationId);
+
+ /**
+ * Get the line number within the document, entity or module containing a particular location
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the line number within the document, entity or module, or -1 if no information is available.
+ */
+
+ public int getLineNumber(long locationId);
+
+ /**
+ * Get the column number within the document, entity, or module containing a particular location
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the column number within the document, entity, or module, or -1 if this is not available
+ */
+
+ public int getColumnNumber(long locationId);
+
+
+}
diff --git a/sf/saxon/event/NamePoolConverter.java b/sf/saxon/event/NamePoolConverter.java
new file mode 100644
index 0000000..90b3c3b
--- /dev/null
+++ b/sf/saxon/event/NamePoolConverter.java
@@ -0,0 +1,86 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.CodedName;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+/**
+* This class is a filter that passes all Receiver events through unchanged,
+* except that it changes namecodes to allow for the source and the destination
+* using different NamePools. This is necessary when a stylesheet has been constructed
+* as a general document (e.g. as the result of a transformation) and is passed to
+* newTemplates() to be compiled as a stylesheet.
+*
+* @author Michael Kay
+*/
+
+
+public class NamePoolConverter extends ProxyReceiver {
+
+ NamePool oldPool;
+ NamePool newPool;
+
+ /**
+ * Constructor
+ * @param next the next receiver in the pipeline
+ * @param oldPool the old namepool
+ * @param newPool th new namepool
+ */
+
+ public NamePoolConverter(Receiver next, NamePool oldPool, NamePool newPool) {
+ super(next);
+ this.oldPool = oldPool;
+ this.newPool = newPool;
+ }
+
+ /**
+ * Set the underlying emitter. This call is mandatory before using the Emitter.
+ * This version is modified from that of the parent class to avoid setting the namePool
+ * of the destination Receiver.
+ */
+
+ @Override
+ public void setUnderlyingReceiver(/*@NotNull*/ Receiver receiver) {
+ nextReceiver = receiver;
+ }
+
+ /**
+ * Output element start tag
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ int nc = newPool.allocate(nameCode.getPrefix(), nameCode.getURI(), nameCode.getLocalPart());
+ nextReceiver.startElement(new CodedName(nc, newPool), typeCode, locationId, properties);
+ }
+
+ /**
+ * Handle a namespace
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ nextReceiver.namespace(namespaceBinding, properties);
+ }
+
+ /**
+ * Handle an attribute
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ int nc = newPool.allocate(nameCode.getPrefix(), nameCode.getURI(), nameCode.getLocalPart());
+ nextReceiver.attribute(new CodedName(nc, newPool), typeCode, value, locationId, properties);
+ }
+
+}
+
diff --git a/sf/saxon/event/NamespaceReducer.java b/sf/saxon/event/NamespaceReducer.java
new file mode 100644
index 0000000..79bb0b8
--- /dev/null
+++ b/sf/saxon/event/NamespaceReducer.java
@@ -0,0 +1,281 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * NamespaceReducer is a ProxyReceiver responsible for removing duplicate namespace
+ * declarations. It also ensures that an xmlns="" undeclaration is output when
+ * necessary. Used on its own, the NamespaceReducer simply eliminates unwanted
+ * namespace declarations. It can also be subclassed, in which case the subclass
+ * can use the services of the NamespaceReducer to resolve QNames.
+ * <p>
+ * The NamespaceReducer also validates namespace-sensitive content.
+ */
+
+public class NamespaceReducer extends ProxyReceiver implements NamespaceResolver
+{
+ // We keep track of namespaces to avoid outputting duplicate declarations. The namespaces
+ // array holds a list of all namespaces currently declared (organised as pairs of entries,
+ // prefix followed by URI). The countStack contains an entry for each element currently open; the
+ // value on the stack is an integer giving the number of namespaces added to the main
+ // namespace stack by that element.
+
+ private NamespaceBinding[] namespaces = new NamespaceBinding[50]; // all namespace codes currently declared
+ private int namespacesSize = 0; // all namespaces currently declared
+ private int[] countStack = new int[50];
+ private int depth = 0;
+
+ // Creating an element does not automatically inherit the namespaces of the containing element.
+ // When the DISINHERIT property is set on startElement(), this indicates that the namespaces
+ // on that element are not to be automatically inherited by its children. So startElement()
+ // stacks a boolean flag indicating whether the children are to disinherit the parent's namespaces.
+
+ private boolean[] disinheritStack = new boolean[50];
+
+ // When a child element does not inherit the namespaces of its parent, it acquires undeclarations
+ // to indicate this fact. This array keeps track of the undeclarations that need to be added to the
+ // current child element.
+
+ private NamespaceBinding[] pendingUndeclarations = null;
+
+ /**
+ * Create a NamespaceReducer
+ * @param next the Receiver to which events will be passed after namespace reduction
+ */
+
+ public NamespaceReducer(Receiver next) {
+ super(next);
+ }
+
+ /**
+ * startElement. This call removes redundant namespace declarations, and
+ * possibly adds an xmlns="" undeclaration.
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+
+ nextReceiver.startElement(elemName, typeCode, locationId, properties);
+
+ // If the parent element specified inherit=no, keep a list of namespaces that need to be
+ // undeclared
+
+ if (depth>0 && disinheritStack[depth-1]) {
+ pendingUndeclarations = new NamespaceBinding[namespacesSize];
+ System.arraycopy(namespaces, 0, pendingUndeclarations, 0, namespacesSize);
+ } else {
+ pendingUndeclarations = null;
+ }
+
+ // Record the current height of the namespace list so it can be reset at endElement time
+
+ countStack[depth] = 0;
+ disinheritStack[depth] = (properties & ReceiverOptions.DISINHERIT_NAMESPACES) != 0;
+ if (++depth >= countStack.length) {
+ int[] newstack = new int[depth*2];
+ System.arraycopy(countStack, 0, newstack, 0, depth);
+ boolean[] disStack2 = new boolean[depth*2];
+ System.arraycopy(disinheritStack, 0, disStack2, 0, depth);
+ countStack = newstack;
+ disinheritStack = disStack2;
+ }
+
+
+ // Ensure that the element namespace is output, unless this is done
+ // automatically by the caller (which is true, for example, for a literal
+ // result element).
+
+ if ((properties & ReceiverOptions.NAMESPACE_OK) == 0) {
+ namespace(elemName.getNamespaceBinding(), 0);
+ }
+
+ }
+
+ /**
+ * Output a namespace node (binding)
+ * @param namespaceBinding the prefix/uri pair to be output
+ * @param properties the properties of the namespace binding
+ * @throws XPathException if any downstream error occurs
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+
+ // Keep the namespace only if it is actually needed
+
+ if (isNeeded(namespaceBinding)) {
+ addToStack(namespaceBinding);
+ countStack[depth - 1]++;
+ nextReceiver.namespace(namespaceBinding, properties);
+ }
+ }
+
+ /**
+ * Determine whether a namespace declaration is needed
+ * @param nsBinding the namespace binding
+ * @return true if the namespace is needed: that is, if it not the XML namespace, is not a duplicate,
+ * and is not a redundant xmlns="".
+ */
+
+ private boolean isNeeded(NamespaceBinding nsBinding) {
+ if (nsBinding.isXmlNamespace()) {
+ // Ignore the XML namespace
+ return false;
+ }
+
+ // First cancel any pending undeclaration of this namespace prefix (there may be more than one)
+
+ String prefix = nsBinding.getPrefix();
+ if (pendingUndeclarations != null) {
+ for (int p=0; p<pendingUndeclarations.length; p++) {
+ NamespaceBinding nb = pendingUndeclarations[p];
+ if (nb != null && prefix.equals(nb.getPrefix())) {
+ pendingUndeclarations[p] = null;
+ //break;
+ }
+ }
+ }
+
+ for (int i=namespacesSize-1; i>=0; i--) {
+ if (namespaces[i].equals(nsBinding)) {
+ // it's a duplicate so we don't need it
+ return false;
+ }
+ if ((namespaces[i].getPrefix().equals(nsBinding.getPrefix()))) {
+ // same prefix, different URI.
+ return true;
+ }
+ }
+
+ // we need it unless it's a redundant xmlns=""
+ return (!nsBinding.isDefaultUndeclaration());
+ }
+
+ /**
+ * Add a namespace declaration to the stack
+ * @param nsBinding the namespace code to be added
+ */
+
+ private void addToStack(NamespaceBinding nsBinding) {
+ // expand the stack if necessary
+ if (namespacesSize+1 >= namespaces.length) {
+ NamespaceBinding[] newlist = new NamespaceBinding[namespacesSize*2];
+ System.arraycopy(namespaces, 0, newlist, 0, namespacesSize);
+ namespaces = newlist;
+ }
+ namespaces[namespacesSize++] = nsBinding;
+ }
+
+ /**
+ * startContent: Add any namespace undeclarations needed to stop
+ * namespaces being inherited from parent elements
+ */
+
+ public void startContent() throws XPathException {
+
+ if (pendingUndeclarations != null) {
+ for (NamespaceBinding ns : pendingUndeclarations) {
+ if (ns != null) {
+ namespace(new NamespaceBinding(ns.getPrefix(), ""), 0);
+ // relies on the namespace() method to prevent duplicate undeclarations
+ }
+ }
+ }
+ pendingUndeclarations = null;
+ nextReceiver.startContent();
+ }
+
+ /**
+ * endElement: Discard the namespaces declared on this element.
+ */
+
+
+ public void endElement () throws XPathException
+ {
+ if (depth-- == 0) {
+ throw new IllegalStateException("Attempt to output end tag with no matching start tag");
+ }
+
+ namespacesSize -= countStack[depth];
+
+ nextReceiver.endElement();
+
+ }
+
+ /**
+ * Get the URI code corresponding to a given prefix code, by searching the
+ * in-scope namespaces. This is a service provided to subclasses.
+ * @param prefixCode the 16-bit prefix code required
+ * @return the 16-bit URI code, or -1 if the prefix is not found
+ */
+
+// protected short getURICode(short prefixCode) {
+// for (int i=namespacesSize-1; i>=0; i--) {
+// if ((namespaces[i]>>16) == (prefixCode)) {
+// return (short)(namespaces[i]&0xffff);
+// }
+// }
+// if (prefixCode == 0) {
+// return 0; // by default, no prefix means no namespace URI
+// } else {
+// return -1;
+// }
+// }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ *
+ * @param prefix the namespace prefix
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is ""
+ * @return the uri for the namespace, or null if the prefix is not in scope
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(String prefix, boolean useDefault) {
+ if ((prefix.length()==0) && !useDefault) {
+ return NamespaceConstant.NULL;
+ } else if ("xml".equals(prefix)) {
+ return NamespaceConstant.XML;
+ } else {
+ for (int i=namespacesSize-1; i>=0; i--) {
+ if ((namespaces[i].getPrefix().equals(prefix))) {
+ return namespaces[i].getURI();
+ }
+ }
+ }
+ return (prefix.length()==0 ? NamespaceConstant.NULL : null);
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ List<String> prefixes = new ArrayList<String>(namespacesSize);
+ for (int i=namespacesSize-1; i>=0; i--) {
+ String prefix = namespaces[i].getPrefix();
+ if (!prefixes.contains(prefix)) {
+ prefixes.add(prefix);
+ }
+ }
+ prefixes.add("xml");
+ return prefixes.iterator();
+ }
+}
+
diff --git a/sf/saxon/event/NoOpenStartTagException.java b/sf/saxon/event/NoOpenStartTagException.java
new file mode 100644
index 0000000..eb63598
--- /dev/null
+++ b/sf/saxon/event/NoOpenStartTagException.java
@@ -0,0 +1,89 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.lib.StandardErrorListener;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+
+/**
+* Exception indicating that an attribute or namespace node has been written when
+* there is no open element to write it to
+*/
+
+public class NoOpenStartTagException extends XPathException {
+
+ /**
+ * Static factory method to create the exception
+ * @param nodeKind the kind of node being created (attribute or namespace)
+ * @param name the name of the node being created
+ * @param hostLanguage XSLT or XQuery (error codes are different in the two cases)
+ * @param parentIsDocument true if the nodes are being added to a document node (rather than an element)
+ * @param isSerializing true if the document is being created in the process of serialization
+ * @param locationProvider Object to provide location information for diagnostics
+ * @param startElementLocationId integer that can be passed to the location provider to get the location
+ * of the offending instruction that created the element node
+ * @return the constructed exception object
+ */
+
+ public static NoOpenStartTagException makeNoOpenStartTagException(
+ int nodeKind, String name, int hostLanguage, boolean parentIsDocument, boolean isSerializing,
+ /*@Nullable*/ final LocationProvider locationProvider, int startElementLocationId) {
+ String message;
+ String errorCode;
+ if (parentIsDocument) {
+ if (isSerializing) {
+ String kind = (nodeKind == Type.ATTRIBUTE ? "attribute" : "namespace");
+ message = "Cannot serialize a free-standing " + kind + " node (" + name + ')';
+ errorCode = "SENR0001";
+ } else {
+ String kind = (nodeKind == Type.ATTRIBUTE ? "an attribute" : "a namespace");
+ message = "Cannot create " + kind + " node (" + name + ") whose parent is a document node";
+ errorCode = (hostLanguage == Configuration.XSLT ? "XTDE0420" : "XPTY0004");
+ }
+ } else {
+ String kind = (nodeKind == Type.ATTRIBUTE ? "An attribute" : "A namespace");
+ message = kind + " node (" + name + ") cannot be created after a child of the containing element";
+ errorCode = (hostLanguage == Configuration.XSLT ? "XTDE0410" : "XQTY0024");
+ }
+ if (locationProvider != null && startElementLocationId > 0) {
+ message += ". Most recent element start tag was output at line " +
+ locationProvider.getLineNumber(startElementLocationId) + " of module " +
+ StandardErrorListener.abbreviatePath(locationProvider.getSystemId(startElementLocationId));
+ }
+ NoOpenStartTagException err = new NoOpenStartTagException(message);
+ err.setErrorCode(errorCode);
+ return err;
+ }
+
+ public NoOpenStartTagException(String message) {
+ super(message);
+ }
+
+// public NoOpenStartTagException(int nodeKind, String name, int hostLanguage, boolean topLevel, boolean isSerializing) {
+// // The contorted conditional here is because super() has to be at the start of the method
+// super((topLevel ?
+// (isSerializing ?
+// "Cannot serialize ")
+// ("Cannot create " +
+// (nodeKind==Type.ATTRIBUTE ? "an attribute" : "a namespace") +
+// " node (" + name + ") whose parent is a document node")
+// :
+// (nodeKind==net.sf.saxon.type.Type.ATTRIBUTE ? "An attribute" : "A namespace") +
+// " node (" + name + ") cannot be created after the children of the containing element"
+// ));
+// if (hostLanguage == Configuration.XSLT) {
+// setErrorCode(topLevel ? "XTDE0420" : "XTDE0410");
+// } else {
+// setErrorCode(topLevel ? "XPTY0004" : "XQTY0024");
+// }
+// }
+
+}
+
diff --git a/sf/saxon/event/OnEmptyHandler.java b/sf/saxon/event/OnEmptyHandler.java
new file mode 100644
index 0000000..f613834
--- /dev/null
+++ b/sf/saxon/event/OnEmptyHandler.java
@@ -0,0 +1,122 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This receiver is inserted into the output pipeline whenever on-empty is used (XSLT 3.0)
+ * on a literal result element, on xsl:element, or on xsl:copy when processing elements.
+ * It delays writing the start tag until significant content is encountered (anything except
+ * namespaces and empty text nodes), and if no significant content has been encountered when the
+ * endElement even occurs, it evaluates the onEmpty expression in place of the original element
+ * constructor.
+ */
+public class OnEmptyHandler extends ProxyReceiver {
+
+ boolean processingParent = true;
+ Expression onEmpty;
+ XPathContext context;
+
+ NodeName savedNodeName;
+ SchemaType savedSchemaType;
+ int savedLocationId;
+ int savedProperties;
+ List<NamespaceBinding> savedNamespaces = new ArrayList<NamespaceBinding>(4);
+
+ public OnEmptyHandler(Receiver next, Expression onEmpty, XPathContext context) {
+ super(next);
+ this.onEmpty = onEmpty;
+ this.context = context;
+ }
+
+ private void flush() throws XPathException {
+ if (savedNodeName != null) {
+ nextReceiver.startElement(savedNodeName, savedSchemaType, savedLocationId, savedProperties);
+ for (NamespaceBinding binding : savedNamespaces) {
+ nextReceiver.namespace(binding, 0);
+ }
+ savedNodeName = null;
+ }
+ }
+
+ @Override
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (processingParent) {
+ savedNodeName = elemName;
+ savedSchemaType = typeCode;
+ savedLocationId = locationId;
+ savedProperties = properties;
+ } else {
+ flush();
+ super.startElement(elemName, typeCode, locationId, properties);
+ }
+ }
+
+ @Override
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ if (savedNodeName != null) {
+ savedNamespaces.add(namespaceBinding);
+ } else {
+ super.namespace(namespaceBinding, properties);
+ }
+ }
+
+ @Override
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ flush();
+ super.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+
+ @Override
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (chars.length() > 0) {
+ flush();
+ super.characters(chars, locationId, properties);
+ }
+ }
+
+ @Override
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ flush();
+ super.processingInstruction(target, data, locationId, properties);
+ }
+
+ @Override
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ flush();
+ super.comment(chars, locationId, properties);
+ }
+
+ @Override
+ public void append(Item item, int locationId, int copyNamespaces) throws XPathException {
+ flush();
+ super.append(item, locationId, copyNamespaces);
+ }
+
+ @Override
+ public void endElement() throws XPathException {
+ if (savedNodeName != null) {
+ // implies we have reached the end of the element without encountering any content
+ onEmpty.process(context);
+ } else {
+ super.endElement();
+ }
+ }
+}
+
diff --git a/sf/saxon/event/PIGrabber.java b/sf/saxon/event/PIGrabber.java
new file mode 100644
index 0000000..451ac04
--- /dev/null
+++ b/sf/saxon/event/PIGrabber.java
@@ -0,0 +1,165 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.lib.StandardURIResolver;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.ProcInstParser;
+import net.sf.saxon.type.SchemaType;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The PIGrabber class is a Receiver that looks for xml-stylesheet processing
+ * instructions and tests whether they match specified criteria; for those that do, it creates
+ * an InputSource object referring to the relevant stylesheet
+ * @author Michael H. Kay
+ */
+
+public class PIGrabber extends ProxyReceiver {
+
+ private Configuration config = null;
+ private String reqMedia = null;
+ private String reqTitle = null;
+ private String baseURI = null;
+ private URIResolver uriResolver = null;
+ private List<String> stylesheets = new ArrayList<String>();
+ private boolean terminated = false;
+
+ public PIGrabber(Receiver next) {
+ super(next);
+ }
+
+ public void setFactory(Configuration config) {
+ this.config = config;
+ }
+
+ public void setCriteria(String media, String title) {
+ this.reqMedia = media;
+ this.reqTitle = title;
+ }
+
+ /**
+ * Set the base URI
+ * @param uri the base URI
+ */
+
+ public void setBaseURI(String uri) {
+ baseURI = uri;
+ }
+
+ /**
+ * Set the URI resolver to be used for the href attribute
+ * @param resolver the URI resolver
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ uriResolver = resolver;
+ }
+
+ /**
+ * Abort the parse when the first start element tag is found
+ */
+
+ public void startElement (NodeName namecode, SchemaType typecode, int locationId, int properties)
+ throws XPathException {
+ terminated = true;
+ // abort the parse when the first start element tag is found
+ throw new XPathException("#start#");
+ }
+
+ /**
+ * Determine whether the parse terminated because the first start element tag was found
+ * @return true if the parse was terminated when the document element was encountered (as distinct
+ * from being terminated because of some exception condition, for example a parse error)
+ */
+
+ public boolean isTerminated() {
+ return terminated;
+ }
+
+ /**
+ * Handle xml-stylesheet PI
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties)
+ throws XPathException {
+ if (target.equals("xml-stylesheet")) {
+
+ String value = data.toString();
+ String piMedia = ProcInstParser.getPseudoAttribute(value, "media");
+ String piTitle = ProcInstParser.getPseudoAttribute(value, "title");
+ String piType = ProcInstParser.getPseudoAttribute(value, "type");
+ String piAlternate = ProcInstParser.getPseudoAttribute(value, "alternate");
+
+ if (piType==null) return;
+
+ // System.err.println("Found xml-stylesheet media=" + piMedia + " title=" + piTitle);
+
+ if ( (piType.equals("text/xml") || piType.equals("application/xml") ||
+ piType.equals("text/xsl") || piType.equals("applicaton/xsl") || piType.equals("application/xml+xslt")) &&
+
+ (reqMedia==null || piMedia==null || reqMedia.equals(piMedia)) &&
+
+ ( ( piTitle==null && (piAlternate==null || piAlternate.equals("no"))) ||
+ ( reqTitle==null ) ||
+ ( piTitle!=null && piTitle.equals(reqTitle) ) ) )
+ {
+ String href = ProcInstParser.getPseudoAttribute(value, "href");
+ if (href==null) {
+ throw new XPathException("xml-stylesheet PI has no href attribute");
+ }
+
+ // System.err.println("Adding " + href);
+ if (piTitle==null && (piAlternate==null || piAlternate.equals("no"))) {
+ stylesheets.add(0, href);
+ } else {
+ stylesheets.add(href);
+ }
+ } else {
+ //System.err.println("No match on required media=" + reqMedia + " title=" + reqTitle );
+ }
+ }
+ }
+
+ /**
+ * Return list of stylesheets that matched, as an array of Source objects
+ * @return null if there were no matching stylesheets.
+ * @throws net.sf.saxon.trans.XPathException if a URI cannot be resolved
+ */
+
+ /*@Nullable*/ public Source[] getAssociatedStylesheets() throws TransformerException {
+ if (stylesheets.size()==0) {
+ return null;
+ }
+ if (uriResolver==null) {
+ uriResolver = new StandardURIResolver(config);
+ }
+ Source[] result = new Source[stylesheets.size()];
+ for (int i=0; i<stylesheets.size(); i++) {
+ String href = stylesheets.get(i);
+ Source s = uriResolver.resolve(href, baseURI);
+ if (s instanceof SAXSource) {
+ ((SAXSource)s).setXMLReader(config.getStyleParser());
+ }
+ if (s == null) {
+ s = config.getSystemURIResolver().resolve(href, baseURI);
+ }
+ result[i] = s;
+ }
+ return result;
+ }
+
+}//
\ No newline at end of file
diff --git a/sf/saxon/event/PathMaintainer.java b/sf/saxon/event/PathMaintainer.java
new file mode 100644
index 0000000..34ffe0f
--- /dev/null
+++ b/sf/saxon/event/PathMaintainer.java
@@ -0,0 +1,106 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.AbsolutePath;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+
+import java.util.HashMap;
+import java.util.Stack;
+
+
+/**
+ * This class sits in a receiver (push) pipeline and maintains the current path.
+ * @author Michael H. Kay
+ */
+
+
+public class PathMaintainer extends ProxyReceiver {
+
+ private Stack<AbsolutePath.PathElement> path = new Stack<AbsolutePath.PathElement>();
+ private Stack<HashMap<NodeName, Integer>> siblingCounters =
+ new Stack<HashMap<NodeName, Integer>>();
+
+ public PathMaintainer(/*@NotNull*/ Receiver next) {
+ super(next);
+ siblingCounters.push(new HashMap<NodeName, Integer>());
+ }
+
+
+ public void startElement (NodeName elemName, SchemaType type, int locationId, int properties) throws XPathException
+ {
+ // System.err.println("startElement " + nameCode);
+ nextReceiver.startElement(elemName, type, locationId, properties);
+ HashMap<NodeName, Integer> counters = siblingCounters.peek();
+ int index = 1;
+ Integer preceding = counters.get(elemName);
+ if (preceding != null) {
+ index = preceding + 1;
+ counters.put(elemName, index);
+ } else {
+ counters.put(elemName, 1);
+ }
+ path.push(new AbsolutePath.PathElement(Type.ELEMENT, elemName, index));
+ siblingCounters.push(new HashMap<NodeName, Integer>());
+ }
+
+ /**
+ * Handle an end-of-element event
+ */
+
+ public void endElement () throws XPathException {
+ nextReceiver.endElement();
+ siblingCounters.pop();
+ path.pop();
+ }
+
+ /**
+ * Get the path to the current location in the stream
+ * @param useURIs set to true if namespace URIs are to appear in the path;
+ * false if prefixes are to be used instead. The prefix will be the one
+ * that is used in the source document, and is potentially ambiguous.
+ * @return the path to the current location, as a string.
+ */
+
+ public String getPath(boolean useURIs) {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ for (AbsolutePath.PathElement pe : path) {
+ fsb.append('/');
+ if (useURIs) {
+ String uri = pe.getName().getURI();
+ if (uri.length()!=0) {
+ fsb.append('"');
+ fsb.append(uri);
+ fsb.append('"');
+ }
+ } else {
+ String prefix = pe.getName().getPrefix();
+ if (prefix.length()!=0) {
+ fsb.append(prefix);
+ fsb.append(':');
+ }
+ }
+ fsb.append(pe.getName().getLocalPart());
+ fsb.append('[');
+ fsb.append(pe.getIndex()+"");
+ fsb.append(']');
+ }
+ return fsb.toString();
+ }
+
+ public AbsolutePath getAbsolutePath() {
+ return new AbsolutePath(path);
+ }
+
+
+}
+
diff --git a/sf/saxon/event/PipelineConfiguration.java b/sf/saxon/event/PipelineConfiguration.java
new file mode 100644
index 0000000..293c631
--- /dev/null
+++ b/sf/saxon/event/PipelineConfiguration.java
@@ -0,0 +1,445 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.SchemaURIResolver;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.URIResolver;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * A PipelineConfiguration sets options that apply to all the operations in a pipeline.
+ * Unlike the global Configuration, these options are always local to a process.
+ */
+
+public class PipelineConfiguration {
+
+ /*@NotNull*/ private Configuration config;
+ private LocationProvider locationProvider;
+ private URIResolver uriResolver;
+ private SchemaURIResolver schemaURIResolver;
+ private Controller controller;
+ private Stack<Item> currentApplyStack;
+ private ParseOptions parseOptions;
+ private boolean isSerializing;
+ private int hostLanguage = Configuration.XSLT;
+ private Map<String, Object> components;
+
+ /**
+ * Create a PipelineConfiguration. Note: the normal way to create
+ * a PipelineConfiguration is via the factory methods in the Controller and
+ * Configuration classes
+ *
+ * @param config the Saxon configuration
+ * @see Configuration#makePipelineConfiguration
+ * @see Controller#makePipelineConfiguration
+ */
+
+ public PipelineConfiguration(/*@NotNull*/ Configuration config) {
+ this.config = config;
+ parseOptions = new ParseOptions();
+ }
+
+ /**
+ * Create a PipelineConfiguration as a copy of an existing
+ * PipelineConfiguration
+ *
+ * @param p the existing PipelineConfiguration
+ */
+
+ public PipelineConfiguration(PipelineConfiguration p) {
+ config = p.config;
+ locationProvider = p.locationProvider;
+ uriResolver = p.uriResolver;
+ schemaURIResolver = p.schemaURIResolver;
+ controller = p.controller;
+ isSerializing = p.isSerializing;
+ parseOptions = new ParseOptions(p.parseOptions);
+ hostLanguage = p.hostLanguage;
+ if (p.components != null) {
+ components = new HashMap<String, Object>(p.components);
+ }
+ }
+
+ /**
+ * Get the Saxon Configuration object
+ *
+ * @return the Saxon Configuration
+ */
+
+ /*@NotNull*/
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Set the Saxon Configuration object
+ *
+ * @param config the Saxon Configuration
+ */
+
+ public void setConfiguration(/*@NotNull*/ Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Get the LocationProvider for interpreting location ids passed down this pipeline
+ *
+ * @return the appropriate LocationProvider
+ */
+
+ public LocationProvider getLocationProvider() {
+ if (locationProvider == null) {
+ locationProvider = DummyLocationProvider.THE_INSTANCE;
+ }
+ return locationProvider;
+ }
+
+ /**
+ * Set the LocationProvider for interpreting location ids passed down this pipeline
+ *
+ * @param locationProvider the LocationProvider
+ */
+
+ public void setLocationProvider(LocationProvider locationProvider) {
+ this.locationProvider = locationProvider;
+ }
+
+ /**
+ * Get a SourceLocator for a given locationId, using this location provider
+ *
+ * @param locationId an integer identifier for a source location
+ * @return an object holding location information
+ */
+
+ public SourceLocator getSourceLocation(long locationId) {
+ return new ExpressionLocation(locationProvider, locationId);
+ }
+
+ /**
+ * Get the ErrorListener set as a property of this pipeline
+ *
+ * @return the ErrorListener; null if none has been set.
+ */
+
+ public ErrorListener getLocalErrorListener() {
+ return parseOptions.getErrorListener();
+ }
+
+ /**
+ * Get an ErrorListener for reporting errors in processing this pipeline; this
+ * will be the ErrorListener set locally in the PipelineConfiguration if there is one,
+ * or the ErrorListener from the Configuration otherwise.
+ *
+ * @return the ErrorListener to be used; never null
+ */
+
+ public ErrorListener getErrorListener() {
+ ErrorListener listener = parseOptions.getErrorListener();
+ if (listener == null) {
+ listener = config.getErrorListener();
+ }
+ return listener;
+ }
+
+ /**
+ * Set the ErrorListener used for reporting errors in processing this pipeline
+ *
+ * @param errorListener the ErrorListener
+ */
+
+ public void setErrorListener(ErrorListener errorListener) {
+ parseOptions.setErrorListener(errorListener);
+ }
+
+ /**
+ * Get the URIResolver used for processing URIs encountered on this pipeline
+ *
+ * @return the URIResolver
+ */
+
+ public URIResolver getURIResolver() {
+ return uriResolver;
+ }
+
+ /**
+ * Set the URIResolver used for processing URIs encountered on this pipeline
+ *
+ * @param uriResolver the URIResolver
+ */
+
+ public void setURIResolver(URIResolver uriResolver) {
+ this.uriResolver = uriResolver;
+ }
+
+ /**
+ * Get the user-defined SchemaURIResolver for resolving URIs used in "import schema"
+ * declarations; returns null if none has been explicitly set.
+ *
+ * @return the SchemaURIResolver
+ */
+
+ public SchemaURIResolver getSchemaURIResolver() {
+ return schemaURIResolver;
+ }
+
+ /**
+ * Set the document parsing and building options to be used on this pipeline
+ *
+ * @param options the options to be used
+ */
+
+ public void setParseOptions(ParseOptions options) {
+ parseOptions = options;
+ }
+
+ /**
+ * Get the document parsing and building options to be used on this pipeline
+ * return the options to be used
+ *
+ * @return the parser options for this pipeline
+ */
+
+ public ParseOptions getParseOptions() {
+ return parseOptions;
+ }
+
+ /**
+ * Say whether xsi:schemaLocation and xsi:noNamespaceSchemaLocation attributes
+ * should be recognized while validating an instance document
+ *
+ * @param recognize true if these attributes should be recognized
+ */
+
+ public void setUseXsiSchemaLocation(boolean recognize) {
+ parseOptions.setUseXsiSchemaLocation(recognize);
+ }
+
+ /**
+ * Say whether validation errors encountered on this pipeline should be treated as fatal
+ * or as recoverable.
+ * <p>Note this is a shortcut for <code>getParseOptions().setContinueAfterValidationErrors()</code>, retained
+ * for backwards compatibility.</p>
+ *
+ * @param recover set to true if validation errors are to be treated as recoverable. If this option is set to true,
+ * such errors will be reported to the ErrorListener using the error() method, and validation will continue.
+ * If it is set to false, errors will be reported using the fatalError() method, and validation will
+ * be abandoned. The default depends on the circumstances: typically during standalone instance validation
+ * the default is true, but during XSLT and XQuery processing it is false.
+ */
+
+ public void setRecoverFromValidationErrors(boolean recover) {
+ parseOptions.setContinueAfterValidationErrors(recover);
+ }
+
+ /**
+ * Ask if this pipeline recovers from validation errors
+ * <p>Note this is a shortcut for <code>getParseOptions().isContinueAfterValidationErrors()</code>, retained
+ * for backwards compatibility.</p>
+ *
+ * @return true if validation errors on this pipeline are treated as recoverable; false if they are treated
+ * as fatal
+ */
+
+ public boolean isRecoverFromValidationErrors() {
+ return parseOptions.isContinueAfterValidationErrors();
+ }
+
+ /**
+ * Set a user-defined SchemaURIResolver for resolving URIs used in "import schema"
+ * declarations.
+ *
+ * @param resolver the SchemaURIResolver
+ */
+
+ public void setSchemaURIResolver(SchemaURIResolver resolver) {
+ schemaURIResolver = resolver;
+ }
+
+ /**
+ * Get the controller associated with this pipelineConfiguration
+ *
+ * @return the controller if it is known; otherwise null.
+ */
+
+ public Controller getController() {
+ return controller;
+ }
+
+ /**
+ * Set the context item. Used only for diagnostics, to indicate
+ * what part of the source document is being processed. Maintained
+ * only by XSLT instructions that change the context, e.g. for-each,
+ * apply-templates, iterate, and for-each-group
+ *
+ * @param contextIterator the context iterator (whose current item is the context item)
+ */
+
+ public void pushCurrentAppliedItem(Item item) {
+ if (currentApplyStack == null) {
+ currentApplyStack = new Stack<Item>();
+ }
+ currentApplyStack.push(item);
+ }
+
+ /**
+ * Get the context item. Used only for diagnostics, to indicate
+ * what part of the source document is being processed. Maintained
+ * only by XSLT instructions that change the context, e.g. for-each,
+ * apply-templates, iterate, and for-each-group
+ *
+ * @return context iterator (whose current item is the context item) if known, otherwise null
+ */
+
+ public void popCurrentAppliedItem() {
+ currentApplyStack.pop();
+ }
+
+ public Item peekCurrentAppliedItem() {
+ if (currentApplyStack != null && currentApplyStack.size() > 0) {
+ return currentApplyStack.peek();
+ } else {
+ return null;
+ }
+ }
+
+ public Stack<Item> getAppliedItemStack() {
+ return currentApplyStack;
+ }
+
+ /**
+ * Set the Controller associated with this pipelineConfiguration
+ *
+ * @param controller the Controller
+ */
+
+ public void setController(Controller controller) {
+ this.controller = controller;
+ }
+
+ /**
+ * Get the host language in use
+ *
+ * @return for example {@link Configuration#XSLT} or {@link Configuration#XQUERY}
+ */
+
+ public int getHostLanguage() {
+ return hostLanguage;
+ }
+
+ /**
+ * Set the host language in use
+ *
+ * @param language for example {@link Configuration#XSLT} or {@link Configuration#XQUERY}
+ */
+
+ public void setHostLanguage(int language) {
+ hostLanguage = language;
+ }
+
+ /**
+ * Ask whether this pipeline is a serializing pipeline
+ *
+ * @return true if this pipeline is producing serialized output
+ */
+
+ public boolean isSerializing() {
+ return isSerializing;
+ }
+
+ /**
+ * Set whether this pipeline is a serializing pipeline
+ *
+ * @param isSerializing true if this pipeline is producing serialized output
+ */
+
+ public void setSerializing(boolean isSerializing) {
+ this.isSerializing = isSerializing;
+ }
+
+ /**
+ * Set whether attribute defaults defined in a schema or DTD are to be expanded or not
+ * (by default, fixed and default attribute values are expanded, that is, they are inserted
+ * into the document during validation as if they were present in the instance being validated)
+ *
+ * @param expand true if defaults are to be expanded, false if not
+ */
+
+ public void setExpandAttributeDefaults(boolean expand) {
+ parseOptions.setExpandAttributeDefaults(expand);
+ }
+
+ /**
+ * Ask whether attribute defaults defined in a schema or DTD are to be expanded or not
+ * (by default, fixed and default attribute values are expanded, that is, they are inserted
+ * into the document during validation as if they were present in the instance being validated)
+ *
+ * @return true if defaults are to be expanded, false if not
+ */
+
+ public boolean isExpandAttributeDefaults() {
+ return parseOptions.isExpandAttributeDefaults();
+ }
+
+ /**
+ * Set a named component of the pipeline
+ *
+ * @param name string the component name
+ * @param value the component value
+ */
+
+ public void setComponent(String name, Object value) {
+ if (components == null) {
+ components = new HashMap<String, Object>();
+ }
+ components.put(name, value);
+ }
+
+ /**
+ * Get a named component of the pipeline
+ *
+ * @param name string the component name
+ * @return the component value, or null if absent
+ */
+
+ public Object getComponent(String name) {
+ if (components == null) {
+ return null;
+ } else {
+ return components.get(name);
+ }
+ }
+
+ private static class DummyLocationProvider implements LocationProvider {
+
+ public final static DummyLocationProvider THE_INSTANCE = new DummyLocationProvider();
+
+ public String getSystemId(long locationId) {
+ return null;
+ }
+
+ public int getLineNumber(long locationId) {
+ return -1;
+ }
+
+ public int getColumnNumber(long locationId) {
+ return -1;
+ }
+ }
+}
+
diff --git a/sf/saxon/event/ProxyReceiver.java b/sf/saxon/event/ProxyReceiver.java
new file mode 100644
index 0000000..2fc9993
--- /dev/null
+++ b/sf/saxon/event/ProxyReceiver.java
@@ -0,0 +1,251 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+/**
+ * A ProxyReceiver is an Receiver that filters data before passing it to another
+ * underlying Receiver.
+ */
+
+public abstract class ProxyReceiver extends SequenceReceiver {
+
+ /*@NotNull*/
+ protected Receiver nextReceiver;
+
+ public ProxyReceiver(/*@NotNull*/ Receiver nextReceiver) {
+ super(nextReceiver.getPipelineConfiguration());
+ setUnderlyingReceiver(nextReceiver);
+ setPipelineConfiguration(nextReceiver.getPipelineConfiguration());
+ }
+
+ public void setSystemId(String systemId) {
+ //noinspection StringEquality
+ if (systemId != this.systemId) {
+ // use of == rather than equals() is deliberate, since this is only an optimization
+ this.systemId = systemId;
+ nextReceiver.setSystemId(systemId);
+ }
+ }
+
+ /**
+ * Set the underlying receiver. This call is mandatory before using the Receiver.
+ * @param receiver the underlying receiver, the one that is to receive events after processing
+ * by this filter.
+ */
+
+ public void setUnderlyingReceiver(/*@NotNull*/ Receiver receiver) {
+ if (receiver != nextReceiver) {
+ nextReceiver = receiver;
+ }
+ }
+
+ /**
+ * Get the underlying Receiver (that is, the next one in the pipeline)
+ * @return the underlying Receiver (that is, the next one in the pipeline)
+ */
+
+ /*@Nullable*/ public Receiver getUnderlyingReceiver() {
+ return nextReceiver;
+ }
+
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipe) {
+ if (pipelineConfiguration != pipe) {
+ pipelineConfiguration = pipe;
+ if (nextReceiver.getPipelineConfiguration() != pipe) {
+ nextReceiver.setPipelineConfiguration(pipe);
+ }
+ }
+ }
+
+ /**
+ * Get the namepool for this configuration
+ */
+
+ public NamePool getNamePool() {
+ return pipelineConfiguration.getConfiguration().getNamePool();
+ }
+
+ /**
+ * Start of event stream
+ */
+
+ public void open() throws XPathException {
+ nextReceiver.open();
+ }
+
+ /**
+ * End of output. Note that closing this receiver also closes the rest of the
+ * pipeline.
+ */
+
+ public void close() throws XPathException {
+ // Note: It's wrong to assume that because we've finished writing to this
+ // receiver, then we've also finished writing to other receivers in the pipe.
+ // In the case where the rest of the pipe is to stay open, the caller should
+ // either avoid doing the close(), or should first set the underlying receiver
+ // to null.
+ nextReceiver.close();
+ }
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ nextReceiver.startDocument(properties);
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ nextReceiver.endDocument();
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param elemName integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties properties of the element node
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ nextReceiver.startElement(elemName, typeCode, locationId, properties);
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element; however, duplicates may be reported.
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ *
+ * @param namespaceBinding the prefix/uri pair representing the namespace binding
+ * @param properties any special properties to be passed on this call
+ * @throws IllegalStateException: attempt to output a namespace when there is no open element
+ * start tag
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ nextReceiver.namespace(namespaceBinding, properties);
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param nameCode The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+
+
+ public void startContent() throws XPathException {
+ nextReceiver.startContent();
+ }
+
+ /**
+ * End of element
+ */
+
+ public void endElement() throws XPathException {
+ nextReceiver.endElement();
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ nextReceiver.characters(chars, locationId, properties);
+ }
+
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ nextReceiver.processingInstruction(target, data, locationId, properties);
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ nextReceiver.comment(chars, locationId, properties);
+ }
+
+
+ /**
+ * Set the URI for an unparsed entity in the document.
+ */
+
+ public void setUnparsedEntity(String name, String uri, String publicId) throws XPathException {
+ nextReceiver.setUnparsedEntity(name, uri, publicId);
+ }
+
+ /**
+ * Append an arbitrary item (node or atomic value) to the output
+ *
+ * @param item the item to be appended
+ * @param locationId the location of the calling instruction, for diagnostics
+ * @param copyNamespaces if the item is an element node, this indicates whether its namespaces
+* need to be copied. Values are {@link net.sf.saxon.om.NodeInfo#ALL_NAMESPACES},
+* {@link net.sf.saxon.om.NodeInfo#LOCAL_NAMESPACES}, {@link net.sf.saxon.om.NodeInfo#NO_NAMESPACES}
+ */
+
+ public void append(Item item, int locationId, int copyNamespaces) throws XPathException {
+ if (nextReceiver instanceof SequenceReceiver) {
+ ((SequenceReceiver)nextReceiver).append(item, locationId, copyNamespaces);
+ } else {
+ throw new UnsupportedOperationException("append() method is not supported in this class");
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return nextReceiver.usesTypeAnnotations();
+ }
+}
+
diff --git a/sf/saxon/event/Receiver.java b/sf/saxon/event/Receiver.java
new file mode 100644
index 0000000..2d829ed
--- /dev/null
+++ b/sf/saxon/event/Receiver.java
@@ -0,0 +1,230 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+import javax.xml.transform.Result;
+/**
+ * Receiver: This interface represents a recipient of XML tree-walking (push) events. It is
+ * based on SAX2's ContentHandler, but adapted to handle additional events, and
+ * to use Saxon's name pool. Namespaces and Attributes are handled by separate events
+ * following the startElement event. Schema types can be defined for elements and attributes.
+ * <p>
+ * The Receiver interface is an important internal interface within Saxon, and provides a powerful
+ * mechanism for integrating Saxon with other applications. It has been designed with extensibility
+ * and stability in mind. However, it should be considered as an interface designed primarily for
+ * internal use, and not as a completely stable part of the public Saxon API.
+ * <p>
+ * @author Michael H. Kay
+ */
+
+public interface Receiver extends Result {
+
+ /**
+ * Set the pipeline configuration
+ * @param pipe the pipeline configuration
+ */
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipe);
+
+ /**
+ * Get the pipeline configuration
+ * @return the pipeline configuration
+ */
+
+ /*@NotNull*/
+ public PipelineConfiguration getPipelineConfiguration();
+
+ /**
+ * Set the System ID of the tree represented by this event stream
+ * @param systemId the system ID (which is used as the base URI of the nodes
+ * if there is no xml:base attribute)
+ */
+
+ public void setSystemId(String systemId);
+
+ /**
+ * Notify the start of the event stream
+ * @throws XPathException if an error occurs
+ */
+
+ public void open() throws XPathException;
+
+ /**
+ * Notify the start of a document node
+ * @param properties bit-significant integer indicating properties of the document node.
+ * The definitions of the bits are in class {@link ReceiverOptions}
+ * @throws XPathException if an error occurs
+ */
+
+ public void startDocument(int properties) throws XPathException;
+
+ /**
+ * Notify the end of a document node
+ * @throws XPathException if an error occurs
+ */
+
+ public void endDocument() throws XPathException;
+
+ /**
+ * Notify an unparsed entity URI.
+ * @param name The name of the unparsed entity
+ * @param systemID The system identifier of the unparsed entity
+ * @param publicID The public identifier of the unparsed entity
+ * @throws XPathException if an error occurs
+ */
+
+ public void setUnparsedEntity(String name, String systemID, String publicID) throws XPathException;
+
+ /**
+ * Notify the start of an element
+ *
+ * @param elemName the name of the element.
+ * @param typeCode the type annotation of the element.
+ * @param locationId an integer which can be interpreted using a {@link LocationProvider} to return
+ * information such as line number and system ID. If no location information is available,
+ * the value zero is supplied.
+ * @param properties bit-significant properties of the element node. If there are no revelant
+ * properties, zero is supplied. The definitions of the bits are in class {@link ReceiverOptions}
+ * @throws XPathException if an error occurs
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties)
+ throws XPathException;
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element. The events represent namespace
+ * declarations and undeclarations rather than in-scope namespace nodes: an undeclaration is represented
+ * by a namespace code of zero. If the sequence of namespace events contains two
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ * @param namespaceBinding contains the namespace prefix and namespace URI
+ * @param properties The most important property is REJECT_DUPLICATES. If this property is set, the
+ * namespace declaration will be rejected if it conflicts with a previous declaration of the same
+ * prefix. If the property is not set, the namespace declaration will be ignored if it conflicts
+ * with a previous declaration. This reflects the fact that when copying a tree, namespaces for child
+ * elements are emitted before the namespaces of their parent element. Unfortunately this conflicts
+ * with the XSLT rule for complex content construction, where the recovery action in the event of
+ * conflicts is to take the namespace that comes last. XSLT therefore doesn't recover from this error:
+ * @throws XPathException if an error occurs
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException;
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ * @param attName The name of the attribute
+ * @param typeCode The type of the attribute, as held in the name pool. The additional bit
+ * NodeInfo.IS_DTD_TYPE may be set to indicate a DTD-derived type.
+ * @param value the string value of the attribute
+ * @param locationId an integer which can be interpreted using a {@link net.sf.saxon.event.LocationProvider} to return
+ * information such as line number and system ID. If no location information is available,
+ * the value zero is supplied.
+ * @param properties Bit significant value. The following bits are defined:
+ * <dt>DISABLE_ESCAPING</dt> <dd>Disable escaping for this attribute</dd>
+ * <dt>NO_SPECIAL_CHARACTERS</dt> <dd>Attribute value contains no special characters</dd>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ * @throws XPathException if an error occurs
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException;
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ * @throws XPathException if an error occurs
+ */
+
+ public void startContent() throws XPathException;
+
+ /**
+ * Notify the end of an element. The receiver must maintain a stack if it needs to know which
+ * element is ending.
+ * @throws XPathException if an error occurs
+ */
+
+ public void endElement() throws XPathException;
+
+ /**
+ * Notify character data. Note that some receivers may require the character data to be
+ * sent in a single event, but in general this is not a requirement.
+ * @param chars The characters
+ * @param locationId an integer which can be interpreted using a {@link net.sf.saxon.event.LocationProvider}
+ * to return information such as line number and system ID. If no location information is available,
+ * the value zero is supplied.
+ * @param properties Bit significant value. The following bits are defined:
+ * <dt>DISABLE_ESCAPING</dt> <dd>Disable escaping for this text node</dd>
+ * <dt>USE_CDATA</dt> <dd>Output as a CDATA section</dd>
+ * @throws XPathException if an error occurs
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties)
+ throws XPathException;
+
+ /**
+ * Output a processing instruction
+ * @param name The PI name. This must be a legal name (it will not be checked).
+ * @param data The data portion of the processing instruction
+ * @param locationId an integer which can be interpreted using a {@link LocationProvider} to return
+ * information such as line number and system ID. If no location information is available,
+ * the value zero is supplied.
+ * @param properties Additional information about the PI.
+ * @throws IllegalArgumentException: the content is invalid for an XML processing instruction
+ * @throws XPathException if an error occurs
+ */
+
+ public void processingInstruction(String name, CharSequence data, int locationId, int properties)
+ throws XPathException;
+
+ /**
+ * Notify a comment. Comments are only notified if they are outside the DTD.
+ * @param content The content of the comment
+ * @param locationId an integer which can be interpreted using a {@link LocationProvider} to return
+ * information such as line number and system ID. If no location information is available,
+ * the value zero is supplied.
+ * @param properties Additional information about the comment.
+ * @throws IllegalArgumentException: the content is invalid for an XML comment
+ * @throws XPathException if an error occurs
+ */
+
+ public void comment(CharSequence content, int locationId, int properties) throws XPathException;
+
+ /**
+ * Notify the end of the event stream
+ * @throws XPathException if an error occurs
+ */
+
+ public void close() throws XPathException;
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation (or conversely, it may
+ * avoid stripping unwanted type annotations)
+ */
+
+ public boolean usesTypeAnnotations();
+
+
+
+}
+
diff --git a/sf/saxon/event/ReceiverOptions.java b/sf/saxon/event/ReceiverOptions.java
new file mode 100644
index 0000000..5a9f9c2
--- /dev/null
+++ b/sf/saxon/event/ReceiverOptions.java
@@ -0,0 +1,133 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+
+/**
+ * ReceiverOptions defines a set of constants, which can be used in
+ * calls to methods on the Receiver interface. The values are
+ * bit-significant.
+ *
+ * @author Michael H. Kay
+ */
+
+
+public class ReceiverOptions {
+
+ /**
+ * Flag to disable output escaping
+ */
+
+ public static final int DISABLE_ESCAPING = 1;
+
+ /**
+ * Flag to disable use of character maps
+ */
+
+ public static final int DISABLE_CHARACTER_MAPS = 2;
+
+ /**
+ * Flag indicating that the value contains no special characters
+ * that need to be escaped
+ */
+
+ public static final int NO_SPECIAL_CHARS = 4;
+
+ /**
+ * Flag indicating that an attribute value was added by the schema processor
+ * because a default value was specified
+ */
+
+ public static final int DEFAULTED_ATTRIBUTE = 8;
+
+ /**
+ * Flag used with character content that has been validated against a nillable element
+ * declaration. Needed because of a peculiar rule for validating xs:key values
+ */
+
+ public static final int NILLED_ELEMENT = 16;
+
+ /**
+ * Flag indicating that duplicate values should be rejected
+ */
+
+ public static final int REJECT_DUPLICATES = 32;
+
+ /**
+ * Flag indicating that the namespace (of an element or attribute name)
+ * has already been declared; it does not need to be generated by the namespace
+ * fixup process.
+ */
+
+ public static final int NAMESPACE_OK = 64;
+
+ /**
+ * Flag passed on startElement indicating that the element does not inherit
+ * the namespaces of its ancestors.
+ */
+
+ public static final int DISINHERIT_NAMESPACES = 128;
+
+ /**
+ * Flag used when an attribute value or text node contains null characters
+ * before and after strings generated by character mapping; these strings
+ * are to be output without escaping
+ */
+
+ public static final int USE_NULL_MARKERS = 256;
+
+ /**
+ * Flag used with character content that has been validated against a nillable element
+ * declaration. Needed because of a peculiar rule for validating xs:key values
+ */
+
+ public static final int NILLABLE_ELEMENT = 512;
+
+ /**
+ * Flag used with the characters() event to indicate that the event represents an entire
+ * text node, that is, the text node has not been fragmented over several characters() events
+ */
+
+ public static final int WHOLE_TEXT_NODE = 1024;
+
+ /**
+ * Flag indicating an element or attribute that has the is-id property
+ */
+
+ public static final int IS_ID = 2048;
+
+ /**
+ * Flag indicating an element or attribute that has the is-idref property (indicating that it is an IDREF
+ * or IDREFS attribute)
+ */
+
+ public static final int IS_IDREF = 4096;
+
+ /**
+ * Flag indicating that the ID/IDREF properties have been set if applicable: if this bit is set,
+ * then the absence of the IS_ID bit means the node is not an ID, and similarly for IS_IDREF
+ */
+
+ public static final int ID_IDREF_CHECKED = 8192;
+
+ /**
+ * Flag set on startDocument() in relation to an xsl:message call with terminate="yes"
+ */
+
+ public static final int TERMINATE = 16384;
+
+ /**
+ * Flag set on startDocument() to indicate that the constructed document must be updateable
+ */
+
+ public static final int MUTABLE_TREE = 32768;
+
+
+
+}
+
diff --git a/sf/saxon/event/ReceivingContentHandler.java b/sf/saxon/event/ReceivingContentHandler.java
new file mode 100644
index 0000000..9deffb2
--- /dev/null
+++ b/sf/saxon/event/ReceivingContentHandler.java
@@ -0,0 +1,698 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.tree.tiny.CompressedWhitespace;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.*;
+import org.xml.sax.ext.Attributes2;
+import org.xml.sax.ext.LexicalHandler;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.TransformerException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+
+/**
+ * ReceivingContentHandler is a glue class that provides a standard SAX ContentHandler
+ * interface to a Saxon Receiver. To achieve this it needs to map names supplied
+ * as strings to numeric name codes, for which purpose it needs access to a name
+ * pool. The class also performs the function of assembling adjacent text nodes.
+ * <p>The class was previously named ContentEmitter.</p>
+ * <p>If the input stream contains the processing instructions assigned by JAXP to switch
+ * disable-output-escaping on or off, these will be reflected in properties set in the corresponding
+ * characters events. In this case adjacent text nodes will not be combined.
+ * @author Michael H. Kay
+ */
+
+public class ReceivingContentHandler
+ implements ContentHandler, LexicalHandler, DTDHandler //, SaxonLocator, SourceLocationProvider
+{
+ private PipelineConfiguration pipe;
+ private Receiver receiver;
+ private boolean inDTD = false; // true while processing the DTD
+ private Locator locator; // a SAX Locator
+ private LocalLocator localLocator = new LocalLocator();
+
+ // buffer for accumulating character data, until the next markup event is received
+
+ private char[] buffer = new char[512];
+ private int charsUsed = 0;
+ private CharSlice slice = new CharSlice(buffer, 0, 0);
+
+ // array for accumulating namespace information
+
+ private NamespaceBinding[] namespaces = new NamespaceBinding[20];
+ private int namespacesUsed = 0;
+
+ // determine whether ignorable whitespace is ignored
+
+ private boolean ignoreIgnorable = false;
+
+ // determine whether DTD attribute types are retained
+
+ private boolean retainDTDAttributeTypes = false;
+
+ // determine whether DTD attribute value defaults should be suppressed
+
+ private boolean suppressDTDAttributeDefaults = false;
+
+ // indicate that escaping is allowed to be disabled using the JAXP-defined processing instructions
+
+ private boolean allowDisableOutputEscaping = false;
+
+ // indicate that escaping is disabled
+
+ private boolean escapingDisabled = false;
+
+ // flag to indicate whether the last tag was a start tag or an end tag
+
+ private boolean afterStartTag = true;
+
+ /**
+ * A local cache is used to avoid allocating namecodes for the same name more than once.
+ * This reduces contention on the NamePool. This is a two-level hashmap: the first level
+ * has the namespace URI as its key, and returns a HashMap which maps lexical QNames to integer
+ * namecodes.
+ */
+
+ private HashMap<String, HashMap<Object, NodeName>> nameCache = new HashMap<String, HashMap<Object, NodeName>>(10);
+ private HashMap<Object, NodeName> noNamespaceNameCache = new HashMap<Object, NodeName>(10);
+
+// private static Class attributes2class;
+// private static Method isSpecifiedMethod;
+
+
+ /**
+ * Create a ReceivingContentHandler and initialise variables
+ */
+
+ public ReceivingContentHandler() {
+ }
+
+ /**
+ * Set the ReceivingContentHandler to its initial state, except for the local name cache,
+ * which is retained
+ */
+
+ public void reset() {
+ pipe = null;
+ receiver = null;
+ ignoreIgnorable = false;
+ retainDTDAttributeTypes = false;
+ charsUsed = 0;
+ slice.setLength(0);
+ namespacesUsed = 0;
+ locator = null;
+ allowDisableOutputEscaping = false;
+ escapingDisabled = false;
+ }
+
+ /**
+ * Set the receiver to which events are passed. ReceivingContentHandler is essentially a translator
+ * that takes SAX events as input and produces Saxon Receiver events as output; these Receiver events
+ * are passed to the supplied Receiver
+ * @param receiver the Receiver of events
+ */
+
+ public void setReceiver(Receiver receiver) {
+ this.receiver = receiver;
+ //receiver = new TracingFilter(receiver);
+ }
+
+ /**
+ * Get the receiver to which events are passed.
+ * @return the underlying Receiver
+ */
+
+ public Receiver getReceiver() {
+ return receiver;
+ }
+
+ /**
+ * Set the pipeline configuration
+ * @param pipe the pipeline configuration. This holds a reference to the Saxon configuration, as well as
+ * information that can vary from one pipeline to another, for example the LocationProvider which resolves
+ * the location of events in a source document
+ */
+
+ public void setPipelineConfiguration(PipelineConfiguration pipe) {
+ this.pipe = pipe;
+ pipe.setLocationProvider(localLocator);
+ Configuration config = pipe.getConfiguration();
+ ignoreIgnorable = pipe.getParseOptions().getStripSpace() != Whitespace.NONE;
+ retainDTDAttributeTypes = config.getBooleanProperty(FeatureKeys.RETAIN_DTD_ATTRIBUTE_TYPES);
+ suppressDTDAttributeDefaults = !pipe.isExpandAttributeDefaults();
+ allowDisableOutputEscaping = (Boolean)config.getConfigurationProperty(FeatureKeys.USE_PI_DISABLE_OUTPUT_ESCAPING);
+ }
+
+ /**
+ * Get the pipeline configuration
+ * @return the pipeline configuration as supplied to
+ {@link #setPipelineConfiguration(PipelineConfiguration)}
+ */
+
+ public PipelineConfiguration getPipelineConfiguration() {
+ return pipe;
+ }
+
+ /**
+ * Get the Configuration object
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return pipe.getConfiguration();
+ }
+
+ /**
+ * Set whether "ignorable whitespace" should be ignored. This method is effective only
+ * if called after setPipelineConfiguration, since the default value is taken from the
+ * configuration.
+ * @param ignore true if ignorable whitespace (whitespace in element content that is notified
+ * via the {@link #ignorableWhitespace(char[], int, int)} method) should be ignored, false if
+ * it should be treated as ordinary text.
+ */
+
+ public void setIgnoreIgnorableWhitespace(boolean ignore) {
+ ignoreIgnorable = ignore;
+ }
+
+ /**
+ * Determine whether "ignorable whitespace" is ignored. This returns the value that was set
+ * using {@link #setIgnoreIgnorableWhitespace} if that has been called; otherwise the value
+ * from the configuration.
+ * @return true if ignorable whitespace is being ignored
+ */
+
+ public boolean isIgnoringIgnorableWhitespace() {
+ return ignoreIgnorable;
+ }
+
+ /**
+ * Receive notification of the beginning of a document.
+ */
+
+ public void startDocument () throws SAXException {
+// System.err.println("ReceivingContentHandler#startDocument");
+ try {
+ charsUsed = 0;
+ namespacesUsed = 0;
+ pipe.setLocationProvider(localLocator);
+ receiver.setPipelineConfiguration(pipe);
+ receiver.open();
+ receiver.startDocument(0);
+ } catch (XPathException err) {
+ throw new SAXException(err);
+ }
+ }
+
+ /**
+ * Receive notification of the end of a document
+ */
+
+ public void endDocument () throws SAXException {
+ // System.err.println("RCH: end document");
+ try {
+ flush(true);
+ receiver.endDocument();
+ receiver.close();
+ } catch (ValidationException err) {
+ err.setLocator(locator);
+ throw new SAXException(err);
+ } catch (XPathException err) {
+ throw new SAXException(err);
+ }
+ }
+
+ /**
+ * Supply a locator that can be called to give information about location in the source document
+ * being parsed.
+ */
+
+ public void setDocumentLocator (Locator locator) {
+ this.locator = locator;
+ }
+
+ /**
+ * Notify a namespace prefix to URI binding
+ */
+
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ //System.err.println("StartPrefixMapping " + prefix + "=" + uri);
+ if (prefix.equals("xmlns")) {
+ // the binding xmlns:xmlns="http://www.w3.org/2000/xmlns/"
+ // should never be reported, but it's been known to happen
+ return;
+ }
+ if (namespacesUsed >= namespaces.length) {
+ NamespaceBinding[] n2 = new NamespaceBinding[namespacesUsed * 2];
+ System.arraycopy(namespaces, 0, n2, 0, namespacesUsed);
+ namespaces = n2;
+ }
+ namespaces[namespacesUsed++] = new NamespaceBinding(prefix, uri);
+ }
+
+ /**
+ * Notify that a namespace binding is going out of scope
+ */
+
+ public void endPrefixMapping(String prefix) throws SAXException {}
+
+ /**
+ * Notify an element start event, including all the associated attributes
+ */
+
+ public void startElement (String uri, String localname, String rawname, Attributes atts)
+ throws SAXException
+ {
+// System.err.println("ReceivingContentHandler#startElement " +
+// uri + "," + localname + "," + rawname +
+// " at line " + locator.getLineNumber() + " of " + locator.getSystemId());
+ //for (int a=0; a<atts.getLength(); a++) {
+ // System.err.println(" Attribute " + atts.getURI(a) + "/" + atts.getLocalName(a) + "/" + atts.getQName(a));
+ //}
+ try {
+ flush(true);
+
+ NodeName elementName = getNodeName(uri, localname, rawname);
+ receiver.startElement(elementName, Untyped.getInstance(), 0, ReceiverOptions.NAMESPACE_OK);
+
+ for (int n=0; n<namespacesUsed; n++) {
+ receiver.namespace(namespaces[n], 0);
+ }
+
+ for (int a=0; a<atts.getLength(); a++) {
+ int properties = ReceiverOptions.NAMESPACE_OK;
+ String qname = atts.getQName(a);
+ if (qname.startsWith("xmlns") && (qname.equals("xmlns") || qname.startsWith("xmlns:"))) {
+ // We normally configure the parser so that it doesn't notify namespaces as attributes.
+ // But when running as a TransformerHandler, we have no control over the feature settings
+ // of the sender of the events. So we filter them out, just in case. There might be cases
+ // where we ought not just to ignore them, but to handle them as namespace events, but
+ // we'll cross that bridge when we come to it.
+ continue;
+ }
+ // Note JDK15 dependency on Attributes2.isSpecified()
+ if (suppressDTDAttributeDefaults
+ && atts instanceof Attributes2
+ && !((Attributes2)atts).isSpecified(qname)) {
+ continue;
+ }
+
+ NodeName attCode = getNodeName(atts.getURI(a), atts.getLocalName(a), atts.getQName(a));
+ String type = atts.getType(a);
+ SimpleType typeCode = BuiltInAtomicType.UNTYPED_ATOMIC;
+ if (retainDTDAttributeTypes) {
+ if (type.equals("CDATA")) {
+ // common case, no action
+ } else if (type.equals("ID")) {
+ typeCode = BuiltInAtomicType.ID;
+ } else if (type.equals("IDREF")) {
+ typeCode = BuiltInAtomicType.IDREF;
+ } else if (type.equals("IDREFS")) {
+ typeCode = BuiltInListType.IDREFS;
+ } else if (type.equals("NMTOKEN")) {
+ typeCode = BuiltInAtomicType.NMTOKEN;
+ } else if (type.equals("NMTOKENS")) {
+ typeCode = BuiltInListType.NMTOKENS;
+ } else if (type.equals("ENTITY")) {
+ typeCode = BuiltInAtomicType.ENTITY;
+ } else if (type.equals("ENTITIES")) {
+ typeCode = BuiltInListType.ENTITIES;
+ }
+ } else {
+ if (type.equals("ID")) {
+ properties |= ReceiverOptions.IS_ID;
+ } else if (type.equals("IDREF")) {
+ properties |= ReceiverOptions.IS_IDREF;
+ } else if (type.equals("IDREFS")) {
+ properties |= ReceiverOptions.IS_IDREF;
+ }
+ }
+
+ receiver.attribute(attCode, typeCode, atts.getValue(a), 0, properties);
+ }
+
+ afterStartTag = true;
+ receiver.startContent();
+
+ namespacesUsed = 0;
+ } catch (ValidationException err) {
+ if (err.getLineNumber() == -1) {
+ err.setLocator(locator);
+ }
+ throw new SAXException(err);
+ } catch (XPathException err) {
+ throw new SAXException(err);
+ }
+ }
+
+ /**
+ * Get the NamePool name code associated with a name appearing in the document
+ * @param uri the namespace URI
+ * @param localname the local part of the name
+ * @param rawname the lexical QName
+ * @return the NamePool name code, newly allocated if necessary
+ * @throws SAXException if the information supplied by the SAX parser is insufficient
+ */
+
+ private NodeName getNodeName(String uri, String localname, String rawname) throws SAXException {
+ // System.err.println("URI=" + uri + " local=" + " raw=" + rawname);
+ // The XML parser isn't required to report the rawname (qname), though all known parsers do.
+ // If none is provided, we give up
+ if (rawname.length() == 0) {
+ throw new SAXException("Saxon requires an XML parser that reports the QName of each element");
+ }
+ // It's also possible (especially when using a TransformerHandler) that the parser
+ // has been configured to report the QName rather than the localname+URI
+ if (localname.length() == 0) {
+ throw new SAXException("Parser configuration problem: namespace reporting is not enabled");
+ }
+
+ // Following code maintains a local cache to remember all the element names that have been
+ // allocated, which reduces contention on the NamePool. It also avoids parsing the lexical QName
+ // when the same name is used repeatedly. We also get a tiny improvement by avoiding the first hash
+ // table lookup for names in the null namespace.
+
+ HashMap<Object, NodeName> map2 = (uri.length() == 0 ? noNamespaceNameCache : nameCache.get(uri));
+ if (map2 == null) {
+ map2 = new HashMap<Object, NodeName>(50);
+ nameCache.put(uri, map2);
+ if (uri.length() == 0) {
+ noNamespaceNameCache = map2;
+ }
+ }
+
+ NodeName n = map2.get(rawname);
+ // we use the rawname (qname) rather than the local name because we want a namecode rather than
+ // a fingerprint - that is, the prefix matters.
+ if (n == null) {
+ if (uri.length()==0) {
+ NoNamespaceName qn = new NoNamespaceName(localname);
+ map2.put(rawname, qn);
+ return qn;
+ } else {
+ String prefix = NameChecker.getPrefix(rawname);
+ FingerprintedQName qn = new FingerprintedQName(prefix, uri, localname);
+ map2.put(rawname, qn);
+ return qn;
+ }
+ } else {
+ return n;
+ }
+
+ }
+
+
+
+
+ /**
+ * Report the end of an element (the close tag)
+ */
+
+ public void endElement (String uri, String localname, String rawname) throws SAXException {
+ //System.err.println("ReceivingContentHandler#End element " + rawname);
+ try {
+ // don't attempt whitespace compression if this end tag follows a start tag
+ flush(!afterStartTag);
+ receiver.endElement();
+ } catch (ValidationException err) {
+ err.maybeSetLocation(ExpressionLocation.makeFromSax(locator));
+ if (!err.hasBeenReported()) {
+ try {
+ pipe.getErrorListener().fatalError(err);
+ } catch (TransformerException e) {
+ //
+ }
+ }
+ err.setHasBeenReported(true);
+ throw new SAXException(err);
+ } catch (XPathException err) {
+ throw new SAXException(err);
+ }
+ afterStartTag = false;
+ }
+
+ /**
+ * Report character data. Note that contiguous character data may be reported as a sequence of
+ * calls on this method, with arbitrary boundaries
+ */
+
+ public void characters (char ch[], int start, int length) throws SAXException {
+ // System.err.println("characters (" + length + ")");
+ // need to concatenate chunks of text before we can decide whether a node is all-white
+
+ while (charsUsed + length > buffer.length) {
+ char[] newbuffer = new char[buffer.length*2];
+ System.arraycopy(buffer, 0, newbuffer, 0, charsUsed);
+ buffer = newbuffer;
+ slice = new CharSlice(buffer, 0, 0);
+ }
+ System.arraycopy(ch, start, buffer, charsUsed, length);
+ charsUsed += length;
+ }
+
+ /**
+ * Report character data classified as "Ignorable whitespace", that is, whitespace text nodes
+ * appearing as children of elements with an element-only content model
+ */
+
+ public void ignorableWhitespace (char ch[], int start, int length) throws SAXException {
+ if (!ignoreIgnorable) {
+ characters(ch, start, length);
+ }
+ }
+
+ /**
+ * Notify the existence of a processing instruction
+ */
+
+ public void processingInstruction (String name, String remainder) throws SAXException {
+ try {
+ flush(true);
+ if (!inDTD) {
+ if (name==null) {
+ // trick used by the old James Clark xp parser to notify a comment
+ comment(remainder.toCharArray(), 0, remainder.length());
+ } else {
+ // some parsers allow through PI names containing colons
+ if (!getConfiguration().getNameChecker().isValidNCName(name)) {
+ throw new SAXException("Invalid processing instruction name (" + name + ')');
+ }
+ if (allowDisableOutputEscaping) {
+ if (name.equals(Result.PI_DISABLE_OUTPUT_ESCAPING)) {
+ //flush();
+ escapingDisabled = true;
+ return;
+ } else if (name.equals(Result.PI_ENABLE_OUTPUT_ESCAPING)) {
+ //flush();
+ escapingDisabled = false;
+ return;
+ }
+ }
+ receiver.processingInstruction(name, Whitespace.removeLeadingWhitespace(remainder), 0, 0);
+ }
+ }
+ } catch (XPathException err) {
+ throw new SAXException(err);
+ }
+ }
+
+ /**
+ * Notify the existence of a comment. Note that in SAX this is part of LexicalHandler interface
+ * rather than the ContentHandler interface.
+ */
+
+ public void comment (char ch[], int start, int length) throws SAXException {
+ try {
+ flush(true);
+ if (!inDTD) {
+ receiver.comment(new CharSlice(ch, start, length), 0, 0);
+ }
+ } catch (XPathException err) {
+ throw new SAXException(err);
+ }
+ }
+
+ /**
+ * Flush buffer for accumulated character data
+ * @param compress true if compression of whitespace should be attempted. This is an expensive
+ * operation, so we avoid doing it when we hit an end tag that follows after a start tag, as
+ * it's not likely to succeed in that situation.
+ * @throws XPathException if flushing the character data fails
+ */
+
+ private void flush(boolean compress) throws XPathException {
+ if (charsUsed > 0) {
+ slice.setLength(charsUsed);
+ CharSequence cs = (compress ? CompressedWhitespace.compress(slice) : slice);
+ receiver.characters(cs, 0,
+ escapingDisabled ? ReceiverOptions.DISABLE_ESCAPING : ReceiverOptions.WHOLE_TEXT_NODE);
+ charsUsed = 0;
+ escapingDisabled = false;
+ }
+ }
+
+ /**
+ * Notify a skipped entity. Saxon ignores this event
+ */
+
+ public void skippedEntity(String name) throws SAXException {}
+
+ // No-op methods to satisfy lexical handler interface
+
+ /**
+ * Register the start of the DTD. Saxon ignores the DTD; however, it needs to know when the DTD starts and
+ * ends so that it can ignore comments in the DTD, which are reported like any other comment, but which
+ * are skipped because they are not part of the XPath data model
+ */
+
+ public void startDTD (String name, String publicId, String systemId) throws SAXException {
+ inDTD = true;
+ }
+
+ /**
+ * Register the end of the DTD. Comments in the DTD are skipped because they
+ * are not part of the XPath data model
+ */
+
+ public void endDTD () throws SAXException {
+ inDTD = false;
+ }
+
+ public void startEntity (String name) throws SAXException {}
+
+ public void endEntity (String name) throws SAXException {}
+
+ public void startCDATA () throws SAXException {}
+
+ public void endCDATA () throws SAXException {}
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Implement DTDHandler interface
+ //////////////////////////////////////////////////////////////////////////////
+
+
+ public void notationDecl( String name,
+ String publicId,
+ String systemId) throws SAXException
+ {}
+
+
+ public void unparsedEntityDecl( String name,
+ String publicId,
+ String systemId,
+ String notationName) throws SAXException {
+ // Some (non-conformant) SAX parsers report the systemId as written.
+ // We need to turn it into an absolute URL.
+
+ String uri = systemId;
+ if (locator != null) {
+ try {
+ URI suppliedURI = new URI(systemId);
+ if (!suppliedURI.isAbsolute()) {
+ String baseURI = locator.getSystemId();
+ if (baseURI != null) { // See bug 2167979
+ URI absoluteURI = new URI(baseURI).resolve(systemId);
+ uri = absoluteURI.toString();
+ }
+ }
+ } catch (URISyntaxException err) {
+ uri = systemId; // fallback
+ }
+ }
+ try {
+ receiver.setUnparsedEntity(name, uri, publicId);
+ } catch (XPathException err) {
+ throw new SAXException(err);
+ }
+ }
+
+
+
+
+ private class LocalLocator implements SaxonLocator, SourceLocationProvider {
+
+ // This class is needed to bridge a SAX Locator to a JAXP SourceLocator
+
+ /**
+ * Return the system identifier for the current document event.
+ * @return A string containing the system identifier, or
+ * null if none is available.
+ */
+
+ public String getSystemId() {
+ return (locator == null ? null : locator.getSystemId());
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ */
+
+ public String getPublicId() {
+ return (locator==null ? null : locator.getPublicId());
+ }
+
+ /**
+ * Return the line number where the current document event ends.
+ * @return The line number, or -1 if none is available.
+ */
+
+ public int getLineNumber() {
+ return (locator==null ? -1 : locator.getLineNumber());
+ }
+
+ /**
+ * Return the character position where the current document event ends.
+ * @return The column number, or -1 if none is available.
+ */
+
+ public int getColumnNumber() {
+ return (locator==null ? -1 : locator.getColumnNumber());
+ }
+
+ /**
+ * Get the line number within the document or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the line number within the document or module.
+ */
+
+ public int getLineNumber(long locationId) {
+ return (locator==null ? -1 : locator.getLineNumber());
+ }
+
+ public int getColumnNumber(long locationId) {
+ return (locator==null ? -1 : locator.getColumnNumber());
+ }
+
+ /**
+ * Get the URI of the document or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the URI of the document or module.
+ */
+
+ /*@Nullable*/ public String getSystemId(long locationId) {
+ return (locator == null ? null : locator.getSystemId());
+ }
+ }
+
+} // end of class ReceivingContentHandler
+
diff --git a/sf/saxon/event/SaxonLocator.java b/sf/saxon/event/SaxonLocator.java
new file mode 100644
index 0000000..9510465
--- /dev/null
+++ b/sf/saxon/event/SaxonLocator.java
@@ -0,0 +1,20 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+import org.xml.sax.Locator;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * SaxonLocator: this interface exists to unify the SAX Locator and JAXP SourceLocator interfaces,
+ * which are identical. It extends both interfaces. Therefore, anything
+ * that implements SaxonLocator can be used both in SAX and in JAXP interfaces.
+ */
+
+public interface SaxonLocator extends Locator, SourceLocator, LocationProvider {}
+
diff --git a/sf/saxon/event/Sender.java b/sf/saxon/event/Sender.java
new file mode 100644
index 0000000..2d55fe2
--- /dev/null
+++ b/sf/saxon/event/Sender.java
@@ -0,0 +1,570 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.evpull.EventIteratorToReceiver;
+import net.sf.saxon.evpull.PullEventSource;
+import net.sf.saxon.expr.number.Numberer_en;
+import net.sf.saxon.lib.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pull.PullProvider;
+import net.sf.saxon.pull.PullPushCopier;
+import net.sf.saxon.pull.PullSource;
+import net.sf.saxon.pull.StaxBridge;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.*;
+
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+* Sender is a helper class that sends events to a Receiver from any kind of Source object
+*/
+
+public abstract class Sender {
+
+ // Converted to an abstract static class in Saxon 9.3
+
+ private static Class[] EMPTY_CLASS_ARRAY = new Class[0];
+ private static Class staxSourceClass;
+
+ static {
+ try {
+ staxSourceClass = Class.forName("javax.xml.transform.stax.StAXSource");
+ } catch (Exception err) {
+ // no action; if StAXSource isn't available then we don't use it.
+ }
+ }
+
+ private Sender() {}
+
+ /**
+ * Send the contents of a Source to a Receiver.
+ * @param source the source to be copied. Note that if the Source contains an InputStream
+ * or Reader then it will be left open, unless it is an AugmentedSource with the pleaseCloseAfterUse
+ * flag set. On the other hand, if it contains a URI that needs to be dereferenced to obtain
+ * an InputStream, then the InputStream will be closed after use.
+ * @param receiver the destination to which it is to be copied. The pipelineConfiguration
+ * of this receiver must have been initialized.
+ * @param options Parse options. If source is an AugmentedSource, any options set in the
+ * AugmentedSource are used in preference to those set in options. If neither specifies
+ * a particular option, the defaults from the Configuration are used. If null is supplied,
+ * the parse options from the PipelineConfiguration of the receiver are used.
+ * @throws net.sf.saxon.trans.XPathException if any error occurs
+ */
+
+ public static void send(Source source, Receiver receiver, /*@Nullable*/ ParseOptions options)
+ throws XPathException {
+ PipelineConfiguration pipe = receiver.getPipelineConfiguration();
+ if (options == null) {
+ options = new ParseOptions(pipe.getParseOptions());
+ } else {
+ options = new ParseOptions(options);
+ }
+ if (source instanceof AugmentedSource) {
+ options.merge(((AugmentedSource)source).getParseOptions());
+ source = ((AugmentedSource)source).getContainedSource();
+ }
+ Configuration config = pipe.getConfiguration();
+ options.applyDefaults(config);
+
+ receiver.setSystemId(source.getSystemId());
+ Receiver next = receiver;
+
+ int schemaValidation = options.getSchemaValidationMode();
+
+ List<FilterFactory> filters = options.getFilters();
+ if (filters != null) {
+ for (int i=filters.size()-1; i>=0; i--) {
+ ProxyReceiver filter = filters.get(i).makeFilter(next);
+ filter.setSystemId(source.getSystemId());
+ next = filter;
+ }
+ }
+
+ if (options.getStripSpace() == Whitespace.ALL) {
+ next = new Stripper(AllElementsSpaceStrippingRule.getInstance(), receiver);
+ } else if (options.getStripSpace() == Whitespace.XSLT) {
+ Controller controller = pipe.getController();
+ if (controller != null) {
+ next = controller.makeStripper(next);
+ }
+ }
+
+ if (source instanceof NodeInfo) {
+ NodeInfo ns = (NodeInfo)source;
+ String baseURI = ns.getBaseURI();
+ if (schemaValidation != Validation.PRESERVE) {
+ next = config.getDocumentValidator(next, baseURI, options);
+ }
+
+ int kind = ns.getNodeKind();
+ if (kind != Type.DOCUMENT && kind != Type.ELEMENT) {
+ throw new IllegalArgumentException("Sender can only handle document or element nodes");
+ }
+ next.setSystemId(baseURI);
+ sendDocumentInfo(ns, next);
+ return;
+
+ } else if (source instanceof PullSource) {
+ sendPullSource((PullSource)source, next, options);
+ return;
+
+ } else if (source instanceof PullEventSource) {
+ sendPullEventSource((PullEventSource)source, next, options);
+ return;
+
+ } else if (source instanceof EventSource) {
+ ((EventSource)source).send(next);
+ return;
+
+ } else if (source instanceof Transmitter) {
+ ((Transmitter)source).transmit(next);
+ return;
+
+ } else if (source instanceof SAXSource) {
+ sendSAXSource((SAXSource)source, next, options);
+ return;
+
+ } else if (source instanceof StreamSource) {
+ StreamSource ss = (StreamSource)source;
+ // Following code allows the .NET platform to use a Pull parser
+ boolean dtdValidation = options.getDTDValidationMode() == Validation.STRICT;
+ Source ps = Configuration.getPlatform().getParserSource(
+ pipe, ss, schemaValidation, dtdValidation, options.getStripSpace());
+ if (ps == ss) {
+ String url = source.getSystemId();
+ InputSource is = new InputSource(url);
+ is.setCharacterStream(ss.getReader());
+ is.setByteStream(ss.getInputStream());
+ boolean reuseParser = false;
+ XMLReader parser = options.getXMLReader();
+ if (parser == null) {
+ parser = config.getSourceParser();
+ if (options.getEntityResolver() != null && parser.getEntityResolver() == null) {
+ parser.setEntityResolver(options.getEntityResolver());
+ }
+ reuseParser = true;
+ }
+ //System.err.println("Using parser: " + parser.getClass().getName());
+ SAXSource sax = new SAXSource(parser, is);
+ sax.setSystemId(source.getSystemId());
+ sendSAXSource(sax, next, options);
+ if (reuseParser) {
+ config.reuseSourceParser(parser);
+ }
+
+ } else {
+ // the Platform substituted a different kind of source
+ // On .NET with a default URIResolver we can expect an AugnmentedSource wrapping a PullSource
+ send(ps, next, options);
+ }
+ return;
+ } else if (staxSourceClass != null && staxSourceClass.isAssignableFrom(source.getClass())) {
+ // Test for a StAXSource
+ // Use reflection to avoid problems if JAXP 1.4 not installed
+ XMLStreamReader reader = null;
+ try {
+ Method getReaderMethod = staxSourceClass.getMethod("getXMLStreamReader", EMPTY_CLASS_ARRAY);
+ reader = (XMLStreamReader)getReaderMethod.invoke(source);
+ } catch (Exception e) {
+ // no action
+ }
+ //XMLStreamReader reader = ((StAXSource)source).getXMLStreamReader();
+ if (reader == null) {
+ throw new XPathException("Saxon can only handle a StAXSource that wraps an XMLStreamReader");
+ }
+ StaxBridge bridge = new StaxBridge();
+ bridge.setXMLStreamReader(reader);
+ sendPullSource(new PullSource(bridge), next, options);
+ return;
+ } else {
+ next = makeValidator(next, source.getSystemId(), options);
+
+ // See if there is a registered SourceResolver than can handle it
+ Source newSource = config.getSourceResolver().resolveSource(source, config);
+ if (newSource instanceof StreamSource ||
+ newSource instanceof SAXSource ||
+ newSource instanceof Transmitter ||
+ newSource instanceof NodeInfo ||
+ newSource instanceof PullSource ||
+ newSource instanceof AugmentedSource ||
+ newSource instanceof EventSource) {
+ send(newSource, next, options);
+ }
+
+ // See if there is a registered external object model that knows about this kind of source
+ // (Note, this should pick up the platform-specific DOM model)
+
+ List externalObjectModels = config.getExternalObjectModels();
+ for (Object externalObjectModel : externalObjectModels) {
+ ExternalObjectModel model = (ExternalObjectModel) externalObjectModel;
+ boolean done = model.sendSource(source, next);
+ if (done) {
+ return;
+ }
+ }
+
+ }
+
+ throw new XPathException("A source of type " + source.getClass().getName() +
+ " is not supported in this environment");
+ }
+
+ /**
+ * Send a copy of a Saxon NodeInfo representing a document or element node to a receiver
+ * @param top the root of the subtree to be send. Despite the method name, this can be a document
+ * node or an element node
+ * @param receiver the destination to receive the events
+ * @throws XPathException if any error occurs
+ */
+
+
+ private static void sendDocumentInfo(NodeInfo top, Receiver receiver)
+ throws XPathException {
+ PipelineConfiguration pipe = receiver.getPipelineConfiguration();
+ NamePool targetNamePool = pipe.getConfiguration().getNamePool();
+ if (top.getNamePool() != targetNamePool) {
+ // This code allows a document in one Configuration to be copied to another, changing
+ // namecodes as necessary
+ receiver = new NamePoolConverter(receiver, top.getNamePool(), targetNamePool);
+ }
+ LocationCopier copier = new LocationCopier(top instanceof DocumentInfo);
+ pipe.setComponent(CopyInformee.class.getName(), copier);
+ pipe.setLocationProvider(copier);
+
+ // start event stream
+ receiver.open();
+
+ // copy the contents of the document
+ receiver.startDocument(0);
+ top.copy(receiver, (CopyOptions.ALL_NAMESPACES | CopyOptions.TYPE_ANNOTATIONS), 0);
+ receiver.endDocument();
+
+ // end event stream
+ receiver.close();
+ }
+
+ /**
+ * Send the contents of a SAXSource to a given Receiver
+ * @param source the SAXSource
+ * @param receiver the destination Receiver
+ * @param options options for parsing the SAXSource
+ * @throws XPathException if any failure occurs processing the Source object
+ */
+
+ private static void sendSAXSource(SAXSource source, Receiver receiver, ParseOptions options)
+ throws XPathException {
+ PipelineConfiguration pipe = receiver.getPipelineConfiguration();
+ XMLReader parser = source.getXMLReader();
+ boolean reuseParser = false;
+ final Configuration config = pipe.getConfiguration();
+ ErrorListener listener = options.getErrorListener();
+ if (listener == null) {
+ listener = pipe.getErrorListener();
+ }
+ ErrorHandler errorHandler = options.getErrorHandler();
+ if (errorHandler == null) {
+ errorHandler = new StandardErrorHandler(listener);
+ }
+ if (parser==null) {
+ parser = options.getXMLReader();
+ }
+ if (parser==null) {
+ SAXSource ss = new SAXSource();
+ ss.setInputSource(source.getInputSource());
+ ss.setSystemId(source.getSystemId());
+ parser = config.getSourceParser();
+ parser.setErrorHandler(errorHandler);
+ if (options.getEntityResolver() != null && parser.getEntityResolver() == null) {
+ parser.setEntityResolver(options.getEntityResolver());
+ }
+ ss.setXMLReader(parser);
+ source = ss;
+ reuseParser = true;
+ } else {
+ // user-supplied parser: ensure that it meets the namespace requirements
+ configureParser(parser);
+ if (parser.getErrorHandler() == null) {
+ parser.setErrorHandler(errorHandler);
+ }
+ }
+
+ if (!pipe.isExpandAttributeDefaults()) { //TODO: put this in ParseOptions
+ try {
+ parser.setFeature("http://xml.org/sax/features/use-attributes2", true);
+ } catch (SAXNotRecognizedException err) {
+ // ignore the failure, we did our best (Xerces gives us an Attribute2 even though it
+ // doesn't recognize this request!)
+ } catch (SAXNotSupportedException err) {
+ // ignore the failure, we did our best
+ }
+ }
+
+ boolean dtdValidation = (options.getDTDValidationMode() == Validation.STRICT ||
+ options.getDTDValidationMode() == Validation.LAX);
+ boolean dtdRecover = options.getDTDValidationMode() == Validation.LAX;
+ try {
+ parser.setFeature("http://xml.org/sax/features/validation", dtdValidation);
+ } catch (SAXNotRecognizedException err) {
+ if (dtdValidation) {
+ throw new XPathException("XML Parser does not recognize request for DTD validation", err);
+ }
+ } catch (SAXNotSupportedException err) {
+ if (dtdValidation) {
+ throw new XPathException("XML Parser does not support DTD validation", err);
+ }
+ }
+
+ boolean xInclude = options.isXIncludeAware();
+ if (xInclude) {
+ boolean tryAgain = false;
+ try {
+ // This feature name is supported in the version of Xerces bundled with JDK 1.5
+ parser.setFeature("http://apache.org/xml/features/xinclude-aware", true);
+ } catch (SAXNotRecognizedException err) {
+ tryAgain = true;
+ } catch (SAXNotSupportedException err) {
+ tryAgain = true;
+ }
+ if (tryAgain) {
+ try {
+ // This feature name is supported in Xerces 2.9.0
+ parser.setFeature("http://apache.org/xml/features/xinclude", true);
+ } catch (SAXNotRecognizedException err) {
+ throw new XPathException("Selected XML parser " + parser.getClass().getName() +
+ " does not recognize request for XInclude processing", err);
+ } catch (SAXNotSupportedException err) {
+ throw new XPathException("Selected XML parser " + parser.getClass().getName() +
+ " does not support XInclude processing", err);
+ }
+ }
+ }
+// if (config.isTiming()) {
+// System.err.println("Using SAX parser " + parser);
+// }
+
+
+
+ receiver = makeValidator(receiver, source.getSystemId(), options);
+
+ // Reuse the previous ReceivingContentHandler if possible (it contains a useful cache of names)
+
+ ReceivingContentHandler ce;
+ final ContentHandler ch = parser.getContentHandler();
+ if (ch instanceof ReceivingContentHandler && config.isCompatible(((ReceivingContentHandler)ch).getConfiguration())) {
+ ce = (ReceivingContentHandler)ch;
+ ce.reset();
+ } else {
+ ce = new ReceivingContentHandler();
+ parser.setContentHandler(ce);
+ parser.setDTDHandler(ce);
+ try {
+ parser.setProperty("http://xml.org/sax/properties/lexical-handler", ce);
+ } catch (SAXNotSupportedException err) { // this just means we won't see the comments
+ // ignore the error
+ } catch (SAXNotRecognizedException err) {
+ // ignore the error
+ }
+ }
+// TracingFilter tf = new TracingFilter();
+// tf.setUnderlyingReceiver(receiver);
+// tf.setPipelineConfiguration(pipe);
+// receiver = tf;
+
+ ce.setReceiver(receiver);
+ ce.setPipelineConfiguration(pipe);
+
+ try {
+ parser.parse(source.getInputSource());
+ } catch (SAXException err) {
+ Exception nested = err.getException();
+ if (nested instanceof XPathException) {
+ throw (XPathException)nested;
+ } else if (nested instanceof RuntimeException) {
+ throw (RuntimeException)nested;
+ } else {
+ // Check for a couple of conditions where the error reporting needs to be improved.
+ // (a) The built-in parser for JDK 1.6 has a nasty habit of not notifying errors to the ErrorHandler
+ // (b) Sometimes (e.g. when given an empty file), the SAXException has no location information
+ if ((errorHandler instanceof StandardErrorHandler && ((StandardErrorHandler)errorHandler).getFatalErrorCount() == 0) ||
+ (err instanceof SAXParseException && ((SAXParseException)err).getSystemId()==null && source.getSystemId() != null)) {
+ //
+ XPathException de = new XPathException("Error reported by XML parser processing " +
+ source.getSystemId() + ": " + err.getMessage(), err);
+ try {
+ listener.fatalError(de);
+ de.setHasBeenReported(true);
+ } catch (TransformerException e) {
+ //
+ }
+ throw de;
+ } else {
+ XPathException de = new XPathException(err);
+ de.setErrorCode(SaxonErrorCode.SXXP0003);
+ de.setHasBeenReported(true);
+ throw de;
+ }
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException("I/O error reported by XML parser processing " +
+ source.getSystemId() + ": " + err.getMessage(), err);
+ }
+ if (errorHandler instanceof StandardErrorHandler) {
+ int errs = ((StandardErrorHandler)errorHandler).getFatalErrorCount();
+ if (errs > 0) {
+ throw new XPathException("The XML parser reported " + errs + (errs == 1 ? " error" : " errors"));
+ }
+ errs = ((StandardErrorHandler)errorHandler).getErrorCount();
+ if (errs > 0) {
+ String message = ("The XML parser reported " + new Numberer_en().toWords(errs).toLowerCase() +
+ " validation error" + (errs == 1 ? "" : "s"));
+ if (dtdRecover) {
+ message += ". Processing continues, because recovery from validation errors was requested";
+ try {
+ listener.warning(new XPathException(message));
+ } catch (TransformerException e) {
+ //
+ }
+ } else {
+ throw new XPathException(message);
+ }
+ }
+ }
+ if (reuseParser) {
+ config.reuseSourceParser(parser);
+ }
+ }
+
+ private static Receiver makeValidator(Receiver receiver, String systemId, ParseOptions options) throws XPathException {
+ PipelineConfiguration pipe = receiver.getPipelineConfiguration();
+ Configuration config = pipe.getConfiguration();
+ int sv = options.getSchemaValidationMode();
+ if (sv != Validation.PRESERVE && sv != Validation.DEFAULT) {
+ Controller controller = pipe.getController();
+ if (controller != null && !controller.getExecutable().isSchemaAware() && sv != Validation.STRIP) {
+ throw new XPathException("Cannot use schema-validated input documents when the query/stylesheet is not schema-aware");
+ }
+ // Add a document validator to the pipeline
+
+// ValidationContext vc = new ValidationContext(config.getConversionRules());
+// vc.setController(controller);
+// vc.setErrorLimit(options.getValidationErrorLimit());
+// vc.setValidationParams(options.getValidationParams());
+ receiver = config.getDocumentValidator(receiver, systemId, options);
+ }
+ return receiver;
+ }
+
+ /**
+ * Copy data from a pull source to a push destination
+ * @param source the pull source
+ * @param receiver the push destination
+ * @param options provides options for schema validation
+ * @throws XPathException if any error occurs
+ */
+
+ private static void sendPullSource(PullSource source, Receiver receiver, ParseOptions options)
+ throws XPathException {
+ PipelineConfiguration pipe = receiver.getPipelineConfiguration();
+ boolean xInclude = options.isXIncludeAware();
+ if (xInclude) {
+ throw new XPathException("XInclude processing is not supported with a pull parser");
+ }
+ receiver = makeValidator(receiver, source.getSystemId(), options);
+
+ PullProvider provider = source.getPullProvider();
+ if (provider instanceof LocationProvider) {
+ pipe.setLocationProvider((LocationProvider)provider);
+ }
+ provider.setPipelineConfiguration(pipe);
+ receiver.setPipelineConfiguration(pipe);
+ PullPushCopier copier = new PullPushCopier(provider, receiver);
+ try {
+ copier.copy();
+ } finally {
+ if (options.isPleaseCloseAfterUse()) {
+ provider.close();
+ }
+ }
+ }
+
+ private static void sendPullEventSource(PullEventSource source, Receiver receiver, ParseOptions options)
+ throws XPathException {
+ PipelineConfiguration pipe = receiver.getPipelineConfiguration();
+ boolean xInclude = options.isXIncludeAware();
+ if (xInclude) {
+ throw new XPathException("XInclude processing is not supported with a pull parser");
+ }
+ receiver = makeValidator(receiver, source.getSystemId(), options);
+
+ receiver.open();
+ EventIterator provider = source.getEventIterator();
+ if (provider instanceof LocationProvider) {
+ pipe.setLocationProvider((LocationProvider)provider);
+ }
+ receiver.setPipelineConfiguration(pipe);
+ SequenceReceiver out = receiver instanceof SequenceReceiver
+ ? ((SequenceReceiver)receiver)
+ : new TreeReceiver(receiver);
+ EventIteratorToReceiver.copy(provider, out);
+ receiver.close();
+ }
+
+
+ /**
+ * Configure a SAX parser to ensure it has the correct namesapce properties set
+ * @param parser the parser to be configured
+ * @throws net.sf.saxon.trans.XPathException if the parser cannot be configured to the
+ * required settings (namespaces=true, namespace-prefixes=false). Note that the SAX
+ * specification requires every XMLReader to support these settings, so this error implies
+ * that the XMLReader is non-conformant; this is not uncommon in cases where the XMLReader
+ * is user-written.
+ */
+
+ public static void configureParser(XMLReader parser) throws XPathException {
+ try {
+ parser.setFeature("http://xml.org/sax/features/namespaces", true);
+ } catch (SAXNotSupportedException err) { // SAX2 parsers MUST support this feature!
+ throw new XPathException(
+ "The SAX2 parser " + parser.getClass().getName() +
+ " does not recognize the 'namespaces' feature", err);
+ } catch (SAXNotRecognizedException err) {
+ throw new XPathException(
+ "The SAX2 parser " + parser.getClass().getName() +
+ " does not support setting the 'namespaces' feature to true", err);
+ }
+
+ try {
+ parser.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
+ } catch (SAXNotSupportedException err) { // SAX2 parsers MUST support this feature!
+ throw new XPathException(
+ "The SAX2 parser "+ parser.getClass().getName() +
+ " does not recognize the 'namespace-prefixes' feature", err);
+ } catch (SAXNotRecognizedException err) {
+ throw new XPathException(
+ "The SAX2 parser " + parser.getClass().getName() +
+ " does not support setting the 'namespace-prefixes' feature to false", err);
+ }
+
+ }
+
+}
+
diff --git a/sf/saxon/event/SequenceCopier.java b/sf/saxon/event/SequenceCopier.java
new file mode 100644
index 0000000..ed71ab2
--- /dev/null
+++ b/sf/saxon/event/SequenceCopier.java
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Copies a sequence, supplied as a SequenceIterator, to a push pipeline, represented by
+ * a SequenceReceiver
+ */
+public class SequenceCopier {
+
+ private SequenceCopier() {
+ }
+
+ public static void copySequence(SequenceIterator in, SequenceReceiver out) throws XPathException {
+ out.open();
+ while (true) {
+ Item item = in.next();
+ if (item == null) {
+ break;
+ }
+ out.append(item, 0, NodeInfo.ALL_NAMESPACES);
+ }
+ out.close();
+ }
+}
+
diff --git a/sf/saxon/event/SequenceOutputter.java b/sf/saxon/event/SequenceOutputter.java
new file mode 100644
index 0000000..3d2f2b1
--- /dev/null
+++ b/sf/saxon/event/SequenceOutputter.java
@@ -0,0 +1,171 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.ListIterator;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * This outputter is used when writing a sequence of atomic values and nodes, that
+ * is, when xsl:variable is used with content and an "as" attribute. The outputter
+ * builds the sequence and provides access to it. (It isn't really an outputter at all,
+ * it doesn't pass the events to anyone, it merely constructs the sequence in memory
+ * and provides access to it). Note that the event sequence can include calls such as
+ * startElement and endElement that require trees to be built. If nodes such as attributes
+ * and text nodes are received while an element is being constructed, the nodes are added
+ * to the tree. Otherwise, "orphan" nodes (nodes with no parent) are created and added
+ * directly to the sequence.
+ *
+ * <p>This class is not used to build temporary trees. For that, the ComplexContentOutputter
+ * is used.</p>
+ *
+ *
+ * @author Michael H. Kay
+ */
+
+public final class SequenceOutputter extends SequenceWriter {
+
+ private List<Item> list;
+ private Controller controller; // enables the SequenceOutputter to be reused
+
+
+ /**
+ * Create a new SequenceOutputter
+ * @param pipe the pipeline configuration
+ */
+
+ public SequenceOutputter(/*@NotNull*/ PipelineConfiguration pipe) {
+ super(pipe);
+ this.list = new ArrayList<Item>(50);
+ }
+
+ public SequenceOutputter(PipelineConfiguration pipe, Controller controller, int estimatedSize) {
+ super(pipe);
+ this.list = new ArrayList<Item>(estimatedSize);
+ this.controller = controller;
+ }
+
+ public SequenceOutputter(PipelineConfiguration pipe, Controller controller) {
+ super(pipe);
+ this.list = new ArrayList<Item>(50);
+ this.controller = controller;
+ }
+
+ /**
+ * Allocate a SequenceOutputter. Used from generated bytecode.
+ * @param context dynamic XPath context
+ * @param hostLang host language (XSLT/XQuery)
+ * @return the allocated SequenceOutputter
+ * @see com.saxonica.bytecode.util.CompilerService
+ */
+
+ /*@Nullable*/ public static SequenceOutputter allocateSequenceOutputter(XPathContext context, int hostLang){
+
+ Controller controller = context.getController();
+ SequenceOutputter seq = controller.allocateSequenceOutputter(20);
+ seq.getPipelineConfiguration().setHostLanguage(hostLang);
+ return seq;
+ }
+ /**
+ * Clear the contents of the SequenceOutputter and make it available for reuse
+ */
+
+ public void reset() {
+ list = new ArrayList<Item>(Math.min(list.size()+10, 50));
+ if (controller != null && adviseReuse()) {
+ controller.reuseSequenceOutputter(this);
+ }
+ }
+
+ /**
+ * Method to be supplied by subclasses: output one item in the sequence.
+ */
+
+ public void write(Item item) {
+ list.add(item);
+ }
+
+ /**
+ * Get the sequence that has been built
+ * @return the value (sequence of items) that have been written to this SequenceOutputter
+ */
+
+ public Sequence getSequence() {
+ switch (list.size()) {
+ case 0:
+ return EmptySequence.getInstance();
+ case 1:
+ return list.get(0);
+ default:
+ return new SequenceExtent<Item>(list);
+ }
+ }
+
+ /**
+ * Get an iterator over the sequence of items that has been constructed
+ * @return an iterator over the items that have been written to this SequenceOutputter
+ */
+
+ public SequenceIterator<? extends Item> iterate() {
+ if (list.isEmpty()) {
+ return EmptyIterator.emptyIterator();
+ } else {
+ return new ListIterator<Item>(list);
+ }
+ }
+
+ /**
+ * Get the list containing the sequence of items
+ * @return the list of items that have been written to this SequenceOutputter
+ */
+
+ public List<Item> getList() {
+ return list;
+ }
+
+ /**
+ * Get the first item in the sequence that has been built
+ * @return the first item in the list of items that have been written to this SequenceOutputter;
+ * or null if the list is empty.
+ */
+
+ public Item getFirstItem() {
+ if (list.isEmpty()) {
+ return null;
+ } else {
+ return list.get(0);
+ }
+ }
+
+ /**
+ * Get the last item in the sequence that has been built, and remove it
+ * @return the last item written
+ */
+
+ public Item popLastItem() {
+ if (list.isEmpty()) {
+ return null;
+ } else {
+ return list.remove(list.size()-1);
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/event/SequenceReceiver.java b/sf/saxon/event/SequenceReceiver.java
new file mode 100644
index 0000000..7f9cba1
--- /dev/null
+++ b/sf/saxon/event/SequenceReceiver.java
@@ -0,0 +1,124 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * SequenceReceiver: this extension of the Receiver interface is used when processing
+ * a sequence constructor. It differs from the Receiver in allowing items (atomic values or
+ * nodes) to be added to the sequence, not just tree-building events.
+ */
+
+public abstract class SequenceReceiver implements Receiver {
+
+ protected boolean previousAtomic = false;
+ /*@NotNull*/
+ protected PipelineConfiguration pipelineConfiguration;
+ /*@Nullable*/
+ protected String systemId = null;
+
+ /**
+ * Create a SequenceReceiver
+ * @param pipe the pipeline configuration
+ */
+
+ public SequenceReceiver(/*@NotNull*/ PipelineConfiguration pipe) {
+ this.pipelineConfiguration = pipe;
+ }
+
+ /*@NotNull*/
+ public final PipelineConfiguration getPipelineConfiguration() {
+ return pipelineConfiguration;
+ }
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipelineConfiguration) {
+ this.pipelineConfiguration = pipelineConfiguration;
+ }
+
+ /**
+ * Get the Saxon Configuration
+ * @return the Configuration
+ */
+
+ public final Configuration getConfiguration() {
+ return pipelineConfiguration.getConfiguration();
+ }
+
+ /**
+ * Set the system ID
+ * @param systemId the URI used to identify the tree being passed across this interface
+ */
+
+ public void setSystemId(/*@Nullable*/ String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the system ID
+ * @return the system ID that was supplied using the setSystemId() method
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Notify an unparsed entity URI.
+ * @param name The name of the unparsed entity
+ * @param systemID The system identifier of the unparsed entity
+ * @param publicID The public identifier of the unparsed entity
+ */
+
+ public void setUnparsedEntity(String name, String systemID, String publicID) throws XPathException {
+ }
+
+ /**
+ * Start the output process
+ */
+
+ public void open() throws XPathException {
+ previousAtomic = false;
+ }
+
+ /**
+ * Append an arbitrary item (node or atomic value) to the output
+ * @param item the item to be appended
+ * @param locationId the location of the calling instruction, for diagnostics
+ * @param copyNamespaces if the item is an element node, this indicates whether its namespaces
+ * need to be copied. Values are {@link net.sf.saxon.om.NodeInfo#ALL_NAMESPACES},
+ * {@link net.sf.saxon.om.NodeInfo#LOCAL_NAMESPACES}, {@link net.sf.saxon.om.NodeInfo#NO_NAMESPACES}
+ * @throws net.sf.saxon.trans.XPathException if the operation fails
+ */
+
+ public abstract void append(Item item, int locationId, int copyNamespaces) throws XPathException;
+
+ /**
+ * Append an item (node or atomic value) to the output
+ * @param item the item to be appended
+ * @throws net.sf.saxon.trans.XPathException if the operation fails
+ */
+
+ public void append(Item item) throws XPathException {
+ append(item, -1, NodeInfo.ALL_NAMESPACES);
+ }
+
+ /**
+ * Get the name pool
+ * @return the Name Pool that was supplied using the setConfiguration() method
+ */
+
+ public NamePool getNamePool() {
+ return pipelineConfiguration.getConfiguration().getNamePool();
+ }
+}
+
diff --git a/sf/saxon/event/SequenceWriter.java b/sf/saxon/event/SequenceWriter.java
new file mode 100644
index 0000000..850be89
--- /dev/null
+++ b/sf/saxon/event/SequenceWriter.java
@@ -0,0 +1,367 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.linked.LinkedTreeBuilder;
+import net.sf.saxon.tree.tiny.TinyBuilder;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.ObjectValue;
+
+/**
+ * This outputter is used when writing a sequence of atomic values and nodes, for
+ * example, when xsl:variable is used with content and an "as" attribute. The outputter
+ * builds the sequence; the concrete subclass is responsible for deciding what to do with the
+ * resulting items.
+ *
+ * <p>This class is not used to build temporary trees. For that, the ComplexContentOutputter
+ * is used.</p>
+ *
+ *
+ * @author Michael H. Kay
+ */
+
+public abstract class SequenceWriter extends SequenceReceiver {
+ private Receiver outputter = null;
+ private Builder builder = null;
+ private int level = 0;
+ private boolean inStartTag = false;
+
+ public SequenceWriter(/*@NotNull*/ PipelineConfiguration pipe) {
+ super(pipe);
+ //System.err.println("SequenceWriter init");
+ }
+
+ /**
+ * Abstract method to be supplied by subclasses: output one item in the sequence.
+ * @param item the item to be written to the sequence
+ * @throws net.sf.saxon.trans.XPathException if any failure occurs while writing the item
+ */
+
+ public abstract void write(Item item) throws XPathException;
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ if (outputter==null) {
+ createTree(((properties & ReceiverOptions.MUTABLE_TREE) != 0));
+ }
+ if (level++ == 0) {
+ outputter.startDocument(properties);
+ }
+ }
+
+ /**
+ * Create a (skeletal) tree to hold a document or element node.
+ * @param mutable set to true if the tree is required to support in-situ updates (other that the initial
+ * sequential writing of nodes to construct the tree)
+ * @throws net.sf.saxon.trans.XPathException if any error occurs while creating the tree
+ */
+
+ private void createTree(boolean mutable) throws XPathException {
+ PipelineConfiguration pipe = getPipelineConfiguration();
+ if (mutable) {
+ TreeModel model = pipe.getController().getModel();
+ if (model.isMutable()) {
+ builder = pipe.getController().makeBuilder();
+ } else {
+ builder = new LinkedTreeBuilder(pipe);
+ }
+ } else {
+ builder = pipe.getController().makeBuilder();
+ }
+ builder.setPipelineConfiguration(pipe);
+ builder.setSystemId(getSystemId());
+ builder.setTiming(false);
+
+ NamespaceReducer reducer = new NamespaceReducer(builder);
+
+ ComplexContentOutputter cco = new ComplexContentOutputter(pipe);
+ cco.setHostLanguage(pipe.getHostLanguage());
+ cco.setReceiver(reducer);
+ outputter = cco;
+
+ outputter.setSystemId(systemId);
+ outputter.setPipelineConfiguration(getPipelineConfiguration());
+ outputter.open();
+ }
+
+ /**
+ * Decide whether reuse of the SequenceWriter is advisable
+ * @return true if reuse is considered advisable
+ */
+
+ protected boolean adviseReuse() {
+ if (builder instanceof TinyBuilder) {
+ TinyTree tree = ((TinyBuilder)builder).getTree();
+ return tree != null && tree.getNumberOfNodes() < 20000;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ if (--level == 0) {
+ outputter.endDocument();
+ DocumentInfo doc = (DocumentInfo)builder.getCurrentRoot();
+ // add the constructed document to the result sequence
+ append(doc, 0, NodeInfo.ALL_NAMESPACES);
+ }
+ previousAtomic = false;
+ }
+
+ /**
+ * Output an element start tag.
+ * @param elemName The element name code - a code held in the Name Pool
+ * @param typeCode Integer code identifying the type of this element. Zero identifies the default
+ * type, that is xs:anyType
+ * @param properties bit-significant flags indicating any special information
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+
+ if (inStartTag) {
+ startContent();
+ }
+
+ if (outputter==null) {
+ createTree(((properties & ReceiverOptions.MUTABLE_TREE) != 0));
+ }
+ //System.err.println("SEQUENCE_WRITER startElement " + this);
+ outputter.startElement(elemName, typeCode, locationId, properties);
+ level++;
+ inStartTag = true;
+ previousAtomic = false;
+ }
+
+ /**
+ * Output an element end tag.
+ */
+
+ public void endElement() throws XPathException {
+ if (inStartTag) {
+ startContent();
+ }
+ //System.err.println("SEQUENCE_WRITER endElement " + this);
+ outputter.endElement();
+ if (--level == 0) {
+ outputter.close();
+ NodeInfo element = builder.getCurrentRoot();
+ append(element, 0, NodeInfo.ALL_NAMESPACES);
+ }
+ previousAtomic = false;
+ }
+
+ /**
+ * Output a namespace declaration. <br>
+ * This is added to a list of pending namespaces for the current start tag.
+ * If there is already another declaration of the same prefix, this one is
+ * ignored.
+ * Note that unlike SAX2 startPrefixMapping(), this call is made AFTER writing the start tag.
+ * @param namespaceBinding The namespace binding
+ * @param properties Allows special properties to be passed if required
+ * @throws net.sf.saxon.trans.XPathException if there is no start tag to write to (created using writeStartTag),
+ * or if character content has been written since the start tag was written.
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties)
+ throws XPathException {
+ if (level == 0) {
+ Orphan o = new Orphan(getConfiguration());
+ o.setNodeKind(Type.NAMESPACE);
+ o.setNodeName(new NoNamespaceName(namespaceBinding.getPrefix()));
+ o.setStringValue(namespaceBinding.getURI());
+ append(o, 0, NodeInfo.ALL_NAMESPACES);
+ } else {
+ outputter.namespace(namespaceBinding, properties);
+ }
+ previousAtomic = false;
+ }
+
+ /**
+ * Output an attribute value. <br>
+ *
+ *
+ * @param attName An integer code representing the name of the attribute, as held in the Name Pool
+ * @param typeCode Integer code identifying the type annotation of the attribute; zero represents
+ * the default type (xs:untypedAtomic)
+ * @param value The value of the attribute
+ * @param properties Bit significant flags for passing extra information to the serializer, e.g.
+ * to disable escaping
+ * @throws net.sf.saxon.trans.XPathException if there is no start tag to write to (created using writeStartTag),
+ * or if character content has been written since the start tag was written.
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ if (level == 0) {
+ Orphan o = new Orphan(getConfiguration());
+ o.setNodeKind(Type.ATTRIBUTE);
+ o.setNodeName(attName);
+ o.setStringValue(value);
+ o.setTypeAnnotation(typeCode);
+ append(o, locationId, NodeInfo.ALL_NAMESPACES);
+ } else {
+ outputter.attribute(attName, typeCode, value, locationId, properties);
+ }
+ previousAtomic = false;
+ }
+
+ /**
+ * The startContent() event is notified after all namespaces and attributes of an element
+ * have been notified, and before any child nodes are notified.
+ * @throws net.sf.saxon.trans.XPathException for any failure
+ */
+
+ public void startContent() throws XPathException {
+ inStartTag = false;
+ outputter.startContent();
+ previousAtomic = false;
+ }
+
+ /**
+ * Produce text content output. <BR>
+ * @param s The String to be output
+ * @param properties bit-significant flags for extra information, e.g. disable-output-escaping
+ * @throws net.sf.saxon.trans.XPathException for any failure
+ */
+
+ public void characters(CharSequence s, int locationId, int properties) throws XPathException {
+ if (level == 0) {
+ Orphan o = new Orphan(getConfiguration());
+ o.setNodeKind(Type.TEXT);
+ o.setStringValue(s.toString());
+ append(o, locationId, NodeInfo.ALL_NAMESPACES);
+ } else {
+ if (s.length() > 0) {
+ if (inStartTag) {
+ startContent();
+ }
+ outputter.characters(s, locationId, properties);
+ }
+ }
+ previousAtomic = false;
+ }
+
+ /**
+ * Write a comment.
+ */
+
+ public void comment(CharSequence comment, int locationId, int properties) throws XPathException {
+ if (inStartTag) {
+ startContent();
+ }
+ if (level == 0) {
+ Orphan o = new Orphan(getConfiguration());
+ o.setNodeKind(Type.COMMENT);
+ o.setStringValue(comment);
+ append(o, locationId, NodeInfo.ALL_NAMESPACES);
+ } else {
+ outputter.comment(comment, locationId, properties);
+ }
+ previousAtomic = false;
+ }
+
+ /**
+ * Write a processing instruction
+ * No-op in this implementation
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (inStartTag) {
+ startContent();
+ }
+ if (level == 0) {
+ Orphan o = new Orphan(getConfiguration());
+ o.setNodeName(new NoNamespaceName(target));
+ o.setNodeKind(Type.PROCESSING_INSTRUCTION);
+ o.setStringValue(data);
+ append(o, locationId, NodeInfo.ALL_NAMESPACES);
+ } else {
+ outputter.processingInstruction(target, data, locationId, properties);
+ }
+ previousAtomic = false;
+ }
+
+ /**
+ * Close the output
+ */
+
+ public void close() throws XPathException {
+ previousAtomic = false;
+ if (outputter != null) {
+ outputter.close();
+ }
+ }
+
+ /**
+ * Append an item to the sequence, performing any necessary type-checking and conversion
+ */
+
+ public void append(/*@Nullable*/ Item item, int locationId, int copyNamespaces) throws XPathException {
+
+ if (item==null) {
+ return;
+ }
+
+ if (level==0) {
+ write(item);
+ previousAtomic = false;
+ } else {
+ if (item instanceof AtomicValue || item instanceof ObjectValue) {
+ // If an atomic value is written to a tree, and the previous item was also
+ // an atomic value, then add a single space to separate them
+ if (previousAtomic) {
+ outputter.characters(" ", 0, 0);
+ }
+ outputter.characters(item.getStringValueCS(), 0, 0);
+ previousAtomic = true;
+ } else if (item instanceof FunctionItem) {
+ XPathException err = new XPathException("Cannot write a function item to an XML tree", "XPTY0004");
+ err.setLocator(getPipelineConfiguration().getSourceLocation(locationId));
+ throw err;
+ } else {
+ NodeInfo node = (NodeInfo)item;
+ if (node.getNodeKind() == Type.ATTRIBUTE && ((SimpleType)node.getSchemaType()).isNamespaceSensitive()) {
+ XPathException err = new XPathException("Cannot copy attributes whose type is namespace-sensitive (QName or NOTATION): " +
+ Err.wrap(node.getDisplayName(), Err.ATTRIBUTE));
+ err.setErrorCode((getPipelineConfiguration().getHostLanguage() == Configuration.XSLT ? "XTTE0950" : "XQTY0086"));
+ throw err;
+ }
+ ((NodeInfo)item).copy(outputter, (CopyOptions.ALL_NAMESPACES | CopyOptions.TYPE_ANNOTATIONS), locationId);
+ previousAtomic = false;
+ }
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return outputter == null || outputter.usesTypeAnnotations();
+ }
+}
+
diff --git a/sf/saxon/event/Sink.java b/sf/saxon/event/Sink.java
new file mode 100644
index 0000000..8e880fc
--- /dev/null
+++ b/sf/saxon/event/Sink.java
@@ -0,0 +1,175 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+/**
+ * A Sink is an Receiver that discards all information passed to it
+ */
+
+public class Sink extends SequenceReceiver {
+
+ public Sink(PipelineConfiguration pipe) {
+ super(pipe);
+ }
+
+ /**
+ * Start of event stream
+ */
+
+ public void open() throws XPathException {
+ }
+
+ /**
+ * End of event stream
+ */
+
+ public void close() throws XPathException {
+ }
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param nameCode integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties for future use. Should be set to zero.
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties)
+ throws XPathException {
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element; however, duplicates may be reported.
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ *
+ * @param namespaceBinding the prefix/uri pair representing the namespace binding to be written
+ * @throws java.lang.IllegalStateException:
+ * attempt to output a namespace when there is no open element
+ * start tag
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param nameCode The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws java.lang.IllegalStateException:
+ * attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ }
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+
+
+ public void startContent() throws XPathException {
+ }
+
+ /**
+ * End of element
+ */
+
+ public void endElement() throws XPathException {
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ }
+
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ }
+
+
+ /**
+ * Append an arbitrary item (node or atomic value) to the output
+ *
+ * @param item the item to be appended
+ * @param locationId the location of the calling instruction, for diagnostics
+ * @param copyNamespaces if the item is an element node, this indicates whether its namespaces
+ * need to be copied. Values are {@link net.sf.saxon.om.NodeInfo#ALL_NAMESPACES},
+ * {@link net.sf.saxon.om.NodeInfo#LOCAL_NAMESPACES}, {@link net.sf.saxon.om.NodeInfo#NO_NAMESPACES}
+ */
+
+ public void append(Item item, int locationId, int copyNamespaces) throws XPathException {
+ }
+
+ /**
+ * Set the URI for an unparsed entity in the document.
+ */
+
+ public void setUnparsedEntity(String name, String uri, String publicId) throws XPathException {
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return false;
+ }
+}
+
diff --git a/sf/saxon/event/SourceLocationProvider.java b/sf/saxon/event/SourceLocationProvider.java
new file mode 100644
index 0000000..d7a19ba
--- /dev/null
+++ b/sf/saxon/event/SourceLocationProvider.java
@@ -0,0 +1,20 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+
+/**
+ * A SourceLocationProvider is a {@link LocationProvider} that represents locations
+ * in the source document from which the events
+ * are derived (as distinct from locations in a query or stylesheet of the instructions causing the
+ * events)
+ */
+
+public interface SourceLocationProvider extends LocationProvider {
+
+}
diff --git a/sf/saxon/event/StartTagBuffer.java b/sf/saxon/event/StartTagBuffer.java
new file mode 100644
index 0000000..a68262f
--- /dev/null
+++ b/sf/saxon/event/StartTagBuffer.java
@@ -0,0 +1,434 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * StartTagBuffer is a ProxyReceiver that buffers attributes and namespace events within a start tag.
+ * It maintains details of the namespace context, and a full set of attribute information, on behalf
+ * of other filters that need access to namespace information or need to process attributes in arbitrary
+ * order.
+ *
+ * <p>StartTagBuffer also implements namespace fixup (the process of creating namespace nodes|bindings on behalf
+ * of constructed element and attribute nodes). Although this would be done anyway, further down the pipeline,
+ * it has to be done early in the case of a validating pipeline, because the namespace bindings must be created
+ * before any namespace-sensitive attribute content is validated.</p>
+ *
+ * <p>The StartTagBuffer also allows error conditions to be buffered. This is because the XSIAttributeHandler
+ * validates attributes such as xsi:type and xsi:nil before attempting to match its parent element against
+ * a particle of its containing type. It is possible that the parent element will match a wildcard particle
+ * with processContents="skip", in which case an invalid xsi:type or xsi:nil attribute is not an error.</p>
+ */
+
+public class StartTagBuffer extends ProxyReceiver implements NamespaceResolver {
+
+ public StartTagBuffer(Receiver next) {
+ super(next);
+ }
+
+ // Details of the pending element event
+
+ protected NodeName elementNameCode;
+ protected SchemaType elementTypeCode;
+ protected int elementLocationId;
+ protected int elementProperties;
+
+ // Details of pending attribute events
+
+ protected AttributeCollectionImpl bufferedAttributes;
+ private boolean acceptAttributes;
+ private boolean inDocument;
+
+ // We keep track of namespaces. The namespaces
+ // array holds a list of all namespaces currently declared (organised as pairs of entries,
+ // prefix followed by URI). The stack contains an entry for each element currently open; the
+ // value on the stack is an Integer giving the number of namespaces added to the main
+ // namespace stack by that element.
+
+ protected NamespaceBinding[] namespaces = new NamespaceBinding[50]; // all namespaces currently declared
+ protected int namespacesSize = 0; // all namespaces currently declared
+ private int[] countStack = new int[50];
+ private int depth = 0;
+ private int attCount = 0;
+
+ /**
+ * Set the pipeline configuration
+ * @param pipe the pipeline configuration
+ */
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipe) {
+ super.setPipelineConfiguration(pipe);
+ bufferedAttributes = new AttributeCollectionImpl(pipe.getConfiguration());
+ }
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ // a document node in the content sequence of an element is ignored. However, we need
+ // to stop attributes being created within the document node.
+ if (depth == 0) {
+ depth++;
+ super.startDocument(properties);
+ }
+ acceptAttributes = false;
+ inDocument = true; // we ought to clear this on endDocument, but it only affects diagnostics
+ }
+
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ if (depth == 1) {
+ depth--;
+ super.endDocument();
+ }
+ }
+
+ /**
+ * startElement
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+
+ elementNameCode = nameCode;
+ elementTypeCode = typeCode;
+ elementLocationId = locationId;
+ elementProperties = properties;
+
+ bufferedAttributes.clear();
+
+ // Record the current height of the namespace list so it can be reset at endElement time
+
+ countStack[depth] = 0;
+ if (++depth >= countStack.length) {
+ int[] newstack = new int[depth*2];
+ System.arraycopy(countStack, 0, newstack, 0, depth);
+ countStack = newstack;
+ }
+
+ // Ensure that the element namespace is output, unless this is done
+ // automatically by the caller (which is true, for example, for a literal
+ // result element).
+
+ acceptAttributes = true;
+ inDocument = false;
+ if ((properties & ReceiverOptions.NAMESPACE_OK) == 0) {
+ namespace(nameCode.getNamespaceBinding(), 0);
+ }
+ attCount = 0;
+ }
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+
+ if (!acceptAttributes) {
+ throw NoOpenStartTagException.makeNoOpenStartTagException(
+ Type.NAMESPACE, namespaceBinding.getPrefix(),
+ getPipelineConfiguration().getHostLanguage(),
+ inDocument, false, null, -1);
+ }
+
+ // avoid duplicates
+ for (int n=0; n<countStack[depth - 1]; n++) {
+ if (namespaces[namespacesSize - 1 - n].equals(namespaceBinding)) {
+ return;
+ }
+ }
+ addToStack(namespaceBinding);
+ countStack[depth - 1]++;
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param attName The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+
+ if (!acceptAttributes) {
+ throw NoOpenStartTagException.makeNoOpenStartTagException(
+ Type.ATTRIBUTE, attName.getDisplayName(),
+ getPipelineConfiguration().getHostLanguage(),
+ inDocument, false, null, -1);
+ }
+
+ // Perform namespace fixup for the attribute
+
+ if (((properties & ReceiverOptions.NAMESPACE_OK) == 0) &&
+ !attName.isInNamespace("")) { // non-null prefix
+ attName = checkProposedPrefix(attName, attCount++);
+ }
+ bufferedAttributes.addAttribute(attName, typeCode, value.toString(), locationId, properties);
+
+ // Note: we're relying on the fact that AttributeCollection can hold two attributes of the same name
+ // and maintain their order, because the check for duplicate attributes is not done until later in the
+ // pipeline. We validate both the attributes (see Bugzilla #4600 which legitimizes this.)
+
+ }
+
+ /**
+ * Add a namespace declaration (or undeclaration) to the stack
+ * @param binding the namespace binding for the declaration
+ */
+
+ private void addToStack(NamespaceBinding binding) {
+ // expand the stack if necessary
+ if (namespacesSize+1 >= namespaces.length) {
+ NamespaceBinding[] newlist = new NamespaceBinding[namespacesSize*2];
+ System.arraycopy(namespaces, 0, newlist, 0, namespacesSize);
+ namespaces = newlist;
+ }
+ namespaces[namespacesSize++] = binding;
+ }
+
+ /**
+ * startContent: Add any namespace undeclarations needed to stop
+ * namespaces being inherited from parent elements
+ */
+
+ public void startContent() throws XPathException {
+ nextReceiver.startElement(elementNameCode, elementTypeCode, elementLocationId,
+ elementProperties | ReceiverOptions.NAMESPACE_OK);
+ declareNamespacesForStartElement();
+
+ final int length = bufferedAttributes.getLength();
+ for (int i=0; i<length; i++) {
+ nextReceiver.attribute(bufferedAttributes.getNodeName(i),
+ bufferedAttributes.getTypeAnnotation(i),
+ bufferedAttributes.getValue(i),
+ bufferedAttributes.getLocationId(i),
+ bufferedAttributes.getProperties(i) | ReceiverOptions.NAMESPACE_OK);
+ }
+ acceptAttributes = false;
+ nextReceiver.startContent();
+ }
+
+ protected void declareNamespacesForStartElement() throws XPathException {
+ for (int i=namespacesSize - countStack[depth-1]; i<namespacesSize; i++) {
+ nextReceiver.namespace(namespaces[i], 0);
+ }
+ }
+
+ /**
+ * Get the namespaces declared (or undeclared) at the current level
+ * @return an array of namespace bindings
+ */
+
+ public NamespaceBinding[] getLocalNamespaces() {
+ int size = countStack[depth-1];
+ if (size == 0) {
+ return NamespaceBinding.EMPTY_ARRAY;
+ } else {
+ NamespaceBinding[] localBindings = new NamespaceBinding[countStack[depth-1]];
+ System.arraycopy(namespaces, namespacesSize - size, localBindings, 0, size);
+ return localBindings;
+ }
+ }
+
+ /**
+ * Signal namespace events for all in-scope namespaces to the current receiver in the pipeline
+ * @throws XPathException if any downstream error occurs
+ */
+
+ protected void declareAllNamespaces() throws XPathException {
+ for (int i=0; i<namespacesSize; i++) {
+ nextReceiver.namespace(namespaces[i], 0);
+ }
+ }
+
+ /**
+ * endElement: Discard the namespaces declared locally on this element.
+ */
+
+ public void endElement () throws XPathException {
+ nextReceiver.endElement();
+ undeclareNamespacesForElement();
+ }
+
+ protected void undeclareNamespacesForElement() {
+ namespacesSize -= countStack[--depth];
+ }
+
+ /**
+ * Determine if the current element has any attributes
+ * @return true if the element has one or more attributes
+ */
+
+ public boolean hasAttributes() {
+ return bufferedAttributes.getLength() > 0;
+ }
+
+ /**
+ * Get the value of the current attribute with a given nameCode
+ * @param nameCode the name of the required attribute
+ * @return the attribute value, or null if the attribute is not present
+ */
+
+ public String getAttribute(int nameCode) {
+ return bufferedAttributes.getValueByFingerprint(nameCode & 0xfffff);
+ }
+
+ /**
+ * Get the value of the current attribute with a given name
+ * @param uri the uri of the name of the required attribute
+ * @param local the local part of the name of the required attribute
+ * @return the attribute value, or null if the attribute is not present
+ */
+
+ public String getAttribute(String uri, String local) {
+ return bufferedAttributes.getValue(uri, local);
+ }
+
+ /**
+ * Get all the attributes on the current element start tag
+ * @return an AttributeCollection containing all the attributes
+ */
+
+ public AttributeCollection getAllAttributes() {
+ return bufferedAttributes;
+ }
+
+ /**
+ * Ask whether the attribute collection contains any attributes
+ * in a specified namespace
+ * @param uri the specified namespace
+ * @return true if there are one or more attributes in this namespace
+ */
+
+ public boolean hasAttributeInNamespace(String uri) {
+ return bufferedAttributes.hasAttributeInNamespace(uri);
+ }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope. f
+ *
+ * @param prefix the namespace prefix
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is ""
+ * @return the uri for the namespace, or null if the prefix is not in scope
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(String prefix, boolean useDefault) {
+ if (prefix.length()==0 && !useDefault) {
+ return NamespaceConstant.NULL;
+ } else if ("xml".equals(prefix)) {
+ return NamespaceConstant.XML;
+ } else {
+ for (int i=namespacesSize-1; i>=0; i--) {
+ if (namespaces[i].getPrefix().equals(prefix)) {
+ String uri = namespaces[i].getURI();
+ if (uri.length()==0) {
+// // we've found a namespace undeclaration, so it's as if the prefix weren't there at all
+ } else {
+ return uri;
+ }
+ }
+ }
+ }
+ return (prefix.length()==0 ? NamespaceConstant.NULL : null);
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ List<String> prefixes = new ArrayList<String>(namespacesSize);
+ for (int i=namespacesSize-1; i>=0; i--) {
+ String prefix = namespaces[i].getPrefix();
+ if (!prefixes.contains(prefix)) {
+ prefixes.add(prefix);
+ }
+ }
+ prefixes.add("xml");
+ return prefixes.iterator();
+ }
+
+ /**
+ * Check that the prefix for an element or attribute is acceptable, allocating a substitute
+ * prefix if not. The prefix is acceptable unless a namespace declaration has been
+ * written that assignes this prefix to a different namespace URI. This method
+ * also checks that the element or attribute namespace has been declared, and declares it
+ * if not.
+ * @param nameCode the proposed element or attribute name
+ * @param seq sequence number of attribute, used for generating distinctive prefixes
+ * @return the actual allocated name, which may be different.
+ * @throws net.sf.saxon.trans.XPathException if any error occurs writing the new namespace binding
+ */
+
+ private NodeName checkProposedPrefix(NodeName nameCode, int seq) throws XPathException {
+ NamespaceBinding binding = nameCode.getNamespaceBinding();
+ String prefix = binding.getPrefix();
+
+ String existingURI = getURIForPrefix(prefix, true);
+ if (existingURI == null) {
+ // prefix has not been declared: declare it now (namespace fixup)
+ namespace(binding, 0);
+ return nameCode;
+ } else {
+ if (binding.getURI().equals(existingURI)) {
+ // prefix is already bound to this URI
+ return nameCode; // all is well
+ } else {
+ // conflict: prefix is currently bound to a different URI
+ prefix = getSubstitutePrefix(binding, seq);
+
+ NodeName newCode = new FingerprintedQName(
+ prefix,
+ nameCode.getURI(),
+ nameCode.getLocalPart());
+ namespace(newCode.getNamespaceBinding(), 0);
+ return newCode;
+ }
+ }
+ }
+
+ /**
+ * It is possible for a single output element to use the same prefix to refer to different
+ * namespaces. In this case we have to generate an alternative prefix for uniqueness. The
+ * one we generate is based on the sequential position of the element/attribute: this is
+ * designed to ensure both uniqueness (with a high probability) and repeatability
+ * @param binding the namespace binding of the proposed element or attribute name
+ * @param seq sequence number of attribute, used for generating distinctive prefixes
+ * @return the actual allocated name, which may be different.
+ */
+
+ private String getSubstitutePrefix(NamespaceBinding binding, int seq) {
+ String prefix = binding.getPrefix();
+ return prefix + '_' + seq;
+ }
+
+}
+
diff --git a/sf/saxon/event/StreamWriterToReceiver.java b/sf/saxon/event/StreamWriterToReceiver.java
new file mode 100644
index 0000000..289216f
--- /dev/null
+++ b/sf/saxon/event/StreamWriterToReceiver.java
@@ -0,0 +1,583 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.lib.StandardURIChecker;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.Untyped;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class implements the XmlStreamWriter interface, translating the events into Saxon
+ * Receiver events. The Receiver can be anything: a serializer, a schema validator, a tree builder.
+ *
+ * <p>The class will attempt to generate namespace prefixes where none have been supplied, unless the
+ * <code>inventPrefixes</code> option is set to false. The preferred mode of use is to call the versions
+ * of <code>writeStartElement</code> and <code>writeAttribute</code> that supply the prefix, URI, and
+ * local name in full. If the prefix is omitted, the class attempts to invent a prefix. If the URI is
+ * omitted, the name is assumed to be in no namespace. The <code>writeNamespace</p> method should be
+ * called only if there is a need to declare a namespace prefix that is not used on any element or
+ * attribute name.</p>
+ *
+ * <p>The class will check all names, URIs, and character content for conformance against XML well-formedness
+ * rules unless the <code>checkValues</code> option is set to false.</p>
+ *
+ * @since 9.3
+ */
+public class StreamWriterToReceiver implements XMLStreamWriter {
+
+ /**
+ * The receiver to which events will be passed
+ */
+
+ private Receiver receiver;
+
+ /**
+ * The Saxon NamePool
+ */
+
+ private NamePool namePool;
+
+ /**
+ * The Name Checker
+ */
+
+ private NameChecker nameChecker;
+
+ /**
+ * Flag to indicate whether names etc are to be checked for well-formedness
+ */
+
+ private boolean isChecking = false;
+
+ /**
+ * The current depth of element nesting. -1 indicates outside startDocument/endDocument; non-negative
+ * values indicate the number of open start element tags
+ */
+
+ private int depth = -1;
+
+ /**
+ * The namespace context used to determine the namespace-prefix mappings
+ * in scope.
+ */
+ //protected NamespaceContext namespaceContext;
+
+ /**
+ * Flag set to true during processing of a start tag.
+ */
+ private boolean inStartTag;
+
+ /**
+ * Flag indicating that an empty element has been requested.
+ */
+ private boolean isEmptyElement;
+
+ /**
+ * Flag indicating that default prefixes should be allocated if the user does not declare them explicitly
+ */
+
+ private boolean inventPrefixes = true;
+
+ /**
+ * inScopeNamespaces represents namespaces that have been declared in the XML stream
+ */
+ private NamespaceReducer inScopeNamespaces;
+
+ /**
+ * declaredNamespaces represents prefix-to-uri bindings that have been set using setPrefix. These
+ * do not necessarily correspond to namespace declarations appearing in the XML stream. Note that
+ * this is a map from URIs to prefixes, not the other way around!
+ */
+
+ private Map<String, String> declaredNamespaces = new HashMap<String, String>(10);
+
+ /**
+ * rootNamespaceContext is the namespace context supplied at the start, is the final fallback
+ * for allocating a prefix to a URI
+ */
+
+ private javax.xml.namespace.NamespaceContext rootNamespaceContext = null;
+
+ /**
+ * Constructor. Creates a StreamWriter as a front-end to a given Receiver.
+ * @param receiver the Receiver that is to receive the events generated
+ * by this StreamWriter.
+ */
+ public StreamWriterToReceiver(Receiver receiver) {
+ // Events are passed through a NamespaceReducer which maintains the namespace context
+ // It also eliminates duplicate namespace declarations, and creates extra namespace declarations
+ // where needed to support prefix-uri mappings used on elements and attributes
+ PipelineConfiguration pipe = receiver.getPipelineConfiguration();
+ this.inScopeNamespaces = new NamespaceReducer(receiver);
+ this.receiver = inScopeNamespaces;
+ this.nameChecker = pipe.getConfiguration().getNameChecker();
+ this.namePool = pipe.getConfiguration().getNamePool();
+ }
+
+ /**
+ * Say whether prefixes are to be invented when none is specified by the user
+ * @param invent true if prefixes are to be invented. Default is true;
+ */
+
+ public void setInventPrefixes(boolean invent) {
+ this.inventPrefixes = invent;
+ }
+
+ /**
+ * Ask whether prefixes are to be invented when none is specified by the user
+ * @return true if prefixes are to be invented. Default is true;
+ */
+
+ public boolean isInventPrefixes() {
+ return this.inventPrefixes;
+ }
+
+ /**
+ * Say whether names and values are to be checked for conformance with XML rules
+ * @param check true if names and values are to be checked. Default is true;
+ */
+
+ public void setCheckValues(boolean check) {
+ this.isChecking = check;
+ }
+
+ /**
+ * Ask whether names and values are to be checked for conformance with XML rules
+ * @return true if names and values are to be checked. Default is true;
+ */
+
+ public boolean isCheckValues() {
+ return this.isChecking;
+ }
+
+
+
+ public void writeStartElement(/*@NotNull*/ String localName) throws XMLStreamException {
+ String uri = inScopeNamespaces.getURIForPrefix("", true);
+ assert uri != null;
+ writeStartElement("", localName, uri);
+ }
+
+ public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException {
+ String prefix = getPrefix(namespaceURI);
+ boolean isDeclared = (prefix != null);
+ if (!isDeclared) {
+ if (inventPrefixes) {
+ prefix = inventPrefix(namespaceURI);
+ } else {
+ throw new XMLStreamException("namespace " + namespaceURI + " has not been declared");
+ }
+ }
+ writeStartElement(prefix, localName, namespaceURI);
+
+ }
+
+ public void writeStartElement(/*@NotNull*/ String prefix, /*@NotNull*/ String localName, /*@NotNull*/ String namespaceURI) throws XMLStreamException {
+ if (depth == -1) {
+ writeStartDocument();
+ }
+ try {
+ if (!isValidURI(namespaceURI)) {
+ throw new IllegalArgumentException("Invalid namespace URI: " + namespaceURI);
+ }
+ if (!isValidNCName(prefix)) {
+ throw new IllegalArgumentException("Invalid prefix: " + prefix);
+ }
+ if (!isValidNCName(localName)) {
+ throw new IllegalArgumentException("Invalid local name: " + localName);
+ }
+
+ startContent();
+ inStartTag = true;
+ depth++;
+ NodeName nc;
+ if (namespaceURI.length()==0) {
+ nc = new NoNamespaceName(localName);
+ } else {
+ nc = new FingerprintedQName(prefix, namespaceURI, localName);
+ }
+ receiver.startElement(nc, Untyped.getInstance(), 0, 0);
+ inStartTag = true;
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ }
+
+ /**
+ * Creates a prefix that is not currently in use.
+ * @param uri the URI for which a prefix is required
+ * @return the chosen prefix
+ */
+ private String inventPrefix(String uri) {
+ String prefix = getPrefix(uri);
+ if (prefix != null) {
+ return prefix;
+ }
+ prefix = namePool.suggestPrefixForURI(uri);
+ if (prefix != null) {
+ return prefix;
+ }
+ int count = 0;
+ while (true) {
+ prefix = "ns" + count;
+ if (inScopeNamespaces.getURIForPrefix(prefix, false) == null) {
+ setPrefix(prefix, uri);
+ return prefix;
+ }
+ count++;
+ }
+ }
+
+ public void writeEmptyElement(String namespaceURI, String localName)
+ throws XMLStreamException {
+ writeStartElement(namespaceURI, localName);
+ isEmptyElement = true;
+ }
+
+ public void writeEmptyElement(String prefix, String localName, String namespaceURI) throws XMLStreamException {
+ writeStartElement(prefix, localName, namespaceURI);
+ isEmptyElement = true;
+ }
+
+ public void writeEmptyElement(String localName) throws XMLStreamException {
+ writeStartElement(localName);
+ isEmptyElement = true;
+ }
+
+ /**
+ * Indicate the end of a start tag if one is open (no action otherwise).
+ * This will also write an end tag if the element was opened as an empty element.
+ * @throws javax.xml.stream.XMLStreamException if an error occurs writing to the output stream
+ */
+ private void startContent() throws XMLStreamException {
+ if (inStartTag) {
+ try {
+ receiver.startContent();
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ inStartTag = false;
+ if (isEmptyElement) {
+ isEmptyElement = false;
+ writeEndElement();
+ }
+ }
+ }
+
+ public void writeEndElement() throws XMLStreamException {
+ if (depth <= 0) {
+ throw new IllegalStateException("writeEndElement with no matching writeStartElement");
+ }
+// if (isEmptyElement) {
+// throw new IllegalStateException("writeEndElement called for an empty element");
+// }
+ try {
+ startContent();
+ receiver.endElement();
+ depth--;
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ }
+
+ public void writeEndDocument() throws XMLStreamException {
+ if (depth == -1) {
+ throw new IllegalStateException("writeEndDocument with no matching writeStartDocument");
+ }
+ try {
+ if (isEmptyElement) {
+ startContent(); // which also ends the element and decrements depth
+ }
+ while (depth > 0) {
+ writeEndElement();
+ }
+ receiver.endDocument();
+ depth = -1;
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ }
+
+ public void close() throws XMLStreamException {
+ if (depth >= 0) {
+ writeEndDocument();
+ }
+ try {
+ receiver.close();
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ }
+
+ public void flush() throws XMLStreamException {
+ // no action
+ }
+
+ public void writeAttribute(String localName, String value) throws XMLStreamException {
+ writeAttribute("", "", localName, value);
+ }
+
+ public void writeAttribute(/*@Nullable*/ String prefix, /*@Nullable*/ String namespaceURI, String localName, String value)
+ throws XMLStreamException {
+ if (!inStartTag) {
+ throw new IllegalStateException("Cannot write attribute when not in a start tag");
+ }
+ if (prefix == null) {
+ prefix = "";
+ }
+ if (namespaceURI == null) {
+ namespaceURI = "";
+ }
+ if (namespaceURI.length() != 0 && !isValidURI(namespaceURI)) {
+ throw new IllegalArgumentException("Invalid attribute namespace URI: " + namespaceURI);
+ }
+ if (prefix.length() != 0 && !isValidNCName(prefix)) {
+ throw new IllegalArgumentException("Invalid attribute prefix: " + prefix);
+ }
+ if (!isValidNCName(localName)) {
+ throw new IllegalArgumentException("Invalid attribute local name: " + localName);
+ }
+ if (!isValidChars(value)) {
+ throw new IllegalArgumentException("Invalid characters in attribute content: " + value);
+ }
+ try {
+ NodeName nn = new FingerprintedQName(prefix, namespaceURI, localName);
+ receiver.attribute(nn, BuiltInAtomicType.UNTYPED_ATOMIC, value, -1, 0);
+ }
+ catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ }
+
+ public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException {
+ String prefix = getPrefix(namespaceURI);
+ if (prefix == null) {
+ if (inventPrefixes) {
+ prefix = inventPrefix(namespaceURI);
+ } else {
+ throw new XMLStreamException("Namespace " + namespaceURI + " has not been declared");
+ }
+ }
+ writeAttribute(prefix, namespaceURI, localName, value);
+ }
+
+ public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException {
+ if (!inStartTag) {
+ throw new IllegalStateException("Cannot write namespace when not in a start tag");
+ }
+ if (prefix == null || "".equals(prefix) || "xmlns".equals(prefix)) {
+ writeDefaultNamespace(namespaceURI);
+ return;
+ }
+ if (!isValidURI(namespaceURI)) {
+ throw new IllegalArgumentException("Invalid namespace URI: " + namespaceURI);
+ }
+ if (!isValidNCName(prefix)) {
+ throw new IllegalArgumentException("Invalid namespace prefix: " + prefix);
+ }
+ outputNamespaceDeclaration(prefix, namespaceURI);
+ }
+
+ public void writeDefaultNamespace(String namespaceURI)
+ throws XMLStreamException {
+ if (!inStartTag) {
+ throw new IllegalStateException();
+ }
+ if (!isValidURI(namespaceURI)) {
+ throw new IllegalArgumentException("Invalid namespace URI: " + namespaceURI);
+ }
+ outputNamespaceDeclaration("", namespaceURI);
+ }
+
+ private void outputNamespaceDeclaration(String prefix, String namespaceURI) throws XMLStreamException {
+ try {
+ if (prefix == null) {
+ prefix = "";
+ }
+ receiver.namespace(new NamespaceBinding(prefix, namespaceURI), 0);
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ }
+
+ public void writeComment(/*@Nullable*/ String data) throws XMLStreamException {
+ if (data == null) {
+ data = "";
+ }
+ try {
+ if (!isValidChars(data)) {
+ throw new IllegalArgumentException("Invalid XML character in comment: " + data);
+ }
+ if (isChecking && data.contains("--")) {
+ throw new IllegalArgumentException("Comment contains '--'");
+ }
+ startContent();
+ receiver.comment(data, 0, 0);
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ }
+
+ public void writeProcessingInstruction(String target) throws XMLStreamException {
+ writeProcessingInstruction(target, "");
+ }
+
+ public void writeProcessingInstruction(/*@NotNull*/ String target, /*@NotNull*/ String data) throws XMLStreamException {
+ try {
+ if (isChecking) {
+ if (!isValidNCName(target) || "xml".equalsIgnoreCase(target)) {
+ throw new IllegalArgumentException("Invalid PITarget: " + target);
+ }
+ if (!isValidChars(data)) {
+ throw new IllegalArgumentException("Invalid character in PI data: " + data);
+ }
+ }
+ startContent();
+ receiver.processingInstruction(target, data, 0, 0);
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ }
+
+ public void writeCData(/*@NotNull*/ String data) throws XMLStreamException {
+ writeCharacters(data);
+ }
+
+ public void writeDTD(String dtd) throws XMLStreamException {
+ // no-op
+ }
+
+ public void writeEntityRef(String name) throws XMLStreamException {
+ throw new UnsupportedOperationException("writeEntityRef");
+ }
+
+ public void writeStartDocument() throws XMLStreamException {
+ writeStartDocument(null, null);
+ }
+
+ public void writeStartDocument(/*@Nullable*/ String version) throws XMLStreamException {
+ writeStartDocument(null, version);
+ }
+
+ public void writeStartDocument(/*@Nullable*/ String encoding, /*@Nullable*/ String version) throws XMLStreamException {
+ if ("1.1".equals(version)) {
+ nameChecker = Name11Checker.getInstance();
+ }
+ if (depth != -1) {
+ throw new IllegalStateException("writeStartDocument must be the first call");
+ }
+ try {
+ receiver.startDocument(0);
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ depth = 0;
+ }
+
+ public void writeCharacters(/*@Nullable*/ String text)
+ throws XMLStreamException {
+ if (text != null) {
+ if (!isValidChars(text)) {
+ throw new IllegalArgumentException("illegal XML character: " + text);
+ }
+ startContent();
+ try {
+ receiver.characters(text, 0, 0);
+ } catch (XPathException err) {
+ throw new XMLStreamException(err);
+ }
+ }
+ }
+
+ public void writeCharacters(char[] text, int start, int len)
+ throws XMLStreamException {
+ writeCharacters(new String(text, start, len));
+ }
+
+ public String getPrefix(String uri) {
+ String prefix = declaredNamespaces.get(uri);
+ if (prefix == null && rootNamespaceContext != null) {
+ prefix = rootNamespaceContext.getPrefix(uri);
+ }
+ return prefix;
+ }
+
+ public void setPrefix(String prefix, String uri) {
+ if (!isValidURI(uri)) {
+ throw new IllegalArgumentException("Invalid namespace URI: " + uri);
+ }
+ if (!"".equals(prefix) && !isValidNCName(prefix)) {
+ throw new IllegalArgumentException("Invalid namespace prefix: " + prefix);
+ }
+ declaredNamespaces.put(uri, prefix); //sic
+ }
+
+ public void setDefaultNamespace(String uri)
+ throws XMLStreamException {
+ setPrefix("", uri);
+ }
+
+ public void setNamespaceContext(javax.xml.namespace.NamespaceContext context)
+ throws XMLStreamException {
+ if (depth > 0) {
+ throw new IllegalStateException("setNamespaceContext may only be called at the start of the document");
+ }
+ // Unfortunately the JAXP NamespaceContext class does not allow us to discover all the namespaces
+ // that were declared, nor to declare new ones. So we have to retain it separately
+ rootNamespaceContext = context;
+ }
+
+ /*@Nullable*/ public javax.xml.namespace.NamespaceContext getNamespaceContext() {
+ // Note: the spec is unclear. We return the namespace context that was supplied to setNamespaceContext,
+ // regardless of any other subsequent additions
+ return rootNamespaceContext;
+ }
+
+ public Object getProperty(String name) throws IllegalArgumentException {
+ throw new IllegalArgumentException(name);
+ }
+
+ /**
+ * Test whether a supplied name is a valid NCName
+ * @param name the name to be tested
+ * @return true if the name is valid or if checking is disabled
+ */
+
+ private boolean isValidNCName(String name) {
+ return !isChecking || nameChecker.isValidNCName(name);
+ }
+
+ /**
+ * Test whether a supplied character string is valid in XML
+ * @param text the string to be tested
+ * @return true if the string is valid or if checking is disabled
+ */
+
+ private boolean isValidChars(String text) {
+ return !isChecking || (nameChecker.firstInvalidChar(text) == -1);
+ }
+
+ /**
+ * Test whether a supplied namespace URI is a valid URI
+ * @param uri the namespace URI to be tested
+ * @return true if the name is valid or if checking is disabled
+ */
+
+ private boolean isValidURI(String uri) {
+ return !isChecking || StandardURIChecker.getInstance().isValidURI(uri);
+ }
+
+}
+
diff --git a/sf/saxon/event/Stripper.java b/sf/saxon/event/Stripper.java
new file mode 100644
index 0000000..6e03b74
--- /dev/null
+++ b/sf/saxon/event/Stripper.java
@@ -0,0 +1,203 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.FingerprintedQName;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.SpaceStrippingRule;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.RuleTarget;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ComplexType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.Whitespace;
+
+import java.io.Serializable;
+
+
+/**
+ * The RuleBasedStripper class performs whitespace stripping according to the rules of
+ * the xsl:strip-space and xsl:preserve-space instructions.
+ * It maintains details of which elements need to be stripped.
+ * The code is written to act as a SAX-like filter to do the stripping.
+ * @author Michael H. Kay
+ */
+
+
+public class Stripper extends ProxyReceiver {
+
+ public final static StripRuleTarget STRIP = new StripRuleTarget(){};
+ public final static StripRuleTarget PRESERVE = new StripRuleTarget(){};
+ protected SpaceStrippingRule rule;
+
+ public Stripper(/*@NotNull*/ SpaceStrippingRule rule, /*@NotNull*/ Receiver next) {
+ super(next);
+ this.rule = rule;
+ }
+
+ // stripStack is used to hold information used while stripping nodes. We avoid allocating
+ // space on the tree itself to keep the size of nodes down. Each entry on the stack is two
+ // booleans, one indicates the current value of xml-space is "preserve", the other indicates
+ // that we are in a space-preserving element.
+
+ // We implement our own stack to avoid the overhead of allocating objects. The two booleans
+ // are held as the leas-significant bits of a byte.
+
+ private byte[] stripStack = new byte[100];
+ private int top = 0;
+
+ /**
+ * Get a clean copy of this stripper. The new copy shares the same PipelineConfiguration
+ * as the original, but the underlying receiver (that is, the destination for post-stripping
+ * events) is changed.
+ * @param next the next receiver in the pipeline for the new Stripper
+ * @return a dublicate of this Stripper, with the output sent to "next".
+ */
+
+ public Stripper getAnother(Receiver next) {
+ return new Stripper(rule, next);
+ }
+
+ /**
+ * Decide whether an element is in the set of white-space preserving element types
+ *
+ *
+ * @param name Identifies the name of the element whose whitespace is to
+ * be preserved
+ * @return ALWAYS_PRESERVE if the element is in the set of white-space preserving
+ * element types, ALWAYS_STRIP if the element is to be stripped regardless of the
+ * xml:space setting, and STRIP_DEFAULT otherwise
+ * @throws XPathException if the rules are ambiguous and ambiguities are to be
+ * reported as errors
+ */
+
+ public final byte isSpacePreserving(NodeName name) throws XPathException {
+ return rule.isSpacePreserving(name);
+ }
+
+ public static final byte ALWAYS_PRESERVE = 0x01; // whitespace always preserved (e.g. xsl:text)
+ public static final byte ALWAYS_STRIP = 0x02; // whitespace always stripped (e.g. xsl:choose)
+ public static final byte STRIP_DEFAULT = 0x00; // no special action
+ public static final byte PRESERVE_PARENT = 0x04; // parent element specifies xml:space="preserve"
+ public static final byte SIMPLE_CONTENT = 0x08; // type annotation indicates simple typed content
+ public static final byte ASSERTIONS_EXIST = 0x10; // XSD 1.1 assertions are in scope
+
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void open () throws XPathException {
+ // System.err.println("Stripper#startDocument()");
+ top = 0;
+ stripStack[top] = ALWAYS_PRESERVE; // {xml:preserve = false, preserve this element = true}
+ super.open();
+ }
+
+ public void startElement (NodeName elemName, SchemaType type, int locationId, int properties) throws XPathException
+ {
+ // System.err.println("startElement " + nameCode);
+ nextReceiver.startElement(elemName, type, locationId, properties);
+
+ byte preserveParent = stripStack[top];
+ byte preserve = (byte)(preserveParent & (PRESERVE_PARENT | ASSERTIONS_EXIST));
+
+ byte elementStrip = isSpacePreserving(elemName);
+ if (elementStrip == ALWAYS_PRESERVE) {
+ preserve |= ALWAYS_PRESERVE;
+ } else if (elementStrip == ALWAYS_STRIP) {
+ preserve |= ALWAYS_STRIP;
+ }
+ if (type != Untyped.getInstance()) {
+ if (preserve == 0) {
+ // if the element has simple content, whitespace stripping is disabled
+ if (type.isSimpleType() || ((ComplexType)type).isSimpleContent()) {
+ preserve |= SIMPLE_CONTENT;
+ }
+ }
+ if (type instanceof ComplexType && ((ComplexType)type).hasAssertions()) {
+ preserve |= ASSERTIONS_EXIST;
+ }
+ }
+
+ // put "preserve" value on top of stack
+
+ top++;
+ if (top >= stripStack.length) {
+ byte[] newStack = new byte[top*2];
+ System.arraycopy(stripStack, 0, newStack, 0, top);
+ stripStack = newStack;
+ }
+ stripStack[top] = preserve;
+ }
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+
+ // test for xml:space="preserve" | "default"
+
+ if (nameCode.equals(XML_SPACE)) {
+ if (Whitespace.normalizeWhitespace(value).equals("preserve")) {
+ stripStack[top] |= PRESERVE_PARENT;
+ } else {
+ stripStack[top] &= ~PRESERVE_PARENT;
+ }
+ }
+ nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+
+ private static NodeName XML_SPACE = new FingerprintedQName("xml", NamespaceConstant.XML, "space", StandardNames.XML_SPACE);
+
+ /**
+ * Handle an end-of-element event
+ */
+
+ public void endElement () throws XPathException
+ {
+ nextReceiver.endElement();
+ top--;
+ }
+
+ /**
+ * Handle a text node
+ */
+
+ public void characters (CharSequence chars, int locationId, int properties) throws XPathException
+ {
+ // assume adjacent chunks of text are already concatenated
+
+ if (((((stripStack[top] & (ALWAYS_PRESERVE | PRESERVE_PARENT | SIMPLE_CONTENT | ASSERTIONS_EXIST)) != 0) &&
+ (stripStack[top] & ALWAYS_STRIP) == 0)
+ || !Whitespace.isWhite(chars))
+ && chars.length() > 0) {
+ nextReceiver.characters(chars, locationId, properties);
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return true;
+ }
+
+ public static class StripRuleTarget implements RuleTarget, Serializable {
+ public void explain(ExpressionPresenter presenter) {
+ // no-op
+ }
+ }
+}
+
diff --git a/sf/saxon/event/TeeOutputter.java b/sf/saxon/event/TeeOutputter.java
new file mode 100644
index 0000000..dadb01d
--- /dev/null
+++ b/sf/saxon/event/TeeOutputter.java
@@ -0,0 +1,264 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+/**
+ * TeeOutputter: a SequenceReceiver that duplicates received events to two different destinations
+ */
+
+public class TeeOutputter extends SequenceReceiver {
+
+ Receiver seq1;
+ Receiver seq2;
+
+ public TeeOutputter(Receiver seq1, Receiver seq2) {
+ super(seq1.getPipelineConfiguration());
+ this.seq1 = seq1;
+ this.seq2 = seq2;
+ }
+
+ /**
+ * Set the first destination
+ * @param seq1 the first output destination
+ */
+
+ protected void setFirstDestination(Receiver seq1) {
+ this.seq1 = seq1;
+ }
+
+ /**
+ * Set the second destination
+ * @param seq2 the second output destination
+ */
+
+ protected void setSecondDestination(Receiver seq2) {
+ this.seq2 = seq2;
+ }
+
+ /**
+ * Get the first destination
+ * @return the first output destination
+ */
+
+ protected Receiver getFirstDestination() {
+ return seq1;
+ }
+
+ /**
+ * Get the second destination
+ * @return the second output destination
+ */
+
+ protected Receiver getSecondDestination() {
+ return seq2;
+ }
+
+ /**
+ * Output an item (atomic value or node) to the sequence
+ */
+
+ public void append(Item item, int locationId, int copyNamespaces) throws XPathException {
+ if (seq1 instanceof SequenceReceiver) {
+ ((SequenceReceiver)seq1).append(item, locationId, NodeInfo.ALL_NAMESPACES);
+ } else {
+ throw new UnsupportedOperationException("append() not supported");
+ }
+ if (seq2 instanceof SequenceReceiver) {
+ ((SequenceReceiver)seq2).append(item, locationId, NodeInfo.ALL_NAMESPACES);
+ } else {
+ throw new UnsupportedOperationException("append() not supported");
+ }
+ }
+
+ @Override
+ public void open() throws XPathException {
+ super.open();
+ seq1.open();
+ seq2.open();
+ }
+
+ /**
+ * Notify the start of a document node
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ seq1.startDocument(properties);
+ seq2.startDocument(properties);
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ seq1.endDocument();
+ seq2.endDocument();
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param nameCode integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool. The value -1
+* indicates the default type, xs:untyped.
+ * @param locationId an integer which can be interpreted using a LocationMap to return
+* information such as line number and system ID. If no location information is available,
+* the value zero is supplied.
+ * @param properties bit-significant properties of the element node. If there are no revelant
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ seq1.startElement(nameCode, typeCode, locationId, properties);
+ seq2.startElement(nameCode, typeCode, locationId, properties);
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element; however, duplicates may be reported.
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ *
+ * @param namespaceBinding the prefix/uri pair
+ * @throws IllegalStateException: attempt to output a namespace when there is no open element
+ * start tag
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ seq1.namespace(namespaceBinding, properties);
+ seq2.namespace(namespaceBinding, properties);
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param nameCode The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param locationId an integer which can be interpreted using a LocationMap to return
+ * information such as line number and system ID. If no location information is available,
+ * the value zero is supplied.
+ * @param properties Bit significant value. The following bits are defined:
+ * <dt>DISABLE_ESCAPING</dt> <dd>Disable escaping for this attribute</dd>
+ * <dt>NO_SPECIAL_CHARACTERS</dt> <dd>Attribute value contains no special characters</dd>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ seq1.attribute(nameCode, typeCode, value, locationId, properties);
+ seq2.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+
+ public void startContent() throws XPathException {
+ seq1.startContent();
+ seq2.startContent();
+ }
+
+ /**
+ * Notify the end of an element. The receiver must maintain a stack if it needs to know which
+ * element is ending.
+ */
+
+ public void endElement() throws XPathException {
+ seq1.endElement();
+ seq2.endElement();
+ }
+
+ /**
+ * Notify character data. Note that some receivers may require the character data to be
+ * sent in a single event, but in general this is not a requirement.
+ *
+ * @param chars The characters
+ * @param locationId an integer which can be interpreted using a LocationMap to return
+* information such as line number and system ID. If no location information is available,
+* the value zero is supplied.
+ * @param properties Bit significant value. The following bits are defined:
+* <dt>DISABLE_ESCAPING</dt> <dd>Disable escaping for this text node</dd>
+* <dt>USE_CDATA</dt> <dd>Output as a CDATA section</dd>
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ seq1.characters(chars, locationId, properties);
+ seq2.characters(chars, locationId, properties);
+ }
+
+ /**
+ * Output a processing instruction
+ *
+ * @param name The PI name. This must be a legal name (it will not be checked).
+ * @param data The data portion of the processing instruction
+ * @param locationId an integer which can be interpreted using a LocationMap to return
+ * information such as line number and system ID. If no location information is available,
+ * the value zero is supplied.
+ * @param properties Additional information about the PI. The following bits are
+ * defined:
+ * <dt>CHECKED</dt> <dd>Data is known to be legal (e.g. doesn't contain "?>")</dd>
+ * @throws IllegalArgumentException: the content is invalid for an XML processing instruction
+ */
+
+ public void processingInstruction(String name, CharSequence data, int locationId, int properties) throws XPathException {
+ seq1.processingInstruction(name, data, locationId, properties);
+ seq2.processingInstruction(name, data, locationId, properties);
+ }
+
+ /**
+ * Notify a comment. Comments are only notified if they are outside the DTD.
+ *
+ * @param content The content of the comment
+ * @param locationId an integer which can be interpreted using a LocationMap to return
+ * information such as line number and system ID. If no location information is available,
+ * the value zero is supplied.
+ * @param properties Additional information about the comment. The following bits are
+ * defined:
+ * <dt>CHECKED</dt> <dd>Comment is known to be legal (e.g. doesn't contain "--")</dd>
+ * @throws IllegalArgumentException: the content is invalid for an XML comment
+ */
+
+ public void comment(CharSequence content, int locationId, int properties) throws XPathException {
+ seq1.comment(content, locationId, properties);
+ seq2.comment(content, locationId, properties);
+ }
+
+ /**
+ * Notify the end of the event stream
+ */
+
+ public void close() throws XPathException {
+ seq1.close();
+ seq2.close();
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return seq1.usesTypeAnnotations() || seq2.usesTypeAnnotations();
+ }
+}
+
diff --git a/sf/saxon/event/TracingFilter.java b/sf/saxon/event/TracingFilter.java
new file mode 100644
index 0000000..7f33e27
--- /dev/null
+++ b/sf/saxon/event/TracingFilter.java
@@ -0,0 +1,230 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.value.Whitespace;
+
+import java.io.PrintStream;
+
+/**
+ * A filter that can be inserted into a Receiver pipeline to trace the events that pass through.
+ * This class is not normally used in Saxon, but is available for diagnostics when needed.
+ */
+public class TracingFilter extends ProxyReceiver {
+
+ private static int nextid = 0;
+ private int id;
+ private String indent = "";
+ private PrintStream out = System.err;
+
+ /**
+ * Create a TracingFilter and allocate a unique Id.
+ * @param nextReceiver the underlying receiver to which the events will be sent
+ */
+
+ public TracingFilter(Receiver nextReceiver) {
+ super(nextReceiver);
+ id = nextid++;
+ }
+
+ /**
+ * Create a TracingFilter, allocate a unique Id, and supply the destination for diagnostic
+ * trace messages
+ * @param nextReceiver the underlying receiver to which the events will be sent
+ * @param diagnosticOutput the destination for diagnostic trace messages
+ */
+
+ public TracingFilter(Receiver nextReceiver, PrintStream diagnosticOutput) {
+ super(nextReceiver);
+ id = nextid++;
+ out = diagnosticOutput;
+ }
+
+ /**
+ * Get the unique id that was allocated to this TracingFilter
+ * @return the unique id (which is included in all diagnostic messages)
+ */
+
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * Append an arbitrary item (node or atomic value) to the output
+ *
+ * @param item the item to be appended
+ * @param locationId the location of the calling instruction, for diagnostics
+ * @param copyNamespaces if the item is an element node, this indicates whether its namespaces
+ * need to be copied. Values are {@link net.sf.saxon.om.NodeInfo#ALL_NAMESPACES},
+ * {@link net.sf.saxon.om.NodeInfo#LOCAL_NAMESPACES}, {@link net.sf.saxon.om.NodeInfo#NO_NAMESPACES}
+ */
+
+ public void append(Item item, int locationId, int copyNamespaces) throws XPathException {
+ out.println("RCVR " + id + indent + " APPEND " + item.getClass().getName());
+ if (nextReceiver instanceof SequenceReceiver) {
+ ((SequenceReceiver)nextReceiver).append(item, locationId, copyNamespaces);
+ } else {
+ super.append(item, locationId, copyNamespaces);
+ }
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param nameCode The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ out.println("RCVR " + id + indent + " ATTRIBUTE " + nameCode.getDisplayName());
+ nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ out.println("RCVR " + id + indent + " CHARACTERS " + (Whitespace.isWhite(chars) ? "(whitespace)" : ""));
+ FastStringBuffer sb = new FastStringBuffer(chars.length() * 3);
+ for (int i=0; i<chars.length(); i++) {
+ sb.append((int)chars.charAt(i) + " ");
+ }
+ out.println(" \"" + sb + '\"');
+ nextReceiver.characters(chars, locationId, properties);
+ }
+
+ /**
+ * End of document
+ */
+
+ public void close() throws XPathException {
+ out.println("RCVR " + id + indent + " CLOSE");
+ nextReceiver.close();
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ out.println("RCVR " + id + indent + " COMMENT");
+ nextReceiver.comment(chars, locationId, properties);
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ out.println("RCVR " + id + indent + " END DOCUMENT");
+ nextReceiver.endDocument();
+ }
+
+ /**
+ * End of element
+ */
+
+ public void endElement() throws XPathException {
+ indent = indent.substring(2);
+ out.println("RCVR " + id + indent + " END ELEMENT");
+ nextReceiver.endElement();
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element; however, duplicates may be reported.
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ *
+ * @param namespaceBinding the namespace (prefix, uri) pair to be notified
+ * @throws IllegalStateException: attempt to output a namespace when there is no open element
+ * start tag
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ out.println("RCVR " + id + indent + " NAMESPACE " +
+ namespaceBinding.getPrefix() + "=" + namespaceBinding.getURI());
+ nextReceiver.namespace(namespaceBinding, properties);
+ }
+
+ /**
+ * Start of event stream
+ */
+
+ public void open() throws XPathException {
+ out.println("RCVR " + id + indent + " OPEN");
+ nextReceiver.open();
+ }
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ out.println("RCVR " + id + indent + " PROCESSING INSTRUCTION");
+ nextReceiver.processingInstruction(target, data, locationId, properties);
+ }
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+
+
+ public void startContent() throws XPathException {
+ out.println("RCVR " + id + indent + " START CONTENT");
+ nextReceiver.startContent();
+ }
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ out.println("RCVR " + id + indent + " START DOCUMENT");
+ nextReceiver.startDocument(properties);
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param nameCode integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties properties of the element node
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ out.println("RCVR " + id + indent + " START ELEMENT " + nameCode.getDisplayName());
+// out.println("RCVR " + id + indent +
+// " (Loc: sysId=" +
+// getPipelineConfiguration().getLocationProvider().getSystemId(locationId) +
+// " line=" +
+// getPipelineConfiguration().getLocationProvider().getLineNumber(locationId) + ")");
+ indent = indent + " ";
+ nextReceiver.startElement(nameCode, typeCode, locationId, properties);
+ }
+}
+
diff --git a/sf/saxon/event/TransformerReceiver.java b/sf/saxon/event/TransformerReceiver.java
new file mode 100644
index 0000000..5ea53a0
--- /dev/null
+++ b/sf/saxon/event/TransformerReceiver.java
@@ -0,0 +1,121 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+
+
+/**
+ * <b>TransformerReceiver</b> is similar in concept to the JAXP TransformerHandler,
+ * except that it implements Saxon's Receiver interface rather than the standard
+ * SAX2 interface. This means that it allows nodes with type annotations to be
+ * passed down a pipeline from one transformation to another.
+ */
+
+public class TransformerReceiver extends ProxyReceiver {
+
+ Controller controller;
+ Builder builder;
+ Result result;
+
+ /**
+ * Create a TransformerReceiver and initialise variables.
+ * @param controller the Controller (Saxon's implementation of the JAXP {@link javax.xml.transform.Transformer})
+ */
+
+ public TransformerReceiver(Controller controller) {
+ super(controller.makeBuilder());
+ this.controller = controller;
+ this.builder = (Builder)getUnderlyingReceiver();
+ }
+
+ /**
+ * Start of event stream
+ */
+
+ public void open() throws XPathException {
+ builder.setSystemId(systemId);
+ Receiver stripper = controller.makeStripper(builder);
+ if (controller.getExecutable().stripsInputTypeAnnotations()) {
+ stripper = controller.getConfiguration().getAnnotationStripper(stripper);
+ }
+ setUnderlyingReceiver(stripper);
+ nextReceiver.open();
+ }
+
+ /**
+ * Get the Transformer used for this transformation
+ * @return the transformer (which will always be an instance of {@link net.sf.saxon.Controller})
+ */
+
+ public Transformer getTransformer() {
+ return controller;
+ }
+
+ /**
+ * Set the SystemId of the document
+ */
+
+ public void setSystemId(String systemId) {
+ super.setSystemId(systemId);
+ controller.setBaseOutputURI(systemId);
+ }
+
+ /**
+ * Set the output destination of the transformation. This method must be called before
+ * the transformation can proceed.
+ * @param result the destination to which the transformation output will be written
+ */
+
+ public void setResult(Result result) {
+ this.result = result;
+ }
+
+ /**
+ * Get the output destination of the transformation
+ * @return the output destination. May be null if no destination has been set.
+ */
+
+ /*@Nullable*/ public Result getResult() {
+ return result;
+ }
+
+ /**
+ * Override the behaviour of close() in ProxyReceiver, so that it fires off
+ * the transformation of the constructed document
+ */
+
+ public void close() throws XPathException {
+ nextReceiver.close();
+ DocumentInfo doc = (DocumentInfo)builder.getCurrentRoot();
+ builder.reset();
+ builder = null;
+ if (doc==null) {
+ throw new XPathException("No source document has been built");
+ }
+ if (result==null) {
+ throw new XPathException("No output destination has been supplied");
+ }
+ try {
+ controller.transformDocument(doc, result);
+ } catch (TransformerException e) {
+ throw XPathException.makeXPathException(e);
+ }
+ }
+
+
+
+
+}
+
diff --git a/sf/saxon/event/Transmitter.java b/sf/saxon/event/Transmitter.java
new file mode 100644
index 0000000..201aead
--- /dev/null
+++ b/sf/saxon/event/Transmitter.java
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.Source;
+
+/**
+ * A Transmitter is a source of events sent to a Receiver. An implementation of this interface
+ * can be used in any Saxon interface allowing a {@link Source} to be supplied, and allows
+ * an input document to be created programmatically in the form of a stream of "push" events
+ * send to the supplied {@link Receiver}.
+ */
+public abstract class Transmitter implements Source {
+
+ private String systemId;
+
+ /**
+ * Send events to a supplied Receiver
+ * @param receiver the Receiver to which events should be sent.
+ *
+ * <p>The pipelineConfiguration property of this Receiver is guaranteed
+ * to be initialized, providing access to objects such as the Saxon Configuration
+ * and NamePool.</p>
+ *
+ * <p>The implementation of this class does not necessarily need to construct Receiver
+ * events directly. It can do so, for example, via the {@link StreamWriterToReceiver}
+ * class, which translates {@link javax.xml.stream.XMLStreamWriter} events to Receiver events,
+ * or via the {@link ReceivingContentHandler} class, which translates SAX
+ * {@link org.xml.sax.ContentHandler} events to Receiver events.</p>
+ *
+ * @throws net.sf.saxon.trans.XPathException if any failure occurs
+ */
+
+ public abstract void transmit(Receiver receiver) throws XPathException;
+
+ /**
+ * Set the system identifier for this Source.
+ * <p/>
+ * <p>The system identifier is optional if the source does not
+ * get its data from a URL, but it may still be useful to provide one.
+ * The application can use a system identifier, for example, to resolve
+ * relative URIs and to include in error messages and warnings.</p>
+ *
+ * @param systemId The system identifier as a URL string.
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the system identifier that was set with setSystemId.
+ *
+ * @return The system identifier that was set with setSystemId, or null
+ * if setSystemId was not called.
+ */
+ public String getSystemId() {
+ return systemId;
+ }
+}
+
diff --git a/sf/saxon/event/TreeReceiver.java b/sf/saxon/event/TreeReceiver.java
new file mode 100644
index 0000000..e4cfe0f
--- /dev/null
+++ b/sf/saxon/event/TreeReceiver.java
@@ -0,0 +1,333 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.ObjectValue;
+
+/**
+ * A TreeReceiver acts as a bridge between a SequenceReceiver, which can receive
+ * events for constructing any kind of sequence, and an ordinary Receiver, which
+ * only handles events relating to the building of trees. To do this, it has to
+ * process any items added to the sequence using the append() interface; all other
+ * events are passed through unchanged.
+ *
+ * <p>If atomic items are appended to the sequence, then adjacent atomic items are
+ * turned in to a text node by converting them to strings and adding a single space
+ * as a separator.</p>
+ *
+ * <p>If a document node is appended to the sequence, then the document node is ignored
+ * and its children are appended to the sequence.</p>
+ *
+ * <p>If any other node is appended to the sequence, then it is pushed to the result
+ * as a sequence of Receiver events, which may involve walking recursively through the
+ * contents of a tree.</p>
+ */
+
+public class TreeReceiver extends SequenceReceiver {
+ private Receiver nextReceiver;
+ private int level = 0;
+ private boolean[] isDocumentLevel = new boolean[20];
+ // The sequence of events can include startElement/endElement pairs or startDocument/endDocument
+ // pairs at any level. A startDocument/endDocument pair is essentially ignored except at the
+ // outermost level, except that a namespace or attribute node cannot be sent when we're at a
+ // document level. See for example schema90963-err.xsl
+ private boolean inStartTag = false;
+
+ /**
+ * Create a TreeReceiver
+ * @param nextInChain the receiver to which events will be directed, after
+ * expanding append events into more primitive tree-based events
+ */
+
+ public TreeReceiver(/*@NotNull*/ Receiver nextInChain) {
+ super(nextInChain.getPipelineConfiguration());
+ nextReceiver = nextInChain;
+ previousAtomic = false;
+ setPipelineConfiguration(nextInChain.getPipelineConfiguration());
+ }
+
+ public void setSystemId(String systemId) {
+ if (systemId != null && !systemId.equals(this.systemId)) {
+ this.systemId = systemId;
+ if (nextReceiver != null) {
+ nextReceiver.setSystemId(systemId);
+ }
+ }
+ }
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipe) {
+ if (pipelineConfiguration != pipe) {
+ pipelineConfiguration = pipe;
+ if (nextReceiver != null) {
+ nextReceiver.setPipelineConfiguration(pipe);
+ }
+ }
+ }
+
+ /**
+ * Get the underlying Receiver (that is, the next one in the pipeline)
+ * @return the underlying Receiver
+ */
+
+ public Receiver getUnderlyingReceiver() {
+ return nextReceiver;
+ }
+
+ /**
+ * Start of event sequence
+ */
+
+ public void open() throws XPathException {
+ if (nextReceiver == null) {
+ throw new IllegalStateException("TreeReceiver.open(): no underlying receiver provided");
+ }
+ nextReceiver.open();
+ previousAtomic = false;
+ }
+
+ /**
+ * End of event sequence
+ */
+
+ public void close() throws XPathException {
+ if (nextReceiver != null) {
+ nextReceiver.close();
+ }
+ previousAtomic = false;
+ }
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ if (level == 0) {
+ nextReceiver.startDocument(properties);
+ }
+ if (isDocumentLevel.length - 1 < level) {
+ boolean[] d2 = new boolean[level*2];
+ System.arraycopy(isDocumentLevel, 0, d2, 0, level);
+ isDocumentLevel = d2;
+ }
+ isDocumentLevel[level++] = true;
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ level--;
+ if (level == 0) {
+ nextReceiver.endDocument();
+ }
+ }
+
+ /**
+ * Notify the start of an element
+ * @param nameCode integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties bit-significant properties of the element node
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (inStartTag) {
+ startContent();
+ }
+ inStartTag = true;
+ nextReceiver.startElement(nameCode, typeCode, locationId, properties);
+ previousAtomic = false;
+ if (isDocumentLevel.length - 1 < level) {
+ boolean[] d2 = new boolean[level*2];
+ System.arraycopy(isDocumentLevel, 0, d2, 0, level);
+ isDocumentLevel = d2;
+ }
+ isDocumentLevel[level++] = false;
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element; however, duplicates may be reported.
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ * @param namespaceBinding the prefix/uri pair
+ * @throws IllegalStateException: attempt to output a namespace when there is no open element
+ * start tag
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ boolean documentLevel = level==0 || isDocumentLevel[level-1];
+ if (documentLevel || !inStartTag) {
+ throw NoOpenStartTagException.makeNoOpenStartTagException(
+ Type.NAMESPACE, namespaceBinding.getPrefix(),
+ getPipelineConfiguration().getHostLanguage(),
+ documentLevel, getPipelineConfiguration().isSerializing(),
+ null, -1);
+ }
+ nextReceiver.namespace(namespaceBinding, properties);
+ previousAtomic = false;
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ * @param nameCode The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ boolean documentLevel = level==0 || isDocumentLevel[level-1];
+ if (documentLevel || !inStartTag) {
+ throw NoOpenStartTagException.makeNoOpenStartTagException(
+ Type.ATTRIBUTE, nameCode.getDisplayName(),
+ getPipelineConfiguration().getHostLanguage(),
+ documentLevel, getPipelineConfiguration().isSerializing(),
+ null, -1);
+ }
+ nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ previousAtomic = false;
+ }
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+
+
+ public void startContent() throws XPathException {
+ inStartTag = false;
+ nextReceiver.startContent();
+ previousAtomic = false;
+ }
+
+ /**
+ * End of element
+ */
+
+ public void endElement() throws XPathException {
+ if (inStartTag) {
+ startContent();
+ }
+ nextReceiver.endElement();
+ previousAtomic = false;
+ level--;
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (chars.length() > 0) {
+ if (inStartTag) {
+ startContent();
+ }
+ nextReceiver.characters(chars, locationId, properties);
+ }
+ previousAtomic = false;
+ }
+
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (inStartTag) {
+ startContent();
+ }
+ nextReceiver.processingInstruction(target, data, locationId, properties);
+ previousAtomic = false;
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (inStartTag) {
+ startContent();
+ }
+ nextReceiver.comment(chars, locationId, properties);
+ previousAtomic = false;
+ }
+
+
+ /**
+ * Set the URI for an unparsed entity in the document.
+ */
+
+ public void setUnparsedEntity(String name, String uri, String publicId) throws XPathException {
+ nextReceiver.setUnparsedEntity(name, uri, publicId);
+ }
+
+ /**
+ * Append an arbitrary item (node or atomic value) to the output
+ */
+
+ public void append(/*@Nullable*/ Item item, int locationId, int copyNamespaces) throws XPathException {
+ if (item != null) {
+ if (item instanceof AtomicValue || item instanceof ObjectValue) {
+ if (previousAtomic) {
+ characters(" ", locationId, 0);
+ }
+ characters(item.getStringValueCS(), locationId, 0);
+ previousAtomic = true;
+ } else if (item instanceof FunctionItem) {
+ throw new XPathException("Cannot add a function item to a tree");
+ } else if (((NodeInfo)item).getNodeKind() == Type.DOCUMENT) {
+ startDocument(0); // needed to ensure that illegal namespaces or attributes in the content are caught
+ SequenceIterator iter = ((NodeInfo)item).iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ Item it = iter.next();
+ if (it == null) break;
+ append(it, locationId, copyNamespaces);
+ }
+ previousAtomic = false;
+ endDocument();
+ } else {
+ int copyOptions = CopyOptions.TYPE_ANNOTATIONS;
+ if (copyNamespaces == NodeInfo.LOCAL_NAMESPACES) {
+ copyOptions |= CopyOptions.LOCAL_NAMESPACES;
+ } else if (copyNamespaces == NodeInfo.ALL_NAMESPACES) {
+ copyOptions |= CopyOptions.ALL_NAMESPACES;
+ }
+ ((NodeInfo)item).copy(this, copyOptions, locationId);
+ previousAtomic = false;
+ }
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return nextReceiver.usesTypeAnnotations();
+ }
+}
+
diff --git a/sf/saxon/event/TypeCheckingFilter.java b/sf/saxon/event/TypeCheckingFilter.java
new file mode 100644
index 0000000..e2c48b5
--- /dev/null
+++ b/sf/saxon/event/TypeCheckingFilter.java
@@ -0,0 +1,314 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.event;
+
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.CombinedNodeTest;
+import net.sf.saxon.pattern.ContentTypeTest;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Cardinality;
+
+import javax.xml.transform.SourceLocator;
+import java.util.HashSet;
+
+/**
+ * A filter on the push pipeline that performs type checking, both of the item type and the
+ * cardinality.
+ * <p>
+ * Note that the TypeCheckingFilter cannot currently check document node tests of the form
+ * document-node(element(X,Y)), so it is not invoked in such cases. This isn't a big problem, because most
+ * instructions that return document nodes materialize them anyway.
+ */
+
+public class TypeCheckingFilter extends ProxyReceiver {
+
+ private ItemType itemType;
+ private int cardinality;
+ private RoleLocator role;
+ private SourceLocator locator;
+ private int count = 0;
+ private int level = 0;
+ private HashSet<Long> checkedElements = new HashSet<Long>(10);
+ // used to avoid repeated checking when a template creates large numbers of elements of the same type
+ // The key is a (namecode, typecode) pair, packed into a single long
+
+ public TypeCheckingFilter(Receiver next) {
+ super(next);
+ }
+
+ public void setRequiredType(ItemType type, int cardinality, RoleLocator role, SourceLocator locator) {
+ itemType = type;
+ this.cardinality = cardinality;
+ this.role = role;
+ this.locator = locator;
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param nameCode The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ if (level == 0) {
+ if (++count == 2) {
+ checkAllowsMany(locationId);
+ }
+ ItemType type = new CombinedNodeTest(
+ new NameTest(Type.ATTRIBUTE, nameCode, getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ATTRIBUTE, typeCode, getConfiguration(), false));
+ checkItemType(type, locationId);
+ }
+ nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (level == 0) {
+ if (++count == 2) {
+ checkAllowsMany(locationId);
+ }
+ ItemType type = NodeKindTest.TEXT;
+ checkItemType(type, locationId);
+ }
+ nextReceiver.characters(chars, locationId, properties);
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (level == 0) {
+ if (++count == 2) {
+ checkAllowsMany(locationId);
+ }
+ ItemType type = NodeKindTest.COMMENT;
+ checkItemType(type, locationId);
+ }
+ nextReceiver.comment(chars, locationId, properties); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element; however, duplicates may be reported.
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ *
+ * @param namespaceBinding the prefix/uri pair
+ * @throws IllegalStateException: attempt to output a namespace when there is no open element
+ * start tag
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ if (level == 0) {
+ if (++count == 2) {
+ checkAllowsMany(0);
+ }
+ ItemType type = NodeKindTest.NAMESPACE;
+ checkItemType(type, 0);
+ }
+ nextReceiver.namespace(namespaceBinding, properties); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (level == 0) {
+ if (++count == 2) {
+ checkAllowsMany(locationId);
+ }
+ ItemType type = NodeKindTest.PROCESSING_INSTRUCTION;
+ checkItemType(type, locationId);
+ }
+ nextReceiver.processingInstruction(target, data, locationId, properties);
+ }
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ if (level == 0) {
+ if (++count == 2) {
+ checkAllowsMany(0);
+ }
+ ItemType type = NodeKindTest.DOCUMENT;
+ checkItemType(type, 0);
+ }
+ level++;
+ nextReceiver.startDocument(properties);
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param nameCode integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties properties of the element node
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (level == 0) {
+ if (++count == 1) {
+ // don't bother with any caching on the first item, it will often be the only one
+ ItemType type = new CombinedNodeTest(
+ new NameTest(Type.ELEMENT, nameCode, getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, typeCode, getConfiguration(), false));
+ checkItemType(type, locationId);
+ } else {
+ if (count == 2) {
+ checkAllowsMany(locationId);
+ }
+ long key = ((long) (nameCode.allocateNameCode(getNamePool()) & NamePool.FP_MASK)) << 32 | (long) (typeCode.getFingerprint());
+ if (!checkedElements.contains(key)) {
+ ItemType type = new CombinedNodeTest(
+ new NameTest(Type.ELEMENT, nameCode, getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, typeCode, getConfiguration(), false));
+ checkItemType(type, locationId);
+ checkedElements.add(key);
+ }
+ }
+ }
+ level++;
+ nextReceiver.startElement(nameCode, typeCode, locationId, properties);
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ level--;
+ nextReceiver.endDocument();
+ }
+
+ /**
+ * End of element
+ */
+
+ public void endElement() throws XPathException {
+ level--;
+ nextReceiver.endElement();
+ }
+
+ /**
+ * End of event stream
+ */
+
+ public void close() throws XPathException {
+ if (count == 0 && !Cardinality.allowsZero(cardinality)) {
+ XPathException err = new XPathException("An empty sequence is not allowed as the " +
+ role.getMessage());
+ String errorCode = role.getErrorCode();
+ err.setErrorCode(errorCode);
+ if (!"XPDY0050".equals(errorCode)) {
+ err.setIsTypeError(true);
+ }
+ throw err;
+ }
+ // don't pass on the close event
+ }
+
+ /**
+ * Output an item (atomic value or node) to the sequence
+ */
+
+ public void append(Item item, int locationId, int copyNamespaces) throws XPathException {
+ if (level == 0) {
+ if (++count == 2) {
+ checkAllowsMany(locationId);
+ }
+ checkItemType(Type.getItemType(item, getConfiguration().getTypeHierarchy()), locationId);
+ }
+ if (nextReceiver instanceof SequenceReceiver) {
+ ((SequenceReceiver)nextReceiver).append(item, locationId, copyNamespaces);
+ } else {
+ super.append(item, locationId, copyNamespaces);
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return true;
+ }
+
+ private void checkItemType(ItemType type, long locationId) throws XPathException {
+ if (!getConfiguration().getTypeHierarchy().isSubType(type, itemType)) {
+ String message = role.composeErrorMessage(itemType, type);
+ String errorCode = role.getErrorCode();
+ XPathException err = new XPathException(message);
+ err.setErrorCode(errorCode);
+ if (!"XPDY0050".equals(errorCode)) {
+ err.setIsTypeError(true);
+ }
+ if (locationId == 0) {
+ err.setLocator(locator);
+ } else {
+ err.setLocator(ExpressionLocation.getSourceLocator(locationId,
+ getPipelineConfiguration().getLocationProvider()));
+ }
+ throw err;
+ }
+ }
+
+ private void checkAllowsMany(long locationId) throws XPathException {
+ if (!Cardinality.allowsMany(cardinality)) {
+ XPathException err = new XPathException("A sequence of more than one item is not allowed as the " +
+ role.getMessage());
+ String errorCode = role.getErrorCode();
+ err.setErrorCode(errorCode);
+ if (!"XPDY0050".equals(errorCode)) {
+ err.setIsTypeError(true);
+ }
+ if (locationId == 0) {
+ err.setLocator(locator);
+ } else {
+ err.setLocator(ExpressionLocation.getSourceLocator(locationId,
+ getPipelineConfiguration().getLocationProvider()));
+ }
+ throw err;
+ }
+ }
+
+
+
+}
+
diff --git a/sf/saxon/event/package.html b/sf/saxon/event/package.html
new file mode 100644
index 0000000..33d1bb0
--- /dev/null
+++ b/sf/saxon/event/package.html
@@ -0,0 +1,67 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.event</title>
+</head>
+
+<body>
+
+<p>This package provides classes that feed SAX-like events from one tree to another.
+Some of these classes are associated with serializing the output of a stylesheet, but there
+are also classes for building a tree from a stream of events, for stripping whitespace, and
+so on.</p>
+
+<p>The {@link net.sf.saxon.event.Receiver} interface defines a class that accepts a stream of events, with one method
+defined for each kind of event. The events are modelled on the design of SAX, but adapted
+to the XPath data model and to the use of Saxon's NamePool. Attributes and namespaces are
+notified individually <i>after</i> the start of the relevant element. Many of the classes
+in this package are implementations of the <code>Receiver</code> interface.</p>
+
+<p>The immediate output of node constructors in a query or stylesheet goes to a {@link net.sf.saxon.event.SequenceReceiver}.
+This is a subclass of <code>Receiver</code> that can handle an arbitrary sequence, containing atomic values
+as well as nodes. When constructing the content of an element, a {@link net.sf.saxon.event.ComplexContentOutputter} is used;
+when constructing the content of a node such as a text node or attribute, a <code>SequenceOutputter</code>
+is used instead.</p>
+
+<p>The final destination of the push pipeline is sometimes a serializer, and sometimes a tree builder.
+The final serialization classes are subclasses of <code>Emitter</code>, but some of the serialization work
+(such as indentation or application of character maps) is done by other classes on the pipeline. These
+are generally constructed by extending the <code>ProxyReceiver</code> class.</p>
+
+<p>The Emitter is an abstract implementation of the Receiver interface. As well as supporting
+the Receiver interface, it provides methods for controlling the destination of serialized output
+(a Writer or OutputStream) and for setting serialization properties (in a Properties object).
+In practice nearly all the implementations of Receiver are currently subclasses of Emitter,
+but this may change in the future.</p>
+
+<p>The package includes emitters for the standard output methods xml, html, and text, and
+proxy emitters to allow a sequence of filters to be applied to the output.</p>,
+
+<p>The class <code>ContentHandlerProxy</code> allows events to be converted into standard SAX events and
+sent to a SAX2 <code>ContentHandler</code>. Similarly, the class <code>ProxyReceiver</code> acts as a
+<code>ContentHandler</code>, accepting SAX2 events and feeding them into a <code>Receiver</code> pipeline.</p>
+
+<p>The class <code>Builder</code> is a <code>Receiver</code> that constructs a tree representation of the
+document in memory. There are two subclasses for Saxon's two native tree models. Other classes such as
+a <code>Stripper</code> and a <code>NamespaceReducer</code> are used to modify the document by adding
+filters to the pipeline.</p>
+
+<p>Saxon's schema validator and serializer are both implemented using this push pipeline model.
+ The classes that perform
+schema validation are part of package: {@link com.saxonica.validate}, while the serialization classes
+ are in {@link net.sf.saxon.serialize}.</p>
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+30 July 2010</i></p>
+</body>
+</html>
diff --git a/sf/saxon/evpull/BlockEventIterator.java b/sf/saxon/evpull/BlockEventIterator.java
new file mode 100644
index 0000000..e9aeae4
--- /dev/null
+++ b/sf/saxon/evpull/BlockEventIterator.java
@@ -0,0 +1,72 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Iterate over the instructions in a Block, concatenating the result of each instruction
+ * into a single combined sequence.
+ */
+
+public class BlockEventIterator implements EventIterator {
+
+ private Expression[] children;
+ private int i = 0;
+ private EventIterator child;
+ private XPathContext context;
+
+ /**
+ * Create an EventIterator over the results of evaluating a Block
+ * @param children the sequence of instructions comprising the Block
+ * @param context the XPath dynamic context
+ */
+
+ public BlockEventIterator(Expression[] children, XPathContext context) {
+ this.children = children;
+ this.context = context;
+ }
+
+ /**
+ * Get the next item in the sequence.
+ *
+ * @return the next item, or null if there are no more items.
+ * @throws XPathException if an error occurs retrieving the next item
+ */
+
+ /*@Nullable*/ public PullEvent next() throws XPathException {
+ while (true) {
+ if (child == null) {
+ child = children[i++].iterateEvents(context);
+ }
+ PullEvent current = child.next();
+ if (current != null) {
+ return current;
+ }
+ child = null;
+ if (i >= children.length) {
+ return null;
+ }
+ }
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return false;
+ }
+}
+
diff --git a/sf/saxon/evpull/BracketedDocumentIterator.java b/sf/saxon/evpull/BracketedDocumentIterator.java
new file mode 100644
index 0000000..aa532da
--- /dev/null
+++ b/sf/saxon/evpull/BracketedDocumentIterator.java
@@ -0,0 +1,82 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * The class is an EventIterator that handles the events arising from a document node constructor:
+ * that is, the start/end event pair for the document node, bracketing a sequence of events for the
+ * content of the document.
+ *
+ * <p>This class does not normalize the content (for example by merging adjacent text nodes). That is the job
+ * of the {@link ComplexContentProcessor}.</p>
+ *
+ */
+public class BracketedDocumentIterator implements EventIterator {
+
+ private EventIterator content;
+ private int state = INITIAL_STATE;
+
+ private static final int INITIAL_STATE = 0;
+ private static final int PROCESSING_CHILDREN = 1;
+ private static final int EXHAUSTED = 2;
+
+ /**
+ * Constructor
+ * @param content iterator that delivers the content of the document
+ */
+
+ public BracketedDocumentIterator(EventIterator content) {
+ this.content = EventStackIterator.flatten(content);
+ state = 0;
+ }
+
+ /**
+ * Get the next event in the sequence
+ * @return the next event, or null when the sequence is exhausted
+ * @throws net.sf.saxon.trans.XPathException if a dynamic evaluation error occurs
+ */
+
+ /*@Nullable*/ public PullEvent next() throws XPathException {
+
+ switch (state) {
+ case INITIAL_STATE:
+ state = PROCESSING_CHILDREN;
+ return StartDocumentEvent.getInstance();
+
+ case PROCESSING_CHILDREN:
+ PullEvent pe = content.next();
+ if (pe == null) {
+ state = EXHAUSTED;
+ return EndDocumentEvent.getInstance();
+ } else {
+ return pe;
+ }
+
+ case EXHAUSTED:
+ return null;
+
+ default:
+ throw new AssertionError("BracketedDocumentIterator state " + state);
+ }
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/evpull/BracketedElementIterator.java b/sf/saxon/evpull/BracketedElementIterator.java
new file mode 100644
index 0000000..f580142
--- /dev/null
+++ b/sf/saxon/evpull/BracketedElementIterator.java
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+
+/**
+ * The class is an EventIterator that handles the events arising from an element constructor:
+ * that is, the start/end event pair for the element node, bracketing a sequence of events for the
+ * content of the element.
+ *
+ * <p>This class does not normalize the content (for example by merging adjacent text nodes). That is the job
+ * of the {@link ComplexContentProcessor}.</p>
+ *
+ * <p>The event stream consumed by a BracketedElementIterator may contain freestanding attribute and namespace nodes.
+ * The event stream delivered by a BracketedElementIterator, however, packages all attributes and namespaces as
+ * part of the startElement event.</p>
+ */
+public class BracketedElementIterator implements EventIterator {
+
+ private StartElementEvent start;
+ private EventIterator content;
+ private PullEvent pendingContent;
+ private EndElementEvent end;
+ private int state = INITIAL_STATE;
+
+ private static final int INITIAL_STATE = 0;
+ private static final int PROCESSING_FIRST_CHILD = 1;
+ private static final int PROCESSING_REMAINING_CHILDREN = 2;
+ private static final int REACHED_END_TAG = 3;
+ private static final int EXHAUSTED = 4;
+
+ /**
+ * Constructor
+ * @param start the StartElementEvent object
+ * @param content iterator that delivers the content of the element
+ * @param end the EndElementEvent object
+ */
+
+ public BracketedElementIterator(StartElementEvent start, EventIterator content, EndElementEvent end) {
+ this.start = start;
+ this.content = EventStackIterator.flatten(content);
+ this.end = end;
+ state = 0;
+ }
+
+ /**
+ * Get the next event in the sequence
+ * @return the next event, or null when the sequence is exhausted
+ * @throws XPathException if a dynamic evaluation error occurs
+ */
+
+ /*@Nullable*/ public PullEvent next() throws XPathException {
+
+ switch (state) {
+ case INITIAL_STATE:
+ while (true) {
+ PullEvent pe = content.next();
+ if (pe == null) {
+ pendingContent = null;
+ state = REACHED_END_TAG;
+ break;
+ } else if (pe instanceof NodeInfo) {
+ int k = ((NodeInfo)pe).getNodeKind();
+ if (k == Type.NAMESPACE) {
+ NamespaceBinding nscode = new NamespaceBinding(
+ ((NodeInfo)pe).getLocalPart(), ((NodeInfo)pe).getStringValue());
+ start.addNamespace(nscode);
+ continue;
+ } else if (k == Type.ATTRIBUTE) {
+ start.addAttribute((NodeInfo)pe);
+ continue;
+ } else if (k == Type.TEXT && ((NodeInfo)pe).getStringValueCS().length() == 0) {
+ // ignore a zero-length text node
+ continue;
+ }
+ }
+ pendingContent = pe;
+ state = PROCESSING_FIRST_CHILD;
+ break;
+ }
+ start.namespaceFixup();
+ return start;
+
+ case PROCESSING_FIRST_CHILD:
+ state = PROCESSING_REMAINING_CHILDREN;
+ return pendingContent;
+
+ case PROCESSING_REMAINING_CHILDREN:
+ PullEvent pe = content.next();
+ if (pe == null) {
+ state = EXHAUSTED;
+ return end;
+ } else {
+ return pe;
+ }
+
+ case REACHED_END_TAG:
+ state = EXHAUSTED;
+ return end;
+
+ case EXHAUSTED:
+ return null;
+
+ default:
+ throw new AssertionError("BracketedEventIterator state " + state);
+ }
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/evpull/ComplexContentProcessor.java b/sf/saxon/evpull/ComplexContentProcessor.java
new file mode 100644
index 0000000..f0c7ef5
--- /dev/null
+++ b/sf/saxon/evpull/ComplexContentProcessor.java
@@ -0,0 +1,275 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * The ComplexContentProcessor is an EventIterator that deals with the events occurring between
+ * a startElement and endElement (or startDocument and endDocument) according to the XSLT/XQuery
+ * rules for constructing complex content. This includes:
+ *
+ * <ul>
+ * <li>Converting atomic values to text nodes (inserting space as a separator between adjacent nodes)</li>
+ * <li>Replacing nested document nodes by their children</li>
+ * <li>Merging adjacent text nodes and dropping zero-length text nodes</li>
+ * <li>Detecting mispositioned or duplicated attribute and namespace nodes</li>
+ *
+ * </ul>
+ *
+ * <p>Note that if the content includes nodes such as element nodes, these will not be decomposed into
+ * a sequence of tree events, they will simply be returned as nodes.</p>
+ */
+public class ComplexContentProcessor implements EventIterator {
+
+ private Configuration config;
+ private EventIterator base;
+ private PullEvent[] startEventStack; // contains either startElement or startDocument events
+ private int depth;
+ private NodeInfo pendingTextNode;
+ private boolean pendingTextNodeIsMutable;
+ private boolean prevAtomic = false;
+ private PullEvent pendingOutput = null;
+
+ /**
+ * Create the ComplexContentProcessor
+ * @param config the Saxon Configuration
+ * @param base the EventIterator that delivers the content of the element or document node
+ */
+
+ public ComplexContentProcessor(Configuration config, EventIterator base) {
+ this.config = config;
+ this.base = EventStackIterator.flatten(base);
+ startEventStack = new PullEvent[20];
+ depth = 0;
+ }
+
+ /**
+ * Get the next event in the sequence. This will never be an EventIterator.
+ *
+ * @return the next event, or null when the sequence is exhausted
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic evaluation error occurs
+ */
+
+ public PullEvent next() throws XPathException {
+ if (pendingOutput != null) {
+ PullEvent next = pendingOutput;
+ pendingOutput = null;
+ return next;
+ } else {
+ return advance();
+ }
+ }
+
+ /*@Nullable*/ private PullEvent advance() throws XPathException {
+ while (true) {
+ if (depth == 0) {
+ PullEvent e = base.next();
+ if (e instanceof StartElementEvent) {
+ push(e);
+ } else if (e instanceof StartDocumentEvent) {
+ push(e);
+ }
+ return e;
+ } else {
+ PullEvent e = base.next();
+ if (e instanceof StartElementEvent) {
+ prevAtomic = false;
+ push(e);
+ if (pendingTextNode != null) {
+ pendingOutput = e;
+ PullEvent next = pendingTextNode;
+ pendingTextNode = null;
+ return next;
+ } else {
+ return e;
+ }
+ } else if (e instanceof StartDocumentEvent) {
+ prevAtomic = false;
+ push(e);
+ if (pendingTextNode != null) {
+ pendingOutput = e;
+ PullEvent next = pendingTextNode;
+ pendingTextNode = null;
+ return next;
+ } else {
+ //continue;
+ }
+ } else if (e instanceof EndElementEvent) {
+ prevAtomic = false;
+ pop();
+ if (pendingTextNode != null) {
+ pendingOutput = e;
+ PullEvent next = pendingTextNode;
+ pendingTextNode = null;
+ return next;
+ } else {
+ return e;
+ }
+ } else if (e instanceof EndDocumentEvent) {
+ prevAtomic = false;
+ pop();
+ if (pendingTextNode != null) {
+ pendingOutput = e;
+ PullEvent next = pendingTextNode;
+ pendingTextNode = null;
+ return next;
+ } else {
+ return e;
+ }
+ } else if (e instanceof NodeInfo) {
+ prevAtomic = false;
+ switch (((NodeInfo)e).getNodeKind()) {
+ case Type.TEXT:
+ if (pendingTextNode == null) {
+ pendingTextNode = (NodeInfo)e;
+ pendingTextNodeIsMutable = false;
+ } else if (pendingTextNodeIsMutable) {
+ FastStringBuffer sb = (FastStringBuffer)((Orphan)pendingTextNode).getStringValueCS();
+ sb.append(((NodeInfo)e).getStringValueCS());
+ } else {
+ Orphan o = new Orphan(config);
+ o.setNodeKind(Type.TEXT);
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ sb.append(pendingTextNode.getStringValueCS());
+ sb.append(((NodeInfo)e).getStringValueCS());
+ o.setStringValue(sb);
+ pendingTextNode = o;
+ pendingTextNodeIsMutable = true;
+ }
+ continue;
+ default:
+ if (pendingTextNode != null) {
+ pendingOutput = e;
+ PullEvent next = pendingTextNode;
+ pendingTextNode = null;
+ return next;
+ } else {
+ return e;
+ }
+ }
+ } else if (e instanceof AtomicValue) {
+ if (prevAtomic) {
+ FastStringBuffer sb = (FastStringBuffer)((Orphan)pendingTextNode).getStringValueCS();
+ sb.append(' ');
+ sb.append(((AtomicValue)e).getStringValueCS());
+ } else if (pendingTextNode != null) {
+ prevAtomic = true;
+ if (pendingTextNodeIsMutable) {
+ FastStringBuffer sb = (FastStringBuffer)((Orphan)pendingTextNode).getStringValueCS();
+ sb.append(((AtomicValue)e).getStringValueCS());
+ } else {
+ Orphan o = new Orphan(config);
+ o.setNodeKind(Type.TEXT);
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ sb.append(pendingTextNode.getStringValueCS());
+ sb.append(((AtomicValue)e).getStringValueCS());
+ o.setStringValue(sb);
+ pendingTextNode = o;
+ pendingTextNodeIsMutable = true;
+ }
+ } else {
+ prevAtomic = true;
+ Orphan o = new Orphan(config);
+ o.setNodeKind(Type.TEXT);
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ sb.append(((AtomicValue)e).getStringValueCS());
+ o.setStringValue(sb);
+ pendingTextNode = o;
+ pendingTextNodeIsMutable = true;
+ }
+ //continue;
+ } else {
+ throw new AssertionError("Unknown event");
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Push a startElement or startDocument event onto the stack. At the same time, if it is a startElement
+ * event, remove any redundant namespace declarations
+ * @param p the startElement or startDocument event
+ */
+
+ private void push(PullEvent p) {
+ if (depth >= startEventStack.length - 1) {
+ PullEvent[] b2 = new PullEvent[depth*2];
+ System.arraycopy(startEventStack, 0, b2, 0, startEventStack.length);
+ startEventStack = b2;
+ }
+ if (p instanceof StartElementEvent) {
+ int retained = 0;
+ NamespaceBinding[] nsp = ((StartElementEvent)p).getLocalNamespaces();
+ for (int nspi = 0; nspi < nsp.length; nspi++) {
+ if (nsp[nspi] == null) {
+ break;
+ }
+ retained++;
+ outer:
+ for (int i=depth-1; i>=0; i--) {
+ PullEvent q = startEventStack[i];
+ if (q instanceof StartElementEvent) {
+ NamespaceBinding[] nsq = ((StartElementEvent)q).getLocalNamespaces();
+ for (int nsqi = 0; nsqi < nsq.length; nsqi++) {
+ if (nsp[nspi] == nsq[nsqi]) {
+ nsp[nspi] = null;
+ retained--;
+ break outer;
+ } else if (nsp[nspi].getPrefix().equals(nsq[nsqi].getPrefix())) {
+ break outer;
+ }
+ }
+ }
+ }
+ }
+ if (retained < nsp.length) {
+ NamespaceBinding[] nsr = new NamespaceBinding[retained];
+ int nsri = 0;
+ for (int nspi=0; nspi<nsp.length; nspi++) {
+ if (nsp[nspi] != null) {
+ nsr[nsri++] = nsp[nspi];
+ if (nsri == retained) {
+ break;
+ }
+ }
+ }
+ ((StartElementEvent)p).setLocalNamespaces(nsr);
+ }
+ }
+ startEventStack[depth++] = p;
+ prevAtomic = false;
+ }
+
+ private void pop() {
+ depth--;
+ prevAtomic = false;
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/evpull/Decomposer.java b/sf/saxon/evpull/Decomposer.java
new file mode 100644
index 0000000..352b1ab
--- /dev/null
+++ b/sf/saxon/evpull/Decomposer.java
@@ -0,0 +1,126 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NameOfNode;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.tiny.TinyNodeImpl;
+import net.sf.saxon.tree.tiny.TinyTreeEventIterator;
+import net.sf.saxon.type.Type;
+
+/**
+ * This class takes a sequence of pull events and turns it into fully-decomposed form, that is, it
+ * takes and document and element nodes in the sequence and turns them into a subsequence consisting of a
+ * start element|document event, a content sequence, and an end element|document event, recursively.
+ *
+ * <p>The resulting sequence is decomposed, but not flat (it will contain nested EventIterators). To flatten
+ * it, use {@link EventStackIterator#flatten(EventIterator)} </p>
+ */
+public class Decomposer implements EventIterator {
+
+ private EventIterator base;
+ private PipelineConfiguration pipe;
+
+ /**
+ * Create a Decomposer, which turns an event sequence into fully decomposed form
+ * @param base the base sequence, which may be fully composed, fully decomposed, or
+ * anything in between
+ * @param pipe the Saxon pipeline configuration
+ */
+
+ public Decomposer(EventIterator base, PipelineConfiguration pipe) {
+ this.pipe = pipe;
+ this.base = EventStackIterator.flatten(base);
+ }
+
+ /**
+ * Create a Decomposer which returns the sequence of events corresponding to
+ * a particular node
+ * @param node the node to be decomposed
+ * @param pipe the Saxon pipeline configuration
+ */
+
+ public Decomposer(NodeInfo node, PipelineConfiguration pipe) {
+ this.pipe = pipe;
+ base = new SingletonEventIterator(node);
+ }
+
+ /**
+ * Get the next event in the sequence
+ *
+ * @return the next event, or null when the sequence is exhausted. Note that since an EventIterator is
+ * itself a PullEvent, this method may return a nested iterator.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic evaluation error occurs
+ */
+
+ public PullEvent next() throws XPathException {
+ PullEvent pe = base.next();
+ if (pe instanceof NodeInfo) {
+ NodeInfo node = (NodeInfo)pe;
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT: {
+ if (node instanceof TinyNodeImpl) {
+ return new TinyTreeEventIterator(((TinyNodeImpl)node), pipe);
+ } else {
+ SequenceIterator content = node.iterateAxis(AxisInfo.CHILD);
+ EventIterator contentEvents = new EventIteratorOverSequence(content);
+ return new BracketedDocumentIterator(
+ new Decomposer(contentEvents, pipe));
+ }
+ }
+ case Type.ELEMENT: {
+ if (node instanceof TinyNodeImpl) {
+ return new TinyTreeEventIterator(((TinyNodeImpl)node), pipe);
+ } else {
+ SequenceIterator content = node.iterateAxis(AxisInfo.CHILD);
+ EventIterator contentEvents = new EventIteratorOverSequence(content);
+ StartElementEvent see = new StartElementEvent(pipe);
+ see.setElementName(new NameOfNode(node));
+ see.setTypeCode(node.getSchemaType());
+ see.setLocalNamespaces(node.getDeclaredNamespaces(null));
+ AxisIterator atts = node.iterateAxis(AxisInfo.ATTRIBUTE);
+ while (true) {
+ NodeInfo att = atts.next();
+ if (att == null) {
+ break;
+ }
+ see.addAttribute(att);
+ }
+ return new BracketedElementIterator(
+ see,
+ new Decomposer(contentEvents, pipe),
+ EndElementEvent.getInstance());
+ }
+ }
+ default:
+ return node;
+ }
+ } else {
+ return pe;
+ }
+ }
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return false;
+ }
+
+}
+
diff --git a/sf/saxon/evpull/EmptyEventIterator.java b/sf/saxon/evpull/EmptyEventIterator.java
new file mode 100644
index 0000000..5522b2a
--- /dev/null
+++ b/sf/saxon/evpull/EmptyEventIterator.java
@@ -0,0 +1,47 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+/**
+ * This class is an EventIterator over an empty sequence. It is a singleton class.
+ */
+public class EmptyEventIterator implements EventIterator {
+
+ private static EmptyEventIterator THE_INSTANCE = new EmptyEventIterator();
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular instance
+ */
+
+ public static EmptyEventIterator getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * Get the next event in the sequence
+ * @return null (there is never a next event)
+ */
+
+ /*@Nullable*/ public PullEvent next() {
+ return null;
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/evpull/EndDocumentEvent.java b/sf/saxon/evpull/EndDocumentEvent.java
new file mode 100644
index 0000000..eba4711
--- /dev/null
+++ b/sf/saxon/evpull/EndDocumentEvent.java
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+/**
+ * Pull event representing the end of a document
+ */
+public class EndDocumentEvent implements PullEvent {
+
+ private final static EndDocumentEvent THE_INSTANCE = new EndDocumentEvent();
+
+ public static EndDocumentEvent getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private EndDocumentEvent() {
+ }
+
+
+}
diff --git a/sf/saxon/evpull/EndElementEvent.java b/sf/saxon/evpull/EndElementEvent.java
new file mode 100644
index 0000000..7f23e26
--- /dev/null
+++ b/sf/saxon/evpull/EndElementEvent.java
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+/**
+ * Pull event representing the end of an element node
+ */
+public class EndElementEvent implements PullEvent {
+
+ private final static EndElementEvent THE_INSTANCE = new EndElementEvent();
+
+ public static EndElementEvent getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private EndElementEvent() {
+ }
+
+
+}
diff --git a/sf/saxon/evpull/EventAnnotationStripper.java b/sf/saxon/evpull/EventAnnotationStripper.java
new file mode 100644
index 0000000..4d23664
--- /dev/null
+++ b/sf/saxon/evpull/EventAnnotationStripper.java
@@ -0,0 +1,74 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.wrapper.VirtualUntypedCopy;
+import net.sf.saxon.type.Type;
+
+/**
+ * This class is an EventIterator that filters a stream of pull events setting
+ * the type annotation on element nodes to xs:untyped and on attribute nodes to
+ * xs:untypedAtomic
+ */
+public class EventAnnotationStripper implements EventIterator {
+
+ private EventIterator base;
+
+ /**
+ * Create an EventAnnotationStripper
+ * @param base the stream of events whose type annotations are to be stripped (set to untyped)
+ */
+
+ public EventAnnotationStripper(EventIterator base) {
+ this.base = EventStackIterator.flatten(base);
+ }
+
+ /**
+ * Get the next event in the sequence
+ *
+ * @return the next event, or null when the sequence is exhausted. Note that since an EventIterator is
+ * itself a PullEvent, this method may return a nested iterator.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic evaluation error occurs
+ */
+
+ public PullEvent next() throws XPathException {
+ PullEvent pe = base.next();
+ if (pe instanceof StartElementEvent) {
+ StartElementEvent see = (StartElementEvent)pe;
+ see.stripTypeAnnotations();
+ return see;
+ } else if (pe instanceof NodeInfo) {
+ // Make a virtual untyped copy of the node
+ switch (((NodeInfo)pe).getNodeKind()) {
+ case Type.ELEMENT:
+ case Type.ATTRIBUTE:
+ return VirtualUntypedCopy.makeVirtualUntypedCopy((NodeInfo)pe, (NodeInfo)pe);
+ default:
+ return pe;
+ }
+ } else {
+ return pe;
+ }
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/evpull/EventIterator.java b/sf/saxon/evpull/EventIterator.java
new file mode 100644
index 0000000..a147311
--- /dev/null
+++ b/sf/saxon/evpull/EventIterator.java
@@ -0,0 +1,34 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * An iterator over a sequence of events
+ */
+public interface EventIterator extends PullEvent {
+
+ /**
+ * Get the next event in the sequence
+ * @return the next event, or null when the sequence is exhausted. Note that since an EventIterator is
+ * itself a PullEvent, this method may return a nested iterator.
+ * @throws XPathException if a dynamic evaluation error occurs
+ */
+
+ /*@Nullable*/ public PullEvent next() throws XPathException;
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence();
+}
+
diff --git a/sf/saxon/evpull/EventIteratorOverSequence.java b/sf/saxon/evpull/EventIteratorOverSequence.java
new file mode 100644
index 0000000..d20cc40
--- /dev/null
+++ b/sf/saxon/evpull/EventIteratorOverSequence.java
@@ -0,0 +1,52 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class maps a SequenceIterator to an EventIterator, by simply returning the items in the sequence
+ * as PullEvents.
+ */
+public class EventIteratorOverSequence implements EventIterator {
+
+ SequenceIterator base;
+
+ /**
+ * Create an EventIterator that wraps a given SequenceIterator
+ * @param base the SequenceIterator to be wrapped
+ */
+
+ public EventIteratorOverSequence(SequenceIterator base) {
+ this.base = base;
+ }
+
+ /**
+ * Get the next PullEvent in the sequence
+ * @return the next PullEvent
+ * @throws XPathException in case of a dynamic error
+ */
+
+ public PullEvent next() throws XPathException {
+ return base.next();
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/evpull/EventIteratorToReceiver.java b/sf/saxon/evpull/EventIteratorToReceiver.java
new file mode 100644
index 0000000..f145d59
--- /dev/null
+++ b/sf/saxon/evpull/EventIteratorToReceiver.java
@@ -0,0 +1,106 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+
+import java.util.Iterator;
+
+/**
+ * Class to read pull events from an EventIterator and write them to a Receiver
+ */
+public class EventIteratorToReceiver {
+
+ /**
+ * Private constructor: this class holds static methods only
+ */
+
+ private EventIteratorToReceiver() {}
+
+ /**
+ * Read the data obtained from an EventIterator and write the same data to a SequenceReceiver
+ * @param in the input EventIterator
+ * @param out the output Receiver
+ * @throws XPathException
+ */
+
+ public static void copy(EventIterator in, SequenceReceiver out) throws XPathException {
+ in = EventStackIterator.flatten(in);
+ int level = 0;
+ out.open();
+ while (true) {
+ PullEvent event = in.next();
+ if (event == null) {
+ break;
+ }
+ if (event instanceof Orphan && ((Orphan)event).getNodeKind() == Type.TEXT) {
+ out.characters(((Orphan)event).getStringValueCS(), 0, 0);
+ } else if (event instanceof DocumentInfo && level > 0) {
+ AxisIterator kids = ((DocumentInfo)event).iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo node = (NodeInfo)kids.next();
+ if (node == null) {
+ break;
+ }
+ out.append(node, 0, 0);
+ }
+ } else if (event instanceof Item) {
+ out.append((Item)event, 0, NodeInfo.ALL_NAMESPACES);
+ } else if (event instanceof StartElementEvent) {
+ StartElementEvent see = (StartElementEvent)event;
+ level++;
+ out.startElement(see.getElementName(), see.getTypeCode(), 0, ReceiverOptions.NAMESPACE_OK);
+ NamespaceBinding[] localNamespaces = see.getLocalNamespaces();
+ for (NamespaceBinding ns : localNamespaces) {
+ if (ns == null) {
+ break;
+ }
+ out.namespace(ns, 0);
+ }
+ if (see.hasAttributes()) {
+ for (Iterator ai=see.iterateAttributes(); ai.hasNext();) {
+ NodeInfo att = (NodeInfo)ai.next();
+ out.attribute(new NameOfNode(att), (SimpleType)att.getSchemaType(), att.getStringValueCS(), 0, 0);
+ }
+ }
+ out.startContent();
+ } else if (event instanceof EndElementEvent) {
+ level--;
+ out.endElement();
+ } else if (event instanceof StartDocumentEvent) {
+ if (level == 0) {
+ out.startDocument(0);
+ } else {
+ // output a zero-length text node to prevent whitespace being added between atomic values
+ out.characters("", 0, 0);
+ }
+ level++;
+ } else if (event instanceof EndDocumentEvent) {
+ level--;
+ if (level == 0) {
+ out.endDocument();
+ } else {
+ // output a zero-length text node to prevent whitespace being added between atomic values
+ out.characters("", 0, 0);
+ }
+ } else {
+ throw new AssertionError("Unknown event class " + event.getClass());
+ }
+
+ }
+ out.close();
+ }
+}
+
diff --git a/sf/saxon/evpull/EventMappingFunction.java b/sf/saxon/evpull/EventMappingFunction.java
new file mode 100644
index 0000000..f55d5f7
--- /dev/null
+++ b/sf/saxon/evpull/EventMappingFunction.java
@@ -0,0 +1,31 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* EventMappingFunction is an interface that must be satisfied by an object passed to an
+* EventMappingIterator. It represents an object which, given an Item, can return an
+* EventIterator that delivers a sequence of zero or more PullEvents.
+*/
+
+public interface EventMappingFunction {
+
+ /**
+ * Map one item to a sequence of pull events.
+ * @param item The item to be mapped.
+ * @return one of the following: (a) an EventIterator over the sequence of items that the supplied input
+ * item maps to, or (b) null if it maps to an empty sequence.
+ */
+
+ public EventIterator map(Item item) throws XPathException;
+
+}
+
diff --git a/sf/saxon/evpull/EventMappingIterator.java b/sf/saxon/evpull/EventMappingIterator.java
new file mode 100644
index 0000000..d162b97
--- /dev/null
+++ b/sf/saxon/evpull/EventMappingIterator.java
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * MappingIterator merges a sequence of sequences into a single sequence.
+ * It takes as inputs an iteration, and a mapping function to be
+ * applied to each Item returned by that iteration. The mapping function itself
+ * returns another iteration. The result is an iteration of iterators. To convert this
+ * int a single flat iterator over a uniform sequence of events, the result must be wrapped
+ * in an {@link EventStackIterator}<p>
+*/
+
+public final class EventMappingIterator implements EventIterator {
+
+ private SequenceIterator base;
+ private EventMappingFunction action;
+
+ /**
+ * Construct a MappingIterator that will apply a specified MappingFunction to
+ * each Item returned by the base iterator.
+ * @param base the base iterator
+ * @param action the mapping function to be applied
+ */
+
+ public EventMappingIterator(SequenceIterator base, EventMappingFunction action) {
+ this.base = base;
+ this.action = action;
+ }
+
+
+ /*@Nullable*/ public PullEvent next() throws XPathException {
+ Item nextSource = base.next();
+ return (nextSource == null ? null : action.map(nextSource));
+ }
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return false;
+ }
+}
+
diff --git a/sf/saxon/evpull/EventStackIterator.java b/sf/saxon/evpull/EventStackIterator.java
new file mode 100644
index 0000000..ef5add9
--- /dev/null
+++ b/sf/saxon/evpull/EventStackIterator.java
@@ -0,0 +1,86 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.trans.XPathException;
+
+import java.util.Stack;
+
+/**
+ * An EventStackIterator is an EventIterator that delivers a flat sequence of PullEvents
+ * containing no nested EventIterators
+ */
+public class EventStackIterator implements EventIterator {
+
+ private Stack<EventIterator> eventStack = new Stack<EventIterator>();
+
+ /**
+ * Factory method to create an iterator that flattens the sequence of PullEvents received
+ * from a base iterator, that is, it returns an EventIterator that will never return any
+ * nested iterators.
+ * @param base the base iterator. Any nested EventIterator returned by the base iterator
+ * will be flattened, recursively.
+ */
+
+ public static EventIterator flatten(EventIterator base) {
+ if (base.isFlatSequence()) {
+ return base;
+ }
+ return new EventStackIterator(base);
+ }
+
+ /**
+ * Create a EventStackIterator that flattens the sequence of PullEvents received
+ * from a base iterator
+ * @param base the base iterator. Any nested EventIterator returned by the base iterator
+ * will be flattened, recursively.
+ */
+
+ private EventStackIterator(EventIterator base) {
+ eventStack.push(base);
+ }
+
+ /**
+ * Get the next event in the sequence. This will never be an EventIterator.
+ *
+ * @return the next event, or null when the sequence is exhausted
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic evaluation error occurs
+ */
+
+ /*@Nullable*/ public PullEvent next() throws XPathException {
+ if (eventStack.isEmpty()) {
+ return null;
+ }
+ EventIterator iter = eventStack.peek();
+ PullEvent next = iter.next();
+ if (next == null) {
+ eventStack.pop();
+ return next();
+ } else if (next instanceof EventIterator) {
+ eventStack.push((EventIterator)next);
+ return next();
+ } else {
+ return next;
+ }
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+
+}
+
diff --git a/sf/saxon/evpull/EventToStaxBridge.java b/sf/saxon/evpull/EventToStaxBridge.java
new file mode 100644
index 0000000..564d652
--- /dev/null
+++ b/sf/saxon/evpull/EventToStaxBridge.java
@@ -0,0 +1,619 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pull.NamespaceContextImpl;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamReader;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+
+/**
+ * This class bridges EventIterator events to XMLStreamReader (Stax) events. That is, it acts
+ * as an XMLStreamReader, fetching the underlying data from an EventIterator.
+ * <p>
+ * An EventIterator may provide access to any XDM sequence, whereas an XMLStreamReader always
+ * reads a document. The conversion of a sequence to a document follows the rules for
+ * "normalizing" a sequence in the Serialization specification: for example, atomic values are
+ * converted into text nodes, with adjacent atomic values being space-separated.
+ */
+public class EventToStaxBridge implements XMLStreamReader {
+
+ private EventIterator provider;
+ private StartElementEvent startElementEvent;
+ private Item currentItem;
+ private Stack stack; // holds instances of StartElementEvent; needed because namespace information
+ // (though not attributes) must be available at EndElement time
+
+ //private NamePool namePool;
+ private boolean previousAtomic;
+ private FastStringBuffer currentTextNode = new FastStringBuffer(FastStringBuffer.SMALL);
+ private int currentStaxEvent = XMLStreamConstants.START_DOCUMENT;
+ private XPathException pendingException = null;
+
+ /**
+ * Create a EventToStaxBridge instance, which wraps a Saxon EventIterator as a Stax XMLStreamReader
+ * @param provider the Saxon EventIterator from which the events will be read. This must return
+ * a fully decomposed event stream, that is, document and element nodes must be presented as separate
+ * events for the start, content, and end.
+ * @param pipe the PipelineConfiguration
+ */
+
+ public EventToStaxBridge(EventIterator provider, PipelineConfiguration pipe) {
+ EventIterator flatIterator = EventStackIterator.flatten(provider);
+ if (flatIterator instanceof LocationProvider) {
+ pipe.setLocationProvider((LocationProvider)flatIterator);
+ }
+ this.provider = new NamespaceMaintainer(flatIterator);
+ this.stack = new Stack();
+ }
+
+ public int getAttributeCount() {
+ if (currentStaxEvent != START_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ return startElementEvent.getAttributeCount();
+ }
+
+ public boolean isAttributeSpecified(int i) {
+ if (currentStaxEvent != START_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ return true;
+ }
+
+ public QName getAttributeName(int i) {
+ if (currentStaxEvent != START_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ NodeInfo att = startElementEvent.getAttribute(i);
+ return new QName(att.getURI(), att.getLocalPart(), att.getPrefix());
+ }
+
+ public String getAttributeLocalName(int i) {
+ if (currentStaxEvent != START_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ return startElementEvent.getAttribute(i).getLocalPart();
+ }
+
+ public String getAttributeNamespace(int i) {
+ if (currentStaxEvent != START_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ return startElementEvent.getAttribute(i).getURI();
+ }
+
+ public String getAttributePrefix(int i) {
+ if (currentStaxEvent != START_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ return startElementEvent.getAttribute(i).getPrefix();
+ }
+
+ public String getAttributeType(int i) {
+ if (currentStaxEvent != START_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ int type = startElementEvent.getAttribute(i).getSchemaType().getFingerprint();
+ if (type == StandardNames.XS_ID) {
+ return "ID";
+ } else if (type == StandardNames.XS_IDREF) {
+ return "IDREF";
+ } else if (type == StandardNames.XS_IDREFS) {
+ return "IDREFS";
+ } else if (type == StandardNames.XS_NMTOKEN) {
+ return "NMTOKEN";
+ } else if (type == StandardNames.XS_NMTOKENS) {
+ return "NMTOKENS";
+ } else if (type == StandardNames.XS_ENTITY) {
+ return "ENTITY";
+ } else if (type == StandardNames.XS_ENTITIES) {
+ return "ENTITIES";
+ }
+ return "CDATA";
+ }
+
+ public String getAttributeValue(int i) {
+ if (currentStaxEvent != START_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ return startElementEvent.getAttribute(i).getStringValue();
+ }
+
+ /*@Nullable*/ public String getAttributeValue(String uri, String local) {
+ for (Iterator iter = startElementEvent.iterateAttributes(); iter.hasNext(); ) {
+ NodeInfo att = (NodeInfo)iter.next();
+ if (att.getURI().equals(uri) && att.getLocalPart().equals(local)) {
+ return att.getStringValue();
+ }
+ }
+ return null;
+ }
+
+ public int getEventType() {
+ return currentStaxEvent;
+ }
+
+ public int getNamespaceCount() {
+ if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ NamespaceBinding[] nscodes = startElementEvent.getLocalNamespaces();
+ for (int i=0; i<nscodes.length; i++) {
+ if (nscodes[i] == null) {
+ return i;
+ }
+ }
+ return nscodes.length;
+ }
+
+ public String getText() {
+ if (currentStaxEvent != CHARACTERS && currentStaxEvent != COMMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ if (previousAtomic) {
+ return currentTextNode.toString();
+ } else {
+ return currentItem.getStringValue();
+ }
+ }
+
+ public int getTextLength() {
+ if (currentStaxEvent != CHARACTERS && currentStaxEvent != COMMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ return getText().length();
+ }
+
+ public int getTextStart() {
+ if (currentStaxEvent != CHARACTERS && currentStaxEvent != COMMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ return 0;
+ }
+
+ public char[] getTextCharacters() {
+ if (currentStaxEvent != CHARACTERS && currentStaxEvent != COMMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ String stringValue = getText();
+ char[] chars = new char[stringValue.length()];
+ stringValue.getChars(0, chars.length, chars, 0);
+ return chars;
+ }
+
+
+ public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length)
+ throws XMLStreamException {
+ if (currentStaxEvent != CHARACTERS && currentStaxEvent != COMMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ if (targetStart < 0 || targetStart > target.length) {
+ throw new IndexOutOfBoundsException("targetStart");
+ }
+ if (length < 0 || targetStart + length > target.length) {
+ throw new IndexOutOfBoundsException("length");
+ }
+ String value = getText();
+ if (sourceStart >= value.length()) {
+ return 0;
+ }
+ int sourceEnd = sourceStart + length;
+ if (sourceEnd > value.length()) {
+ sourceEnd = value.length();
+ }
+ value.getChars(sourceStart, sourceEnd, target, targetStart);
+ return sourceEnd - sourceStart;
+ }
+
+// Diagnostic version of next() method
+// public int next() throws XMLStreamException {
+// System.err.println("calling next: ");
+// try {
+// int x = nextx();
+// System.err.println("next: " + x);
+// return x;
+// } catch (XMLStreamException e) {
+// System.err.println("end of stream ");
+// throw e;
+// } catch (IllegalStateException e) {
+// System.err.println("illegal end of stream ");
+// throw e;
+// }
+// }
+
+ public int next() throws XMLStreamException {
+ if (pendingException != null) {
+ throw new XMLStreamException(pendingException);
+ }
+ PullEvent p;
+ try {
+ p = provider.next();
+ } catch (XPathException e) {
+ throw new XMLStreamException(e);
+ }
+ if (p == null) {
+ // The spec is ambivalent here; it also says IllegalStateException is appropriate
+ throw new NoSuchElementException("end of stream");
+ }
+ startElementEvent = null;
+ if (p instanceof StartDocumentEvent) {
+ // STAX doesn't actually report START_DOCUMENT: it's the initial state before reading any events
+ currentStaxEvent = XMLStreamConstants.START_DOCUMENT;
+ return next();
+ } else if (p instanceof StartElementEvent) {
+ startElementEvent = (StartElementEvent)p;
+ currentStaxEvent = XMLStreamConstants.START_ELEMENT;
+ stack.push(p);
+ return currentStaxEvent;
+ } else if (p instanceof EndElementEvent) {
+ currentStaxEvent = XMLStreamConstants.END_ELEMENT;
+ startElementEvent = (StartElementEvent)stack.pop();
+ return currentStaxEvent;
+ } else if (p instanceof EndDocumentEvent) {
+ currentStaxEvent = XMLStreamConstants.END_DOCUMENT;
+ return currentStaxEvent;
+ } else if (p instanceof NodeInfo) {
+ currentItem = (NodeInfo)p;
+ switch (((NodeInfo)p).getNodeKind()) {
+ case Type.COMMENT:
+ currentStaxEvent = XMLStreamConstants.COMMENT;
+ return currentStaxEvent;
+ case Type.PROCESSING_INSTRUCTION:
+ currentStaxEvent = XMLStreamConstants.PROCESSING_INSTRUCTION;
+ return currentStaxEvent;
+ case Type.TEXT:
+ currentStaxEvent = XMLStreamConstants.CHARACTERS;
+ return currentStaxEvent;
+ case Type.ATTRIBUTE:
+ throw new XMLStreamException("Encountered top-level attribute in sequence");
+ default:
+ throw new AssertionError("Unexpected node kind (sequence not decomposed?)");
+ }
+ } else if (p instanceof AtomicValue) {
+ currentItem = (AtomicValue)p;
+ currentStaxEvent = XMLStreamConstants.CHARACTERS;
+ previousAtomic = true;
+ return currentStaxEvent;
+ } else if (p instanceof EventIterator) {
+ throw new AssertionError("EventToStaxBridge requires a flattened event sequence");
+ } else {
+ throw new AssertionError("Unhandled pull event: " + p.getClass().getName());
+ }
+ }
+
+ public int nextTag() throws XMLStreamException {
+ if (pendingException != null) {
+ throw new XMLStreamException(pendingException);
+ }
+ int eventType = next();
+ while ((eventType == XMLStreamConstants.CHARACTERS && isWhiteSpace()) // skip whitespace
+ || (eventType == XMLStreamConstants.CDATA && isWhiteSpace())
+ // skip whitespace
+ || eventType == XMLStreamConstants.SPACE
+ || eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
+ || eventType == XMLStreamConstants.COMMENT
+ ) {
+ eventType = next();
+ }
+ if (eventType != XMLStreamConstants.START_ELEMENT && eventType != XMLStreamConstants.END_ELEMENT) {
+ throw new XMLStreamException("expected start or end tag", getLocation());
+ }
+ return eventType;
+ }
+
+ public void close() throws XMLStreamException {
+ //System.err.println("close");
+ if (pendingException != null) {
+ throw new XMLStreamException(pendingException);
+ }
+ }
+
+ public boolean hasName() {
+ return currentStaxEvent == XMLStreamConstants.START_ELEMENT ||
+ currentStaxEvent == XMLStreamConstants.END_ELEMENT;
+ }
+
+ public boolean hasNext() throws XMLStreamException {
+ //System.err.println("hasNext()? " + (currentStaxEvent != XMLStreamConstants.END_DOCUMENT));
+ if (pendingException != null) {
+ throw new XMLStreamException(pendingException);
+ }
+ return currentStaxEvent != XMLStreamConstants.END_DOCUMENT;
+ }
+
+ public boolean hasText() {
+ return currentStaxEvent == XMLStreamConstants.CHARACTERS || currentStaxEvent == XMLStreamConstants.COMMENT;
+ }
+
+ public boolean isCharacters() {
+ return currentStaxEvent == XMLStreamConstants.CHARACTERS;
+ }
+
+ public boolean isEndElement() {
+ return currentStaxEvent == XMLStreamConstants.END_ELEMENT;
+ }
+
+ public boolean isStandalone() {
+ return false;
+ }
+
+ public boolean isStartElement() {
+ return currentStaxEvent == XMLStreamConstants.START_ELEMENT;
+ }
+
+ public boolean isWhiteSpace() {
+ return currentStaxEvent == XMLStreamConstants.CHARACTERS && Whitespace.isWhite(getText());
+ }
+
+ public boolean standaloneSet() {
+ return false;
+ }
+
+
+ public String getCharacterEncodingScheme() {
+ return null;
+ }
+
+ public String getElementText() throws XMLStreamException {
+ if (pendingException != null) {
+ throw new XMLStreamException(pendingException);
+ }
+ if (getEventType() != XMLStreamConstants.START_ELEMENT) {
+ throw new XMLStreamException("parser must be on START_ELEMENT to read next text", getLocation());
+ }
+ int eventType = next();
+ StringBuffer content = new StringBuffer();
+ while (eventType != XMLStreamConstants.END_ELEMENT) {
+ if (eventType == XMLStreamConstants.CHARACTERS
+ || eventType == XMLStreamConstants.CDATA
+ || eventType == XMLStreamConstants.SPACE
+ || eventType == XMLStreamConstants.ENTITY_REFERENCE) {
+ content.append(getText());
+ } else if (eventType == XMLStreamConstants.PROCESSING_INSTRUCTION
+ || eventType == XMLStreamConstants.COMMENT) {
+ // skipping
+ } else if (eventType == XMLStreamConstants.END_DOCUMENT) {
+ throw new XMLStreamException("unexpected end of document when reading element text content", getLocation());
+ } else if (eventType == XMLStreamConstants.START_ELEMENT) {
+ throw new XMLStreamException("element text content may not contain START_ELEMENT", getLocation());
+ } else {
+ throw new XMLStreamException("Unexpected event type " + eventType, getLocation());
+ }
+ eventType = next();
+ }
+ return content.toString();
+ }
+
+ public String getEncoding() {
+ return null;
+ }
+
+ public String getLocalName() {
+ if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ return startElementEvent.getElementName().getLocalPart();
+ }
+
+ public String getNamespaceURI() {
+ if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
+ return null;
+ }
+ return startElementEvent.getElementName().getURI();
+ }
+
+ public String getPIData() {
+ if (currentStaxEvent != XMLStreamConstants.PROCESSING_INSTRUCTION) {
+ throw new IllegalStateException("Not positioned at a processing instruction");
+ }
+ return currentItem.getStringValue();
+ }
+
+ public String getPITarget() {
+ if (currentStaxEvent != XMLStreamConstants.PROCESSING_INSTRUCTION) {
+ throw new IllegalStateException("Not positioned at a processing instruction");
+ }
+ return ((NodeInfo)currentItem).getLocalPart();
+ }
+
+ public String getPrefix() {
+ if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
+ return null;
+ }
+ return startElementEvent.getElementName().getPrefix();
+ }
+
+ public String getVersion() {
+ return "1.0";
+ }
+
+ public String getNamespacePrefix(int i) {
+ if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ NamespaceBinding nscode = startElementEvent.getLocalNamespaces()[i];
+ return nscode.getPrefix();
+ }
+
+ public String getNamespaceURI(int i) {
+ if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ NamespaceBinding nscode = startElementEvent.getLocalNamespaces()[i];
+ return nscode.getURI();
+ }
+
+ public NamespaceContext getNamespaceContext() {
+ return new NamespaceContextImpl((NamespaceResolver)provider);
+ }
+
+ public QName getName() {
+ if (currentStaxEvent != START_ELEMENT && currentStaxEvent != END_ELEMENT) {
+ throw new IllegalStateException(""+currentStaxEvent);
+ }
+ NodeName name = startElementEvent.getElementName();
+ return new QName(name.getURI(), name.getLocalPart(), name.getPrefix());
+ }
+
+ public Location getLocation() {
+ if (currentItem instanceof NodeInfo) {
+ final NodeInfo node = (NodeInfo)currentItem;
+ return new Location() {
+ public int getCharacterOffset() {
+ return -1;
+ }
+
+ public int getColumnNumber() {
+ return node.getColumnNumber();
+ }
+
+ public int getLineNumber() {
+ return node.getLineNumber();
+ }
+
+ public String getPublicId() {
+ return null;
+ }
+
+ public String getSystemId() {
+ return node.getSystemId();
+ }
+ };
+
+ } else if (startElementEvent != null) {
+ PipelineConfiguration pipe = startElementEvent.getPipelineConfiguration();
+ final LocationProvider provider = pipe.getLocationProvider();
+ final int locationId = startElementEvent.getLocationId();
+ if (provider != null) {
+ return new Location() {
+ public int getCharacterOffset() {
+ return -1;
+ }
+
+ public int getColumnNumber() {
+ return provider.getColumnNumber(locationId);
+ }
+
+ public int getLineNumber() {
+ return provider.getLineNumber(locationId);
+ }
+
+ public String getPublicId() {
+ return null;
+ }
+
+ public String getSystemId() {
+ return provider.getSystemId(locationId);
+ }
+ };
+ }
+ }
+ return DummyLocation.THE_INSTANCE;
+ }
+
+ public Object getProperty(String s) throws IllegalArgumentException {
+ return null;
+ }
+
+ public void require(int event, String uri, String local) throws XMLStreamException {
+ if (pendingException != null) {
+ throw new XMLStreamException(pendingException);
+ }
+ if (currentStaxEvent != event) {
+ throw new XMLStreamException("Required event type is " + event + ", actual event is " + currentStaxEvent);
+ }
+ if (uri != null && !uri.equals(getNamespaceURI())) {
+ throw new XMLStreamException("Required namespace is " + uri + ", actual is " + getNamespaceURI());
+ }
+ if (local != null && !local.equals(getLocalName())) {
+ throw new XMLStreamException("Required local name is " + local + ", actual is " + getLocalName());
+ }
+ }
+
+ public String getNamespaceURI(String prefix) {
+ if (prefix.equals("xmlns")) {
+ return NamespaceConstant.XMLNS;
+ }
+ return ((NamespaceResolver)provider).getURIForPrefix(prefix, true);
+ }
+
+ /**
+ * Get the underlying event stream
+ */
+
+ public EventIterator getProvider() {
+ return provider;
+ }
+
+ private static class DummyLocation implements Location{
+ public static final Location THE_INSTANCE = new DummyLocation();
+
+ private DummyLocation() {}
+
+ public int getCharacterOffset() {
+ return -1;
+ }
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+ public java.lang.String getPublicId() {
+ return null;
+ }
+
+ public java.lang.String getSystemId() {
+ return null;
+ }
+ }
+
+ /**
+ * Temporary test program
+ * @param args command line arguments. First argument is a file containing an XML document
+ * @throws Exception
+ */
+
+// public static void main(String[] args) throws Exception {
+// Configuration config = new Configuration();
+// DocumentInfo doc = config.buildDocument(new StreamSource(new File(args[0])));
+// PipelineConfiguration pipe = config.makePipelineConfiguration();
+// pipe.setHostLanguage(Configuration.XQUERY);
+// EventIterator ei = new Decomposer(doc, pipe);
+// XMLStreamReader sr = new EventToStaxBridge(ei, config.getNamePool());
+// StaxBridge bridge = new StaxBridge();
+// bridge.setXMLStreamReader(sr);
+//
+// bridge.setPipelineConfiguration(pipe);
+// Receiver out = config.getSerializerFactory().getReceiver(new StreamResult(System.out), pipe, new Properties());
+// PullPushCopier copier = new PullPushCopier(bridge, out);
+// copier.copy();
+// }
+
+}
+
diff --git a/sf/saxon/evpull/NamespaceMaintainer.java b/sf/saxon/evpull/NamespaceMaintainer.java
new file mode 100644
index 0000000..19f77a7
--- /dev/null
+++ b/sf/saxon/evpull/NamespaceMaintainer.java
@@ -0,0 +1,200 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * NamespaceMaintainer is an EventIterator responsible for maintaining namespace context in an
+ * event stream. It allows the current namespace context to be determined at any time while
+ * processing the stream of events.
+ *
+ * <p>Note that this class merely provides the service of keeping track of which namespaces are
+ * currently in scope. It does not attempt to remove duplicate namespace declarations, and it does
+ * not perform namespace fixup.</p>
+ */
+
+public class NamespaceMaintainer implements EventIterator, NamespaceResolver {
+
+ private EventIterator base;
+
+ // We keep track of namespaces to avoid outputting duplicate declarations. The namespaces
+ // vector holds a list of all namespaces currently declared (organised as integer namespace codes).
+ // The countStack contains an entry for each element currently open; the
+ // value on the countStack is an Integer giving the number of namespaces added to the main
+ // namespace stack by that element.
+
+ private NamespaceBinding[] allNamespaces = new NamespaceBinding[50]; // all namespace codes currently declared
+ private int allNamespacesSize = 0; // all namespaces currently declared
+ private int[] namespaceCountStack = new int[50]; // one entry per started element, holding the number
+ private int depth = 0; // current depth of element nesting
+
+ /**
+ * Create a namespace context for a pull-events pipeline
+ * @param base the previous stage in the pipeline, from which events are read
+ */
+
+ public NamespaceMaintainer(EventIterator base) {
+ this.base = EventStackIterator.flatten(base);
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+
+ /**
+ * Get the next event in the sequence
+ *
+ * @return the next event, or null when the sequence is exhausted. Note that since an EventIterator is
+ * itself a PullEvent, this method may return a nested iterator.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic evaluation error occurs
+ */
+
+ public PullEvent next() throws XPathException {
+ PullEvent event = base.next();
+ if (event instanceof StartElementEvent) {
+ startElement((StartElementEvent)event);
+ } else if (event instanceof EndElementEvent) {
+ endElement();
+ }
+ return event;
+ }
+
+ private void startElement(StartElementEvent event) throws XPathException {
+
+ // Record the current height of the namespace list so it can be reset at endElement time
+
+ NamespaceBinding[] declaredNamespaces = event.getLocalNamespaces();
+ int numberOfDeclaredNamespaces = declaredNamespaces.length;
+ for (int i=0; i<declaredNamespaces.length; i++) {
+ if (declaredNamespaces[i] == null) {
+ numberOfDeclaredNamespaces = i;
+ break;
+ }
+ }
+
+ if (depth >= namespaceCountStack.length) {
+ int[] newstack = new int[depth * 2];
+ System.arraycopy(namespaceCountStack, 0, newstack, 0, depth);
+ namespaceCountStack = newstack;
+ }
+ namespaceCountStack[depth++] = numberOfDeclaredNamespaces;
+
+ // expand the stack if necessary
+ while (allNamespacesSize + numberOfDeclaredNamespaces >= allNamespaces.length) {
+ NamespaceBinding[] newlist = new NamespaceBinding[allNamespacesSize * 2];
+ System.arraycopy(allNamespaces, 0, newlist, 0, allNamespacesSize);
+ allNamespaces = newlist;
+ }
+
+ for (int i=0; i<declaredNamespaces.length; i++) {
+ if (declaredNamespaces[i] == null) {
+ break;
+ }
+ allNamespaces[allNamespacesSize++] = declaredNamespaces[i];
+ }
+
+ // test code
+// System.err.println("==============");
+// System.err.println("ELEMENT " + namePool.getDisplayName(event.getNameCode()));
+// for (Iterator iter = iteratePrefixes(); iter.hasNext();) {
+// String prefix = (String)iter.next();
+// String uri = (getURIForPrefix(prefix, true));
+// System.err.println(" '" + prefix + "'='" + uri + "'");
+// }
+// System.err.println("==============");
+
+ }
+
+ private void endElement() {
+ allNamespacesSize -= namespaceCountStack[--depth];
+ }
+
+
+ /**
+ * Get the URI code corresponding to a given prefix code, by searching the
+ * in-scope namespaces. This is a service provided to subclasses.
+ *
+ * @param prefixCode the 16-bit prefix code required
+ * @return the 16-bit URI code, or -1 if the prefix is not found
+ */
+
+// protected short getURICode(short prefixCode) {
+// for (int i = allNamespacesSize - 1; i >= 0; i--) {
+// if ((allNamespaces[i].getPrefix().equals(== (prefixCode)) {
+// return (short) (allNamespaces[i] & 0xffff);
+// }
+// }
+// if (prefixCode == 0) {
+// return 0; // by default, no prefix means no namespace URI
+// } else {
+// return -1;
+// }
+// }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ *
+ * @param prefix the namespace prefix
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is ""
+ * @return the uri for the namespace, or null if the prefix is not in scope
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(String prefix, boolean useDefault) {
+ if ((prefix.length()==0) && !useDefault) {
+ return NamespaceConstant.NULL;
+ } else if ("xml".equals(prefix)) {
+ return NamespaceConstant.XML;
+ } else {
+ for (int i = allNamespacesSize - 1; i >= 0; i--) {
+ if ((allNamespaces[i].getPrefix().equals(prefix))) {
+ return allNamespaces[i].getURI();
+ }
+ }
+ return (prefix.length()==0 ? NamespaceConstant.NULL : null);
+ }
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ List<String> prefixes = new ArrayList<String>(allNamespacesSize);
+ for (int i = allNamespacesSize - 1; i >= 0; i--) {
+ String prefix = allNamespaces[i].getPrefix();
+ if (!prefixes.contains(prefix)) {
+ prefixes.add(prefix);
+ }
+ }
+ prefixes.add("xml");
+ return prefixes.iterator();
+ }
+
+
+}
+
diff --git a/sf/saxon/evpull/PullEvent.java b/sf/saxon/evpull/PullEvent.java
new file mode 100644
index 0000000..160cff3
--- /dev/null
+++ b/sf/saxon/evpull/PullEvent.java
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+/**
+ * A PullEvent is one of the following:
+ *
+ * <ul>
+ * <li>An item (that is, a node or an atomic value)</li>
+ * <li>A startElement, endElement, startDocument, or endDocument event</li>
+ * <li>An EventIterator, representing a sequence of PullEvents</li>
+ * </ul>
+ */
+
+public interface PullEvent {
+}
+
diff --git a/sf/saxon/evpull/PullEventSource.java b/sf/saxon/evpull/PullEventSource.java
new file mode 100644
index 0000000..522c492
--- /dev/null
+++ b/sf/saxon/evpull/PullEventSource.java
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import javax.xml.transform.Source;
+
+/**
+ * A PullSource is a JAXP Source that encapsulates a PullProvider - that is, an object
+ * that supplies an XML document as a sequence of events that are read under the control
+ * of the recipient. Note that although PullSource implements the JAXP Source interface,
+ * it is not necessarily acceptable to every JAXP implementation that accepts a Source
+ * as input: Source is essentially a marker interface and users of Source objects need
+ * to understand the individual implementation.
+ */
+
+public class PullEventSource implements Source {
+
+ private String systemId;
+ private EventIterator provider;
+
+ /**
+ * Create a PullSource based on a supplied EventIterator
+ * @param provider the underlying EventIterator
+ */
+
+ public PullEventSource(EventIterator provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Get the EventIterator
+ * @return the underlying EventIterator
+ */
+
+ public EventIterator getEventIterator() {
+ return provider;
+ }
+
+ /**
+ * Set the system identifier for this Source.
+ * <p/>
+ * <p>The system identifier is optional if the source does not
+ * get its data from a URL, but it may still be useful to provide one.
+ * The application can use a system identifier, for example, to resolve
+ * relative URIs and to include in error messages and warnings.</p>
+ *
+ * @param systemId The system identifier as a URL string.
+ */
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the system identifier that was set with setSystemId.
+ *
+ * @return The system identifier that was set with setSystemId, or null
+ * if setSystemId was not called.
+ */
+ public String getSystemId() {
+ return systemId;
+ }
+}
+
diff --git a/sf/saxon/evpull/PullEventTracer.java b/sf/saxon/evpull/PullEventTracer.java
new file mode 100644
index 0000000..6aaac1d
--- /dev/null
+++ b/sf/saxon/evpull/PullEventTracer.java
@@ -0,0 +1,133 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+
+import javax.xml.transform.stream.StreamSource;
+import java.io.File;
+import java.io.PrintStream;
+
+/**
+ * Diagnostic class to display the sequence of events reported by an EventIterator
+ */
+public class PullEventTracer implements EventIterator {
+
+ private EventIterator base;
+ private String label = ("PET" + hashCode()).substring(0, 8) + ": ";
+ private PrintStream out;
+ //@SuppressWarnings({"FieldCanBeLocal"})
+ private NamePool pool;
+
+ /**
+ * Create a tracer for pull events
+ * @param base the event iterator whose events are to be traced
+ * @param config the Saxon configuration
+ */
+
+ public PullEventTracer(EventIterator base, Configuration config) {
+ this.base = base;
+ pool = config.getNamePool();
+ out = config.getStandardErrorOutput();
+ }
+
+
+ /**
+ * Get the next event in the sequence
+ *
+ * @return the next event, or null when the sequence is exhausted. Note that since an EventIterator is
+ * itself a PullEvent, this method may return a nested iterator.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic evaluation error occurs
+ */
+
+ /*@Nullable*/ public PullEvent next() throws XPathException {
+ PullEvent pe = base.next();
+ if (pe == null) {
+ return null;
+ }
+ if (pe instanceof StartDocumentEvent) {
+ out.println(label + "StartDocument");
+ label = " " + label;
+ } else if (pe instanceof StartElementEvent) {
+ out.println(label + "StartElement " + ((StartElementEvent)pe).getElementName().getDisplayName());
+ label = " " + label;
+ } else if (pe instanceof EndDocumentEvent) {
+ label = label.substring(2);
+ out.println(label + "EndDocument");
+ } else if (pe instanceof EndElementEvent) {
+ label = label.substring(2);
+ out.println(label + "EndElement");
+ } else if (pe instanceof NodeInfo) {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ fsb.append(label);
+ int kind = ((NodeInfo)pe).getNodeKind();
+ fsb.append(NodeKindTest.toString(kind));
+ if (kind == Type.ELEMENT || kind == Type.ATTRIBUTE) {
+ fsb.append(' ');
+ fsb.append(((NodeInfo)pe).getDisplayName());
+ }
+ fsb.append(" \"");
+ fsb.append(((NodeInfo)pe).getStringValueCS());
+ fsb.append('"');
+ out.println(fsb.toString());
+ } else if (pe instanceof AtomicValue) {
+ out.println(label + Type.displayTypeName((AtomicValue)pe) + ' ' + pe);
+ } else if (pe instanceof EventIterator) {
+ out.println(label + "** NESTED ITERATOR **");
+ } else {
+ out.println(label + pe.getClass().getName());
+ }
+
+ return pe;
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return base.isFlatSequence();
+ }
+
+ /**
+ * Main method for testing only
+ * @param args not used
+ * @throws Exception
+ */
+
+ public static void main(String[] args) throws Exception {
+ Configuration config = new Configuration();
+ DocumentInfo doc = config.buildDocument(new StreamSource(new File("c:/MyJava/samples/data/books.xml")));
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ pipe.setHostLanguage(Configuration.XQUERY);
+ EventIterator e = new Decomposer(new SingletonEventIterator(doc), pipe);
+ e = EventStackIterator.flatten(e);
+ e = new PullEventTracer(e, config);
+ while (true) {
+ PullEvent pe = e.next();
+ if (pe == null) {
+ break;
+ }
+ }
+ }
+}
+
diff --git a/sf/saxon/evpull/SequenceComposer.java b/sf/saxon/evpull/SequenceComposer.java
new file mode 100644
index 0000000..62e62c1
--- /dev/null
+++ b/sf/saxon/evpull/SequenceComposer.java
@@ -0,0 +1,242 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.TreeReceiver;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.query.QueryResult;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.TinyBuilder;
+
+import javax.xml.transform.stream.StreamSource;
+import java.io.File;
+
+/**
+ * This class takes a sequence of pull events and composes them into a sequence of items. This involves building
+ * any element or document nodes that are presented in decomposed form.
+ *
+ * <p>Note: this SequenceIterator does not implement the <code>getAnother()</code> method, which limits its use,
+ * since <code>getAnother()</code> is needed to support the XPath <code>last()</code> function.
+ */
+
+public class SequenceComposer implements SequenceIterator {
+
+ private EventIterator base;
+ private int position = 0;
+ /*@Nullable*/ private Item current = null;
+ private PipelineConfiguration pipe;
+
+ /**
+ * Create a sequence composer
+ * @param iter the underlying event iterator
+ * @param pipe the pipeline configuration
+ */
+
+ public SequenceComposer(EventIterator iter, PipelineConfiguration pipe) {
+ base = EventStackIterator.flatten(iter);
+ this.pipe = pipe;
+ }
+
+ /**
+ * Get the next item in the sequence. This method changes the state of the
+ * iterator, in particular it affects the result of subsequent calls of
+ * position() and current().
+ *
+ * @return the next item, or null if there are no more items. Once a call
+ * on next() has returned null, no further calls should be made. The preferred
+ * action for an iterator if subsequent calls on next() are made is to return
+ * null again, and all implementations within Saxon follow this rule.
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error occurs retrieving the next item
+ * @since 8.4
+ */
+
+ public Item next() throws XPathException {
+ PullEvent pe = base.next();
+ if (pe == null) {
+ position = -1;
+ current = null;
+ return null;
+ }
+ if (pe instanceof Item) {
+ current = (Item)pe;
+ position++;
+ return current;
+ } else if (pe instanceof StartDocumentEvent || pe instanceof StartElementEvent) {
+ SubtreeIterator sub = new SubtreeIterator(base, pe);
+ TinyBuilder builder = new TinyBuilder(pipe);
+ TreeReceiver receiver = new TreeReceiver(builder);
+ EventIteratorToReceiver.copy(sub, receiver);
+ current = builder.getCurrentRoot();
+ position++;
+ return current;
+ } else {
+ throw new IllegalStateException(pe.getClass().getName());
+ }
+ }
+
+
+ /**
+ * Get the current value in the sequence (the one returned by the
+ * most recent call on next()). This will be null before the first
+ * call of next(). This method does not change the state of the iterator.
+ *
+ * @return the current item, the one most recently returned by a call on
+ * next(). Returns null if next() has not been called, or if the end
+ * of the sequence has been reached.
+ * @since 8.4
+ */
+
+ public Item current() {
+ return current;
+ }
+
+ public void close() {
+
+ }
+
+ /**
+ * Get another SequenceIterator that iterates over the same items as the original,
+ * but which is repositioned at the start of the sequence.
+ * <p/>
+ * This method allows access to all the items in the sequence without disturbing the
+ * current position of the iterator. Internally, its main use is in evaluating the last()
+ * function.
+ * <p/>
+ * This method does not change the state of the iterator.
+ *
+ * @return a SequenceIterator that iterates over the same items,
+ * positioned before the first item
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error occurs
+ * @since 8.4
+ */
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ throw new UnsupportedOperationException("getAnother");
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ * @since 8.6
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+ /**
+ * Get the current position. This will usually be zero before the first call
+ * on next(), otherwise it will be the number of times that next() has
+ * been called. Once next() has returned null, the preferred action is
+ * for subsequent calls on position() to return -1, but not all existing
+ * implementations follow this practice. (In particular, the EmptyIterator
+ * is stateless, and always returns 0 as the value of position(), whether
+ * or not next() has been called.)
+ * <p/>
+ * This method does not change the state of the iterator.
+ *
+ * @return the current position, the position of the item returned by the
+ * most recent call of next(). This is 1 after next() has been successfully
+ * called once, 2 after it has been called twice, and so on. If next() has
+ * never been called, the method returns zero. If the end of the sequence
+ * has been reached, the value returned will always be <= 0; the preferred
+ * value is -1.
+ * @since 8.4
+ */
+
+ public int position() {
+ return position;
+ }
+
+ private static class SubtreeIterator implements EventIterator {
+
+ private int level = 0;
+ private EventIterator base;
+ private PullEvent first;
+
+ public SubtreeIterator(EventIterator base, PullEvent first) {
+ this.base = base;
+ this.first = first;
+ }
+
+ /**
+ * Get the next event in the sequence
+ *
+ * @return the next event, or null when the sequence is exhausted. Note that since an EventIterator is
+ * itself a PullEvent, this method may return a nested iterator.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic evaluation error occurs
+ */
+
+ public PullEvent next() throws XPathException {
+ if (first != null) {
+ PullEvent pe = first;
+ first = null;
+ return pe;
+ }
+ if (level < 0) {
+ return null;
+ }
+ PullEvent pe = base.next();
+ if (pe instanceof StartElementEvent || pe instanceof StartDocumentEvent) {
+ level++;
+ } else if (pe instanceof EndElementEvent || pe instanceof EndDocumentEvent) {
+ level--;
+ }
+ return pe;
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return base.isFlatSequence();
+ }
+ }
+
+ /**
+ * Main method for testing only
+ * @param args not used
+ * @throws Exception
+ */
+
+ public static void main(String[] args) throws Exception {
+ Configuration config = new Configuration();
+ DocumentInfo doc = config.buildDocument(new StreamSource(new File("c:/MyJava/samples/data/books.xml")));
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ pipe.setHostLanguage(Configuration.XQUERY);
+ EventIterator e = new Decomposer(new SingletonEventIterator(doc), pipe);
+ SequenceIterator iter = new SequenceComposer(e, pipe);
+ while (true) {
+ NodeInfo item = (NodeInfo)iter.next();
+ if (item == null) {
+ break;
+ }
+ System.out.println(QueryResult.serialize(item));
+ }
+ }
+}
+
diff --git a/sf/saxon/evpull/SingletonEventIterator.java b/sf/saxon/evpull/SingletonEventIterator.java
new file mode 100644
index 0000000..dfa1ab0
--- /dev/null
+++ b/sf/saxon/evpull/SingletonEventIterator.java
@@ -0,0 +1,56 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class represents an EventIterator over a sequence containing a single pull event.
+ */
+public class SingletonEventIterator implements EventIterator {
+
+ /*@Nullable*/ private PullEvent event;
+
+ /**
+ * Create an iterator over a sequence containing a single pull event
+ * @param event the single event. This must not be an EventIterator
+ */
+
+ public SingletonEventIterator(PullEvent event) {
+ this.event = event;
+ if (event instanceof EventIterator) {
+ throw new IllegalArgumentException("event");
+ }
+ }
+
+ /**
+ * Get the next event in the sequence
+ *
+ * @return the next event, or null when the sequence is exhausted
+ * @throws XPathException if a dynamic evaluation error occurs
+ */
+
+ public PullEvent next() throws XPathException {
+ PullEvent next = event;
+ event = null;
+ return next;
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/evpull/StartDocumentEvent.java b/sf/saxon/evpull/StartDocumentEvent.java
new file mode 100644
index 0000000..9122595
--- /dev/null
+++ b/sf/saxon/evpull/StartDocumentEvent.java
@@ -0,0 +1,24 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+/**
+ * A PullEvent representing the start of a document node
+ */
+public class StartDocumentEvent implements PullEvent {
+
+ private final static StartDocumentEvent THE_INSTANCE = new StartDocumentEvent();
+
+ public static StartDocumentEvent getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private StartDocumentEvent() {
+ }
+
+}
diff --git a/sf/saxon/evpull/StartElementEvent.java b/sf/saxon/evpull/StartElementEvent.java
new file mode 100644
index 0000000..d9602c0
--- /dev/null
+++ b/sf/saxon/evpull/StartElementEvent.java
@@ -0,0 +1,339 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This is a PullEvent representing the start of an element node. It contains (or potentially contains) all the
+ * namespace declarations and attributes associated with the element.
+ */
+public class StartElementEvent implements PullEvent {
+
+ PipelineConfiguration pipe;
+ private NodeName elementName;
+ private SchemaType typeCode;
+ /*@Nullable*/ private NamespaceBinding[] localNamespaces;
+ private List<NodeInfo> attributes;
+ private int locationId = -1;
+
+ /**
+ * Create a Start Element Event
+ * @param pipe the pipeline configuration
+ */
+
+ public StartElementEvent(PipelineConfiguration pipe) {
+ this.pipe = pipe;
+ }
+
+ /**
+ * Set the nameCode of this element
+ * @param elementName the namecode of the element (its name as identified in the NamePool)
+ */
+
+ public void setElementName(NodeName elementName) {
+ this.elementName = elementName;
+ }
+
+ /**
+ * Get the nameCode of this element
+ * @return the nameCode representing the element's name
+ */
+
+ public NodeName getElementName() {
+ return elementName;
+ }
+
+ /**
+ * Set the typeCode of this element
+ * @param typeCode the element's type annotation
+ */
+
+ public void setTypeCode(SchemaType typeCode) {
+ this.typeCode = typeCode;
+ }
+
+ /**
+ * Get the typeCode of this element
+ * @return the element's type annotation
+ */
+
+ public SchemaType getTypeCode() {
+ return typeCode;
+ }
+
+ /**
+ * Set the namespaces that are locally declared (or undeclared) on this element
+ * @param nscodes integer array of namespace codes
+ */
+
+ public void setLocalNamespaces(NamespaceBinding[] nscodes) {
+ localNamespaces = nscodes;
+ }
+
+ /**
+ * Add a namespace code representing a locally declared namespace
+ * @param nscode a namespace code
+ * @throws XPathException
+ */
+
+ public void addNamespace(NamespaceBinding nscode) throws XPathException {
+ if (localNamespaces == null) {
+ localNamespaces = new NamespaceBinding[]{nscode, null, null, null};
+ }
+ for (int n=0; n<localNamespaces.length; n++) {
+ NamespaceBinding nn = localNamespaces[n];
+ if (nn == null) {
+ localNamespaces[n] = nscode;
+ if (n < localNamespaces.length - 1) {
+ localNamespaces[n+1] = null;
+ }
+ return;
+ }
+ if (nn.equals(nscode)) {
+ return;
+ }
+ if ((nn.getPrefix().equals(nscode.getPrefix()))) {
+ String prefix = nscode.getPrefix();
+ String uri1 = nn.getURI();
+ String uri2 = nscode.getURI();
+ XPathException err = new XPathException(
+ "Cannot create two namespace nodes with the same prefix mapped to different URIs (prefix=" +
+ (prefix.length() == 0 ? "\"\"" : prefix) + ", URI=" +
+ (uri1.length() == 0 ? "\"\"" : uri1) + ", URI=" +
+ (uri2.length() == 0 ? "\"\"" : uri2) + ")");
+ err.setErrorCode("XTDE0430");
+ throw err;
+ }
+ }
+ NamespaceBinding[] n2 = new NamespaceBinding[localNamespaces.length * 2 + 2];
+ System.arraycopy(localNamespaces, 0, n2, 0, localNamespaces.length);
+ n2[localNamespaces.length] = nscode;
+ n2[localNamespaces.length+1] = null;
+ localNamespaces = n2;
+ }
+
+ /**
+ * Get the namespaces locally declared on this element
+ * @return an array of namespace codes
+ */
+
+ public NamespaceBinding[] getLocalNamespaces() {
+ if (localNamespaces == null) {
+ return NamespaceBinding.EMPTY_ARRAY;
+ }
+ return localNamespaces;
+ }
+
+ /**
+ * Add an attribute to the element node
+ * @param att the attribute to be added
+ * @throws XPathException in the event of a dynamic error, for example a duplicate attribute in XQuery
+ */
+
+ public void addAttribute(NodeInfo att) throws XPathException {
+ if (attributes == null) {
+ attributes = new ArrayList<NodeInfo>(4);
+ }
+ int fp = att.getFingerprint();
+ for (int a=0; a<attributes.size(); a++) {
+ int fp2 = attributes.get(a).getFingerprint();
+ if (fp == fp2) {
+ if (pipe.getHostLanguage() == Configuration.XQUERY) {
+ // In XQuery, duplicate attributes are an error
+ XPathException err = new XPathException(
+ "Cannot create an element having two attributes with the same name: " +
+ Err.wrap(att.getDisplayName(), Err.ATTRIBUTE));
+ err.setErrorCode("XQDY0025");
+ if (locationId != -1) {
+ err.setLocator(new ExpressionLocation(pipe.getLocationProvider(), locationId));
+ }
+ throw err;
+ } else {
+ // In XSLT, the last attribute to be added wins
+ attributes.set(a, att);
+ return;
+ }
+ }
+ }
+ attributes.add(att);
+ }
+
+ /**
+ * Ask whether the element has any attributes
+ * @return true if the element has one or more attributes
+ */
+
+ public boolean hasAttributes() {
+ return attributes != null && !attributes.isEmpty();
+ }
+
+ /**
+ * Ask how may attributes the element has
+ * @return the number of attributes that the element has
+ */
+
+ public int getAttributeCount() {
+ return attributes == null ? 0 : attributes.size();
+ }
+
+ /**
+ * Get an iterator over the attributes of this element
+ * @return an iterator which delivers NodeInfo objects representing the attributes of this element
+ */
+
+ public Iterator iterateAttributes() {
+ if (attributes == null) {
+ return Collections.EMPTY_LIST.iterator();
+ } else {
+ return attributes.iterator();
+ }
+ }
+
+ /**
+ * Get the n'th attribute if there is one
+ * @param index the index of the attributes, starting from zero
+ * @return a NodeInfo representing the attribute, or null if there is no such attribute
+ */
+
+ public NodeInfo getAttribute(int index) {
+ if (attributes == null) {
+ return null;
+ } else {
+ return attributes.get(index);
+ }
+ }
+
+ /**
+ * Perform namespace fixup. This is done after all the attributes and explicit namespaces have been added.
+ * Namespace fixup ensures that a namespace declaration is present for the element name and for every
+ * attribute name, and that the prefixes of the element and each attribute are consistent with the declared
+ * namespaces, changing any prefixes in the event of a conflict.
+ */
+
+ public void namespaceFixup() {
+ elementName = fixup(elementName, 0);
+ if (attributes != null) {
+ for (int a=0; a<attributes.size(); a++) {
+ NodeInfo oldAtt = attributes.get(a);
+ NodeName oldCode = new NameOfNode(oldAtt);
+ NodeName newCode = fixup(new NameOfNode(oldAtt), a);
+ if (oldCode != newCode) {
+ Orphan att = new Orphan(oldAtt.getConfiguration());
+ att.setNodeKind(Type.ATTRIBUTE);
+ att.setNodeName(newCode);
+ att.setStringValue(oldAtt.getStringValue());
+ att.setTypeAnnotation(oldAtt.getSchemaType());
+ att.setSystemId(oldAtt.getSystemId());
+ attributes.set(a, att);
+ }
+ }
+ }
+ }
+
+ private NodeName fixup(NodeName nameCode, int seq) {
+ String prefix = nameCode.getPrefix();
+ String uri = nameCode.getURI();
+ NamespaceBinding nsBinding = nameCode.getNamespaceBinding();
+ if (prefix.length()==0 && uri.length()==0) {
+ return nameCode;
+ }
+ if (localNamespaces != null) {
+ for (int n=0; n<localNamespaces.length; n++) {
+ NamespaceBinding nn = localNamespaces[n];
+ if (nn == null) {
+ break;
+ }
+ if ((prefix.equals(nn.getPrefix()))) {
+ if (uri.equals(nn.getURI())) {
+ return nameCode;
+ }
+ // Same as an existing prefix, but mapped to a different URI: we need to choose a new prefix
+ String local = nameCode.getLocalPart();
+ String prefix2 = prefix + "_" + seq;
+ FingerprintedQName newCode = new FingerprintedQName(prefix2, uri, local);
+ return fixup(newCode, seq);
+ }
+ }
+ }
+ // Namespace declaration not found: we need to add it
+ try {
+ addNamespace(nsBinding);
+ } catch (XPathException err) {
+ throw new AssertionError(err);
+ }
+ return nameCode;
+ }
+
+ /**
+ * Strip type annotations from the element and its attributes
+ */
+
+ public void stripTypeAnnotations() {
+ setTypeCode(Untyped.getInstance());
+ if (attributes != null) {
+ for (int i=0; i<attributes.size(); i++) {
+ NodeInfo att = attributes.get(i);
+ if (!BuiltInAtomicType.UNTYPED_ATOMIC.equals(att.getSchemaType())) {
+ Orphan o = new Orphan(att.getConfiguration());
+ o.setNodeKind(Type.ATTRIBUTE);
+ o.setNodeName(new NameOfNode(att));
+ o.setStringValue(att.getStringValue());
+ o.setSystemId(att.getSystemId());
+ o.setTypeAnnotation(BuiltInAtomicType.UNTYPED_ATOMIC);
+ attributes.set(i, o);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the PipelineConfiguration
+ * @return the PipelineConfiguration
+ */
+
+ public PipelineConfiguration getPipelineConfiguration() {
+ return pipe;
+ }
+
+ /**
+ * Set the location associated with the event
+ * @param locationId a location identifier, to be interpreted by the locationProvider
+ * associated with the PipelineConfiguration
+ */
+
+ public void setLocationId(int locationId) {
+ this.locationId = locationId;
+ }
+
+ /**
+ * Get the location associated with the event
+ * @return a location identifier, to be interpreted by the locationProvider
+ * associated with the PipelineConfiguration, or -1 if none is available
+ */
+
+ public int getLocationId() {
+ return locationId;
+ }
+}
+
diff --git a/sf/saxon/evpull/StaxToEventBridge.java b/sf/saxon/evpull/StaxToEventBridge.java
new file mode 100644
index 0000000..b808526
--- /dev/null
+++ b/sf/saxon/evpull/StaxToEventBridge.java
@@ -0,0 +1,540 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.NamespaceReducer;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.SaxonLocator;
+import net.sf.saxon.event.SourceLocationProvider;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.om.FingerprintedQName;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NoNamespaceName;
+import net.sf.saxon.pull.UnparsedEntity;
+import net.sf.saxon.serialize.XMLEmitter;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.stream.*;
+import javax.xml.stream.events.EntityDeclaration;
+import javax.xml.transform.TransformerException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * This class implements the Saxon EventIterator API on top of a standard StAX parser
+ * (or any other StAX XMLStreamReader implementation)
+ */
+
+public class StaxToEventBridge implements EventIterator, SaxonLocator, SourceLocationProvider {
+
+ private Configuration config;
+ private XMLStreamReader reader;
+ private PipelineConfiguration pipe;
+ /*@Nullable*/ private List unparsedEntities = null;
+ PullEvent currentEvent = null;
+ int depth = 0;
+ boolean ignoreIgnorable = false;
+
+ /**
+ * Create a new instance of the class
+ */
+
+ public StaxToEventBridge() {
+
+ }
+
+ /**
+ * Supply an input stream containing XML to be parsed. A StAX parser is created using
+ * the JAXP XMLInputFactory.
+ * @param systemId The Base URI of the input document
+ * @param inputStream the stream containing the XML to be parsed
+ * @throws net.sf.saxon.trans.XPathException if an error occurs creating the StAX parser
+ */
+
+ public void setInputStream(String systemId, InputStream inputStream) throws XPathException {
+ try {
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+ //XMLInputFactory factory = new WstxInputFactory();
+ factory.setXMLReporter(new StaxErrorReporter());
+ reader = factory.createXMLStreamReader(systemId, inputStream);
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ /**
+ * Supply an XMLStreamReader: the events reported by this XMLStreamReader will be translated
+ * into EventIterator events
+ * @param reader the supplier of XML events, typically an XML parser
+ */
+
+ public void setXMLStreamReader(XMLStreamReader reader) {
+ this.reader = reader;
+ }
+
+ /**
+ * Set configuration information. This must only be called before any events
+ * have been read.
+ * @param pipe the pipeline configuration
+ */
+
+ public void setPipelineConfiguration(PipelineConfiguration pipe) {
+ this.pipe = new PipelineConfiguration(pipe);
+ this.pipe.setLocationProvider(this);
+ config = pipe.getConfiguration();
+ ignoreIgnorable = config.getStripsWhiteSpace() != Whitespace.NONE;
+ }
+
+ /**
+ * Get configuration information.
+ * @return the pipeline configuration
+ */
+
+ public PipelineConfiguration getPipelineConfiguration() {
+ return pipe;
+ }
+
+ /**
+ * Get the XMLStreamReader used by this StaxBridge. This is available only after
+ * setInputStream() or setXMLStreamReader() has been called
+ * @return the instance of XMLStreamReader allocated when setInputStream() was called,
+ * or the instance supplied directly to setXMLStreamReader()
+ */
+
+ public XMLStreamReader getXMLStreamReader() {
+ return reader;
+ }
+
+ /**
+ * Get the name pool
+ * @return the name pool
+ */
+
+ public NamePool getNamePool() {
+ return pipe.getConfiguration().getNamePool();
+ }
+
+ /**
+ * Get the next event
+ * @return the next event; or null to indicate the end of the event stream
+ */
+
+ public PullEvent next() throws XPathException {
+ if (currentEvent == null) {
+ // StAX isn't reporting START_DOCUMENT so we supply it ourselves
+ currentEvent = StartDocumentEvent.getInstance();
+ return currentEvent;
+ }
+ if (currentEvent instanceof EndDocumentEvent) {
+ try {
+ reader.close();
+ } catch (XMLStreamException e) {
+ //
+ }
+ return null;
+ }
+ try {
+ if (reader.hasNext()) {
+ int event = reader.next();
+ //System.err.println("Read event " + event);
+ currentEvent = translate(event);
+ } else {
+ currentEvent = null;
+ }
+ } catch (XMLStreamException e) {
+ String message = e.getMessage();
+ // Following code recognizes the messages produced by the Sun Zephyr parser
+ if (message.startsWith("ParseError at")) {
+ int c = message.indexOf("\nMessage: ");
+ if (c > 0) {
+ message = message.substring(c + 10);
+ }
+ }
+ XPathException err = new XPathException("Error reported by XML parser: " + message);
+ err.setErrorCode(SaxonErrorCode.SXXP0003);
+ err.setLocator(translateLocation(e.getLocation()));
+ throw err;
+ }
+ return currentEvent;
+ }
+
+ /**
+ * Translate a StAX event into a Saxon PullEvent
+ * @param event the StAX event
+ * @return the Saxon PullEvent
+ * @throws XPathException
+ */
+
+ private PullEvent translate(int event) throws XPathException {
+ //System.err.println("EVENT " + event);
+ switch (event) {
+ case XMLStreamConstants.ATTRIBUTE:
+ return next(); // attributes are reported as part of StartElement
+ case XMLStreamConstants.CDATA:
+ case XMLStreamConstants.CHARACTERS:
+ if (depth == 0 && reader.isWhiteSpace()) {
+ return next();
+ } else {
+ Orphan o = new Orphan(config);
+ o.setNodeKind(Type.TEXT);
+ CharSlice value = new CharSlice(
+ reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
+ o.setStringValue(value);
+ return o;
+ }
+ case XMLStreamConstants.COMMENT: {
+ Orphan o = new Orphan(config);
+ o.setNodeKind(Type.COMMENT);
+ CharSlice value = new CharSlice(
+ reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
+ o.setStringValue(value);
+ return o;
+ }
+ case XMLStreamConstants.DTD:
+ unparsedEntities = (List)reader.getProperty("javax.xml.stream.entities");
+ return next();
+ case XMLStreamConstants.END_DOCUMENT:
+ return EndDocumentEvent.getInstance();
+ case XMLStreamConstants.END_ELEMENT:
+ depth--;
+ return EndElementEvent.getInstance();
+ case XMLStreamConstants.ENTITY_DECLARATION:
+ return next();
+ case XMLStreamConstants.ENTITY_REFERENCE:
+ return next();
+ case XMLStreamConstants.NAMESPACE:
+ return next(); // namespaces are reported as part of StartElement
+ case XMLStreamConstants.NOTATION_DECLARATION:
+ return next();
+ case XMLStreamConstants.PROCESSING_INSTRUCTION:{
+ Orphan o = new Orphan(config);
+ o.setNodeKind(Type.PROCESSING_INSTRUCTION);
+ String local = reader.getPITarget();
+ o.setNodeName(new NoNamespaceName(local));
+ o.setStringValue(reader.getText());
+ return o;
+ }
+ case XMLStreamConstants.SPACE:
+ if (depth == 0) {
+ return next();
+ } else if (ignoreIgnorable) {
+ // (Brave attempt, but Woodstox doesn't seem to report ignorable whitespace)
+ return next();
+ } else {
+ Orphan o = new Orphan(config);
+ o.setNodeKind(Type.TEXT);
+ o.setStringValue(reader.getText());
+ return o;
+ }
+ case XMLStreamConstants.START_DOCUMENT:
+ return next(); // we supplied the START_DOCUMENT ourselves
+ case XMLStreamConstants.START_ELEMENT:
+ depth++;
+ StartElementEvent see = new StartElementEvent(pipe);
+ String elocal = reader.getLocalName();
+ String euri = reader.getNamespaceURI();
+ String eprefix = reader.getPrefix();
+ if (eprefix == null) {
+ eprefix = "";
+ }
+ if (euri == null) {
+ euri = "";
+ }
+ see.setElementName(new FingerprintedQName(eprefix, euri, elocal));
+ see.setTypeCode(Untyped.getInstance());
+ int attCount = reader.getAttributeCount();
+ for (int index=0; index<attCount; index++) {
+ String local = reader.getAttributeLocalName(index);
+ String uri = reader.getAttributeNamespace(index);
+ String prefix = reader.getAttributePrefix(index);
+ if (prefix == null) {
+ prefix = "";
+ }
+ if (uri == null) {
+ uri = "";
+ }
+ Orphan o = new Orphan(config);
+ o.setNodeKind(Type.ATTRIBUTE);
+ o.setNodeName(new FingerprintedQName(prefix, uri, local));
+ o.setStringValue(reader.getAttributeValue(index));
+ see.addAttribute(o);
+ }
+ see.namespaceFixup();
+ return see;
+ default:
+ throw new IllegalStateException("Unknown StAX event " + event);
+
+
+ }
+ }
+
+
+ /**
+ * Return the public identifier for the current document event.
+ * <p/>
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup
+ * triggering the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ public String getPublicId() {
+ return reader.getLocation().getPublicId();
+ }
+
+ /**
+ * Return the system identifier for the current document event.
+ * <p/>
+ * <p>The return value is the system identifier of the document
+ * entity or of the external parsed entity in which the markup
+ * triggering the event appears.</p>
+ * <p/>
+ * <p>If the system identifier is a URL, the parser must resolve it
+ * fully before passing it to the application. For example, a file
+ * name must always be provided as a <em>file:...</em> URL, and other
+ * kinds of relative URI are also resolved against their bases.</p>
+ *
+ * @return A string containing the system identifier, or null
+ * if none is available.
+ * @see #getPublicId
+ */
+ public String getSystemId() {
+ return reader.getLocation().getSystemId();
+ }
+
+ /**
+ * Return the line number where the current document event ends.
+ * Lines are delimited by line ends, which are defined in
+ * the XML specification.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of diagnostics;
+ * it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.
+ * In some cases, these "line" numbers match what would be displayed
+ * as columns, and in others they may not match the source text
+ * due to internal entity expansion. </p>
+ * <p/>
+ * <p>The return value is an approximation of the line number
+ * in the document entity or external parsed entity where the
+ * markup triggering the event appears.</p>
+ * <p/>
+ * <p>If possible, the SAX driver should provide the line position
+ * of the first character after the text associated with the document
+ * event. The first line is line 1.</p>
+ *
+ * @return The line number, or -1 if none is available.
+ * @see #getColumnNumber
+ */
+ public int getLineNumber() {
+ return reader.getLocation().getLineNumber();
+ }
+
+ /**
+ * Return the column number where the current document event ends.
+ * This is one-based number of Java <code>char</code> values since
+ * the last line end.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of diagnostics;
+ * it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.
+ * For example, when lines contain combining character sequences, wide
+ * characters, surrogate pairs, or bi-directional text, the value may
+ * not correspond to the column in a text editor's display. </p>
+ * <p/>
+ * <p>The return value is an approximation of the column number
+ * in the document entity or external parsed entity where the
+ * markup triggering the event appears.</p>
+ * <p/>
+ * <p>If possible, the SAX driver should provide the line position
+ * of the first character after the text associated with the document
+ * event. The first column in each line is column 1.</p>
+ *
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return reader.getLocation().getColumnNumber();
+ }
+
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+ /**
+ * Get a list of unparsed entities.
+ *
+ * @return a list of unparsed entities, or null if the information is not available, or
+ * an empty list if there are no unparsed entities. Each item in the list will
+ * be an instance of {@link net.sf.saxon.pull.UnparsedEntity}
+ */
+
+ public List getUnparsedEntities() {
+ if (unparsedEntities == null) {
+ return null;
+ }
+ List list = new ArrayList(unparsedEntities.size());
+ for (int i=0; i<unparsedEntities.size(); i++) {
+ Object ent = unparsedEntities.get(i);
+ String name = null;
+ String systemId = null;
+ String publicId = null;
+ String baseURI = null;
+ if (ent instanceof EntityDeclaration) {
+ // This is what we would expect from the StAX API spec
+ EntityDeclaration ed = (EntityDeclaration)ent;
+ name = ed.getName();
+ systemId = ed.getSystemId();
+ publicId = ed.getPublicId();
+ baseURI = ed.getBaseURI();
+ } else if (ent.getClass().getName().equals("com.ctc.wstx.ent.UnparsedExtEntity")) {
+ // Woodstox 3.0.0 returns this: use introspection to get the data we need
+ try {
+ Class woodstoxClass = ent.getClass();
+ Class[] noArgs = new Class[0];
+ Method method = woodstoxClass.getMethod("getName", noArgs);
+ name = (String)method.invoke(ent, (Object[]) noArgs);
+ method = woodstoxClass.getMethod("getSystemId", noArgs);
+ systemId = (String)method.invoke(ent, (Object[]) noArgs);
+ method = woodstoxClass.getMethod("getPublicId", noArgs);
+ publicId = (String)method.invoke(ent, (Object[]) noArgs);
+ method = woodstoxClass.getMethod("getBaseURI", noArgs);
+ baseURI = (String)method.invoke(ent, (Object[]) noArgs);
+ } catch (NoSuchMethodException e) {
+ //
+ } catch (IllegalAccessException e) {
+ //
+ } catch (InvocationTargetException e) {
+ //
+ }
+ }
+ if (name != null) {
+ try {
+ systemId = new URI(baseURI).resolve(systemId).toString();
+ } catch (URISyntaxException err) {
+ //
+ }
+ UnparsedEntity ue = new UnparsedEntity();
+ ue.setName(name);
+ ue.setSystemId(systemId);
+ ue.setPublicId(publicId);
+ ue.setBaseURI(baseURI);
+ list.add(ue);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Translate a StAX Location object to a Saxon Locator
+ * @param location the StAX Location object
+ * @return a Saxon/SAX SourceLocator object
+ */
+
+ private ExpressionLocation translateLocation(Location location) {
+ ExpressionLocation loc = new ExpressionLocation();
+ if (location != null) {
+ loc.setLineNumber(location.getLineNumber());
+ loc.setColumnNumber(location.getColumnNumber());
+ loc.setSystemId(location.getSystemId());
+ //loc.setPublicId(location.getPublicId());
+ }
+ return loc;
+ }
+
+
+ /**
+ * Error reporting class for StAX parser errors
+ */
+
+ private class StaxErrorReporter implements XMLReporter {
+
+ public void report(String message, String errorType,
+ Object relatedInformation, Location location)
+ throws XMLStreamException {
+ ExpressionLocation loc = translateLocation(location);
+ XPathException err = new XPathException("Error reported by XML parser: " + message + " (" + errorType + ')');
+ err.setLocator(loc);
+ try {
+ pipe.getErrorListener().error(err);
+ } catch (TransformerException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ }
+
+ /**
+ * Simple test program
+ * Usage: java StaxBridge in.xml [out.xml]
+ * @param args command line arguments
+ */
+
+ public static void main(String[] args) throws Exception {
+ for (int i=0; i<1; i++) {
+ long startTime = System.currentTimeMillis();
+ PipelineConfiguration pipe = new Configuration().makePipelineConfiguration();
+ StaxToEventBridge puller = new StaxToEventBridge();
+ File f = new File(args[0]);
+ puller.setInputStream(f.toURI().toString(), new FileInputStream(f));
+ puller.setPipelineConfiguration(pipe);
+ XMLEmitter emitter = new XMLEmitter();
+ emitter.setPipelineConfiguration(pipe);
+ emitter.setOutputProperties(new Properties());
+ if (args.length > 1) {
+ emitter.setOutputStream(new FileOutputStream(args[1]));
+ } else {
+ emitter.setOutputStream(System.out);
+ }
+ NamespaceReducer r = new NamespaceReducer(emitter);
+
+ EventIteratorToReceiver.copy(puller, r);
+ System.err.println("Elapsed time: " + (System.currentTimeMillis() - startTime) + "ms");
+ }
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return false;
+ }
+}
+
diff --git a/sf/saxon/evpull/TracingEventIterator.java b/sf/saxon/evpull/TracingEventIterator.java
new file mode 100644
index 0000000..04f1d7b
--- /dev/null
+++ b/sf/saxon/evpull/TracingEventIterator.java
@@ -0,0 +1,61 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.evpull;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class is a filter for a sequence of pull events; it returns the input sequence unchanged,
+ * but traces execution to System.err
+ */
+public class TracingEventIterator implements EventIterator {
+
+ private EventIterator base;
+
+ /**
+ * Create an event iterator that traces all events received from the base sequence, and returns
+ * them unchanged
+ * @param base the iterator over the base sequence
+ */
+
+ public TracingEventIterator(EventIterator base) {
+ this.base = base;
+ }
+
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return base.isFlatSequence();
+ }
+
+ /**
+ * Get the next event in the sequence
+ *
+ * @return the next event, or null when the sequence is exhausted. Note that since an EventIterator is
+ * itself a PullEvent, this method may return a nested iterator.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic evaluation error occurs
+ */
+
+ /*@Nullable*/ public PullEvent next() throws XPathException {
+ PullEvent next = base.next();
+ if (next == null) {
+ System.err.println("EVPULL end-of-sequence");
+ } else {
+ System.err.println("EVPULL " + next.getClass().getName());
+ }
+ return next;
+ }
+}
+
diff --git a/sf/saxon/evpull/package.html b/sf/saxon/evpull/package.html
new file mode 100644
index 0000000..39b9ada
--- /dev/null
+++ b/sf/saxon/evpull/package.html
@@ -0,0 +1,68 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.evpull</title>
+</head>
+
+<body>
+
+<p>This package provides classes that implement a StAX-like pull pipeline in which a recieving component
+makes calls on a provider component to supply information from the XML stream one event at a time. The
+object that is supplied in response to these calls is a {@link net.sf.saxon.evpull.PullEvent}, and
+a component that can be invoked to deliver a sequence of these objects is a {@link net.sf.saxon.evpull.EventIterator}.</p>
+
+<p>An {@link net.sf.saxon.evpull.EventIterator} is itself a {@link net.sf.saxon.evpull.PullEvent}, so an event provider
+may return a sequence of events in response to a single call by returning an iterator. A sequence of events containing
+no iterators is referred to as a <i>flat</i> sequence, and any sequence of events can be converted to a flat
+sequence by inserting an {@link net.sf.saxon.evpull.EventStackIterator} into the pipeline.</p>
+
+<p>Pull processing is not used extensively in Saxon, and is generally not used at all unless explicitly
+requested. It can be requested, for example, by supplying a {@link net.sf.saxon.evpull.PullEventSource} object to an interface
+that expects an XML document to be supplied as a JAXP {@link javax.xml.transform.Source}. It is also used
+in XQJ when methods such as {@link javax.xml.xquery.XQDataFactory#createItemFromDocument} are used
+to construct an XML document from a supplied {@link javax.xml.stream.XMLStreamReader}. In such cases
+Saxon uses the class {@link net.sf.saxon.evpull.StaxToEventBridge} to convert StAX events to its own
+{@link net.sf.saxon.evpull.PullEvent} events. Conversion in the opposite direction uses the class
+{@link net.sf.saxon.evpull.EventToStaxBridge}.</p>
+
+<p>It is possible to request pull-mode evaluation of XQuery code using the method
+{@link net.sf.saxon.query.XQueryExpression#iterateEvents}. This causes any document and element
+node constructors to be evaluated in pull mode, returning events representing start/end document/element
+rather than actually constructing the result tree in memory. The relevant expressions in the expression
+tree provide an <code>iterateEvents()</code> method to support this mode of execution.</p>
+
+<p>A sequence of events is said to be <i>composed</i> if it consists entirely of items (that is, a node
+is passed as a single event, rather than by walking the tree); it is said to be <i>decomposed</i>
+if if consists entirely of StAX-like events. In general, the classes in this package handle sequences
+that mix both styles. A fully-composed sequence, however, is normally handled using the
+ {@link net.sf.saxon.om.SequenceIterator} interface rather than by the classes in this package.
+ The {@link net.sf.saxon.evpull.SequenceComposer} returns a full composed event stream
+ from a decomposed or mixed stream, constructing tree fragments when
+necessary to achieve this; the {@link net.sf.saxon.evpull.Decomposer}
+does the inverse, walking any tree fragments to deliver the corresponding start-element and end-element
+events.</p>
+
+<p>The class {@link net.sf.saxon.evpull.EventIteratorOverSequence} converts a stream of items obtained
+from a {@link net.sf.saxon.om.SequenceIterator} into a composed stream of {@link net.sf.saxon.evpull.PullEvent} events.</p>
+
+<p>The class {@link net.sf.saxon.evpull.EventIteratorToReceiver} reads a sequence of events from a pull pipeline
+and outputs the same sequence of events to a push pipeline using the {@link net.sf.saxon.event.Receiver}
+interface.</p>
+
+<p>The package {@link net.sf.saxon.pull} represents an earlier attempt at pull-mode processing in Saxon.
+It is retained because it provides some capabilities not yet present in this package.</p>
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+30 July 2010</i></p>
+</body>
+</html>
diff --git a/sf/saxon/expr/AdjacentTextNodeMerger.java b/sf/saxon/expr/AdjacentTextNodeMerger.java
new file mode 100644
index 0000000..f634b17
--- /dev/null
+++ b/sf/saxon/expr/AdjacentTextNodeMerger.java
@@ -0,0 +1,272 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.AdjacentTextNodeMergerCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.AdjacentTextNodeMergerAdjunct;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.instruct.Block;
+import net.sf.saxon.expr.instruct.Choose;
+import net.sf.saxon.expr.instruct.ValueOf;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AdjacentTextNodeMergingIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Cardinality;
+
+import java.util.List;
+
+
+
+/**
+ * This class performs the first phase of processing in "constructing simple content":
+ * it takes an input sequence, eliminates empty text nodes, and combines adjacent text nodes
+ * into one.
+ * @since 9.3
+ */
+public class AdjacentTextNodeMerger extends UnaryExpression {
+
+ public AdjacentTextNodeMerger(Expression p0) {
+ super(p0);
+ }
+
+ /**
+ * Make an AdjacentTextNodeMerger expression with a given operand, or a simpler equivalent expression if appropriate
+ * @param base the operand expression
+ * @return an AdjacentTextNodeMerger or equivalent expression
+ */
+
+ public static Expression makeAdjacentTextNodeMerger(Expression base) {
+ if (base instanceof Literal && ((Literal)base).getValue() instanceof AtomicSequence) {
+ return base;
+ } else {
+ return new AdjacentTextNodeMerger(base);
+ }
+ }
+
+ @Override
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ if (operand instanceof Literal && ((Literal)operand).getValue() instanceof AtomicValue) {
+ return operand;
+ } else {
+ return super.simplify(visitor);
+ }
+ }
+
+ /*@NotNull*/
+ @Override
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.typeCheck(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+ // This wrapper expression is unnecessary if the base expression cannot return text nodes,
+ // or if it can return at most one item
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (th.relationship(getBaseExpression().getItemType(th), NodeKindTest.TEXT) == TypeHierarchy.DISJOINT) {
+ return getBaseExpression();
+ }
+ if (!Cardinality.allowsMany(getBaseExpression().getCardinality())) {
+ return getBaseExpression();
+ }
+ // In a choose expression, we can push the wrapper down to the action branches (whence it may disappear)
+ if (getBaseExpression() instanceof Choose) {
+ Choose choose = (Choose) getBaseExpression();
+ Expression[] actions = choose.getActions();
+ for (int i=0; i<actions.length; i++) {
+ AdjacentTextNodeMerger atm2 = new AdjacentTextNodeMerger(actions[i]);
+ actions[i] = atm2.typeCheck(visitor, contextItemType);
+ }
+ return choose;
+ }
+ // In a Block expression, check whether adjacent text nodes can occur (used in test strmode089)
+ // Code deleted:
+ if (getBaseExpression() instanceof Block) {
+ Block block = (Block) getBaseExpression();
+ Expression[] actions = block.getChildren();
+ boolean prevtext = false;
+ boolean needed = false;
+ boolean maybeEmpty = false;
+ for (int i=0; i<actions.length; i++) {
+ boolean maybetext;
+ if (actions[i] instanceof ValueOf) {
+ maybetext = true;
+ Expression content = ((ValueOf)actions[i]).getContentExpression();
+ if (content instanceof StringLiteral) {
+ // if it's empty, we could remove it now, but that's awkward and probably doesn't happen
+ maybeEmpty |= ((StringLiteral)content).getStringValue().length() == 0;
+ } else {
+ maybeEmpty = true;
+ }
+ } else {
+ maybetext = th.relationship(actions[i].getItemType(th), NodeKindTest.TEXT) != TypeHierarchy.DISJOINT;
+ maybeEmpty |= maybetext;
+ }
+ if (prevtext && maybetext) {
+ needed = true;
+ break; // may contain adjacent text nodes
+ }
+ if (maybetext && Cardinality.allowsMany(actions[i].getCardinality())) {
+ needed = true;
+ break; // may contain adjacent text nodes
+ }
+ prevtext = maybetext;
+ }
+ if (!needed) {
+ // We don't need to merge adjacent text nodes, we only need to remove empty ones.
+ if (maybeEmpty) {
+ return new EmptyTextNodeRemover(block);
+ } else {
+ return block;
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Determine the data type of the expression, if possible. The default
+ * implementation for unary expressions returns the item type of the operand
+ * @param th the type hierarchy cache
+ * @return the item type of the items in the result sequence, insofar as this
+ * is known statically.
+ */
+
+ /*@NotNull*/
+ @Override
+ public ItemType getItemType(TypeHierarchy th) {
+ return getBaseExpression().getItemType(th);
+ }
+
+ @Override
+ public int computeCardinality() {
+ return getBaseExpression().getCardinality() | StaticProperty.ALLOWS_ZERO;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new AdjacentTextNodeMerger(getBaseExpression().copy());
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is prefered.
+ */
+
+ public int getImplementationMethod() {
+ return Expression.PROCESS_METHOD | Expression.ITERATE_METHOD | ITEM_FEED_METHOD | WATCH_METHOD ;
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new AdjacentTextNodeMergerCompiler();
+ }
+//#endif
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(syntacticContext, allowExtensions, null);
+ }
+
+ @Override
+ public AdjacentTextNodeMergerAdjunct getStreamingAdjunct() {
+ return new AdjacentTextNodeMergerAdjunct();
+ }
+//#endif
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ @Override
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ return new AdjacentTextNodeMergingIterator(getBaseExpression().iterate(context));
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(XPathContext context, int locationId, int options) throws XPathException {
+ SequenceReceiver out = context.getReceiver();
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ SequenceIterator iter = getBaseExpression().iterate(context);
+ boolean prevText = false;
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (isTextNode(item)) {
+ CharSequence s = item.getStringValueCS();
+ if (s.length() > 0) {
+ fsb.append(s);
+ prevText = true;
+ }
+
+ } else {
+ if (prevText) {
+ out.characters(fsb, locationId, options);
+ }
+ prevText = false;
+ fsb.setLength(0);
+ out.append(item, locationId, options);
+ }
+ }
+ if (prevText) {
+ out.characters(fsb, locationId, options);
+ }
+ }
+
+
+ @Override
+ public String getExpressionName() {
+ return "mergeAdjacentText";
+ }
+
+ /**
+ * Ask whether an item is a text node
+ * @param item the item in question
+ * @return true if the item is a node of kind text
+ */
+
+ public static boolean isTextNode(Item item) {
+ return item instanceof NodeInfo && ((NodeInfo)item).getNodeKind() == Type.TEXT;
+ }
+
+}
+
diff --git a/sf/saxon/expr/AnalyzeMappingFunction.java b/sf/saxon/expr/AnalyzeMappingFunction.java
new file mode 100644
index 0000000..24ee3d5
--- /dev/null
+++ b/sf/saxon/expr/AnalyzeMappingFunction.java
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.regex.RegexIterator;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.*;
+
+/**
+ * Mapping function that maps the sequence of matching/non-matching strings to the
+ * sequence delivered by applying the matching-substring and non-matching-substring
+ * expressions respectively to each such string
+ */
+
+ public class AnalyzeMappingFunction implements ContextMappingFunction<StringValue> {
+
+ private RegexIterator base;
+ private XPathContext c2;
+ private Expression nonMatchExpr;
+ private Expression matchingExpr;
+
+ public AnalyzeMappingFunction(RegexIterator base, XPathContext c2, Expression nonMatchExpr, Expression matchingExpr) {
+ this.base = base;
+ this.c2 = c2;
+ this.nonMatchExpr = nonMatchExpr;
+ this.matchingExpr = matchingExpr;
+ }
+
+ /**
+ * Map one item to a sequence.
+ *
+ * @param context The processing context. Some mapping functions use this because they require
+ * context information. Some mapping functions modify the context by maintaining the context item
+ * and position. In other cases, the context may be null.
+ * @return either (a) a SequenceIterator over the sequence of items that the supplied input
+ * item maps to, or (b) an Item if it maps to a single item, or (c) null if it maps to an empty
+ * sequence.
+ */
+
+ public SequenceIterator<StringValue> map(XPathContext context) throws XPathException {
+ if (base.isMatching()) {
+ if (matchingExpr != null) {
+ return (SequenceIterator<StringValue>)matchingExpr.iterate(c2);
+ }
+ } else {
+ if (nonMatchExpr != null) {
+ return (SequenceIterator<StringValue>)nonMatchExpr.iterate(c2);
+ }
+ }
+ return EmptyIterator.getInstance();
+ }
+ }
+
diff --git a/sf/saxon/expr/AndExpression.java b/sf/saxon/expr/AndExpression.java
new file mode 100644
index 0000000..aea7f56
--- /dev/null
+++ b/sf/saxon/expr/AndExpression.java
@@ -0,0 +1,144 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.AndExpressionCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.instruct.Choose;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.functions.NotFn;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+
+public class AndExpression extends BooleanExpression{
+
+ /**
+ * Construct a boolean AND expression
+ * @param p1 the first operand
+ * @param p2 the second operand
+ */
+
+ public AndExpression(Expression p1, Expression p2) {
+ super(p1, Token.AND, p2);
+ }
+
+
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+
+ // If the value can be determined from knowledge of one operand, precompute the result
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (Literal.isConstantBoolean(operand0, false) || Literal.isConstantBoolean(operand1, false)) {
+ // A and false() => false()
+ // false() and B => false()
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ } else if (Literal.isConstantBoolean(operand0, true)) {
+ // true() and B => B
+ return forceToBoolean(operand1, th);
+ } else if (Literal.isConstantBoolean(operand1, true)) {
+ // A and true() => A
+ return forceToBoolean(operand0, th);
+ }
+
+ // Rewrite (A and B) as (if (A) then B else false()). The benefit of this is that when B is a recursive
+ // function call, it is treated as a tail call (test qxmp290). To avoid disrupting other optimizations
+ // of "and" expressions (specifically, where clauses in FLWOR expressions), do this ONLY if B is a user
+ // function call (we can't tell if it's recursive), and it's not in a loop.
+
+
+ if (e == this &&
+ operand1 instanceof UserFunctionCall &&
+ th.isSubType(operand1.getItemType(th), BuiltInAtomicType.BOOLEAN) &&
+ !visitor.isLoopingSubexpression(null)) {
+ Expression cond = Choose.makeConditional(operand0, operand1, Literal.makeLiteral(BooleanValue.FALSE));
+ ExpressionTool.copyLocationInfo(this, cond);
+ return cond;
+ }
+ return this;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new AndExpression(operand0.copy(), operand1.copy());
+ }
+
+
+ /**
+ * Return the negation of this boolean expression, that is, an expression that returns true
+ * when this expression returns false, and vice versa
+ *
+ * @return the negation of this expression
+ */
+
+ public Expression negate() {
+ // Apply de Morgan's laws
+ // not(A and B) ==> not(A) or not(B)
+ NotFn not0 = (NotFn) SystemFunctionCall.makeSystemFunction("not", new Expression[]{operand0});
+ NotFn not1 = (NotFn) SystemFunctionCall.makeSystemFunction("not", new Expression[]{operand1});
+ return new OrExpression(not0, not1);
+
+ }
+
+
+ /**
+ * Evaluate as a boolean.
+ */
+
+ public boolean effectiveBooleanValue(XPathContext c) throws XPathException {
+ return operand0.effectiveBooleanValue(c) && operand1.effectiveBooleanValue(c);
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the AndExpression
+ *
+ * @return the relevantExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new AndExpressionCompiler();
+ }
+ //#endif
+
+
+}
+
diff --git a/sf/saxon/expr/ArithmeticExpression.java b/sf/saxon/expr/ArithmeticExpression.java
new file mode 100644
index 0000000..c9fd58b
--- /dev/null
+++ b/sf/saxon/expr/ArithmeticExpression.java
@@ -0,0 +1,398 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ArithmeticCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+/**
+ * Arithmetic Expression: an expression using one of the operators
+ * plus, minus, multiply, div, idiv, mod. Note that this code does not handle backwards
+ * compatibility mode: see {@link ArithmeticExpression10}
+ */
+
+public class ArithmeticExpression extends BinaryExpression {
+
+ private Calculator calculator;
+ protected boolean simplified = false;
+
+ /**
+ * Create an arithmetic expression
+ * @param p0 the first operand
+ * @param operator the operator, for example {@link Token#PLUS}
+ * @param p1 the second operand
+ */
+
+ public ArithmeticExpression(Expression p0, int operator, Expression p1) {
+ super(p0, operator, p1);
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "arithmetic";
+ }
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ if (simplified) {
+ // Don't simplify more than once; because in XSLT the static context on subsequent calls
+ // might not know whether backwards compatibility is in force or not
+ return this;
+ }
+ simplified = true;
+ Expression e = super.simplify(visitor);
+ if (e == this && visitor.getStaticContext().isInBackwardsCompatibleMode()) {
+ return new ArithmeticExpression10(operand0, operator, operand1);
+ } else {
+ if (operator == Token.NEGATE && Literal.isAtomic(operand1)) {
+ // very early evaluation of expressions like "-1", so they are treated as numeric literals
+ AtomicValue val = (AtomicValue)((Literal)operand1).getValue();
+ if (val instanceof NumericValue) {
+ return Literal.makeLiteral(((NumericValue)val).negate());
+ }
+ }
+ return e;
+ }
+ }
+
+ /**
+ * Get the calculator allocated to evaluate this expression
+ * @return the calculator, a helper object that does the actual calculation
+ */
+
+ public Calculator getCalculator() {
+ return calculator;
+ }
+
+ /**
+ * Type-check the expression statically. We try to work out which particular
+ * arithmetic function to use if the types of operands are known an compile time.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+
+ Expression oldOp0 = operand0;
+ Expression oldOp1 = operand1;
+
+ operand0 = visitor.typeCheck(operand0, contextItemType);
+ operand1 = visitor.typeCheck(operand1, contextItemType);
+
+
+ SequenceType atomicType = SequenceType.OPTIONAL_ATOMIC;
+
+ RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0);
+ //role0.setSourceLocator(this);
+ operand0 = TypeChecker.staticTypeCheck(operand0, atomicType, false, role0, visitor);
+ final ItemType itemType0 = operand0.getItemType(th);
+ if (itemType0 instanceof ErrorType) {
+ return Literal.makeEmptySequence();
+ }
+ AtomicType type0 = (AtomicType) itemType0.getPrimitiveItemType();
+ if (type0.getFingerprint() == StandardNames.XS_UNTYPED_ATOMIC) {
+ operand0 = UntypedSequenceConverter.makeUntypedSequenceConverter(visitor.getConfiguration(), operand0, BuiltInAtomicType.DOUBLE);
+ type0 = BuiltInAtomicType.DOUBLE;
+ } else if (/*!(operand0 instanceof UntypedAtomicConverter)*/
+ (operand0.getSpecialProperties()&StaticProperty.NOT_UNTYPED_ATOMIC) == 0 &&
+ th.relationship(type0, BuiltInAtomicType.UNTYPED_ATOMIC) != TypeHierarchy.DISJOINT) {
+ operand0 = UntypedSequenceConverter.makeUntypedSequenceConverter(visitor.getConfiguration(), operand0, BuiltInAtomicType.DOUBLE);
+ type0 = (AtomicType)operand0.getItemType(th);
+ }
+
+ // System.err.println("First operand"); operand0.display(10);
+
+ RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1);
+ //role1.setSourceLocator(this);
+ operand1 = TypeChecker.staticTypeCheck(operand1, atomicType, false, role1, visitor);
+ final ItemType itemType1 = operand1.getItemType(th);
+ if (itemType1 instanceof ErrorType) {
+ return Literal.makeEmptySequence();
+ }
+ AtomicType type1 = (AtomicType)itemType1.getPrimitiveItemType();
+ if (type1.getFingerprint() == StandardNames.XS_UNTYPED_ATOMIC) {
+ operand1 = UntypedSequenceConverter.makeUntypedSequenceConverter(visitor.getConfiguration(), operand1, BuiltInAtomicType.DOUBLE);
+ type1 = BuiltInAtomicType.DOUBLE;
+ } else if (/*!(operand1 instanceof UntypedAtomicConverter) &&*/
+ (operand1.getSpecialProperties()&StaticProperty.NOT_UNTYPED_ATOMIC) == 0 &&
+ th.relationship(type1, BuiltInAtomicType.UNTYPED_ATOMIC) != TypeHierarchy.DISJOINT) {
+ operand1 = UntypedSequenceConverter.makeUntypedSequenceConverter(visitor.getConfiguration(), operand1, BuiltInAtomicType.DOUBLE);
+ type1 = (AtomicType)operand1.getItemType(th);
+ }
+
+ if (operand0 != oldOp0) {
+ adoptChildExpression(operand0);
+ }
+
+ if (operand1 != oldOp1) {
+ adoptChildExpression(operand1);
+ }
+
+ if (Literal.isEmptySequence(operand0) ||
+ Literal.isEmptySequence(operand1)) {
+ return Literal.makeEmptySequence();
+ }
+
+ if (type0.isExternalType() || type1.isExternalType()) {
+ XPathException de = new XPathException("Arithmetic operators are not defined for external objects");
+ de.setLocator(this);
+ de.setErrorCode("XPTY0004");
+ throw de;
+ }
+
+ if (operator == Token.NEGATE) {
+ if (operand1 instanceof Literal && ((Literal)operand1).getValue() instanceof NumericValue) {
+ NumericValue nv = (NumericValue)((Literal)operand1).getValue();
+ return Literal.makeLiteral(nv.negate());
+ } else {
+ NegateExpression ne = new NegateExpression(operand1);
+ ne.setBackwardsCompatible(false);
+ return visitor.typeCheck(ne, contextItemType);
+ }
+ }
+
+ // Get a calculator to implement the arithmetic operation. If the types are not yet specifically known,
+ // we allow this to return an "ANY" calculator which defers the decision. However, we only allow this if
+ // at least one of the operand types is AnyAtomicType or (otherwise unspecified) numeric.
+
+ boolean mustResolve = !(type0.equals(BuiltInAtomicType.ANY_ATOMIC) || type1.equals(BuiltInAtomicType.ANY_ATOMIC)
+ || type0.equals(BuiltInAtomicType.NUMERIC) || type1.equals(BuiltInAtomicType.NUMERIC));
+
+ calculator = Calculator.getCalculator(
+ type0.getFingerprint(), type1.getFingerprint(), mapOpCode(operator), mustResolve);
+
+ if (calculator == null) {
+ XPathException de = new XPathException("Arithmetic operator is not defined for arguments of types (" +
+ type0.getDescription() + ", " + type1.getDescription() + ")");
+ de.setLocator(this);
+ de.setErrorCode("XPTY0004");
+ throw de;
+ }
+
+ try {
+ if ((operand0 instanceof Literal) && (operand1 instanceof Literal)) {
+ return Literal.makeLiteral(SequenceTool.toGroundedValue(evaluateItem(visitor.getStaticContext().makeEarlyEvaluationContext())));
+ }
+ } catch (XPathException err) {
+ // if early evaluation fails, suppress the error: the value might
+ // not be needed at run-time, or it might be due to context such as the implicit timezone
+ // not being available yet
+ }
+ return this;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ /*@Nullable*/@Override
+ public IntegerValue[] getIntegerBounds() {
+ IntegerValue[] bounds0 = operand0.getIntegerBounds();
+ IntegerValue[] bounds1 = operand1.getIntegerBounds();
+ if (bounds0 == null || bounds1 == null) {
+ return null;
+ } else {
+ switch (operator) {
+ case Token.PLUS:
+ return new IntegerValue[]{bounds0[0].plus(bounds1[0]), bounds0[1].plus(bounds1[1])};
+ case Token.MINUS:
+ return new IntegerValue[]{bounds0[0].minus(bounds1[1]), bounds0[1].minus(bounds1[0])};
+ case Token.MULT:
+ if (operand1 instanceof Literal) {
+ IntegerValue val1 = bounds1[0];
+ if (val1.signum() > 0) {
+ return new IntegerValue[]{bounds0[0].times(val1), bounds0[1].times(val1)};
+ } else {
+ return null;
+ }
+ } else if (operand0 instanceof Literal) {
+ IntegerValue val0 = bounds1[0];
+ if (val0.signum() > 0) {
+ return new IntegerValue[]{bounds1[0].times(val0), bounds1[1].times(val0)};
+ } else {
+ return null;
+ }
+ }
+ case Token.DIV:
+ case Token.IDIV:
+ if (operand1 instanceof Literal) {
+ IntegerValue val1 = bounds1[0];
+ if (val1.signum() > 0) {
+ try {
+ return new IntegerValue[]{bounds0[0].idiv(val1), bounds0[1].idiv(val1)};
+ } catch (XPathException e) {
+ return null;
+ }
+ }
+ }
+ return null;
+ default:
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ArithmeticExpression ae = new ArithmeticExpression(operand0.copy(), operator, operand1.copy());
+ ae.calculator = calculator;
+ ae.simplified = simplified;
+ return ae;
+ }
+
+ /**
+ * Static method to apply arithmetic to two values
+ * @param value0 the first value
+ * @param operator the operator as denoted in the Calculator class, for example {@link Calculator#PLUS}
+ * @param value1 the second value
+ * @param context the XPath dynamic evaluation context
+ * @return the result of the arithmetic operation
+ */
+
+ public static AtomicValue compute(AtomicValue value0, int operator, AtomicValue value1, XPathContext context)
+ throws XPathException {
+ int p0 = value0.getPrimitiveType().getFingerprint();
+ int p1 = value1.getPrimitiveType().getFingerprint();
+ Calculator calculator = Calculator.getCalculator(p0, p1, operator, false);
+ return calculator.compute(value0, value1, context);
+ }
+
+ /**
+ * Map operator codes from those in the Token class to those in the Calculator class
+ * @param op an operator denoted by a constant in the {@link Token} class, for example {@link Token#PLUS}
+ * @return an operator denoted by a constant defined in the {@link Calculator} class, for example
+ * {@link Calculator#PLUS}
+ */
+
+ public static int mapOpCode(int op) {
+ switch (op) {
+ case Token.PLUS:
+ return Calculator.PLUS;
+ case Token.MINUS:
+ case Token.NEGATE:
+ return Calculator.MINUS;
+ case Token.MULT:
+ return Calculator.TIMES;
+ case Token.DIV:
+ return Calculator.DIV;
+ case Token.IDIV:
+ return Calculator.IDIV;
+ case Token.MOD:
+ return Calculator.MOD;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ }
+
+ /**
+ * Determine the data type of the expression, insofar as this is known statically
+ * @param th the type hierarchy cache
+ * @return the atomic type of the result of this arithmetic expression
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (calculator == null) {
+ return BuiltInAtomicType.ANY_ATOMIC; // type is not known statically
+ } else {
+ ItemType t1 = operand0.getItemType(th);
+ if (!(t1 instanceof AtomicType)) {
+ t1 = t1.getAtomizedItemType();
+ }
+ ItemType t2 = operand1.getItemType(th);
+ if (!(t2 instanceof AtomicType)) {
+ t2 = t2.getAtomizedItemType();
+ }
+ ItemType resultType = calculator.getResultType((AtomicType) t1.getPrimitiveItemType(),
+ (AtomicType) t2.getPrimitiveItemType());
+
+ if (resultType.equals(BuiltInAtomicType.ANY_ATOMIC)) {
+ // there are a few special cases where we can do better. For example, given X+1, where the type of X
+ // is unknown, we can still infer that the result is numeric. (Not so for X*2, however, where it could
+ // be a duration)
+ if ((operator == Token.PLUS || operator == Token.MINUS) &&
+ (th.isSubType(t2, BuiltInAtomicType.NUMERIC) || th.isSubType(t1, BuiltInAtomicType.NUMERIC))) {
+ resultType = BuiltInAtomicType.NUMERIC;
+ }
+ }
+ return resultType;
+ }
+ }
+
+ /**
+ * Evaluate the expression.
+ */
+
+ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+
+ AtomicValue v0 = (AtomicValue) operand0.evaluateItem(context);
+ if (v0 == null) {
+ return null;
+ }
+
+ AtomicValue v1 = (AtomicValue) operand1.evaluateItem(context);
+ if (v1 == null) {
+ return null;
+ }
+
+ try {
+ return calculator.compute(v0, v1, context);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Arithmetic expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ArithmeticCompiler();
+ }
+ //#endif
+
+
+}
+
diff --git a/sf/saxon/expr/ArithmeticExpression10.java b/sf/saxon/expr/ArithmeticExpression10.java
new file mode 100644
index 0000000..c98d2fa
--- /dev/null
+++ b/sf/saxon/expr/ArithmeticExpression10.java
@@ -0,0 +1,349 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.instruct.Choose;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.functions.NumberFn;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+/**
+ * Arithmetic Expression: an expression using one of the operators
+ * plus, minus, multiply, div, idiv, mod, in backwards
+ * compatibility mode: see {@link ArithmeticExpression} for the non-backwards
+ * compatible case.
+ */
+
+public class ArithmeticExpression10 extends BinaryExpression implements Callable {
+
+ private Calculator calculator;
+
+ /**
+ * Create an arithmetic expression to be evaluated in XPath 1.0 mode
+ * @param p0 the first operand
+ * @param operator the operator, for example {@link Token#PLUS}
+ * @param p1 the second operand
+ */
+
+ public ArithmeticExpression10(Expression p0, int operator, Expression p1) {
+ super(p0, operator, p1);
+ }
+
+ /**
+ * Determine whether the expression is to be evaluated in backwards-compatible mode
+ * @return true, always
+ */
+
+ public boolean isBackwardsCompatible() {
+ return true;
+ }
+
+ /**
+ * Type-check the expression statically. We try to work out which particular
+ * arithmetic function to use if the types of operands are known an compile time.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ final Configuration config = visitor.getConfiguration();
+ final TypeHierarchy th = config.getTypeHierarchy();
+
+ if (Literal.isEmptySequence(operand0)) {
+ return Literal.makeLiteral(DoubleValue.NaN);
+ }
+
+ if (Literal.isEmptySequence(operand1)) {
+ return Literal.makeLiteral(DoubleValue.NaN);
+ }
+
+ Expression oldOp0 = operand0;
+ Expression oldOp1 = operand1;
+
+ operand0 = visitor.typeCheck(operand0, contextItemType);
+ operand1 = visitor.typeCheck(operand1, contextItemType);
+
+
+ SequenceType atomicType = SequenceType.OPTIONAL_ATOMIC;
+
+ RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0);
+ //role0.setSourceLocator(this);
+ operand0 = TypeChecker.staticTypeCheck(operand0, atomicType, true, role0, visitor);
+
+ RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1);
+ //role1.setSourceLocator(this);
+ operand1 = TypeChecker.staticTypeCheck(operand1, atomicType, true, role1, visitor);
+
+ final ItemType itemType0 = operand0.getItemType(th);
+ if (itemType0 instanceof ErrorType) {
+ return Literal.makeLiteral(DoubleValue.NaN);
+ }
+ AtomicType type0 = (AtomicType) itemType0.getPrimitiveItemType();
+
+ final ItemType itemType1 = operand1.getItemType(th);
+ if (itemType1 instanceof ErrorType) {
+ return Literal.makeLiteral(DoubleValue.NaN);
+ }
+ AtomicType type1 = (AtomicType)itemType1.getPrimitiveItemType();
+
+ // If both operands are integers, use integer arithmetic and convert the result to a double
+ if (th.isSubType(type0, BuiltInAtomicType.INTEGER) &&
+ th.isSubType(type1, BuiltInAtomicType.INTEGER) &&
+ (operator == Token.PLUS || operator == Token.MINUS || operator == Token.MULT)) {
+ ArithmeticExpression arith = new ArithmeticExpression(operand0, operator, operand1);
+ arith.simplified = true;
+ NumberFn n = (NumberFn) SystemFunctionCall.makeSystemFunction("number", new Expression[]{arith});
+ return n.typeCheck(visitor, contextItemType);
+ }
+
+ if (calculator == null) {
+ operand0 = createConversionCode(operand0, config, type0);
+ }
+ type0 = (AtomicType) operand0.getItemType(th).getPrimitiveItemType();
+
+ // System.err.println("First operand"); operand0.display(10);
+
+
+
+ if (calculator == null) {
+ operand1 = createConversionCode(operand1, config, type1);
+ }
+
+ type1 = (AtomicType) operand1.getItemType(th).getPrimitiveItemType();
+
+ if (operand0 != oldOp0) {
+ adoptChildExpression(operand0);
+ }
+
+ if (operand1 != oldOp1) {
+ adoptChildExpression(operand1);
+ }
+
+ if (operator == Token.NEGATE) {
+ if (operand1 instanceof Literal) {
+ GroundedValue v = ((Literal)operand1).getValue();
+ if (v instanceof NumericValue) {
+ return Literal.makeLiteral(((NumericValue)v).negate());
+ }
+ }
+ NegateExpression ne = new NegateExpression(operand1);
+ ne.setBackwardsCompatible(true);
+ return visitor.typeCheck(ne, contextItemType);
+ }
+
+ // Get a calculator to implement the arithmetic operation. If the types are not yet specifically known,
+ // we allow this to return an "ANY" calculator which defers the decision. However, we only allow this if
+ // at least one of the operand types is AnyAtomicType or (otherwise unspecified) numeric.
+
+ boolean mustResolve = !(type0.equals(BuiltInAtomicType.ANY_ATOMIC) || type1.equals(BuiltInAtomicType.ANY_ATOMIC)
+ || type0.equals(BuiltInAtomicType.NUMERIC) || type1.equals(BuiltInAtomicType.NUMERIC));
+
+ calculator = assignCalculator(type0, type1, mustResolve);
+
+ try {
+ if ((operand0 instanceof Literal) && (operand1 instanceof Literal)) {
+ return Literal.makeLiteral(
+ SequenceTool.toGroundedValue(evaluateItem(visitor.getStaticContext().makeEarlyEvaluationContext())));
+ }
+ } catch (XPathException err) {
+ // if early evaluation fails, suppress the error: the value might
+ // not be needed at run-time
+ }
+ return this;
+ }
+
+ private Calculator assignCalculator(AtomicType type0, AtomicType type1, boolean mustResolve) throws XPathException {
+ Calculator calculator = Calculator.getCalculator(type0.getFingerprint(), type1.getFingerprint(),
+ ArithmeticExpression.mapOpCode(operator), mustResolve);
+
+ if (calculator == null) {
+ XPathException de = new XPathException("Arithmetic operator is not defined for arguments of types (" +
+ type0.getDescription() + ", " + type1.getDescription() + ")");
+ de.setLocator(this);
+ de.setErrorCode("XPTY0004");
+ throw de;
+ }
+ return calculator;
+ }
+
+ private Expression createConversionCode(
+ Expression operand, final Configuration config, AtomicType type) {
+ TypeHierarchy th = config.getTypeHierarchy();
+ if (Cardinality.allowsMany(operand.getCardinality())) {
+ Expression fie = FirstItemExpression.makeFirstItemExpression(operand);
+ ExpressionTool.copyLocationInfo(this, fie);
+ operand = fie;
+ }
+
+ if (th.isSubType(type, BuiltInAtomicType.DOUBLE) ||
+ th.isSubType(type, BuiltInAtomicType.DATE) ||
+ th.isSubType(type, BuiltInAtomicType.TIME) ||
+ th.isSubType(type, BuiltInAtomicType.DATE_TIME) ||
+ th.isSubType(type, BuiltInAtomicType.DURATION)) {
+ return operand;
+ }
+ if (th.isSubType(type, BuiltInAtomicType.BOOLEAN) ||
+ th.isSubType(type, BuiltInAtomicType.STRING) ||
+ th.isSubType(type, BuiltInAtomicType.UNTYPED_ATOMIC) ||
+ th.isSubType(type, BuiltInAtomicType.FLOAT) ||
+ th.isSubType(type, BuiltInAtomicType.DECIMAL)) {
+ if (operand instanceof Literal) {
+ GroundedValue val = ((Literal)operand).getValue();
+ return Literal.makeLiteral(NumberFn.convert((AtomicValue)val, config));
+ } else {
+ return SystemFunctionCall.makeSystemFunction("number", new Expression[]{operand});
+ }
+ }
+ // If we can't determine the primitive type at compile time, we generate a run-time typeswitch
+
+ LetExpression let = new LetExpression();
+ let.setRequiredType(SequenceType.OPTIONAL_ATOMIC);
+ let.setVariableQName(new StructuredQName("nn", NamespaceConstant.SAXON, "nn" + let.hashCode()));
+ let.setSequence(operand);
+
+ LocalVariableReference var = new LocalVariableReference(let);
+ Expression isDouble = new InstanceOfExpression(
+ var, SequenceType.makeSequenceType(BuiltInAtomicType.DOUBLE, StaticProperty.ALLOWS_ZERO_OR_ONE));
+
+ var = new LocalVariableReference(let);
+ Expression isDecimal = new InstanceOfExpression(
+ var, SequenceType.makeSequenceType(BuiltInAtomicType.DECIMAL, StaticProperty.ALLOWS_ZERO_OR_ONE));
+
+ var = new LocalVariableReference(let);
+ Expression isFloat = new InstanceOfExpression(
+ var, SequenceType.makeSequenceType(BuiltInAtomicType.FLOAT, StaticProperty.ALLOWS_ZERO_OR_ONE));
+
+ var = new LocalVariableReference(let);
+ Expression isString = new InstanceOfExpression(
+ var, SequenceType.makeSequenceType(BuiltInAtomicType.STRING, StaticProperty.ALLOWS_ZERO_OR_ONE));
+
+ var = new LocalVariableReference(let);
+ Expression isUntypedAtomic = new InstanceOfExpression(
+ var, SequenceType.makeSequenceType(BuiltInAtomicType.UNTYPED_ATOMIC, StaticProperty.ALLOWS_ZERO_OR_ONE));
+
+ var = new LocalVariableReference(let);
+ Expression isBoolean = new InstanceOfExpression(
+ var, SequenceType.makeSequenceType(BuiltInAtomicType.BOOLEAN, StaticProperty.ALLOWS_ZERO_OR_ONE));
+
+ Expression condition = new OrExpression(isDouble, isDecimal);
+ condition = new OrExpression(condition, isFloat);
+ condition = new OrExpression(condition, isString);
+ condition = new OrExpression(condition, isUntypedAtomic);
+ condition = new OrExpression(condition, isBoolean);
+
+ var = new LocalVariableReference(let);
+ NumberFn fn = (NumberFn) SystemFunctionCall.makeSystemFunction("number", new Expression[]{var});
+
+ var = new LocalVariableReference(let);
+ var.setStaticType(SequenceType.SINGLE_ATOMIC, null, 0);
+ Expression action = Choose.makeConditional(condition, fn, var);
+ let.setAction(action);
+ return let;
+ }
+
+ /**
+ * Determine the data type of the expression, if this is known statically
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (calculator == null) {
+ return BuiltInAtomicType.ANY_ATOMIC; // type is not known statically
+ } else {
+ ItemType t1 = operand0.getItemType(th);
+ if (!(t1 instanceof AtomicType)) {
+ t1 = t1.getAtomizedItemType();
+ }
+ ItemType t2 = operand1.getItemType(th);
+ if (!(t2 instanceof AtomicType)) {
+ t2 = t2.getAtomizedItemType();
+ }
+ return calculator.getResultType((AtomicType) t1.getPrimitiveItemType(),
+ (AtomicType) t2.getPrimitiveItemType());
+ }
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ArithmeticExpression10 a2 = new ArithmeticExpression10(operand0.copy(), operator, operand1.copy());
+ a2.calculator = calculator;
+ return a2;
+ }
+
+ /**
+ * Evaluate the expression.
+ */
+
+ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+
+ Calculator calc = calculator;
+ AtomicValue v1 = (AtomicValue) operand0.evaluateItem(context);
+ if (v1 == null) {
+ return DoubleValue.NaN;
+ }
+
+ AtomicValue v2 = (AtomicValue) operand1.evaluateItem(context);
+ if (v2 == null) {
+ return DoubleValue.NaN;
+ }
+
+ if (calc == null) {
+ // This shouldn't happen. It's a fallback for a failure to assign the calculator earlier
+ // at compile time. It has been known to happen when simplify() is called without typeCheck().
+ calc = assignCalculator(v1.getPrimitiveType(), v2.getPrimitiveType(), true);
+ }
+
+ return calc.compute(v1, v2, context);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ Calculator calc = calculator;
+ AtomicValue v1 = (AtomicValue) arguments[0].head();
+ if (v1 == null) {
+ return DoubleValue.NaN;
+ }
+
+ AtomicValue v2 = (AtomicValue) arguments[1].head();
+ if (v2 == null) {
+ return DoubleValue.NaN;
+ }
+
+ if (calc == null) {
+ // This shouldn't happen. It's a fallback for a failure to assign the calculator earlier
+ // at compile time. It has been known to happen when simplify() is called without typeCheck().
+ calc = assignCalculator(v1.getPrimitiveType(), v2.getPrimitiveType(), true);
+ }
+
+ return calc.compute(v1, v2, context);
+ }
+}
+
diff --git a/sf/saxon/expr/Assignation.java b/sf/saxon/expr/Assignation.java
new file mode 100644
index 0000000..07b141f
--- /dev/null
+++ b/sf/saxon/expr/Assignation.java
@@ -0,0 +1,492 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+* Assignation is an abstract superclass for the kinds of expression
+* that declare range variables: for, some, and every.
+*/
+
+public abstract class Assignation extends Expression implements Binding {
+
+ protected int slotNumber = -999; // slot number for range variable
+ // (initialized to ensure a crash if no real slot is allocated)
+ protected Expression sequence; // the expression over which the variable ranges
+ protected Expression action; // the action performed for each value of the variable
+ protected StructuredQName variableName;
+ protected SequenceType requiredType;
+ int refCount = 2;
+
+ //protected RangeVariable declaration;
+
+
+
+ /**
+ * Set the required type (declared type) of the variable
+ * @param requiredType the required type
+ */
+ public void setRequiredType(SequenceType requiredType) {
+ this.requiredType = requiredType;
+ }
+
+ /**
+ * Set the name of the variable
+ * @param variableName the name of the variable
+ */
+
+ public void setVariableQName(StructuredQName variableName) {
+ this.variableName = variableName;
+ }
+
+
+ /**
+ * Get the name of the variable
+ * @return the variable name, as a QName
+ */
+
+ public StructuredQName getVariableQName() {
+ return variableName;
+ }
+
+ public StructuredQName getObjectName() {
+ return variableName;
+ }
+
+
+ /**
+ * Get the declared type of the variable
+ *
+ * @return the declared type
+ */
+
+ public SequenceType getRequiredType() {
+ return requiredType;
+ }
+
+ /**
+ * If the variable is bound to an integer, get the minimum and maximum possible values.
+ * Return null if unknown or not applicable
+ */
+ public IntegerValue[] getIntegerBoundsForVariable() {
+ return sequence.getIntegerBounds();
+ }
+
+ /**
+ * If this is a local variable held on the local stack frame, return the corresponding slot number.
+ * In other cases, return -1.
+ */
+
+ public int getLocalSlotNumber() {
+ return slotNumber;
+ }
+
+ /**
+ * Get the value of the range variable
+ */
+
+ public Sequence evaluateVariable(XPathContext context) throws XPathException {
+ Sequence actual = context.evaluateLocalVariable(slotNumber);
+ if (!(actual instanceof GroundedValue || actual instanceof NodeInfo)) {
+ actual = SequenceTool.toGroundedValue(actual);
+ context.setLocalVariable(slotNumber, actual);
+ }
+ return actual;
+ }
+
+ /**
+ * Add the "return" or "satisfies" expression, and fix up all references to the
+ * range variable that occur within that expression
+ * @param action the expression that occurs after the "return" keyword of a "for"
+ * expression, the "satisfies" keyword of "some/every", or the ":=" operator of
+ * a "let" expression.
+ *
+ *
+ */
+
+ public void setAction(Expression action) {
+ this.action = action;
+ adoptChildExpression(action);
+ }
+
+ /**
+ * Indicate whether the binding is local or global. A global binding is one that has a fixed
+ * value for the life of a query or transformation; any other binding is local.
+ */
+
+ public final boolean isGlobal() {
+ return false;
+ }
+
+ /**
+ * Test whether it is permitted to assign to the variable using the saxon:assign
+ * extension element. This will only be for an XSLT global variable where the extra
+ * attribute saxon:assignable="yes" is present.
+ */
+
+ public final boolean isAssignable() {
+ return false;
+ }
+
+ /**
+ * Check to ensure that this expression does not contain any inappropriate updating subexpressions.
+ * This check is overridden for those expressions that permit updating subexpressions.
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression has a non-permitted updateing subexpression
+ */
+
+ public void checkForUpdatingSubexpressions() throws XPathException {
+ sequence.checkForUpdatingSubexpressions();
+ if (sequence.isUpdatingExpression()) {
+ XPathException err = new XPathException(
+ "An updating expression cannot be used to initialize a variable", "XUST0001");
+ err.setLocator(sequence);
+ throw err;
+ }
+ action.checkForUpdatingSubexpressions();
+ }
+
+ /**
+ * Determine whether this is an updating expression as defined in the XQuery update specification
+ * @return true if this is an updating expression
+ */
+
+ public boolean isUpdatingExpression() {
+ return action.isUpdatingExpression();
+ }
+
+ /**
+ * Get the action expression
+ * @return the action expression (introduced by "return" or "satisfies")
+ */
+
+ public Expression getAction() {
+ return action;
+ }
+
+ /**
+ * Set the "sequence" expression - the one to which the variable is bound
+ * @param sequence the expression to which the variable is bound
+ */
+
+ public void setSequence(Expression sequence) {
+ this.sequence = sequence;
+ adoptChildExpression(sequence);
+ }
+
+ /**
+ * Get the "sequence" expression - the one to which the variable is bound
+ * @return the expression to which the variable is bound
+ */
+
+ public Expression getSequence() {
+ return sequence;
+ }
+
+ /**
+ * Set the slot number for the range variable
+ * @param nr the slot number to be used
+ */
+
+ public void setSlotNumber(int nr) {
+ slotNumber = nr;
+ }
+
+ /**
+ * Get the number of slots required. Normally 1, except for a FOR expression with an AT clause, where it is 2.
+ * @return the number of slots required
+ */
+
+ public int getRequiredSlots() {
+ return 1;
+ }
+
+ /**
+ * Simplify the expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ sequence = visitor.simplify(sequence);
+ action = visitor.simplify(action);
+ return this;
+ }
+
+ public boolean hasVariableBinding(Binding binding){
+
+ return this == binding;
+ }
+
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ sequence = doPromotion(sequence, offer);
+ if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES ||
+ offer.action == PromotionOffer.UNORDERED ||
+ offer.action == PromotionOffer.REPLACE_CURRENT) {
+ action = doPromotion(action, offer);
+ } else if (offer.action == PromotionOffer.RANGE_INDEPENDENT ||
+ offer.action == PromotionOffer.FOCUS_INDEPENDENT) {
+ // Pass the offer to the action expression only if the action isn't dependent on the
+ // variable bound by this assignation
+ Binding[] savedBindingList = offer.bindingList;
+ offer.bindingList = extendBindingList(offer.bindingList);
+ action = doPromotion(action, offer);
+ offer.bindingList = savedBindingList;
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Suppress validation on contained element constructors, on the grounds that the parent element
+ * is already performing validation. The default implementation does nothing.
+ */
+
+ public void suppressValidation(int validationMode) {
+ action.suppressValidation(validationMode);
+ }
+
+ /**
+ * Extend an array of variable bindings to include the binding(s) defined in this expression
+ * @param in a set of variable bindings
+ * @return a set of variable bindings including all those supplied plus this one
+ */
+
+ public Binding[] extendBindingList(/*@Nullable*/ Binding[] in) {
+ Binding[] newBindingList;
+ if (in == null) {
+ newBindingList = new Binding[1];
+ } else {
+ newBindingList = new Binding[in.length + 1];
+ System.arraycopy(in, 0, newBindingList, 0, in.length);
+ }
+ newBindingList[newBindingList.length - 1] = this;
+ return newBindingList;
+ }
+
+ /**
+ * Get the immediate subexpressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(sequence, action);
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ SubExpressionInfo sequenceInfo = new SubExpressionInfo(sequence, true, false, INSPECTION_CONTEXT);
+ SubExpressionInfo actionInfo = new SubExpressionInfo(action, true, !(this instanceof LetExpression), INHERITED_CONTEXT);
+ return new PairIterator<SubExpressionInfo>(sequenceInfo, actionInfo);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (sequence == original) {
+ sequence = replacement;
+ found = true;
+ }
+ if (action == original) {
+ action = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet varPath = sequence.addToPathMap(pathMap, pathMapNodeSet);
+ pathMap.registerPathForVariable(this, varPath);
+ return action.addToPathMap(pathMap, pathMapNodeSet);
+ }
+
+ /**
+ * Get the display name of the range variable, for diagnostics only
+ * @return the lexical QName of the range variable. For system allocated
+ * variables, the conventional namespace prefix "zz" is used.
+ */
+
+ public String getVariableName() {
+ if (variableName == null) {
+ return "zz:var" + hashCode();
+ } else {
+ return variableName.getDisplayName();
+ }
+ }
+
+ /**
+ * Get the name of the range variable as a Name or EQName.
+ * @return the name of the range variable. For system allocated
+ * variables, the namespace "http://ns.saxonica.com/anonymous-var"
+ * is used. For names in no namespace, the local name alone is used
+ */
+
+ public String getVariableEQName() {
+ if (variableName == null) {
+ return "Q{http://ns.saxonica.com/anonymous-var}var" + hashCode();
+ } else if (variableName.isInNamespace("")) {
+ return variableName.getLocalPart();
+ } else {
+ return variableName.getEQName();
+ }
+ }
+
+ /**
+ * Refine the type information associated with this variable declaration. This is useful when the
+ * type of the variable has not been explicitly declared (which is common); the variable then takes
+ * a static type based on the type of the expression to which it is bound. The effect of this call
+ * is to update the static expression type for all references to this variable.
+ * @param type the inferred item type of the expression to which the variable is bound
+ * @param cardinality the inferred cardinality of the expression to which the variable is bound
+ * @param constantValue the constant value to which the variable is bound (null if there is no constant value)
+ * @param properties other static properties of the expression to which the variable is bound
+ * @param visitor an expression visitor to provide context information
+ * @param currentExpression the expression that binds the variable
+ */
+
+ public void refineTypeInformation(ItemType type, int cardinality,
+ GroundedValue constantValue, int properties,
+ ExpressionVisitor visitor,
+ Assignation currentExpression) {
+ List references = new ArrayList();
+ ExpressionTool.gatherVariableReferences(currentExpression.getAction(), this, references);
+ for (Iterator iter=references.iterator(); iter.hasNext();) {
+ BindingReference ref = (BindingReference)iter.next();
+ if (ref instanceof VariableReference) {
+ ((VariableReference)ref).refineVariableType(type, cardinality, constantValue, properties, visitor);
+ visitor.resetStaticProperties();
+ }
+ }
+ }
+
+ /**
+ * Register a variable reference that refers to the variable bound in this expression
+ * @param isLoopingReference - true if the reference occurs within a loop, such as the predicate
+ * of a filter expression
+ */
+
+ public void addReference(boolean isLoopingReference) {
+ if (refCount != FilterExpression.FILTERED) {
+ refCount += (isLoopingReference ? 10 : 1);
+ }
+ }
+
+ /**
+ * Get the (nominal) count of the number of references to this variable
+ * @return zero if there are no references, one if there is a single reference that is not in
+ * a loop, some higher number if there are multiple references (or a single reference in a loop),
+ * or the special value @link RangeVariable#FILTERED} if there are any references
+ * in filter expressions that require searching.
+ */
+
+ public int getNominalReferenceCount() {
+ return refCount;
+ }
+
+ /**
+ * Test whether the variable bound by this let expression should be indexable
+ * @return true if the variable should be indexable
+ */
+
+ public boolean isIndexedVariable() {
+ return (refCount == FilterExpression.FILTERED);
+ }
+
+ /**
+ * Replace all references to the variable bound by this let expression,
+ * that occur within the action expression, with the given expression
+ *
+ * @param opt The optimizer
+ * @param seq the expression
+ * @throws net.sf.saxon.trans.XPathException
+ */
+
+ public void replaceVariable(Optimizer opt, Expression seq) throws XPathException {
+ PromotionOffer offer2 = new PromotionOffer(opt);
+ offer2.action = PromotionOffer.INLINE_VARIABLE_REFERENCES;
+ offer2.bindingList = new Binding[] {this};
+ offer2.containingExpression = seq;
+ action = doPromotion(action, offer2);
+ if (offer2.accepted) {
+ // there might be further references to the variable
+ offer2.accepted = false;
+ replaceVariable(opt, seq);
+ }
+ if (isIndexedVariable() && seq instanceof VariableReference) {
+ Binding newBinding = ((VariableReference) seq).getBinding();
+ if (newBinding instanceof Assignation) {
+ ((Assignation) newBinding).setIndexedVariable();
+ }
+ }
+ }
+
+ /**
+ * Indicate that the variable bound by this let expression should be indexable
+ * (because it is used in an appropriate filter expression)
+ */
+
+ public void setIndexedVariable() {
+ refCount = FilterExpression.FILTERED;
+ }
+}
+
diff --git a/sf/saxon/expr/AtomicSequenceConverter.java b/sf/saxon/expr/AtomicSequenceConverter.java
new file mode 100644
index 0000000..c55b75d
--- /dev/null
+++ b/sf/saxon/expr/AtomicSequenceConverter.java
@@ -0,0 +1,381 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.AtomicSequenceConverterCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.AtomicSequenceConverterAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.SequenceExtent;
+import net.sf.saxon.value.StringValue;
+
+import java.util.List;
+
+
+/**
+ * An AtomicSequenceConverter is an expression that performs a cast on each member of
+ * a supplied sequence
+ */
+
+public class AtomicSequenceConverter extends UnaryExpression {
+
+ public static ToStringMappingFunction TO_STRING_MAPPER = new ToStringMappingFunction();
+
+ private PlainType requiredItemType;
+ /*@Nullable*/ protected Converter converter;
+
+ /**
+ * Constructor
+ *
+ * @param sequence this must be a sequence of atomic values. This is not checked; a ClassCastException
+ * will occur if the precondition is not satisfied.
+ * @param requiredItemType the item type to which all items in the sequence should be converted,
+ */
+
+ public AtomicSequenceConverter(Expression sequence, PlainType requiredItemType) {
+ super(sequence);
+ this.requiredItemType = requiredItemType;
+ }
+
+
+ public void allocateConverter(Configuration config, boolean allowNull) {
+
+ ItemType sourceType = operand.getItemType(config.getTypeHierarchy());
+ final ConversionRules rules = config.getConversionRules();
+ if (sourceType instanceof ErrorType) {
+ converter = Converter.IDENTITY_CONVERTER;
+ } else if (!(sourceType instanceof AtomicType)) {
+ converter = null;
+ } else if (requiredItemType instanceof AtomicType) {
+ converter = rules.getConverter((AtomicType) sourceType, (AtomicType)requiredItemType);
+ } else if (((SimpleType)requiredItemType).isUnionType()) {
+ converter = new StringConverter.StringToUnionConverter(requiredItemType, rules);
+ }
+
+ if (converter == null && !allowNull) {
+ // source type not known statically; create a converter that decides at run-time
+ converter = new Converter(rules) {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ Converter converter = rules.getConverter(input.getPrimitiveType(), (AtomicType)requiredItemType);
+ if (converter == null) {
+ return new ValidationFailure("Cannot convert value from " + input.getPrimitiveType() + " to " + requiredItemType);
+ } else {
+ return converter.convert(input);
+ }
+ }
+ };
+ }
+
+ }
+
+ /**
+ * Get the required item type (the target type of the conversion
+ * @return the required item type
+ */
+
+ public PlainType getRequiredItemType() {
+ return requiredItemType;
+ }
+
+ /**
+ * Get the converter used to convert the items in the sequence
+ *
+ * @return the converter. Note that a converter is always allocated during the typeCheck() phase,
+ * even if the source type is not known.
+ */
+
+ public Converter getConverter() {
+ return converter;
+ }
+
+ public void setConverter(Converter converter) {
+ this.converter = converter;
+ }
+
+ /**
+ * Simplify an expression
+ *
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ operand = visitor.simplify(operand);
+ if (operand instanceof Literal && requiredItemType instanceof AtomicType) {
+ if (Literal.isEmptySequence(operand)) {
+ return operand;
+ }
+ Configuration config = visitor.getConfiguration();
+ allocateConverter(config, true);
+ if (converter != null) {
+ GroundedValue val = SequenceExtent.makeSequenceExtent(
+ iterate(visitor.getStaticContext().makeEarlyEvaluationContext()));
+ return Literal.makeLiteral(val);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ Configuration config = visitor.getConfiguration();
+ final TypeHierarchy th = config.getTypeHierarchy();
+ if (th.isSubType(operand.getItemType(th), requiredItemType)) {
+ return operand;
+ } else {
+ if (converter == null) {
+ allocateConverter(config, true);
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ */
+ /*@NotNull*/
+ @Override
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (operand instanceof UntypedSequenceConverter) {
+ UntypedSequenceConverter asc = (UntypedSequenceConverter)operand;
+ ItemType ascType = asc.getItemType(th);
+ if (ascType == requiredItemType) {
+ return operand;
+ } else if ((requiredItemType == BuiltInAtomicType.STRING || requiredItemType == BuiltInAtomicType.UNTYPED_ATOMIC) &&
+ (ascType == BuiltInAtomicType.STRING || ascType == BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ UntypedSequenceConverter old = (UntypedSequenceConverter)operand;
+ UntypedSequenceConverter asc2 = new UntypedSequenceConverter(
+ old.getBaseExpression(),
+ requiredItemType);
+ return asc2.typeCheck(visitor, contextItemType)
+ .optimize(visitor, contextItemType);
+ }
+ } else if (operand instanceof AtomicSequenceConverter) {
+ AtomicSequenceConverter asc = (AtomicSequenceConverter)operand;
+ ItemType ascType = asc.getItemType(th);
+ if (ascType == requiredItemType) {
+ return operand;
+ } else if ((requiredItemType == BuiltInAtomicType.STRING || requiredItemType == BuiltInAtomicType.UNTYPED_ATOMIC) &&
+ (ascType == BuiltInAtomicType.STRING || ascType == BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ AtomicSequenceConverter old = (AtomicSequenceConverter)operand;
+ AtomicSequenceConverter asc2 = new AtomicSequenceConverter(
+ old.getBaseExpression(),
+ requiredItemType
+ );
+ return asc2.typeCheck(visitor, contextItemType)
+ .optimize(visitor, contextItemType);
+ }
+ }
+ return this;
+
+ }
+
+ /**
+ * Determine the special properties of this expression
+ *
+ * @return {@link StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ return super.computeSpecialProperties() | StaticProperty.NON_CREATIVE;
+ }
+
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(syntacticContext, allowExtensions, reasons);
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public AtomicSequenceConverterAdjunct getStreamingAdjunct() {
+ return new AtomicSequenceConverterAdjunct();
+ }
+
+//#endif
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ AtomicSequenceConverter atomicConverter = new AtomicSequenceConverter(getBaseExpression().copy(), requiredItemType);
+ atomicConverter.setConverter(converter);
+ return atomicConverter;
+ }
+
+ /**
+ * Iterate over the sequence of values
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends AtomicValue> iterate(final XPathContext context) throws XPathException {
+ SequenceIterator base = operand.iterate(context);
+ if (converter == null) {
+ allocateConverter(context.getConfiguration(), false);
+ }
+ if (converter == Converter.TO_STRING) {
+ return new ItemMappingIterator(base, TO_STRING_MAPPER, true);
+ } else {
+ AtomicSequenceMappingFunction mapper = new AtomicSequenceMappingFunction();
+ mapper.setConverter(converter);
+ return new ItemMappingIterator(base, mapper, true);
+ }
+ }
+
+ /**
+ * Mapping function wrapped around a converter
+ */
+
+ public static class AtomicSequenceMappingFunction
+ implements ItemMappingFunction<Item, AtomicValue> {
+ private Converter converter;
+
+ public void setConverter(Converter converter) {
+ this.converter = converter;
+ }
+
+ public AtomicValue mapItem(Item item) throws XPathException {
+ return converter.convert((AtomicValue)item).asAtomic();
+ }
+ }
+
+ /**
+ * Mapping function that converts every item in a sequence to a string
+ */
+
+ public static class ToStringMappingFunction
+ implements ItemMappingFunction<Item, StringValue> {
+ public StringValue mapItem(Item item) throws XPathException {
+ return StringValue.makeStringValue(item.getStringValueCS());
+ }
+ }
+
+
+ /**
+ * Evaluate as an Item. This should only be called if the AtomicSequenceConverter has cardinality zero-or-one
+ */
+
+ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+ AtomicValue item = (AtomicValue)operand.evaluateItem(context);
+ if (item == null) {
+ return null;
+ }
+ return converter.convert(item).asAtomic();
+ }
+
+ /**
+ * Determine the data type of the items returned by the expression, if possible
+ *
+ * @param th the type hierarchy cache
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
+ * or Type.ITEM (meaning not known in advance)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return requiredItemType;
+ }
+
+ /**
+ * Determine the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ return operand.getCardinality();
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return super.equals(other) &&
+ requiredItemType.equals(((AtomicSequenceConverter) other).requiredItemType);
+ }
+
+ /**
+ * get HashCode for comparing two expressions. Note that this hashcode gives the same
+ * result for (A op B) and for (B op A), whether or not the operator is commutative.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ requiredItemType.hashCode();
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new AtomicSequenceConverterCompiler();
+ }
+ //#endif
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ *
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+ @Override
+ public String getExpressionName() {
+ return "convertItems";
+ }
+
+ @Override
+ protected String displayOperator(Configuration config) {
+ return "convertItems";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("convertItems");
+ destination.emitAttribute("to", requiredItemType.toString());
+ operand.explain(destination);
+ destination.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/Atomizer.java b/sf/saxon/expr/Atomizer.java
new file mode 100644
index 0000000..4c8630b
--- /dev/null
+++ b/sf/saxon/expr/Atomizer.java
@@ -0,0 +1,602 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.AtomizerCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.AtomizerAdjunct;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.expr.instruct.Block;
+import net.sf.saxon.expr.instruct.Choose;
+import net.sf.saxon.expr.instruct.ValueOf;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.*;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.ObjectValue;
+import net.sf.saxon.value.StringValue;
+
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+* An Atomizer is an expression corresponding essentially to the fn:data() function: it
+* maps a sequence by replacing nodes with their typed values
+*/
+
+public final class Atomizer extends UnaryExpression {
+
+ private boolean untyped = false; //set to true if it is known that the nodes being atomized will be untyped
+ private boolean singleValued = false; // set to true if all atomized nodes will atomize to a single atomic value
+ /*@Nullable*/ private ItemType operandItemType = null;
+
+ /**
+ * Constructor
+ * @param sequence the sequence to be atomized
+ * */
+
+ public Atomizer(Expression sequence) {
+ super(sequence);
+ sequence.setFlattened(true);
+ }
+
+ /**
+ * Make an atomizer with a given operand
+ * @param sequence the operand
+ * @return an Atomizer that atomizes the given operand, or another expression that returns the same result
+ */
+
+ public static Expression makeAtomizer(Expression sequence) {
+ if (sequence instanceof Literal && ((Literal)sequence).getValue() instanceof AtomicSequence) {
+ return sequence;
+ } else {
+ return new Atomizer(sequence);
+ }
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided directly. The other methods will always be available
+ * indirectly, using an implementation that relies on one of the other methods.
+ * @return the implementation method, for example {@link #ITERATE_METHOD} or {@link #EVALUATE_METHOD} or
+ * {@link #PROCESS_METHOD}
+ */
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | WATCH_METHOD;
+ }
+
+ public ItemType getOperandItemType(TypeHierarchy th) {
+ if (operandItemType == null) {
+ operandItemType = operand.getItemType(th);
+ }
+ return operandItemType;
+ }
+
+ /**
+ * Simplify an expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ untyped = !visitor.getExecutable().isSchemaAware();
+ computeSingleValued(visitor.getConfiguration().getTypeHierarchy());
+ operand = visitor.simplify(operand);
+ if (operand instanceof Literal) {
+ GroundedValue val = ((Literal)operand).getValue();
+
+ if (val instanceof AtomicValue) {
+ return operand;
+ }
+ SequenceIterator iter = val.iterate();
+ while (true) {
+ // if all items in the sequence are atomic (they generally will be, since this is
+ // done at compile time), then return the sequence
+ Item i = iter.next();
+ if (i == null) {
+ return operand;
+ }
+ if (i instanceof NodeInfo) {
+ return this;
+ }
+ if (i instanceof FunctionItem) {
+ XPathException err = new XPathException("Cannot atomize a function item", "FOTY0013");
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ } else if (operand instanceof ValueOf && (((ValueOf)operand).getOptions()& ReceiverOptions.DISABLE_ESCAPING) == 0) {
+ // XSLT users tend to use ValueOf unnecessarily
+ return ((ValueOf)operand).convertToCastAsString();
+ }
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ untyped = !visitor.getExecutable().isSchemaAware();
+ operand = visitor.typeCheck(operand, contextItemType);
+ // If the configuration allows typed data, check whether the content type of these particular nodes is untyped
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ computeSingleValued(th);
+ visitor.resetStaticProperties();
+ ItemType operandType = operand.getItemType(th);
+ if (th.isSubType(operandType, BuiltInAtomicType.ANY_ATOMIC)) {
+ return operand;
+ }
+ if (!operandType.isAtomizable()) {
+ XPathException err;
+ if (operandType instanceof FunctionItemType) {
+ err = new XPathException(
+ "Cannot atomize a function item", "FOTY0013");
+ } else {
+ err = new XPathException(
+ "Cannot atomize an element that is defined in the schema to have element-only content", "FOTY0012");
+ }
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ operand.setFlattened(true);
+ return this;
+ }
+
+ private void computeSingleValued(TypeHierarchy th) {
+ singleValued = untyped;
+ if (!singleValued) {
+ ItemType nodeType = operand.getItemType(th);
+ if (nodeType instanceof NodeTest) {
+ SchemaType st = ((NodeTest)nodeType).getContentType();
+ if (st == Untyped.getInstance() || st.isAtomicType() || (st.isComplexType() && st != AnyType.getInstance())) {
+ singleValued = true;
+ }
+ int mask = ((NodeTest)nodeType).getNodeKindMask();
+ if ((mask & (1<<Type.ELEMENT | 1<<Type.ATTRIBUTE)) == 0) {
+ singleValued = true;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression exp = super.optimize(visitor, contextItemType);
+ if (exp == this) {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (th.isSubType(operand.getItemType(th), BuiltInAtomicType.ANY_ATOMIC)) {
+ return operand;
+ }
+ if (operand instanceof ValueOf && (((ValueOf)operand).getOptions()& ReceiverOptions.DISABLE_ESCAPING) == 0) {
+ // XSLT users tend to use ValueOf unnecessarily
+ Expression cast = ((ValueOf)operand).convertToCastAsString();
+ return cast.optimize(visitor, contextItemType);
+ }
+ if (operand instanceof LetExpression || operand instanceof ForExpression) {
+ // replace data(let $x := y return z) by (let $x := y return data(z))
+ Expression action = ((Assignation)operand).getAction();
+ ((Assignation)operand).setAction(new Atomizer(action));
+ return operand.optimize(visitor, contextItemType);
+ }
+ if (operand instanceof Choose) {
+ // replace data(if x then y else z) by (if x then data(y) else data(z)
+ Expression[] actions = ((Choose)operand).getActions();
+ for (int i=0; i<actions.length; i++) {
+ actions[i] = new Atomizer(actions[i]);
+ }
+ return operand.optimize(visitor, contextItemType);
+ }
+ if (operand instanceof Block) {
+ // replace data((x,y,z)) by (data(x), data(y), data(z)) as some of the atomizers
+ // may prove to be redundant. (Also, it helps streaming)
+ Expression[] children = ((Block)operand).getChildren();
+ Expression[] atomizedChildren = new Expression[children.length];
+ for (int i=0; i<children.length; i++) {
+ atomizedChildren[i] = new Atomizer(children[i]);
+ }
+ Block newBlock = new Block();
+ newBlock.setChildren(atomizedChildren);
+ return newBlock.typeCheck(visitor, contextItemType).optimize(visitor, contextItemType);
+ }
+ }
+ return exp;
+ }
+
+ /**
+ * Ask whether it is known that any nodes in the input will always be untyped
+ * @return true if it is known that all nodes in the input will be untyped
+ */
+ public boolean isUntyped() {
+ return untyped;
+ }
+
+
+ /**
+ * Determine the special properties of this expression
+ * @return {@link StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ p &= ~StaticProperty.NODESET_PROPERTIES;
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Atomizer copy = new Atomizer(getBaseExpression().copy());
+ copy.untyped = untyped;
+ copy.singleValued = singleValued;
+ return copy;
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new MonoIterator<SubExpressionInfo>(
+ new SubExpressionInfo(getBaseExpression(), true, false, NODE_VALUE_CONTEXT));
+ }
+
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(NODE_VALUE_CONTEXT, allowExtensions, reasons);
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public AtomizerAdjunct getStreamingAdjunct() {
+ return new AtomizerAdjunct();
+ }
+
+//#endif
+
+ /**
+ * Iterate over the sequence of values
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends AtomicValue> iterate(XPathContext context) throws XPathException {
+ SequenceIterator base = operand.iterate(context);
+ return getAtomizingIterator(base, untyped);
+ }
+
+ /**
+ * Evaluate as an Item. This should only be called if the Atomizer has cardinality zero-or-one,
+ * which will only be the case if the underlying expression has cardinality zero-or-one.
+ */
+
+ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+ Item i = operand.evaluateItem(context);
+ if (i==null) {
+ return null;
+ }
+ if (i instanceof NodeInfo) {
+ return ((NodeInfo) i).atomize().head();
+ } else if (i instanceof AtomicValue) {
+ return (AtomicValue)i;
+ } else if (i instanceof ObjectValue) {
+ return StringValue.makeStringValue(i.getStringValue());
+ } else {
+ throw new XPathException("Cannot atomize a function item", "FOTY0013");
+ }
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ super.process(context); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ /**
+ * Determine the data type of the items returned by the expression, if possible
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER. For this class, the
+ * result is always an atomic type, but it might be more specific.
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ operandItemType = operand.getItemType(th);
+ return getAtomizedItemType(operand, untyped, th);
+ }
+
+ /**
+ * Compute the type that will result from atomizing the result of a given expression
+ * @param operand the given expression
+ * @param alwaysUntyped true if it is known that nodes will always be untyped
+ * @param th the type hierarchy cache
+ * @return the item type of the result of evaluating the operand expression, after atomization, or
+ * null if it is known that atomization will return an error
+ */
+
+ public static ItemType getAtomizedItemType(Expression operand, boolean alwaysUntyped, TypeHierarchy th) {
+ ItemType in = operand.getItemType(th);
+ if (in.isPlainType()) {
+ return in;
+ } else if (in instanceof NodeTest) {
+
+ if (in instanceof ErrorType) {
+ return in;
+ }
+ int kinds = ((NodeTest)in).getNodeKindMask();
+ if (alwaysUntyped) {
+ // Some node-kinds always have a typed value that's a string
+
+ if ((kinds | STRING_KINDS) == STRING_KINDS) {
+ return BuiltInAtomicType.STRING;
+ }
+ // Some node-kinds are always untyped atomic; some are untypedAtomic provided that the configuration
+ // is untyped
+
+ if ((kinds | UNTYPED_IF_UNTYPED_KINDS) == UNTYPED_IF_UNTYPED_KINDS) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ } else {
+ if ((kinds | UNTYPED_KINDS) == UNTYPED_KINDS) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ }
+
+ return in.getAtomizedItemType();
+ } else if (in instanceof ExternalObjectType) {
+ return in.getAtomizedItemType();
+ } else if (in instanceof FunctionItemType) {
+ return null;
+ }
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+
+ /**
+ * Node kinds whose typed value is always a string
+ */
+ private static final int STRING_KINDS =
+ (1<<Type.NAMESPACE) | (1<<Type.COMMENT) | (1<<Type.PROCESSING_INSTRUCTION);
+
+ /**
+ * Node kinds whose typed value is always untypedAtomic
+ */
+
+ private static final int UNTYPED_KINDS =
+ (1<<Type.TEXT) | (1<<Type.DOCUMENT);
+
+ /**
+ * Node kinds whose typed value is untypedAtomic if the configuration is untyped
+ */
+
+ private static final int UNTYPED_IF_UNTYPED_KINDS =
+ (1<<Type.TEXT) | (1<<Type.ELEMENT) | (1<<Type.DOCUMENT) | (1<<Type.ATTRIBUTE);
+
+ /**
+ * Determine the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ if (untyped || singleValued) {
+ return operand.getCardinality();
+ } else {
+ if (Cardinality.allowsMany(operand.getCardinality())) {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ ItemType in = operandItemType;
+ if (in == null) {
+ try {
+ in = operand.getItemType(getContainer().getExecutable().getConfiguration().getTypeHierarchy());
+ } catch (NullPointerException npe) {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ }
+ if (in.isPlainType()) {
+ return operand.getCardinality();
+ }
+ if (in instanceof NodeTest) {
+ SchemaType schemaType = ((NodeTest)in).getContentType();
+ if (schemaType.isAtomicType()) {
+ // can return at most one atomic value per node
+ return operand.getCardinality();
+ }
+ }
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet result = operand.addToPathMap(pathMap, pathMapNodeSet);
+ if (result != null) {
+ TypeHierarchy th = getExecutable().getConfiguration().getTypeHierarchy();
+ ItemType operandItemType = operand.getItemType(th);
+ if (th.relationship(NodeKindTest.ELEMENT, operandItemType) != TypeHierarchy.DISJOINT ||
+ th.relationship(NodeKindTest.DOCUMENT, operandItemType) != TypeHierarchy.DISJOINT) {
+ result.setAtomized();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get an iterator that returns the result of atomizing the sequence delivered by the supplied
+ * iterator
+ * @param base the supplied iterator, the input to atomization
+ * @param untyped this can safely be set to true if it is known that all nodes in the base sequence will
+ * be untyped; but it is always OK to set it to false.
+ * @return an iterator that returns atomic values, the result of the atomization
+ * @throws net.sf.saxon.trans.XPathException if a dynamic evaluation error occurs
+ */
+
+ public static SequenceIterator<? extends AtomicValue> getAtomizingIterator(SequenceIterator base, boolean untyped) throws XPathException {
+ if (base instanceof AxisIterator) {
+ return new AxisAtomizingIterator((AxisIterator)base);
+ }
+ if ((base.getProperties() & SequenceIterator.LAST_POSITION_FINDER) != 0) {
+ int count = ((LastPositionFinder)base).getLength();
+ if (count == 0) {
+ return EmptyIterator.emptyIterator();
+ } else if (count == 1) {
+ Item first = base.next();
+ assert first != null;
+ if (first instanceof AtomicValue) {
+ return SingletonIterator.makeIterator((AtomicValue)first);
+ } else if (first instanceof NodeInfo) {
+ return ((NodeInfo)first).atomize().iterate();
+ } else if (first instanceof ObjectValue) {
+ return SingletonIterator.makeIterator(StringValue.makeStringValue(first.getStringValue()));
+ } else {
+ throw new XPathException("Attempting to atomize a function item", "FOTY0013");
+ }
+ }
+ }
+ //return new MappingIterator(base, AtomizingFunction.getInstance());
+ if (untyped) {
+ return new UntypedAtomizingIterator(base);
+ } else {
+ return new AtomizingIterator(base);
+ }
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Atomizer expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new AtomizerCompiler();
+ }
+ //#endif
+
+
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public String getExpressionName() {
+ return "data";
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+ @Override
+ public String toString() {
+ return "data(" + operand.toString() + ")";
+ }
+
+ /**
+ * Implement the mapping function. This is stateless, so there is a singleton instance.
+ */
+
+ public static class AtomizingFunction implements MappingFunction<Item, AtomicValue> {
+
+ /**
+ * Private constructor, ensuring that everyone uses the singleton instance
+ */
+
+ private AtomizingFunction(){}
+
+ private static final AtomizingFunction theInstance = new AtomizingFunction();
+
+ /**
+ * Get the singleton instance
+ * @return the singleton instance of this mapping function
+ */
+
+ public static AtomizingFunction getInstance() {
+ return theInstance;
+ }
+
+ public SequenceIterator map(Item item) throws XPathException {
+ if (item instanceof NodeInfo) {
+ return ((NodeInfo)item).atomize().iterate();
+ } else if (item instanceof AtomicValue) {
+ return SingletonIterator.makeIterator((AtomicValue)item);
+ } else {
+ throw new XPathException("Cannot atomize a function item or external object", "FOTY0013");
+ }
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/AxisAtomizingIterator.java b/sf/saxon/expr/AxisAtomizingIterator.java
new file mode 100644
index 0000000..dfdf742
--- /dev/null
+++ b/sf/saxon/expr/AxisAtomizingIterator.java
@@ -0,0 +1,113 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+* This iterator returns a sequence of atomic values, the result of atomizing the sequence
+ * of nodes returned by an underlying AxisIterator.
+*/
+
+public final class AxisAtomizingIterator implements SequenceIterator<AtomicValue> {
+
+ private AxisIterator base;
+ /*@Nullable*/ private SequenceIterator results = null;
+ private AtomicValue current = null;
+ private int position = 0;
+
+ /**
+ * Construct an atomizing iterator
+ * @param base the base iterator (whose nodes are to be atomized)
+ */
+
+ public AxisAtomizingIterator(AxisIterator base) {
+ this.base = base;
+ }
+
+ public AtomicValue next() throws XPathException {
+ AtomicValue nextItem;
+ while (true) {
+ if (results != null) {
+ nextItem = (AtomicValue)results.next();
+ if (nextItem != null) {
+ break;
+ } else {
+ results = null;
+ }
+ }
+ // Avoid calling next() to materialize the NodeInfo object
+ if (base.moveNext()) {
+ Sequence atomized = base.atomize();
+ if (atomized instanceof AtomicValue) {
+ // common case (the atomized value of the node is a single atomic value)
+ results = null;
+ nextItem = (AtomicValue)atomized;
+ break;
+ } else {
+ results = atomized.iterate();
+ nextItem = (AtomicValue)results.next();
+ if (nextItem == null) {
+ results = null;
+ } else {
+ break;
+ }
+ }
+ // now go round the loop to get the next item from the base sequence
+ } else {
+ results = null;
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+
+ current = nextItem;
+ position++;
+ return nextItem;
+ }
+
+ public AtomicValue current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<AtomicValue> getAnother() {
+ // System.err.println(this + " getAnother() ");
+ AxisIterator newBase = base.getAnother();
+ return new AxisAtomizingIterator(newBase);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED}, {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/expr/AxisExpression.java b/sf/saxon/expr/AxisExpression.java
new file mode 100644
index 0000000..aef33ac
--- /dev/null
+++ b/sf/saxon/expr/AxisExpression.java
@@ -0,0 +1,1073 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.AxisExpressionCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.z.IntHashSet;
+import net.sf.saxon.z.IntIterator;
+import net.sf.saxon.z.IntSet;
+
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * An AxisExpression is always obtained by simplifying a PathExpression.
+ * It represents a PathExpression that starts either at the context node, and uses
+ * a simple node-test with no filters. For example "*", "title", "./item",
+ * "@*", or "ancestor::chapter*".
+ * <p/>
+ * <p>An AxisExpression delivers nodes in axis order (not in document order).
+ * To get nodes in document order, in the case of a reverse axis, the expression
+ * should be wrapped in a call on reverse().</p>
+ */
+
+public final class AxisExpression extends Expression {
+
+ private byte axis;
+ /*@Nullable*/
+ private NodeTest test;
+ /*@Nullable*/
+ private ItemType itemType = null;
+ private ItemType contextItemType = null;
+ private int computedCardinality = -1;
+ private boolean doneWarnings = false;
+ private boolean contextMaybeUndefined = true;
+
+ /**
+ * Constructor for an AxisExpression whose origin is the context item
+ *
+ * @param axis The axis to be used in this AxisExpression: relevant constants are defined
+ * in class {@link net.sf.saxon.om.AxisInfo}.
+ * @param nodeTest The conditions to be satisfied by selected nodes. May be null,
+ * indicating that any node on the axis is acceptable
+ * @see net.sf.saxon.om.AxisInfo
+ */
+
+ public AxisExpression(byte axis, /*@Nullable*/ NodeTest nodeTest) {
+ this.axis = axis;
+ this.test = nodeTest;
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ *
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "axisStep";
+ }
+
+ /**
+ * Simplify an expression
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ Expression e2 = super.simplify(visitor);
+ if (e2 != this) {
+ return e2;
+ }
+ if (axis == AxisInfo.PARENT && (test == null || test instanceof AnyNodeTest)) {
+ ParentNodeExpression p = new ParentNodeExpression();
+ ExpressionTool.copyLocationInfo(this, p);
+ return p;
+ }
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (contextItemType == null) {
+ XPathException err = new XPathException("Axis step " + toString(visitor.getConfiguration().getNamePool()) +
+ " cannot be used here: the context item is absent");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPDY0002");
+ err.setLocator(this);
+ throw err;
+ } else {
+ contextMaybeUndefined = contextItemType.contextMaybeUndefined;
+ }
+ Configuration config = visitor.getConfiguration();
+ TypeHierarchy th = config.getTypeHierarchy();
+ int relation = th.relationship(contextItemType.itemType, AnyNodeTest.getInstance());
+
+ if (relation == TypeHierarchy.DISJOINT) {
+ XPathException err = new XPathException("Axis step " + toString(visitor.getConfiguration().getNamePool()) +
+ " cannot be used here: the context item is not a node");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0020");
+ err.setLocator(this);
+ throw err;
+ } else if (relation == TypeHierarchy.OVERLAPS || relation == TypeHierarchy.SUBSUMES) {
+ // need to insert a dynamic check of the context item type
+ ContextItemExpression exp = new ContextItemExpression();
+ ExpressionTool.copyLocationInfo(this, exp);
+ RoleLocator role = new RoleLocator(RoleLocator.AXIS_STEP, AxisInfo.axisName[axis], 0);
+ role.setErrorCode("XPTY0020");
+ ItemChecker checker = new ItemChecker(exp, AnyNodeTest.getInstance(), role);
+ ExpressionTool.copyLocationInfo(this, checker);
+ SimpleStepExpression step = new SimpleStepExpression(checker, this);
+ ExpressionTool.copyLocationInfo(this, step);
+ return step;
+ }
+
+ StaticContext env = visitor.getStaticContext();
+
+ if (this.contextItemType == contextItemType && doneWarnings) {
+ return this;
+ }
+
+ this.contextItemType = contextItemType.itemType;
+ doneWarnings = true;
+
+ ItemType contextType = this.contextItemType;
+
+ if (contextType instanceof NodeTest) {
+ int origin = contextType.getPrimitiveType();
+ if (origin != Type.NODE) {
+ if (AxisInfo.isAlwaysEmpty(axis, origin)) {
+ env.issueWarning("The " + AxisInfo.axisName[axis] + " axis starting at " +
+ (origin == Type.ELEMENT || origin == Type.ATTRIBUTE ? "an " : "a ") +
+ NodeKindTest.nodeKindName(origin) + " node will never select anything",
+ this);
+ return Literal.makeEmptySequence();
+ }
+ }
+
+ if (test != null) {
+ int kind = test.getPrimitiveType();
+ if (kind != Type.NODE) {
+ if (!AxisInfo.containsNodeKind(axis, kind)) {
+ env.issueWarning("The " + AxisInfo.axisName[axis] + " axis will never select any " +
+ NodeKindTest.nodeKindName(kind) + " nodes",
+ this);
+ return Literal.makeEmptySequence();
+ }
+ }
+ if (axis == AxisInfo.SELF && kind != Type.NODE && origin != Type.NODE && kind != origin) {
+ env.issueWarning("The self axis will never select any " +
+ NodeKindTest.nodeKindName(kind) +
+ " nodes when starting at " +
+ (origin == Type.ELEMENT || origin == Type.ATTRIBUTE ? "an " : "a ") +
+ NodeKindTest.nodeKindName(origin) + " node", this);
+ return Literal.makeEmptySequence();
+ }
+ if (axis == AxisInfo.SELF) {
+ itemType = new CombinedNodeTest(test, Token.INTERSECT, (NodeTest) contextType);
+ }
+
+ // If the content type of the context item is known, see whether the node test can select anything
+
+ if (contextType instanceof DocumentNodeTest && kind == Type.ELEMENT) {
+ NodeTest elementTest = ((DocumentNodeTest) contextType).getElementTest();
+ IntSet outermostElementNames = elementTest.getRequiredNodeNames();
+ if (outermostElementNames != null) {
+ IntSet selectedElementNames = test.getRequiredNodeNames();
+ if (selectedElementNames != null) {
+ if (axis == AxisInfo.CHILD) {
+ // check that the name appearing in the step is one of the names allowed by the nodetest
+
+ if (selectedElementNames.intersect(outermostElementNames).isEmpty()) {
+ env.issueWarning("Starting at a document node, the step is selecting an element whose name " +
+ "is not among the names of child elements permitted for this document node type", this);
+
+ return Literal.makeEmptySequence();
+ }
+
+ if (env.isSchemaAware() &&
+ elementTest instanceof SchemaNodeTest &&
+ outermostElementNames.size() == 1) {
+ IntIterator oeni = outermostElementNames.iterator();
+ int outermostElementName = (oeni.hasNext() ? oeni.next() : -1);
+ SchemaDeclaration decl = config.getElementDeclaration(outermostElementName);
+ if (decl == null) {
+ env.issueWarning("Element " + config.getNamePool().getDisplayName(outermostElementName) +
+ " is not declared in the schema", this);
+ itemType = elementTest;
+ } else {
+ SchemaType contentType = decl.getType();
+ itemType = new CombinedNodeTest(
+ elementTest, Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, contentType, config, true));
+ }
+ } else {
+ itemType = elementTest;
+ }
+ return this;
+
+ } else if (axis == AxisInfo.DESCENDANT) {
+ // check that the name appearing in the step is one of the names allowed by the nodetest
+ boolean canMatchOutermost = !selectedElementNames.intersect(outermostElementNames).isEmpty();
+ if (!canMatchOutermost) {
+ // The expression /descendant::x starting at the document node doesn't match the outermost
+ // element, so replace it by child::*/descendant::x, and check that
+ Expression path = ExpressionTool.makePathExpression(new AxisExpression(AxisInfo.CHILD, elementTest), new AxisExpression(AxisInfo.DESCENDANT, test), false);
+ ExpressionTool.copyLocationInfo(this, path);
+ return path.typeCheck(visitor, contextItemType);
+ }
+ }
+ }
+ }
+ }
+
+ SchemaType contentType = ((NodeTest) contextType).getContentType();
+ if (contentType == AnyType.getInstance()) {
+ // fast exit in non-schema-aware case
+ return this;
+ }
+
+ int targetfp = test.getFingerprint();
+
+ if (contentType.isSimpleType()) {
+ if ((axis == AxisInfo.CHILD || axis == AxisInfo.DESCENDANT || axis == AxisInfo.DESCENDANT_OR_SELF) &&
+ (kind == Type.ELEMENT || kind == Type.ATTRIBUTE || kind == Type.DOCUMENT)) {
+ env.issueWarning("The " + AxisInfo.axisName[axis] + " axis will never select any " +
+ NodeKindTest.nodeKindName(kind) +
+ " nodes when starting at " +
+ (origin == Type.ATTRIBUTE ? "an attribute node" : getStartingNodeDescription(contentType)),
+ this);
+ } else if (axis == AxisInfo.CHILD && kind == Type.TEXT &&
+ (visitor.getParentExpression() instanceof Atomizer)) {
+ env.issueWarning("Selecting the text nodes of an element with simple content may give the " +
+ "wrong answer in the presence of comments or processing instructions. It is usually " +
+ "better to omit the '/text()' step", this);
+ } else if (axis == AxisInfo.ATTRIBUTE) {
+ Iterator extensions = config.getExtensionsOfType(contentType);
+ boolean found = false;
+ if (targetfp == -1) {
+ while (extensions.hasNext()) {
+ ComplexType extension = (ComplexType) extensions.next();
+ if (extension.allowsAttributes()) {
+ found = true;
+ break;
+ }
+ }
+ } else {
+ while (extensions.hasNext()) {
+ ComplexType extension = (ComplexType) extensions.next();
+ try {
+ if (extension.getAttributeUseType(targetfp) != null) {
+ found = true;
+ break;
+ }
+ } catch (SchemaException e) {
+ // ignore the error
+ }
+ }
+ }
+ if (!found) {
+ env.issueWarning("The " + AxisInfo.axisName[axis] + " axis will never select " +
+ (targetfp == -1 ?
+ "any attribute nodes" :
+ "an attribute node named " + getDiagnosticName(targetfp, env)) +
+ " when starting at " + getStartingNodeDescription(contentType), this);
+ // Despite the warning, leave the expression unchanged. This is because
+ // we don't necessarily know about all extended types at compile time:
+ // in particular, we don't seal the XML Schema namespace to block extensions
+ // of built-in types
+ }
+ }
+ } else if (((ComplexType) contentType).isSimpleContent() &&
+ (axis == AxisInfo.CHILD || axis == AxisInfo.DESCENDANT || axis == AxisInfo.DESCENDANT_OR_SELF) &&
+ (kind == Type.ELEMENT || kind == Type.DOCUMENT)) {
+ // We don't need to consider extended types here, because a type with complex content
+ // can never be defined as an extension of a type with simple content
+ env.issueWarning("The " + AxisInfo.axisName[axis] + " axis will never select any " +
+ NodeKindTest.nodeKindName(kind) +
+ " nodes when starting at " +
+ getStartingNodeDescription(contentType) +
+ ", as this type requires simple content", this);
+ return Literal.makeEmptySequence();
+ } else if (((ComplexType) contentType).isEmptyContent() &&
+ (axis == AxisInfo.CHILD || axis == AxisInfo.DESCENDANT || axis == AxisInfo.DESCENDANT_OR_SELF)) {
+ for (Iterator iter = config.getExtensionsOfType(contentType); iter.hasNext(); ) {
+ ComplexType extension = (ComplexType) iter.next();
+ if (!extension.isEmptyContent()) {
+ return this;
+ }
+ }
+ env.issueWarning("The " + AxisInfo.axisName[axis] + " axis will never select any" +
+ " nodes when starting at " +
+ getStartingNodeDescription(contentType) +
+ ", as this type requires empty content", this);
+ return Literal.makeEmptySequence();
+ } else if (axis == AxisInfo.ATTRIBUTE) {
+ if (targetfp == -1) {
+ if (!((ComplexType) contentType).allowsAttributes()) {
+ env.issueWarning("The complex type " + contentType.getDescription() +
+ " allows no attributes other than the standard attributes in the xsi namespace", this);
+ }
+ } else {
+ try {
+ SchemaType schemaType;
+ if (targetfp == StandardNames.XSI_TYPE) {
+ schemaType = BuiltInAtomicType.QNAME;
+ } else if (targetfp == StandardNames.XSI_SCHEMA_LOCATION) {
+ schemaType = BuiltInListType.ANY_URIS;
+ } else if (targetfp == StandardNames.XSI_NO_NAMESPACE_SCHEMA_LOCATION) {
+ schemaType = BuiltInAtomicType.ANY_URI;
+ } else if (targetfp == StandardNames.XSI_NIL) {
+ schemaType = BuiltInAtomicType.BOOLEAN;
+ } else {
+ schemaType = ((ComplexType) contentType).getAttributeUseType(targetfp);
+ }
+ if (schemaType == null) {
+ env.issueWarning("The complex type " + contentType.getDescription() +
+ " does not allow an attribute named " + getDiagnosticName(targetfp, env), this);
+ return Literal.makeEmptySequence();
+ } else {
+ itemType = new CombinedNodeTest(
+ test,
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ATTRIBUTE, schemaType, env.getConfiguration(), false));
+ }
+ } catch (SchemaException e) {
+ // ignore the exception
+ }
+ }
+ } else if (axis == AxisInfo.CHILD && kind == Type.ELEMENT) {
+ try {
+ int childElement = targetfp;
+ if (targetfp == -1) {
+ // select="child::*"
+ if (((ComplexType) contentType).containsElementWildcard()) {
+ return this;
+ }
+ IntHashSet children = new IntHashSet();
+ ((ComplexType) contentType).gatherAllPermittedChildren(children, false);
+ if (children.isEmpty()) {
+ env.issueWarning("The complex type " + contentType.getDescription() +
+ " does not allow children", this);
+ return Literal.makeEmptySequence();
+ }
+// if (children.contains(-1)) {
+// return this;
+// }
+ if (children.size() == 1) {
+ IntIterator iter = children.iterator();
+ if (iter.hasNext()) {
+ childElement = iter.next();
+ }
+ } else {
+ return this;
+ }
+ }
+ SchemaType schemaType = ((ComplexType) contentType).getElementParticleType(childElement, true);
+ if (schemaType == null) {
+ env.issueWarning("The complex type " + contentType.getDescription() +
+ " does not allow a child element named " + getDiagnosticName(childElement, env), this);
+ return Literal.makeEmptySequence();
+ } else {
+ itemType = new CombinedNodeTest(
+ test,
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, schemaType, env.getConfiguration(), true));
+ computedCardinality = ((ComplexType) contentType).getElementParticleCardinality(childElement, true);
+ visitor.resetStaticProperties();
+ if (computedCardinality == StaticProperty.ALLOWS_ZERO) {
+ // this shouldn't happen, because we've already checked for this a different way.
+ // but it's worth being safe (there was a bug involving an incorrect inference here)
+ env.issueWarning("The complex type " + contentType.getDescription() +
+ " appears not to allow a child element named " + getDiagnosticName(childElement, env), this);
+ return Literal.makeEmptySequence();
+ }
+ if (!Cardinality.allowsMany(computedCardinality)) {
+ // if there can be at most one child of this name, create a FirstItemExpression
+ // to stop the search after the first one is found
+ return FirstItemExpression.makeFirstItemExpression(this);
+ }
+ }
+ } catch (SchemaException e) {
+ // ignore the exception
+ }
+ } else if (axis == AxisInfo.DESCENDANT && kind == Type.ELEMENT && targetfp != -1) {
+ // when searching for a specific element on the descendant axis, try to produce a more
+ // specific path that avoids searching branches of the tree where the element cannot occur
+ try {
+ IntHashSet descendants = new IntHashSet();
+ ((ComplexType) contentType).gatherAllPermittedDescendants(descendants);
+ if (descendants.contains(-1)) {
+ return this;
+ }
+ if (descendants.contains(targetfp)) {
+ IntHashSet children = new IntHashSet();
+ ((ComplexType) contentType).gatherAllPermittedChildren(children, false);
+ IntHashSet usefulChildren = new IntHashSet();
+ boolean considerSelf = false;
+ boolean considerDescendants = false;
+ for (IntIterator child = children.iterator(); child.hasNext(); ) {
+ int c = child.next();
+ if (c == targetfp) {
+ usefulChildren.add(c);
+ considerSelf = true;
+ }
+ SchemaType st = ((ComplexType) contentType).getElementParticleType(c, true);
+ if (st == null) {
+ throw new AssertionError("Can't find type for child element " + c);
+ }
+ if (st instanceof ComplexType) {
+ IntHashSet subDescendants = new IntHashSet();
+ ((ComplexType) st).gatherAllPermittedDescendants(subDescendants);
+ if (subDescendants.contains(targetfp)) {
+ usefulChildren.add(c);
+ considerDescendants = true;
+ }
+ }
+ }
+ itemType = test;
+ if (considerDescendants) {
+ SchemaType st = ((ComplexType) contentType).getDescendantElementType(targetfp);
+ if (st != AnyType.getInstance()) {
+ itemType = new CombinedNodeTest(
+ test, Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, st, config, true));
+ }
+ //return this;
+ }
+ if (usefulChildren.size() < children.size()) {
+ NodeTest childTest = makeUnionNodeTest(usefulChildren, visitor.getConfiguration().getNamePool());
+ AxisExpression first = new AxisExpression(AxisInfo.CHILD, childTest);
+ ExpressionTool.copyLocationInfo(this, first);
+ byte nextAxis;
+ if (considerSelf) {
+ nextAxis = (considerDescendants ? AxisInfo.DESCENDANT_OR_SELF : AxisInfo.SELF);
+ } else {
+ nextAxis = AxisInfo.DESCENDANT;
+ }
+ AxisExpression next = new AxisExpression(nextAxis, (NodeTest) itemType);
+ ExpressionTool.copyLocationInfo(this, next);
+ Expression path = ExpressionTool.makePathExpression(first, next, false);
+ ExpressionTool.copyLocationInfo(this, path);
+ return path.typeCheck(visitor, contextItemType);
+ }
+ } else {
+ env.issueWarning("The complex type " + contentType.getDescription() +
+ " does not allow a descendant element named " + getDiagnosticName(targetfp, env), this);
+ }
+ } catch (SchemaException e) {
+ throw new AssertionError(e);
+ }
+
+
+ }
+ }
+ }
+
+ return this;
+ }
+
+ /*
+ * Get a string representation of a name to use in diagnostics
+ */
+
+ private static String getDiagnosticName(int fp, StaticContext env) {
+ NamePool pool = env.getNamePool();
+ String uri = pool.getURI(fp);
+ NamespaceResolver resolver = env.getNamespaceResolver();
+ for (Iterator<String> it = resolver.iteratePrefixes(); it.hasNext(); ) {
+ String prefix = it.next();
+ if (uri.equals(resolver.getURIForPrefix(prefix, true))) {
+ if (prefix.length() == 0) {
+ return "{" + uri + "}" + pool.getLocalName(fp);
+ } else {
+ return prefix + ":" + pool.getLocalName(fp);
+ }
+ }
+ }
+ return "{" + uri + "}" + pool.getLocalName(fp);
+ }
+
+ private static String getStartingNodeDescription(SchemaType type) {
+ String s = type.getDescription();
+ if (s.startsWith("of element")) {
+ return "a valid element named" + s.substring("of element".length());
+ } else if (s.startsWith("of attribute")) {
+ return "a valid attribute named" + s.substring("of attribute".length());
+ } else {
+ return "a node with " + (type.isSimpleType() ? "simple" : "complex") + " type " + s;
+ }
+ }
+
+ /**
+ * Make a union node test for a set of supplied element fingerprints
+ *
+ * @param elements the set of integer element fingerprints to be tested for. Must not
+ * be empty.
+ * @param pool the name pool
+ * @return a NodeTest that returns true if the node is an element whose name is one of the names
+ * in this set
+ */
+
+ private NodeTest makeUnionNodeTest(IntHashSet elements, NamePool pool) {
+ NodeTest test = null;
+ for (IntIterator iter = elements.iterator(); iter.hasNext(); ) {
+ int fp = iter.next();
+ NodeTest nextTest = new NameTest(Type.ELEMENT, fp, pool);
+ if (test == null) {
+ test = nextTest;
+ } else {
+ test = new CombinedNodeTest(test, Token.UNION, nextTest);
+ }
+ }
+ return test;
+ }
+
+ /**
+ * Get the static type of the context item for this AxisExpression. May be null if not known.
+ *
+ * @return the statically-inferred type, or null if not known
+ */
+
+ public ItemType getContextItemType() {
+ return contextItemType;
+ }
+
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ */
+
+// public Expression optimize(ExpressionVisitor visitor, ItemType contextItemType) {
+// return this;
+// }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ if (!(other instanceof AxisExpression)) {
+ return false;
+ }
+ if (axis != ((AxisExpression) other).axis) {
+ return false;
+ }
+ if (test == null) {
+ return ((AxisExpression) other).test == null;
+ }
+ return test.toString().equals(((AxisExpression) other).test.toString());
+ }
+
+ /**
+ * get HashCode for comparing two expressions
+ */
+
+ public int hashCode() {
+ // generate an arbitrary hash code that depends on the axis and the node test
+ int h = 9375162 + axis << 20;
+ if (test != null) {
+ h ^= test.getPrimitiveType() << 16;
+ h ^= test.getFingerprint();
+ }
+ return h;
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
+ * XPathContext.CURRENT_NODE
+ */
+
+// public int getIntrinsicDependencies() {
+// return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+// }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ AxisExpression a2 = new AxisExpression(axis, test);
+ a2.itemType = itemType;
+ a2.contextItemType = contextItemType;
+ a2.computedCardinality = computedCardinality;
+ a2.doneWarnings = doneWarnings;
+ return a2;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ return StaticProperty.CONTEXT_DOCUMENT_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET |
+ StaticProperty.NON_CREATIVE |
+ (AxisInfo.isForwards[axis] ? StaticProperty.ORDERED_NODESET : StaticProperty.REVERSE_DOCUMENT_ORDER) |
+ (AxisInfo.isPeerAxis[axis] ? StaticProperty.PEER_NODESET : 0) |
+ (AxisInfo.isSubtreeAxis[axis] ? StaticProperty.SUBTREE_NODESET : 0) |
+ ((axis == AxisInfo.ATTRIBUTE || axis == AxisInfo.NAMESPACE) ? StaticProperty.ATTRIBUTE_NS_NODESET : 0);
+ }
+
+ /**
+ * Determine the data type of the items returned by this expression
+ *
+ * @param th the type hierarchy cache
+ * @return Type.NODE or a subtype, based on the NodeTest in the axis step, plus
+ * information about the content type if this is known from schema analysis
+ */
+
+ /*@NotNull*/
+ public final ItemType getItemType(TypeHierarchy th) {
+ if (itemType != null) {
+ return itemType;
+ }
+ int p = AxisInfo.principalNodeType[axis];
+ switch (p) {
+ case Type.ATTRIBUTE:
+ case Type.NAMESPACE:
+ return NodeKindTest.makeNodeKindTest(p);
+ default:
+ if (test == null) {
+ return AnyNodeTest.getInstance();
+ } else {
+ return test;
+ }
+ }
+ }
+
+ /**
+ * Determine the intrinsic dependencies of an expression, that is, those which are not derived
+ * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
+ * on the context position, while (position()+1) does not. The default implementation
+ * of the method returns 0, indicating "no dependencies".
+ *
+ * @return a set of bit-significant flags identifying the "intrinsic"
+ * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
+ */
+ @Override
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ }
+
+ /**
+ * Determine the cardinality of the result of this expression
+ */
+
+ public final int computeCardinality() {
+ if (computedCardinality != -1) {
+ // This takes care of the case where cardinality was computed during type checking of the child axis
+ return computedCardinality;
+ }
+ NodeTest originNodeType;
+ NodeTest nodeTest = test;
+ if (contextItemType instanceof NodeTest) {
+ originNodeType = (NodeTest) contextItemType;
+ } else if (contextItemType instanceof AnyItemType) {
+ originNodeType = AnyNodeTest.getInstance();
+ } else {
+ // context item not a node - we'll report a type error somewhere along the line
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ if (axis == AxisInfo.ATTRIBUTE && nodeTest instanceof NameTest) {
+ SchemaType contentType = originNodeType.getContentType();
+ if (contentType instanceof ComplexType) {
+ try {
+ return ((ComplexType) contentType).getAttributeUseCardinality(nodeTest.getFingerprint());
+ } catch (SchemaException err) {
+ // shouldn't happen; play safe
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+ } else if (contentType instanceof SimpleType) {
+ return StaticProperty.EMPTY;
+ }
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ } else if (axis == AxisInfo.DESCENDANT && nodeTest instanceof NameTest && nodeTest.getPrimitiveType() == Type.ELEMENT) {
+ SchemaType contentType = originNodeType.getContentType();
+ if (contentType instanceof ComplexType) {
+ try {
+ return ((ComplexType) contentType).getDescendantElementCardinality(nodeTest.getFingerprint());
+ } catch (SchemaException err) {
+ // shouldn't happen; play safe
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ } else {
+ return StaticProperty.EMPTY;
+ }
+
+ } else if (axis == AxisInfo.SELF) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ } else {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ // the parent axis isn't handled by this class
+ }
+
+ /**
+ * Determine whether the expression can be evaluated without reference to the part of the context
+ * document outside the subtree rooted at the context node.
+ *
+ * @return true if the expression has no dependencies on the context node, or if the only dependencies
+ * on the context node are downward selections using the self, child, descendant, attribute, and namespace
+ * axes.
+ */
+
+ public boolean isSubtreeExpression() {
+ return AxisInfo.isSubtreeAxis[axis];
+ }
+
+ /**
+ * Get the axis
+ *
+ * @return the axis number, for example {@link net.sf.saxon.om.AxisInfo#CHILD}
+ */
+
+ public byte getAxis() {
+ return axis;
+ }
+
+ /**
+ * Get the NodeTest. Returns null if the AxisExpression can return any node.
+ *
+ * @return the node test, or null if all nodes are returned
+ */
+
+ public NodeTest getNodeTest() {
+ return test;
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ if (pathMapNodeSet == null) {
+ ContextItemExpression cie = new ContextItemExpression();
+ cie.setContainer(getContainer());
+ pathMapNodeSet = new PathMap.PathMapNodeSet(pathMap.makeNewRoot(cie));
+ }
+ return pathMapNodeSet.createArc(axis, (test == null ? AnyNodeTest.getInstance() : test));
+ }
+
+ /**
+ * Ask whether there is a possibility that the context item will be undefined
+ *
+ * @return true if this is a possibility
+ */
+
+ public boolean isContextPossiblyUndefined() {
+ return contextMaybeUndefined;
+ }
+
+ //#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ switch (axis) {
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.NAMESPACE:
+ if (syntacticContext == NAVIGATION_CONTEXT) {
+ addFreeRangingMessage(axis, reasons);
+ return W3C_FREE_RANGING;
+ } else {
+ return W3C_MOTIONLESS;
+ }
+ case AxisInfo.SELF:
+ if (syntacticContext == INSPECTION_CONTEXT) {
+ return W3C_MOTIONLESS;
+ } else if (syntacticContext == NODE_VALUE_CONTEXT) {
+ return W3C_CONSUMING;
+ } else {
+ addFreeRangingMessage(axis, reasons);
+ return W3C_FREE_RANGING;
+ }
+ case AxisInfo.PARENT:
+ case AxisInfo.ANCESTOR:
+ case AxisInfo.ANCESTOR_OR_SELF:
+ if (syntacticContext == INSPECTION_CONTEXT) {
+ return W3C_MOTIONLESS;
+ } else {
+ addFreeRangingMessage(axis, reasons);
+ return W3C_FREE_RANGING;
+ }
+ case AxisInfo.CHILD:
+ case AxisInfo.DESCENDANT:
+ case AxisInfo.DESCENDANT_OR_SELF:
+ if (syntacticContext == NAVIGATION_CONTEXT) {
+ addFreeRangingMessage(axis, reasons);
+ return W3C_FREE_RANGING;
+ } else {
+ return W3C_CONSUMING;
+ }
+ default:
+ addFreeRangingMessage(axis, reasons);
+ return W3C_FREE_RANGING;
+
+ }
+ }
+//#endif
+
+ private void addFreeRangingMessage(int axis, List<String> reasons) {
+ if (reasons != null) {
+ reasons.add("The " + AxisInfo.axisName[axis] + " axis in a general context is free-ranging (used at line " + getLineNumber() + ")");
+ }
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+
+ NodeTest test = getNodeTest();
+
+ if (test == null) {
+ test = AnyNodeTest.getInstance();
+ }
+ if (test instanceof AnyNodeTest && (axis == AxisInfo.CHILD || axis == AxisInfo.DESCENDANT)) {
+ test = AnyChildNodeTest.getInstance();
+ }
+ int kind = test.getPrimitiveType();
+ if (axis == AxisInfo.SELF) {
+ return new ItemTypePattern(test);
+ } else if (axis == AxisInfo.ATTRIBUTE) {
+ if (kind == Type.NODE) {
+ // attribute::node() matches any attribute, and only an attribute
+ return new ItemTypePattern(NodeKindTest.ATTRIBUTE);
+ } else if (!AxisInfo.containsNodeKind(axis, kind)) {
+ // for example, attribute::comment()
+ return new ItemTypePattern(ErrorType.getInstance());
+ } else {
+ return new ItemTypePattern(test);
+ }
+ } else if (axis == AxisInfo.CHILD || axis == AxisInfo.DESCENDANT || axis == AxisInfo.DESCENDANT_OR_SELF) {
+ if (kind != Type.NODE && !AxisInfo.containsNodeKind(axis, kind)) {
+ return new ItemTypePattern(ErrorType.getInstance());
+ }
+ return new ItemTypePattern(test);
+ } else if (axis == AxisInfo.NAMESPACE) {
+ if (kind == Type.NODE) {
+ // namespace::node() matches any attribute, and only an attribute
+ return new ItemTypePattern(NodeKindTest.NAMESPACE);
+ } else if (!AxisInfo.containsNodeKind(axis, kind)) {
+ // for example, namespace::comment()
+ return new ItemTypePattern(ErrorType.getInstance());
+ } else {
+ return new ItemTypePattern(test);
+ }
+ } else {
+ throw new XPathException("Only downwards axes are allowed in a pattern", "XTSE0340");
+ }
+ // TODO: //A only matches an A element in a tree rooted at a document
+ }
+
+//#ifdefined STREAM
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ SlashExpression s = new SlashExpression(new ContextItemExpression(), this);
+ //SlashExpression s = new SlashExpression(new AxisExpression(AxisInfo.SELF, AnyNodeTest.getInstance()), this);
+ try {
+ return s.toPattern(config, true);
+ } catch (XPathException e) {
+ reasonForFailure.add(e.getMessage());
+ return null;
+ }
+ }
+
+//#endif
+
+ /**
+ * Evaluate the path-expression in a given context to return a NodeSet
+ *
+ * @param context the evaluation context
+ */
+
+ /*@NotNull*/
+ public AxisIterator<? extends NodeInfo> iterate(XPathContext context) throws XPathException {
+ Item item = context.getContextItem();
+ if (item == null) {
+ // Might as well do the test anyway, whether or not contextMaybeUndefined is set
+ NamePool pool;
+ try {
+ pool = context.getConfiguration().getNamePool();
+ } catch (Exception err) {
+ pool = null;
+ }
+ XPathException err = new XPathException("The context item for axis step " +
+ (pool == null ? toString() : toString(pool)) + " is absent");
+ err.setErrorCode("XPDY0002");
+ err.setXPathContext(context);
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ try {
+ if (test == null) {
+ return ((NodeInfo) item).iterateAxis(axis);
+ } else {
+ return ((NodeInfo) item).iterateAxis(axis, test);
+ }
+ } catch (ClassCastException cce) {
+ NamePool pool;
+ try {
+ pool = context.getConfiguration().getNamePool();
+ } catch (Exception err) {
+ pool = null;
+ }
+ XPathException err = new XPathException("The context item for axis step " +
+ (pool == null ? toString() : toString(pool)) + " is not a node");
+ err.setErrorCode("XPTY0020");
+ err.setXPathContext(context);
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ } catch (UnsupportedOperationException err) {
+ if (err.getCause() instanceof XPathException) {
+ XPathException ec = (XPathException) err.getCause();
+ ec.maybeSetLocation(this);
+ ec.maybeSetContext(context);
+ throw ec;
+ } else {
+ // the namespace axis is not supported for all tree implementations
+ dynamicError(err.getMessage(), "XPST0010", context);
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Iterate the axis from a given starting node, without regard to context
+ *
+ * @param origin the starting node
+ * @return the iterator over the axis
+ */
+
+ public SequenceIterator iterate(Item origin) throws XPathException {
+ try {
+ if (test == null) {
+ return ((NodeInfo) origin).iterateAxis(axis);
+ } else {
+ return ((NodeInfo) origin).iterateAxis(axis, test);
+ }
+ } catch (ClassCastException cce) {
+ XPathException err = new XPathException("The context item for axis step " +
+ toString() + " is not a node");
+ err.setErrorCode("XPTY0020");
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+
+ //#ifdefined BYTECODE
+
+ /**
+ * Return the compiler of the Axis expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new AxisExpressionCompiler();
+ }
+ //#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("axis");
+ destination.emitAttribute("name", AxisInfo.axisName[axis]);
+ destination.emitAttribute("nodeTest", (test == null ? "node()" : test.toString()));
+ destination.endElement();
+ }
+
+ /**
+ * Represent the expression as a string. The resulting string will be a valid XPath 3.0 expression
+ * with no dependencies on namespace bindings other than the binding of the prefix "xs" to the XML Schema
+ * namespace.
+ *
+ * @return the expression as a string in XPath 3.0 syntax
+ */
+
+ public String toString() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.TINY);
+ fsb.append(AxisInfo.axisName[axis]);
+ fsb.append("::");
+ fsb.append(test == null ? "node()" : test.toString());
+ return fsb.toString();
+ }
+
+ /**
+ * Represent the expression as a string for diagnostics
+ *
+ * @param pool the name pool, used for expanding names in the node test
+ * @return a string representation of the expression
+ */
+
+ public String toString(NamePool pool) {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.TINY);
+ fsb.append(AxisInfo.axisName[axis]);
+ fsb.append("::");
+ fsb.append(test == null ? "node()" : test.toString());
+ return fsb.toString();
+ }
+}
+
diff --git a/sf/saxon/expr/BigRangeIterator.java b/sf/saxon/expr/BigRangeIterator.java
new file mode 100644
index 0000000..2108cce
--- /dev/null
+++ b/sf/saxon/expr/BigRangeIterator.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.UncheckedXPathException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.value.IntegerValue;
+
+import java.math.BigInteger;
+
+/**
+ * An Iterator that produces numeric values in a monotonic sequence,
+ * where the integers may exceed the range of a Long
+*/
+
+public class BigRangeIterator implements SequenceIterator<IntegerValue>,
+ LastPositionFinder<IntegerValue>,
+ LookaheadIterator<IntegerValue> {
+
+ BigInteger start;
+ BigInteger currentValue;
+ BigInteger limit;
+
+ /**
+ * Create an iterator over a range of monotonically increasing integers
+ * @param start the first integer in the sequence
+ * @param end the last integer in the sequence. Must be >= start.
+ */
+
+ public BigRangeIterator(BigInteger start, BigInteger end) throws XPathException {
+ if (end.subtract(start).compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {
+ throw new XPathException("Saxon limit on sequence length exceeded (2^32-1)");
+ }
+ this.start = start;
+ currentValue = start.subtract(BigInteger.valueOf(1));
+ limit = end;
+ }
+
+ public boolean hasNext() {
+ return currentValue.compareTo(limit) < 0;
+ }
+
+ /*@Nullable*/ public IntegerValue next() {
+ currentValue = currentValue.add(BigInteger.valueOf(1));
+ if (currentValue.compareTo(limit) > 0) {
+ return null;
+ }
+ return IntegerValue.makeIntegerValue(currentValue);
+ }
+
+ public IntegerValue current() {
+ if (currentValue.compareTo(limit) > 0) {
+ return null;
+ } else {
+ return IntegerValue.makeIntegerValue(currentValue);
+ }
+ }
+
+ public int position() {
+ if (currentValue.compareTo(limit) > 0) {
+ return -1;
+ } else {
+ BigInteger pos = currentValue.subtract(start).add(BigInteger.valueOf(1));
+ if (pos.abs().compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {
+ throw new UncheckedXPathException(new XPathException("Sequence exceeds Saxon limit (32-bit integer)"));
+ }
+ return pos.intValue();
+ }
+ }
+
+ public void close() {
+ }
+
+ public int getLength() {
+ BigInteger len = limit.subtract(start).add(BigInteger.valueOf(1));
+ if (len.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {
+ throw new UncheckedXPathException(new XPathException("Sequence exceeds Saxon limit (32-bit integer)"));
+ }
+ return len.intValue();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<IntegerValue> getAnother() throws XPathException {
+ return new BigRangeIterator(start, limit);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED}, {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD | LAST_POSITION_FINDER;
+ }
+
+}
+
diff --git a/sf/saxon/expr/BinaryExpression.java b/sf/saxon/expr/BinaryExpression.java
new file mode 100644
index 0000000..9c1ae67
--- /dev/null
+++ b/sf/saxon/expr/BinaryExpression.java
@@ -0,0 +1,449 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Cardinality;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+* Binary Expression: a numeric or boolean expression consisting of the
+* two operands and an operator
+*/
+
+public abstract class BinaryExpression extends Expression {
+
+ protected Expression operand0;
+ protected Expression operand1;
+ protected int operator; // represented by the token number from class Tokenizer
+
+ /**
+ * Create a binary expression identifying the two operands and the operator
+ * @param p0 the left-hand operand
+ * @param op the operator, as a token returned by the Tokenizer (e.g. Token.AND)
+ * @param p1 the right-hand operand
+ */
+
+ public BinaryExpression(Expression p0, int op, Expression p1) {
+ operator = op;
+ operand0 = p0;
+ operand1 = p1;
+ adoptChildExpression(p0);
+ adoptChildExpression(p1);
+ }
+
+ /**
+ * Simplify an expression
+ * @return the simplified expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ operand0 = visitor.simplify(operand0);
+ operand1 = visitor.simplify(operand1);
+ return this;
+ }
+
+ /**
+ * Type-check the expression. Default implementation for binary operators that accept
+ * any kind of operand
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand0 = visitor.typeCheck(operand0, contextItemType);
+ operand1 = visitor.typeCheck(operand1, contextItemType);
+ // if both operands are known, pre-evaluate the expression
+ try {
+ if ((operand0 instanceof Literal) && (operand1 instanceof Literal)) {
+ GroundedValue v = SequenceTool.toGroundedValue(
+ evaluateItem(visitor.getStaticContext().makeEarlyEvaluationContext()));
+ return Literal.makeLiteral(v);
+ }
+ } catch (XPathException err) {
+ // if early evaluation fails, suppress the error: the value might
+ // not be needed at run-time
+ }
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand0 = visitor.optimize(operand0, contextItemType);
+ operand1 = visitor.optimize(operand1, contextItemType);
+ // if both operands are known, pre-evaluate the expression
+ try {
+ if ((operand0 instanceof Literal) && (operand1 instanceof Literal)) {
+ Item item = evaluateItem(visitor.getStaticContext().makeEarlyEvaluationContext());
+ if(item != null){
+ GroundedValue v = SequenceTool.toGroundedValue(item);
+ return Literal.makeLiteral(v);
+ }
+ }
+ } catch (XPathException err) {
+ // if early evaluation fails, suppress the error: the value might
+ // not be needed at run-time
+ }
+ return this;
+ }
+
+
+ /**
+ * Mark an expression as being "flattened". This is a collective term that includes extracting the
+ * string value or typed value, or operations such as simple value construction that concatenate text
+ * nodes before atomizing. The implication of all of these is that although the expression might
+ * return nodes, the identity of the nodes has no significance. This is called during type checking
+ * of the parent expression.
+ *
+ * @param flattened set to true if the result of the expression is atomized or otherwise turned into
+ * an atomic value
+ */
+
+ public void setFlattened(boolean flattened) {
+ operand0.setFlattened(flattened);
+ operand1.setFlattened(flattened);
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ if (offer.action != PromotionOffer.UNORDERED) {
+ operand0 = doPromotion(operand0, offer);
+ operand1 = doPromotion(operand1, offer);
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Get the immediate subexpressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(operand0, operand1);
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * works off the results of iterateSubExpressions()
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new PairIterator<SubExpressionInfo>(
+ new SubExpressionInfo(operand0, true, false, NODE_VALUE_CONTEXT),
+ new SubExpressionInfo(operand1, true, false, NODE_VALUE_CONTEXT));
+ }
+
+ /**
+ * Get the subexpressions (arguments to this expression)
+ * @return the arguments, as an array
+ */
+
+ public Expression[] getArguments() {
+ return new Expression[]{operand0, operand1};
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (operand0 == original) {
+ operand0 = replacement;
+ found = true;
+ }
+ if (operand1 == original) {
+ operand1 = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Get the operator
+ * @return the operator, for example {@link Token#PLUS}
+ */
+
+ public int getOperator() {
+ return operator;
+ }
+
+ /**
+ * Get the operands
+ * @return the two operands of the binary expression, as an array of length 2
+ */
+
+ public Expression[] getOperands() {
+ return new Expression[] {operand0, operand1};
+ }
+
+ /**
+ * Determine the static cardinality. Default implementation returns [0..1] if either operand
+ * can be empty, or [1..1] otherwise.
+ */
+
+ public int computeCardinality() {
+ if (Cardinality.allowsZero(operand0.getCardinality()) ||
+ Cardinality.allowsZero(operand1.getCardinality())) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ } else {
+ return StaticProperty.EXACTLY_ONE;
+ }
+ }
+
+ /**
+ * Determine the special properties of this expression
+ * @return {@link StaticProperty#NON_CREATIVE}. This is overridden
+ * for some subclasses.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Determine whether a binary operator is commutative, that is, A op B = B op A.
+ * @param operator the operator, for example {@link Token#PLUS}
+ * @return true if the operator is commutative
+ */
+
+ protected static boolean isCommutative(int operator) {
+ return (operator == Token.AND ||
+ operator == Token.OR ||
+ operator == Token.UNION ||
+ operator == Token.INTERSECT ||
+ operator == Token.PLUS ||
+ operator == Token.MULT ||
+ operator == Token.EQUALS ||
+ operator == Token.FEQ ||
+ operator == Token.NE ||
+ operator == Token.FNE
+ );
+ }
+
+ /**
+ * Determine whether an operator is associative, that is, ((a^b)^c) = (a^(b^c))
+ * @param operator the operator, for example {@link Token#PLUS}
+ * @return true if the operator is associative
+ */
+
+ protected static boolean isAssociative(int operator) {
+ return (operator == Token.AND ||
+ operator == Token.OR ||
+ operator == Token.UNION ||
+ operator == Token.INTERSECT ||
+ operator == Token.PLUS ||
+ operator == Token.MULT
+ );
+ }
+ /**
+ * Test if one operator is the inverse of another, so that (A op1 B) is
+ * equivalent to (B op2 A). Commutative operators are the inverse of themselves
+ * and are therefore not listed here.
+ * @param op1 the first operator
+ * @param op2 the second operator
+ * @return true if the operators are the inverse of each other
+ */
+ protected static boolean isInverse(int op1, int op2) {
+ return op1 != op2 && op1 == Token.inverse(op2);
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof BinaryExpression) {
+ BinaryExpression b = (BinaryExpression)other;
+ if (operator == b.operator) {
+ if (operand0.equals(b.operand0) &&
+ operand1.equals(b.operand1)) {
+ return true;
+ }
+ if (isCommutative(operator) &&
+ operand0.equals(b.operand1) &&
+ operand1.equals(b.operand0)) {
+ return true;
+ }
+ if (isAssociative(operator) &&
+ pairwiseEqual(flattenExpression(new ArrayList(4)),
+ b.flattenExpression(new ArrayList(4)))) {
+ return true;
+ }
+ }
+ if (isInverse(operator, b.operator) &&
+ operand0.equals(b.operand1) &&
+ operand1.equals(b.operand0)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Flatten an expression with respect to an associative operator: for example
+ * the expression (a+b) + (c+d) becomes list(a,b,c,d), with the list in canonical
+ * order (sorted by hashCode)
+ * @param list a list provided by the caller to contain the result
+ * @return the list of expressions
+ */
+
+ private List flattenExpression(List list) {
+ if (operand0 instanceof BinaryExpression &&
+ ((BinaryExpression)operand0).operator == operator) {
+ ((BinaryExpression)operand0).flattenExpression(list);
+ } else {
+ int h = operand0.hashCode();
+ list.add(operand0);
+ int i = list.size()-1;
+ while (i > 0 && h > list.get(i-1).hashCode()) {
+ list.set(i, list.get(i-1));
+ list.set(i-1, operand0);
+ i--;
+ }
+ }
+ if (operand1 instanceof BinaryExpression &&
+ ((BinaryExpression)operand1).operator == operator) {
+ ((BinaryExpression)operand1).flattenExpression(list);
+ } else {
+ int h = operand1.hashCode();
+ list.add(operand1);
+ int i = list.size()-1;
+ while (i > 0 && h > list.get(i-1).hashCode()) {
+ list.set(i, list.get(i-1));
+ list.set(i-1, operand1);
+ i--;
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Compare whether two lists of expressions are pairwise equal
+ * @param a the first list of expressions
+ * @param b the second list of expressions
+ * @return true if the two lists are equal
+ */
+
+ private boolean pairwiseEqual(List a, List b) {
+ if (a.size() != b.size()) {
+ return false;
+ }
+ for (int i=0; i<a.size(); i++) {
+ if (!a.get(i).equals(b.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get a hashCode for comparing two expressions. Note that this hashcode gives the same
+ * result for (A op B) and for (B op A), whether or not the operator is commutative.
+ */
+
+ public int hashCode() {
+ // Ensure that an operator and its inverse get the same hash code,
+ // so that (A lt B) has the same hash code as (B gt A)
+ int op = Math.min(operator, Token.inverse(operator));
+ return ("BinaryExpression " + op).hashCode()
+ ^ operand0.hashCode()
+ ^ operand1.hashCode();
+ }
+
+ /**
+ * Represent the expression as a string. The resulting string will be a valid XPath 3.0 expression
+ * with no dependencies on namespace bindings other than the binding of the prefix "xs" to the XML Schema
+ * namespace.
+ * @return the expression as a string in XPath 3.0 syntax
+ */
+
+ public String toString() {
+ return ExpressionTool.parenthesize(operand0) +
+ " " + displayOperator() + " " +
+ ExpressionTool.parenthesize(operand1);
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ * @param out the output destination for the displayed expression tree
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("operator");
+ out.emitAttribute("op", displayOperator());
+ explainExtraAttributes(out);
+ operand0.explain(out);
+ operand1.explain(out);
+ out.endElement();
+ }
+
+ /**
+ * Add subclass-specific attributes to the expression tree explanation. Default implementation
+ * does nothing; this is provided for subclasses to override.
+ * @param out the output destination for the displayed expression tree
+ */
+
+ protected void explainExtraAttributes(ExpressionPresenter out) {}
+
+ /**
+ * Display the operator used by this binary expression
+ * @return String representation of the operator (for diagnostic display only)
+ */
+
+ protected String displayOperator() {
+ return Token.tokens[operator];
+ }
+
+}
+
diff --git a/sf/saxon/expr/Binding.java b/sf/saxon/expr/Binding.java
new file mode 100644
index 0000000..5b9f0b2
--- /dev/null
+++ b/sf/saxon/expr/Binding.java
@@ -0,0 +1,92 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+/**
+* Binding is a interface used to represent the run-time properties and methods
+* associated with a variable: specifically, a method to get the value
+* of the variable.
+*/
+
+public interface Binding {
+
+ /**
+ * Get the declared type of the variable
+ * @return the declared type
+ */
+
+ public SequenceType getRequiredType();
+
+ /**
+ * If the variable is bound to an integer, get the minimum and maximum possible values.
+ * Return null if unknown or not applicable
+ * @return a pair of integers containing the minimum and maximum values for the integer value;
+ * or null if the value is not an integer or the range is unknown
+ */
+
+ /*@Nullable*/ public IntegerValue[] getIntegerBoundsForVariable();
+
+ /**
+ * Evaluate the variable
+ *
+ * @param context the XPath dynamic evaluation context
+ * @return the result of evaluating the variable
+ * @throws net.sf.saxon.trans.XPathException if an error occurs while evaluating
+ * the variable
+ */
+
+ public Sequence evaluateVariable(XPathContext context) throws XPathException;
+
+ /**
+ * Indicate whether the binding is local or global. A global binding is one that has a fixed
+ * value for the life of a query or transformation; any other binding is local.
+ * @return true if the binding is global
+ */
+
+ public boolean isGlobal();
+
+ /**
+ * Test whether it is permitted to assign to the variable using the saxon:assign
+ * extension element. This will only be for an XSLT global variable where the extra
+ * attribute saxon:assignable="yes" is present.
+ * @return true if the binding is assignable
+ */
+
+ public boolean isAssignable();
+
+ /**
+ * If this is a local variable held on the local stack frame, return the corresponding slot number.
+ * In other cases, return -1.
+ * @return the slot number on the local stack frame
+ */
+
+ public int getLocalSlotNumber();
+
+ /**
+ * Get the name of the variable
+ * @return the name of the variable, as a structured QName
+ */
+
+ public StructuredQName getVariableQName();
+
+ /**
+ * Register a variable reference that refers to the variable bound in this expression
+ * @param isLoopingReference - true if the reference occurs within a loop, such as the predicate
+ * of a filter expression
+ */
+
+ public void addReference(boolean isLoopingReference);
+
+}
+
diff --git a/sf/saxon/expr/BindingReference.java b/sf/saxon/expr/BindingReference.java
new file mode 100644
index 0000000..aec0204
--- /dev/null
+++ b/sf/saxon/expr/BindingReference.java
@@ -0,0 +1,42 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.value.SequenceType;
+
+/**
+* BindingReference is a interface used to mark references to a variable declaration. The main
+* implementation is VariableReference, which represents a reference to a variable in an XPath
+* expression, but it is also used to represent a reference to a variable in a saxon:assign instruction.
+*/
+
+public interface BindingReference {
+
+ /**
+ * Fix up the static type of this variable reference; optionally, supply a constant value for
+ * the variable. Also supplies other static properties of the expression to which the variable
+ * is bound, for example whether it is an ordered node-set.
+ * @param type The static type of the variable reference, typically either the declared type
+ * of the variable, or the static type of the expression to which the variable is bound
+ * @param constantValue if non-null, indicates that the value of the variable is known at compile
+ * time, and supplies the value
+ * @param properties static properties of the expression to which the variable is bound
+ */
+
+ public void setStaticType(SequenceType type, /*@Nullable*/ GroundedValue constantValue, int properties);
+
+ /**
+ * Fix up this binding reference to a binding
+ * @param binding the Binding to which the variable refers
+ */
+
+ public void fixup(Binding binding);
+
+}
+
diff --git a/sf/saxon/expr/BooleanExpression.java b/sf/saxon/expr/BooleanExpression.java
new file mode 100644
index 0000000..b6154f0
--- /dev/null
+++ b/sf/saxon/expr/BooleanExpression.java
@@ -0,0 +1,194 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.functions.BooleanFn;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Boolean expression: two truth values combined using AND or OR.
+ */
+
+public abstract class BooleanExpression extends BinaryExpression implements Negatable {
+
+ /**
+ * Construct a boolean expression
+ * @param p1 the first operand
+ * @param operator one of {@link Token#AND} or {@link Token#OR}
+ * @param p2 the second operand
+ */
+
+ public BooleanExpression(Expression p1, int operator, Expression p2) {
+ super(p1, operator, p2);
+ }
+
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ *
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+ @Override
+ public String getExpressionName() {
+ return Token.tokens[getOperator()] + "-expression";
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.typeCheck(visitor, contextItemType);
+ if (e == this) {
+ XPathException err0 = TypeChecker.ebvError(operand0, visitor.getConfiguration().getTypeHierarchy());
+ if (err0 != null) {
+ err0.setLocator(this);
+ throw err0;
+ }
+ XPathException err1 = TypeChecker.ebvError(operand1, visitor.getConfiguration().getTypeHierarchy());
+ if (err1 != null) {
+ err1.setLocator(this);
+ throw err1;
+ }
+ // Precompute the EBV of any constant operand
+ if (operand0 instanceof Literal && !(((Literal)operand0).getValue() instanceof BooleanValue)) {
+ operand0 = Literal.makeLiteral(BooleanValue.get(operand0.effectiveBooleanValue(null)));
+ }
+ if (operand1 instanceof Literal && !(((Literal)operand1).getValue() instanceof BooleanValue)) {
+ operand1 = Literal.makeLiteral(BooleanValue.get(operand1.effectiveBooleanValue(null)));
+ }
+ }
+ return e;
+ }
+
+ /**
+ * Determine the static cardinality. Returns [1..1]
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ final Expression e = super.optimize(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ operand0 = ExpressionTool.unsortedIfHomogeneous(opt, operand0);
+ operand1 = ExpressionTool.unsortedIfHomogeneous(opt, operand1);
+
+ Expression op0 = BooleanFn.rewriteEffectiveBooleanValue(operand0, visitor, contextItemType);
+ if (op0 != null) {
+ operand0 = op0;
+ }
+ Expression op1 = BooleanFn.rewriteEffectiveBooleanValue(operand1, visitor, contextItemType);
+ if (op1 != null) {
+ operand1 = op1;
+ }
+ return this;
+ }
+
+ protected Expression forceToBoolean(Expression in, TypeHierarchy th) {
+ if (in.getItemType(th) == BuiltInAtomicType.BOOLEAN && in.getCardinality() == StaticProperty.ALLOWS_ONE) {
+ return in;
+ } else {
+ return SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{in});
+ }
+ }
+
+ /**
+ * Check whether this specific instance of the expression is negatable
+ *
+ * @return true if it is
+ */
+
+ public boolean isNegatable(ExpressionVisitor visitor) {
+ return true;
+ }
+
+ /**
+ * Return the negation of this boolean expression, that is, an expression that returns true
+ * when this expression returns false, and vice versa
+ *
+ * @return the negation of this expression
+ */
+
+ public abstract Expression negate();
+
+ /**
+ * Evaluate the expression
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Evaluate as a boolean.
+ */
+
+ public abstract boolean effectiveBooleanValue(XPathContext c) throws XPathException;
+
+ /**
+ * Determine the data type of the expression
+ * @return BuiltInAtomicType.BOOLEAN
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Construct a list containing the "anded" subexpressions of an expression:
+ * if the expression is (A and B and C), this returns (A, B, C).
+ * @param exp the expression to be decomposed
+ * @param list the list to which the subexpressions are to be added.
+ */
+
+ public static void listAndComponents(Expression exp, List list) {
+ if (exp instanceof BooleanExpression && ((BooleanExpression)exp).getOperator() == Token.AND) {
+ for (Iterator iter = exp.iterateSubExpressions(); iter.hasNext();) {
+ listAndComponents((Expression)iter.next(), list);
+ }
+ } else {
+ list.add(exp);
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/Calculator.java b/sf/saxon/expr/Calculator.java
new file mode 100644
index 0000000..83969a3
--- /dev/null
+++ b/sf/saxon/expr/Calculator.java
@@ -0,0 +1,1116 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * This class evaluates arithmetic expressions; it acts as a helper class to the ArithmeticExpression
+ * class. There are many subclasses for the different kinds of arithmetic expression, and static methods
+ * that allow the right subclass to be selected, either at compile time or at run time.
+ */
+
+public abstract class Calculator implements Serializable {
+
+ public static final int PLUS = 0;
+ public static final int MINUS = 1;
+ public static final int TIMES = 2;
+ public static final int DIV = 3;
+ public static final int MOD = 4;
+ public static final int IDIV = 5;
+
+ /**
+ * Calculators used for the six operators when the static type information does not allow
+ * a more specific calculator to be chosen
+ */
+
+ public final static Calculator[] ANY_ANY = {
+ new AnyPlusAny(),
+ new AnyMinusAny(),
+ new AnyTimesAny(),
+ new AnyDivAny(),
+ new AnyModAny(),
+ new AnyIdivAny()
+ };
+
+ // NOTE: these fields are public because they are referenced from the Java code generated by the XQuery compiler
+
+ /**
+ * Calculators used when the first operand is a double
+ */
+
+ public final static Calculator[] DOUBLE_DOUBLE = {
+ new DoublePlusDouble(),
+ new DoubleMinusDouble(),
+ new DoubleTimesDouble(),
+ new DoubleDivDouble(),
+ new DoubleModDouble(),
+ new DoubleIdivDouble()
+ };
+
+ public final static Calculator[] DOUBLE_FLOAT = DOUBLE_DOUBLE;
+ public final static Calculator[] DOUBLE_DECIMAL = DOUBLE_DOUBLE;
+ //public final static Calculator[] DOUBLE_PDECIMAL = DOUBLE_DOUBLE;
+ public final static Calculator[] DOUBLE_INTEGER = DOUBLE_DOUBLE;
+
+ /**
+ * Calculators used when the first operand is a float
+ */
+
+ public final static Calculator[] FLOAT_DOUBLE = DOUBLE_DOUBLE;
+ public final static Calculator[] FLOAT_FLOAT = {
+ new FloatPlusFloat(),
+ new FloatMinusFloat(),
+ new FloatTimesFloat(),
+ new FloatDivFloat(),
+ new FloatModFloat(),
+ new FloatIdivFloat()
+ };
+ public final static Calculator[] FLOAT_DECIMAL = FLOAT_FLOAT;
+ //public final static Calculator[] FLOAT_PDECIMAL = FLOAT_FLOAT;
+ public final static Calculator[] FLOAT_INTEGER = FLOAT_FLOAT;
+
+ /**
+ * Calculators used when the first operand is a decimal
+ */
+
+ public final static Calculator[] DECIMAL_DOUBLE = DOUBLE_DOUBLE;
+ public final static Calculator[] DECIMAL_FLOAT = FLOAT_FLOAT;
+ public final static Calculator[] DECIMAL_DECIMAL = {
+ new DecimalPlusDecimal(),
+ new DecimalMinusDecimal(),
+ new DecimalTimesDecimal(),
+ new DecimalDivDecimal(),
+ new DecimalModDecimal(),
+ new DecimalIdivDecimal()
+ };
+ public final static Calculator[] DECIMAL_INTEGER = DECIMAL_DECIMAL;
+
+ /**
+ * Calculators used when the first operand is an integer
+ */
+
+ public final static Calculator[] INTEGER_DOUBLE = DOUBLE_DOUBLE;
+ public final static Calculator[] INTEGER_FLOAT = FLOAT_FLOAT;
+ public final static Calculator[] INTEGER_DECIMAL = DECIMAL_DECIMAL;
+ public final static Calculator[] INTEGER_INTEGER = {
+ new IntegerPlusInteger(),
+ new IntegerMinusInteger(),
+ new IntegerTimesInteger(),
+ new IntegerDivInteger(),
+ new IntegerModInteger(),
+ new IntegerIdivInteger()
+ };
+
+/**
+ * Calculators used when the first operand is a precisionDecimal
+ */
+
+ //public final static Calculator[] PDECIMAL_DOUBLE = DOUBLE_DOUBLE;
+ //public final static Calculator[] PDECIMAL_FLOAT = FLOAT_FLOAT;
+// public final static Calculator[] PDECIMAL_PDECIMAL = {
+// new PDecimalPlusPDecimal(),
+// new PDecimalMinusPDecimal(),
+// new PDecimalTimesPDecimal(),
+// new PDecimalDivPDecimal(),
+// new PDecimalModPDecimal(),
+// new DecimalIdivDecimal() // sic
+// };
+ // public final static Calculator[] PDECIMAL_DECIMAL = PDECIMAL_PDECIMAL;
+ //public final static Calculator[] PDECIMAL_INTEGER = PDECIMAL_PDECIMAL;
+
+ //public final static Calculator[] DECIMAL_PDECIMAL = PDECIMAL_PDECIMAL;
+
+ /**
+ * Calculators used when both operands are xs:dateTime, xs:date, or xs:time
+ */
+
+ /*@Nullable*/ public final static Calculator[] DATETIME_DATETIME = {
+ null,
+ new DateTimeMinusDateTime(),
+ null, null, null, null
+ };
+
+ /**
+ * Calculators used when the first operand is xs:dateTime, xs:date, or xs:time,
+ * and the second is a duration
+ */
+
+ public final static Calculator[] DATETIME_DURATION = {
+ new DateTimePlusDuration(),
+ new DateTimeMinusDuration(),
+ null, null, null, null
+ };
+
+ /**
+ * Calculators used when the second operand is xs:dateTime, xs:date, or xs:time,
+ * and the first is a duration
+ */
+
+ public final static Calculator[] DURATION_DATETIME = {
+ new DurationPlusDateTime(),
+ null, null, null, null, null
+ };
+
+ /**
+ * Calculators used when the both operands are durations
+ */
+
+ public final static Calculator[] DURATION_DURATION = {
+ new DurationPlusDuration(),
+ new DurationMinusDuration(),
+ null,
+ new DurationDivDuration(),
+ null, null
+ };
+
+ /**
+ * Calculators used when the first operand is a duration and the second is numeric
+ */
+
+ public final static Calculator[] DURATION_NUMERIC = {
+ null, null,
+ new DurationTimesNumeric(),
+ new DurationDivNumeric(),
+ null, null
+ };
+
+ /**
+ * Calculators used when the second operand is a duration and the first is numeric
+ */
+
+ public final static Calculator[] NUMERIC_DURATION = {
+ null, null,
+ new NumericTimesDuration(),
+ null, null, null
+ };
+
+ /**
+ * Table mapping argument types to the Calculator class used to implement them
+ */
+
+ private static IntHashMap<Calculator[]> table = new IntHashMap(100);
+ private static IntHashMap<String> nameTable = new IntHashMap(100);
+
+ private static void def(int typeA, int typeB, Calculator[] calculatorSet, String setName) {
+ int key = (typeA & 0xffff)<<16 | (typeB & 0xffff);
+ table.put(key, calculatorSet);
+ nameTable.put(key, setName);
+ // As well as the entries added directly, we also add derived entries for other types
+ // considered primitive
+ if (typeA == StandardNames.XS_DURATION) {
+ def(StandardNames.XS_DAY_TIME_DURATION, typeB, calculatorSet, setName);
+ def(StandardNames.XS_YEAR_MONTH_DURATION, typeB, calculatorSet, setName);
+ }
+ if (typeB == StandardNames.XS_DURATION) {
+ def(typeA, StandardNames.XS_DAY_TIME_DURATION, calculatorSet, setName);
+ def(typeA, StandardNames.XS_YEAR_MONTH_DURATION, calculatorSet, setName);
+ }
+ if (typeA == StandardNames.XS_DATE_TIME) {
+ def(StandardNames.XS_DATE, typeB, calculatorSet, setName);
+ def(StandardNames.XS_TIME, typeB, calculatorSet, setName);
+ }
+ if (typeB == StandardNames.XS_DATE_TIME) {
+ def(typeA, StandardNames.XS_DATE, calculatorSet, setName);
+ def(typeA, StandardNames.XS_TIME, calculatorSet, setName);
+ }
+ if (typeA == StandardNames.XS_DOUBLE) {
+ def(StandardNames.XS_UNTYPED_ATOMIC, typeB, calculatorSet, setName);
+ }
+ if (typeB == StandardNames.XS_DOUBLE) {
+ def(typeA, StandardNames.XS_UNTYPED_ATOMIC, calculatorSet, setName);
+ }
+ }
+
+ /**
+ * Factory method to get a calculator for a given combination of types
+ * @param typeA fingerprint of the primitive type of the first operand
+ * @param typeB fingerprint of the primitive type of the second operand
+ * @param operator the arithmetic operator in use
+ * @param mustResolve indicates that a concrete Calculator is required (rather than
+ * an ANY_ANY calculator which needs to be further resolved at run-time)
+ * @return null if no suitable Calculator can be found.
+ */
+
+ public static Calculator getCalculator(int typeA, int typeB, int operator, boolean mustResolve) {
+ int key = (typeA & 0xffff)<<16 | (typeB & 0xffff);
+ Calculator[] set = table.get(key);
+ if (set == null) {
+ if (mustResolve) {
+ return null;
+ } else {
+ return ANY_ANY[operator];
+ }
+ } else {
+ return set[operator];
+ }
+ }
+
+ /**
+ * Get the name of the calculator set for a given combination of types
+ * @param typeA the fingerprint of the primitive type of the first operand
+ * @param typeB the fingerprint of the primitive type of the second operand
+ * @return null if no suitable Calculator can be found.
+ */
+
+ public static String getCalculatorSetName(int typeA, int typeB) {
+ int key = (typeA & 0xffff)<<16 | (typeB & 0xffff);
+ return nameTable.get(key);
+ }
+
+ static {
+ def(StandardNames.XS_DOUBLE, StandardNames.XS_DOUBLE, DOUBLE_DOUBLE, "DOUBLE_DOUBLE");
+ def(StandardNames.XS_DOUBLE, StandardNames.XS_FLOAT, DOUBLE_FLOAT, "DOUBLE_FLOAT");
+ def(StandardNames.XS_DOUBLE, StandardNames.XS_DECIMAL, DOUBLE_DECIMAL, "DOUBLE_DECIMAL");
+ //def(StandardNames.XS_DOUBLE, StandardNames.XS_PRECISION_DECIMAL, DOUBLE_PDECIMAL, "DOUBLE_PDECIMAL");
+ def(StandardNames.XS_DOUBLE, StandardNames.XS_INTEGER, DOUBLE_INTEGER, "DOUBLE_INTEGER");
+ def(StandardNames.XS_FLOAT, StandardNames.XS_DOUBLE, FLOAT_DOUBLE, "FLOAT_DOUBLE");
+ def(StandardNames.XS_FLOAT, StandardNames.XS_FLOAT, FLOAT_FLOAT, "FLOAT_FLOAT");
+ def(StandardNames.XS_FLOAT, StandardNames.XS_DECIMAL, FLOAT_DECIMAL, "FLOAT_DECIMAL");
+ //def(StandardNames.XS_FLOAT, StandardNames.XS_PRECISION_DECIMAL, FLOAT_PDECIMAL, "FLOAT_PDECIMAL");
+ def(StandardNames.XS_FLOAT, StandardNames.XS_INTEGER, FLOAT_INTEGER, "FLOAT_INTEGER");
+ def(StandardNames.XS_DECIMAL, StandardNames.XS_DOUBLE, DECIMAL_DOUBLE, "DECIMAL_DOUBLE");
+ def(StandardNames.XS_DECIMAL, StandardNames.XS_FLOAT, DECIMAL_FLOAT, "DECIMAL_FLOAT");
+ def(StandardNames.XS_DECIMAL, StandardNames.XS_DECIMAL, DECIMAL_DECIMAL, "DECIMAL_DECIMAL");
+ //def(StandardNames.XS_DECIMAL, StandardNames.XS_PRECISION_DECIMAL, DECIMAL_PDECIMAL, "DECIMAL_PDECIMAL");
+ def(StandardNames.XS_DECIMAL, StandardNames.XS_INTEGER, DECIMAL_INTEGER, "DECIMAL_INTEGER");
+ def(StandardNames.XS_INTEGER, StandardNames.XS_DOUBLE, INTEGER_DOUBLE, "INTEGER_DOUBLE");
+ def(StandardNames.XS_INTEGER, StandardNames.XS_FLOAT, INTEGER_FLOAT, "INTEGER_FLOAT");
+ def(StandardNames.XS_INTEGER, StandardNames.XS_DECIMAL, INTEGER_DECIMAL, "INTEGER_DECIMAL");
+ def(StandardNames.XS_INTEGER, StandardNames.XS_INTEGER, INTEGER_INTEGER, "INTEGER_INTEGER");
+ //def(StandardNames.XS_PRECISION_DECIMAL, StandardNames.XS_DOUBLE, PDECIMAL_DOUBLE, "PDECIMAL_DOUBLE");
+ //def(StandardNames.XS_PRECISION_DECIMAL, StandardNames.XS_FLOAT, PDECIMAL_FLOAT, "PDECIMAL_FLOAT");
+ //def(StandardNames.XS_PRECISION_DECIMAL, StandardNames.XS_DECIMAL, PDECIMAL_DECIMAL, "PDECIMAL_DECIMAL");
+ //def(StandardNames.XS_PRECISION_DECIMAL, StandardNames.XS_PRECISION_DECIMAL, PDECIMAL_PDECIMAL, "PDECIMAL_PDECIMAL");
+ //def(StandardNames.XS_PRECISION_DECIMAL, StandardNames.XS_INTEGER, PDECIMAL_INTEGER, "PDECIMAL_INTEGER");
+ def(StandardNames.XS_DATE_TIME, StandardNames.XS_DATE_TIME, DATETIME_DATETIME, "DATETIME_DATETIME");
+ def(StandardNames.XS_DATE_TIME, StandardNames.XS_DURATION, DATETIME_DURATION, "DATETIME_DURATION");
+ def(StandardNames.XS_DURATION, StandardNames.XS_DATE_TIME, DURATION_DATETIME, "DURATION_DATETIME");
+ def(StandardNames.XS_DURATION, StandardNames.XS_DURATION, DURATION_DURATION, "DURATION_DURATION");
+ def(StandardNames.XS_DURATION, StandardNames.XS_DOUBLE, DURATION_NUMERIC, "DURATION_NUMERIC");
+ def(StandardNames.XS_DURATION, StandardNames.XS_FLOAT, DURATION_NUMERIC, "DURATION_NUMERIC");
+ def(StandardNames.XS_DURATION, StandardNames.XS_DECIMAL, DURATION_NUMERIC, "DURATION_NUMERIC");
+ def(StandardNames.XS_DURATION, StandardNames.XS_INTEGER, DURATION_NUMERIC, "DURATION_NUMERIC");
+ def(StandardNames.XS_DOUBLE, StandardNames.XS_DURATION, NUMERIC_DURATION, "NUMERIC_DURATION");
+ def(StandardNames.XS_FLOAT, StandardNames.XS_DURATION, NUMERIC_DURATION, "NUMERIC_DURATION");
+ def(StandardNames.XS_DECIMAL, StandardNames.XS_DURATION, NUMERIC_DURATION, "NUMERIC_DURATION");
+ //def(StandardNames.XS_PRECISION_DECIMAL, StandardNames.XS_DURATION, NUMERIC_DURATION, "NUMERIC_DURATION");
+ def(StandardNames.XS_INTEGER, StandardNames.XS_DURATION, NUMERIC_DURATION, "NUMERIC_DURATION");
+ }
+
+ /**
+ * Perform an arithmetic operation
+ * @param a the first operand. Must not be null, and must be an instance of the type implied by the
+ * class name.
+ * @param b the second operand. Must not be null, and must be an instance of the type implied by the
+ * class name.
+ * @param c the XPath dynamic evaluation context
+ * @throws XPathException in the event of an arithmetic error
+ * @return the result of the computation, as a value of the correct primitive type
+ */
+
+ public abstract AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException;
+
+ /**
+ * Get the type of the result of the calculator, given arguments types typeA and typeB
+ * @param typeA the type of the first operand
+ * @param typeB the type of the second operand
+ * @return the type of the result
+ */
+
+ public abstract AtomicType getResultType(AtomicType typeA, AtomicType typeB);
+
+ /**
+ * Arithmetic: anyAtomicType + AnyAtomicType
+ */
+
+ public static class AnyPlusAny extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ Calculator calc = getCalculator(
+ a.getItemType().getPrimitiveType(), b.getItemType().getPrimitiveType(), PLUS, true);
+ if (calc == null) {
+ throw new XPathException("Unsuitable types for + operation (" +
+ Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c);
+ } else {
+ return calc.compute(a, b, c);
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+ }
+
+ /**
+ * Arithmetic: anyAtomicType - AnyAtomicType
+ */
+
+ public static class AnyMinusAny extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ Calculator calc = getCalculator(
+ a.getItemType().getPrimitiveType(), b.getItemType().getPrimitiveType(), MINUS, true);
+ if (calc == null) {
+ throw new XPathException("Unsuitable types for - operation (" +
+ Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c);
+ } else {
+ return calc.compute(a, b, c);
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+ }
+
+ /**
+ * Arithmetic: anyAtomicType * AnyAtomicType
+ */
+
+ public static class AnyTimesAny extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ Calculator calc = getCalculator(
+ a.getItemType().getPrimitiveType(), b.getItemType().getPrimitiveType(), TIMES, true);
+ if (calc == null) {
+ throw new XPathException("Unsuitable types for * operation (" +
+ Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c);
+ } else {
+ return calc.compute(a, b, c);
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+ }
+
+ /**
+ * Arithmetic: anyAtomicType div AnyAtomicType
+ */
+
+ public static class AnyDivAny extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ Calculator calc = getCalculator(
+ a.getItemType().getPrimitiveType(), b.getItemType().getPrimitiveType(), DIV, true);
+ if (calc == null) {
+ throw new XPathException("Unsuitable types for div operation (" +
+ Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c);
+ } else {
+ return calc.compute(a, b, c);
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+ }
+
+ /**
+ * Arithmetic: anyAtomicType mod AnyAtomicType
+ */
+
+ public static class AnyModAny extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ Calculator calc = getCalculator(
+ a.getItemType().getPrimitiveType(), b.getItemType().getPrimitiveType(), MOD, true);
+ if (calc == null) {
+ throw new XPathException("Unsuitable types for mod operation (" +
+ Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c);
+ } else {
+ return calc.compute(a, b, c);
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+ }
+
+ /**
+ * Arithmetic: anyAtomicType idiv AnyAtomicType
+ */
+
+ public static class AnyIdivAny extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ Calculator calc = getCalculator(
+ a.getItemType().getPrimitiveType(), b.getItemType().getPrimitiveType(), IDIV, true);
+ if (calc == null) {
+ throw new XPathException("Unsuitable types for idiv operation (" +
+ Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ")", "XPTY0004", c);
+ } else {
+ return calc.compute(a, b, c);
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+ }
+
+ /**
+ * Arithmetic: double + double (including types that promote to double)
+ */
+
+ public static class DoublePlusDouble extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new DoubleValue(((NumericValue)a).getDoubleValue() + ((NumericValue)b).getDoubleValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DOUBLE;
+ }
+ }
+
+ /**
+ * Arithmetic: double - double (including types that promote to double)
+ */
+
+ public static class DoubleMinusDouble extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new DoubleValue(((NumericValue)a).getDoubleValue() - ((NumericValue)b).getDoubleValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DOUBLE;
+ }
+ }
+
+ /**
+ * Arithmetic: double * double (including types that promote to double)
+ */
+
+ public static class DoubleTimesDouble extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new DoubleValue(((NumericValue)a).getDoubleValue() * ((NumericValue)b).getDoubleValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DOUBLE;
+ }
+ }
+
+ /**
+ * Arithmetic: double div double (including types that promote to double)
+ */
+
+ public static class DoubleDivDouble extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new DoubleValue(((NumericValue)a).getDoubleValue() / ((NumericValue)b).getDoubleValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DOUBLE;
+ }
+ }
+
+ /**
+ * Arithmetic: double mod double (including types that promote to double)
+ */
+
+ public static class DoubleModDouble extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new DoubleValue(((NumericValue)a).getDoubleValue() % ((NumericValue)b).getDoubleValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DOUBLE;
+ }
+ }
+
+ /**
+ * Arithmetic: double idiv double (including types that promote to double)
+ */
+
+ private static class DoubleIdivDouble extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ double A = ((NumericValue)a).getDoubleValue();
+ double B = ((NumericValue)b).getDoubleValue();
+ if (B == 0.0) {
+ throw new XPathException("Integer division by zero", "FOAR0001", c);
+ }
+ if (Double.isNaN(A) || Double.isInfinite(A)) {
+ throw new XPathException("First operand of idiv is NaN or infinity", "FOAR0002", c);
+ }
+ if (Double.isNaN(B)) {
+ throw new XPathException("Second operand of idiv is NaN", "FOAR0002", c);
+ }
+ return IntegerValue.makeIntegerValue(new DoubleValue(A / B)).asAtomic();
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ /**
+ * Arithmetic: float + float (including types that promote to float)
+ */
+
+ public static class FloatPlusFloat extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new FloatValue(((NumericValue)a).getFloatValue() + ((NumericValue)b).getFloatValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.FLOAT;
+ }
+ }
+
+ /**
+ * Arithmetic: float - float (including types that promote to float)
+ */
+
+ public static class FloatMinusFloat extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new FloatValue(((NumericValue)a).getFloatValue() - ((NumericValue)b).getFloatValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.FLOAT;
+ }
+ }
+
+ /**
+ * Arithmetic: float * float (including types that promote to float)
+ */
+
+ public static class FloatTimesFloat extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new FloatValue(((NumericValue)a).getFloatValue() * ((NumericValue)b).getFloatValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.FLOAT;
+ }
+ }
+
+ /**
+ * Arithmetic: float div float (including types that promote to float)
+ */
+
+ public static class FloatDivFloat extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new FloatValue(((NumericValue)a).getFloatValue() / ((NumericValue)b).getFloatValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.FLOAT;
+ }
+ }
+
+ /**
+ * Arithmetic: float mod float (including types that promote to float)
+ */
+
+ public static class FloatModFloat extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return new FloatValue(((NumericValue)a).getFloatValue() % ((NumericValue)b).getFloatValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.FLOAT;
+ }
+ }
+
+ /**
+ * Arithmetic: float idiv float (including types that promote to float)
+ */
+
+ public static class FloatIdivFloat extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ float A = ((NumericValue)a).getFloatValue();
+ float B = ((NumericValue)b).getFloatValue();
+ if (B == 0.0) {
+ throw new XPathException("Integer division by zero", "FOAR0001", c);
+ }
+ if (Float.isNaN(A) || Float.isInfinite(A)) {
+ throw new XPathException("First operand of idiv is NaN or infinity", "FOAR0002", c);
+ }
+ if (Float.isNaN(B)) {
+ throw new XPathException("Second operand of idiv is NaN", "FOAR0002", c);
+ }
+ return Converter.FLOAT_TO_INTEGER.convert(new FloatValue(A / B)).asAtomic();
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ /**
+ * Arithmetic: decimal + decimal (including types that promote to decimal, that is, integer)
+ */
+
+ public static class DecimalPlusDecimal extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ if (a instanceof IntegerValue && b instanceof IntegerValue) {
+ return ((IntegerValue)a).plus((IntegerValue)b);
+ } else {
+ return new DecimalValue(
+ ((NumericValue)a).getDecimalValue().add(((NumericValue)b).getDecimalValue()));
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DECIMAL;
+ }
+ }
+
+ /**
+ * Arithmetic: decimal - decimal (including types that promote to decimal, that is, integer)
+ */
+
+ public static class DecimalMinusDecimal extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ if (a instanceof IntegerValue && b instanceof IntegerValue) {
+ return ((IntegerValue)a).minus((IntegerValue)b);
+ } else {
+ return new DecimalValue(
+ ((NumericValue)a).getDecimalValue().subtract(((NumericValue)b).getDecimalValue()));
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DECIMAL;
+ }
+ }
+
+ /**
+ * Arithmetic: decimal * decimal (including types that promote to decimal, that is, integer)
+ */
+
+ public static class DecimalTimesDecimal extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ if (a instanceof IntegerValue && b instanceof IntegerValue) {
+ return ((IntegerValue)a).times((IntegerValue)b);
+ } else {
+ return new DecimalValue(
+ ((NumericValue)a).getDecimalValue().multiply(((NumericValue)b).getDecimalValue()));
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DECIMAL;
+ }
+ }
+
+ /**
+ * Arithmetic: decimal div decimal (including types that promote to decimal, that is, integer)
+ */
+
+ public static class DecimalDivDecimal extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return decimalDivide((NumericValue)a, (NumericValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DECIMAL;
+ }
+ }
+
+ public static DecimalValue decimalDivide(NumericValue a, NumericValue b) throws XPathException {
+ final BigDecimal A = a.getDecimalValue();
+ final BigDecimal B = b.getDecimalValue();
+ int scale = Math.max(DecimalValue.DIVIDE_PRECISION,
+ Math.max(A.scale(), B.scale()));
+ try {
+ BigDecimal result = A.divide(B, scale, BigDecimal.ROUND_HALF_DOWN);
+ return new DecimalValue(result);
+ } catch (ArithmeticException err) {
+ if (b.compareTo(0) == 0) {
+ throw new XPathException("Decimal divide by zero", "FOAR0001");
+ } else {
+ throw err;
+ }
+ }
+ }
+
+ /**
+ * Arithmetic: decimal mod decimal (including types that promote to decimal, that is, integer)
+ */
+
+ public static class DecimalModDecimal extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ if (a instanceof IntegerValue && b instanceof IntegerValue) {
+ return ((IntegerValue)a).mod((IntegerValue)b);
+ }
+
+ final BigDecimal A = ((NumericValue)a).getDecimalValue();
+ final BigDecimal B = ((NumericValue)b).getDecimalValue();
+ try {
+ //BigDecimal quotient = A.divide(B, 0, BigDecimal.ROUND_DOWN);
+ //BigDecimal remainder = A.subtract(quotient.multiply(B));
+ return new DecimalValue(A.remainder(B));
+ } catch (ArithmeticException err) {
+ if (((NumericValue)b).compareTo(0) == 0) {
+ throw new XPathException("Decimal modulo zero", "FOAR0001", c);
+ } else {
+ throw err;
+ }
+ }
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DECIMAL;
+ }
+ }
+
+ /**
+ * Arithmetic: decimal idiv decimal (including types that promote to decimal, that is, integer)
+ */
+
+ public static class DecimalIdivDecimal extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ if (a instanceof IntegerValue && b instanceof IntegerValue) {
+ return ((IntegerValue)a).idiv((IntegerValue)b);
+ }
+
+ final BigDecimal A = ((NumericValue)a).getDecimalValue();
+ final BigDecimal B = ((NumericValue)b).getDecimalValue();
+ if (B.signum() == 0) {
+ throw new XPathException("Integer division by zero", "FOAR0001", c);
+ }
+ //BigInteger quot = A.divide(B, 0, BigDecimal.ROUND_DOWN).toBigInteger();
+ BigInteger quot = A.divideToIntegralValue(B).toBigInteger();
+ return BigIntegerValue.makeIntegerValue(quot);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ /**
+ * Arithmetic: precisionDecimal + precisionDecimal (including types that promote to decimal, that is, integer)
+ */
+
+// private static class PDecimalPlusPDecimal extends Calculator {
+// public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+// if (a instanceof IntegerValue && b instanceof IntegerValue) {
+// return ((IntegerValue)a).plus((IntegerValue)b);
+// } else if (a.isNaN() || b.isNaN()) {
+// return (PrecisionDecimalValue.NaN);
+// } else if ((a instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)a).isInfinite()) ||
+// (b instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)b).isInfinite()) ) {
+// return new PrecisionDecimalValue(((DoubleValue)DOUBLE_DOUBLE[PLUS].compute(a, b, c)).getDoubleValue());
+// } else {
+// return new PrecisionDecimalValue(
+// ((NumericValue)a).getDecimalValue().add(((NumericValue)b).getDecimalValue()));
+// }
+// }
+// public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+// return BuiltInAtomicType.PRECISION_DECIMAL;
+// }
+// }
+
+ /**
+ * Arithmetic: precisionDecimal - precisionDecimal (including types that promote to decimal, that is, integer)
+ */
+
+// private static class PDecimalMinusPDecimal extends Calculator {
+// public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+// if (a instanceof IntegerValue && b instanceof IntegerValue) {
+// return ((IntegerValue)a).minus((IntegerValue)b);
+// } else if (a.isNaN() || b.isNaN()) {
+// return (PrecisionDecimalValue.NaN);
+// } else if ((a instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)a).isInfinite()) ||
+// (b instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)b).isInfinite()) ) {
+// return new PrecisionDecimalValue(((DoubleValue)DOUBLE_DOUBLE[MINUS].compute(a, b, c)).getDoubleValue());
+// } else {
+// return new PrecisionDecimalValue(
+// ((NumericValue)a).getDecimalValue().subtract(((NumericValue)b).getDecimalValue()));
+// }
+// }
+// public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+// return BuiltInAtomicType.PRECISION_DECIMAL;
+// }
+// }
+
+ /**
+ * Arithmetic: precisionDecimal * precisionDecimal (including types that promote to decimal, that is, integer)
+ */
+
+// private static class PDecimalTimesPDecimal extends Calculator {
+// public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+// if (a instanceof IntegerValue && b instanceof IntegerValue) {
+// return ((IntegerValue)a).times((IntegerValue)b);
+// } else if (a.isNaN() || b.isNaN()) {
+// return (PrecisionDecimalValue.NaN);
+// } else if ((a instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)a).isInfinite()) ||
+// (b instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)b).isInfinite()) ) {
+// return new PrecisionDecimalValue(((DoubleValue)DOUBLE_DOUBLE[TIMES].compute(a, b, c)).getDoubleValue());
+// } else {
+// return new PrecisionDecimalValue(
+// ((NumericValue)a).getDecimalValue().multiply(((NumericValue)b).getDecimalValue()));
+// }
+// }
+// public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+// return BuiltInAtomicType.PRECISION_DECIMAL;
+// }
+// }
+
+ /**
+ * Arithmetic: precisionDecimal div precisionDecimal (including types that promote to decimal, that is, integer)
+ */
+
+// private static class PDecimalDivPDecimal extends Calculator {
+// public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+// if (a.isNaN() || b.isNaN()) {
+// return (PrecisionDecimalValue.NaN);
+// } else if ((a instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)a).isInfinite()) ||
+// (b instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)b).isInfinite()) ) {
+// return new PrecisionDecimalValue(((DoubleValue)DOUBLE_DOUBLE[DIV].compute(a, b, c)).getDoubleValue());
+// }
+// final BigDecimal A = ((NumericValue)a).getDecimalValue();
+// final BigDecimal B = ((NumericValue)b).getDecimalValue();
+// int scale = Math.max(DecimalValue.DIVIDE_PRECISION,
+// Math.max(A.scale(), B.scale()));
+// try {
+// BigDecimal result = A.divide(B, scale, BigDecimal.ROUND_HALF_DOWN);
+// return new PrecisionDecimalValue(result);
+// } catch (ArithmeticException err) {
+// if (((NumericValue)b).compareTo(0) == 0) {
+// throw new XPathException("PrecisionDecimal divide by zero", "FOAR0001", c);
+// } else {
+// throw err;
+// }
+// }
+// }
+// public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+// return BuiltInAtomicType.DECIMAL;
+// }
+// }
+
+ /**
+ * Arithmetic: precisionDecimal mod precisionDecimal (including types that promote to decimal, that is, integer)
+ */
+
+// public static class PDecimalModPDecimal extends Calculator {
+// public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+// if (a instanceof IntegerValue && b instanceof IntegerValue) {
+// return ((IntegerValue)a).mod((IntegerValue)b);
+// } else if (a.isNaN() || b.isNaN()) {
+// return (PrecisionDecimalValue.NaN);
+// } else if ((a instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)a).isInfinite()) ||
+// (b instanceof PrecisionDecimalValue && ((PrecisionDecimalValue)b).isInfinite()) ) {
+// return new PrecisionDecimalValue(((DoubleValue)DOUBLE_DOUBLE[MOD].compute(a, b, c)).getDoubleValue());
+// }
+//
+// final BigDecimal A = ((NumericValue)a).getDecimalValue();
+// final BigDecimal B = ((NumericValue)b).getDecimalValue();
+// try {
+// BigDecimal quotient = A.divide(B, 0, BigDecimal.ROUND_DOWN);
+// BigDecimal remainder = A.subtract(quotient.multiply(B));
+// return new PrecisionDecimalValue(remainder);
+// } catch (ArithmeticException err) {
+// if (((NumericValue)b).compareTo(0) == 0) {
+// throw new XPathException("Decimal modulo zero", "FOAR0001", c);
+// } else {
+// throw err;
+// }
+// }
+// }
+// public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+// return BuiltInAtomicType.DECIMAL;
+// }
+// }
+
+ /**
+ * Arithmetic: integer + integer
+ */
+
+ public static class IntegerPlusInteger extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((IntegerValue)a).plus((IntegerValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ /**
+ * Arithmetic: integer - integer
+ */
+
+ public static class IntegerMinusInteger extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((IntegerValue)a).minus((IntegerValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ /**
+ * Arithmetic: integer * integer
+ */
+
+ public static class IntegerTimesInteger extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((IntegerValue)a).times((IntegerValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ /**
+ * Arithmetic: integer div integer
+ */
+
+ public static class IntegerDivInteger extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((IntegerValue)a).div((IntegerValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DECIMAL;
+ }
+ }
+
+ /**
+ * Arithmetic: integer mod integer
+ */
+
+ public static class IntegerModInteger extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((IntegerValue)a).mod((IntegerValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ /**
+ * Arithmetic: integer idiv integer
+ */
+
+ public static class IntegerIdivInteger extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((IntegerValue)a).idiv((IntegerValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ /**
+ * Arithmetic: date/time/dateTime - date/time/dateTime
+ */
+
+ private static class DateTimeMinusDateTime extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((CalendarValue)a).subtract((CalendarValue)b, c);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DAY_TIME_DURATION;
+ }
+ }
+
+ /**
+ * Arithmetic: date/time/dateTime + duration
+ */
+
+ private static class DateTimePlusDuration extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((CalendarValue)a).add((DurationValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return typeA;
+ }
+ }
+
+ /**
+ * Arithmetic: date/time/dateTime - duration
+ */
+
+ private static class DateTimeMinusDuration extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((CalendarValue)a).add(((DurationValue)b).multiply(-1.0));
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return typeA;
+ }
+ }
+
+ /**
+ * Arithmetic: duration + date/time/dateTime
+ */
+
+ private static class DurationPlusDateTime extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((CalendarValue)b).add((DurationValue)a);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return typeB;
+ }
+ }
+
+ /**
+ * Arithmetic: duration + duration
+ */
+
+ private static class DurationPlusDuration extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((DurationValue)a).add((DurationValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return typeA;
+ }
+ }
+
+ /**
+ * Arithmetic: duration - duration
+ */
+
+ private static class DurationMinusDuration extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((DurationValue)a).subtract((DurationValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return typeA;
+ }
+ }
+
+ /**
+ * Arithmetic: duration div duration
+ */
+
+ private static class DurationDivDuration extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((DurationValue)a).divide((DurationValue)b);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return BuiltInAtomicType.DECIMAL;
+ }
+ }
+
+ /**
+ * Arithmetic: duration * number
+ */
+
+ private static class DurationTimesNumeric extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((DurationValue)a).multiply(((NumericValue)b).getDoubleValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return typeA;
+ }
+ }
+
+ /**
+ * Arithmetic: number * duration
+ */
+
+ private static class NumericTimesDuration extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ return ((DurationValue)b).multiply(((NumericValue)a).getDoubleValue());
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return typeB;
+ }
+ }
+
+ /**
+ * Arithmetic: duration div number
+ */
+
+ private static class DurationDivNumeric extends Calculator {
+ public AtomicValue compute(AtomicValue a, AtomicValue b, XPathContext c) throws XPathException {
+ double d = 1.0 / ((NumericValue)b).getDoubleValue();
+ return ((DurationValue)a).multiply(d);
+ }
+ public AtomicType getResultType(AtomicType typeA, AtomicType typeB) {
+ return typeA;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/expr/Callable.java b/sf/saxon/expr/Callable.java
new file mode 100644
index 0000000..47b78cb
--- /dev/null
+++ b/sf/saxon/expr/Callable.java
@@ -0,0 +1,42 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A generic interface for calling expressions by supplying the values of their subexpressions
+ */
+
+public interface Callable {
+
+ /**
+ * Call the Callable.
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences.
+ * <p>Generally it is advisable, if calling iterate() to process a supplied sequence, to
+ * call it only once; if the value is required more than once, it should first be converted
+ * to a {@link net.sf.saxon.om.GroundedValue} by calling the utility methd
+ * SequenceTool.toGroundedValue().</p>
+ * <p>If the expected value is a single item, the item should be obtained by calling
+ * Sequence.head(): it cannot be assumed that the item will be passed as an instance of
+ * {@link net.sf.saxon.om.Item} or {@link net.sf.saxon.value.AtomicValue}.</p>
+ * <p>It is the caller's responsibility to perform any type conversions required
+ * to convert arguments to the type expected by the callee. An exception is where
+ * this Callable is explicitly an argument-converting wrapper around the original
+ * Callable.</p>
+ * @return the result of the evaluation, in the form of a Sequence. It is the responsibility
+ * of the callee to ensure that the type of result conforms to the expected result type.
+ * @throws XPathException if a dynamic error occurs during the evaluation of the expression
+ */
+
+ /*@Nullable*/
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException;
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/CardinalityChecker.java b/sf/saxon/expr/CardinalityChecker.java
new file mode 100644
index 0000000..8dbf897
--- /dev/null
+++ b/sf/saxon/expr/CardinalityChecker.java
@@ -0,0 +1,474 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.CardinalityCheckerCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.CardinalityCheckerAdjunct;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.event.TypeCheckingFilter;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.DocumentNodeTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.List;
+
+
+/**
+* A CardinalityChecker implements the cardinality checking of "treat as": that is,
+* it returns the supplied sequence, checking that its cardinality is correct
+*/
+
+public final class CardinalityChecker extends UnaryExpression {
+
+ private int requiredCardinality = -1;
+ private RoleLocator role;
+
+ /**
+ * Private Constructor: use factory method
+ * @param sequence the base sequence whose cardinality is to be checked
+ * @param cardinality the required cardinality
+ * @param role information to be used in error reporting
+ */
+
+ private CardinalityChecker(Expression sequence, int cardinality, RoleLocator role) {
+ super(sequence);
+ requiredCardinality = cardinality;
+ this.role = role;
+ //computeStaticProperties();
+ adoptChildExpression(sequence);
+ }
+
+ /**
+ * Factory method to construct a CardinalityChecker. The method may create an expression that combines
+ * the cardinality checking with the functionality of the underlying expression class
+ * @param sequence the base sequence whose cardinality is to be checked
+ * @param cardinality the required cardinality
+ * @param role information to be used in error reporting
+ * @return a new Expression that does the CardinalityChecking (not necessarily a CardinalityChecker)
+ */
+
+ public static Expression makeCardinalityChecker(Expression sequence, int cardinality, RoleLocator role) {
+ Expression result;
+ if (sequence instanceof Atomizer && !Cardinality.allowsMany(cardinality)) {
+ Expression base = ((Atomizer)sequence).getBaseExpression();
+ result = new SingletonAtomizer(base, role, Cardinality.allowsZero(cardinality));
+ } else {
+ result = new CardinalityChecker(sequence, cardinality, role);
+ }
+ ExpressionTool.copyLocationInfo(sequence, result);
+ return result;
+ }
+
+ /**
+ * Get the required cardinality
+ * @return the cardinality required by this checker
+ */
+
+ public int getRequiredCardinality() {
+ return requiredCardinality;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ if (requiredCardinality == StaticProperty.ALLOWS_ZERO_OR_MORE ||
+ Cardinality.subsumes(requiredCardinality, operand.getCardinality())) {
+ return operand;
+ }
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.optimize(operand, contextItemType);
+ if (requiredCardinality == StaticProperty.ALLOWS_ZERO_OR_MORE ||
+ Cardinality.subsumes(requiredCardinality, operand.getCardinality())) {
+ return operand;
+ }
+ // do cardinality checking before item checking (may avoid the need for a mapping iterator)
+ if (operand instanceof ItemChecker) {
+ ItemChecker checker = (ItemChecker)operand;
+ Expression base = checker.getBaseExpression();
+ operand = base;
+ checker.replaceSubExpression(base, this);
+ checker.resetLocalStaticProperties();
+ this.resetLocalStaticProperties();
+ return checker;
+ }
+ if(operand instanceof Literal) {
+ return new Literal(SequenceExtent.makeSequenceExtent(iterate(visitor.makeDynamicContext())));
+ }
+ return this;
+ }
+
+
+ /**
+ * Set the error code to be returned (this is used when evaluating the functions such
+ * as exactly-one() which have their own error codes)
+ * @param code the error code to be used
+ */
+
+ public void setErrorCode(String code) {
+ role.setErrorCode(code);
+ }
+
+ /**
+ * Get the RoleLocator, which contains diagnostic information for use if the cardinality check fails
+ * @return the diagnostic information
+ */
+
+ public RoleLocator getRoleLocator() {
+ return role;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided. This implementation provides both iterate() and
+ * process() methods natively.
+ */
+
+ public int getImplementationMethod() {
+ int m = ITERATE_METHOD | PROCESS_METHOD | ITEM_FEED_METHOD;
+ if (!Cardinality.allowsMany(requiredCardinality)) {
+ m |= EVALUATE_METHOD;
+ }
+ return m;
+ }
+
+
+//#ifdefined STREAM
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(syntacticContext, allowExtensions, reasons);
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public CardinalityCheckerAdjunct getStreamingAdjunct() {
+ return new CardinalityCheckerAdjunct();
+ }
+
+//#endif
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return operand.getIntegerBounds();
+ }
+
+ /**
+ * Iterate over the sequence of values
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ SequenceIterator base = operand.iterate(context);
+
+ // If the base iterator knows how many items there are, then check it now rather than wasting time
+
+ if ((base.getProperties() & SequenceIterator.LAST_POSITION_FINDER) != 0) {
+ int count = ((LastPositionFinder)base).getLength();
+ if (count == 0 && !Cardinality.allowsZero(requiredCardinality)) {
+ typeError("An empty sequence is not allowed as the " +
+ role.getMessage(), role.getErrorCode(), context);
+ } else if (count == 1 && requiredCardinality == StaticProperty.EMPTY) {
+ typeError("The only value allowed for the " +
+ role.getMessage() + " is an empty sequence", role.getErrorCode(), context);
+ } else if (count > 1 && !Cardinality.allowsMany(requiredCardinality)) {
+ typeError("A sequence of more than one item is not allowed as the " +
+ role.getMessage() + depictSequenceStart(base.getAnother(), 2),
+ role.getErrorCode(), context);
+ }
+ return base;
+ }
+
+ // Otherwise return an iterator that does the checking on the fly
+
+ return new CardinalityCheckingIterator(base, requiredCardinality, role, this);
+
+ }
+
+ /**
+ * Show the first couple of items in a sequence in an error message
+ * @param seq iterator over the sequence
+ * @param max maximum number of items to be shown
+ * @return a message display of the contents of the sequence
+ */
+
+ public static String depictSequenceStart(SequenceIterator seq, int max) {
+ try {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ int count = 0;
+ sb.append(" (");
+ while (true) {
+ Item next = seq.next();
+ if (next == null) {
+ sb.append(") ");
+ return sb.toString();
+ }
+ if (count++ > 0) {
+ sb.append(", ");
+ }
+ if (count > max) {
+ sb.append("...) ");
+ return sb.toString();
+ }
+
+ sb.append(Err.depict(next));
+ }
+ } catch (XPathException e) {
+ return "";
+ }
+ }
+
+ /**
+ * Evaluate as an Item. For this class, this implies checking that the underlying
+ * expression delivers a singleton.
+ */
+
+ /*@Nullable*/ public Item evaluateItem(XPathContext context) throws XPathException {
+ SequenceIterator iter = operand.iterate(context);
+ Item first = iter.next();
+ if (first == null) {
+ if (!Cardinality.allowsZero(requiredCardinality)) {
+ typeError("An empty sequence is not allowed as the " +
+ role.getMessage(), role.getErrorCode(), context);
+ return null;
+ }
+ } else {
+ if (requiredCardinality == StaticProperty.EMPTY) {
+ typeError("An empty sequence is required as the " +
+ role.getMessage(), role.getErrorCode(), context);
+ return null;
+ }
+ }
+ Item second = iter.next();
+ if (second != null) {
+ typeError("A sequence of more than one item is not allowed as the " +
+ role.getMessage() + depictSequenceStart(iter.getAnother(), 2), role.getErrorCode(), context);
+ return null;
+ }
+ return first;
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ Expression next = operand;
+ ItemType type = Type.ITEM_TYPE;
+ if (next instanceof ItemChecker) {
+ type = ((ItemChecker)next).getRequiredType();
+ next = ((ItemChecker)next).getBaseExpression();
+ }
+ if ((next.getImplementationMethod() & PROCESS_METHOD) != 0 && !(type instanceof DocumentNodeTest)) {
+ SequenceReceiver out = context.getReceiver();
+ TypeCheckingFilter filter = new TypeCheckingFilter(out);
+ filter.setRequiredType(type, requiredCardinality, role, this);
+ context.setReceiver(filter);
+ next.process(context);
+ try {
+ filter.close();
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ throw e;
+ }
+ context.setReceiver(out);
+ } else {
+ super.process(context);
+ }
+ }
+
+ /**
+ * Determine the data type of the items returned by the expression, if possible
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
+ * or Type.ITEM (meaning not known in advance)
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return operand.getItemType(th);
+ }
+
+ /**
+ * Determine the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ return requiredCardinality;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ return operand.getSpecialProperties();
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new CardinalityChecker(getBaseExpression().copy(), requiredCardinality, role);
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return super.equals(other) &&
+ requiredCardinality == ((CardinalityChecker)other).requiredCardinality;
+ }
+
+ /**
+ * get HashCode for comparing two expressions. Note that this hashcode gives the same
+ * result for (A op B) and for (B op A), whether or not the operator is commutative.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ requiredCardinality;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CardinalityCheck expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CardinalityCheckerCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("checkCardinality");
+ out.emitAttribute("occurs", Cardinality.toString(requiredCardinality));
+ operand.explain(out);
+ out.endElement();
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form. For subclasses of Expression that represent XPath expressions, the result should always
+ * be a string that parses as an XPath 3.0 expression.
+ */
+ @Override
+ public String toString() {
+ switch (requiredCardinality) {
+ case StaticProperty.ALLOWS_ONE:
+ return "exactly-one(" + operand.toString() + ")";
+ case StaticProperty.ALLOWS_ZERO_OR_ONE:
+ return "zero-or-one(" + operand.toString() + ")";
+ case StaticProperty.ALLOWS_ONE_OR_MORE:
+ return "one-or-more(" + operand.toString() + ")";
+ case StaticProperty.EMPTY:
+ return "must-be-empty(" + operand.toString() + ")";
+ default:
+ return "check(" + operand.toString() + ")";
+ }
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "CheckCardinality";
+ }
+}
+
diff --git a/sf/saxon/expr/CardinalityCheckingIterator.java b/sf/saxon/expr/CardinalityCheckingIterator.java
new file mode 100644
index 0000000..27f03dd
--- /dev/null
+++ b/sf/saxon/expr/CardinalityCheckingIterator.java
@@ -0,0 +1,138 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Cardinality;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * CardinalityCheckingIterator returns the items in an underlying sequence
+ * unchanged, but checks that the number of items conforms to the required
+ * cardinality. Because cardinality checks are required to take place even
+ * if the consumer of the sequence does not require all the items, we read
+ * the first two items at initialization time. This is sufficient to perform
+ * the checks; after that we can simply return the items from the base sequence.
+ */
+
+public final class CardinalityCheckingIterator implements SequenceIterator {
+
+ private SequenceIterator base;
+ private int requiredCardinality;
+ private RoleLocator role;
+ private SourceLocator locator;
+ /*@Nullable*/ private Item first = null;
+ private Item second = null;
+ private Item current = null;
+ private int position = 0;
+
+ /**
+ * Construct an CardinalityCheckingIterator that will return the same items as the base sequence,
+ * checking how many there are
+ *
+ * @param base the base iterator
+ * @param requiredCardinality the required Cardinality
+ */
+
+ public CardinalityCheckingIterator(SequenceIterator base, int requiredCardinality,
+ RoleLocator role, SourceLocator locator)
+ throws XPathException {
+ this.base = base;
+ this.requiredCardinality = requiredCardinality;
+ this.role = role;
+ this.locator = locator;
+ first = base.next();
+ if (first==null) {
+ if (!Cardinality.allowsZero(requiredCardinality)) {
+ typeError("An empty sequence is not allowed as the " +
+ role.getMessage(), role.getErrorCode());
+ }
+ } else {
+ if (requiredCardinality == StaticProperty.EMPTY) {
+ typeError("The only value allowed for the " +
+ role.getMessage() + " is an empty sequence", role.getErrorCode());
+ }
+ second = base.next();
+ if (second!=null && !Cardinality.allowsMany(requiredCardinality)) {
+ typeError( "A sequence of more than one item is not allowed as the " +
+ role.getMessage() + CardinalityChecker.depictSequenceStart(base.getAnother(), 2),
+ role.getErrorCode());
+ }
+ }
+ }
+
+ public Item next() throws XPathException {
+ if (position < 2) {
+ if (position == 0) {
+ current = first;
+ position = (first==null ? -1 : 1);
+ return current;
+ } else if (position == 1) {
+ current = second;
+ position = (second==null ? -1 : 2);
+ return current;
+ } else {
+ // position == -1
+ return null;
+ }
+ }
+ current = base.next();
+ if (current == null) {
+ position = -1;
+ } else {
+ position++;
+ }
+ return current;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new CardinalityCheckingIterator(base.getAnother(), requiredCardinality, role, locator);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED},
+ * {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+ private void typeError(String message, String errorCode) throws XPathException {
+ XPathException e = new XPathException(message, locator);
+ e.setIsTypeError(true);
+ e.setErrorCode(errorCode);
+ throw e;
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/CastExpression.java b/sf/saxon/expr/CastExpression.java
new file mode 100644
index 0000000..ba679cc
--- /dev/null
+++ b/sf/saxon/expr/CastExpression.java
@@ -0,0 +1,444 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+
+import com.saxonica.bytecode.CastExpressionCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.functions.StringFn;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+
+
+/**
+ * Cast Expression: implements "cast as data-type ( expression )". It also allows an internal
+ * cast, which has the same semantics as a user-requested cast, but maps an empty sequence to
+ * an empty sequence.
+ *
+ * This expression class does not handle casting to a union type.
+*/
+
+public class CastExpression extends CastingExpression {
+
+ /**
+ * Create a cast expression
+ * @param source expression giving the value to be converted
+ * @param target the type to which the value is to be converted
+ * @param allowEmpty true if the expression allows an empty sequence as input, producing
+ * an empty sequence as output. If false, an empty sequence is a type error.
+ */
+
+ public CastExpression(Expression source, AtomicType target, boolean allowEmpty) {
+ super(source, target, allowEmpty);
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ SequenceType atomicType = SequenceType.makeSequenceType(BuiltInAtomicType.ANY_ATOMIC, getCardinality());
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP, "cast as", 0);
+ ItemType sourceItemType = null;
+
+// if (getTargetType().isNamespaceSensitive()) {
+// int rel = th.relationship(operand.getItemType(th), AnyNodeTest.getInstance());
+// if (rel == TypeHierarchy.SAME_TYPE || rel == TypeHierarchy.SUBSUMED_BY) {
+// // See spec bug 11964
+// XPathException err = new XPathException("Atomization is not allowed when casting to QName or NOTATION");
+// err.setErrorCode("XPTY0004");
+// err.setLocator(this);
+// err.setIsTypeError(true);
+// throw err;
+// } else if (rel != TypeHierarchy.DISJOINT) {
+// operand = new ItemChecker(operand, BuiltInAtomicType.ANY_ATOMIC, role);
+// }
+// sourceItemType = BuiltInAtomicType.ANY_ATOMIC;
+// } else {
+ operand = TypeChecker.staticTypeCheck(operand, atomicType, false, role, visitor);
+ sourceItemType = operand.getItemType(th);
+// }
+
+ if (sourceItemType instanceof ErrorType) {
+ if (allowsEmpty()) {
+ return Literal.makeEmptySequence();
+ } else {
+ XPathException err = new XPathException("Cast does not allow an empty sequence as input");
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+ AtomicType sourceType = (AtomicType) sourceItemType;
+ int r = th.relationship(sourceType, getTargetType());
+ if (r == TypeHierarchy.SAME_TYPE) {
+ return operand;
+ } else if (r == TypeHierarchy.SUBSUMED_BY) {
+ // It's generally true that any expression defined to return an X is allowed to return a subtype of X.
+ // However, people seem to get upset if we treat the cast as a no-op.
+ converter = new Converter.UpCastingConverter(getTargetType());
+ } else {
+
+ ConversionRules rules = visitor.getConfiguration().getConversionRules();
+
+ if (!(sourceType == BuiltInAtomicType.ANY_ATOMIC || sourceType.getFingerprint() == Type.EMPTY)) {
+ converter = rules.getConverter(sourceType, getTargetType());
+ if (converter == null) {
+ XPathException err = new XPathException("Casting from " + sourceType + " to " + getTargetType() +
+ " can never succeed");
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ } else {
+ if (getTargetType().isNamespaceSensitive()) {
+ converter.setNamespaceResolver(nsResolver);
+ }
+ }
+ if (converter.isXPath30Conversion() && !visitor.getStaticContext().getXPathLanguageLevel().equals(DecimalValue.THREE)
+ && !(operand instanceof Literal && getTargetType().getPrimitiveType() == StandardNames.XS_QNAME)) {
+ XPathException err = new XPathException("Casting from " + sourceType + " to " + getTargetType() +
+ " requires XPath 3.0 functionality to be enabled");
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+ }
+
+ if (operand instanceof Literal) {
+ return preEvaluate();
+ }
+
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Expression e2 = super.optimize(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ // Eliminate pointless casting between untypedAtomic and string
+ if (getTargetType() == BuiltInAtomicType.UNTYPED_ATOMIC) {
+ if (operand instanceof StringFn) {
+ Expression e = ((StringFn)operand).getArguments()[0];
+ if (e.getItemType(th) instanceof AtomicType && e.getCardinality() == StaticProperty.EXACTLY_ONE) {
+ operand = e;
+ }
+ } else if (operand instanceof CastExpression) {
+ if (((CastExpression)operand).getTargetType() == BuiltInAtomicType.UNTYPED_ATOMIC) {
+ return operand;
+ } else if (((CastExpression)operand).getTargetType() == BuiltInAtomicType.STRING) {
+ ((CastExpression)operand).setTargetType(BuiltInAtomicType.UNTYPED_ATOMIC);
+ return operand;
+ }
+ } else if (operand instanceof AtomicSequenceConverter) {
+ if (operand.getItemType(th) == BuiltInAtomicType.UNTYPED_ATOMIC) {
+ return operand;
+ } else if (operand.getItemType(th) == BuiltInAtomicType.STRING) {
+ AtomicSequenceConverter old = (AtomicSequenceConverter)operand;
+ AtomicSequenceConverter asc = new AtomicSequenceConverter(
+ old.getBaseExpression(),
+ BuiltInAtomicType.UNTYPED_ATOMIC
+ );
+ return asc.typeCheck(visitor, contextItemType)
+ .optimize(visitor, contextItemType);
+ }
+ }
+ }
+ // avoid converting anything to a string and then back again
+ if (operand instanceof StringFn) {
+ Expression e = ((StringFn)operand).getArguments()[0];
+ ItemType et = e.getItemType(th);
+ if (et instanceof AtomicType &&
+ e.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ th.isSubType(et, getTargetType())) {
+ return e;
+ }
+ }
+ // avoid converting anything to untypedAtomic and then back again
+ if (operand instanceof CastExpression) {
+ ItemType it = ((CastExpression)operand).getTargetType();
+ if (th.isSubType(it, BuiltInAtomicType.STRING) || th.isSubType(it, BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ Expression e = ((CastExpression)operand).getBaseExpression();
+ ItemType et = e.getItemType(th);
+ if (et instanceof AtomicType &&
+ e.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ th.isSubType(et, getTargetType())) {
+ return e;
+ }
+ }
+ }
+ if (operand instanceof AtomicSequenceConverter) {
+ ItemType it = operand.getItemType(th);
+ if (th.isSubType(it, BuiltInAtomicType.STRING) || th.isSubType(it, BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ Expression e = ((AtomicSequenceConverter)operand).getBaseExpression();
+ ItemType et = e.getItemType(th);
+ if (et instanceof AtomicType &&
+ e.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ th.isSubType(et, getTargetType())) {
+ return e;
+ }
+ }
+ }
+ // if the operand can't be empty, then set allowEmpty to false to provide more information for analysis
+ if (!Cardinality.allowsZero(operand.getCardinality())) {
+ setAllowEmpty(false);
+ resetLocalStaticProperties();
+ }
+
+ if (operand instanceof Literal) {
+ return preEvaluate();
+ }
+ return this;
+ }
+
+ /**
+ * Perform early (compile-time) evaluation
+ */
+
+ protected Expression preEvaluate() throws XPathException {
+ GroundedValue literalOperand = ((Literal)operand).getValue();
+ if (literalOperand instanceof AtomicValue && converter != null) {
+ if (converter.isXPath30Conversion() && !getExecutable().isAllowXPath30() &&
+ !(converter instanceof StringConverter.StringToQName && isOperandIsStringLiteral())) {
+ XPathException e = new XPathException("Casting from " + ((AtomicValue)literalOperand).getPrimitiveType() + " to " + getTargetType() + " requires XPath 3.0 to be enabled");
+ e.setLocator(this);
+ e.setErrorCode("XPTY0004");
+ throw e;
+ }
+ ConversionResult result = converter.convert((AtomicValue)literalOperand);
+ if (result instanceof ValidationFailure) {
+ ValidationFailure err = (ValidationFailure)result;
+ String code = err.getErrorCode();
+ if (code == null) {
+ code = "FORG0001";
+ }
+ XPathException xpe = new XPathException(err.getMessage(), this);
+ xpe.setErrorCode(code);
+ throw xpe;
+ } else {
+ return Literal.makeLiteral((AtomicValue)result);
+ }
+ }
+ if (literalOperand instanceof EmptySequence) {
+ if (allowsEmpty()) {
+ return operand;
+ } else {
+ XPathException err = new XPathException("Cast can never succeed: the operand must not be an empty sequence");
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Get the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ return (allowsEmpty() && Cardinality.allowsZero(operand.getCardinality())
+ ? StaticProperty.ALLOWS_ZERO_OR_ONE : StaticProperty.EXACTLY_ONE);
+ }
+
+ /**
+ * Get the static type of the expression
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return getTargetType();
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ /*@Nullable*/@Override
+ public IntegerValue[] getIntegerBounds() {
+ if (converter == Converter.BOOLEAN_TO_INTEGER) {
+ return new IntegerValue[]{Int64Value.ZERO, Int64Value.PLUS_ONE};
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ CastExpression c2 = new CastExpression(getBaseExpression().copy(), getTargetType(), allowsEmpty());
+ c2.converter = converter;
+ c2.nsResolver = nsResolver;
+ c2.setOperandIsStringLiteral(isOperandIsStringLiteral());
+ return c2;
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ /*@Nullable*/ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+ AtomicValue value = (AtomicValue)operand.evaluateItem(context);
+ if (value==null) {
+ if (allowsEmpty()) {
+ return null;
+ } else {
+ XPathException e = new XPathException("Cast does not allow an empty sequence");
+ e.setXPathContext(context);
+ e.setLocator(this);
+ e.setErrorCode("XPTY0004");
+ throw e;
+ }
+ }
+
+ Converter converter = this.converter;
+ if (converter == null) {
+ ConversionRules rules = context.getConfiguration().getConversionRules();
+ converter = rules.getConverter(value.getPrimitiveType(), getTargetType());
+ if (converter == null) {
+ XPathException e = new XPathException("Casting from " + value.getPrimitiveType() + " to " + getTargetType() + " is not permitted");
+ e.setXPathContext(context);
+ e.setLocator(this);
+ e.setErrorCode("XPTY0004");
+ throw e;
+ }
+ if (converter.isXPath30Conversion() && !getExecutable().isAllowXPath30() &&
+ !(converter instanceof StringConverter.StringToQName && isOperandIsStringLiteral())) {
+ XPathException e = new XPathException("Casting from " + value.getPrimitiveType() + " to " + getTargetType() + " requires XPath 3.0 to be enabled");
+ e.setXPathContext(context);
+ e.setLocator(this);
+ e.setErrorCode("XPTY0004");
+ throw e;
+ }
+ if (nsResolver != null) {
+ converter.setNamespaceResolver(nsResolver);
+ }
+ }
+ ConversionResult result = converter.convert(value);
+ if (result instanceof ValidationFailure) {
+ ValidationFailure err = (ValidationFailure)result;
+ String code = err.getErrorCode();
+ if (code == null) {
+ code = "FORG0001";
+ }
+ dynamicError(err.getMessage(), code, context);
+ return null;
+ }
+ return (AtomicValue)result;
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof CastExpression &&
+ operand.equals(((CastExpression)other).operand) &&
+ getTargetType() == ((CastExpression)other).getTargetType() &&
+ getTargetType() == ((CastExpression)other).getTargetType();
+ }
+
+ /**
+ * get HashCode for comparing two expressions. Note that this hashcode gives the same
+ * result for (A op B) and for (B op A), whether or not the operator is commutative.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ getTargetType().hashCode();
+ }
+
+ /**
+ * Represent the expression as a string. The resulting string will be a valid XPath 3.0 expression
+ * with no dependencies on namespace bindings.
+ * @return the expression as a string in XPath 3.0 syntax
+ */
+
+ public String toString() {
+ return getTargetType().getEQName() + "(" + operand.toString() + ")";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("cast");
+ out.emitAttribute("as", getTargetType().getEQName());
+ operand.explain(out);
+ out.endElement();
+ }
+
+
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Cast expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CastExpressionCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/CastToList.java b/sf/saxon/expr/CastToList.java
new file mode 100644
index 0000000..ad37461
--- /dev/null
+++ b/sf/saxon/expr/CastToList.java
@@ -0,0 +1,298 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.CastToListCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+/**
+ * Expression class for a cast to a List type
+ */
+public class CastToList extends UnaryExpression {
+
+ private ListType targetType;
+ private boolean allowEmpty;
+ /*@Nullable*/
+ private NamespaceResolver nsResolver = null;
+
+ public CastToList(Expression source, ListType targetType, boolean allowEmpty) {
+ super(source);
+ this.targetType = targetType;
+ this.allowEmpty = allowEmpty;
+ }
+
+ public boolean isAllowEmpty(){
+ return allowEmpty;
+ }
+
+ public ListType getTargetType(){
+ return targetType;
+ }
+
+ public NamespaceResolver getNamespaceResolver(){
+ return nsResolver;
+ }
+
+ /**
+ * Simplify the expression
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ if (targetType.isNamespaceSensitive()) {
+ StaticContext env = visitor.getStaticContext();
+ nsResolver = env.getNamespaceResolver();
+ }
+ operand = visitor.simplify(operand);
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ SequenceType atomicType = SequenceType.makeSequenceType(BuiltInAtomicType.STRING, (allowEmpty ? StaticProperty.ALLOWS_ZERO_OR_ONE : StaticProperty.EXACTLY_ONE));
+
+ RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP, "cast as", 0);
+ //role.setSourceLocator(this);
+ operand = TypeChecker.staticTypeCheck(operand, atomicType, false, role, visitor);
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ boolean maybeString = th.relationship(operand.getItemType(th), BuiltInAtomicType.STRING) != TypeHierarchy.DISJOINT;
+ boolean maybeUntyped = th.relationship(operand.getItemType(th), BuiltInAtomicType.UNTYPED_ATOMIC) != TypeHierarchy.DISJOINT;
+
+ if(!maybeString && !maybeUntyped){
+ XPathException err = new XPathException("Casting to list requires an xs:string or xs:untypedAtomic operand");
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+
+ if (operand instanceof Literal) {
+ GroundedValue literalOperand = ((Literal) operand).getValue();
+ if (literalOperand instanceof AtomicValue) {
+ try {
+ SequenceIterator seq = iterate(visitor.getStaticContext().makeEarlyEvaluationContext());
+ return Literal.makeLiteral(SequenceTool.toGroundedValue(seq));
+ } catch (XPathException err) {
+ err.maybeSetErrorCode("FORG0001");
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+ if (literalOperand instanceof EmptySequence) {
+ if (allowEmpty) {
+ return operand;
+ } else {
+ XPathException err = new XPathException("Cast can never succeed: the operand must not be an empty sequence");
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Expression e2 = super.optimize(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ // if the operand can't be empty, then set allowEmpty to false to provide more information for analysis
+ if (!Cardinality.allowsZero(operand.getCardinality())) {
+ allowEmpty = false;
+ resetLocalStaticProperties();
+ }
+ return this;
+ }
+
+ /**
+ * Get the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Get the static type of the expression
+ *
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (targetType.getItemType() instanceof ItemType) {
+ return (ItemType) targetType.getItemType();
+ }
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+
+ /**
+ * Determine the special properties of this expression
+ *
+ * @return {@link net.sf.saxon.expr.StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ CastToList c = new CastToList(getBaseExpression().copy(), targetType, allowEmpty);
+ c.nsResolver = nsResolver;
+ return c;
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ AtomicValue value = (AtomicValue) operand.evaluateItem(context);
+ if (value == null) {
+ if (allowEmpty) {
+ return EmptyIterator.emptyIterator();
+ } else {
+ XPathException e = new XPathException("Cast does not allow an empty sequence");
+ e.setXPathContext(context);
+ e.setLocator(this);
+ e.setErrorCode("XPTY0004");
+ throw e;
+ }
+ }
+ return cast(value.getStringValueCS(), targetType, nsResolver, context.getConfiguration().getConversionRules()).iterate();
+ }
+
+ /**
+ * Cast a string value to a list type
+ * @param value the input string value
+ * @param targetType the target list type
+ * @param nsResolver the namespace context, needed if the type is namespace-sensitive
+ * @param rules the conversion rules
+ * @return the sequence of atomic values that results from the conversion
+ * @throws XPathException if the conversion fails
+ */
+
+ public static AtomicSequence cast(CharSequence value, ListType targetType, final NamespaceResolver nsResolver, final ConversionRules rules)
+ throws XPathException {
+ ValidationFailure failure = targetType.validateContent(value, nsResolver, rules);
+ if(failure != null) {
+ throw failure.makeException();
+ }
+ return targetType.getTypedValue(value, nsResolver, rules);
+ }
+
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return super.equals(other) &&
+ targetType == ((CastToList) other).targetType &&
+ allowEmpty == ((CastToList) other).allowEmpty &&
+ nsResolver == ((CastToList) other).nsResolver;
+ }
+
+ /**
+ * get HashCode for comparing two expressions.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ targetType.hashCode();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CastToList expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CastToListCompiler();
+ }
+//#endif
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return targetType.getEQName() + "(" + operand.toString() + ")";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("castToList");
+ out.emitAttribute("as", targetType.toString());
+ operand.explain(out);
+ out.endElement();
+ }
+
+}
+
+// Copyright (c) 2011-2012 Saxonica Limited. All rights reserved.
+
+
diff --git a/sf/saxon/expr/CastToUnion.java b/sf/saxon/expr/CastToUnion.java
new file mode 100644
index 0000000..941890c
--- /dev/null
+++ b/sf/saxon/expr/CastToUnion.java
@@ -0,0 +1,353 @@
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.CastToUnionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.util.Set;
+
+/**
+ * Expression class for a cast to a union type
+ */
+public class CastToUnion extends UnaryExpression {
+
+ private UnionType targetType;
+ private boolean allowEmpty;
+ /*@Nullable*/
+ private NamespaceResolver nsResolver = null;
+
+ public CastToUnion(Expression source, UnionType targetType, boolean allowEmpty) {
+ super(source);
+ this.targetType = targetType;
+ this.allowEmpty = allowEmpty;
+ }
+
+ public boolean isAllowEmpty() {
+ return allowEmpty;
+ }
+
+ public UnionType getTargetType() {
+ return targetType;
+ }
+
+ public NamespaceResolver getNamespaceResolver() {
+ return nsResolver;
+ }
+
+ /**
+ * Simplify the expression
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ if (targetType.isNamespaceSensitive()) {
+ StaticContext env = visitor.getStaticContext();
+ nsResolver = env.getNamespaceResolver();
+ }
+ operand = visitor.simplify(operand);
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ SequenceType atomicType = SequenceType.makeSequenceType(BuiltInAtomicType.ANY_ATOMIC, getCardinality());
+
+ RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP, "cast as", 0);
+ //role.setSourceLocator(this);
+ operand = TypeChecker.staticTypeCheck(operand, atomicType, false, role, visitor);
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+
+ if (operand instanceof Literal) {
+ GroundedValue literalOperand = ((Literal) operand).getValue();
+ if (literalOperand instanceof AtomicValue) {
+ GroundedValue av = SequenceTool.toGroundedValue(iterate(visitor.getStaticContext().makeEarlyEvaluationContext()));
+ return Literal.makeLiteral(av);
+ }
+ if (literalOperand instanceof EmptySequence) {
+ if (allowEmpty) {
+ return operand;
+ } else {
+ XPathException err = new XPathException("Cast can never succeed: the operand must not be an empty sequence");
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Expression e2 = super.optimize(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ // if the operand can't be empty, then set allowEmpty to false to provide more information for analysis
+ if (!Cardinality.allowsZero(operand.getCardinality())) {
+ allowEmpty = false;
+ resetLocalStaticProperties();
+ }
+ return this;
+ }
+
+ /**
+ * Get the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ int c = StaticProperty.ALLOWS_ONE;
+ if (allowEmpty && Cardinality.allowsZero(operand.getCardinality())) {
+ c |= StaticProperty.ALLOWS_ZERO;
+ }
+ if (targetType.containsListType()) {
+ c |= StaticProperty.ALLOWS_ZERO;
+ c |= StaticProperty.ALLOWS_MANY;
+ }
+ return c;
+ }
+
+ /**
+ * Get the static type of the expression
+ *
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (targetType instanceof PlainType) {
+ return (PlainType)targetType;
+ }
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+
+ /**
+ * Determine the special properties of this expression
+ *
+ * @return {@link StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ CastToUnion c = new CastToUnion(getBaseExpression().copy(), targetType, allowEmpty);
+ c.nsResolver = nsResolver;
+ return c;
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ ConversionRules rules = context.getConfiguration().getConversionRules();
+ AtomicValue value = (AtomicValue) operand.evaluateItem(context);
+ if (value == null) {
+ if (allowEmpty) {
+ return null;
+ } else {
+ XPathException e = new XPathException("Cast does not allow an empty sequence");
+ e.setXPathContext(context);
+ e.setLocator(this);
+ e.setErrorCode("XPTY0004");
+ throw e;
+ }
+ }
+
+ try {
+ AtomicSequence result = cast(value, targetType, nsResolver, rules);
+ return result.iterate();
+ } catch (XPathException err) {
+ err.maybeSetContext(context);
+ err.maybeSetLocation(this);
+ err.setErrorCode("FORG0001");
+ throw err;
+ }
+ }
+
+ /**
+ * Static method to perform the castable check of an atomic value to a union type
+ *
+ * @param value the input value to be converted. Must not be null.
+ * @param targetType the union type to which the value is to be converted
+ * @param nsResolver the namespace context, required if the type is namespace-sensitive
+ * @param context the XPath dynamic evaluation context
+ * @return the result of the conversion (may be a sequence if the union includes list types in its membership)
+ * @throws XPathException if the conversion fails
+ */
+ public static boolean castable(AtomicValue value, UnionType targetType,
+ NamespaceResolver nsResolver, XPathContext context) {
+
+ try {
+ CastToUnion.cast(value, targetType, nsResolver, context.getConfiguration().getConversionRules());
+ return true;
+ } catch (XPathException err) {
+ return false;
+ }
+ }
+
+ /**
+ * Static method to perform the cast of an atomic value to a union type
+ *
+ * @param value the input value to be converted. Must not be null.
+ * @param targetType the union type to which the value is to be converted
+ * @param nsResolver the namespace context, required if the type is namespace-sensitive
+ * @param rules the conversion rules
+ * @return the result of the conversion (may be a sequence if the union includes list types in its membership)
+ * @throws XPathException if the conversion fails
+ */
+
+ public static AtomicSequence cast(AtomicValue value, UnionType targetType,
+ NamespaceResolver nsResolver, ConversionRules rules)
+ throws XPathException {
+ //ConversionRules rules = context.getConfiguration().getConversionRules();
+ if (value == null) {
+ throw new NullPointerException();
+ }
+
+ // 1. If the value is a string or untypedAtomic, try casting to each of the member types
+
+ if (value instanceof StringValue && !(value instanceof AnyURIValue)) {
+ try {
+ return targetType.getTypedValue(value.getStringValueCS(), nsResolver, rules);
+ } catch (ValidationException e) {
+ e.setErrorCode("FORG0001");
+ throw e;
+ }
+ }
+
+ // 2. If the value is an instance of a type in the transitive membership of the union, return it unchanged
+
+ AtomicType label = value.getItemType();
+ Set<PlainType> memberTypes = targetType.getPlainMemberTypes();
+ while (label != null) {
+ if (memberTypes.contains(label)) {
+ return value;
+ } else {
+ label = (label.getBaseType() instanceof AtomicType ? (AtomicType) label.getBaseType() : null);
+ }
+ }
+
+ // 3. if the value can be cast to any of the member types, return the result of that cast
+
+ for (PlainType type : memberTypes) {
+ if (type instanceof AtomicType) {
+ Converter c = rules.getConverter(value.getItemType(), (AtomicType) type);
+ if (c != null) {
+ ConversionResult result = c.convert(value);
+ if (result instanceof AtomicValue) {
+ return (AtomicValue) result;
+ }
+ }
+ }
+ }
+
+ throw new XPathException("Cannot convert the supplied value to the required union type", "FORG0001");
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CastToUnion expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public CastToUnionCompiler getExpressionCompiler() {
+ return new CastToUnionCompiler();
+ }
+//#endif
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return super.equals(other) &&
+ targetType == ((CastToUnion) other).targetType &&
+ allowEmpty == ((CastToUnion) other).allowEmpty &&
+ nsResolver == ((CastToUnion) other).nsResolver;
+ }
+
+ /**
+ * get HashCode for comparing two expressions.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ targetType.hashCode();
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return targetType.getEQName() + "(" + operand.toString() + ")";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("castToUnion");
+ out.emitAttribute("as", targetType.toString());
+ operand.explain(out);
+ out.endElement();
+ }
+
+}
+
+// Copyright (c) 2011 Saxonica Limited. All rights reserved.
+
+
diff --git a/sf/saxon/expr/CastableExpression.java b/sf/saxon/expr/CastableExpression.java
new file mode 100644
index 0000000..ba08038
--- /dev/null
+++ b/sf/saxon/expr/CastableExpression.java
@@ -0,0 +1,269 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.CastableExpressionCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+* Castable Expression: implements "Expr castable as atomic-type?".
+* The implementation simply wraps a cast expression with a try/catch.
+*/
+
+public final class CastableExpression extends CastingExpression {
+
+ /**
+ * Create a "castable" expression of the form "source castable as target"
+ * @param source The source expression
+ * @param target The type being tested against
+ * @param allowEmpty true if an empty sequence is acceptable, that is if the expression
+ * was written as "source castable as target?"
+ */
+
+ public CastableExpression(Expression source, AtomicType target, boolean allowEmpty) {
+ super(source, target, allowEmpty);
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+
+ // We need to take care here. The usual strategy of wrapping the operand in an expression that
+ // does type-checking doesn't work here, because an error in the type checking should be caught,
+ // while an error in evaluating the expression as written should not.
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType sourceItemType = operand.getItemType(th);
+
+ AtomicType atomizedType = (AtomicType)sourceItemType.getAtomizedItemType().getPrimitiveItemType();
+ if (!(atomizedType == BuiltInAtomicType.ANY_ATOMIC)) {
+ converter = visitor.getConfiguration().getConversionRules().getConverter(atomizedType, getTargetType());
+ if (converter == null) {
+ if (!allowsEmpty() || !Cardinality.allowsZero(operand.getCardinality())) {
+ // Conversion from source to target type will never succeed
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+ } else {
+ if (getTargetPrimitiveType().isNamespaceSensitive()) {
+ converter.setNamespaceResolver(nsResolver);
+ }
+ if (converter.isAlwaysSuccessful() && !allowsEmpty() && operand.getCardinality() == StaticProperty.ALLOWS_ONE) {
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ }
+ }
+ }
+
+ if (operand instanceof Literal) {
+ return preEvaluate();
+ }
+
+ return this;
+ }
+
+
+ protected Expression preEvaluate() throws XPathException {
+ GroundedValue literalOperand = ((Literal)operand).getValue();
+ if (literalOperand instanceof AtomicValue && converter != null) {
+ if (converter.isXPath30Conversion() && !getExecutable().isAllowXPath30() &&
+ !(converter instanceof StringConverter.StringToQName && isOperandIsStringLiteral())) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+ ConversionResult result = converter.convert((AtomicValue)literalOperand);
+ return Literal.makeLiteral(BooleanValue.get(!(result instanceof ValidationFailure)));
+ }
+ if (literalOperand instanceof EmptySequence) {
+ return Literal.makeLiteral(BooleanValue.get(allowsEmpty()));
+ }
+ if (literalOperand.getLength() > 1) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+ return this;
+ }
+
+ /**
+ * Optimize the expression
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.optimize(operand, contextItemType);
+ if (operand instanceof Literal) {
+ return preEvaluate();
+ }
+ return this;
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof CastableExpression &&
+ operand.equals(((CastableExpression)other).operand) &&
+ getTargetType() == ((CastableExpression)other).getTargetType() &&
+ allowsEmpty() == ((CastableExpression)other).allowsEmpty();
+ }
+
+ /**
+ * get HashCode for comparing two expressions. Note that this hashcode gives the same
+ * result for (A op B) and for (B op A), whether or not the operator is commutative.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ 0x5555;
+ }
+
+ /**
+ * Determine the data type of the result of the Castable expression
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /*@NotNull*/
+ public Expression copy() {
+ CastableExpression ce = new CastableExpression(getBaseExpression().copy(), getTargetType(), allowsEmpty());
+ ce.nsResolver = nsResolver;
+ ce.converter = converter;
+ return ce;
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ // This method does its own atomization so that it can distinguish between atomization
+ // failures and casting failures
+ int count = 0;
+ SequenceIterator iter = operand.iterate(context);
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (item instanceof NodeInfo) {
+ Sequence atomizedValue = ((NodeInfo)item).atomize();
+ int length = SequenceTool.getLength(atomizedValue);
+ count += length;
+ if (count > 1) {
+ return false;
+ }
+ if (length != 0) {
+ AtomicValue av = (AtomicValue)atomizedValue.head();
+ if (!isCastable(av, getTargetType(), context)) {
+ return false;
+ }
+ }
+ } else {
+ AtomicValue av = (AtomicValue)item;
+ count++;
+ if (count > 1) {
+ return false;
+ }
+ if (!isCastable(av, getTargetType(), context)) {
+ return false;
+ }
+ }
+ }
+ return count != 0 || allowsEmpty();
+ }
+
+ /**
+ * Determine whether a value is castable to a given type
+ * @param value the value to be tested
+ * @param targetType the type to be tested against
+ * @param context XPath dynamic context
+ * @return true if the value is castable to the required type
+ */
+
+ private boolean isCastable(AtomicValue value, AtomicType targetType, XPathContext context) {
+ Converter converter = this.converter;
+ if (converter == null) {
+ converter = context.getConfiguration().getConversionRules().getConverter(value.getPrimitiveType(), targetType);
+ if (converter == null) {
+ return false;
+ }
+ if (converter.isAlwaysSuccessful()) {
+ return true;
+ }
+ if (converter.isXPath30Conversion() && !getExecutable().isAllowXPath30() &&
+ !(converter instanceof StringConverter.StringToQName && isOperandIsStringLiteral())) {
+ return false;
+ }
+ if (nsResolver != null) {
+ converter.setNamespaceResolver(nsResolver);
+ }
+ }
+ return !(converter.convert(value) instanceof ValidationFailure);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Castable expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CastableExpressionCompiler();
+ }
+//#endif
+
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return operand.toString() + " castable as " + getTargetType().getEQName();
+ }
+
+
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("castable");
+ out.emitAttribute("as", getTargetType().getEQName());
+ operand.explain(out);
+ out.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/CastableToList.java b/sf/saxon/expr/CastableToList.java
new file mode 100644
index 0000000..5791cb4
--- /dev/null
+++ b/sf/saxon/expr/CastableToList.java
@@ -0,0 +1,262 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.CastableToListCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.ListType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.*;
+
+/**
+ * Expression class for a cast to a List type
+ */
+public class CastableToList extends UnaryExpression {
+
+ private ListType targetType;
+ private boolean allowEmpty;
+ /*@Nullable*/
+ private NamespaceResolver nsResolver = null;
+
+ public CastableToList(Expression source, ListType targetType, boolean allowEmpty) {
+ super(source);
+ this.targetType = targetType;
+ this.allowEmpty = allowEmpty;
+ }
+
+
+ public boolean isAllowEmpty(){
+ return allowEmpty;
+ }
+
+ public ListType getTargetType(){
+ return targetType;
+ }
+
+ public NamespaceResolver getNamespaceResolver(){
+ return nsResolver;
+ }
+
+ /**
+ * Simplify the expression
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ if (targetType.isNamespaceSensitive()) {
+ StaticContext env = visitor.getStaticContext();
+ nsResolver = env.getNamespaceResolver();
+ }
+ operand = visitor.simplify(operand);
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ SequenceType atomicType = SequenceType.makeSequenceType(BuiltInAtomicType.STRING, (allowEmpty ? StaticProperty.ALLOWS_ZERO_OR_ONE : StaticProperty.EXACTLY_ONE));
+
+ RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP, "castable as", 0);
+ //role.setSourceLocator(this);
+ operand = TypeChecker.staticTypeCheck(operand, atomicType, false, role, visitor);
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ boolean maybeString = th.relationship(operand.getItemType(th), BuiltInAtomicType.STRING) != TypeHierarchy.DISJOINT;
+ boolean maybeUntyped = th.relationship(operand.getItemType(th), BuiltInAtomicType.UNTYPED_ATOMIC) != TypeHierarchy.DISJOINT;
+
+ if (!maybeString && !maybeUntyped){
+ // Casting from other types can never succeed
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+
+ if (operand instanceof Literal) {
+ GroundedValue literalOperand = ((Literal) operand).getValue();
+ if (literalOperand instanceof AtomicValue) {
+ return Literal.makeLiteral(BooleanValue.get(effectiveBooleanValue(visitor.getStaticContext().makeEarlyEvaluationContext())));
+ }
+ if (literalOperand instanceof EmptySequence) {
+ return Literal.makeLiteral(BooleanValue.get((allowEmpty ? true : false)));
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Expression e2 = super.optimize(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ // if the operand can't be empty, then set allowEmpty to false to provide more information for analysis
+ if (!Cardinality.allowsZero(operand.getCardinality())) {
+ allowEmpty = false;
+ resetLocalStaticProperties();
+ }
+ return this;
+ }
+
+ /**
+ * Get the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Get the static type of the expression
+ *
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Determine the special properties of this expression
+ *
+ * @return {@link net.sf.saxon.expr.StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ CastableToList c = new CastableToList(getBaseExpression().copy(), targetType, allowEmpty);
+ c.nsResolver = nsResolver;
+ return c;
+ }
+
+
+ /**
+ * Evaluate the expression
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ AtomicValue value = (AtomicValue) operand.evaluateItem(context);
+ if (value == null) {
+ return allowEmpty;
+ }
+ try {
+ CastToList.cast(value.getStringValue(), targetType, nsResolver, context.getConfiguration().getConversionRules());
+ return true;
+ } catch (XPathException err) {
+ return false;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CastableList expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CastableToListCompiler();
+ }
+//#endif
+
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return super.equals(other) &&
+ targetType == ((CastableToList) other).targetType &&
+ allowEmpty == ((CastableToList) other).allowEmpty &&
+ nsResolver == ((CastableToList) other).nsResolver;
+ }
+
+ /**
+ * get HashCode for comparing two expressions.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ targetType.hashCode();
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return operand.toString() + " castable as " + targetType.getEQName();
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("castableToList");
+ out.emitAttribute("as", targetType.toString());
+ operand.explain(out);
+ out.endElement();
+ }
+
+}
+
+// Copyright (c) 2011 Saxonica Limited. All rights reserved.
+
+
diff --git a/sf/saxon/expr/CastableToUnion.java b/sf/saxon/expr/CastableToUnion.java
new file mode 100644
index 0000000..c16b928
--- /dev/null
+++ b/sf/saxon/expr/CastableToUnion.java
@@ -0,0 +1,273 @@
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.CastableToUnionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.type.UnionType;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+ * Expression class for a cast to a union type
+ */
+public class CastableToUnion extends UnaryExpression {
+
+ private UnionType targetType;
+ private boolean allowEmpty;
+ /*@Nullable*/
+ private NamespaceResolver nsResolver = null;
+
+ public CastableToUnion(Expression source, UnionType targetType, boolean allowEmpty) {
+ super(source);
+ this.targetType = targetType;
+ this.allowEmpty = allowEmpty;
+ }
+
+ public boolean isAllowEmpty() {
+ return allowEmpty;
+ }
+
+ public UnionType getTargetType() {
+ return targetType;
+ }
+
+ public NamespaceResolver getNamespaceResolver() {
+ return nsResolver;
+ }
+
+ /**
+ * Simplify the expression
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ if (targetType.isNamespaceSensitive()) {
+ StaticContext env = visitor.getStaticContext();
+ nsResolver = env.getNamespaceResolver();
+ }
+ operand = visitor.simplify(operand);
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+// ItemType sourceItemType = operand.getItemType(th);
+// if (sourceItemType instanceof ErrorType) {
+// return Literal.makeLiteral(BooleanValue.get(allowEmpty));
+// }
+//
+
+ if (operand instanceof Literal) {
+ GroundedValue literalOperand = ((Literal) operand).getValue();
+ if (literalOperand instanceof AtomicValue) {
+ AtomicValue av = evaluateItem(visitor.getStaticContext().makeEarlyEvaluationContext());
+ return Literal.makeLiteral(av);
+ }
+ if (literalOperand instanceof EmptySequence) {
+ return Literal.makeLiteral(BooleanValue.get(allowEmpty));
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Expression e2 = super.optimize(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ // if the operand can't be empty, then set allowEmpty to false to provide more information for analysis
+ if (!Cardinality.allowsZero(operand.getCardinality())) {
+ allowEmpty = false;
+ resetLocalStaticProperties();
+ }
+ return this;
+ }
+
+ /**
+ * Get the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ return (allowEmpty && Cardinality.allowsZero(operand.getCardinality())
+ ? StaticProperty.ALLOWS_ZERO_OR_ONE : StaticProperty.EXACTLY_ONE);
+ }
+
+ /**
+ * Get the static type of the expression
+ *
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return (ItemType)targetType;
+ }
+
+ /**
+ * Determine the special properties of this expression
+ *
+ * @return {@link net.sf.saxon.expr.StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+ public boolean allowsEmpty() {
+ return allowEmpty;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ CastableToUnion c = new CastableToUnion(getBaseExpression().copy(), targetType, allowEmpty);
+ c.nsResolver = nsResolver;
+ return c;
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ // This method does its own atomization so that it can distinguish between atomization
+ // failures and casting failures
+ int count = 0;
+ SequenceIterator iter = operand.iterate(context);
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (item instanceof NodeInfo) {
+ Sequence atomizedValue = ((NodeInfo)item).atomize();
+ int length = SequenceTool.getLength(atomizedValue);
+ count += length;
+ if (count > 1) {
+ return false;
+ }
+ if (length != 0) {
+ AtomicValue av = (AtomicValue)atomizedValue.head();
+ if (!net.sf.saxon.expr.CastToUnion.castable(av, targetType, nsResolver, context)) {
+ return false;
+ }
+ }
+ } else {
+ AtomicValue av = (AtomicValue)item;
+ count++;
+ if (count > 1) {
+ return false;
+ }
+ if (!net.sf.saxon.expr.CastToUnion.castable(av, targetType, nsResolver, context)) {
+ return false;
+ }
+ }
+ }
+ return count != 0 || allowEmpty;
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return super.equals(other) &&
+ targetType == ((CastableToUnion) other).targetType &&
+ allowEmpty == ((CastableToUnion) other).allowEmpty &&
+ nsResolver == ((CastableToUnion) other).nsResolver;
+ }
+
+ /**
+ * get HashCode for comparing two expressions.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ targetType.hashCode();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CastableToUnion expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public CastableToUnionCompiler getExpressionCompiler() {
+ return new CastableToUnionCompiler();
+ }
+//#endif
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return operand.toString() + " castable as " + targetType.getEQName();
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("castableToUnion");
+ out.emitAttribute("as", targetType.getEQName());
+ operand.explain(out);
+ out.endElement();
+ }
+
+}
+
+// Copyright (c) 2011 Saxonica Limited. All rights reserved.
+
+
diff --git a/sf/saxon/expr/CastingExpression.java b/sf/saxon/expr/CastingExpression.java
new file mode 100644
index 0000000..2d86e98
--- /dev/null
+++ b/sf/saxon/expr/CastingExpression.java
@@ -0,0 +1,203 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.Converter;
+
+import java.util.List;
+
+/**
+ * Casting Expression: abstract superclass for "cast as X" and "castable as X", which share a good deal of logic
+*/
+
+public abstract class CastingExpression extends UnaryExpression {
+
+ private AtomicType targetType;
+ private AtomicType targetPrimitiveType;
+ private boolean allowEmpty = false;
+ protected Converter converter;
+ protected NamespaceResolver nsResolver;
+ private boolean operandIsStringLiteral = false;
+
+ /**
+ * Create a cast expression
+ * @param source expression giving the value to be converted
+ * @param target the type to which the value is to be converted
+ * @param allowEmpty true if the expression allows an empty sequence as input, producing
+ * an empty sequence as output. If false, an empty sequence is a type error.
+ */
+
+ public CastingExpression(Expression source, AtomicType target, boolean allowEmpty) {
+ super(source);
+ this.allowEmpty = allowEmpty;
+ targetType = target;
+ targetPrimitiveType = (AtomicType)target.getPrimitiveItemType();
+ //derived = (targetType.getFingerprint() != targetPrimitiveType.getFingerprint());
+ }
+
+
+
+ /**
+ * Get the primitive base type of the target type of the cast
+ * @return the primitive type of the target type
+ */
+
+ public AtomicType getTargetPrimitiveType() {
+ return targetPrimitiveType;
+ }
+
+ /**
+ * Set the target type
+ * @param type the target type for the cast
+ */
+
+ public void setTargetType(AtomicType type) {
+ targetType = type;
+ }
+
+ /**
+ * Get the target type (the result type)
+ * @return the target type
+ */
+
+ public AtomicType getTargetType() {
+ return targetType;
+ }
+
+ /**
+ * Say whether the expression accepts an empty sequence as input (producing an empty sequence as output)
+ * @param allow true if an empty sequence is accepted
+ */
+
+ public void setAllowEmpty(boolean allow) {
+ allowEmpty = allow;
+ }
+
+ /**
+ * Ask whether the expression accepts an empty sequence as input (producing an empty sequence as output)
+ * @return true if an empty sequence is accepted
+ */
+
+ public boolean allowsEmpty() {
+ return allowEmpty;
+ }
+
+ /**
+ * Say whether the operand to the cast expression was supplied in the form of a string literal. This is
+ * relevant only for XPath 2.0 / XQuery 1.0, and then only when the target type is a QName or NOTATION.
+ * @param option true if the operand was supplied as a string literal
+ */
+
+ public void setOperandIsStringLiteral(boolean option) {
+ operandIsStringLiteral = option;
+ }
+
+ /**
+ * Ask whether the operand to the cast expression was supplied in the form of a string literal. This is
+ * relevant only for XPath 2.0 / XQuery 1.0, and then only when the target type is a QName or NOTATION.
+ * @return true if the operand was supplied as a string literal
+ */
+
+ public boolean isOperandIsStringLiteral() {
+ return operandIsStringLiteral;
+ }
+ /**
+ * Get the Converter allocated to implement this cast expression, if any
+ * @return the Converter if one has been statically allocated, or null if not
+ */
+
+ public Converter getConverter() {
+ return converter;
+ }
+
+ /**
+ * Set the namespace resolver, if any
+ * @param resolver the namespace resolver to be used if the target type is namespace-sensitive
+ */
+
+ public void setNamespaceResolver(NamespaceResolver resolver) {
+ nsResolver = resolver;
+ }
+
+ /**
+ * Get the namespace resolver, if any
+ * @return the namespace resolver that was statically allocated if the target type is namespace-sensitive
+ */
+
+ public NamespaceResolver getNamespaceResolver() {
+ return nsResolver;
+ }
+
+
+ /**
+ * Simplify the expression
+ * @return the simplified expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ StaticContext env = visitor.getStaticContext();
+ if ((targetType instanceof BuiltInAtomicType) && !env.isAllowedBuiltInType((BuiltInAtomicType)targetType)) {
+ // this is checked here because the ConstructorFunctionLibrary doesn't have access to the static
+ // context at bind time
+ XPathException err = new XPathException("The type " + targetType.getDisplayName() +
+ " is not recognized by a Basic XSLT Processor", this);
+ err.setErrorCode("XPST0080");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ if (nsResolver == null && targetType.isNamespaceSensitive()) {
+ nsResolver = env.getNamespaceResolver();
+ }
+ operand = visitor.simplify(operand);
+ return this;
+ }
+
+
+ /**
+ * Determine the special properties of this expression
+ * @return {@link net.sf.saxon.expr.StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * constructs streamable that would not be so according to W3C rules.
+ * @param reasons
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(syntacticContext, allowExtensions, null);
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/CollationMap.java b/sf/saxon/expr/CollationMap.java
new file mode 100644
index 0000000..d404f62
--- /dev/null
+++ b/sf/saxon/expr/CollationMap.java
@@ -0,0 +1,120 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+/**
+ * This object maps collation URIs to collations. Logically this function is part of the static
+ * context, but it is often needed dynamically, so it is defined as a separate component that can
+ * safely be retained at run-time.
+ */
+public class CollationMap implements Serializable {
+
+ private Configuration config;
+ private String defaultCollationName;
+ private HashMap<String, StringCollator> map;
+
+ /**
+ * Create a collation map
+ * @param config the Saxon configuration
+ */
+
+ public CollationMap(Configuration config) {
+ CollationMap old = config.getCollationMap();
+ this.config = config;
+ this.defaultCollationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
+ }
+
+ /**
+ * Create a copy of a collation map
+ * @param in the collation map to be copied
+ */
+
+ public CollationMap(CollationMap in) {
+ if (in.map != null) {
+ map = new HashMap<String, StringCollator>(in.map);
+ }
+ config = in.config;
+ defaultCollationName = in.defaultCollationName;
+ }
+
+ /**
+ * Set the name of the default collation
+ * @param name the default collation name (should be a URI, but this is not enforced)
+ * @throws NullPointerException if the supplied name is null
+ */
+
+ public void setDefaultCollationName(/*@Nullable*/ String name) {
+ if (name == null) {
+ throw new NullPointerException("defaultCollationName");
+ }
+ defaultCollationName = name;
+ }
+
+ /**
+ * Get the name of the default collation
+ * @return the default collation name (should be a URI, but this is not enforced)
+ */
+
+ public String getDefaultCollationName() {
+ return defaultCollationName;
+ }
+
+ /**
+ * Get the default collation
+ * @return the default collation, as a StringCollator
+ */
+
+ public StringCollator getDefaultCollation() {
+ return getNamedCollation(defaultCollationName);
+ }
+
+ /**
+ * Register a named collation
+ * @param absoluteURI the name of the collation. This should be an absolute URI, but
+ * this is not enforced
+ * @param collator the StringCollator that implements the collating rules
+ */
+
+ public void setNamedCollation(String absoluteURI, StringCollator collator) {
+ if (map == null) {
+ map = new HashMap<String, StringCollator>();
+ }
+ map.put(absoluteURI, collator);
+ }
+
+ /**
+ * Get the collation with a given collation name. If the collation name has
+ * not been registered in this CollationMap, the CollationURIResolver registered
+ * with the Configuration is called. If this cannot resolve the collation name,
+ * it should return null.
+ * @param name the collation name (should be an absolute URI)
+ * @return the StringCollator with this name if known, or null if not known
+ */
+
+ public StringCollator getNamedCollation(String name) {
+ if (name.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
+ return CodepointCollator.getInstance();
+ }
+ if (map != null) {
+ StringCollator c = map.get(name);
+ if (c != null) {
+ return c;
+ }
+ }
+ return config.getCollationURIResolver().resolve(name, null, config);
+ }
+}
+
diff --git a/sf/saxon/expr/CompareToIntegerConstant.java b/sf/saxon/expr/CompareToIntegerConstant.java
new file mode 100644
index 0000000..cf84b92
--- /dev/null
+++ b/sf/saxon/expr/CompareToIntegerConstant.java
@@ -0,0 +1,426 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.CompareToIntegerConstantCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.DoubleSortComparer;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.NumericValue;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class implements a comparison of a numeric value to an integer constant using one of the operators
+ * eq, ne, lt, gt, le, ge. The semantics are identical to ValueComparison, but this is a fast path for an
+ * important common case.
+ */
+
+public class CompareToIntegerConstant extends Expression implements ComparisonExpression {
+
+ private Expression operand;
+ private long comparand;
+ private int operator;
+
+ /**
+ * Create the expression
+ * @param operand the operand to be compared with an integer constant. This must
+ * have a static type of NUMERIC, and a cardinality of EXACTLY ONE
+ * @param operator the comparison operator,
+ * one of {@link Token#FEQ}, {@link Token#FNE}, {@link Token#FGE},
+ * {@link Token#FGT}, {@link Token#FLE}, {@link Token#FLT}
+ * @param comparand the integer constant
+ */
+
+ public CompareToIntegerConstant(Expression operand, int operator, long comparand) {
+ this.operand = operand;
+ this.operator = operator;
+ this.comparand = comparand;
+ adoptChildExpression(operand);
+ }
+
+ /**
+ * Get the expression on the lhs of the comparison
+ * @return the left hand operand
+ */
+
+ public Expression getOperand() {
+ return operand;
+ }
+
+ /**
+ * Get the integer value on the rhs of the expression
+ * @return the integer constant
+ */
+
+ public long getComparand() {
+ return comparand;
+ }
+
+ /**
+ * Get the comparison operator
+ * @return one of {@link Token#FEQ}, {@link Token#FNE}, {@link Token#FGE},
+ * {@link Token#FGT}, {@link Token#FLE}, {@link Token#FLT}
+ */
+
+ public int getComparisonOperator() {
+ return operator;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided directly. The other methods will always be available
+ * indirectly, using an implementation that relies on one of the other methods.
+ * @return the value {@link #EVALUATE_METHOD}
+ */
+
+ public int getImplementationMethod() {
+ return EVALUATE_METHOD;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ * @param visitor the expression visitor
+ * @return the simplified expression
+ * @throws XPathException
+ * if an error is discovered during expression
+ * rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ operand = visitor.simplify(operand);
+ return this;
+ }
+
+ /**
+ * Offer promotion for this subexpression. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @return if the offer is not accepted, return this expression unchanged.
+ * Otherwise return the result of rewriting the expression to promote
+ * this subexpression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ operand = doPromotion(operand, offer);
+ return this;
+ }
+ }
+
+ public int computeSpecialProperties() {
+ return StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Compute the dependencies of an expression, as the union of the
+ * dependencies of its subexpressions. (This is overridden for path expressions
+ * and filter expressions, where the dependencies of a subexpression are not all
+ * propogated). This method should be called only once, to compute the dependencies;
+ * after that, getDependencies should be used.
+ *
+ * @return the depencies, as a bit-mask
+ */
+
+ public int computeDependencies() {
+ return operand.getDependencies();
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new CompareToIntegerConstant(operand.copy(), operator, comparand);
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator(operand);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ if (original == operand) {
+ operand = replacement;
+ return true;
+ }
+ return false;
+ }
+
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(NODE_VALUE_CONTEXT, allowExtensions, reasons);
+ }
+//#endif
+
+ /**
+ * Evaluate an expression as a single item. This always returns either a single Item or
+ * null (denoting the empty sequence). No conversion is done. This method should not be
+ * used unless the static type of the expression is a subtype of "item" or "item?": that is,
+ * it should not be called if the expression may return a sequence. There is no guarantee that
+ * this condition will be detected.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the node or atomic value that results from evaluating the
+ * expression; or null to indicate that the result is an empty
+ * sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ NumericValue n = (NumericValue)operand.evaluateItem(context);
+ if (n.isNaN()) {
+ return (operator == Token.FNE);
+ }
+ int c = n.compareTo(comparand);
+ switch (operator) {
+ case Token.FEQ:
+ return c == 0;
+ case Token.FNE:
+ return c != 0;
+ case Token.FGT:
+ return c > 0;
+ case Token.FLT:
+ return c < 0;
+ case Token.FGE:
+ return c >= 0;
+ case Token.FLE:
+ return c <= 0;
+ default:
+ throw new UnsupportedOperationException("Unknown operator " + operator);
+ }
+ }
+
+ protected int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Perform type checking of an expression and its subexpressions. This is the second phase of
+ * static optimization.
+ * <p/>
+ * <p>This checks statically that the operands of the expression have
+ * the correct type; if necessary it generates code to do run-time type checking or type
+ * conversion. A static type error is reported only if execution cannot possibly succeed, that
+ * is, if a run-time type error is inevitable. The call may return a modified form of the expression.</p>
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable. However, the types of such functions and
+ * variables may not be accurately known if they have not been explicitly declared.</p>
+ * <p/>
+ * <p>If the implementation returns a value other than "this", then it is required to ensure that
+ * the parent pointer and location information in the returned expression have been set up correctly.
+ * It should not rely on the caller to do this, although for historical reasons many callers do so.</p>
+ *
+ * @param visitor the expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten to perform necessary run-time type checks,
+ * and to perform other type-related optimizations
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions. This is the third and final
+ * phase of static optimization.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor the expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.optimize(operand, contextItemType);
+ if (operand instanceof Literal) {
+ Literal lit = Literal.makeLiteral(BooleanValue.get(effectiveBooleanValue(null)));
+ ExpressionTool.copyLocationInfo(this, lit);
+ return lit;
+ }
+ return this;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CompareToIntegerConstant expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CompareToIntegerConstantCompiler();
+ }
+//#endif
+
+ /**
+ * Determine the data type of the expression, if possible. All expression return
+ * sequences, in general; this method determines the type of the items within the
+ * sequence, assuming that (a) this is known in advance, and (b) it is the same for
+ * all items in the sequence.
+ * <p/>
+ * <p>This method should always return a result, though it may be the best approximation
+ * that is available at the time.</p>
+ *
+ * @param th the type hierarchy cache
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER,
+ * Type.NODE, or Type.ITEM (meaning not known at compile time)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("compareToInteger");
+ destination.emitAttribute("op", Token.tokens[operator]);
+ destination.emitAttribute("value", comparand+"");
+ operand.explain(destination);
+ destination.endElement();
+ }
+
+ /**
+ * <p>The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form.</p>
+ * <p/>
+ * <p>For subclasses of Expression that represent XPath expressions, the result should always be a string that
+ * parses as an XPath 3.0 expression</p>
+ *
+ * @return a representation of the expression as a string
+ */
+ @Override
+ public String toString() {
+ return ExpressionTool.parenthesize(operand) + " " +
+ Token.tokens[operator] + " " + comparand;
+ }
+
+ /**
+ * Get the AtomicComparer used to compare atomic values. This encapsulates any collation that is used
+ */
+
+ public AtomicComparer getAtomicComparer() {
+ return DoubleSortComparer.getInstance();
+ // Note: this treats NaN=NaN as true, but it doesn't matter, because the rhs will never be NaN.
+ }
+
+ /**
+ * Get the primitive (singleton) operator used: one of Token.FEQ, Token.FNE, Token.FLT, Token.FGT,
+ * Token.FLE, Token.FGE
+ */
+
+ public int getSingletonOperator() {
+ return operator;
+ }
+
+ /**
+ * Get the two operands of the comparison
+ * @return the two operands
+ */
+
+ public Expression[] getOperands() {
+ return new Expression[] {operand, Literal.makeLiteral(Int64Value.makeIntegerValue(comparand))};
+ }
+
+ /**
+ * Determine whether untyped atomic values should be converted to the type of the other operand
+ *
+ * @return true if untyped values should be converted to the type of the other operand, false if they
+ * should be converted to strings.
+ */
+
+ public boolean convertsUntypedToOther() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/expr/ComparisonExpression.java b/sf/saxon/expr/ComparisonExpression.java
new file mode 100644
index 0000000..4d3fed2
--- /dev/null
+++ b/sf/saxon/expr/ComparisonExpression.java
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.sort.AtomicComparer;
+
+import java.io.Serializable;
+
+/**
+ * Interface implemented by expressions that perform a comparison
+ */
+public interface ComparisonExpression extends Serializable {
+
+ /**
+ * Get the AtomicComparer used to compare atomic values. This encapsulates any collation that is used
+ */
+
+ public AtomicComparer getAtomicComparer();
+
+ /**
+ * Get the primitive (singleton) operator used: one of Token.FEQ, Token.FNE, Token.FLT, Token.FGT,
+ * Token.FLE, Token.FGE
+ */
+
+ public int getSingletonOperator();
+
+ /**
+ * Get the two operands of the comparison
+ */
+
+ public Expression[] getOperands();
+
+ /**
+ * Determine whether untyped atomic values should be converted to the type of the other operand
+ * @return true if untyped values should be converted to the type of the other operand, false if they
+ * should be converted to strings.
+ */
+
+ public boolean convertsUntypedToOther();
+}
+
diff --git a/sf/saxon/expr/Container.java b/sf/saxon/expr/Container.java
new file mode 100644
index 0000000..e018093
--- /dev/null
+++ b/sf/saxon/expr/Container.java
@@ -0,0 +1,58 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.expr.instruct.Executable;
+
+import javax.xml.transform.SourceLocator;
+import java.io.Serializable;
+
+/**
+ * A Container is something other than an expression that can act as the container of an expression.
+ * It is typically an object such as a function, a global variable, or in XSLT a template, or an attribute set.
+ * When free-standing XPath expressions are compiled, the static context for the expression acts as its
+ * container.
+ */
+
+public interface Container extends SourceLocator, Serializable {
+
+ /**
+ * Get the Executable (representing a complete stylesheet or query) of which this Container forms part
+ * @return the executable
+ */
+
+ /*@Nullable*/ public Executable getExecutable();
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ * @return the location provider
+ */
+
+ public LocationProvider getLocationProvider();
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+
+ public int getHostLanguage();
+
+ /**
+ * Get the granularity of the container. During successive phases of compilation, growing
+ * expression trees are rooted in containers of increasing granularity. The granularity
+ * of the container is used to avoid "repotting" a tree more frequently than is required,
+ * as this requires a complete traversal of the tree which can take a measurable time.
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity();
+}
+
diff --git a/sf/saxon/expr/ContextItemExpression.java b/sf/saxon/expr/ContextItemExpression.java
new file mode 100644
index 0000000..34008e0
--- /dev/null
+++ b/sf/saxon/expr/ContextItemExpression.java
@@ -0,0 +1,338 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ContextItemCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.AnchorPattern;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.List;
+
+
+/**
+ * This class represents the expression "(dot)", which always returns the context item.
+ * This may be a AtomicValue or a Node.
+ */
+
+public class ContextItemExpression extends Expression {
+
+ ItemType itemType = Type.ITEM_TYPE;
+ boolean contextPossiblyUndefined = true;
+
+ /**
+ * Create the expression
+ */
+
+ public ContextItemExpression() {
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ *
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "contextItem";
+ }
+
+ /**
+ * Create a clone copy of this expression
+ *
+ * @return a copy of this expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ContextItemExpression cie2 = new ContextItemExpression();
+ cie2.itemType = itemType;
+ cie2.contextPossiblyUndefined = contextPossiblyUndefined;
+ return cie2;
+ }
+
+ protected String getErrorCodeForUndefinedContext() {
+ return "XPDY0002";
+ }
+
+ /**
+ * Type-check the expression.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (contextItemType == null || contextItemType.itemType == null) {
+ XPathException err = new XPathException("There is no context item");
+ err.setErrorCode(getErrorCodeForUndefinedContext());
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ visitor.issueWarning("Evaluation will always fail: there is no context item", this);
+ return new ErrorExpression(err);
+ } else {
+ contextPossiblyUndefined = contextItemType.contextMaybeUndefined;
+ }
+ itemType = contextItemType.itemType;
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ // In XSLT, we don't catch this error at the typeCheck() phase because it's done one XPath expression
+ // at a time. So we repeat the check here.
+ if (contextItemType == null) {
+ XPathException err = new XPathException("The context item is undefined at this point");
+ err.setErrorCode(getErrorCodeForUndefinedContext());
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ return this;
+ }
+
+ /**
+ * Determine the item type
+ *
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return itemType;
+ }
+
+ /**
+ * Ask whether the context item may possibly be undefined
+ *
+ * @return true if it might be undefined
+ */
+
+ public boolean isContextPossiblyUndefined() {
+ return contextPossiblyUndefined;
+ }
+
+ /**
+ * Get the static cardinality
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Determine the special properties of this expression
+ *
+ * @return the value {@link StaticProperty#NON_CREATIVE}
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE | StaticProperty.CONTEXT_DOCUMENT_NODESET;
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return (other instanceof ContextItemExpression);
+ }
+
+ /**
+ * get HashCode for comparing two expressions
+ */
+
+ public int hashCode() {
+ return "ContextItemExpression".hashCode();
+ }
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ if (pathMapNodeSet == null) {
+ pathMapNodeSet = new PathMap.PathMapNodeSet(pathMap.makeNewRoot(this));
+ }
+ return pathMapNodeSet;
+ }
+
+ /**
+ * Determine whether the expression can be evaluated without reference to the part of the context
+ * document outside the subtree rooted at the context node.
+ *
+ * @return true if the expression has no dependencies on the context node, or if the only dependencies
+ * on the context node are downward selections using the self, child, descendant, attribute, and namespace
+ * axes.
+ */
+
+ public boolean isSubtreeExpression() {
+ return true;
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ switch (syntacticContext) {
+ case NAVIGATION_CONTEXT:
+ if (reasons != null) {
+ reasons.add("Context item expression (.) in a navigational context is free-ranging");
+ }
+ return W3C_FREE_RANGING;
+ case INSPECTION_CONTEXT:
+ return W3C_MOTIONLESS;
+ case NODE_VALUE_CONTEXT:
+ default:
+ return W3C_CONSUMING;
+ }
+ }
+//#endif
+
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ return new AnchorPattern();
+ //throw new XPathException("'.' is not valid as a step in a path pattern");
+ }
+
+ //#ifdefined STREAM
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ return new AnchorPattern();
+ }
+
+ //#endif
+
+ /**
+ * Iterate over the value of the expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ Item item = context.getContextItem();
+ if (item == null) {
+ dynamicError("The context item is absent", getErrorCodeForUndefinedContext(), context);
+ }
+ return SingletonIterator.makeIterator(item);
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ Item item = context.getContextItem();
+ if (item == null) {
+ dynamicError("The context item is absent", getErrorCodeForUndefinedContext(), context);
+ }
+ return item;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ContextItem expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ContextItemCompiler();
+ }
+//#endif
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return ".";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("dot");
+ destination.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/ContextMappingFunction.java b/sf/saxon/expr/ContextMappingFunction.java
new file mode 100644
index 0000000..33a9d04
--- /dev/null
+++ b/sf/saxon/expr/ContextMappingFunction.java
@@ -0,0 +1,37 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * ContextMappingFunction is an interface that must be satisfied by an object passed to a
+ * ContextMappingIterator. It represents an object which, given an Item, can return a
+ * SequenceIterator that delivers a sequence of zero or more Items.
+ * <p>
+ * This is a specialization of the more general MappingFunction class: it differs in that
+ * each item being processed becomes the context item while it is being processed.
+*/
+
+public interface ContextMappingFunction<T extends Item> {
+
+ /**
+ * Map one item to a sequence.
+ * @param context The processing context. The item to be mapped is the context item identified
+ * from this context: the values of position() and last() also relate to the set of items being mapped
+ * @return a SequenceIterator over the sequence of items that the supplied input
+ * item maps to
+ * @throws XPathException if a dynamic error is detected
+ */
+
+ public abstract SequenceIterator<T> map(XPathContext context) throws XPathException;
+
+}
+
diff --git a/sf/saxon/expr/ContextMappingIterator.java b/sf/saxon/expr/ContextMappingIterator.java
new file mode 100644
index 0000000..4a06c24
--- /dev/null
+++ b/sf/saxon/expr/ContextMappingIterator.java
@@ -0,0 +1,117 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* ContextMappingIterator merges a sequence of sequences into a single flat
+* sequence. It takes as inputs an iteration, and a mapping function to be
+* applied to each Item returned by that iteration. The mapping function itself
+* returns another iteration. The result is an iteration of the concatenation of all
+* the iterations returned by the mapping function.<p>
+*
+* This is a specialization of the MappingIterator class: it differs in that it
+* sets each item being processed as the context item
+*/
+
+public final class ContextMappingIterator<T extends Item> implements SequenceIterator<T> {
+
+ private SequenceIterator base;
+ private ContextMappingFunction<T> action;
+ private XPathContext context;
+ /*@Nullable*/ private SequenceIterator<T> stepIterator = null;
+ private T current = null;
+ private int position = 0;
+
+ /**
+ * Construct a ContextMappingIterator that will apply a specified ContextMappingFunction to
+ * each Item returned by the base iterator.
+ * @param action the mapping function to be applied
+ * @param context the processing context. The mapping function is applied to each item returned
+ * by context.getCurrentIterator() in turn.
+ */
+
+ public ContextMappingIterator(ContextMappingFunction<T> action, XPathContext context) {
+ base = context.getCurrentIterator();
+ this.action = action;
+ this.context = context;
+ }
+
+ public T next() throws XPathException {
+ T nextItem;
+ while (true) {
+ if (stepIterator != null) {
+ nextItem = stepIterator.next();
+ if (nextItem != null) {
+ break;
+ } else {
+ stepIterator = null;
+ }
+ }
+ if (base.next() != null) {
+ // Call the supplied mapping function
+ stepIterator = action.map(context);
+ nextItem = stepIterator.next();
+ if (nextItem == null) {
+ stepIterator = null;
+ } else {
+ break;
+ }
+
+ } else {
+ stepIterator = null;
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+
+ current = nextItem;
+ position++;
+ return nextItem;
+ }
+
+ public T current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ SequenceIterator newBase = base.getAnother();
+ XPathContextMinor c2 = context.newMinorContext();
+ c2.setCurrentIterator(newBase);
+ return new ContextMappingIterator(action, c2);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED}, {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/expr/ContextSwitchingExpression.java b/sf/saxon/expr/ContextSwitchingExpression.java
new file mode 100644
index 0000000..a33c398
--- /dev/null
+++ b/sf/saxon/expr/ContextSwitchingExpression.java
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+/**
+ * Interface implemented by expressions that switch the context, for example A/B or A[B]
+ */
+public interface ContextSwitchingExpression {
+
+ /**
+ * Get the subexpression that sets the context item
+ * @return the subexpression that sets the context item, position, and size to each of its
+ * items in turn
+ */
+
+ public Expression getControllingExpression();
+
+ /**
+ * Get the subexpression that is evaluated in the new context
+ * @return the subexpression evaluated in the context set by the controlling expression
+ */
+
+ public Expression getControlledExpression();
+}
+
diff --git a/sf/saxon/expr/CurrentItemExpression.java b/sf/saxon/expr/CurrentItemExpression.java
new file mode 100644
index 0000000..a9cd350
--- /dev/null
+++ b/sf/saxon/expr/CurrentItemExpression.java
@@ -0,0 +1,41 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ContextItemCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+
+/**
+ * The expression is generated when compiling the current() function in XSLT. It differs from
+ * the ContextItemExpression "." only in the error code that is returned when there is no context item.
+ */
+
+public class CurrentItemExpression extends ContextItemExpression {
+
+ /**
+ * Get the error code for use when there is no context item
+ * @return the string "XTDE1360"
+ */
+
+ protected String getErrorCodeForUndefinedContext() {
+ return "XTDE1360";
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CurrentItem expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ContextItemCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/expr/DifferenceEnumeration.java b/sf/saxon/expr/DifferenceEnumeration.java
new file mode 100644
index 0000000..2cdcb50
--- /dev/null
+++ b/sf/saxon/expr/DifferenceEnumeration.java
@@ -0,0 +1,148 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.sort.ItemOrderComparer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+
+/**
+* An enumeration representing a nodeset that is teh difference of two other NodeSets.
+* There is an "except" operator in XPath 2.0 to create such an expression.
+*/
+
+
+public class DifferenceEnumeration implements SequenceIterator {
+
+
+ private SequenceIterator p1;
+ private SequenceIterator p2;
+
+ /*@Nullable*/ private NodeInfo nextNode1 = null;
+ private NodeInfo nextNode2 = null;
+ private ItemOrderComparer comparer;
+
+ //private NodeInfo nextNode = null;
+ private NodeInfo current = null;
+ private int position = 0;
+
+ /**
+ * Form an enumeration of the difference of two nodesets, that is, the nodes
+ * that are in p1 and that are not in p2.
+ * @param p1 the first operand, with nodes delivered in document order
+ * @param p2 the second operand, with nodes delivered in document order
+ * @param comparer the comparer
+ */
+
+ public DifferenceEnumeration(SequenceIterator p1, SequenceIterator p2,
+ ItemOrderComparer comparer) throws XPathException {
+ this.p1 = p1;
+ this.p2 = p2;
+ this.comparer = comparer;
+
+ // move to the first node in each input nodeset
+
+ nextNode1 = next(p1);
+ nextNode2 = next(p2);
+ }
+
+ /**
+ * Get the next item from one of the input sequences,
+ * checking that it is a node.
+ * @param iter the iterator from which the next node is to be read
+ * @return the node that was read, or null if the stream is exhausted
+ */
+
+ private NodeInfo next(SequenceIterator iter) throws XPathException {
+ return (NodeInfo)iter.next();
+ // rely on type-checking to prevent a ClassCastException
+ }
+
+ public Item next() throws XPathException {
+ // main merge loop: if the node in p1 has a lower key value that that in p2, return it;
+ // if they are equal, advance both nodesets; if p1 is higher, advance p2.
+
+ while (true) {
+
+ if (nextNode1 == null) {
+ current = null;
+ position = -1;
+ return null;
+ }
+
+ if (nextNode2 == null) {
+ // second node-set is exhausted; return the next node from the first node-set
+ return deliver();
+ }
+
+ int c = comparer.compare(nextNode1, nextNode2);
+ if (c<0) { // p1 is lower
+ return deliver();
+
+ } else if (c>0) { // p1 is higher
+ nextNode2 = next(p2);
+ if (nextNode2 == null) {
+ return deliver();
+ }
+
+ } else { // keys are equal
+ nextNode2 = next(p2);
+ nextNode1 = next(p1);
+ }
+ }
+ }
+
+ /**
+ * Deliver the next node from the first node-set, advancing the iterator to
+ * look-ahead for the next item, and setting the current and position variables.
+ * @return the next node from the first node-set
+ * @throws XPathException
+ */
+ private NodeInfo deliver() throws XPathException {
+ current = nextNode1;
+ nextNode1 = next(p1);
+ position++;
+ return current;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ p1.close();
+ p2.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new DifferenceEnumeration(p1.getAnother(), p2.getAnother(), comparer);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link SequenceIterator#GROUNDED}, {@link SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+}
+
diff --git a/sf/saxon/expr/EagerLetExpression.java b/sf/saxon/expr/EagerLetExpression.java
new file mode 100644
index 0000000..af43934
--- /dev/null
+++ b/sf/saxon/expr/EagerLetExpression.java
@@ -0,0 +1,56 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.LetExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * An EagerLetExpression is the same as a LetExpression except that the variable is evaluated using
+ * eager evaluation rather than lazy evaluation. This is used when performing diagnostic tracing.
+ */
+
+public class EagerLetExpression extends LetExpression {
+
+ public EagerLetExpression() {}
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e == this) {
+ setEvaluationMode(ExpressionTool.eagerEvaluationMode(sequence));
+ }
+ return e;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the EagerLet expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new LetExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * Evaluate the variable.
+ */
+
+// protected ValueRepresentation eval(XPathContext context) throws XPathException {
+// return ExpressionTool.eagerEvaluate(sequence, context);
+// }
+
+}
+
+
diff --git a/sf/saxon/expr/EarlyEvaluationContext.java b/sf/saxon/expr/EarlyEvaluationContext.java
new file mode 100644
index 0000000..cdeccca
--- /dev/null
+++ b/sf/saxon/expr/EarlyEvaluationContext.java
@@ -0,0 +1,413 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.instruct.ParameterSet;
+import net.sf.saxon.expr.sort.GroupIterator;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.regex.RegexIterator;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trans.Mode;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.Rule;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.DateTimeValue;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Iterator;
+
+/**
+ * This class is an implementation of XPathContext used when evaluating constant sub-expressions at
+ * compile time.
+ */
+
+public class EarlyEvaluationContext implements XPathContext, Serializable {
+
+ private CollationMap collationMap;
+ private Configuration config;
+
+ /**
+ * Create an early evaluation context, used for evaluating constant expressions at compile time
+ * @param config the Saxon configuration
+ * @param map the available collations
+ */
+
+ public EarlyEvaluationContext(Configuration config, CollationMap map) {
+ this.config = config;
+ collationMap = map;
+ }
+
+ /**
+ * Set a new output destination, supplying the output format details. <BR>
+ * Note that it is the caller's responsibility to close the Writer after use.
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs; and
+ * specifically, if an attempt is made to switch to a final output
+ * destination while writing a temporary tree or sequence
+ */
+
+ public void changeOutputDestination(Receiver receiver, ParseOptions options) throws XPathException {
+ notAllowed();
+ }
+
+ /**
+ * Get the value of a local variable, identified by its slot number
+ */
+
+ /*@Nullable*/ public Sequence evaluateLocalVariable(int slotnumber) {
+ notAllowed();
+ return null;
+ }
+
+ /**
+ * Get the calling XPathContext (the next one down the stack). This will be null if unknown, or
+ * if the bottom of the stack has been reached.
+ */
+
+ public XPathContext getCaller() {
+ return null;
+ }
+
+ /**
+ * Get a named collation
+ */
+
+ public StringCollator getCollation(String name) throws XPathException {
+ return collationMap.getNamedCollation(name);
+ }
+
+ /**
+ * Get the Configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the context item
+ *
+ * @return the context item, or null if the context item is undefined
+ */
+
+ public Item getContextItem() {
+ return null;
+ }
+
+ /**
+ * Get the Controller. May return null when running outside XSLT or XQuery
+ */
+
+ public Controller getController() {
+ return null;
+ }
+
+ /**
+ * Get the current group iterator. This supports the current-group() and
+ * current-grouping-key() functions in XSLT 2.0
+ *
+ * @return the current grouped collection
+ */
+
+ public GroupIterator getCurrentGroupIterator() {
+ notAllowed();
+ return null;
+ }
+
+ /**
+ * Get the current iterator.
+ * This encapsulates the context item, context position, and context size.
+ *
+ * @return the current iterator, or null if there is no current iterator
+ * (which means the context item, position, and size are undefined).
+ */
+
+ public SequenceIterator getCurrentIterator() {
+ return null;
+ }
+
+ /**
+ * Get the current mode.
+ *
+ * @return the current mode
+ */
+
+ public Mode getCurrentMode() {
+ notAllowed();
+ return null;
+ }
+
+ /**
+ * Get the current regex iterator. This supports the functionality of the regex-group()
+ * function in XSLT 2.0.
+ *
+ * @return the current regular expressions iterator
+ */
+
+ public RegexIterator getCurrentRegexIterator() {
+ return null;
+ }
+
+ /**
+ * Get the current template. This is used to support xsl:apply-imports
+ *
+ * @return the current template
+ */
+
+ public Rule getCurrentTemplateRule() {
+ return null;
+ }
+
+ /**
+ * Get the context size (the position of the last item in the current node list)
+ *
+ * @return the context size
+ * @throws net.sf.saxon.trans.XPathException
+ * if the context position is undefined
+ */
+
+ public int getLast() throws XPathException {
+ XPathException err = new XPathException("The context item is absent");
+ err.setErrorCode("XPDY0002");
+ throw err;
+ }
+
+ /**
+ * Get the local (non-tunnel) parameters that were passed to the current function or template
+ *
+ * @return a ParameterSet containing the local parameters
+ */
+
+ public ParameterSet getLocalParameters() {
+ notAllowed();
+ return null;
+ }
+
+ /**
+ * Get the Name Pool
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ /**
+ * Get information about the creating expression or other construct.
+ */
+
+ public InstructionInfo getOrigin() {
+ return null;
+ }
+
+ /**
+ * Get the type of location from which this context was created.
+ */
+
+ public int getOriginatingConstructType() {
+ return -1;
+ }
+
+ /**
+ * Get the Receiver to which output is currently being written.
+ *
+ * @return the current Receiver
+ */
+ public SequenceReceiver getReceiver() {
+ notAllowed();
+ return null;
+ }
+
+ /**
+ * Get a reference to the local stack frame for variables. Note that it's
+ * the caller's job to make a local copy of this. This is used for creating
+ * a Closure containing a retained copy of the variables for delayed evaluation.
+ *
+ * @return array of variables.
+ */
+
+ public StackFrame getStackFrame() {
+ notAllowed();
+ return null;
+ }
+
+ /**
+ * Get the tunnel parameters that were passed to the current function or template. This includes all
+ * active tunnel parameters whether the current template uses them or not.
+ *
+ * @return a ParameterSet containing the tunnel parameters
+ */
+
+ public ParameterSet getTunnelParameters() {
+ notAllowed();
+ return null;
+ }
+
+ /**
+ * Determine whether the context position is the same as the context size
+ * that is, whether position()=last()
+ */
+
+ public boolean isAtLast() throws XPathException {
+ XPathException err = new XPathException("The context item is absent");
+ err.setErrorCode("XPDY0002");
+ throw err;
+ }
+
+ /**
+ * Construct a new context without copying (used for the context in a function call)
+ */
+
+ public XPathContextMajor newCleanContext() {
+ notAllowed();
+ return null;
+ }
+
+ /**
+ * Construct a new context as a copy of another. The new context is effectively added
+ * to the top of a stack, and contains a pointer to the previous context
+ */
+
+ public XPathContextMajor newContext() {
+ Controller controller = new Controller(config);
+ return controller.newXPathContext();
+// notAllowed();
+// return null;
+ }
+
+ /**
+ * Construct a new minor context. A minor context can only hold new values of the focus
+ * (currentIterator) and current output destination.
+ */
+
+ public XPathContextMinor newMinorContext() {
+ return newContext().newMinorContext();
+// notAllowed();
+// return null;
+ }
+
+ /**
+ * Set the calling XPathContext
+ */
+
+ public void setCaller(XPathContext caller) {
+ // no-op
+ }
+
+ /**
+ * Set a new sequence iterator.
+ */
+
+ public void setCurrentIterator(SequenceIterator iter) {
+ notAllowed();
+ }
+
+ /**
+ * Set the value of a local variable, identified by its slot number
+ */
+
+ public void setLocalVariable(int slotnumber, Sequence value) {
+ notAllowed();
+ }
+
+ /**
+ * Change the Receiver to which output is written
+ */
+
+ public void setReceiver(SequenceReceiver receiver) {
+ notAllowed();
+ }
+
+ /**
+ * Use local parameter. This is called when a local xsl:param element is processed.
+ * If a parameter of the relevant name was supplied, it is bound to the xsl:param element.
+ * Otherwise the method returns false, so the xsl:param default will be evaluated
+ *
+ * @param parameterId
+ * @param slotNumber
+ * @param isTunnel True if a tunnel parameter is required, else false @return true if a parameter of this name was supplied, false if not
+ */
+
+ public int useLocalParameter(int parameterId, int slotNumber, boolean isTunnel) throws XPathException {
+ return ParameterSet.NOT_SUPPLIED;
+ }
+
+ /**
+ * Get the current date and time. This implementation always throws a
+ * NoDynamicContextException.
+ * @return the current date and time. All calls within a single query or transformation
+ * will return the same value
+ */
+
+ public DateTimeValue getCurrentDateTime() throws NoDynamicContextException {
+ throw new NoDynamicContextException("current-dateTime");
+ }
+
+ /**
+ * Get the implicit timezone, as a positive or negative offset from UTC in minutes.
+ * The range is -14hours to +14hours. This implementation always throws a
+ * NoDynamicContextException.
+ * @return the implicit timezone, as an offset from UTC in minutes
+ */
+
+ public int getImplicitTimezone() throws NoDynamicContextException{
+ throw new NoDynamicContextException("implicit-timezone");
+ }
+
+
+ /**
+ * Get the context stack. This method returns an iterator whose items are instances of
+ * {@link net.sf.saxon.trace.ContextStackFrame}, starting with the top-most stackframe and
+ * ending at the point the query or transformation was invoked by a calling application.
+ *
+ * @return an iterator over a copy of the run-time call stack
+ */
+
+ public Iterator iterateStackFrames() {
+ return Collections.EMPTY_LIST.iterator();
+ }
+
+ /**
+ * Get the current exception (in saxon:catch)
+ * @return the current exception, or null if there is none defined
+ */
+
+ public XPathException getCurrentException() {
+ return null;
+ }
+
+ public void notifyChildThreads() throws XPathException {
+ getCaller().notifyChildThreads();
+ }
+
+ /**
+ * Ask whether the XSLT output state is "temporary" or "final"
+ *
+ * @return true to set temporary output state; false to set final output state
+ */
+ public boolean isTemporaryOutputState() {
+ return false;
+ }
+
+ /**
+ * Throw an error for operations that aren't supported when doing early evaluation of constant
+ * subexpressions
+ */
+
+ private void notAllowed() {
+ throw new UnsupportedOperationException(
+ new NoDynamicContextException("Internal error: early evaluation of subexpression with no context"));
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/EmptyTextNodeRemover.java b/sf/saxon/expr/EmptyTextNodeRemover.java
new file mode 100644
index 0000000..bd51751
--- /dev/null
+++ b/sf/saxon/expr/EmptyTextNodeRemover.java
@@ -0,0 +1,158 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.EmptyTextNodeRemoverCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.EmptyTextNodeRemoverAdjunct;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.Iterator;
+
+
+/**
+ * This class performs the first phase of processing in "constructing simple content":
+ * it takes an input sequence, eliminates empty text nodes, and combines adjacent text nodes
+ * into one.
+ * @since 9.3
+ */
+public class EmptyTextNodeRemover extends UnaryExpression
+ implements ItemMappingFunction {
+
+ public EmptyTextNodeRemover(Expression p0) {
+ super(p0);
+ }
+
+ /**
+ * Determine the data type of the expression, if possible. The default
+ * implementation for unary expressions returns the item type of the operand
+ * @param th the type hierarchy cache
+ * @return the item type of the items in the result sequence, insofar as this
+ * is known statically.
+ */
+
+ /*@NotNull*/
+ @Override
+ public ItemType getItemType(TypeHierarchy th) {
+ return getBaseExpression().getItemType(th);
+ }
+
+ @Override
+ public int computeCardinality() {
+ return getBaseExpression().getCardinality() | StaticProperty.ALLOWS_ZERO;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new EmptyTextNodeRemover(getBaseExpression().copy());
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new MonoIterator<SubExpressionInfo>(
+ new SubExpressionInfo(getBaseExpression(), true, false, INHERITED_CONTEXT));
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is prefered.
+ */
+
+ public int getImplementationMethod() {
+ return Expression.ITERATE_METHOD | ITEM_FEED_METHOD | WATCH_METHOD ;
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ @Override
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ return new ItemMappingIterator(getBaseExpression().iterate(context), this);
+ }
+
+ /**
+ * Map an item to another item
+ * @param item The input item to be mapped.
+ * @return the result of the mapping: maybe null
+ * @throws XPathException
+ */
+
+ /*@Nullable*/ public Item mapItem(Item item) throws XPathException {
+ if (item instanceof NodeInfo &&
+ ((NodeInfo)item).getNodeKind() == Type.TEXT &&
+ item.getStringValueCS().length() == 0) {
+ return null;
+ } else {
+ return item;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the EmptyTextNodeRemover expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new EmptyTextNodeRemoverCompiler();
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public EmptyTextNodeRemoverAdjunct getStreamingAdjunct() {
+ return new EmptyTextNodeRemoverAdjunct();
+ }
+
+
+//#endif
+
+
+
+ @Override
+ public String getExpressionName() {
+ return "emptyTextNodeRemover";
+ }
+
+}
+
diff --git a/sf/saxon/expr/ErrorExpression.java b/sf/saxon/expr/ErrorExpression.java
new file mode 100644
index 0000000..66126d8
--- /dev/null
+++ b/sf/saxon/expr/ErrorExpression.java
@@ -0,0 +1,154 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ErrorExpressionCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+
+/**
+* Error expression: this expression is generated when the supplied expression cannot be
+* parsed, and the containing element enables forwards-compatible processing. It defers
+* the generation of an error message until an attempt is made to evaluate the expression
+*/
+
+public class ErrorExpression extends Expression {
+
+ private XPathException exception; // the error found when parsing this expression
+
+ /**
+ * This constructor is never executed, but it is used in the expression parser
+ * as a dummy so that the Java compiler recognizes parsing methods as always returning
+ * a non-null result.
+ */
+ public ErrorExpression() {
+ this(new XPathException("Unspecified error"));
+ }
+
+ /**
+ * Constructor
+ * @param exception the error to be thrown when this expression is evaluated
+ */
+
+ public ErrorExpression(XPathException exception) {
+ this.exception = exception;
+ exception.setLocator(this); // to remove any links to the compile-time stylesheet objects
+ }
+
+ /**
+ * Get the wrapped exception
+ * @return the exception to be thrown when the expression is evaluated
+ */
+
+ public XPathException getException() {
+ return exception;
+ }
+
+ /**
+ * Type-check the expression.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Evaluate the expression. This always throws the exception registered when the expression
+ * was first parsed.
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ // copy the exception for thread-safety, because we want to add context information
+ XPathException err = new XPathException(exception.getMessage());
+ err.setLocator(this);
+ err.setErrorCodeQName(exception.getErrorCodeQName());
+ err.setXPathContext(context);
+ throw err;
+ }
+
+ /**
+ * Iterate over the expression. This always throws the exception registered when the expression
+ * was first parsed.
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ evaluateItem(context);
+ return null; // to fool the compiler
+ }
+
+ /**
+ * Determine the data type of the expression, if possible
+ * @return Type.ITEM (meaning not known in advance)
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Determine the static cardinality
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ // we return a liberal value, so that we never get a type error reported
+ // statically
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new ErrorExpression(exception);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Error expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ErrorExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("error");
+ destination.emitAttribute("message", exception.getMessage());
+ destination.endElement();
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/ErrorIterator.java b/sf/saxon/expr/ErrorIterator.java
new file mode 100644
index 0000000..77dc96b
--- /dev/null
+++ b/sf/saxon/expr/ErrorIterator.java
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A SequenceIterator that throws an exception as soon as its next() method is called. Used when
+ * the method that returns the iterator isn't allowed to throw a checked exception itself.
+ */
+public class ErrorIterator implements SequenceIterator {
+
+ private XPathException exception;
+
+ public ErrorIterator(XPathException exception) {
+ this.exception = exception;
+ }
+
+ /**
+ * Get the next item in the sequence. This method changes the state of the
+ * iterator, in particular it affects the result of subsequent calls of
+ * position() and current().
+ *
+ * @return the next item, or null if there are no more items. Once a call
+ * on next() has returned null, no further calls should be made. The preferred
+ * action for an iterator if subsequent calls on next() are made is to return
+ * null again, and all implementations within Saxon follow this rule.
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error occurs retrieving the next item
+ * @since 8.4
+ */
+
+ public Item next() throws XPathException {
+ throw exception;
+ }
+
+ /**
+ * Get the current value in the sequence (the one returned by the
+ * most recent call on next()). This will be null before the first
+ * call of next(). This method does not change the state of the iterator.
+ *
+ * @return the current item, the one most recently returned by a call on
+ * next(). Returns null if next() has not been called, or if the end
+ * of the sequence has been reached.
+ * @since 8.4
+ */
+
+ /*@Nullable*/ public Item current() {
+ return null;
+ }
+
+ /**
+ * Get the current position. This will usually be zero before the first call
+ * on next(), otherwise it will be the number of times that next() has
+ * been called. Once next() has returned null, the preferred action is
+ * for subsequent calls on position() to return -1, but not all existing
+ * implementations follow this practice. (In particular, the EmptyIterator
+ * is stateless, and always returns 0 as the value of position(), whether
+ * or not next() has been called.)
+ * <p/>
+ * This method does not change the state of the iterator.
+ *
+ * @return the current position, the position of the item returned by the
+ * most recent call of next(). This is 1 after next() has been successfully
+ * called once, 2 after it has been called twice, and so on. If next() has
+ * never been called, the method returns zero. If the end of the sequence
+ * has been reached, the value returned will always be <= 0; the preferred
+ * value is -1.
+ * @since 8.4
+ */
+
+ public int position() {
+ return 0;
+ }
+
+ public void close() {
+
+ }
+
+ /**
+ * Get another SequenceIterator that iterates over the same items as the original,
+ * but which is repositioned at the start of the sequence.
+ * <p/>
+ * This method allows access to all the items in the sequence without disturbing the
+ * current position of the iterator. Internally, its main use is in evaluating the last()
+ * function.
+ * <p/>
+ * This method does not change the state of the iterator.
+ *
+ * @return a SequenceIterator that iterates over the same items,
+ * positioned before the first item
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error occurs
+ * @since 8.4
+ */
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return this;
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ * @since 8.6
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/expr/EveryItemMappingIterator.java b/sf/saxon/expr/EveryItemMappingIterator.java
new file mode 100644
index 0000000..ae5fb09
--- /dev/null
+++ b/sf/saxon/expr/EveryItemMappingIterator.java
@@ -0,0 +1,84 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * EveryItemMappingIterator applies a mapping function to each item in a sequence.
+ * The mapping function always returns a single item (never null)
+ * <p/>
+ * This is a specialization of the more general MappingIterator class, for use
+ * in cases where a single input item always maps to exactly one output item
+ */
+
+public final class EveryItemMappingIterator implements SequenceIterator {
+
+ private SequenceIterator base;
+ private ItemMappingFunction action;
+ /*@Nullable*/ private Item current = null;
+
+ /**
+ * Construct an ItemMappingIterator that will apply a specified DummyItemMappingFunction to
+ * each Item returned by the base iterator.
+ *
+ * @param base the base iterator
+ * @param action the mapping function to be applied
+ */
+
+ public EveryItemMappingIterator(SequenceIterator base, ItemMappingFunction action) {
+ this.base = base;
+ this.action = action;
+ }
+
+ public Item next() throws XPathException {
+ Item nextSource = base.next();
+ if (nextSource == null) {
+ current = null;
+ return null;
+ }
+ // Call the supplied mapping function
+ current = action.mapItem(nextSource);
+ return current;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return base.position();
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new EveryItemMappingIterator(base.getAnother(), action);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED},
+ * {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+}
+
diff --git a/sf/saxon/expr/Expression.java b/sf/saxon/expr/Expression.java
new file mode 100644
index 0000000..b7fb29d
--- /dev/null
+++ b/sf/saxon/expr/Expression.java
@@ -0,0 +1,1409 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.CallableExpressionCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.IsWholeNumberCompiler;
+import com.saxonica.bytecode.util.CannotCompileException;
+import com.saxonica.stream.ExpressionInverter;
+import com.saxonica.stream.adjunct.StreamingAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.TypeCheckerEnvironment;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.evpull.EmptyEventIterator;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.evpull.EventIteratorOverSequence;
+import net.sf.saxon.evpull.SingletonEventIterator;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.LocationMap;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.functions.IntegratedFunctionCall;
+import net.sf.saxon.functions.IsWholeNumber;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.pattern.NodeSetPattern;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.style.StyleElement;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.z.IntHashSet;
+import net.sf.saxon.z.IntIterator;
+
+import javax.xml.transform.SourceLocator;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Interface supported by an XPath expression. This includes both compile-time
+ * and run-time methods.
+ * <p/>
+ * <p>Two expressions are considered equal if they return the same result when evaluated in the
+ * same context.</p>
+ */
+
+public abstract class Expression
+ implements Serializable, SourceLocator, InstructionInfo {
+
+ public static final int EVALUATE_METHOD = 1;
+ public static final int ITERATE_METHOD = 2;
+ public static final int PROCESS_METHOD = 4;
+ public static final int WATCH_METHOD = 8;
+ public static final int ITEM_FEED_METHOD = 16;
+ public static final int EVENT_FEED_METHOD = 32;
+ public static final int PUSH_SELECTION = 64;
+ public static final int EFFECTIVE_BOOLEAN_VALUE = 128;
+
+ public static final int W3C_MOTIONLESS = 0;
+ public static final int W3C_GROUP_CONSUMING = 1;
+ public static final int W3C_CONSUMING = 2;
+ public static final int W3C_FREE_RANGING = 3;
+
+ public static final int NAVIGATION_CONTEXT = 0;
+ public static final int NODE_VALUE_CONTEXT = 1;
+ public static final int INHERITED_CONTEXT = 2;
+ public static final int INSPECTION_CONTEXT = 3;
+
+ protected int staticProperties = -1;
+ protected int locationId = -1;
+ /*@Nullable*/ private Container container;
+ private int[] slotsUsed;
+ private int evaluationMethod;
+
+ private void writeObject(ObjectOutputStream oos) throws IOException {
+ //System.err.println("Expression " + this.getClass());
+ if (container instanceof StyleElement) {
+ container = null;
+ }
+ oos.defaultWriteObject();
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ *
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return getClass().getName();
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided directly. The other methods will always be available
+ * indirectly, using an implementation that relies on one of the other methods.
+ *
+ * @return the implementation method, for example {@link #ITERATE_METHOD} or {@link #EVALUATE_METHOD} or
+ * {@link #PROCESS_METHOD}
+ */
+
+ public int getImplementationMethod() {
+ if (Cardinality.allowsMany(getCardinality())) {
+ return ITERATE_METHOD;
+ } else {
+ return EVALUATE_METHOD;
+ }
+ }
+
+ /**
+ * Determine whether this expression implements its own method for static type checking
+ *
+ * @return true if this expression has a non-trivial implementation of the staticTypeCheck()
+ * method
+ */
+
+ public boolean implementsStaticTypeCheck() {
+ return false;
+ }
+
+ public boolean hasVariableBinding(Binding binding) {
+ return false;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression
+ * rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Perform type checking of an expression and its subexpressions. This is the second phase of
+ * static optimization.
+ * <p/>
+ * <p>This checks statically that the operands of the expression have
+ * the correct type; if necessary it generates code to do run-time type checking or type
+ * conversion. A static type error is reported only if execution cannot possibly succeed, that
+ * is, if a run-time type error is inevitable. The call may return a modified form of the expression.</p>
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable. However, the types of such functions and
+ * variables may not be accurately known if they have not been explicitly declared.</p>
+ * <p/>
+ * <p>If the implementation returns a value other than "this", then it is required to ensure that
+ * the location information in the returned expression have been set up correctly.
+ * It should not rely on the caller to do this, although for historical reasons many callers do so.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten to perform necessary run-time type checks,
+ * and to perform other type-related optimizations
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor,
+ /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType)
+ throws XPathException {
+ return this;
+ }
+
+ /**
+ * Static type checking of some expressions is delegated to the expression itself, by calling
+ * this method. The default implementation of the method throws UnsupportedOperationException.
+ * If there is a non-default implementation, then implementsStaticTypeCheck() will return true
+ *
+ * @param req the required type
+ * @param backwardsCompatible true if backwards compatibility mode applies
+ * @param role the role of the expression in relation to the required type
+ * @param visitor an expression visitor
+ * @return the expression after type checking (perhaps augmented with dynamic type checking code)
+ * @throws XPathException if failures occur, for example if the static type of one branch of the conditional
+ * is incompatible with the required type
+ */
+
+ public Expression staticTypeCheck(SequenceType req,
+ boolean backwardsCompatible,
+ RoleLocator role, TypeCheckerEnvironment visitor)
+ throws XPathException {
+ throw new UnsupportedOperationException("staticTypeCheck");
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions. This is the third and final
+ * phase of static optimization.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor,
+ /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Offer promotion for this subexpression. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>This method must be overridden for any Expression that has subexpressions.</p>
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent the containing expression in the expression tree
+ * @return if the offer is not accepted, return this expression unchanged.
+ * Otherwise return the result of rewriting the expression to promote
+ * this subexpression
+ * @throws XPathException if any error is detected
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ // The following temporary code checks that this method is implemented for all expressions
+ // that have subexpressions. (Another way to do this is to see what errors arise when the
+ // method is declared abstract).
+
+// if (iterateSubExpressions().hasNext()) {
+// throw new UnsupportedOperationException("promote is not implemented for " + getClass());
+// }
+ return this;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+
+ public final int getSpecialProperties() {
+ if (staticProperties == -1) {
+ computeStaticProperties();
+ }
+ return staticProperties & StaticProperty.SPECIAL_PROPERTY_MASK;
+ }
+
+ /**
+ * Determine the static cardinality of the expression. This establishes how many items
+ * there will be in the result of the expression, at compile time (i.e., without
+ * actually evaluating the result.
+ *
+ * @return one of the values Cardinality.ONE_OR_MORE,
+ * Cardinality.ZERO_OR_MORE, Cardinality.EXACTLY_ONE,
+ * Cardinality.ZERO_OR_ONE, Cardinality.EMPTY. This default
+ * implementation returns ZERO_OR_MORE (which effectively gives no
+ * information).
+ */
+
+ public int getCardinality() {
+ if (staticProperties == -1) {
+ computeStaticProperties();
+ }
+ return staticProperties & StaticProperty.CARDINALITY_MASK;
+ }
+
+ /**
+ * Determine the data type of the expression, if possible. All expression return
+ * sequences, in general; this method determines the type of the items within the
+ * sequence, assuming that (a) this is known in advance, and (b) it is the same for
+ * all items in the sequence.
+ * <p/>
+ * <p>This method should always return a result, though it may be the best approximation
+ * that is available at the time.</p>
+ *
+ * @param th the type hierarchy cache
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER,
+ * Type.NODE, or Type.ITEM (meaning not known at compile time)
+ */
+
+ /*@NotNull*/
+ public abstract ItemType getItemType(TypeHierarchy th);
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
+ * XPathContext.CURRENT_NODE. The default implementation combines the intrinsic
+ * dependencies of this expression with the dependencies of the subexpressions,
+ * computed recursively. This is overridden for expressions such as FilterExpression
+ * where a subexpression's dependencies are not necessarily inherited by the parent
+ * expression.
+ *
+ * @return a set of bit-significant flags identifying the dependencies of
+ * the expression
+ */
+
+ public int getDependencies() {
+ // Implemented as a memo function: we only compute the dependencies
+ // for each expression once
+ if (staticProperties == -1) {
+ computeStaticProperties();
+ }
+ return staticProperties & StaticProperty.DEPENDENCY_MASK;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+
+ /*@Nullable*/
+ public IntegerValue[] getIntegerBounds() {
+ return null;
+ }
+
+ public static final IntegerValue UNBOUNDED_LOWER = (IntegerValue) IntegerValue.makeIntegerValue(new DoubleValue(-1e100));
+ public static final IntegerValue UNBOUNDED_UPPER = (IntegerValue) IntegerValue.makeIntegerValue(new DoubleValue(+1e100));
+ public static final IntegerValue MAX_STRING_LENGTH = Int64Value.makeIntegerValue(Integer.MAX_VALUE);
+ public static final IntegerValue MAX_SEQUENCE_LENGTH = Int64Value.makeIntegerValue(Integer.MAX_VALUE);
+
+//#ifdefined STREAM
+
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return ExpressionInverter.getStreamability(this, syntacticContext, allowExtensions, reasons);
+ }
+//#endif
+
+ /**
+ * Get the immediate sub-expressions of this expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ List<Expression> list = Collections.emptyList();
+ return list.iterator();
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * works off the results of iterateSubExpressions()
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ // default implementation
+ List<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>();
+ for (Iterator<Expression> kids = iterateSubExpressions(); kids.hasNext(); ) {
+ list.add(new SubExpressionInfo(kids.next(), true, false, NAVIGATION_CONTEXT));
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Mark an expression as being "flattened". This is a collective term that includes extracting the
+ * string value or typed value, or operations such as simple value construction that concatenate text
+ * nodes before atomizing. The implication of all of these is that although the expression might
+ * return nodes, the identity of the nodes has no significance. This is called during type checking
+ * of the parent expression.
+ *
+ * @param flattened set to true if the result of the expression is atomized or otherwise turned into
+ * an atomic value
+ */
+
+ public void setFlattened(boolean flattened) {
+ // no action in general
+ }
+
+ /**
+ * Mark an expression as filtered: that is, it appears as the base expression in a filter expression.
+ * This notification currently has no effect except when the expression is a variable reference.
+ *
+ * @param filtered if true, marks this expression as the base of a filter expression
+ */
+
+ public void setFiltered(boolean filtered) {
+ // default: do nothing
+ }
+
+ /**
+ * Evaluate an expression as a single item. This always returns either a single Item or
+ * null (denoting the empty sequence). No conversion is done. This method should not be
+ * used unless the static type of the expression is a subtype of "item" or "item?": that is,
+ * it should not be called if the expression may return a sequence. There is no guarantee that
+ * this condition will be detected.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the node or atomic value that results from evaluating the
+ * expression; or null to indicate that the result is an empty
+ * sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@Nullable*/
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ return iterate(context).next();
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ Item value = evaluateItem(context);
+ return SingletonIterator.makeIterator(value);
+ }
+
+ /**
+ * Deliver the result of the expression as a sequence of events.
+ * <p/>
+ * <p>The events (of class {@link net.sf.saxon.evpull.PullEvent}) are either complete
+ * items, or one of startElement, endElement, startDocument, or endDocument, known
+ * as semi-nodes. The stream of events may also include a nested EventIterator.
+ * If a start-end pair exists in the sequence, then the events between
+ * this pair represent the content of the document or element. The content sequence will
+ * have been processed to the extent that any attribute and namespace nodes in the
+ * content sequence will have been merged into the startElement event. Namespace fixup
+ * will have been performed: that is, unique prefixes will have been allocated to element
+ * and attribute nodes, and all namespaces will be declared by means of a namespace node
+ * in the startElement event or in an outer startElement forming part of the sequence.
+ * However, duplicate namespaces may appear in the sequence.</p>
+ * <p>The content of an element or document may include adjacent or zero-length text nodes,
+ * atomic values, and nodes represented as nodes rather than broken down into events.</p>
+ *
+ * @param context The dynamic evaluation context
+ * @return the result of the expression as an iterator over a sequence of PullEvent objects
+ * @throws XPathException if a dynamic error occurs during expression evaluation
+ */
+
+ /*@Nullable*/
+ public EventIterator iterateEvents(XPathContext context) throws XPathException {
+ int m = getImplementationMethod();
+ if ((m & EVALUATE_METHOD) != 0) {
+ Item item = evaluateItem(context);
+ if (item == null) {
+ return EmptyEventIterator.getInstance();
+ } else {
+ return new SingletonEventIterator(item);
+ }
+ } else {
+ return new EventIteratorOverSequence(iterate(context));
+ }
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ if (Cardinality.allowsMany(getCardinality())) {
+ return ExpressionTool.effectiveBooleanValue(iterate(context));
+ } else {
+ return ExpressionTool.effectiveBooleanValue(evaluateItem(context));
+ }
+ }
+
+ /**
+ * Evaluate an expression as a String. This function must only be called in contexts
+ * where it is known that the expression will return a single string (or where an empty sequence
+ * is to be treated as a zero-length string). Implementations should not attempt to convert
+ * the result to a string, other than converting () to "". This method is used mainly to
+ * evaluate expressions produced by compiling an attribute value template.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the value of the expression, evaluated in the current context.
+ * The expression must return a string or (); if the value of the
+ * expression is (), this method returns "".
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ * @throws ClassCastException if the result type of the
+ * expression is not xs:string?
+ */
+
+ public CharSequence evaluateAsString(XPathContext context) throws XPathException {
+ Item o = evaluateItem(context);
+// if (o instanceof AtomicValue && !((AtomicValue)o).hasBuiltInType()) {
+// o = ((AtomicValue) o).getPrimitiveValue();
+// }
+ StringValue value = (StringValue) o; // the ClassCastException is deliberate
+ if (value == null) return "";
+ return value.getStringValueCS();
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ int m = getImplementationMethod();
+
+ if ((m & EVALUATE_METHOD) != 0) {
+ Item item = evaluateItem(context);
+ if (item != null) {
+ context.getReceiver().append(item, locationId, NodeInfo.ALL_NAMESPACES);
+ }
+
+ } else if ((m & ITERATE_METHOD) != 0) {
+
+ SequenceIterator iter = iterate(context);
+ SequenceReceiver out = context.getReceiver();
+ try {
+ Item it;
+ while ((it = iter.next()) != null) {
+ out.append(it, locationId, NodeInfo.ALL_NAMESPACES);
+ }
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+
+ } else {
+ throw new AssertionError("process() is not implemented in the subclass " + getClass());
+ }
+ }
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ * @throws net.sf.saxon.trans.XPathException
+ * if evaluation fails
+ * @throws UnsupportedOperationException if the expression is not an updating expression
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ throw new UnsupportedOperationException("Expression " + getClass() + " is not an updating expression");
+ }
+
+ /**
+ * <p>The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form.</p>
+ *
+ * <p>For subclasses of Expression that represent XPath expressions, the result should always be a string that
+ * parses as an XPath 3.0 expression. The expression produced should be equivalent to the original making certain
+ * assumptions about the static context. In general the expansion will make no assumptions about namespace bindings,
+ * except that (a) the prefix "xs" is used to refer to the XML Schema namespace, and (b) the default funcion namespace
+ * is assumed to be the "fn" namespace.</p>
+ *
+ * <p>In the case of XSLT instructions and XQuery expressions, the toString() method gives an abstracted view of the syntax
+ * that is not designed in general to be parseable.</p>
+ *
+ * @return a representation of the expression as a string
+ */
+
+ public String toString() {
+ // fallback implementation
+ FastStringBuffer buff = new FastStringBuffer(FastStringBuffer.SMALL);
+ String className = getClass().getName();
+ while (true) {
+ int dot = className.indexOf('.');
+ if (dot >= 0) {
+ className = className.substring(dot + 1);
+ } else {
+ break;
+ }
+ }
+ buff.append(className);
+ Iterator iter = iterateSubExpressions();
+ boolean first = true;
+ while (iter.hasNext()) {
+ buff.append(first ? "(" : ", ");
+ buff.append(iter.next().toString());
+ first = false;
+ }
+ if (!first) {
+ buff.append(")");
+ }
+ return buff.toString();
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+
+ public abstract void explain(ExpressionPresenter out);
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied outputstream.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+
+ public final void explain(OutputStream out) {
+ Executable exec = getExecutable();
+ if (exec != null) {
+ ExpressionPresenter ep = new ExpressionPresenter(exec.getConfiguration(), out);
+ explain(ep);
+ ep.close();
+ }
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ *
+ * @param parentType the "given complex type": the method is checking that the nodes returned by this
+ * expression are acceptable members of the content model of this type
+ * @param env the static context
+ * @param whole if true, we want to check that the value of this expression satisfies the content model
+ * as a whole; if false we want to check that the value of the expression is acceptable as one part
+ * of the content
+ * @throws XPathException if the value delivered by this expression cannot be part of the content model
+ * of the given type
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ //
+ }
+
+ /**
+ * Mark an expression as being in a given Container. This link is used primarily for diagnostics:
+ * the container links to the location map held in the executable.
+ * <p/>
+ * <p>This affects the expression and all its subexpressions. Any subexpressions that are not in the
+ * same container are marked with the new container, and this proceeds recursively. However, any
+ * subexpression that is already in the correct container is not modified.</p>
+ *
+ * @param container The container of this expression.
+ */
+
+ public void setContainer(/*@Nullable*/ Container container) {
+ this.container = container;
+ if (container != null) {
+ Iterator children = iterateSubExpressions();
+ while (children.hasNext()) {
+ Expression child = (Expression) children.next();
+ // child can be null while expressions are under construction
+ if (child != null && child.getContainer() != container &&
+ (child.container == null || child.container.getContainerGranularity() < container.getContainerGranularity())) {
+ child.setContainer(container);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the container in which this expression is located. This will usually be a top-level construct
+ * such as a function or global variable, and XSLT template, or an XQueryExpression. In the case of
+ * free-standing XPath expressions it will be the StaticContext object
+ *
+ * @return the expression's container
+ */
+
+ /*@Nullable*/
+ public Container getContainer() {
+ return container;
+ }
+
+ /**
+ * Set up a parent-child relationship between this expression and a given child expression.
+ * <p/>
+ * Note: many calls on this method are now redundant, but are kept in place for "belt-and-braces"
+ * reasons. The rule is that an implementation of simplify(), typeCheck(), or optimize() that returns
+ * a value other than "this" is required to set the location information and parent pointer in the new
+ * child expression. However, in the past this was often left to the caller, which did it by calling
+ * this method, either unconditionally on return from one of these methods, or after testing that the
+ * returned object was not the same as the original.
+ *
+ * @param child the child expression
+ */
+
+ public void adoptChildExpression(/*@Nullable*/ Expression child) {
+ if (child == null) {
+ return;
+ }
+
+ if (container == null) {
+ container = child.container;
+ } else {
+ child.setContainer(container);
+ }
+
+ if (locationId == -1) {
+ ExpressionTool.copyLocationInfo(child, this);
+ } else if (child.locationId == -1) {
+ ExpressionTool.copyLocationInfo(this, child);
+ }
+ resetLocalStaticProperties();
+ }
+
+ /**
+ * Set the location ID on an expression.
+ *
+ * @param id the location id
+ */
+
+ public void setLocationId(int id) {
+ locationId = id;
+ }
+
+ /**
+ * Get the location ID of the expression
+ *
+ * @return a location identifier, which can be turned into real
+ * location information by reference to a location provider
+ */
+
+ public final int getLocationId() {
+ return locationId;
+ }
+
+ /**
+ * Get the line number of the expression
+ */
+
+ public int getLineNumber() {
+ if (locationId == -1) {
+ return -1;
+ }
+ return locationId & 0xfffff;
+ }
+
+ /**
+ * Get the column number of the expression
+ */
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Get the systemId of the module containing the expression
+ */
+
+ /*@Nullable*/
+ public String getSystemId() {
+ if (locationId == -1) {
+ return null;
+ }
+ Executable exec = getExecutable();
+ if (exec == null) {
+ return null;
+ }
+ LocationMap map = exec.getLocationMap();
+ if (map == null) {
+ return null;
+ }
+ return map.getSystemId(locationId);
+ }
+
+ /**
+ * Get the publicId of the module containing the expression (to satisfy the SourceLocator interface)
+ */
+
+ /*@Nullable*/
+ public final String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Get the executable containing this expression
+ *
+ * @return the containing Executable
+ */
+
+ /*@Nullable*/
+ public Executable getExecutable() {
+ Container container = getContainer();
+ return container == null ? null : container.getExecutable();
+ }
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ *
+ * @return the LocationProvider used to turn the location id into real location information
+ */
+
+ /*@Nullable*/
+ public LocationProvider getLocationProvider() {
+ Container container = getContainer();
+ return container == null ? null : container.getLocationProvider();
+ }
+
+ /**
+ * Promote a subexpression if possible, and if the expression was changed, carry out housekeeping
+ * to reset the static properties and correct the parent pointers in the tree
+ *
+ * @param subexpression the subexpression that is a candidate for promotion
+ * @param offer details of the promotion being considered @return the result of the promotion. This will be the current expression if no promotion
+ * actions have taken place
+ * @return the expression that results from doing the promotion as requested. Returns null if and only
+ * if the supplied subexpression is null.
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error occurs
+ */
+
+ public final Expression doPromotion(Expression subexpression, PromotionOffer offer)
+ throws XPathException {
+ if (subexpression == null) {
+ return null;
+ }
+ Expression e = subexpression.promote(offer, this);
+ if (e != subexpression) {
+ adoptChildExpression(e);
+ } else if (offer.accepted) {
+ resetLocalStaticProperties();
+ }
+ return e;
+ }
+
+ /**
+ * Compute the static properties. This should only be done once for each
+ * expression.
+ */
+
+ public final void computeStaticProperties() {
+ staticProperties =
+ computeDependencies() |
+ computeCardinality() |
+ computeSpecialProperties();
+ }
+
+ /**
+ * Reset the static properties of the expression to -1, so that they have to be recomputed
+ * next time they are used.
+ */
+
+ public void resetLocalStaticProperties() {
+ staticProperties = -1;
+ }
+
+ /**
+ * Compute the static cardinality of this expression
+ *
+ * @return the computed cardinality, as one of the values {@link StaticProperty#ALLOWS_ZERO_OR_ONE},
+ * {@link StaticProperty#EXACTLY_ONE}, {@link StaticProperty#ALLOWS_ONE_OR_MORE},
+ * {@link StaticProperty#ALLOWS_ZERO_OR_MORE}
+ */
+
+ protected abstract int computeCardinality();
+
+ /**
+ * Compute the special properties of this expression. These properties are denoted by a bit-significant
+ * integer, possible values are in class {@link StaticProperty}. The "special" properties are properties
+ * other than cardinality and dependencies, and most of them relate to properties of node sequences, for
+ * example whether the nodes are in document order.
+ *
+ * @return the special properties, as a bit-significant integer
+ */
+
+ protected int computeSpecialProperties() {
+ return 0;
+ }
+
+ /**
+ * Compute the dependencies of an expression, as the union of the
+ * dependencies of its subexpressions. (This is overridden for path expressions
+ * and filter expressions, where the dependencies of a subexpression are not all
+ * propogated). This method should be called only once, to compute the dependencies;
+ * after that, getDependencies should be used.
+ *
+ * @return the depencies, as a bit-mask
+ */
+
+ public int computeDependencies() {
+ int dependencies = getIntrinsicDependencies();
+ for (Iterator children = iterateSubExpressions(); children.hasNext(); ) {
+ Expression child = (Expression) children.next();
+ dependencies |= child.getDependencies();
+ }
+ return dependencies;
+ }
+
+ /**
+ * Determine the intrinsic dependencies of an expression, that is, those which are not derived
+ * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
+ * on the context position, while (position()+1) does not. The default implementation
+ * of the method returns 0, indicating "no dependencies".
+ *
+ * @return a set of bit-significant flags identifying the "intrinsic"
+ * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
+ */
+
+ public int getIntrinsicDependencies() {
+ return 0;
+ }
+
+ /**
+ * Check to ensure that this expression does not contain any inappropriate updating subexpressions.
+ * This check is overridden for those expressions that permit updating subexpressions.
+ *
+ * @throws XPathException if the expression has a non-permitted updating subexpression
+ */
+
+ public void checkForUpdatingSubexpressions() throws XPathException {
+ for (Iterator iter = iterateSubExpressions(); iter.hasNext(); ) {
+ Expression sub = (Expression) iter.next();
+ sub.checkForUpdatingSubexpressions();
+ if (sub.isUpdatingExpression()) {
+ XPathException err = new XPathException(
+ "Updating expression appears in a context where it is not permitted", "XUST0001");
+ err.setLocator(sub);
+ throw err;
+ }
+ }
+ }
+
+ /**
+ * Determine whether this is an updating expression as defined in the XQuery update specification
+ *
+ * @return true if this is an updating expression
+ */
+
+ public boolean isUpdatingExpression() {
+ for (Iterator iter = iterateSubExpressions(); iter.hasNext(); ) {
+ if (((Expression) iter.next()).isUpdatingExpression()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether this is a vacuous expression as defined in the XQuery update specification
+ *
+ * @return true if this expression is vacuous
+ */
+
+ public boolean isVacuousExpression() {
+ return false;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public abstract Expression copy();
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ // overridden in subclasses
+ throw new IllegalArgumentException("Invalid replacement");
+ }
+
+ /**
+ * Suppress validation on contained element constructors, on the grounds that the parent element
+ * is already performing validation. The default implementation does nothing.
+ *
+ * @param parentValidationMode the kind of validation being performed on the parent expression
+ */
+
+ public void suppressValidation(int parentValidationMode) {
+ // do nothing
+ }
+
+ /**
+ * Mark tail-recursive calls on stylesheet functions. For most expressions, this does nothing.
+ *
+ * @param qName the name of the function
+ * @param arity the arity (number of parameters) of the function
+ * @return {@link UserFunctionCall#NOT_TAIL_CALL} if no tail call was found;
+ * {@link UserFunctionCall#FOREIGN_TAIL_CALL} if a tail call on a different function was found;
+ * @link UserFunctionCall#SELF_TAIL_CALL} if a tail recursive call was found and if this call accounts for the whole of the value.
+ */
+
+ public int markTailFunctionCalls(StructuredQName qName, int arity) {
+ return UserFunctionCall.NOT_TAIL_CALL;
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws XPathException if conversion is not possible
+ */
+
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ ItemType type = getItemType(config.getTypeHierarchy());
+ if (((getDependencies() & StaticProperty.DEPENDS_ON_NON_DOCUMENT_FOCUS) == 0) &&
+ (type instanceof NodeTest || this instanceof VariableReference)) {
+ return new NodeSetPattern(this);
+ }
+ throw new XPathException("Cannot convert the expression {" + toString() + "} to a pattern", "XTSE0340");
+ }
+
+//#ifdefined STREAM
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ ItemType type = getItemType(config.getTypeHierarchy());
+ if (((getDependencies() & StaticProperty.DEPENDS_ON_NON_DOCUMENT_FOCUS) == 0) &&
+ (type instanceof NodeTest || this instanceof VariableReference)) {
+ return new NodeSetPattern(this);
+ }
+ reasonForFailure.add("Cannot convert the expression {" + toString() + "} to a streaming pattern");
+ return null;
+ }
+
+//#endif
+
+ /**
+ * Get the local variables (identified by their slot numbers) on which this expression depends.
+ * Should only be called if the caller has established that there is a dependency on local variables.
+ *
+ * @return an array of integers giving the slot numbers of the local variables referenced in this
+ * expression.
+ */
+
+ public final synchronized int[] getSlotsUsed() {
+ // synchronized because it's calculated lazily at run-time the first time it's needed
+ if (slotsUsed != null) {
+ return slotsUsed;
+ }
+ IntHashSet slots = new IntHashSet(10);
+ gatherSlotsUsed(this, slots);
+ slotsUsed = new int[slots.size()];
+ int i = 0;
+ IntIterator iter = slots.iterator();
+ while (iter.hasNext()) {
+ slotsUsed[i++] = iter.next();
+ }
+ Arrays.sort(slotsUsed);
+ return slotsUsed;
+ }
+
+ private static void gatherSlotsUsed(Expression exp, IntHashSet slots) {
+ if (exp instanceof VariableReference) {
+ Binding binding = ((VariableReference) exp).getBinding();
+ if (binding == null) {
+ throw new NullPointerException("Unbound variable at line " + exp.getLineNumber());
+ }
+ if (!binding.isGlobal()) {
+ int slot = binding.getLocalSlotNumber();
+ if (slot != -1) {
+ if (!slots.contains(slot)) {
+ slots.add(slot);
+ }
+ }
+ }
+ } else if (exp instanceof SuppliedParameterReference) {
+ int slot = ((SuppliedParameterReference) exp).getSlotNumber();
+ slots.add(slot);
+ } else {
+ Iterator iter = exp.iterateSubExpressions();
+ while (iter.hasNext()) {
+ Expression sub = (Expression) iter.next();
+ gatherSlotsUsed(sub, slots);
+ }
+ }
+ }
+
+ /**
+ * Method used in subclasses to signal a dynamic error
+ *
+ * @param message the error message
+ * @param code the error code
+ * @param context the XPath dynamic context
+ * @throws XPathException always thrown, to signal a dynamic error
+ */
+
+ protected void dynamicError(String message, String code, XPathContext context) throws XPathException {
+ XPathException err = new XPathException(message, this);
+ err.setXPathContext(context);
+ err.setErrorCode(code);
+ throw err;
+ }
+
+ /**
+ * Method used in subclasses to signal a runtime type error
+ *
+ * @param message the error message
+ * @param errorCode the error code
+ * @param context the XPath dynamic context
+ * @throws XPathException always thrown, to signal a dynamic error
+ */
+
+ protected void typeError(String message, String errorCode, XPathContext context) throws XPathException {
+ XPathException e = new XPathException(message, this);
+ e.setIsTypeError(true);
+ e.setErrorCode(errorCode);
+ e.setXPathContext(context);
+ throw e;
+ }
+
+ /**
+ * Get the type of this expression for use in tracing and diagnostics
+ *
+ * @return the type of expression, as enumerated in class {@link net.sf.saxon.trace.Location}
+ */
+
+ public int getConstructType() {
+ return Location.XPATH_EXPRESSION;
+ }
+
+ /*@Nullable*/
+ public StructuredQName getObjectName() {
+ return null;
+ }
+
+ /*@Nullable*/
+ public Object getProperty(String name) {
+ if (name.equals("expression")) {
+ return this;
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Get the line number within the document or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the line number within the document or module.
+ */
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+ /**
+ * Get the URI of the document or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the URI of the document or module.
+ */
+
+ /*@Nullable*/
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ /**
+ * Get an iterator over all the properties available. The values returned by the iterator
+ * will be of type String, and each string can be supplied as input to the getProperty()
+ * method to retrieve the value of the property. The iterator may return properties whose
+ * value is null.
+ */
+
+ public Iterator<String> getProperties() {
+ return new MonoIterator<String>("expression");
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ *
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+
+ public int getHostLanguage() {
+ Container container = getContainer();
+ if (container == null) {
+ return Configuration.XPATH;
+ } else {
+ return container.getHostLanguage();
+ }
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ /*@Nullable*/
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, /*@Nullable*/ PathMap.PathMapNodeSet pathMapNodeSet) {
+ boolean dependsOnFocus = ExpressionTool.dependsOnFocus(this);
+ Executable exec = getExecutable();
+ if (exec == null) {
+ throw new NullPointerException();
+ }
+ TypeHierarchy th = exec.getConfiguration().getTypeHierarchy();
+ PathMap.PathMapNodeSet attachmentPoint;
+ if (pathMapNodeSet == null) {
+ if (dependsOnFocus) {
+ ContextItemExpression cie = new ContextItemExpression();
+ cie.setContainer(getContainer());
+ pathMapNodeSet = new PathMap.PathMapNodeSet(pathMap.makeNewRoot(cie));
+ }
+ attachmentPoint = pathMapNodeSet;
+ } else {
+ attachmentPoint = (dependsOnFocus ? pathMapNodeSet : null);
+ }
+ PathMap.PathMapNodeSet result = new PathMap.PathMapNodeSet();
+ for (Iterator iter = iterateSubExpressions(); iter.hasNext(); ) {
+ Expression child = (Expression) iter.next();
+ result.addNodeSet(child.addToPathMap(pathMap, attachmentPoint));
+ }
+ if (getItemType(th) instanceof AtomicType) {
+ // if expression returns an atomic value then any nodes accessed don't contribute to the result
+ return null;
+ } else {
+ return result;
+ }
+ }
+
+ /**
+ * Determine whether the expression can be evaluated without reference to the part of the context
+ * document outside the subtree rooted at the context node.
+ *
+ * @return true if the expression has no dependencies on the context node, or if the only dependencies
+ * on the context node are downward selections using the self, child, descendant, attribute, and namespace
+ * axes.
+ */
+
+ public boolean isSubtreeExpression() {
+ if (ExpressionTool.dependsOnFocus(this)) {
+ if ((getIntrinsicDependencies() & StaticProperty.DEPENDS_ON_FOCUS) != 0) {
+ return false;
+ } else {
+ Iterator sub = iterateSubExpressions();
+ while (sub.hasNext()) {
+ Expression s = (Expression) sub.next();
+ if (!s.isSubtreeExpression()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ public void setEvaluationMethod(int method) {
+ this.evaluationMethod = method;
+ }
+
+ public int getEvaluationMethod() {
+ return evaluationMethod;
+ }
+
+//#ifdefined BYTECODE
+
+ /**
+ * Return the compiler relating to a particular expression
+ *
+ * @return the relevant ExpressionCompiler
+ * @throws CannotCompileException if it is not possible to generate bytecode for this expression
+ */
+ public ExpressionCompiler getExpressionCompiler() throws CannotCompileException {
+ if (this instanceof Callable) {
+ if (this instanceof IntegratedFunctionCall) {
+ IntegratedFunctionCall fc = (IntegratedFunctionCall) this;
+ if (fc.getFunction().getDefinition() instanceof IsWholeNumber) {
+ return new IsWholeNumberCompiler();
+ }
+ }
+ return new CallableExpressionCompiler();
+ }
+ throw new CannotCompileException(this);
+ }
+
+//#endif
+
+//#ifdefined STREAM
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+
+ public StreamingAdjunct getStreamingAdjunct() {
+ return null;
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/FilterExpression.java b/sf/saxon/expr/FilterExpression.java
new file mode 100644
index 0000000..dcc4f86
--- /dev/null
+++ b/sf/saxon/expr/FilterExpression.java
@@ -0,0 +1,1366 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.FilterExpressionCompiler;
+import com.saxonica.stream.adjunct.FilterExpressionAdjunct;
+import net.sf.saxon.pattern.GeneralPositionalPattern;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.instruct.Choose;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.functions.*;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.List;
+
+
+
+/**
+ * A FilterExpression contains a base expression and a filter predicate, which may be an
+ * integer expression (positional filter), or a boolean expression (qualifier)
+ */
+
+public final class FilterExpression extends Expression implements ContextSwitchingExpression {
+
+ /*@NotNull*/
+ private Expression start;
+ /*@NotNull*/
+ private Expression filter;
+ private boolean filterIsPositional; // true if the value of the filter might depend on
+ // the context position
+ private boolean filterIsSingletonBoolean; // true if the filter expression always returns a single boolean
+ private boolean filterIsIndependentNumeric; // true if the filter expression returns a number that doesn't
+ // depend on the context item or position. (It may depend on last()).
+ public static final int FILTERED = 10000;
+
+ //private int isIndexable = 0;
+
+ /**
+ * Constructor
+ *
+ * @param start A node-set expression denoting the absolute or relative set of nodes from which the
+ * navigation path should start.
+ * @param filter An expression defining the filter predicate
+ */
+
+ public FilterExpression(/*@NotNull*/ Expression start, /*@NotNull*/ Expression filter) {
+ this.start = start;
+ this.filter = filter;
+ adoptChildExpression(start);
+ adoptChildExpression(filter);
+ start.setFiltered(true);
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "filter";
+ }
+
+ /**
+ * Get the data type of the items returned
+ *
+ * @param th the type hierarchy cache
+ * @return an integer representing the data type
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ // special case the filter [. instance of x]
+ if (filter instanceof InstanceOfExpression &&
+ ((InstanceOfExpression)filter).getBaseExpression() instanceof ContextItemExpression) {
+ return ((InstanceOfExpression)filter).getRequiredItemType();
+ }
+ return start.getItemType(th);
+ }
+
+ /**
+ * Get the underlying expression
+ *
+ * @return the expression being filtered
+ */
+
+ public Expression getControllingExpression() {
+ return start;
+ }
+
+ /**
+ * @see com.saxonica.bytecode.FilterExpressionCompiler
+ * @return the boolean of filter is positional
+ * */
+ public boolean isFilterIsPositional(){
+ return filterIsPositional;
+ }
+
+ /**
+ * Get the subexpression that is evaluated in the new context
+ * @return the subexpression evaluated in the context set by the controlling expression
+ */
+
+ public Expression getControlledExpression() {
+ return filter;
+ }
+
+ /**
+ * Get the filter expression
+ *
+ * @return the expression acting as the filter predicate
+ */
+
+ public Expression getFilter() {
+ return filter;
+ }
+
+
+ /**
+ * Determine if the filter is positional
+ *
+ * @param th the Type Hierarchy (for cached access to type information)
+ * @return true if the value of the filter depends on the position of the item against
+ * which it is evaluated
+ */
+
+ public boolean isPositional(TypeHierarchy th) {
+ return isPositionalFilter(filter, th);
+ }
+
+ /**
+ * Test if the filter always returns a singleton boolean
+ *
+ * @return true if the filter is a simple boolean expression
+ */
+
+ public boolean isSimpleBooleanFilter() {
+ return filterIsSingletonBoolean;
+ }
+
+ /**
+ * Determine whether the filter is a simple independent numeric, that is, an expression
+ * that satisfies the following conditions: (a) its value is numeric;
+ * (b) the value does not depend on the context item or position;
+ * (c) the cardinality is zero or one.
+ *
+ * @return true if the filter is a numeric value that does not depend on the context item or position
+ */
+
+ public boolean isIndependentNumericFilter() {
+ return filterIsIndependentNumeric;
+ }
+
+ /**
+ * Simplify an expression
+ *
+ * @param visitor the expression visitor
+ * @return the simplified expression
+ * @throws XPathException if any failure occurs
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+
+ start = visitor.simplify(start);
+ filter = visitor.simplify(filter);
+
+ // ignore the filter if the base expression is an empty sequence
+ if (Literal.isEmptySequence(start)) {
+ return start;
+ }
+
+ // check whether the filter is a constant true() or false()
+ if (filter instanceof Literal && !(((Literal)filter).getValue() instanceof NumericValue)) {
+ try {
+ if (filter.effectiveBooleanValue(visitor.getStaticContext().makeEarlyEvaluationContext())) {
+ return start;
+ } else {
+ return Literal.makeEmptySequence();
+ }
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ throw e;
+ }
+ }
+
+ // check whether the filter is [last()] (note, position()=last() is handled elsewhere)
+
+ if (filter instanceof Last) {
+ filter = new IsLastExpression(true);
+ adoptChildExpression(filter);
+ }
+
+ return this;
+
+ }
+
+ /**
+ * Type-check the expression
+ *
+ * @param visitor the expression visitor
+ * @param contextItemType the type of the context item for this expression
+ * @return the expression after type-checking (potentially modified to add run-time
+ * checks and/or conversions)
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Expression start2 = visitor.typeCheck(start, contextItemType);
+ if (start2 != start) {
+ start = start2;
+ adoptChildExpression(start2);
+ }
+ start.setFiltered(true);
+ if (Literal.isEmptySequence(start)) {
+ return start;
+ }
+
+ Expression filter2 = visitor.typeCheck(filter, new ExpressionVisitor.ContextItemType(start.getItemType(th), false));
+ if (filter2 != filter) {
+ filter = filter2;
+ adoptChildExpression(filter2);
+ }
+
+ // The filter expression usually doesn't need to be sorted
+
+ filter2 = ExpressionTool.unsortedIfHomogeneous(visitor.getConfiguration().obtainOptimizer(), filter);
+ if (filter2 != filter) {
+ filter = filter2;
+ adoptChildExpression(filter2);
+ }
+
+ // detect head expressions (E[1]) and treat them specially
+
+ if (Literal.isConstantOne(filter)) {
+ Expression fie = FirstItemExpression.makeFirstItemExpression(start);
+ ExpressionTool.copyLocationInfo(this, fie);
+ return fie;
+ }
+
+ // determine whether the filter might depend on position
+ filterIsPositional = isPositionalFilter(filter, th);
+
+ // determine whether the filter always evaluates to a single boolean
+ filterIsSingletonBoolean =
+ filter.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ filter.getItemType(th).equals(BuiltInAtomicType.BOOLEAN);
+
+ // determine whether the filter evaluates to a single number, where the number will be the same for
+ // all values in the sequence (it may depend on last())
+ filterIsIndependentNumeric =
+ th.isSubType(filter.getItemType(th), BuiltInAtomicType.NUMERIC) &&
+ (filter.getDependencies() &
+ (StaticProperty.DEPENDS_ON_CONTEXT_ITEM | StaticProperty.DEPENDS_ON_POSITION)) == 0 &&
+ !Cardinality.allowsMany(filter.getCardinality());
+ visitor.resetStaticProperties();
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final StaticContext env = visitor.getStaticContext();
+ final Configuration config = visitor.getConfiguration();
+ final Optimizer opt = config.obtainOptimizer();
+ final boolean debug = config.getBooleanProperty(FeatureKeys.TRACE_OPTIMIZER_DECISIONS);
+ final TypeHierarchy th = config.getTypeHierarchy();
+
+ Expression start2 = visitor.optimize(start, contextItemType);
+ if (start2 != start) {
+ start = start2;
+ adoptChildExpression(start2);
+ }
+ start.setFiltered(true);
+
+ Expression originalFilter;
+ try {
+ originalFilter = filter.copy();
+ } catch (UnsupportedOperationException err) {
+ originalFilter = null;
+ }
+ ExpressionVisitor.ContextItemType baseItemType = new ExpressionVisitor.ContextItemType(start.getItemType(th), false);
+ Expression filter2 = filter.optimize(visitor, baseItemType);
+ if (filter2 != filter) {
+ filter = filter2;
+ adoptChildExpression(filter2);
+ }
+
+ // The filter expression usually doesn't need to be sorted
+
+ filter2 = ExpressionTool.unsortedIfHomogeneous(opt, filter);
+ if (filter2 != filter) {
+ filter = filter2;
+ adoptChildExpression(filter2);
+ }
+
+ // if the result of evaluating the filter cannot include numeric values, then we can use
+ // its effective boolean value
+
+ ItemType filterType = filter.getItemType(th);
+ if (!th.isSubType(filterType, BuiltInAtomicType.BOOLEAN)
+ && th.relationship(filterType, BuiltInAtomicType.NUMERIC) == TypeHierarchy.DISJOINT) {
+ BooleanFn f = (BooleanFn) SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{filter});
+ filter = visitor.optimize(f, baseItemType);
+ }
+
+ // the filter expression may have been reduced to a constant boolean by previous optimizations
+ if (filter instanceof Literal && ((Literal)filter).getValue() instanceof BooleanValue) {
+ if (((BooleanValue)((Literal)filter).getValue()).getBooleanValue()) {
+ if (debug) {
+ opt.trace("Redundant filter removed", start);
+ }
+ return start;
+ } else {
+ if (debug) {
+ opt.trace("Filter expression eliminated because predicate is always false",
+ Literal.makeEmptySequence());
+ }
+ return Literal.makeEmptySequence();
+ }
+ }
+
+ // determine whether the filter might depend on position
+ filterIsPositional = isPositionalFilter(filter, th);
+ filterIsSingletonBoolean =
+ filter.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ filter.getItemType(th).equals(BuiltInAtomicType.BOOLEAN);
+
+ // determine whether the filter is indexable
+ if (!filterIsPositional && !visitor.isOptimizeForStreaming()) {
+ int isIndexable = opt.isIndexableFilter(filter);
+ if (isIndexable == 0 && filter != originalFilter && originalFilter != null) {
+ // perhaps the original filter was indexable; if so, revert to the original
+ // this happens when [@a = 1] is rewritten as [some $x in @a satisfies $x eq 1]
+ // TODO: this rollback mechanism is very unsatisfactory. Better: make the some expression indexable!
+ int origIndexable = opt.isIndexableFilter(originalFilter);
+ if (origIndexable != 0) {
+ isIndexable = origIndexable;
+ filter = originalFilter;
+ adoptChildExpression(originalFilter);
+ }
+ }
+ // If the filter is indexable consider creating a key, or an indexed filter expression
+ // (This happens in Saxon-EE only)
+ if (isIndexable != 0) {
+ boolean contextIsDoc = contextItemType != null && th.isSubType(contextItemType.itemType, NodeKindTest.DOCUMENT);
+ Expression f = opt.tryIndexedFilter(this, visitor, isIndexable > 0, contextIsDoc);
+ if (f != this) {
+ return f.typeCheck(visitor, contextItemType).optimize(visitor, contextItemType);
+ }
+ }
+ }
+
+ // if the filter is positional, try changing f[a and b] to f[a][b] to increase
+ // the chances of finishing early.
+
+ if (filterIsPositional &&
+ filter instanceof BooleanExpression &&
+ ((BooleanExpression)filter).operator == Token.AND) {
+ BooleanExpression bf = (BooleanExpression)filter;
+ if (isExplicitlyPositional(bf.operand0) &&
+ !isExplicitlyPositional(bf.operand1)) {
+ Expression p0 = forceToBoolean(bf.operand0, env.getConfiguration());
+ Expression p1 = forceToBoolean(bf.operand1, env.getConfiguration());
+ FilterExpression f1 = new FilterExpression(start, p0);
+ ExpressionTool.copyLocationInfo(this, f1);
+ FilterExpression f2 = new FilterExpression(f1, p1);
+ ExpressionTool.copyLocationInfo(this, f2);
+ if (debug) {
+ opt.trace("Composite filter replaced by nested filter expressions", f2);
+ }
+ return visitor.optimize(f2, contextItemType);
+ }
+ if (isExplicitlyPositional(bf.operand1) &&
+ !isExplicitlyPositional(bf.operand0)) {
+ Expression p0 = forceToBoolean(bf.operand0, env.getConfiguration());
+ Expression p1 = forceToBoolean(bf.operand1, env.getConfiguration());
+ FilterExpression f1 = new FilterExpression(start, p1);
+ ExpressionTool.copyLocationInfo(this, f1);
+ FilterExpression f2 = new FilterExpression(f1, p0);
+ ExpressionTool.copyLocationInfo(this, f2);
+ if (debug) {
+ opt.trace("Composite filter replaced by nested filter expressions", f2);
+ }
+ return visitor.optimize(f2, contextItemType);
+ }
+ }
+
+ if (filter instanceof IsLastExpression &&
+ ((IsLastExpression)filter).getCondition()) {
+
+ if (start instanceof Literal) {
+ filter = Literal.makeLiteral(new Int64Value(((Literal)start).getValue().getLength()));
+ } else {
+ return new LastItemExpression(start);
+ }
+ }
+
+ Expression subsequence = tryToRewritePositionalFilter(visitor);
+ if (subsequence != null) {
+ if (debug) {
+ opt.trace("Rewrote Filter Expression as:", subsequence);
+ }
+ ExpressionTool.copyLocationInfo(this, subsequence);
+ return subsequence.simplify(visitor)
+ .typeCheck(visitor, contextItemType)
+ .optimize(visitor, contextItemType);
+ }
+
+ // If any subexpressions within the filter are not dependent on the focus,
+ // promote them: this causes them to be evaluated once, outside the filter
+ // expression. Note: we do this even if the filter is numeric, because it ensures that
+ // the subscript is pre-evaluated, allowing direct indexing into the sequence.
+
+ PromotionOffer offer = new PromotionOffer(opt);
+ offer.action = PromotionOffer.FOCUS_INDEPENDENT;
+ offer.promoteDocumentDependent = (start.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0;
+ offer.containingExpression = this;
+ filter2 = doPromotion(filter, offer);
+ if (filter2 != filter) {
+ filter = filter2;
+ adoptChildExpression(filter2);
+ }
+
+ if (offer.containingExpression instanceof LetExpression) {
+ if (debug) {
+ opt.trace("Subexpression extracted from filter because independent of context", offer.containingExpression);
+ }
+ offer.containingExpression = visitor.optimize(offer.containingExpression, contextItemType);
+ }
+ Expression result = offer.containingExpression;
+
+ if (result instanceof FilterExpression) {
+ final Sequence sequence = ((FilterExpression) result).tryEarlyEvaluation(visitor);
+ if (sequence != null) {
+ GroundedValue value = SequenceTool.toGroundedValue(sequence);
+ return Literal.makeLiteral(value);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return start.getIntegerBounds();
+ }
+
+ private Sequence tryEarlyEvaluation(ExpressionVisitor visitor) throws XPathException {
+ // Attempt early evaluation of a filter expression if the base sequence is constant and the
+ // filter depends only on the context. (This can't be done if, for example, the predicate uses
+ // local variables, even variables declared within the predicate)
+ try {
+ if (start instanceof Literal &&
+ !ExpressionTool.refersToVariableOrFunction(filter) &&
+ (filter.getDependencies()&~StaticProperty.DEPENDS_ON_FOCUS) == 0) {
+ XPathContext context = visitor.getStaticContext().makeEarlyEvaluationContext();
+ return SequenceExtent.makeSequenceExtent(iterate(context));
+ }
+ } catch (Exception e) {
+ // can happen for a variety of reasons, for example the filter references a global parameter,
+ // references the doc() function, etc.
+ return null;
+ }
+ return null;
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet target = start.addToPathMap(pathMap, pathMapNodeSet);
+ filter.addToPathMap(pathMap, target);
+ return target;
+ }
+
+ /**
+ * Construct an expression that obtains the effective boolean value of a given expression,
+ * by wrapping it in a call of the boolean() function
+ *
+ * @param in the given expression
+ * @param config the Saxon configuration
+ * @return an expression that wraps the given expression in a call to the fn:boolean() function
+ */
+
+ private static Expression forceToBoolean(Expression in, Configuration config) {
+ final TypeHierarchy th = config.getTypeHierarchy();
+ if (in.getItemType(th).getPrimitiveType() == StandardNames.XS_BOOLEAN) {
+ return in;
+ }
+ return SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{in});
+ }
+
+ /**
+ * Attempt to rewrite a filter expression whose predicate is a test of the form
+ * [position() op expr] as a call on functions such as subsequence, remove, or saxon:itemAt
+ * @param visitor the current expression visitor
+ * @return the rewritten expression if a rewrite was possible, or null otherwise
+ */
+
+ private Expression tryToRewritePositionalFilter(ExpressionVisitor visitor) throws XPathException {
+ if (visitor.isOptimizeForStreaming()) {
+ // TODO: we're suppressing these optimizations because they generate expressions that
+ // can't currently be streamed. But in principle the optimizations are still worth doing.
+ return null;
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (filter instanceof Literal) {
+ GroundedValue val = ((Literal)filter).getValue();
+ if (val instanceof NumericValue) {
+ if (((NumericValue)val).isWholeNumber()) {
+ long lvalue = ((NumericValue)val).longValue();
+ if (lvalue <= 0) {
+ return Literal.makeEmptySequence();
+ } else if (lvalue == 1) {
+ return FirstItemExpression.makeFirstItemExpression(start);
+ } else {
+ return new SubscriptExpression(start, filter);
+ }
+ } else {
+ return Literal.makeEmptySequence();
+ }
+ } else {
+ return (ExpressionTool.effectiveBooleanValue(val.iterate()) ? start : Literal.makeEmptySequence());
+ }
+ }
+ if (th.isSubType(filter.getItemType(th), BuiltInAtomicType.NUMERIC) &&
+ filter.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ (filter.getDependencies() & StaticProperty.DEPENDS_ON_FOCUS) == 0) {
+ return new SubscriptExpression(start, filter);
+ }
+ if (filter instanceof ComparisonExpression) {
+ VendorFunctionLibrary lib = visitor.getConfiguration().getVendorFunctionLibrary();
+ StaticContext env = visitor.getStaticContext();
+ Expression[] operands = ((ComparisonExpression)filter).getOperands();
+ int operator = ((ComparisonExpression)filter).getSingletonOperator();
+ Expression comparand;
+ if (operands[0] instanceof Position
+ && th.isSubType(operands[1].getItemType(th), BuiltInAtomicType.NUMERIC)) {
+ comparand = operands[1];
+ } else if (operands[1] instanceof Position
+ && th.isSubType(operands[0].getItemType(th), BuiltInAtomicType.NUMERIC)) {
+ comparand = operands[0];
+ operator = Token.inverse(operator);
+ } else {
+ return null;
+ }
+
+ if (ExpressionTool.dependsOnFocus(comparand)) {
+ return null;
+ }
+
+ int card = comparand.getCardinality();
+ if (Cardinality.allowsMany(card)) {
+ return null;
+ }
+
+ // If the comparand might be an empty sequence, do the base rewrite and then wrap the
+ // rewritten expression EXP in "let $n := comparand if exists($n) then EXP else ()
+ if (Cardinality.allowsZero(card)) {
+ LetExpression let = new LetExpression();
+ let.setRequiredType(SequenceType.makeSequenceType(comparand.getItemType(th), card));
+ let.setVariableQName(new StructuredQName("pp", NamespaceConstant.SAXON, "pp" + let.hashCode()));
+ let.setSequence(comparand);
+ comparand = new LocalVariableReference(let);
+ LocalVariableReference existsArg = new LocalVariableReference(let);
+ Exists exists = (Exists) SystemFunctionCall.makeSystemFunction("exists", new Expression[]{existsArg});
+ Expression rewrite = tryToRewritePositionalFilterSupport(start, comparand, operator, th, lib, env);
+ if (rewrite == null) {
+ return this;
+ }
+ Expression choice = Choose.makeConditional(exists, rewrite);
+ let.setAction(choice);
+ return let;
+ } else {
+ return tryToRewritePositionalFilterSupport(start, comparand, operator, th, lib, env);
+ }
+ } else if (filter instanceof IntegerRangeTest) {
+ // rewrite SEQ[position() = N to M]
+ // => let $n := N return subsequence(SEQ, $n, (M - ($n - 1))
+ // (precise form is optimized for the case where $n is a literal, especially N = 1)
+ Expression val = ((IntegerRangeTest)filter).getValueExpression();
+ if (!(val instanceof Position)) {
+ return null;
+ }
+ Expression min = ((IntegerRangeTest)filter).getMinValueExpression();
+ Expression max = ((IntegerRangeTest)filter).getMaxValueExpression();
+
+ if (ExpressionTool.dependsOnFocus(min)) {
+ return null;
+ }
+ if (ExpressionTool.dependsOnFocus(max)) {
+ if (max instanceof Last) {
+ return SystemFunctionCall.makeSystemFunction("subsequence", new Expression[]{start, min});
+ } else {
+ return null;
+ }
+ }
+
+ LetExpression let = new LetExpression();
+ let.setRequiredType(SequenceType.SINGLE_INTEGER);
+ let.setVariableQName(new StructuredQName("nn", NamespaceConstant.SAXON, "nn" + let.hashCode()));
+ let.setSequence(min);
+ min = new LocalVariableReference(let);
+ LocalVariableReference min2 = new LocalVariableReference(let);
+ Expression minMinusOne = new ArithmeticExpression(
+ min2, Token.MINUS, Literal.makeLiteral(Int64Value.makeIntegerValue(1)));
+ Expression length = new ArithmeticExpression(max, Token.MINUS, minMinusOne);
+ Subsequence subs = (Subsequence) SystemFunctionCall.makeSystemFunction(
+ "subsequence", new Expression[]{start, min, length});
+ let.setAction(subs);
+ return let;
+
+ } else {
+ return null;
+ }
+ }
+
+ private static Expression tryToRewritePositionalFilterSupport(
+ Expression start, Expression comparand, int operator,
+ TypeHierarchy th, VendorFunctionLibrary lib, StaticContext env)
+ throws XPathException {
+ if (th.isSubType(comparand.getItemType(th), BuiltInAtomicType.INTEGER)) {
+ switch (operator) {
+ case Token.FEQ: {
+ if (Literal.isConstantOne(comparand)) {
+ return FirstItemExpression.makeFirstItemExpression(start);
+ } else if (comparand instanceof Literal && ((IntegerValue)((Literal)comparand).getValue()).asBigInteger().compareTo(BigInteger.ZERO) <= 0) {
+ return Literal.makeEmptySequence();
+ } else {
+ return new SubscriptExpression(start, comparand);
+ }
+ }
+ case Token.FLT: {
+
+ Expression[] args = new Expression[3];
+ args[0] = start;
+ args[1] = Literal.makeLiteral(Int64Value.makeIntegerValue(1));
+ if (Literal.isAtomic(comparand)) {
+ long n = ((NumericValue)((Literal)comparand).getValue()).longValue();
+ args[2] = Literal.makeLiteral(Int64Value.makeIntegerValue(n - 1));
+ } else {
+ args[2] = new ArithmeticExpression(
+ comparand, Token.MINUS, Literal.makeLiteral(Int64Value.makeIntegerValue(1)));
+ }
+ return SystemFunctionCall.makeSystemFunction("subsequence", args);
+ }
+ case Token.FLE: {
+ Expression[] args = new Expression[3];
+ args[0] = start;
+ args[1] = Literal.makeLiteral(Int64Value.makeIntegerValue(1));
+ args[2] = comparand;
+ return SystemFunctionCall.makeSystemFunction("subsequence", args);
+ }
+ case Token.FNE: {
+ return SystemFunctionCall.makeSystemFunction("remove", new Expression[]{start, comparand});
+ }
+ case Token.FGT: {
+ Expression[] args = new Expression[2];
+ args[0] = start;
+ if (Literal.isAtomic(comparand)) {
+ long n = ((NumericValue)((Literal)comparand).getValue()).longValue();
+ args[1] = Literal.makeLiteral(Int64Value.makeIntegerValue(n + 1));
+ } else {
+ args[1] = new ArithmeticExpression(
+ comparand, Token.PLUS, Literal.makeLiteral(Int64Value.makeIntegerValue(1)));
+ }
+ return SystemFunctionCall.makeSystemFunction("subsequence", args);
+ }
+ case Token.FGE: {
+ return SystemFunctionCall.makeSystemFunction("subsequence", new Expression[]{start, comparand});
+ }
+ default:
+ throw new IllegalArgumentException("operator");
+ }
+
+ } else {
+ // the comparand is not known statically to be an integer
+ switch (operator) {
+ case Token.FEQ: {
+ return new SubscriptExpression(start, comparand);
+ }
+ case Token.FLT: {
+ // rewrite SEQ[position() lt V] as
+ // let $N := V return subsequence(SEQ, 1, if (is-whole-number($N)) then $N-1 else floor($N)))
+ LetExpression let = new LetExpression();
+ let.setRequiredType(SequenceType.makeSequenceType(
+ comparand.getItemType(th), StaticProperty.ALLOWS_ONE));
+ let.setVariableQName(new StructuredQName("pp", NamespaceConstant.SAXON, "pp" + let.hashCode()));
+ let.setSequence(comparand);
+ LocalVariableReference isWholeArg = new LocalVariableReference(let);
+ LocalVariableReference arithArg = new LocalVariableReference(let);
+ LocalVariableReference floorArg = new LocalVariableReference(let);
+ Expression isWhole = lib.makeSaxonFunction(
+ "is-whole-number", new Expression[] {isWholeArg}, env, start.getContainer());
+ Expression minusOne = new ArithmeticExpression(
+ arithArg, Token.MINUS, Literal.makeLiteral(Int64Value.makeIntegerValue(1)));
+ Floor floor = (Floor) SystemFunctionCall.makeSystemFunction(
+ "floor", new Expression[]{floorArg});
+ Expression choice = Choose.makeConditional(isWhole, minusOne, floor);
+ Subsequence subs = (Subsequence) SystemFunctionCall.makeSystemFunction(
+ "subsequence", new Expression[]{start, Literal.makeLiteral(Int64Value.makeIntegerValue(1)), choice});
+ let.setAction(subs);
+ //decl.fixupReferences(let);
+ return let;
+ }
+ case Token.FLE: {
+ Floor floor = (Floor) SystemFunctionCall.makeSystemFunction(
+ "floor", new Expression[]{comparand});
+ return SystemFunctionCall.makeSystemFunction(
+ "subsequence", new Expression[]{start, Literal.makeLiteral(Int64Value.makeIntegerValue(1)), floor});
+ }
+ case Token.FNE: {
+ // rewrite SEQ[position() ne V] as
+ // let $N := V return remove(SEQ, if (is-whole-number($N)) then xs:integer($N) else 0)
+ LetExpression let = new LetExpression();
+ ExpressionTool.copyLocationInfo(start, let);
+ let.setRequiredType(SequenceType.makeSequenceType(
+ comparand.getItemType(th), StaticProperty.ALLOWS_ONE));
+ let.setVariableQName(new StructuredQName("pp", NamespaceConstant.SAXON, "pp" + let.hashCode()));
+ let.setSequence(comparand);
+ LocalVariableReference isWholeArg = new LocalVariableReference(let);
+ LocalVariableReference castArg = new LocalVariableReference(let);
+ Expression isWhole = lib.makeSaxonFunction(
+ "is-whole-number", new Expression[] {isWholeArg}, env, start.getContainer());
+ ExpressionTool.copyLocationInfo(start, isWhole);
+ Expression cast = new CastExpression(castArg, BuiltInAtomicType.INTEGER, false);
+ ExpressionTool.copyLocationInfo(start, cast);
+ Expression choice = Choose.makeConditional(
+ isWhole, cast, Literal.makeLiteral(Int64Value.makeIntegerValue(0)));
+ Remove rem = (Remove) SystemFunctionCall.makeSystemFunction(
+ "remove", new Expression[]{start, choice});
+ let.setAction(rem);
+ return let;
+ }
+ case Token.FGT: {
+ // rewrite SEQ[position() gt V] as
+ // let $N := V return subsequence(SEQ, if (is-whole-number($N)) then $N+1 else ceiling($N)))
+ LetExpression let = new LetExpression();
+ let.setRequiredType(SequenceType.makeSequenceType(
+ comparand.getItemType(th), StaticProperty.ALLOWS_ONE));
+ let.setVariableQName(new StructuredQName("pp", NamespaceConstant.SAXON, "pp" + let.hashCode()));
+ let.setSequence(comparand);
+ LocalVariableReference isWholeArg = new LocalVariableReference(let);
+ LocalVariableReference arithArg = new LocalVariableReference(let);
+ LocalVariableReference ceilingArg = new LocalVariableReference(let);
+ Expression isWhole = lib.makeSaxonFunction(
+ "is-whole-number", new Expression[] {isWholeArg}, env, start.getContainer());
+ Expression plusOne = new ArithmeticExpression(
+ arithArg, Token.PLUS, Literal.makeLiteral(Int64Value.makeIntegerValue(1)));
+ Ceiling ceiling = (Ceiling) SystemFunctionCall.makeSystemFunction(
+ "ceiling", new Expression[]{ceilingArg});
+ Expression choice = Choose.makeConditional(isWhole, plusOne, ceiling);
+ Subsequence subs = (Subsequence) SystemFunctionCall.makeSystemFunction(
+ "subsequence", new Expression[]{start, choice});
+ let.setAction(subs);
+ return let;
+ }
+ case Token.FGE: {
+ // rewrite SEQ[position() ge V] => subsequence(SEQ, ceiling(V))
+ Ceiling ceiling = (Ceiling) SystemFunctionCall.makeSystemFunction(
+ "ceiling", new Expression[]{comparand});
+ return SystemFunctionCall.makeSystemFunction(
+ "subsequence", new Expression[]{start, ceiling});
+ }
+ default:
+ throw new IllegalArgumentException("operator");
+ }
+ }
+ }
+
+ /**
+ * Promote this expression if possible
+ *
+ * @param offer details of the promotion that is possible
+ * @param parent
+ * @return the promoted expression (or the original expression, unchanged)
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ if (offer.action == PromotionOffer.RANGE_INDEPENDENT && start instanceof FilterExpression) {
+ // Try to handle the situation where we have (EXP[PRED1])[PRED2], and
+ // EXP[PRED2] does not depend on the range variable, but PRED1 does
+ TypeHierarchy th = offer.getOptimizer().getConfiguration().getTypeHierarchy();
+ FilterExpression newfe = promoteIndependentPredicates(offer.bindingList, offer.getOptimizer(), th);
+ if (newfe != this) {
+ return newfe.promote(offer, parent);
+ }
+ }
+ if (!(offer.action == PromotionOffer.UNORDERED && filterIsPositional)) {
+ start = doPromotion(start, offer);
+ }
+ if (offer.action == PromotionOffer.REPLACE_CURRENT) {
+ filter = doPromotion(filter, offer);
+ } else if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES) {
+ filter = doPromotion(filter, offer);
+ } else {
+ // Don't pass on other requests. We could pass them on, but only after augmenting
+ // them to say we are interested in subexpressions that don't depend on either the
+ // outer context or the inner context.
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Rearrange a filter expression so that predicates that are independent of a given
+ * set of range variables come first, allowing them to be promoted along with the base
+ * expression
+ *
+ * @param bindings the given set of range variables
+ * @param th the type hierarchy cache
+ * @return the expression after promoting independent predicates
+ */
+
+ private FilterExpression promoteIndependentPredicates(Binding[] bindings, Optimizer opt, TypeHierarchy th) {
+ if (!ExpressionTool.dependsOnVariable(start, bindings)) {
+ return this;
+ }
+ if (isPositional(th)) {
+ return this;
+ }
+ if (start instanceof FilterExpression) {
+ FilterExpression fe = (FilterExpression)start;
+ if (fe.isPositional(th)) {
+ return this;
+ }
+ if (!ExpressionTool.dependsOnVariable(fe.filter, bindings)) {
+ return this;
+ }
+ if (!ExpressionTool.dependsOnVariable(filter, bindings)) {
+ FilterExpression result = new FilterExpression(
+ new FilterExpression(fe.start, filter).promoteIndependentPredicates(bindings, opt, th),
+ fe.filter);
+ opt.trace("Reordered filter predicates:", result);
+ return result;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Determine whether an expression, when used as a filter, is potentially positional;
+ * that is, where it either contains a call on position() or last(), or where it is capable of returning
+ * a numeric result.
+ *
+ * @param exp the expression to be examined
+ * @param th the type hierarchy cache
+ * @return true if the expression depends on position() or last() explicitly or implicitly
+ */
+
+ private static boolean isPositionalFilter(Expression exp, TypeHierarchy th) {
+ ItemType type = exp.getItemType(th);
+ if (type.equals(BuiltInAtomicType.BOOLEAN)) {
+ // common case, get it out of the way quickly
+ return isExplicitlyPositional(exp);
+ }
+ return (type.equals(BuiltInAtomicType.ANY_ATOMIC) ||
+ type instanceof AnyItemType ||
+ type.equals(BuiltInAtomicType.INTEGER) ||
+ type.equals(BuiltInAtomicType.NUMERIC) ||
+ th.isSubType(type, BuiltInAtomicType.NUMERIC) ||
+ isExplicitlyPositional(exp));
+ }
+
+ /**
+ * Determine whether an expression, when used as a filter, has an explicit dependency on position() or last()
+ *
+ * @param exp the expression being tested
+ * @return true if the expression is explicitly positional, that is, if it contains an explicit call on
+ * position() or last()
+ */
+
+ private static boolean isExplicitlyPositional(Expression exp) {
+ return (exp.getDependencies() & (StaticProperty.DEPENDS_ON_POSITION | StaticProperty.DEPENDS_ON_LAST)) != 0;
+ }
+
+ /**
+ * Get the immediate subexpressions of this expression
+ *
+ * @return the subexpressions, as an array
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(start, filter);
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new PairIterator<SubExpressionInfo>(
+ new SubExpressionInfo(start, true, false, INHERITED_CONTEXT),
+ new SubExpressionInfo(filter, false, true, INSPECTION_CONTEXT)
+ );
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (start == original) {
+ start = replacement;
+ found = true;
+ }
+ if (filter == original) {
+ filter = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Get the static cardinality of this expression
+ *
+ * @return the cardinality. The method attempts to determine the case where the
+ * filter predicate is guaranteed to select at most one item from the sequence being filtered
+ */
+
+ public int computeCardinality() {
+ if (filter instanceof Literal && ((Literal)filter).getValue() instanceof NumericValue) {
+ if (((NumericValue)((Literal)filter).getValue()).compareTo(1) == 0 &&
+ !Cardinality.allowsZero(start.getCardinality())) {
+ return StaticProperty.ALLOWS_ONE;
+ } else {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+ }
+ if (filterIsIndependentNumeric) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+ if (filter instanceof IsLastExpression && ((IsLastExpression)filter).getCondition()) {
+ return start.getCardinality() & ~StaticProperty.ALLOWS_MANY;
+ }
+ if (!Cardinality.allowsMany(start.getCardinality())) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-significant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return the static properties of the expression, as a bit-significant value
+ */
+
+ public int computeSpecialProperties() {
+ return start.getSpecialProperties();
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ *
+ * @param other the expression to be compared with this one
+ * @return true if the two expressions are statically equivalent
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof FilterExpression) {
+ FilterExpression f = (FilterExpression)other;
+ return (start.equals(f.start) &&
+ filter.equals(f.filter));
+ }
+ return false;
+ }
+
+ /**
+ * get HashCode for comparing two expressions
+ *
+ * @return the hash code
+ */
+
+ public int hashCode() {
+ return "FilterExpression".hashCode() + start.hashCode() + filter.hashCode();
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ Expression base = getControllingExpression();
+ Expression filter = getFilter();
+ TypeHierarchy th = config.getTypeHierarchy();
+ Pattern basePattern = base.toPattern(config, is30);
+ if (!isPositional(th)) {
+ return new PatternWithPredicate(basePattern, filter);
+ } else if (basePattern instanceof ItemTypePattern &&
+ basePattern.getItemType() instanceof NodeTest &&
+ filterIsPositional &&
+ (filter.getDependencies()&StaticProperty.DEPENDS_ON_LAST) == 0) {
+ return new GeneralPositionalPattern((NodeTest)basePattern.getItemType(), filter);
+ }
+ if (base.getItemType(th) instanceof NodeTest) {
+ return new GeneralNodePattern(this, (NodeTest)base.getItemType(th));
+ } else {
+ throw new XPathException("The filtered expression in an XSLT 2.0 pattern must be a simple step");
+ }
+ }
+
+ /**
+ * Iterate over the results, returning them in the correct order
+ *
+ * @param context the dynamic context for the evaluation
+ * @return an iterator over the expression results
+ * @throws XPathException if any dynamic error occurs
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+
+ // Fast path where both operands are constants, or simple variable references
+
+ Expression startExp = start;
+ Sequence startValue = null;
+ if (startExp instanceof Literal) {
+ startValue = ((Literal)startExp).getValue();
+ } else if (startExp instanceof VariableReference) {
+ startValue = ((VariableReference)startExp).evaluateVariable(context);
+ if (startValue instanceof GroundedValue) {
+ startExp = Literal.makeLiteral((GroundedValue)startValue);
+ }
+ }
+
+ if (startValue instanceof EmptySequence) {
+ return EmptyIterator.getInstance();
+ }
+
+ Sequence filterValue = null;
+ if (filter instanceof Literal) {
+ filterValue = ((Literal)filter).getValue();
+ } else if (filter instanceof VariableReference) {
+ filterValue = ((VariableReference)filter).evaluateVariable(context);
+ }
+
+ // Handle the case where the filter is a value. Because of earlier static rewriting, this covers
+ // all cases where the filter expression is independent of the context, that is, where the
+ // value of the filter expression is the same for all items in the sequence being filtered.
+
+ if (filterValue != null) {
+ filterValue = SequenceTool.toGroundedValue(filterValue);
+ if (filterValue instanceof NumericValue) {
+ // Filter is a constant number
+ if (((NumericValue)filterValue).isWholeNumber()) {
+ int pos = (int)(((NumericValue)filterValue).longValue());
+ if (startValue != null) {
+ // if sequence is a value, use direct indexing
+ Item item = SequenceTool.itemAt(startValue, pos - 1);
+ return (item == null ? EmptyIterator.getInstance() : item.iterate());
+ }
+ if (pos > 0) {
+ SequenceIterator base = startExp.iterate(context);
+ return SubsequenceIterator.make(base, pos, pos);
+ } else {
+ // index is less than one, no items will be selected
+ return EmptyIterator.getInstance();
+ }
+ } else {
+ // a non-integer value will never be equal to position()
+ return EmptyIterator.getInstance();
+ }
+ }
+ // Filter is a value that we can treat as boolean
+ boolean b;
+ try {
+ b = ((GroundedValue)filterValue).effectiveBooleanValue();
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ if (b) {
+ return start.iterate(context);
+ } else {
+ return EmptyIterator.getInstance();
+ }
+ }
+
+ // get an iterator over the base nodes
+
+ SequenceIterator base = startExp.iterate(context);
+
+ // quick exit for an empty sequence
+
+ if (base instanceof EmptyIterator) {
+ return base;
+ }
+
+ if (filterIsPositional && !filterIsSingletonBoolean) {
+ return new FilterIterator(base, filter, context);
+ } else {
+ return new FilterIterator.NonNumeric(base, filter, context);
+ }
+
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
+ * XPathContext.CURRENT_NODE
+ *
+ * @return the dependencies
+ */
+
+ public int computeDependencies() {
+ // not all dependencies in the filter expression matter, because the context node,
+ // position, and size are not dependent on the outer context.
+ return (start.getDependencies() |
+ (filter.getDependencies() & (StaticProperty.DEPENDS_ON_XSLT_CONTEXT |
+ StaticProperty.DEPENDS_ON_LOCAL_VARIABLES |
+ StaticProperty.DEPENDS_ON_USER_FUNCTIONS)));
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ FilterExpression fe = new FilterExpression(start.copy(), filter.copy());
+ fe.filterIsIndependentNumeric = filterIsIndependentNumeric;
+ fe.filterIsPositional = filterIsPositional;
+ fe.filterIsSingletonBoolean = filterIsSingletonBoolean;
+ return fe;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Filter expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new FilterExpressionCompiler();
+ }
+//#endif
+
+//#ifdefined STREAM
+
+
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ if (allowExtensions && ((start.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) == 0)) {
+ // base expression does not return nodes from the context document, so the filter is immaterial
+ return start.getStreamability(syntacticContext, allowExtensions, reasons);
+ }
+
+ int p = filter.getStreamability(INSPECTION_CONTEXT, allowExtensions, reasons);
+ if (p != W3C_MOTIONLESS) {
+ reasons.add("For streaming, the predicate of a filter expression must be motionless");
+ return W3C_FREE_RANGING;
+ }
+
+ TypeHierarchy th = getExecutable().getConfiguration().getTypeHierarchy();
+ if (isPositional(th)) {
+ if ((filter.getDependencies() & StaticProperty.DEPENDS_ON_LAST) != 0) {
+ reasons.add("Filter depends on last()");
+ return W3C_FREE_RANGING;
+ }
+ if (start instanceof AxisExpression &&
+ ((AxisExpression)start).getAxis() == AxisInfo.CHILD &&
+ start.getItemType(th).getPrimitiveType() == Type.ELEMENT) {
+ return start.getStreamability(syntacticContext, allowExtensions, reasons);
+ } else {
+ reasons.add("Positional predicates can be used only to filter simple AxisSteps using the child axis");
+ return W3C_FREE_RANGING;
+ }
+ }
+
+ return start.getStreamability(syntacticContext, allowExtensions, reasons);
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public FilterExpressionAdjunct getStreamingAdjunct() {
+ return new FilterExpressionAdjunct();
+ }
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ TypeHierarchy th = config.getTypeHierarchy();
+ Expression base = getControllingExpression();
+ Expression filter = getFilter();
+ int fs = filter.getStreamability(Expression.INSPECTION_CONTEXT, true, reasonForFailure);
+ if (fs != Expression.W3C_MOTIONLESS) {
+ reasonForFailure.add("The filter predicate must be motionless");
+ return null;
+ }
+
+ Pattern basePattern = base.toStreamingPattern(config, reasonForFailure);
+ if (!reasonForFailure.isEmpty()) {
+ return null;
+ }
+ if (isPositional(th)) {
+ if ((filter.getDependencies() & StaticProperty.DEPENDS_ON_LAST) != 0) {
+ reasonForFailure.add("Cannot use last() in predicate when streaming");
+ return null;
+ }
+ if (basePattern instanceof ItemTypePattern) {
+ return new GeneralPositionalPattern((NodeTest)basePattern.getItemType(), filter);
+ }
+ if (basePattern instanceof AncestorQualifiedPattern &&
+ ((AncestorQualifiedPattern)basePattern).getUpwardsAxis() == AxisInfo.PARENT) {
+ int axis = ((AncestorQualifiedPattern)basePattern).getUpwardsAxis();
+ if (axis != AxisInfo.PARENT) {
+ reasonForFailure.add("Cannot use a positional predicate when streaming except with the child axis");
+ return null;
+ }
+ Pattern step = ((AncestorQualifiedPattern)basePattern).getBasePattern();
+ Pattern up = ((AncestorQualifiedPattern)basePattern).getUpperPattern();
+ if (!(step instanceof ItemTypePattern)) {
+ reasonForFailure.add("Cannot use a positional predicate when streaming except with a simple node test");
+ return null;
+ }
+ return new AncestorQualifiedPattern(
+ new GeneralPositionalPattern((NodeTest)step.getItemType(), filter),
+ up, AxisInfo.PARENT);
+ } else {
+ reasonForFailure.add("Cannot use positional predicates as part of a complex pattern (" + base.toString() + ")");
+ return null;
+ }
+ } else {
+ return new PatternWithPredicate(basePattern, filter);
+ }
+ }
+
+//#endif
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return ExpressionTool.parenthesize(start) + "[" + filter.toString() + "]";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the ExpressionPresenter to be used
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("filterExpression");
+ start.explain(out);
+ filter.explain(out);
+ out.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/FilterIterator.java b/sf/saxon/expr/FilterIterator.java
new file mode 100644
index 0000000..f3892c7
--- /dev/null
+++ b/sf/saxon/expr/FilterIterator.java
@@ -0,0 +1,219 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.NumericValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+* A CompiledFilterIterator filters an input sequence using a filter expression. Note that a CompiledFilterIterator
+* is not used where the filter is a constant number (PositionFilter is used for this purpose instead),
+* so this class does no optimizations for numeric predicates.
+*/
+
+public class FilterIterator implements SequenceIterator {
+
+ protected SequenceIterator base;
+ protected Expression filter;
+ private int position = 0;
+ /*@Nullable*/ private Item current = null;
+ protected XPathContext filterContext;
+
+ /**
+ * Constructor
+ * @param base An iteration of the items to be filtered
+ * @param filter The expression defining the filter predicate
+ * @param context The context in which the expression is being evaluated
+ */
+
+ public FilterIterator(SequenceIterator base, Expression filter,
+ XPathContext context) {
+ this.base = base;
+ this.filter = filter;
+ filterContext = context.newMinorContext();
+ filterContext.setCurrentIterator(base);
+ }
+
+ /**
+ * Set the base iterator
+ * @param base the iterator over the sequence to be filtered
+ * @param context the context in which the (outer) filter expression is evaluated
+ */
+
+ public void setSequence(SequenceIterator base, XPathContext context) {
+ this.base = base;
+ filterContext = context.newMinorContext();
+ filterContext.setCurrentIterator(base);
+ }
+
+ /**
+ * Get the next item if there is one
+ */
+
+ public Item next() throws XPathException {
+ current = getNextMatchingItem();
+ if (current == null) {
+ position = -1;
+ } else {
+ position++;
+ }
+ return current;
+ }
+
+ /**
+ * Get the next item in the base sequence that matches the filter predicate
+ * if there is such an item, or null if not.
+ * @return the next item that matches the predicate
+ */
+
+ protected Item getNextMatchingItem() throws XPathException {
+ while (true) {
+ Item next = base.next();
+ if (next == null) {
+ return null;
+ }
+ if (matches()) {
+ return next;
+ }
+ }
+ }
+
+ /**
+ * Determine whether the context item matches the filter predicate
+ * @return true if the context item matches
+ */
+
+ protected boolean matches() throws XPathException {
+
+ // This code is carefully designed to avoid reading more items from the
+ // iteration of the filter expression than are absolutely essential.
+
+ // The code is almost identical to the code in ExpressionTool#effectiveBooleanValue
+ // except for the handling of a numeric result
+
+ SequenceIterator iterator = filter.iterate(filterContext);
+ Item first = iterator.next();
+ if (first == null) {
+ return false;
+ }
+ if (first instanceof NodeInfo) {
+ return true;
+ } else {
+ if (first instanceof BooleanValue) {
+ if (iterator.next() != null) {
+ ExpressionTool.ebvError("sequence of two or more items starting with a boolean");
+ }
+ return ((BooleanValue)first).getBooleanValue();
+ } else if (first instanceof StringValue) {
+ if (iterator.next() != null) {
+ ExpressionTool.ebvError("sequence of two or more items starting with a string");
+ }
+ return (first.getStringValueCS().length()!=0);
+ } else if (first instanceof Int64Value) {
+ if (iterator.next() != null) {
+ ExpressionTool.ebvError("sequence of two or more items starting with a numeric value");
+ }
+ return ((Int64Value)first).longValue() == base.position();
+
+ } else if (first instanceof NumericValue) {
+ if (iterator.next() != null) {
+ ExpressionTool.ebvError("sequence of two or more items starting with a numeric value");
+ }
+ return ((NumericValue)first).compareTo(base.position()) == 0;
+ } else {
+ ExpressionTool.ebvError("sequence starting with an atomic value other than a boolean, number, or string");
+ return false;
+ }
+ }
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Get another iterator to return the same nodes
+ */
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new FilterIterator(base.getAnother(), filter,
+ filterContext);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link SequenceIterator#GROUNDED}, {@link SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+ /**
+ * Subclass to handle the common special case where it is statically known
+ * that the filter cannot return a numeric value
+ */
+
+ public static final class NonNumeric extends FilterIterator {
+
+ /**
+ * Create a CompiledFilterIterator for the situation where it is known that the filter
+ * expression will never evaluate to a number value. For this case we can simply
+ * use the effective boolean value of the predicate
+ * @param base iterator over the sequence to be filtered
+ * @param filter the filter expression
+ * @param context the current context (for evaluating the filter expression as a whole).
+ * A new context will be created to evaluate the predicate.
+ */
+
+ public NonNumeric(SequenceIterator base, Expression filter,
+ XPathContext context) {
+ super(base, filter, context);
+ }
+
+ /**
+ * Determine whether the context item matches the filter predicate
+ */
+
+ protected boolean matches() throws XPathException {
+ return filter.effectiveBooleanValue(filterContext);
+ }
+
+ /**
+ * Get another iterator to return the same nodes
+ */
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new FilterIterator.NonNumeric(base.getAnother(), filter, filterContext);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/expr/FirstItemExpression.java b/sf/saxon/expr/FirstItemExpression.java
new file mode 100644
index 0000000..f07f921
--- /dev/null
+++ b/sf/saxon/expr/FirstItemExpression.java
@@ -0,0 +1,165 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.FirstItemExpressionCompiler;
+import com.saxonica.stream.adjunct.FirstItemExpressionAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+
+import java.util.List;
+
+
+
+/**
+* A FirstItemExpression returns the first item in the sequence returned by a given
+* base expression
+*/
+
+public final class FirstItemExpression extends SingleItemFilter {
+
+ /**
+ * Private Constructor
+ * @param base A sequence expression denoting sequence whose first item is to be returned
+ */
+
+ private FirstItemExpression(Expression base) {
+ operand = base;
+ adoptChildExpression(base);
+ //computeStaticProperties();
+ }
+
+ /**
+ * Static factory method
+ * @param base A sequence expression denoting sequence whose first item is to be returned
+ * @return the FirstItemExpression, or an equivalent
+ */
+
+ public static Expression makeFirstItemExpression(Expression base) {
+ if (base instanceof FirstItemExpression) {
+ return base;
+ } else {
+ return new FirstItemExpression(base);
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new FirstItemExpression(getBaseExpression().copy());
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ Pattern basePattern = operand.toPattern(config, is30);
+ ItemType type = basePattern.getItemType();
+ if (type instanceof NodeTest) {
+ if (basePattern instanceof ItemTypePattern) {
+ return new SimplePositionalPattern(
+ (NodeTest)type, Literal.makeLiteral(Int64Value.PLUS_ONE), Token.FEQ);
+ } else {
+ return new GeneralNodePattern(this, (NodeTest)type);
+ }
+ } else {
+ // For a non-node pattern, the predicate [1] is always true
+ return basePattern;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ SequenceIterator iter = operand.iterate(context);
+ Item result = iter.next();
+ iter.close();
+ return result;
+ }
+
+ public String getExpressionName() {
+ return "firstItem";
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the FirstItem expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new FirstItemExpressionCompiler();
+ }
+//#endif
+
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(syntacticContext, allowExtensions, reasons);
+ }
+
+ @Override
+ public FirstItemExpressionAdjunct getStreamingAdjunct() {
+ return new FirstItemExpressionAdjunct();
+ }
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ TypeHierarchy th = config.getTypeHierarchy();
+ Expression base = getBaseExpression();
+
+ if (base instanceof AxisExpression &&
+ ((AxisExpression)base).getAxis() == AxisInfo.CHILD &&
+ base.getItemType(th).getPrimitiveType() == Type.ELEMENT) {
+ return new SimplePositionalPattern(
+ (NodeTest)base.getItemType(th),
+ Literal.makeLiteral(IntegerValue.PLUS_ONE),
+ Token.FEQ);
+ } else {
+ return super.toStreamingPattern(config, reasonForFailure);
+ }
+ }
+
+ //#endif
+}
+
diff --git a/sf/saxon/expr/ForExpression.java b/sf/saxon/expr/ForExpression.java
new file mode 100644
index 0000000..201d8bf
--- /dev/null
+++ b/sf/saxon/expr/ForExpression.java
@@ -0,0 +1,841 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ForExpressionCompiler;
+import com.saxonica.stream.Streamability;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.evpull.EventMappingFunction;
+import net.sf.saxon.evpull.EventMappingIterator;
+import net.sf.saxon.expr.instruct.Choose;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* A ForExpression maps an expression over a sequence.
+* This version works with range variables, it doesn't change the context information
+*/
+
+public class ForExpression extends Assignation {
+
+ /*@Nullable*/ protected PositionVariable positionVariable = null;
+ int actionCardinality = StaticProperty.ALLOWS_MANY;
+
+ /**
+ * Create a "for" expression (for $x at $p in SEQUENCE return ACTION)
+ */
+
+ public ForExpression() {
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "for";
+ }
+
+ /**
+ * Set the reference to the position variable (XQuery only)
+ * @param decl the range variable declaration for the position variable
+ */
+
+ public void setPositionVariable (PositionVariable decl) {
+ positionVariable = decl;
+ }
+
+ /*@Nullable*/ public PositionVariable getPositionVariable () {
+ return positionVariable;
+ }
+
+ public boolean hasVariableBinding(Binding binding){
+ return this == binding || positionVariable == binding;
+ }
+
+ /**
+ * Get the name of the position variable
+ * @return the name of the position variable ("at $p") if there is one, or null if not
+ */
+
+ /*@Nullable*/ public StructuredQName getPositionVariableName() {
+ if (positionVariable == null) {
+ return null;
+ } else {
+ return positionVariable.getVariableQName();
+ }
+ }
+
+ /**
+ * Set the slot number for the range variable
+ * @param nr the slot number allocated to the range variable on the local stack frame.
+ * This implicitly allocates the next slot number to the position variable if there is one.
+ */
+
+ public void setSlotNumber(int nr) {
+ super.setSlotNumber(nr);
+ if (positionVariable != null) {
+ positionVariable.setSlotNumber(nr+1);
+ }
+ }
+
+ /**
+ * Get the number of slots required.
+ * @return normally 1, except for a FOR expression with an AT clause, where it is 2.
+ */
+
+ public int getRequiredSlots() {
+ return (positionVariable == null ? 1 : 2);
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ // The order of events is critical here. First we ensure that the type of the
+ // sequence expression is established. This is used to establish the type of the variable,
+ // which in turn is required when type-checking the action part.
+
+ sequence = visitor.typeCheck(sequence, contextItemType);
+ if (Literal.isEmptySequence(sequence)) {
+ return sequence;
+ }
+
+ if (requiredType != null) {
+ // if declaration is null, we've already done the type checking in a previous pass
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ SequenceType decl = requiredType;
+ SequenceType sequenceType = SequenceType.makeSequenceType(
+ decl.getPrimaryType(), StaticProperty.ALLOWS_ZERO_OR_MORE);
+ RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, variableName, 0
+ );
+ //role.setSourceLocator(this);
+ sequence = TypeChecker.strictTypeCheck(
+ sequence, sequenceType, role, visitor.getStaticContext());
+ ItemType actualItemType = sequence.getItemType(th);
+ refineTypeInformation(actualItemType,
+ getRangeVariableCardinality(),
+ null,
+ sequence.getSpecialProperties(), visitor, this);
+ }
+
+ action = visitor.typeCheck(action, contextItemType);
+ if (Literal.isEmptySequence(action)) {
+ return action;
+ }
+ actionCardinality = action.getCardinality();
+ return this;
+ }
+
+ /**
+ * Get the cardinality of the range variable
+ * @return the cardinality of the range variable (StaticProperty.EXACTLY_ONE). Can be overridden
+ * in a subclass
+ */
+
+ protected int getRangeVariableCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Optimize the expression
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ boolean debug = opt.getConfiguration().getBooleanProperty(FeatureKeys.TRACE_OPTIMIZER_DECISIONS);
+
+ // Try to promote any WHERE clause appearing immediately within the FOR expression
+
+ if (Choose.isSingleBranchChoice(action)) {
+ Expression act2 = visitor.optimize(action, contextItemType);
+ if (act2 != action) {
+ action = act2;
+ adoptChildExpression(action);
+ visitor.resetStaticProperties();
+ }
+ }
+
+ Expression p = promoteWhereClause(positionVariable);
+ if (p != null) {
+ if (debug) {
+ opt.trace("Promoted where clause in for $" + getVariableName(), p);
+ }
+ return visitor.optimize(p, contextItemType);
+ }
+
+ // See if there is a simple "where" condition that can be turned into a predicate
+
+// Expression pred = convertWhereToPredicate(visitor, contextItemType);
+// if (pred != null) {
+// if (debug) {
+// opt.trace("Converted where clause in for $" + getVariableName() + " to predicate", pred);
+// }
+// if (pred != this) {
+// return visitor.optimize(pred, contextItemType);
+// }
+// }
+
+ Expression seq2 = visitor.optimize(sequence, contextItemType);
+ if (seq2 != sequence) {
+ sequence = seq2;
+ adoptChildExpression(sequence);
+ visitor.resetStaticProperties();
+ return optimize(visitor, contextItemType);
+ }
+
+ if (Literal.isEmptySequence(sequence)) {
+ return sequence;
+ }
+
+ Expression act2 = visitor.optimize(action, contextItemType);
+ if (act2 != action) {
+ action = act2;
+ adoptChildExpression(action);
+ visitor.resetStaticProperties();
+ // it's now worth re-attempting the "where" clause optimizations
+ return optimize(visitor, contextItemType);
+ }
+
+ if (Literal.isEmptySequence(action)) {
+ return action;
+ }
+
+ Expression e2 = extractLoopInvariants(visitor, contextItemType);
+ if (e2 != null && e2 != this) {
+ if (debug) {
+ opt.trace("Extracted invariant in 'for $" + getVariableName() + "' loop", e2);
+ }
+ return visitor.optimize(e2, contextItemType);
+ }
+
+ // Simplify an expression of the form "for $b in a/b/c return $b/d".
+ // (XQuery users seem to write these a lot!)
+
+ if (positionVariable==null &&
+ sequence instanceof SlashExpression && action instanceof SlashExpression) {
+ SlashExpression path2 = (SlashExpression)action;
+ Expression start2 = path2.getControllingExpression();
+ Expression step2 = path2.getControlledExpression();
+ if (start2 instanceof VariableReference && ((VariableReference)start2).getBinding() == this &&
+ ExpressionTool.getReferenceCount(action, this, false) == 1 &&
+ ((step2.getDependencies() & (StaticProperty.DEPENDS_ON_POSITION | StaticProperty.DEPENDS_ON_LAST)) == 0)) {
+ Expression newPath = new SlashExpression(sequence, path2.getControlledExpression());
+ ExpressionTool.copyLocationInfo(this, newPath);
+ newPath = visitor.typeCheck(visitor.simplify(newPath), contextItemType);
+ if (newPath instanceof SlashExpression) {
+ // if not, it has been wrapped in a DocumentSorter or Reverser, which makes it ineligible.
+ // see test qxmp299, where this condition isn't satisfied
+ if (debug) {
+ opt.trace("Collapsed return clause of for $" + getVariableName() +
+ " into path expression", newPath);
+ }
+ return visitor.optimize(newPath, contextItemType);
+ }
+ }
+ }
+
+ // Simplify an expression of the form "for $x in EXPR return $x". These sometimes
+ // arise as a result of previous optimization steps.
+
+ if (action instanceof VariableReference && ((VariableReference)action).getBinding() == this) {
+ if (debug) {
+ opt.trace("Collapsed redundant for expression $" + getVariableName(), sequence);
+ }
+ return sequence;
+ }
+
+ // Rewrite an expression of the form "for $x at $p in EXPR return $p" as "1 to count(EXPR)"
+
+ if (action instanceof VariableReference && ((VariableReference)action).getBinding() == positionVariable) {
+ FunctionCall count = SystemFunctionCall.makeSystemFunction("count", new Expression[]{sequence});
+ RangeExpression range = new RangeExpression(Literal.makeLiteral(Int64Value.PLUS_ONE), Token.TO, count);
+ if (debug) {
+ opt.trace("Replaced 'for $x at $p in EXP return $p' by '1 to count(EXP)'", range);
+ }
+ return range.optimize(visitor, contextItemType);
+ }
+
+ // If the cardinality of the sequence is exactly one, rewrite as a LET expression
+
+ if (sequence.getCardinality() == StaticProperty.EXACTLY_ONE) {
+ LetExpression let = new LetExpression();
+ let.setVariableQName(variableName);
+ let.setRequiredType(SequenceType.makeSequenceType(
+ sequence.getItemType(visitor.getConfiguration().getTypeHierarchy()),
+ StaticProperty.EXACTLY_ONE));
+ let.setSequence(sequence);
+ let.setAction(action);
+ let.setSlotNumber(slotNumber);
+ ExpressionTool.rebindVariableReferences(action, this, let);
+ if (positionVariable != null) {
+ LetExpression let2 =new LetExpression();
+ let2.setVariableQName(positionVariable.getVariableQName());
+ let2.setSequence(Literal.makeLiteral(IntegerValue.PLUS_ONE));
+ let2.setAction(let);
+ let2.setSlotNumber(positionVariable.getLocalSlotNumber());
+ let2.setRequiredType(SequenceType.SINGLE_INTEGER);
+ ExpressionTool.rebindVariableReferences(let, positionVariable, let2);
+ let = let2;
+ }
+ return let.typeCheck(visitor, contextItemType).optimize(visitor, contextItemType);
+ }
+
+ if (visitor.isOptimizeForStreaming() && positionVariable == null) {
+ Expression e3 = visitor.getConfiguration().obtainOptimizer().optimizeForExpressionForStreaming(this);
+ if (e3 != this) {
+ return visitor.optimize(e3, contextItemType);
+ }
+ }
+
+ //declaration = null; // let the garbage collector take it
+ return this;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ /*@Nullable*/@Override
+ public IntegerValue[] getIntegerBounds() {
+ return action.getIntegerBounds();
+ }
+
+ /**
+ * Extract subexpressions in the action part that don't depend on the range variable
+ * @param visitor the expression visitor
+ * @param contextItemType the item type of the context item
+ * @return the optimized expression if it has changed, or null if no optimization was possible
+ */
+
+ private Expression extractLoopInvariants(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ // Extract subexpressions that don't depend on the range variable or the position variable
+ // If a subexpression is (or might be) creative, this is, if it creates new nodes, we don't
+ // extract it from the loop, but we do extract its non-creative subexpressions
+
+ //if (positionVariable == null) {
+ PromotionOffer offer = new PromotionOffer(visitor.getConfiguration().obtainOptimizer());
+ offer.containingExpression = this;
+ offer.action = PromotionOffer.RANGE_INDEPENDENT;
+ if (positionVariable == null) {
+ offer.bindingList = new Binding[] {this};
+ } else {
+ offer.bindingList = new Binding[] {this, positionVariable};
+ }
+ action = doPromotion(action, offer);
+ if (offer.containingExpression instanceof LetExpression) {
+ // a subexpression has been promoted
+ //offer.containingExpression.setParentExpression(container);
+ // try again: there may be further subexpressions to promote
+ offer.containingExpression = visitor.optimize(offer.containingExpression, contextItemType);
+ }
+ return offer.containingExpression;
+ //}
+ //return null;
+
+ }
+
+ /**
+ * Promote a WHERE clause whose condition doesn't depend on the variable being bound.
+ * This rewrites an expression of the form
+ *
+ * <p>let $i := SEQ return if (C) then R else ()</p>
+ * <p>to the form:</p>
+ * <p>if (C) then (let $i := SEQ return R) else ()</p>
+ *
+ * @param positionBinding the binding of the position variable if any
+ * @return an expression in which terms from the WHERE clause that can be extracted have been extracted
+ */
+
+ /*@Nullable*/ protected Expression promoteWhereClause(/*@Nullable*/ Binding positionBinding) {
+ if (Choose.isSingleBranchChoice(action)) {
+ Expression condition = ((Choose)action).getConditions()[0];
+ Binding[] bindingList;
+ if (positionBinding == null) {
+ bindingList = new Binding[] {this};
+ } else {
+ bindingList = new Binding[] {this, positionBinding};
+ }
+ List list = new ArrayList(5);
+ Expression promotedCondition = null;
+ BooleanExpression.listAndComponents(condition, list);
+ for (int i = list.size() - 1; i >= 0; i--) {
+ Expression term = (Expression) list.get(i);
+ if (!ExpressionTool.dependsOnVariable(term, bindingList)) {
+ if (promotedCondition == null) {
+ promotedCondition = term;
+ } else {
+ promotedCondition = new AndExpression(term, promotedCondition);
+ }
+ list.remove(i);
+ }
+ }
+ if (promotedCondition != null) {
+ if (list.isEmpty()) {
+ // the whole if() condition has been promoted
+ Expression oldThen = ((Choose)action).getActions()[0];
+ setAction(oldThen);
+ return Choose.makeConditional(condition, this);
+ } else {
+ // one or more terms of the if() condition have been promoted
+ Expression retainedCondition = (Expression) list.get(0);
+ for (int i = 1; i < list.size(); i++) {
+ retainedCondition = new AndExpression(retainedCondition, (Expression) list.get(i));
+ }
+ ((Choose)action).getConditions()[0] = retainedCondition;
+ Expression newIf = Choose.makeConditional(
+ promotedCondition, this, Literal.makeEmptySequence());
+ ExpressionTool.copyLocationInfo(this, newIf);
+ return newIf;
+ }
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ForExpression forExp = new ForExpression();
+ forExp.setRequiredType(requiredType);
+ forExp.setVariableQName(variableName);
+ forExp.setSequence(sequence.copy());
+ Expression newAction = action.copy();
+ forExp.setAction(newAction);
+ forExp.variableName = variableName;
+ forExp.slotNumber = slotNumber;
+ ExpressionTool.rebindVariableReferences(newAction, this, forExp);
+ if (positionVariable != null) {
+ PositionVariable pv2 = new PositionVariable();
+ pv2.setVariableQName(positionVariable.getVariableQName());
+ forExp.setPositionVariable(pv2);
+ ExpressionTool.rebindVariableReferences(newAction, positionVariable, pv2);
+ }
+ return forExp;
+ }
+
+ /**
+ * Mark tail function calls: only possible if the for expression iterates zero or one times.
+ * (This arises in XSLT/XPath, which does not have a LET expression, so FOR gets used instead)
+ */
+
+ public int markTailFunctionCalls(StructuredQName qName, int arity) {
+ if (!Cardinality.allowsMany(sequence.getCardinality())) {
+ return ExpressionTool.markTailFunctionCalls(action, qName, arity);
+ } else {
+ return UserFunctionCall.NOT_TAIL_CALL;
+ }
+ }
+
+ /**
+ * Extend an array of variable bindings to include the binding(s) defined in this expression
+ */
+
+ public Binding[] extendBindingList(Binding[] in) {
+ if (positionVariable == null) {
+ return super.extendBindingList(in);
+ }
+ Binding[] newBindingList = new Binding[in.length+2];
+ System.arraycopy(in, 0, newBindingList, 0, in.length);
+ newBindingList[in.length] = this;
+ newBindingList[in.length+1] = positionVariable;
+ return newBindingList;
+ }
+
+ /**
+ * Determine whether this is a vacuous expression as defined in the XQuery update specification
+ * @return true if this expression is vacuous
+ */
+
+ public boolean isVacuousExpression() {
+ return action.isVacuousExpression();
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided. This implementation provides both iterate() and
+ * process() methods natively.
+ */
+
+ public int getImplementationMethod() {
+ return ITERATE_METHOD | PROCESS_METHOD;
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ action.checkPermittedContents(parentType, env, false);
+ }
+
+//#ifdefined STREAM
+
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ if (sequence.getStreamability(syntacticContext, allowExtensions, reasons) == W3C_MOTIONLESS &&
+ sequence.getStreamability(syntacticContext, allowExtensions, reasons) == W3C_MOTIONLESS) {
+ return W3C_MOTIONLESS;
+ }
+ if (allowExtensions && !ExpressionTool.dependsOnFocus(action)) {
+ // See if we can rewrite as a streamable mapping expression
+ Expression rewrite = Streamability.rewriteForExpressionAsMappingExpression(this);
+ if (rewrite != null) {
+ int rs = rewrite.getStreamability(syntacticContext, allowExtensions, reasons);
+ if (rs == W3C_FREE_RANGING) {
+ reasons.add("Equivalent mapping expression is not streamable");
+ } else {
+ return rs;
+ }
+ } else {
+ reasons.add("Unable to convert the for expression to an equivalent mapping expression");
+ }
+ }
+ reasons.add("A for expression is free-ranging unless both subexpressions are motionless");
+ return W3C_FREE_RANGING;
+
+ }
+//#endif
+
+ /**
+ * Iterate over the sequence of values
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+
+ // First create an iteration of the base sequence.
+
+ // Then create a MappingIterator which applies a mapping function to each
+ // item in the base sequence. The mapping function is essentially the "return"
+ // expression, wrapped in a MappingAction object that is responsible also for
+ // setting the range variable at each step.
+
+ SequenceIterator base = sequence.iterate(context);
+ int pslot = (positionVariable == null ? -1 : positionVariable.getLocalSlotNumber());
+ MappingAction map = new MappingAction(context, getLocalSlotNumber(), pslot, action);
+ switch (actionCardinality) {
+ case StaticProperty.EXACTLY_ONE:
+ return new ItemMappingIterator(base, map, true);
+ case StaticProperty.ALLOWS_ZERO_OR_ONE:
+ return new ItemMappingIterator(base, map, false);
+ default:
+ return new MappingIterator(base, map);
+ }
+ }
+
+ /**
+ * Deliver the result of the expression as a sequence of events.
+ * @param context The dynamic evaluation context
+ * @return the result of the expression as an iterator over a sequence of PullEvent objects
+ * @throws XPathException if a dynamic error occurs during expression evaluation
+ */
+
+ /*@Nullable*/ public EventIterator iterateEvents(XPathContext context) throws XPathException {
+
+ // First create an iteration of the base sequence.
+
+ // Then create an EventMappingIterator which applies a mapping function to each
+ // item in the base sequence. The mapping function is essentially the "return"
+ // expression, wrapped in an EventMappingAction object that is responsible also for
+ // setting the range variable at each step.
+
+ SequenceIterator base = sequence.iterate(context);
+ EventMappingFunction map = new EventMappingAction(context, getLocalSlotNumber(), positionVariable, action);
+ return new EventMappingIterator(base, map);
+ }
+
+ /**
+ * Process this expression as an instruction, writing results to the current
+ * outputter
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ SequenceIterator iter = sequence.iterate(context);
+ int position = 1;
+ int slot = getLocalSlotNumber();
+ int pslot = -1;
+ if (positionVariable != null) {
+ pslot = positionVariable.getLocalSlotNumber();
+ }
+ while (true) {
+ Item item = iter.next();
+ if (item == null) break;
+ context.setLocalVariable(slot, item);
+ if (pslot >= 0) {
+ context.setLocalVariable(pslot, Int64Value.makeIntegerValue(position++));
+ }
+ action.process(context);
+ }
+ }
+
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ SequenceIterator iter = sequence.iterate(context);
+ int position = 1;
+ int slot = getLocalSlotNumber();
+ int pslot = -1;
+ if (positionVariable != null) {
+ pslot = positionVariable.getLocalSlotNumber();
+ }
+ while (true) {
+ Item item = iter.next();
+ if (item == null) break;
+ context.setLocalVariable(slot, item);
+ if (pslot >= 0) {
+ context.setLocalVariable(pslot, Int64Value.makeIntegerValue(position++));
+ }
+ action.evaluatePendingUpdates(context, pul);
+ }
+ }
+
+ /**
+ * Determine the data type of the items returned by the expression, if possible
+ * @return one of the values Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
+ * or Type.ITEM (meaning not known in advance)
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return action.getItemType(th);
+ }
+
+ /**
+ * Determine the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ int c1 = sequence.getCardinality();
+ int c2 = action.getCardinality();
+ return Cardinality.multiply(c1, c2);
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ * @return a representation of the expression as a string
+ */
+
+ public String toString() {
+ return "for $" + getVariableEQName() +
+ " in " + (sequence==null ? "(...)" : sequence.toString()) +
+ " return " + (action==null ? "(...)" : ExpressionTool.parenthesize(action));
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("for");
+ explainSpecializedAttributes(out);
+ out.emitAttribute("variable", getVariableEQName());
+ out.emitAttribute("as", sequence.getItemType(out.getTypeHierarchy()).toString());
+ if (positionVariable != null) {
+ out.emitAttribute("at", positionVariable.getVariableQName().getEQName());
+ }
+ out.startSubsidiaryElement("in");
+ sequence.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("return");
+ action.explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+ protected void explainSpecializedAttributes(ExpressionPresenter out) {
+ // no action
+ }
+
+ /**
+ * The MappingAction represents the action to be taken for each item in the
+ * source sequence. It acts as the MappingFunction for the mapping iterator.
+ */
+
+ public static class MappingAction implements MappingFunction, ItemMappingFunction, StatefulMappingFunction {
+
+ protected XPathContext context;
+ private int slotNumber;
+ private Expression action;
+ private int pslot = -1;
+ private int position = 1;
+
+ public MappingAction() {}
+
+ public MappingAction(XPathContext context,
+ int slotNumber,
+ int pslot,
+ Expression action) {
+ this.context = context;
+ this.slotNumber = slotNumber;
+ this.pslot = pslot;
+ this.action = action;
+ }
+
+ /*@Nullable*/ public SequenceIterator map(Item item) throws XPathException {
+ context.setLocalVariable(slotNumber, item);
+ if (pslot >= 0) {
+ context.setLocalVariable(pslot, Int64Value.makeIntegerValue(position++));
+ }
+ return action.iterate(context);
+ }
+
+ /*@Nullable*/ public Item mapItem(Item item) throws XPathException {
+ context.setLocalVariable(slotNumber, item);
+ if (pslot >= 0) {
+ context.setLocalVariable(pslot, Int64Value.makeIntegerValue(position++));
+ }
+ return action.evaluateItem(context);
+ }
+
+ public StatefulMappingFunction getAnother() {
+ // Create a copy of the stack frame, so that changes made to local variables by the cloned
+ // iterator are not seen by the original iterator
+ XPathContextMajor c2 = context.newContext();
+ StackFrame oldstack = context.getStackFrame();
+ Sequence[] vars = oldstack.getStackFrameValues();
+ Sequence[] newvars = new Sequence[vars.length];
+ System.arraycopy(vars, 0, newvars, 0, vars.length);
+ c2.setStackFrame(oldstack.getStackFrameMap(), newvars);
+ return new MappingAction(c2, slotNumber, pslot, action);
+ }
+ }
+
+ /**
+ * The EventMappingAction represents the action to be taken for each item in the
+ * source sequence. It acts as the EventMappingFunction for the mapping iterator, and
+ * also provides the Binding of the position variable (at $n) in XQuery, if used.
+ */
+
+ protected static class EventMappingAction implements EventMappingFunction {
+
+ private XPathContext context;
+ private int slotNumber;
+ private Expression action;
+ private int position = 1;
+ private int pslot = -1;
+
+ public EventMappingAction(XPathContext context,
+ int slotNumber,
+ /*@Nullable*/ PositionVariable positionBinding,
+ Expression action) {
+ this.context = context;
+ this.slotNumber = slotNumber;
+ if (positionBinding != null) {
+ pslot = positionBinding.getLocalSlotNumber();
+ }
+ this.action = action;
+ }
+
+ /*@Nullable*/ public EventIterator map(Item item) throws XPathException {
+ context.setLocalVariable(slotNumber, item);
+ if (pslot >= 0) {
+ context.setLocalVariable(pslot, Int64Value.makeIntegerValue(position++));
+ }
+ return action.iterateEvents(context);
+ }
+
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the For expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ForExpressionCompiler();
+ }
+//#endif
+
+
+ /**
+ * Get the type of this expression for use in tracing and diagnostics
+ * @return the type of expression, as enumerated in class {@link net.sf.saxon.trace.Location}
+ */
+
+ public int getConstructType() {
+ return Location.FOR_EXPRESSION;
+ }
+
+}
+
diff --git a/sf/saxon/expr/FunctionCall.java b/sf/saxon/expr/FunctionCall.java
new file mode 100644
index 0000000..10df268
--- /dev/null
+++ b/sf/saxon/expr/FunctionCall.java
@@ -0,0 +1,459 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+* Abstract superclass for calls to system-defined and user-defined functions
+*/
+
+public abstract class FunctionCall extends Expression {
+
+ /**
+ * The name of the function
+ */
+
+ private StructuredQName name;
+
+ /**
+ * The array of expressions representing the actual parameters
+ * to the function call
+ */
+
+ protected Expression[] argument;
+
+ /**
+ * Set the name of the function being called
+ * @param name the name of the function
+ */
+
+ public final void setFunctionName(StructuredQName name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the qualified of the function being called
+ * @return the qualified name
+ */
+
+ public StructuredQName getFunctionName() {
+ return name;
+ }
+
+ /**
+ * Determine the number of actual arguments supplied in the function call
+ * @return the arity (the number of arguments)
+ */
+
+ public final int getNumberOfArguments() {
+ return argument.length;
+ }
+
+ /**
+ * Method called by the expression parser when all arguments have been supplied
+ * @param args the expressions contained in the argument list of the function call
+ */
+
+ public void setArguments(Expression[] args) {
+ argument = args;
+ for (Expression arg : args) {
+ adoptChildExpression(arg);
+ }
+ }
+
+ /**
+ * Get the expressions supplied as actual arguments to the function
+ * @return the array of expressions supplied in the argument list of the function call
+ */
+
+ public Expression[] getArguments() {
+ return argument;
+ }
+
+ /**
+ * Simplify the function call. Default method is to simplify each of the supplied arguments and
+ * evaluate the function if all are now known.
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ return simplifyArguments(visitor);
+ }
+
+ /**
+ * Simplify the arguments of the function.
+ * Called from the simplify() method of each function.
+ * @return the result of simplifying the arguments of the expression
+ * @param visitor an expression visitor
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ protected final Expression simplifyArguments(ExpressionVisitor visitor) throws XPathException {
+ for (int i=0; i<argument.length; i++) {
+ Expression exp = visitor.simplify(argument[i]);
+ if (exp != argument[i]) {
+ adoptChildExpression(exp);
+ argument[i] = exp;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Type-check the expression. This also calls preEvaluate() to evaluate the function
+ * if all the arguments are constant; functions that do not require this behavior
+ * can override the preEvaluate method.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ boolean fixed = true;
+ for (int i=0; i<argument.length; i++) {
+ Expression exp = visitor.typeCheck(argument[i], contextItemType);
+ if (exp != argument[i]) {
+ adoptChildExpression(exp);
+ argument[i] = exp;
+ }
+ if (!(argument[i] instanceof Literal)) {
+ fixed = false;
+ }
+ }
+ checkArguments(visitor);
+ if (fixed) {
+ try {
+ return preEvaluate(visitor);
+ } catch (NoDynamicContextException err) {
+ // Early evaluation failed, typically because the implicit timezone is not yet known.
+ // Try again later at run-time.
+ return this;
+ }
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ boolean fixed = true;
+ for (int i=0; i<argument.length; i++) {
+ Expression exp = visitor.optimize(argument[i], contextItemType);
+ if (exp != argument[i]) {
+ adoptChildExpression(exp);
+ argument[i] = exp;
+ }
+ if (fixed && !(argument[i] instanceof Literal)) {
+ fixed = false;
+ }
+ }
+ checkArguments(visitor);
+ if (fixed) {
+ return preEvaluate(visitor);
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Pre-evaluate a function at compile time. Functions that do not allow
+ * pre-evaluation, or that need access to context information, can override this method.
+ * @param visitor an expression visitor
+ * @return the result of the early evaluation, or the original expression, or potentially
+ * a simplified expression
+ * @throws net.sf.saxon.trans.XPathException if evaluation fails
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ if (getIntrinsicDependencies() != 0) {
+ return this;
+ }
+ try {
+ Literal lit = Literal.makeLiteral(SequenceExtent.makeSequenceExtent(
+ iterate(visitor.getStaticContext().makeEarlyEvaluationContext())));
+ ExpressionTool.copyLocationInfo(this, lit);
+ return lit;
+ } catch (NoDynamicContextException e) {
+ // early evaluation failed, usually because implicit timezone required
+ return this;
+ } catch (UnsupportedOperationException e) {
+ if (e.getCause() instanceof NoDynamicContextException) {
+ return this;
+ } else {
+ throw e;
+ }
+ }
+ }
+
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ if (offer.action != PromotionOffer.UNORDERED) {
+ for (int i=0; i<argument.length; i++) {
+ argument[i] = doPromotion(argument[i], offer);
+ }
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Method supplied by each class of function to check arguments during parsing, when all
+ * the argument expressions have been read
+ * @param visitor the expression visitor
+ * @throws net.sf.saxon.trans.XPathException if the arguments are incorrect
+ */
+
+ protected abstract void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException;
+
+ /**
+ * Check number of arguments. <BR>
+ * A convenience routine for use in subclasses.
+ * @param min the minimum number of arguments allowed
+ * @param max the maximum number of arguments allowed
+ * @return the actual number of arguments
+ * @throws net.sf.saxon.trans.XPathException if the number of arguments is out of range
+ */
+
+ protected int checkArgumentCount(int min, int max) throws XPathException {
+ int numArgs = argument.length;
+ String msg = null;
+ if (min==max && numArgs != min) {
+ msg = "Function " + getDisplayName() + " must have " + min + pluralArguments(min);
+ } else if (numArgs < min) {
+ msg = "Function " + getDisplayName() + " must have at least " + min + pluralArguments(min);
+ } else if (numArgs > max) {
+ msg = "Function " + getDisplayName() + " must have no more than " + max + pluralArguments(max);
+ }
+ if (msg != null) {
+ XPathException err = new XPathException(msg, "XPST0017");
+ err.setIsStaticError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ return numArgs;
+ }
+
+ /**
+ * Utility routine used in constructing error messages: get the word "argument" or "arguments"
+ * @param num the number of arguments
+ * @return the singular or plural word
+ */
+
+ private static String pluralArguments(int num) {
+ if (num==1) return " argument";
+ return " arguments";
+ }
+
+ /**
+ * Get the immediate subexpressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+// try {
+ return Arrays.asList(argument).iterator();
+// } catch (NullPointerException err) {
+// // typically caused by doing CopyLocationInfo after creating the function
+// // but before creating its arguments
+// throw err;
+// }
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ for (int i=0; i<argument.length; i++) {
+ if (argument[i] == original) {
+ argument[i] = replacement;
+ found = true;
+ }
+ }
+ return found;
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodes the node in the PathMap representing the focus at the point where this expression
+ * is called. Set to null if this expression appears at the top level, in which case the expression, if it
+ * is registered in the path map at all, must create a new path map root.
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addExternalFunctionCallToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodes) {
+ // Except in the case of system functions, we have no idea where a function call might
+ // navigate, so we assume the worst, and register that the path has unknown dependencies
+ PathMap.PathMapNodeSet result = new PathMap.PathMapNodeSet();
+ for (Iterator iter = iterateSubExpressions(); iter.hasNext(); ) {
+ Expression child = (Expression)iter.next();
+ result.addNodeSet(child.addToPathMap(pathMap, pathMapNodes));
+ }
+ result.setHasUnknownDependencies();
+ return result;
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public final String getExpressionName() {
+ return getDisplayName();
+ }
+
+ /**
+ * Get the name of the function for display in messages
+ * @return the name of the function as a lexical QName
+ */
+
+ public final String getDisplayName() {
+ StructuredQName fName = getFunctionName();
+ return (fName == null ? "(anonymous)" : fName.getDisplayName());
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ FastStringBuffer buff = new FastStringBuffer(FastStringBuffer.SMALL);
+ StructuredQName fName = getFunctionName();
+ String f;
+ if (fName == null) {
+ f = "$anonymousFunction";
+ } else if (fName.isInNamespace(NamespaceConstant.FN)) {
+ f = fName.getLocalPart();
+ } else {
+ f = fName.getEQName();
+ }
+ buff.append(f);
+ Iterator iter = iterateSubExpressions();
+ boolean first = true;
+ while (iter.hasNext()) {
+ buff.append(first ? "(" : ", ");
+ buff.append(iter.next().toString());
+ first = false;
+ }
+ buff.append(first ? "()" : ")");
+ return buff.toString();
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("functionCall");
+ out.emitAttribute("name", getDisplayName());
+ for (Expression anArgument : argument) {
+ anArgument.explain(out);
+ }
+ out.endElement();
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+
+ public boolean equals(Object o) {
+ if (!(o instanceof FunctionCall)) {
+ return false;
+ }
+ FunctionCall f = (FunctionCall)o;
+ if (!getFunctionName().equals(f.getFunctionName())) {
+ return false;
+ }
+ if (getNumberOfArguments() != f.getNumberOfArguments()) {
+ return false;
+ }
+ for (int i=0; i<getNumberOfArguments(); i++) {
+ if (!argument[i].equals(f.argument[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get hashCode in support of equals() method
+ */
+
+ public int hashCode() {
+ int h = getFunctionName().hashCode();
+ for (int i=0; i<getNumberOfArguments(); i++) {
+ h ^= argument[i].hashCode();
+ }
+ return h;
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/GeneralComparison.java b/sf/saxon/expr/GeneralComparison.java
new file mode 100644
index 0000000..43966c3
--- /dev/null
+++ b/sf/saxon/expr/GeneralComparison.java
@@ -0,0 +1,878 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.GeneralComparisonCompiler;
+import com.saxonica.stream.adjunct.GeneralComparisonAdjunct;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.functions.Minimax;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+
+/**
+ * GeneralComparison: a boolean expression that compares two expressions
+ * for equals, not-equals, greater-than or less-than. This implements the operators
+ * =, !=, <, >, etc. This implementation is not used when in backwards-compatible mode
+ */
+
+public class GeneralComparison extends BinaryExpression implements ComparisonExpression, Callable {
+
+ public static final int ONE_TO_ONE = 0;
+ public static final int MANY_TO_ONE = 1;
+ public static final int MANY_TO_MANY = 2;
+ // Note, a one-to-many comparison is inverted into a many-to-one comparison
+
+ protected int singletonOperator;
+ protected AtomicComparer comparer;
+ protected boolean needsRuntimeCheck = true;
+ protected int comparisonCardinality = MANY_TO_MANY;
+
+
+ /**
+ * Create a relational expression identifying the two operands and the operator
+ *
+ * @param p0 the left-hand operand
+ * @param op the operator, as a token returned by the Tokenizer (e.g. Token.LT)
+ * @param p1 the right-hand operand
+ */
+
+ public GeneralComparison(Expression p0, int op, Expression p1) {
+ super(p0, op, p1);
+ singletonOperator = getCorrespondingSingletonOperator(op);
+ }
+
+ /**
+ * Ask whether a runtime check of the types of the operands is needed
+ */
+
+ public boolean needsRuntimeCheck() {
+ return needsRuntimeCheck;
+ }
+
+ /**
+ * Say whether a runtime check of the types of the operands is needed
+ */
+
+ public void setNeedsRuntimeCheck(boolean needsCheck) {
+ needsRuntimeCheck = needsCheck;
+ }
+
+ /**
+ * Ask whether the comparison is known to be many-to-one, one-to-one, or many-to-many.
+ * (Note, an expression that is one-to-many will be converted to one that is many-to-one).
+ */
+
+ public int getComparisonCardinality() {
+ return comparisonCardinality;
+ }
+
+ /**
+ * Say whether the comparison is known to be many-to-one, one-to-one, or many-to-many.
+ */
+ public void setComparisonCardinality(int card) {
+ comparisonCardinality = card;
+ }
+
+ /**
+ * Set the comparer to be used
+ *
+ * @param comparer the comparer to be used
+ */
+
+ public void setAtomicComparer(AtomicComparer comparer) {
+ this.comparer = comparer;
+ }
+
+ /*@NotNull*/
+ @Override
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ Expression e = super.simplify(visitor);
+ if (e != this) {
+ ExpressionTool.copyLocationInfo(this, e);
+ return e;
+ } else if (visitor.getStaticContext().isInBackwardsCompatibleMode()) {
+ Expression[] operands = getOperands();
+ GeneralComparison10 gc10 = new GeneralComparison10(operands[0], getOperator(), operands[1]);
+ gc10.setAtomicComparer(getAtomicComparer());
+ return gc10;
+ } else {
+ Expression[] operands = getOperands();
+ GeneralComparison20 gc20 = new GeneralComparison20(operands[0], getOperator(), operands[1]);
+ gc20.setAtomicComparer(getAtomicComparer());
+ return gc20;
+ }
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ *
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "GeneralComparison";
+ }
+
+ /**
+ * Get the AtomicComparer used to compare atomic values. This encapsulates any collation that is used
+ */
+
+ public AtomicComparer getAtomicComparer() {
+ return comparer;
+ }
+
+ /**
+ * Get the primitive (singleton) operator used: one of Token.FEQ, Token.FNE, Token.FLT, Token.FGT,
+ * Token.FLE, Token.FGE
+ */
+
+ public int getSingletonOperator() {
+ return singletonOperator;
+ }
+
+ /**
+ * Determine whether untyped atomic values should be converted to the type of the other operand
+ *
+ * @return true if untyped values should be converted to the type of the other operand, false if they
+ * should be converted to strings.
+ */
+
+ public boolean convertsUntypedToOther() {
+ return true;
+ }
+
+ /**
+ * Determine the static cardinality. Returns [1..1]
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Type-check the expression
+ *
+ * @return the checked expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+
+ Expression oldOp0 = operand0;
+ Expression oldOp1 = operand1;
+
+ operand0 = visitor.typeCheck(operand0, contextItemType);
+ operand1 = visitor.typeCheck(operand1, contextItemType);
+
+ // If either operand is statically empty, return false
+
+ if (Literal.isEmptySequence(operand0) || Literal.isEmptySequence(operand1)) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+
+ // Neither operand needs to be sorted
+
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ operand0 = ExpressionTool.unsorted(opt, operand0, false);
+ operand1 = ExpressionTool.unsorted(opt, operand1, false);
+
+ SequenceType atomicType = SequenceType.ATOMIC_SEQUENCE;
+
+ RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0);
+ //role0.setSourceLocator(this);
+ operand0 = TypeChecker.staticTypeCheck(operand0, atomicType, false, role0, visitor);
+
+ RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1);
+ //role1.setSourceLocator(this);
+ operand1 = TypeChecker.staticTypeCheck(operand1, atomicType, false, role1, visitor);
+
+ if (operand0 != oldOp0) {
+ adoptChildExpression(operand0);
+ }
+
+ if (operand1 != oldOp1) {
+ adoptChildExpression(operand1);
+ }
+
+ ItemType t0 = operand0.getItemType(th); // this is always an atomic type or empty-sequence()
+ ItemType t1 = operand1.getItemType(th); // this is always an atomic type or empty-sequence()
+
+ if (t0 instanceof ErrorType || t1 instanceof ErrorType) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+
+ if (((AtomicType) t0).isExternalType() || ((AtomicType) t1).isExternalType()) {
+ XPathException err = new XPathException("Cannot perform comparisons involving external objects");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ throw err;
+ }
+
+ BuiltInAtomicType pt0 = (BuiltInAtomicType) t0.getPrimitiveItemType();
+ BuiltInAtomicType pt1 = (BuiltInAtomicType) t1.getPrimitiveItemType();
+
+ int c0 = operand0.getCardinality();
+ int c1 = operand1.getCardinality();
+
+ if (c0 == StaticProperty.EMPTY || c1 == StaticProperty.EMPTY) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+
+ if (t0.equals(BuiltInAtomicType.ANY_ATOMIC) || t0.equals(BuiltInAtomicType.UNTYPED_ATOMIC) ||
+ t1.equals(BuiltInAtomicType.ANY_ATOMIC) || t1.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ // then no static type checking is possible
+ } else {
+
+ if (!Type.isPossiblyComparable(pt0, pt1, Token.isOrderedOperator(singletonOperator))) {
+ XPathException err = new XPathException("Cannot compare " +
+ t0.toString() + " to " + t1.toString());
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+
+ }
+
+ needsRuntimeCheck = !Type.isGuaranteedGenerallyComparable(pt0, pt1, Token.isOrderedOperator(singletonOperator));
+
+ if (c0 == StaticProperty.EXACTLY_ONE &&
+ c1 == StaticProperty.EXACTLY_ONE &&
+ !t0.equals(BuiltInAtomicType.ANY_ATOMIC) &&
+ !t1.equals(BuiltInAtomicType.ANY_ATOMIC)) {
+
+ // Use a value comparison if both arguments are singletons, and if the comparison operator to
+ // be used can be determined.
+
+ Expression e0 = operand0;
+ Expression e1 = operand1;
+
+ if (t0.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ if (t1.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ e0 = new CastExpression(operand0, BuiltInAtomicType.STRING, false);
+ adoptChildExpression(e0);
+ e1 = new CastExpression(operand1, BuiltInAtomicType.STRING, false);
+ adoptChildExpression(e1);
+ } else if (th.isSubType(t1, BuiltInAtomicType.NUMERIC)) {
+ e0 = new CastExpression(operand0, BuiltInAtomicType.DOUBLE, false);
+ adoptChildExpression(e0);
+ } else {
+ e0 = new CastExpression(operand0, pt1, false);
+ adoptChildExpression(e0);
+ }
+ } else if (t1.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ if (th.isSubType(t0, BuiltInAtomicType.NUMERIC)) {
+ e1 = new CastExpression(operand1, BuiltInAtomicType.DOUBLE, false);
+ adoptChildExpression(e1);
+ } else {
+ e1 = new CastExpression(operand1, pt0, false);
+ adoptChildExpression(e1);
+ }
+ }
+
+ ValueComparison vc = new ValueComparison(e0, singletonOperator, e1);
+ vc.setAtomicComparer(comparer);
+ ExpressionTool.copyLocationInfo(this, vc);
+ return visitor.typeCheck(visitor.simplify(vc), contextItemType);
+ }
+
+ StaticContext env = visitor.getStaticContext();
+ if (comparer == null) {
+ // In XSLT, only do this the first time through, otherwise default-collation may be missed
+ final String defaultCollationName = env.getDefaultCollationName();
+ StringCollator collation = env.getCollation(defaultCollationName);
+ if (collation == null) {
+ collation = CodepointCollator.getInstance();
+ }
+ comparer = GenericAtomicComparer.makeAtomicComparer(
+ pt0, pt1, collation, visitor.getConfiguration().getConversionContext());
+ }
+
+
+ // evaluate the expression now if both arguments are constant
+
+ if ((operand0 instanceof Literal) && (operand1 instanceof Literal)) {
+ return Literal.makeLiteral((AtomicValue) evaluateItem(env.makeEarlyEvaluationContext()));
+ }
+
+ return this;
+ }
+
+ private static Expression makeMinOrMax(Expression exp, String function) {
+ FunctionCall fn = SystemFunctionCall.makeSystemFunction(function, new Expression[]{exp});
+ ((Minimax) fn).setIgnoreNaN(true);
+ return fn;
+ }
+
+ /**
+ * Optimize the expression
+ *
+ * @return the checked expression
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ final StaticContext env = visitor.getStaticContext();
+ final Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+
+ operand0 = visitor.optimize(operand0, contextItemType);
+ operand1 = visitor.optimize(operand1, contextItemType);
+
+ // If either operand is statically empty, return false
+
+ if (Literal.isEmptySequence(operand0) || Literal.isEmptySequence(operand1)) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+
+ // Neither operand needs to be sorted
+
+ operand0 = ExpressionTool.unsorted(opt, operand0, false);
+ operand1 = ExpressionTool.unsorted(opt, operand1, false);
+
+ if (operand0 instanceof Literal && operand1 instanceof Literal) {
+ return Literal.makeLiteral(
+ SequenceTool.toGroundedValue(evaluateItem(visitor.getStaticContext().makeEarlyEvaluationContext())));
+ }
+
+ ItemType t0 = operand0.getItemType(th);
+ ItemType t1 = operand1.getItemType(th);
+
+ int c0 = operand0.getCardinality();
+ int c1 = operand1.getCardinality();
+
+ boolean checkTypes = (t0 == BuiltInAtomicType.ANY_ATOMIC ||
+ t1 == BuiltInAtomicType.ANY_ATOMIC ||
+ !t0.equals(t1));
+
+ // Check if neither argument allows a sequence of >1
+
+ boolean many0 = Cardinality.allowsMany(c0);
+ boolean many1 = Cardinality.allowsMany(c1);
+
+ if (many0) {
+ if (many1) {
+ comparisonCardinality = MANY_TO_MANY;
+ } else {
+ comparisonCardinality = MANY_TO_ONE;
+ }
+ } else {
+ if (many1) {
+ GeneralComparison mc = getInverseComparison();
+ mc.comparisonCardinality = MANY_TO_ONE;
+ ExpressionTool.copyLocationInfo(this, mc);
+ mc.comparer = comparer;
+ mc.needsRuntimeCheck = needsRuntimeCheck;
+ return visitor.optimize(mc, contextItemType);
+ } else {
+ comparisonCardinality = ONE_TO_ONE;
+ }
+ }
+
+ if (comparer == null) {
+ final String defaultCollationName = env.getDefaultCollationName();
+ StringCollator comp = env.getCollation(defaultCollationName);
+ if (comp == null) {
+ comp = CodepointCollator.getInstance();
+ }
+ BuiltInAtomicType pt0 = (BuiltInAtomicType) t0.getPrimitiveItemType();
+ BuiltInAtomicType pt1 = (BuiltInAtomicType) t1.getPrimitiveItemType();
+ comparer = GenericAtomicComparer.makeAtomicComparer(pt0, pt1, comp,
+ env.getConfiguration().getConversionContext());
+ }
+
+ // If one operand is numeric, then construct code
+ // to force the other operand to numeric
+
+ // TODO: shouldn't this happen during type checking?
+
+ Expression e0 = operand0;
+ Expression e1 = operand1;
+
+ boolean numeric0 = th.isSubType(t0, BuiltInAtomicType.NUMERIC);
+ boolean numeric1 = th.isSubType(t1, BuiltInAtomicType.NUMERIC);
+ if (numeric1 && !numeric0) {
+ RoleLocator role = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0);
+ //role.setSourceLocator(this);
+ e0 = TypeChecker.staticTypeCheck(e0, SequenceType.NUMERIC_SEQUENCE, false, role, visitor);
+ }
+
+ if (numeric0 && !numeric1) {
+ RoleLocator role = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1);
+ //role.setSourceLocator(this);
+ e1 = TypeChecker.staticTypeCheck(e1, SequenceType.NUMERIC_SEQUENCE, false, role, visitor);
+ }
+
+
+ // look for (N to M = I)
+ // First a variable range...
+
+ if (operand0 instanceof RangeExpression &&
+ th.isSubType(operand1.getItemType(th), BuiltInAtomicType.NUMERIC) &&
+ operator == Token.EQUALS &&
+ !Cardinality.allowsMany(operand1.getCardinality())) {
+ Expression min = ((RangeExpression) operand0).operand0;
+ Expression max = ((RangeExpression) operand0).operand1;
+ IntegerRangeTest ir = new IntegerRangeTest(operand1, min, max);
+ ExpressionTool.copyLocationInfo(this, ir);
+ return ir;
+ }
+
+ // Now a fixed range...
+
+ if (operand0 instanceof Literal) {
+ GroundedValue value0 = ((Literal) operand0).getValue();
+ if (value0 instanceof IntegerRange &&
+ th.isSubType(operand1.getItemType(th), BuiltInAtomicType.NUMERIC) &&
+ operator == Token.EQUALS &&
+ !Cardinality.allowsMany(operand1.getCardinality())) {
+ long min = ((IntegerRange) value0).getStart();
+ long max = ((IntegerRange) value0).getEnd();
+ IntegerRangeTest ir = new IntegerRangeTest(operand1,
+ Literal.makeLiteral(Int64Value.makeIntegerValue(min)),
+ Literal.makeLiteral(Int64Value.makeIntegerValue(max)));
+ ExpressionTool.copyLocationInfo(this, ir);
+ return ir;
+ }
+ }
+
+ // If the operator is gt, ge, lt, le then replace X < Y by min(X) < max(Y)
+
+ // This optimization is done only in the case where at least one of the
+ // sequences is known to be purely numeric. It isn't safe if both sequences
+ // contain untyped atomic values, because in that case, the type of the
+ // comparison isn't known in advance. For example [(1, U1) < ("fred", U2)]
+ // involves both string and numeric comparisons.
+
+ if (operator != Token.EQUALS && operator != Token.NE &&
+ (th.isSubType(t0, BuiltInAtomicType.NUMERIC) || th.isSubType(t1, BuiltInAtomicType.NUMERIC))) {
+
+ // System.err.println("** using minimax optimization **");
+ ValueComparison vc;
+ switch (operator) {
+ case Token.LT:
+ case Token.LE:
+ vc = new ValueComparison(makeMinOrMax(e0, "min"),
+ singletonOperator,
+ makeMinOrMax(e1, "max"));
+ vc.setResultWhenEmpty(BooleanValue.FALSE);
+ vc.setAtomicComparer(comparer);
+ break;
+ case Token.GT:
+ case Token.GE:
+ vc = new ValueComparison(makeMinOrMax(e0, "max"),
+ singletonOperator,
+ makeMinOrMax(e1, "min"));
+ vc.setResultWhenEmpty(BooleanValue.FALSE);
+ vc.setAtomicComparer(comparer);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown operator " + operator);
+ }
+
+ ExpressionTool.copyLocationInfo(this, vc);
+ return visitor.typeCheck(vc, contextItemType);
+ }
+
+
+ // evaluate the expression now if both arguments are constant
+
+ if ((operand0 instanceof Literal) && (operand1 instanceof Literal)) {
+ return Literal.makeLiteral((AtomicValue) evaluateItem(env.makeEarlyEvaluationContext()));
+ }
+
+ // Finally, convert to use the GeneralComparisonEE algorithm if in Saxon-EE
+ return visitor.getConfiguration().obtainOptimizer().optimizeGeneralComparison(this, false);
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/ public Expression copy() {
+ GeneralComparison gc = new GeneralComparison(operand0.copy(), operator, operand1.copy());
+ gc.comparer = comparer;
+ gc.singletonOperator = singletonOperator;
+ gc.needsRuntimeCheck = needsRuntimeCheck;
+ gc.comparisonCardinality = comparisonCardinality;
+ return gc;
+ }
+
+ /**
+ * Evaluate the expression in a given context
+ *
+ * @param context the given context for evaluation
+ * @return a BooleanValue representing the result of the numeric comparison of the two operands
+ */
+
+ /*@Nullable*/ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ switch (comparisonCardinality) {
+ case ONE_TO_ONE: {
+ AtomicValue value0 = (AtomicValue) operand0.evaluateItem(context);
+ AtomicValue value1 = (AtomicValue) operand1.evaluateItem(context);
+ return BooleanValue.get(evaluateOneToOne(value0, value1, context));
+ }
+ case MANY_TO_ONE: {
+ SequenceIterator iter0 = operand0.iterate(context);
+ AtomicValue value1 = (AtomicValue) operand1.evaluateItem(context);
+ return BooleanValue.get(evaluateManyToOne(iter0, value1, context));
+ }
+ case MANY_TO_MANY: {
+ SequenceIterator iter1 = operand0.iterate(context);
+ SequenceIterator iter2 = operand1.iterate(context);
+ return BooleanValue.get(evaluateManyToMany(iter1, iter2, context));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws XPathException if a dynamic error occurs during the evaluation of the expression
+ */
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ switch (comparisonCardinality) {
+ case ONE_TO_ONE: {
+ AtomicValue value0 = (AtomicValue) arguments[0].head();
+ AtomicValue value1 = (AtomicValue) arguments[1].head();
+ return BooleanValue.get(evaluateOneToOne(value0, value1, context));
+ }
+ case MANY_TO_ONE: {
+ SequenceIterator iter0 = arguments[0].iterate();
+ AtomicValue value1 = (AtomicValue) arguments[1].head();
+ return BooleanValue.get(evaluateManyToOne(iter0, value1, context));
+ }
+ case MANY_TO_MANY: {
+ SequenceIterator iter1 = arguments[0].iterate();
+ SequenceIterator iter2 = arguments[1].iterate();
+ return BooleanValue.get(evaluateManyToMany(iter1, iter2, context));
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Evaluate the expression in a boolean context
+ *
+ * @param context the given context for evaluation
+ * @return a boolean representing the result of the numeric comparison of the two operands
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ switch (comparisonCardinality) {
+ case ONE_TO_ONE: {
+ AtomicValue value0 = (AtomicValue) operand0.evaluateItem(context);
+ AtomicValue value1 = (AtomicValue) operand1.evaluateItem(context);
+ return evaluateOneToOne(value0, value1, context);
+ }
+ case MANY_TO_ONE: {
+ SequenceIterator iter0 = operand0.iterate(context);
+ AtomicValue value1 = (AtomicValue) operand1.evaluateItem(context);
+ return evaluateManyToOne(iter0, value1, context);
+ }
+ case MANY_TO_MANY: {
+ SequenceIterator iter1 = operand0.iterate(context);
+ SequenceIterator iter2 = operand1.iterate(context);
+ return evaluateManyToMany(iter1, iter2, context);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Evaluate a (zero-or-one)-to-(zero-or-one) comparison
+ * @param value0 the first value, or null if empty
+ * @param value1 the second value, or null if empty
+ * @param context dynamic evaluation context
+ * @return the comparison result
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ private boolean evaluateOneToOne(AtomicValue value0, AtomicValue value1, XPathContext context) throws XPathException {
+ try {
+ return !(value0 == null || value1 == null) &&
+ compare(value0, singletonOperator, value1, comparer, needsRuntimeCheck, context);
+ } catch (XPathException e) {
+ // re-throw the exception with location information added
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+
+ }
+
+ /**
+ * Evaluate a (zero-to-many)-to-(zero-or-one) comparison
+ * @param iter0 iterator over the first value
+ * @param value1 the second value, or null if empty
+ * @param context dynamic evaluation context
+ * @return the comparison result
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ private boolean evaluateManyToOne(SequenceIterator iter0, AtomicValue value1, XPathContext context) throws XPathException {
+ try {
+ if (value1 == null) {
+ return false;
+ }
+ AtomicValue item0;
+ while ((item0 = (AtomicValue) iter0.next()) != null) {
+ if (compare(item0, singletonOperator, value1, comparer, needsRuntimeCheck, context)) {
+ iter0.close();
+ return true;
+ }
+ }
+ return false;
+ } catch (XPathException e) {
+ // re-throw the exception with location information added
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+
+ }
+
+ /**
+ * Evaluate a (zero-or-one)-to-(zero-or-one) comparison
+ * @param iter0 iterator over the first value
+ * @param iter1 iterator the second value
+ * @param context dynamic evaluation context
+ * @return the comparison result
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public boolean evaluateManyToMany(SequenceIterator iter0, SequenceIterator iter1, XPathContext context) throws XPathException {
+ try {
+ boolean exhausted0 = false;
+ boolean exhausted1 = false;
+
+ List<AtomicValue> value0 = new ArrayList<AtomicValue>();
+ List<AtomicValue> value1 = new ArrayList<AtomicValue>();
+
+ // Read items from the two sequences alternately, in each case comparing the item to
+ // all items that have previously been read from the other sequence. In the worst case
+ // the number of comparisons is N*M, and the memory usage is (max(N,M)*2) where N and M
+ // are the number of items in the two sequences. In practice, either M or N is often 1,
+ // meaning that in this case neither list will ever hold more than one item.
+
+ while (true) {
+ if (!exhausted0) {
+ AtomicValue item0 = (AtomicValue) iter0.next();
+ if (item0 == null) {
+ if (exhausted1) {
+ return false;
+ }
+ exhausted0 = true;
+ } else {
+ for (AtomicValue item1 : value1) {
+ if (compare(item0, singletonOperator, item1, comparer, needsRuntimeCheck, context)) {
+ iter0.close();
+ iter1.close();
+ return true;
+ }
+ }
+ if (!exhausted1) {
+ value0.add(item0);
+ }
+ }
+ }
+ if (!exhausted1) {
+ AtomicValue item1 = (AtomicValue) iter1.next();
+ if (item1 == null) {
+ if (exhausted0) {
+ return false;
+ }
+ exhausted1 = true;
+ } else {
+ for (AtomicValue item0 : value0) {
+ if (compare(item0, singletonOperator, item1, comparer, needsRuntimeCheck, context)) {
+ iter0.close();
+ iter1.close();
+ return true;
+ }
+ }
+ if (!exhausted0) {
+ value1.add(item1);
+ }
+ }
+ }
+ }
+ } catch (XPathException e) {
+ // re-throw the exception with location information added
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+
+ }
+
+ /**
+ * Compare two atomic values
+ *
+ * @param a0 the first value
+ * @param operator the operator, for example {@link net.sf.saxon.expr.parser.Token#EQUALS}
+ * @param a1 the second value
+ * @param comparer the comparer to be used to perform the comparison
+ * @param checkTypes set to true if the operand types need to be checked for comparability at runtime
+ * @param context the XPath evaluation context @return true if the comparison succeeds
+ */
+
+ public static boolean compare(AtomicValue a0,
+ int operator,
+ AtomicValue a1,
+ AtomicComparer comparer,
+ boolean checkTypes,
+ XPathContext context) throws XPathException {
+
+ final ConversionRules rules = context.getConfiguration().getConversionRules();
+ boolean u0 = (a0 instanceof UntypedAtomicValue);
+ boolean u1 = (a1 instanceof UntypedAtomicValue);
+ if (u0 != u1) {
+ // one value untyped, the other not
+ if (u0) {
+ // a1 is untyped atomic
+ if (a1 instanceof NumericValue) {
+ a0 = Converter.convert(a0, BuiltInAtomicType.DOUBLE, rules).asAtomic();
+ } else {
+ a0 = Converter.convert(a0, a1.getPrimitiveType(), rules).asAtomic();
+ }
+ } else {
+ // a2 is untyped atomic
+ if (a0 instanceof NumericValue) {
+ a1 = Converter.convert(a1, BuiltInAtomicType.DOUBLE, rules).asAtomic();
+ } else {
+ a1 = Converter.convert(a1, a0.getPrimitiveType(), rules).asAtomic();
+ }
+ }
+ checkTypes = false; // No further checking needed if conversion succeeded
+ }
+ return ValueComparison.compare(a0, operator, a1, comparer.provideContext(context), checkTypes);
+
+ }
+
+ /**
+ * Determine the data type of the expression
+ *
+ * @param th the type hierarchy cache
+ * @return the value BuiltInAtomicType.BOOLEAN
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Return the singleton form of the comparison operator, e.g. FEQ for EQUALS, FGT for GT
+ *
+ * @param op the many-to-many form of the operator, for example {@link Token#LE}
+ * @return the corresponding singleton operator, for example {@link Token#FLE}
+ */
+
+ public static int getCorrespondingSingletonOperator(int op) {
+ switch (op) {
+ case Token.EQUALS:
+ return Token.FEQ;
+ case Token.GE:
+ return Token.FGE;
+ case Token.NE:
+ return Token.FNE;
+ case Token.LT:
+ return Token.FLT;
+ case Token.GT:
+ return Token.FGT;
+ case Token.LE:
+ return Token.FLE;
+ default:
+ return op;
+ }
+ }
+
+ protected GeneralComparison getInverseComparison() {
+ return new GeneralComparison(operand1, Token.inverse(operator), operand0);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the GeneralComparison expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new GeneralComparisonCompiler();
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public GeneralComparisonAdjunct getStreamingAdjunct() {
+ return new GeneralComparisonAdjunct();
+ }
+
+ //#endif
+
+// protected String displayOperator() {
+// return "many-to-many " + super.displayOperator();
+// }
+
+ protected void explainExtraAttributes(ExpressionPresenter out) {
+ String cc = "";
+ switch (comparisonCardinality) {
+ case ONE_TO_ONE:
+ cc = "one-to-one";
+ break;
+ case MANY_TO_ONE:
+ cc = "many-to-one";
+ break;
+ case MANY_TO_MANY:
+ cc = "many-to-many";
+ break;
+ }
+ out.emitAttribute("cardinality", cc);
+ }
+
+}
+
diff --git a/sf/saxon/expr/GeneralComparison10.java b/sf/saxon/expr/GeneralComparison10.java
new file mode 100644
index 0000000..c32969c
--- /dev/null
+++ b/sf/saxon/expr/GeneralComparison10.java
@@ -0,0 +1,442 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.functions.NumberFn;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.DoubleValue;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * GeneralComparison10: a boolean expression that compares two expressions
+ * for equals, not-equals, greater-than or less-than. This implements the operators
+ * =, !=, <, >, etc. This version of the class implements general comparisons
+ * in XPath 1.0 backwards compatibility mode, as defined in the Oct 2004 revision
+ * of the specifications.
+*/
+
+public class GeneralComparison10 extends BinaryExpression implements Callable {
+
+ protected int singletonOperator;
+ protected AtomicComparer comparer;
+ private boolean atomize0 = true;
+ private boolean atomize1 = true;
+ private boolean maybeBoolean0 = true;
+ private boolean maybeBoolean1 = true;
+
+ /**
+ * Create a general comparison identifying the two operands and the operator
+ * @param p0 the left-hand operand
+ * @param op the operator, as a token returned by the Tokenizer (e.g. Token.LT)
+ * @param p1 the right-hand operand
+ */
+
+ public GeneralComparison10(Expression p0, int op, Expression p1) {
+ super(p0, op, p1);
+ singletonOperator = GeneralComparison.getCorrespondingSingletonOperator(op);
+ }
+
+ /**
+ * Determine the static cardinality. Returns [1..1]
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Type-check the expression
+ * @return the checked expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ operand0 = visitor.typeCheck(operand0, contextItemType);
+ operand1 = visitor.typeCheck(operand1, contextItemType);
+
+ StaticContext env = visitor.getStaticContext();
+ StringCollator comp = env.getCollation(env.getDefaultCollationName());
+ if (comp==null) {
+ comp = CodepointCollator.getInstance();
+ }
+
+ XPathContext context = env.makeEarlyEvaluationContext();
+ comparer = new GenericAtomicComparer(comp, context);
+
+ // evaluate the expression now if both arguments are constant
+
+ if ((operand0 instanceof Literal) && (operand1 instanceof Literal)) {
+ return Literal.makeLiteral((AtomicValue)evaluateItem(context));
+ }
+
+ return this;
+ }
+
+ public void setAtomicComparer(AtomicComparer comparer) {
+ this.comparer = comparer;
+ }
+
+ /**
+ * Optimize the expression
+ * @return the checked expression
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ StaticContext env = visitor.getStaticContext();
+
+ operand0 = visitor.optimize(operand0, contextItemType);
+ operand1 = visitor.optimize(operand1, contextItemType);
+
+ // Neither operand needs to be sorted
+
+ operand0 = ExpressionTool.unsorted(opt, operand0, false);
+ operand1 = ExpressionTool.unsorted(opt, operand1, false);
+
+ // evaluate the expression now if both arguments are constant
+
+ if ((operand0 instanceof Literal) && (operand1 instanceof Literal)) {
+ return Literal.makeLiteral(
+ (AtomicValue)evaluateItem(env.makeEarlyEvaluationContext()));
+ }
+
+ final TypeHierarchy th = env.getConfiguration().getTypeHierarchy();
+ ItemType type0 = operand0.getItemType(th);
+ ItemType type1 = operand1.getItemType(th);
+
+ if (type0.isPlainType()) {
+ atomize0 = false;
+ }
+ if (type1.isPlainType()) {
+ atomize1 = false;
+ }
+
+ if (th.relationship(type0, BuiltInAtomicType.BOOLEAN) == TypeHierarchy.DISJOINT) {
+ maybeBoolean0 = false;
+ }
+ if (th.relationship(type1, BuiltInAtomicType.BOOLEAN) == TypeHierarchy.DISJOINT) {
+ maybeBoolean1 = false;
+ }
+
+ if (!maybeBoolean0 && !maybeBoolean1) {
+ int n0 = th.relationship(type0, BuiltInAtomicType.NUMERIC);
+ int n1 = th.relationship(type1, BuiltInAtomicType.NUMERIC);
+ boolean maybeNumeric0 = (n0 != TypeHierarchy.DISJOINT);
+ boolean maybeNumeric1 = (n1 != TypeHierarchy.DISJOINT);
+ boolean numeric0 = (n0 == TypeHierarchy.SUBSUMED_BY || n0 == TypeHierarchy.SAME_TYPE);
+ boolean numeric1 = (n1 == TypeHierarchy.SUBSUMED_BY || n1 == TypeHierarchy.SAME_TYPE);
+ // Use the 2.0 path if we don't have to deal with the possibility of boolean values,
+ // or the complications of converting values to numbers
+ if (operator == Token.EQUALS || operator == Token.NE) {
+ if ((!maybeNumeric0 && !maybeNumeric1) || (numeric0 && numeric1)) {
+ GeneralComparison gc = new GeneralComparison20(operand0, operator, operand1);
+ BinaryExpression binExp = visitor.getConfiguration().obtainOptimizer().optimizeGeneralComparison(gc, false);
+ ExpressionTool.copyLocationInfo(this, binExp);
+ return visitor.optimize(visitor.typeCheck(binExp, contextItemType), contextItemType);
+ }
+ } else if (numeric0 && numeric1) {
+ GeneralComparison gc = new GeneralComparison20(operand0, operator, operand1);
+ BinaryExpression binExp = visitor.getConfiguration().obtainOptimizer().optimizeGeneralComparison(gc, false);
+ ExpressionTool.copyLocationInfo(this, binExp);
+ return visitor.optimize(visitor.typeCheck(binExp, contextItemType), contextItemType);
+ }
+ }
+
+ return this;
+ }
+
+
+
+ /**
+ * Evaluate the expression in a given context
+ * @param context the given context for evaluation
+ * @return a BooleanValue representing the result of the numeric comparison of the two operands
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Evaluate the expression: interface for use by compiled bytecode
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws XPathException if a dynamic error occurs during the evaluation of the expression
+ */
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(arguments[0].iterate(), arguments[1].iterate(), context));
+ }
+
+ /**
+ * Evaluate the expression giving a boolean result
+ * @param context the given context for evaluation
+ * @return a boolean representing the result of the comparison of the two operands
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ return effectiveBooleanValue(operand0.iterate(context), operand1.iterate(context), context);
+ }
+
+ /**
+ * Evaluate the expression giving a boolean result
+ * @param iter0 iterator over the value of the first argument
+ * @param iter1 iterator over the value of the second argument
+ * @param context the given context for evaluation
+ * @return a boolean representing the result of the comparison of the two operands
+ */
+
+ private boolean effectiveBooleanValue(SequenceIterator iter0, SequenceIterator iter1, XPathContext context)
+ throws XPathException {
+
+ // If the first operand is a singleton boolean,
+ // compare it with the effective boolean value of the other operand
+
+ boolean iter0used = false;
+ boolean iter1used = false;
+
+ if (maybeBoolean0) {
+ iter0used = true;
+ Item i01 = iter0.next();
+ Item i02 = (i01 == null ? null : iter0.next());
+ if (i01 instanceof BooleanValue && i02 == null) {
+ iter0.close();
+ boolean b = ExpressionTool.effectiveBooleanValue(iter1);
+ return compare((BooleanValue)i01, singletonOperator, BooleanValue.get(b), comparer, context);
+ }
+ if (i01 == null && !maybeBoolean1) {
+ iter0.close();
+ return false;
+ }
+ }
+
+ // If the second operand is a singleton boolean,
+ // compare it with the effective boolean value of the other operand
+
+ if (maybeBoolean1) {
+ iter1used = true;
+ Item i11 = iter1.next();
+ Item i12 = (i11 == null ? null : iter1.next());
+ if (i11 instanceof BooleanValue && i12 == null) {
+ iter1.close();
+ if (iter0used) {
+ iter0 = iter0.getAnother();
+ }
+ boolean b = ExpressionTool.effectiveBooleanValue(iter0);
+ return compare(BooleanValue.get(b), singletonOperator, (BooleanValue)i11, comparer, context);
+ }
+ if (i11 == null && !maybeBoolean0) {
+ iter1.close();
+ return false;
+ }
+ }
+
+ // Atomize both operands where necessary
+
+ if (iter0used) {
+ iter0 = iter0.getAnother();
+ }
+
+ if (iter1used) {
+ iter1 = iter1.getAnother();
+ }
+
+ if (atomize0) {
+ iter0 = Atomizer.getAtomizingIterator(iter0, false);
+ }
+
+ if (atomize1) {
+ iter1 = Atomizer.getAtomizingIterator(iter1, false);
+ }
+
+ // If the operator is one of <, >, <=, >=, then convert both operands to sequences of xs:double
+ // using the number() function
+
+ if (operator == Token.LT || operator == Token.LE || operator == Token.GT || operator == Token.GE) {
+ final Configuration config = context.getConfiguration();
+ ItemMappingFunction map = new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ return NumberFn.convert((AtomicValue)item, config);
+ }
+ };
+ iter0 = new ItemMappingIterator(iter0, map, true);
+ iter1 = new ItemMappingIterator(iter1, map, true);
+ }
+
+ // Compare all pairs of atomic values in the two atomized sequences
+
+ List seq1 = null;
+ while (true) {
+ AtomicValue item0 = (AtomicValue)iter0.next();
+ if (item0 == null) {
+ return false;
+ }
+ if (iter1 != null) {
+ while (true) {
+ AtomicValue item1 = (AtomicValue)iter1.next();
+ if (item1 == null) {
+ iter1 = null;
+ if (seq1 == null) {
+ // second operand is empty
+ return false;
+ }
+ break;
+ }
+ try {
+ if (compare(item0, singletonOperator, item1, comparer, context)) {
+ return true;
+ }
+ if (seq1 == null) {
+ seq1 = new ArrayList(40);
+ }
+ seq1.add(item1);
+ } catch (XPathException e) {
+ // re-throw the exception with location information added
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+ } else {
+ //assert seq1 != null;
+ Iterator listIter1 = seq1.iterator();
+ while (listIter1.hasNext()) {
+ AtomicValue item1 = (AtomicValue)listIter1.next();
+ if (compare(item0, singletonOperator, item1, comparer, context)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ GeneralComparison10 gc = new GeneralComparison10(operand0.copy(), operator, operand1.copy());
+ gc.comparer = comparer;
+ gc.atomize0 = atomize0;
+ gc.atomize1 = atomize1;
+ gc.maybeBoolean0 = maybeBoolean0;
+ gc.maybeBoolean1 = maybeBoolean1;
+ return gc;
+ }
+
+ /**
+ * Compare two atomic values
+ * @param a0 the first value to be compared
+ * @param operator the comparison operator
+ * @param a1 the second value to be compared
+ * @param comparer the comparer to be used (perhaps including a collation)
+ * @param context the XPath dynamic context
+ * @return the result of the comparison
+ */
+
+ private static boolean compare(AtomicValue a0,
+ int operator,
+ AtomicValue a1,
+ AtomicComparer comparer,
+ XPathContext context) throws XPathException {
+
+ comparer = comparer.provideContext(context);
+ ConversionRules rules = context.getConfiguration().getConversionRules();
+
+ BuiltInAtomicType t0 = a0.getPrimitiveType();
+ BuiltInAtomicType t1 = a1.getPrimitiveType();
+
+ // If either operand is a number, convert both operands to xs:double using
+ // the rules of the number() function, and compare them
+
+ if (t0.isPrimitiveNumeric() || t1.isPrimitiveNumeric()) {
+ DoubleValue v0 = NumberFn.convert(a0, context.getConfiguration());
+ DoubleValue v1 = NumberFn.convert(a1, context.getConfiguration());
+ return ValueComparison.compare(v0, operator, v1, comparer, false);
+ }
+
+ // If either operand is a string, or if both are untyped atomic, convert
+ // both operands to strings and compare them
+
+ if (t0.equals(BuiltInAtomicType.STRING) || t1.equals(BuiltInAtomicType.STRING) ||
+ (t0.equals(BuiltInAtomicType.UNTYPED_ATOMIC) && t1.equals(BuiltInAtomicType.UNTYPED_ATOMIC))) {
+ StringValue s0 = StringValue.makeStringValue(a0.getStringValueCS());
+ StringValue s1 = StringValue.makeStringValue(a1.getStringValueCS());
+ return ValueComparison.compare(s0, operator, s1, comparer, false);
+ }
+
+ // If either operand is untyped atomic,
+ // convert it to the type of the other operand, and compare
+
+ if (t0.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ a0 = rules.getStringConverter(t1).convert(a0).asAtomic();
+ }
+
+ if (t1.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ a1 = rules.getStringConverter(t0).convert(a1).asAtomic();
+ }
+
+ return ValueComparison.compare(a0, operator, a1, comparer, false);
+ }
+
+ /**
+ * Determine the data type of the expression
+ * @return Type.BOOLEAN
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+
+
+// protected String displayOperator() {
+// return "many-to-many (1.0) " + super.displayOperator();
+// }
+
+ protected void explainExtraAttributes(ExpressionPresenter out) {
+ out.emitAttribute("cardinality", "many-to-many (1.0)");
+ }
+
+}
+
diff --git a/sf/saxon/expr/GeneralComparison20.java b/sf/saxon/expr/GeneralComparison20.java
new file mode 100644
index 0000000..ed33e3b
--- /dev/null
+++ b/sf/saxon/expr/GeneralComparison20.java
@@ -0,0 +1,65 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.GeneralComparisonCompiler;
+import net.sf.saxon.expr.parser.Token;
+
+/**
+ * The class GeneralComparison20 specializes GeneralComparison for the case where
+ * the comparison is done with 2.0 semantics (i.e. with backwards compatibility off).
+ * It differs from the superclass in that it will never turn the expression into
+ * a GeneralComparison10, which could lead to non-terminating optimizations
+ */
+public class GeneralComparison20 extends GeneralComparison {
+
+ /**
+ * Create a relational expression identifying the two operands and the operator
+ *
+ * @param p0 the left-hand operand
+ * @param op the operator, as a token returned by the Tokenizer (e.g. Token.LT)
+ * @param p1 the right-hand operand
+ */
+ public GeneralComparison20(Expression p0, int op, Expression p1) {
+ super(p0, op, p1);
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ GeneralComparison20 gc = new GeneralComparison20(operand0.copy(), operator, operand1.copy());
+ gc.comparer = comparer;
+ gc.singletonOperator = singletonOperator;
+ gc.needsRuntimeCheck = needsRuntimeCheck;
+ gc.comparisonCardinality = comparisonCardinality;
+ return gc;
+ }
+
+ protected GeneralComparison getInverseComparison() {
+ return new GeneralComparison20(operand1, Token.inverse(operator), operand0);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the GeneralComparison20 expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new GeneralComparisonCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/GroupVariableReference.java b/sf/saxon/expr/GroupVariableReference.java
new file mode 100644
index 0000000..29b4be1
--- /dev/null
+++ b/sf/saxon/expr/GroupVariableReference.java
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.stream.adjunct.GroupVariableReferenceAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.instruct.ForEachGroup;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.List;
+
+/**
+ * This is a variant of LocalVariableReference used when the variable in question represents the group
+ * bound by the bind-group attribute of xsl:for-each-group. It differs in that the streaming behavior is
+ * different
+ */
+public class GroupVariableReference extends LocalVariableReference {
+
+ private ForEachGroup controllingExpression;
+
+ public GroupVariableReference() {
+ super();
+ }
+
+ public GroupVariableReference(Binding binding) {
+ super(binding);
+ }
+
+ @Override
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return super.optimize(visitor, contextItemType); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ /**
+ * Create a clone copy of this VariableReference
+ * @return the cloned copy
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ if (binding == null) {
+ throw new UnsupportedOperationException("Cannot copy a variable reference whose binding is unknown");
+ }
+ GroupVariableReference ref = new GroupVariableReference();
+ ref.binding = binding;
+ ref.staticType = staticType;
+ ref.slotNumber = slotNumber;
+ ref.constantValue = constantValue;
+ ref.displayName = displayName;
+ ref.controllingExpression = controllingExpression;
+ binding.addReference(isInLoop());
+ ExpressionTool.copyLocationInfo(this, ref);
+ return ref;
+ }
+
+ public void setControllingExpression(ForEachGroup feg) {
+ this.controllingExpression = feg;
+ resetLocalStaticProperties();
+ }
+
+ public ForEachGroup getControllingExpression() {
+ return controllingExpression;
+ }
+
+ /**
+ * Determine the special properties of this expression. The properties such as document-ordering are the same as
+ * the properties of the grouping population as a whole.
+ *
+ * @return {@link net.sf.saxon.expr.StaticProperty#NON_CREATIVE} (unless the variable is assignable using saxon:assign)
+ */
+ @Override
+ public int computeSpecialProperties() {
+ if (controllingExpression == null) {
+ return 0;
+ } else {
+ return controllingExpression.getSelectExpression().getSpecialProperties();
+ }
+ }
+
+ /**
+ * Get the static cardinality
+ */
+ @Override
+ public int computeCardinality() {
+ return StaticProperty.ALLOWS_ONE_OR_MORE;
+ }
+
+ //#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return W3C_GROUP_CONSUMING;
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public GroupVariableReferenceAdjunct getStreamingAdjunct() {
+ return new GroupVariableReferenceAdjunct();
+ }
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ return null;
+ }
+
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/HomogeneityChecker.java b/sf/saxon/expr/HomogeneityChecker.java
new file mode 100644
index 0000000..c44a6ee
--- /dev/null
+++ b/sf/saxon/expr/HomogeneityChecker.java
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.HomogeneityCheckerCompiler;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.sort.DocumentSorter;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.HomogeneityCheckerIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+/**
+ * This class is an expression that does a run-time check of the result of a "/" expression
+ * to ensure that (a) the results consists entirely of atomic values and function items, or entirely of nodes,
+ * and (b) if the results are nodes, then they are deduplicated and sorted into document order.
+ */
+public class HomogeneityChecker extends UnaryExpression {
+
+ public HomogeneityChecker(Expression base) {
+ super(base);
+ }
+
+ /**
+ * Type-check the expression. Default implementation for unary operators that accept
+ * any kind of operand
+ */
+ /*@NotNull*/
+ @Override
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (getBaseExpression() instanceof HomogeneityChecker) {
+ return visitor.typeCheck(getBaseExpression(), contextItemType);
+ }
+ Expression e = super.typeCheck(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType type = getBaseExpression().getItemType(th);
+ int rel = th.relationship(type, AnyNodeTest.getInstance());
+ if (rel == TypeHierarchy.DISJOINT) {
+ // expression cannot return nodes, so this checker is redundant
+ if(getBaseExpression() instanceof SlashExpression && ((SlashExpression)getBaseExpression()).getLeadingSteps() instanceof SlashExpression &&
+ (((SlashExpression)getBaseExpression()).getLeadingSteps().getSpecialProperties() & StaticProperty.ORDERED_NODESET) == 0){
+ DocumentSorter ds = new DocumentSorter(((SlashExpression)getBaseExpression()).getLeadingSteps());
+ SlashExpression se = new SlashExpression(ds, ((SlashExpression)getBaseExpression()).getLastStep());
+ ExpressionTool.copyLocationInfo(this, se);
+ return se;
+ } else {
+ return getBaseExpression();
+ }
+ } else if (rel == TypeHierarchy.SAME_TYPE || rel == TypeHierarchy.SUBSUMED_BY) {
+ // expression always returns nodes, so replace this expression with a DocumentSorter
+ DocumentSorter ds = new DocumentSorter(getBaseExpression());
+ ExpressionTool.copyLocationInfo(this, ds);
+ return ds;
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ @Override
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (getBaseExpression() instanceof HomogeneityChecker) {
+ return visitor.optimize(getBaseExpression(), contextItemType);
+ }
+ return super.optimize(visitor, contextItemType); //To change body of overridden methods use File | Settings | File Templates.
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+ /*@NotNull*/
+ @Override
+ public Expression copy() {
+ return new HomogeneityChecker(getBaseExpression().copy());
+ }
+
+ /**
+ * Iterate the path-expression in a given context
+ * @param context the evaluation context
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(final XPathContext context) throws XPathException {
+
+ // This class delivers the result of the path expression in unsorted order,
+ // without removal of duplicates. If sorting and deduplication are needed,
+ // this is achieved by wrapping the path expression in a DocumentSorter
+
+ SequenceIterator base = getBaseExpression().iterate(context);
+ return new HomogeneityCheckerIterator(base, this);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the HomogeneityChecker expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new HomogeneityCheckerCompiler();
+ }
+//#endif
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ *
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+ @Override
+ public String getExpressionName() {
+ return "homogeneityChecker";
+ }
+}
+
diff --git a/sf/saxon/expr/IdentityComparison.java b/sf/saxon/expr/IdentityComparison.java
new file mode 100644
index 0000000..46d3cab
--- /dev/null
+++ b/sf/saxon/expr/IdentityComparison.java
@@ -0,0 +1,254 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.IdentityComparisonCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.expr.sort.GlobalOrderComparer;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.List;
+
+
+/**
+* IdentityComparison: a boolean expression that compares two nodes
+* for equals, not-equals, greater-than or less-than based on identity and
+* document ordering
+*/
+
+public final class IdentityComparison extends BinaryExpression {
+
+ private boolean generateIdEmulation = false;
+ // this flag is set if an "X is Y" or "X isnot Y" comparison is being used
+ // to emulate generate-id(X) = / != generate-id(Y). The handling of an empty
+ // sequence in the two cases is different.
+
+ /**
+ * Create an identity comparison identifying the two operands and the operator
+ * @param p1 the left-hand operand
+ * @param op the operator, as a token returned by the Tokenizer (e.g. Token.LT)
+ * @param p2 the right-hand operand
+ */
+
+ public IdentityComparison(Expression p1, int op, Expression p2) {
+ super(p1, op, p2);
+ }
+
+ /**
+ * Set flag to indicate different empty-sequence behavior when emulating
+ * comparison of two generate-id's. This is relevant when operands evaluate
+ * to an empty sequence; if both are empty, the "is" operator returns (), while the
+ * generate-id() comparison compares two empty strings, which returns true; if one is
+ * empty, the "is" operator returns (), while the generate-id() comparison returns false.
+ * @param flag true if this function is being used to compare generate-id() output
+ */
+
+ public void setGenerateIdEmulation(boolean flag) {
+ generateIdEmulation = flag;
+ }
+
+ /**
+ * Test the flag that indicates different empty-sequence behavior when emulating
+ * comparison of two generate-id's
+ * @return true if this function is being used to compare generate-id() output
+ */
+
+ public boolean isGenerateIdEmulation() {
+ return generateIdEmulation;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ operand0 = visitor.typeCheck(operand0, contextItemType);
+ operand1 = visitor.typeCheck(operand1, contextItemType);
+
+ if (!generateIdEmulation) {
+ if (Literal.isEmptySequence(operand0) || Literal.isEmptySequence(operand1)) {
+ return Literal.makeEmptySequence();
+ }
+ }
+
+ RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0);
+ //role0.setSourceLocator(this);
+ operand0 = TypeChecker.staticTypeCheck(
+ operand0, SequenceType.OPTIONAL_NODE, false, role0, visitor);
+
+ RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1);
+ //role1.setSourceLocator(this);
+ operand1 = TypeChecker.staticTypeCheck(
+ operand1, SequenceType.OPTIONAL_NODE, false, role1, visitor);
+
+ if (!Cardinality.allowsZero(operand0.getCardinality()) && !Cardinality.allowsZero(operand1.getCardinality())) {
+ generateIdEmulation = false;
+ // because the flag only makes a difference if one of the operands evaluates to ()
+ }
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression r = super.optimize(visitor, contextItemType);
+ if (r != this) {
+ if (!generateIdEmulation) {
+ if (Literal.isEmptySequence(operand0) || Literal.isEmptySequence(operand1)) {
+ return Literal.makeEmptySequence();
+ }
+ }
+ }
+ return r;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ IdentityComparison ic = new IdentityComparison(operand0.copy(), operator, operand1.copy());
+ ic.generateIdEmulation = generateIdEmulation;
+ return ic;
+ }
+
+//#ifdefined STREAM
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return super.getStreamability(syntacticContext, allowExtensions, reasons); //To change body of overridden methods use File | Settings | File Templates.
+ }
+//#endif
+
+ /**
+ * Evaluate the expression
+ */
+
+ /*@Nullable*/ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ NodeInfo node0 = getNode(operand0, context);
+ if (node0==null) {
+ if (generateIdEmulation) {
+ return BooleanValue.get(getNode(operand1, context)==null);
+ }
+ return null;
+ }
+
+ NodeInfo node1 = getNode(operand1, context);
+ if (node1==null) {
+ if (generateIdEmulation) {
+ return BooleanValue.FALSE;
+ }
+ return null;
+ }
+
+ return BooleanValue.get(compareIdentity(node0, node1));
+ }
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ NodeInfo node0 = getNode(operand0, context);
+ if (node0==null) {
+ return generateIdEmulation && getNode(operand1, context) == null;
+ }
+
+ NodeInfo node1 = getNode(operand1, context);
+ return node1 != null && compareIdentity(node0, node1);
+
+ }
+
+ private boolean compareIdentity(NodeInfo node0, NodeInfo node1) {
+
+ switch (operator) {
+ case Token.IS:
+ return node0.isSameNodeInfo(node1);
+ case Token.PRECEDES:
+ return GlobalOrderComparer.getInstance().compare(node0, node1) < 0;
+ case Token.FOLLOWS:
+ return GlobalOrderComparer.getInstance().compare(node0, node1) > 0;
+ default:
+ throw new UnsupportedOperationException("Unknown node identity test");
+ }
+ }
+
+ private static NodeInfo getNode(Expression exp, XPathContext c) throws XPathException {
+ return (NodeInfo)exp.evaluateItem(c);
+ }
+
+
+ /**
+ * Determine the data type of the expression
+ * @return Type.BOOLEAN
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the IdentityComparison expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new IdentityComparisonCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/InstanceOfExpression.java b/sf/saxon/expr/InstanceOfExpression.java
new file mode 100644
index 0000000..3117411
--- /dev/null
+++ b/sf/saxon/expr/InstanceOfExpression.java
@@ -0,0 +1,273 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.InstanceOfCompiler;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.List;
+
+/**
+* InstanceOf Expression: implements "Expr instance of data-type"
+*/
+
+public final class InstanceOfExpression extends UnaryExpression {
+
+ ItemType targetType;
+ int targetCardinality;
+
+ /**
+ * Construct an "instance of" expression in the form "source instance of target"
+ * @param source the expression whose type is to be tested
+ * @param target the type against which it is tested
+ */
+
+ public InstanceOfExpression(Expression source, SequenceType target) {
+ super(source);
+ targetType = target.getPrimaryType();
+ if (targetType == null) {
+ throw new IllegalArgumentException("Primary item type must not be null");
+ }
+ targetCardinality = target.getCardinality();
+ }
+
+ /**
+ * Get the item type that we are testing for membership of
+ * @return the item type
+ */
+
+ public ItemType getRequiredItemType() {
+ return targetType;
+ }
+
+ /**
+ * Get the cardinality that we are testing for membership of
+ * @return the required cardinality
+ */
+
+ public int getRequiredCardinality() {
+ return targetCardinality;
+ }
+
+ /**
+ * Type-check the expression
+ * @return the checked expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ if (operand instanceof Literal) {
+ Literal lit = Literal.makeLiteral(
+ evaluateItem(visitor.getStaticContext().makeEarlyEvaluationContext()));
+ ExpressionTool.copyLocationInfo(this, lit);
+ return lit;
+ }
+
+ // See if we can get the answer by static analysis.
+
+ if (Cardinality.subsumes(targetCardinality, operand.getCardinality())) {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ int relation = th.relationship(operand.getItemType(th), targetType);
+ if (relation == TypeHierarchy.SAME_TYPE || relation == TypeHierarchy.SUBSUMED_BY) {
+ Literal lit = Literal.makeLiteral(BooleanValue.TRUE);
+ ExpressionTool.copyLocationInfo(this, lit);
+ return lit;
+ } else if (relation == TypeHierarchy.DISJOINT) {
+ // if the item types are disjoint, the result might still be true if both sequences are empty
+ if (!Cardinality.allowsZero(targetCardinality) || !Cardinality.allowsZero(operand.getCardinality())) {
+ Literal lit = Literal.makeLiteral(BooleanValue.FALSE);
+ ExpressionTool.copyLocationInfo(this, lit);
+ return lit;
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+ if (Cardinality.subsumes(targetCardinality, operand.getCardinality())) {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ int relation = th.relationship(operand.getItemType(th), targetType);
+ if (relation == TypeHierarchy.SAME_TYPE || relation == TypeHierarchy.SUBSUMED_BY) {
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ } else if (relation == TypeHierarchy.DISJOINT) {
+ // if the item types are disjoint, the result might still be true if both sequences are empty
+ if (!Cardinality.allowsZero(targetCardinality) || !Cardinality.allowsZero(operand.getCardinality())) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+ }
+ }
+ return this;
+ }
+
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(INSPECTION_CONTEXT, allowExtensions, reasons);
+ }
+//#endif
+
+
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return super.equals(other) &&
+ targetType == ((InstanceOfExpression)other).targetType &&
+ targetCardinality == ((InstanceOfExpression)other).targetCardinality;
+ }
+
+ /**
+ * get HashCode for comparing two expressions. Note that this hashcode gives the same
+ * result for (A op B) and for (B op A), whether or not the operator is commutative.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ targetType.hashCode() ^ targetCardinality;
+ }
+
+ /**
+ * Determine the cardinality
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new InstanceOfExpression(getBaseExpression().copy(),
+ SequenceType.makeSequenceType(targetType, targetCardinality));
+ }
+
+ /**
+ * Determine the data type of the result of the InstanceOf expression
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Evaluate the expression as a boolean
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ SequenceIterator iter = operand.iterate(context);
+ return isInstance(iter, context);
+ }
+
+ private boolean isInstance(SequenceIterator iter, XPathContext context) throws XPathException {
+ int count = 0;
+ while (true) {
+ Item item = iter.next();
+ if (item == null) break;
+ count++;
+ if (!targetType.matchesItem(item, false, context.getConfiguration())) {
+ iter.close();
+ return false;
+ }
+ if (count==2 && !Cardinality.allowsMany(targetCardinality)) {
+ iter.close();
+ return false;
+ }
+ }
+ return !(count == 0 && ((targetCardinality & StaticProperty.ALLOWS_ZERO) == 0));
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the InstanceOf expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new InstanceOfCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("instance");
+ destination.emitAttribute("of", targetType.toString());
+ destination.emitAttribute("occurs", Cardinality.getOccurrenceIndicator(targetCardinality));
+ operand.explain(destination);
+ destination.endElement();
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+ @Override
+ public String toString() {
+ String occ = Cardinality.getOccurrenceIndicator(targetCardinality);
+ return "(" + operand.toString() + " instance of " +
+ targetType.toString() + occ + ")";
+ }
+}
+
diff --git a/sf/saxon/expr/IntegerRangeTest.java b/sf/saxon/expr/IntegerRangeTest.java
new file mode 100644
index 0000000..9246fb6
--- /dev/null
+++ b/sf/saxon/expr/IntegerRangeTest.java
@@ -0,0 +1,261 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.IntegerRangeTestCompiler;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.NumericValue;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+* An IntegerRangeTest is an expression of the form
+ * E = N to M
+ * where E is numeric, and N and M are both expressions of type integer.
+*/
+
+public class IntegerRangeTest extends Expression {
+
+ /*@Nullable*/ Expression value;
+ Expression min;
+ Expression max;
+
+ /**
+ * Construct a IntegerRangeTest
+ * @param value the integer value to be tested to see if it is in the range min to max inclusive
+ * @param min the lowest permitted value
+ * @param max the highest permitted value
+ */
+
+ public IntegerRangeTest(Expression value, Expression min, Expression max) {
+ this.value = value;
+ this.min = min;
+ this.max = max;
+ }
+
+ /**
+ * Get the value to be tested
+ * @return the expression that evaluates to the value being tested
+ */
+
+ public Expression getValueExpression() {
+ return value;
+ }
+
+ /**
+ * Get the expression denoting the start of the range
+ * @return the expression denoting the minumum value
+ */
+
+ public Expression getMinValueExpression() {
+ return min;
+ }
+
+ /**
+ * Get the expression denoting the end of the range
+ * @return the expression denoting the maximum value
+ */
+
+ public Expression getMaxValueExpression() {
+ return max;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ // Already done, we only get one of these expressions after the operands have been analyzed
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Get the data type of the items returned
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Determine the static cardinality
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new IntegerRangeTest(value.copy(), min.copy(), max.copy());
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ Expression[] e = {value, min, max};
+ return Arrays.asList(e).iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (value == original) {
+ value = replacement;
+ found = true;
+ }
+ if (min == original) {
+ min = replacement;
+ found = true;
+ }
+ if (max == original) {
+ max = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ if (offer.action != PromotionOffer.UNORDERED) {
+ value = doPromotion(value, offer);
+ min = doPromotion(min, offer);
+ max = doPromotion(max, offer);
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public BooleanValue evaluateItem(XPathContext c) throws XPathException {
+ NumericValue v = (NumericValue)value.evaluateItem(c);
+ if (v==null) {
+ return BooleanValue.FALSE;
+ }
+
+ if (!v.isWholeNumber()) {
+ return BooleanValue.FALSE;
+ }
+
+ NumericValue v2 = (NumericValue)min.evaluateItem(c);
+
+ if (v.compareTo(v2) < 0) {
+ return BooleanValue.FALSE;
+ }
+
+ NumericValue v3 = (NumericValue)max.evaluateItem(c);
+
+ return BooleanValue.get(v.compareTo(v3) <= 0);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the IntegerRangeTest expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new IntegerRangeTestCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("integerRangeTest");
+ value.explain(destination);
+ min.explain(destination);
+ max.explain(destination);
+ destination.endElement();
+ }
+
+ /**
+ * <p>The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form.</p>
+ * <p/>
+ * <p>For subclasses of Expression that represent XPath expressions, the result should always be a string that
+ * parses as an XPath 3.0 expression. </p>
+ * <p/>
+ *
+ * @return a representation of the expression as a string
+ */
+ @Override
+ public String toString() {
+ return ExpressionTool.parenthesize(value) + " = (" +
+ ExpressionTool.parenthesize(min) + " to " +
+ ExpressionTool.parenthesize(max) + ")";
+ }
+}
+
diff --git a/sf/saxon/expr/IntersectionEnumeration.java b/sf/saxon/expr/IntersectionEnumeration.java
new file mode 100644
index 0000000..30b0c09
--- /dev/null
+++ b/sf/saxon/expr/IntersectionEnumeration.java
@@ -0,0 +1,123 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.sort.ItemOrderComparer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* An enumeration representing a nodeset that is an intersection of two other NodeSets.
+* This implements the XPath 2.0 operator "intersect".
+*/
+
+
+public class IntersectionEnumeration implements SequenceIterator {
+
+ private SequenceIterator e1;
+ private SequenceIterator e2;
+ /*@Nullable*/ private NodeInfo nextNode1 = null;
+ private NodeInfo nextNode2 = null;
+ private ItemOrderComparer comparer;
+
+ private NodeInfo current = null;
+ private int position = 0;
+
+ /**
+ * Form an enumeration of the intersection of the nodes in two nodesets
+ * @param p1 the first operand: must be in document order
+ * @param p2 the second operand: must be in document order
+ * @param comparer Comparer to be used for putting nodes in document order
+ */
+
+ public IntersectionEnumeration(SequenceIterator p1, SequenceIterator p2,
+ ItemOrderComparer comparer ) throws XPathException {
+ e1 = p1;
+ e2 = p2;
+ this.comparer = comparer;
+
+ // move to the first node in each input nodeset
+
+ nextNode1 = next(e1);
+ nextNode2 = next(e2);
+ }
+
+ /**
+ * Get the next item from one of the input sequences,
+ * checking that it is a node.
+ * @param iter the iterator from which the next item is to be taken
+ * @return the next value returned by that iterator
+ */
+
+ private NodeInfo next(SequenceIterator iter) throws XPathException {
+ return (NodeInfo)iter.next();
+ // rely on type-checking to prevent a ClassCastException
+ }
+
+ public Item next() throws XPathException {
+ // main merge loop: iterate whichever sequence has the lower value, returning when a pair
+ // is found that match.
+
+ if (nextNode1 == null || nextNode2 == null) {
+ current = null;
+ position = -1;
+ return null;
+ }
+
+ while (nextNode1 != null && nextNode2 != null) {
+ int c = comparer.compare(nextNode1, nextNode2);
+ if (c<0) {
+ nextNode1 = next(e1);
+ } else if (c>0) {
+ nextNode2 = next(e2);
+ } else { // keys are equal
+ current = nextNode2; // which is the same as nextNode1
+ nextNode2 = next(e2);
+ nextNode1 = next(e1);
+ position++;
+ return current;
+ }
+ }
+ return null;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ e1.close();
+ e2.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new IntersectionEnumeration(e1.getAnother(), e2.getAnother(), comparer);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link SequenceIterator#GROUNDED}, {@link SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+}
+
diff --git a/sf/saxon/expr/IsLastExpression.java b/sf/saxon/expr/IsLastExpression.java
new file mode 100644
index 0000000..a55a8c3
--- /dev/null
+++ b/sf/saxon/expr/IsLastExpression.java
@@ -0,0 +1,150 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.IsLastExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+
+
+/**
+* A position() eq last() expression, generated by the optimizer.
+*/
+
+public final class IsLastExpression extends Expression {
+
+ private boolean condition;
+
+ /**
+ * Construct a condition that tests position() eq last() (if condition
+ * is true) or position() ne last() (if condition is false).
+ * @param condition true if we are testing "equals", false for "not equals".
+ */
+
+ public IsLastExpression(boolean condition){
+ this.condition = condition;
+ }
+
+ /**
+ * Get the condition we are testing for
+ * @return true if we are testing "equals", false for "not equals".
+ */
+
+ public boolean getCondition() {
+ return condition;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) {
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) {
+ return this;
+ }
+
+ /**
+ * Determine the special properties of this expression
+ * @return {@link StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+ public BooleanValue evaluateItem(XPathContext c) throws XPathException {
+ return BooleanValue.get(condition==c.isAtLast());
+ }
+
+ /**
+ * Determine the data type of the expression
+ * @return Type.BOOLEAN
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Determine the static cardinality
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Get the dependencies of this expression on the context
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_POSITION | StaticProperty.DEPENDS_ON_LAST;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new IsLastExpression(condition);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the IsLast expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new IsLastExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("isLast");
+ destination.emitAttribute("condition", (condition ? "true" : "false"));
+ destination.endElement();
+ }
+
+ /**
+ * <p>The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form.</p>
+ * <p/>
+ * <p>For subclasses of Expression that represent XPath expressions, the result should always be a string that
+ * parses as an XPath 3.0 expression.</p>
+ *
+ * @return a representation of the expression as a string
+ */
+ @Override
+ public String toString() {
+ if (condition) {
+ return "position() eq last()";
+ } else {
+ return "position() ne last()";
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/ItemChecker.java b/sf/saxon/expr/ItemChecker.java
new file mode 100644
index 0000000..9f366cf
--- /dev/null
+++ b/sf/saxon/expr/ItemChecker.java
@@ -0,0 +1,387 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ItemCheckerCompiler;
+import com.saxonica.stream.adjunct.ItemCheckerAdjunct;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.event.TypeCheckingFilter;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.CombinedNodeTest;
+import net.sf.saxon.pattern.DocumentNodeTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.IntegerValue;
+
+import java.util.List;
+
+
+/**
+* A ItemChecker implements the item type checking of "treat as": that is,
+* it returns the supplied sequence, checking that all its items are of the correct type
+*/
+
+public final class ItemChecker extends UnaryExpression {
+
+ private ItemType requiredItemType;
+ private RoleLocator role;
+
+ /**
+ * Constructor
+ * @param sequence the expression whose value we are checking
+ * @param itemType the required type of the items in the sequence
+ * @param role information used in constructing an error message
+ */
+
+ public ItemChecker(Expression sequence, ItemType itemType, RoleLocator role) {
+ super(sequence);
+ requiredItemType = itemType;
+ this.role = role;
+ adoptChildExpression(sequence);
+ }
+
+ /**
+ * Get the required type
+ * @return the required type of the items in the sequence
+ */
+
+ public ItemType getRequiredType() {
+ return requiredItemType;
+ }
+
+ /**
+ * Get the RoleLocator (used to construct error messages)
+ * @return the RoleLocator
+ */
+
+ public RoleLocator getRoleLocator() {
+ return role;
+ }
+
+ /**
+ * Simplify an expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ operand = visitor.simplify(operand);
+ if (requiredItemType instanceof AnyItemType) {
+ return operand;
+ }
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ // When typeCheck is called a second time, we might have more information...
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ int card = operand.getCardinality();
+ if (card == StaticProperty.EMPTY) {
+ //value is always empty, so no item checking needed
+ return operand;
+ }
+ ItemType supplied = operand.getItemType(th);
+ int relation = th.relationship(requiredItemType, supplied);
+ if (relation == TypeHierarchy.SAME_TYPE || relation == TypeHierarchy.SUBSUMES) {
+ return operand;
+ } else if (relation == TypeHierarchy.DISJOINT) {
+ final NamePool namePool = visitor.getConfiguration().getNamePool();
+ if (Cardinality.allowsZero(card)) {
+
+ String message = role.composeErrorMessage(
+ requiredItemType, operand.getItemType(th));
+ visitor.getStaticContext().issueWarning("The only value that can pass type-checking is an empty sequence. " +
+ message, this);
+ } else if (requiredItemType.equals(BuiltInAtomicType.STRING) && th.isSubType(supplied, BuiltInAtomicType.ANY_URI)) {
+ // URI promotion will take care of this at run-time
+ return operand;
+ } else {
+ String message = role.composeErrorMessage(requiredItemType, operand.getItemType(th));
+ XPathException err = new XPathException(message);
+ err.setErrorCode(role.getErrorCode());
+ err.setLocator(this);
+ err.setIsTypeError(role.isTypeError());
+ throw err;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided. This implementation provides both iterate() and
+ * process() methods natively.
+ */
+
+ public int getImplementationMethod() {
+ int m = ITERATE_METHOD | PROCESS_METHOD | ITEM_FEED_METHOD;
+ if (!Cardinality.allowsMany(getCardinality())) {
+ m |= EVALUATE_METHOD;
+ }
+ return m;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(syntacticContext, allowExtensions, reasons);
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public ItemCheckerAdjunct getStreamingAdjunct() {
+ return new ItemCheckerAdjunct();
+ }
+
+ //#endif
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ /*@Nullable*/@Override
+ public IntegerValue[] getIntegerBounds() {
+ return operand.getIntegerBounds();
+ }
+
+ /**
+ * Iterate over the sequence of values
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ SequenceIterator base = operand.iterate(context);
+ return new ItemMappingIterator(base, getMappingFunction(context), true);
+ }
+
+ /**
+ * Get the mapping function used to implement this item check. This mapping function is applied
+ * to each item in the input sequence.
+ * @param context The dynamic context used to evaluate the mapping function
+ * @return the mapping function. This will be an identity mapping: the output sequence is the same
+ * as the input sequence, unless the dynamic type checking reveals an error.
+ */
+
+ public ItemMappingFunction getMappingFunction(XPathContext context) {
+ return new ItemTypeCheckingFunction<Item>(requiredItemType, role, this, context.getConfiguration());
+ }
+
+ /**
+ * Evaluate as an Item.
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ Item item = operand.evaluateItem(context);
+ if (item==null) return null;
+ testConformance(item, context);
+ return item;
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ Expression next = operand;
+ int card = StaticProperty.ALLOWS_ZERO_OR_MORE;
+ if (next instanceof CardinalityChecker) {
+ card = ((CardinalityChecker)next).getRequiredCardinality();
+ next = ((CardinalityChecker)next).getBaseExpression();
+ }
+ if ((next.getImplementationMethod() & PROCESS_METHOD) != 0 && !(requiredItemType instanceof DocumentNodeTest)) {
+ SequenceReceiver out = context.getReceiver();
+ TypeCheckingFilter filter = new TypeCheckingFilter(out);
+ filter.setRequiredType(requiredItemType, card, role, this);
+ context.setReceiver(filter);
+ next.process(context);
+ filter.close();
+ context.setReceiver(out);
+ } else {
+ super.process(context);
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new ItemChecker(getBaseExpression().copy(), requiredItemType, role);
+ }
+
+
+ private void testConformance(Item item, XPathContext context) throws XPathException {
+ if (!requiredItemType.matchesItem(item, true, (context == null ? null : context.getConfiguration()))) {
+ String message;
+ if (context == null) {
+ // no name pool available
+ message = "Supplied value of type " + Type.displayTypeName(item) +
+ " does not match the required type of " + role.getMessage();
+ } else {
+ final NamePool pool = context.getNamePool();
+ final TypeHierarchy th = context.getConfiguration().getTypeHierarchy();
+ message = role.composeErrorMessage(requiredItemType, Type.getItemType(item, th));
+ }
+ String errorCode = role.getErrorCode();
+ if ("XPDY0050".equals(errorCode)) {
+ // error in "treat as" assertion
+ dynamicError(message, errorCode, context);
+ } else {
+ typeError(message, errorCode, context);
+ }
+ }
+ }
+
+ /**
+ * Determine the data type of the items returned by the expression
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ ItemType operandType = operand.getItemType(th);
+ int relationship = th.relationship(requiredItemType, operandType);
+ switch (relationship) {
+ case TypeHierarchy.OVERLAPS:
+ if (requiredItemType instanceof NodeTest && operandType instanceof NodeTest) {
+ return new CombinedNodeTest((NodeTest)requiredItemType, Token.INTERSECT, (NodeTest)operandType);
+ } else {
+ // we don't know how to intersect atomic types, it doesn't actually happen
+ return requiredItemType;
+ }
+
+ case TypeHierarchy.SUBSUMES:
+ case TypeHierarchy.SAME_TYPE:
+ // shouldn't happen, but it doesn't matter
+ return operandType;
+ case TypeHierarchy.SUBSUMED_BY:
+ default:
+ return requiredItemType;
+ }
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return super.equals(other) &&
+ requiredItemType == ((ItemChecker)other).requiredItemType;
+ }
+
+ /**
+ * get HashCode for comparing two expressions. Note that this hashcode gives the same
+ * result for (A op B) and for (B op A), whether or not the operator is commutative.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() ^ requiredItemType.hashCode();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ItemChecker expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ItemCheckerCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("treat");
+ out.emitAttribute("as", requiredItemType.toString());
+ operand.explain(out);
+ out.endElement();
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "TreatAs";
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ String typeDesc = requiredItemType.toString();
+ return "(" + operand.toString() + ") treat as " + typeDesc;
+ }
+
+}
+
+
diff --git a/sf/saxon/expr/ItemMappingFunction.java b/sf/saxon/expr/ItemMappingFunction.java
new file mode 100644
index 0000000..72501e2
--- /dev/null
+++ b/sf/saxon/expr/ItemMappingFunction.java
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * ItemMappingFunction is an interface that must be satisfied by an object passed to a
+ * ItemMappingIterator. It represents an object which, given an Item, can return either
+ * another Item, or null.
+ * <p/>
+ * The interface is generic, paramterized by F (from) the input item type, and T (to),
+ * the output item type
+ */
+
+public interface ItemMappingFunction<F extends Item, T extends Item> {
+
+ /**
+ * Map one item to another item.
+ *
+ * @param item The input item to be mapped.
+ * @return either the output item, or null.
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ /*@Nullable*/
+ public T mapItem(F item) throws XPathException;
+
+}
+
diff --git a/sf/saxon/expr/ItemMappingIterator.java b/sf/saxon/expr/ItemMappingIterator.java
new file mode 100644
index 0000000..030b7f2
--- /dev/null
+++ b/sf/saxon/expr/ItemMappingIterator.java
@@ -0,0 +1,152 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+
+/**
+ * ItemMappingIterator applies a mapping function to each item in a sequence.
+ * The mapping function either returns a single item, or null (representing an
+ * empty sequence).
+ * <p/>
+ * This is a specialization of the more general MappingIterator class, for use
+ * in cases where a single input item never maps to a sequence of more than one
+ * output item.
+ */
+
+public final class ItemMappingIterator<F extends Item, T extends Item>
+ implements SequenceIterator<T>, LookaheadIterator<T>, LastPositionFinder<T> {
+
+ private SequenceIterator<F> base;
+ private ItemMappingFunction<F, T> action;
+ /*@Nullable*/ private T current = null;
+ private int position = 0;
+ private boolean oneToOne = false;
+
+ /**
+ * Construct an ItemMappingIterator that will apply a specified DummyItemMappingFunction to
+ * each Item returned by the base iterator.
+ *
+ * @param base the base iterator
+ * @param action the mapping function to be applied.
+ */
+
+ public ItemMappingIterator(SequenceIterator<F> base, ItemMappingFunction<F, T> action) {
+ this.base = base;
+ this.action = action;
+ }
+
+ /**
+ * Construct an ItemMappingIterator that will apply a specified DummyItemMappingFunction to
+ * each Item returned by the base iterator.
+ *
+ * @param base the base iterator
+ * @param action the mapping function to be applied
+ * @param oneToOne true if this iterator is one-to-one
+ */
+
+ public ItemMappingIterator(SequenceIterator<F> base, ItemMappingFunction<F, T> action, boolean oneToOne) {
+ this.base = base;
+ this.action = action;
+ this.oneToOne = oneToOne;
+ }
+
+ /**
+ * Say whether this ItemMappingIterator is one-to-one: that is, for every input item, there is
+ * always exactly one output item. The default is false.
+ * @param oneToOne true if this iterator is one-to-one
+ */
+
+ public void setOneToOne(boolean oneToOne) {
+ this.oneToOne = oneToOne;
+ }
+
+ /**
+ * Ask whether this ItemMappingIterator is one-to-one: that is, for every input item, there is
+ * always exactly one output item. The default is false.
+ * @return true if this iterator is one-to-one
+ */
+
+ public boolean isOneToOne() {
+ return oneToOne;
+ }
+
+ public boolean hasNext() {
+ // Must only be called if this is a lookahead iterator, which will only be true if the base iterator
+ // is a lookahead iterator and one-to-one is true
+ return ((LookaheadIterator)base).hasNext();
+ }
+
+ public T next() throws XPathException {
+ while (true) {
+ F nextSource = base.next();
+ if (nextSource == null) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ // Call the supplied mapping function
+ current = action.mapItem(nextSource);
+ if (current != null) {
+ position++;
+ return current;
+ }
+ // otherwise go round the loop to get the next item from the base sequence
+ }
+ }
+
+ public T current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<T> getAnother() throws XPathException {
+ SequenceIterator<F> newBase = base.getAnother();
+ ItemMappingFunction<F, T> newAction = action instanceof StatefulMappingFunction ?
+ (ItemMappingFunction<F, T>)((StatefulMappingFunction<F, T>)action).getAnother() :
+ action;
+ return new ItemMappingIterator<F, T>(newBase, newAction, oneToOne);
+ }
+
+ public int getLength() throws XPathException {
+ // Must only be called if this is a last-position-finder iterator, which will only be true if the base iterator
+ // is a last-position-finder iterator and one-to-one is true
+ return ((LastPositionFinder)base).getLength();
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED},
+ * {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ if (oneToOne) {
+ return base.getProperties() & (LOOKAHEAD | LAST_POSITION_FINDER);
+ } else {
+ return 0;
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/ItemTypeCheckingFunction.java b/sf/saxon/expr/ItemTypeCheckingFunction.java
new file mode 100644
index 0000000..7a11a8b
--- /dev/null
+++ b/sf/saxon/expr/ItemTypeCheckingFunction.java
@@ -0,0 +1,80 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * A mapping function for use in conjunction with an {@link ItemMappingIterator} that checks that
+ * all the items in a sequence are instances of a given item type
+ *
+ * @param <T> Defines a subtype of item to be checked
+ */
+public class ItemTypeCheckingFunction<T extends Item> implements ItemMappingFunction<T, T> {
+
+ private ItemType requiredItemType;
+ private SourceLocator locator;
+ private RoleLocator role;
+ private Configuration config = null;
+
+ /**
+ * Create the type-checking function
+ *
+ * @param requiredItemType the item type that all items in the sequence must conform to
+ * @param role information for error messages
+ * @param locator the location of the expression for error messages
+ * @param config the Saxon configuration
+ */
+
+ public ItemTypeCheckingFunction(ItemType requiredItemType, RoleLocator role, SourceLocator locator, Configuration config) {
+ this.requiredItemType = requiredItemType;
+ this.role = role;
+ this.locator = locator;
+ this.config = config;
+ }
+
+ public T mapItem(T item) throws XPathException {
+ testConformance(item, config);
+ return item;
+ }
+
+ private void testConformance(T item, Configuration config) throws XPathException {
+ if (!requiredItemType.matchesItem(item, true, config)) {
+ String message;
+
+ final NamePool pool = config.getNamePool();
+ final TypeHierarchy th = config.getTypeHierarchy();
+ message = role.composeErrorMessage(requiredItemType, Type.getItemType(item, th));
+
+ String errorCode = role.getErrorCode();
+ if ("XPDY0050".equals(errorCode)) {
+ // error in "treat as" assertion
+ XPathException te = new XPathException(message, errorCode);
+ te.setLocator(locator);
+ te.setIsTypeError(false);
+ throw te;
+ } else {
+ XPathException te = new XPathException(message, errorCode);
+ te.setLocator(locator);
+ te.setIsTypeError(true);
+ throw te;
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/expr/JPConverter.java b/sf/saxon/expr/JPConverter.java
new file mode 100644
index 0000000..59c4e48
--- /dev/null
+++ b/sf/saxon/expr/JPConverter.java
@@ -0,0 +1,723 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.functions.map.ImmutableMap;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.lib.ExternalObjectModel;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ExternalObjectType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.*;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URL;
+import java.util.*;
+
+/**
+ * This class together with its embedded subclasses handles conversion from Java values to XPath values.
+ *
+ * The general principle is to allocate a specific JPConverter at compile time wherever possible. If there
+ * is insufficient type information to make this feasible, a general-purpose JPConverter is allocated, which
+ * in turn allocates a more specific converter at run-time to do the actual work.
+ */
+
+public abstract class JPConverter implements Serializable {
+
+ private static HashMap<Class, JPConverter> map = new HashMap<Class, JPConverter>();
+
+ static {
+ map.put(SequenceIterator.class, new FromSequenceIterator());
+ map.put(Sequence.class, FromSequence.INSTANCE);
+ map.put(String.class, FromString.INSTANCE);
+ map.put(Boolean.class, FromBoolean.INSTANCE);
+ map.put(boolean.class, FromBoolean.INSTANCE);
+ map.put(Double.class, FromDouble.INSTANCE);
+ map.put(double.class, FromDouble.INSTANCE);
+ map.put(Float.class, FromFloat.INSTANCE);
+ map.put(float.class, FromFloat.INSTANCE);
+ map.put(BigDecimal.class, FromBigDecimal.INSTANCE);
+ map.put(BigInteger.class, FromBigInteger.INSTANCE);
+ map.put(Long.class, FromLong.INSTANCE);
+ map.put(long.class, FromLong.INSTANCE);
+ map.put(Integer.class, FromInt.INSTANCE);
+ map.put(int.class, FromInt.INSTANCE);
+ map.put(Short.class, FromShort.INSTANCE);
+ map.put(short.class, FromShort.INSTANCE);
+ map.put(Byte.class, FromByte.INSTANCE);
+ map.put(byte.class, FromByte.INSTANCE);
+ map.put(Character.class, FromCharacter.INSTANCE);
+ map.put(char.class, FromCharacter.INSTANCE);
+ //map.put(QName.class, new FromQName());
+ map.put(URI.class, FromURI.INSTANCE);
+ map.put(URL.class, FromURI.INSTANCE);
+ map.put(Date.class, FromDate.INSTANCE);
+ //map.put(Source.class, FromSource.INSTANCE);
+ map.put(long[].class, FromLongArray.INSTANCE);
+ map.put(int[].class, FromIntArray.INSTANCE);
+ map.put(short[].class, FromShortArray.INSTANCE);
+ map.put(byte[].class, FromByteArray.INSTANCE);
+ map.put(char[].class, FromCharArray.INSTANCE);
+ map.put(double[].class, FromDoubleArray.INSTANCE);
+ map.put(float[].class, FromFloatArray.INSTANCE);
+ map.put(boolean[].class, FromBooleanArray.INSTANCE);
+ map.put(Collection.class, FromCollection.INSTANCE);
+//#ifdefined BYTECODE
+ map.put(Map.class, FromMap.INSTANCE);
+//#endif
+ //map.put(Object.class, FromExternalObject.INSTANCE);
+ }
+
+ /**
+ * Allocate a Java-to-XPath converter for a given class of Java objects
+ * @param javaClass the class of the Java object to be converted (this may be the static type
+ * or the dynamic type, depending when the converter is allocated)
+ * @param config the Saxon Configuration
+ * @return a suitable converter
+ */
+
+ public static JPConverter allocate(Class javaClass, Configuration config) {
+ JPConverter c = map.get(javaClass);
+ if (c != null) {
+ return c;
+ }
+
+ if (javaClass.getName().equals("javax.xml.namespace.QName")) {
+ return FromQName.INSTANCE;
+ }
+
+ if (NodeInfo.class.isAssignableFrom(javaClass)) {
+ return new FromSequence(AnyNodeTest.getInstance(), StaticProperty.ALLOWS_ZERO_OR_ONE);
+ }
+
+ if (Source.class.isAssignableFrom(javaClass) && !(DOMSource.class.isAssignableFrom(javaClass))) {
+ return FromSource.INSTANCE;
+ }
+
+ for (Map.Entry<Class, JPConverter> e : map.entrySet()) {
+ if (e.getKey().isAssignableFrom(javaClass)) {
+ return e.getValue();
+ }
+ }
+
+ List<ExternalObjectModel> externalObjectModels = config.getExternalObjectModels();
+ for (ExternalObjectModel model : externalObjectModels) {
+ JPConverter converter = model.getJPConverter(javaClass, config);
+ if (converter != null) {
+ return converter;
+ }
+ }
+
+ if (javaClass.isArray()) {
+ Class itemClass = javaClass.getComponentType();
+ return new FromObjectArray(allocate(itemClass, config));
+ }
+
+ if(javaClass.equals(Void.TYPE)) {
+ return VoidConverter.INSTANCE;
+ }
+
+ return new WrapExternalObject(new ExternalObjectType(javaClass, config));
+ }
+
+ /**
+ * Convert a Java object to an equivalent XPath value
+ *
+ * @param object the java object to be converted
+ * @param context the XPath dynamic evaluation context
+ * @return the XPath value resulting from the conversion
+ * @throws XPathException if the conversion is not possible or if it fails
+ */
+
+ /*@Nullable*/ public abstract Sequence convert(Object object, XPathContext context) throws XPathException;
+
+ /**
+ * Get the item type of the XPath value that will result from the conversion
+ * @return the XPath item type
+ */
+
+ public abstract ItemType getItemType();
+
+ /**
+ * Get the cardinality of the XPath value that will result from the conversion
+ * @return the cardinality of the result
+ */
+
+ public int getCardinality() {
+ // default implementation
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ public static class FromSequenceIterator extends JPConverter {
+ public static final FromSequenceIterator INSTANCE = new FromSequenceIterator();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return SequenceExtent.makeSequenceExtent((SequenceIterator)object);
+ }
+ public ItemType getItemType() {
+ return AnyItemType.getInstance();
+ }
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ }
+
+ public static class FromSequence extends JPConverter {
+
+ public static final FromSequence INSTANCE =
+ new FromSequence(AnyItemType.getInstance(), StaticProperty.ALLOWS_ZERO_OR_MORE);
+
+ private ItemType resultType;
+ private int cardinality;
+
+ public FromSequence(ItemType resultType, int cardinality) {
+ this.resultType = resultType;
+ this.cardinality = cardinality;
+ }
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return (object instanceof Closure ?
+ SequenceExtent.makeSequenceExtent(((Closure) object).iterate()) :
+ (Sequence)object);
+ }
+ public ItemType getItemType() {
+ return resultType;
+ }
+ public int getCardinality() {
+ return cardinality;
+ }
+
+ }
+
+ public static class FromString extends JPConverter {
+ public static final FromString INSTANCE = new FromString();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new StringValue((String)object);
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.STRING;
+ }
+
+ }
+
+ public static class FromBoolean extends JPConverter {
+ public static final FromBoolean INSTANCE = new FromBoolean();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return BooleanValue.get(((Boolean)object).booleanValue());
+ }
+
+ public ItemType getItemType() {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ }
+
+ public static class FromDouble extends JPConverter {
+ public static final FromDouble INSTANCE = new FromDouble();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new DoubleValue(((Double)object).doubleValue());
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.DOUBLE;
+ }
+ }
+
+ public static class FromFloat extends JPConverter {
+ public static final FromFloat INSTANCE = new FromFloat();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new FloatValue(((Float)object).floatValue());
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.FLOAT;
+ }
+ }
+
+ public static class FromBigDecimal extends JPConverter {
+ public static final FromBigDecimal INSTANCE = new FromBigDecimal();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new DecimalValue((BigDecimal)object);
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.DECIMAL;
+ }
+ }
+
+ public static class FromBigInteger extends JPConverter {
+ public static final FromBigInteger INSTANCE = new FromBigInteger();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return IntegerValue.makeIntegerValue((BigInteger)object);
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ public static class FromLong extends JPConverter {
+ public static final FromLong INSTANCE = new FromLong();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new Int64Value(((Long)object).longValue());
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ public static class FromInt extends JPConverter {
+ public static final FromInt INSTANCE = new FromInt();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new Int64Value(((Integer)object).intValue());
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ public static class FromShort extends JPConverter {
+ public static final FromShort INSTANCE = new FromShort();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new Int64Value(((Short)object).intValue());
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ public static class FromByte extends JPConverter {
+ public static final FromByte INSTANCE = new FromByte();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new Int64Value(((Byte)object).intValue());
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.INTEGER;
+ }
+ }
+
+ public static class FromCharacter extends JPConverter {
+ public static final FromCharacter INSTANCE = new FromCharacter();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new StringValue(object.toString());
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.STRING;
+ }
+ }
+
+ public static class FromQName extends JPConverter {
+ public static final FromQName INSTANCE = new FromQName();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ javax.xml.namespace.QName qn = (javax.xml.namespace.QName)object;
+ // Note JDK 1.5 dependency
+ return new QNameValue(qn.getPrefix(), qn.getNamespaceURI(), qn.getLocalPart());
+ //return Value.makeQNameValue(object, context.getConfiguration());
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.QNAME;
+ }
+
+ }
+
+ public static class FromURI extends JPConverter {
+ // also used for URL
+ public static final FromURI INSTANCE = new FromURI();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return new AnyURIValue(object.toString());
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.ANY_URI;
+ }
+ }
+
+ public static class FromDate extends JPConverter {
+ public static final FromDate INSTANCE = new FromDate();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return DateTimeValue.fromJavaDate((Date)object);
+ }
+ public ItemType getItemType() {
+ return BuiltInAtomicType.DATE_TIME;
+ }
+ }
+
+ public static class WrapExternalObject extends JPConverter {
+
+ public static final WrapExternalObject INSTANCE = new WrapExternalObject(BuiltInAtomicType.ANY_ATOMIC);
+
+ private ItemType resultType;
+
+ public WrapExternalObject(ItemType resultType) {
+ this.resultType = resultType;
+ }
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return object==null ? null : new ObjectValue(object);
+ }
+ public ItemType getItemType() {
+ return resultType;
+ }
+ }
+
+
+
+ public static class VoidConverter extends JPConverter {
+
+ public static final VoidConverter INSTANCE = new VoidConverter();
+
+ public VoidConverter() {
+
+ }
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return EmptySequence.getInstance();
+ }
+
+ /**
+ * Deliberately avoid giving type information
+ * */
+ public ItemType getItemType() {
+ return AnyItemType.getInstance();
+ }
+ }
+
+ public static class FromCollection extends JPConverter {
+
+ public static final FromCollection INSTANCE = new FromCollection();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Item[] array = new Item[((Collection)object).size()];
+ int a = 0;
+ for (Object obj : ((Collection) object)) {
+ JPConverter itemConverter = allocate(obj.getClass(), context.getConfiguration());
+ try {
+ Item item = SequenceTool.asItem(itemConverter.convert(obj, context));
+ if (item != null) {
+ array[a++] = item;
+ }
+ } catch (XPathException e) {
+ throw new XPathException(
+ "Returned Collection contains an object that cannot be converted to an Item ("
+ + obj.getClass() + "): " + e.getMessage(),
+ SaxonErrorCode.SXJE0051);
+ }
+ }
+ return new SequenceExtent(array, 0, a);
+ }
+
+ public ItemType getItemType() {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Get the cardinality of the XPath value that will result from the conversion
+ * @return the cardinality of the result
+ */
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+
+//#ifdefined BYTECODE
+ public static class FromMap extends JPConverter {
+
+ public static final FromMap INSTANCE = new FromMap();
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Map<Object, Object> jMap = (Map<Object, Object>)object;
+ ImmutableMap pMap = new ImmutableMap();
+ for (Map.Entry<Object, Object> entry : jMap.entrySet()) {
+ Object key = entry.getKey();
+ Object value = entry.getValue();
+ JPConverter keyConverter = allocate(key.getClass(), context.getConfiguration());
+ JPConverter valueConverter = allocate(value.getClass(), context.getConfiguration());
+ try {
+ Item keyItem = SequenceTool.asItem(keyConverter.convert(key, context));
+ Sequence keyValue = valueConverter.convert(value, context);
+ if (keyItem instanceof AtomicValue && keyValue != null) {
+ pMap.inSituPut((AtomicValue)keyItem, keyValue, context);
+ }
+ } catch (XPathException e) {
+ throw new XPathException(
+ "Returned Map contains an object that cannot be converted to an Item ("
+ + key.getClass() + "): " + e.getMessage(),
+ SaxonErrorCode.SXJE0051);
+ }
+ }
+ return pMap;
+ }
+
+ public ItemType getItemType() {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Get the cardinality of the XPath value that will result from the conversion
+ * @return the cardinality of the result
+ */
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+//#endif
+
+
+ public static class FromSource extends JPConverter {
+
+ public static final FromSource INSTANCE = new FromSource();
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ ParseOptions options = new ParseOptions();
+ Controller controller = context.getController();
+ if (controller != null) {
+ options.setSchemaValidationMode(controller.getSchemaValidationMode());
+ }
+ return context.getConfiguration().buildDocument((Source)object, options);
+ }
+
+ public ItemType getItemType() {
+ return AnyNodeTest.getInstance();
+ }
+
+ }
+
+ public static class FromLongArray extends JPConverter {
+
+ public static final FromLongArray INSTANCE = new FromLongArray();
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Item[] array = new Item[((long[])object).length];
+ for (int i = 0; i < array.length; i++){
+ array[i] = Int64Value.makeDerived(((long[])object)[i], BuiltInAtomicType.LONG);
+ }
+ return new SequenceExtent(array);
+ }
+
+ public ItemType getItemType() {
+ return BuiltInAtomicType.LONG;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+
+ public static class FromIntArray extends JPConverter {
+
+ public static final FromIntArray INSTANCE = new FromIntArray();
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Item[] array = new Item[((int[])object).length];
+ for (int i = 0; i < array.length; i++){
+ array[i] = Int64Value.makeDerived(((int[])object)[i], BuiltInAtomicType.INT);
+ }
+ return new SequenceExtent(array);
+ }
+
+ public ItemType getItemType() {
+ return BuiltInAtomicType.INT;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+
+ public static class FromShortArray extends JPConverter {
+
+ public static final FromShortArray INSTANCE = new FromShortArray();
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Item[] array = new Item[((short[])object).length];
+ for (int i = 0; i < array.length; i++){
+ array[i] = Int64Value.makeDerived(((short[])object)[i], BuiltInAtomicType.SHORT);
+ }
+ return new SequenceExtent(array);
+ }
+
+ public ItemType getItemType() {
+ return BuiltInAtomicType.SHORT;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+
+ public static class FromByteArray extends JPConverter {
+
+ public static final FromByteArray INSTANCE = new FromByteArray();
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Item[] array = new Item[((byte[])object).length];
+ for (int i = 0; i < array.length; i++){
+ array[i] = Int64Value.makeDerived(255 & (int)((byte[])object)[i], BuiltInAtomicType.BYTE);
+ }
+ return new SequenceExtent(array);
+ }
+
+ public ItemType getItemType() {
+ return BuiltInAtomicType.BYTE;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+
+ public static class FromCharArray extends JPConverter {
+
+ public static final FromCharArray INSTANCE = new FromCharArray();
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ return StringValue.makeStringValue(new String((char[])object));
+ }
+
+ public ItemType getItemType() {
+ return BuiltInAtomicType.STRING;
+ }
+
+ }
+
+ public static class FromDoubleArray extends JPConverter {
+
+ public static final FromDoubleArray INSTANCE = new FromDoubleArray();
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Item[] array = new Item[((double[])object).length];
+ for (int i = 0; i < array.length; i++){
+ array[i] = new DoubleValue(((double[])object)[i]);
+ }
+ return new SequenceExtent(array);
+ }
+
+ public ItemType getItemType() {
+ return BuiltInAtomicType.DOUBLE;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+
+ public static class FromFloatArray extends JPConverter {
+
+ public static final FromFloatArray INSTANCE = new FromFloatArray();
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Item[] array = new Item[((float[])object).length];
+ for (int i = 0; i < array.length; i++){
+ array[i] = new DoubleValue(((float[])object)[i]);
+ }
+ return new SequenceExtent(array);
+ }
+
+ public ItemType getItemType() {
+ return BuiltInAtomicType.FLOAT;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+
+ public static class FromBooleanArray extends JPConverter {
+
+ public static final FromBooleanArray INSTANCE = new FromBooleanArray();
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Item[] array = new Item[((boolean[])object).length];
+ for (int i = 0; i < array.length; i++){
+ array[i] = BooleanValue.get(((boolean[])object)[i]);
+ }
+ return new SequenceExtent(array);
+ }
+
+ public ItemType getItemType() {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+
+ public static class FromObjectArray extends JPConverter {
+
+ private JPConverter itemConverter;
+
+ public FromObjectArray(JPConverter itemConverter) {
+ this.itemConverter = itemConverter;
+ }
+
+ public Sequence convert(Object object, XPathContext context) throws XPathException {
+ Object[] arrayObject = (Object[])object;
+ Item[] newArray = new Item[arrayObject.length];
+ int a = 0;
+ for (Object member : arrayObject) {
+ if (member != null) {
+ try {
+ Item newItem = SequenceTool.asItem(itemConverter.convert(member, context));
+ if (newItem != null) {
+ newArray[a++] = newItem;
+ }
+ } catch (XPathException e) {
+ throw new XPathException(
+ "Returned array contains an object that cannot be converted to an Item (" +
+ member.getClass() + "): " + e.getMessage(),
+ SaxonErrorCode.SXJE0051);
+ }
+ } else {
+ throw new XPathException("Returned array contains null values: cannot convert to items", SaxonErrorCode.SXJE0051);
+ }
+ }
+ return new SequenceExtent(newArray, 0, a);
+ }
+
+ public ItemType getItemType() {
+ return itemConverter.getItemType();
+ }
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ }
+
+
+// public static class General extends JPConverter {
+// public static General INSTANCE = new General();
+// public ValueRepresentation convert(Object object, XPathContext context) throws XPathException {
+// // fall back to old code
+// return Value.convertToBestFit(object, context.getConfiguration());
+// }
+// public ItemType getItemType() {
+// return AnyItemType.getInstance();
+// }
+// }
+
+
+
+}
+
diff --git a/sf/saxon/expr/LastItemExpression.java b/sf/saxon/expr/LastItemExpression.java
new file mode 100644
index 0000000..a4c599c
--- /dev/null
+++ b/sf/saxon/expr/LastItemExpression.java
@@ -0,0 +1,84 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.LastItemExpressionCompiler;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ReversibleIterator;
+
+/**
+ * A LastItemExpression returns the last item in the sequence returned by a given
+ * base expression. The evaluation strategy is to read the input sequence with a one-item lookahead.
+*/
+
+public final class LastItemExpression extends SingleItemFilter {
+
+ /**
+ * Constructor
+ * @param base A sequence expression denoting sequence whose first item is to be returned
+ */
+
+ public LastItemExpression(Expression base) {
+ operand = base;
+ adoptChildExpression(base);
+ //computeStaticProperties();
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new LastItemExpression(getBaseExpression().copy());
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ /*@Nullable*/ public Item evaluateItem(XPathContext context) throws XPathException {
+ SequenceIterator forwards = operand.iterate(context);
+ if (forwards instanceof ReversibleIterator) {
+ return ((ReversibleIterator)forwards).getReverseIterator().next();
+ } else {
+ Item current = null;
+ while (true) {
+ Item item = forwards.next();
+ if (item == null) {
+ return current;
+ }
+ current = item;
+ }
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the LastItem expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new LastItemExpressionCompiler();
+ }
+//#endif
+
+
+ public String getExpressionName() {
+ return "lastItem";
+ }
+
+}
+
diff --git a/sf/saxon/expr/LastPositionFinder.java b/sf/saxon/expr/LastPositionFinder.java
new file mode 100644
index 0000000..dd51808
--- /dev/null
+++ b/sf/saxon/expr/LastPositionFinder.java
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+
+
+/**
+* A LastPositionFinder is an interface implemented by any SequenceIterator that is
+ * able to return the position of the last item in the sequence.
+*/
+
+public interface LastPositionFinder<T extends Item> {
+
+ /**
+ * Get the last position (that is, the number of items in the sequence). This method is
+ * non-destructive: it does not change the state of the iterator.
+ * The result is undefined if the next() method of the iterator has already returned null.
+ * This method must not be called unless the result of getProperties() on the iterator
+ * includes the bit setting {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER}
+ * @return the number of items in the sequence
+ * @throws XPathException if an error occurs evaluating the sequence in order to determine
+ * the number of items
+ */
+
+ public int getLength() throws XPathException;
+
+}
+
diff --git a/sf/saxon/expr/LetExpression.java b/sf/saxon/expr/LetExpression.java
new file mode 100644
index 0000000..aad263d
--- /dev/null
+++ b/sf/saxon/expr/LetExpression.java
@@ -0,0 +1,735 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.LetExpressionCompiler;
+import com.saxonica.stream.adjunct.LetExpressionAdjunct;
+import net.sf.saxon.TypeCheckerEnvironment;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A LetExpression is modelled on the XQuery syntax let $x := expr return expr. This syntax
+ * is not available in the surface XPath language, but it is used internally in an optimized
+ * expression tree.
+ */
+
+public class LetExpression extends Assignation implements TailCallReturner {
+
+ // This integer holds an approximation to the number of times that the declared variable is referenced.
+ // The value 1 means there is only one reference and it is not in a loop, which means that the value will
+ // not be retained in memory. If there are multiple references or references within a loop, the value will
+ // be a small integer > 1. The special value FILTERED indicates that there is a reference within a loop
+ // in the form $x[predicate], which indicates that the value should potentially be indexable. The initial
+ // value 2 is for safety; if a LetExpression is optimized without first being typechecked (which happens
+ // in the case of optimizer-created variables) then this ensures that no damaging rewrites are done.
+
+ private int evaluationMode = ExpressionTool.UNDECIDED;
+
+ /**
+ * Create a LetExpression
+ */
+
+ public LetExpression() {
+ //System.err.println("let");
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "let";
+ }
+
+ public void setRefCount(int refCount){
+ this.refCount = refCount;
+ }
+
+ /**
+ * Type-check the expression. This also has the side-effect of counting the number of references
+ * to the variable (treating references that occur within a loop specially)
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ // The order of events is critical here. First we ensure that the type of the
+ // sequence expression is established. This is used to establish the type of the variable,
+ // which in turn is required when type-checking the action part.
+
+ sequence = visitor.typeCheck(sequence, contextItemType);
+
+ RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, getVariableQName(), 0);
+ //role.setSourceLocator(this);
+ sequence = TypeChecker.strictTypeCheck(
+ sequence, requiredType, role, visitor.getStaticContext());
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ final ItemType actualItemType = sequence.getItemType(th);
+
+ refineTypeInformation(actualItemType,
+ sequence.getCardinality(),
+ (sequence instanceof Literal ? ((Literal) sequence).getValue() : null),
+ sequence.getSpecialProperties(), visitor, this);
+
+ boolean indexed = (refCount == FilterExpression.FILTERED);
+ refCount = 0;
+ action = visitor.typeCheck(action, contextItemType);
+ if (indexed) {
+ refCount = FilterExpression.FILTERED;
+ }
+// if (refCount == 0) {
+// System.err.println("refCount == 0");
+// action = visitor.typeCheck(action, contextItemType);
+// }
+ return this;
+ }
+
+
+ /**
+ * Determine whether this expression implements its own method for static type checking
+ *
+ * @return true - this expression has a non-trivial implementation of the staticTypeCheck()
+ * method
+ */
+
+ public boolean implementsStaticTypeCheck() {
+ return true;
+ }
+
+ /**
+ * Static type checking for let expressions is delegated to the expression itself,
+ * and is performed on the "action" expression, to allow further delegation to the branches
+ * of a conditional
+ * @param req the required type
+ * @param backwardsCompatible true if backwards compatibility mode applies
+ * @param role the role of the expression in relation to the required type
+ * @param visitor an expression visitor
+ * @return the expression after type checking (perhaps augmented with dynamic type checking code)
+ * @throws XPathException if failures occur, for example if the static type of one branch of the conditional
+ * is incompatible with the required type
+ */
+
+ public Expression staticTypeCheck(SequenceType req,
+ boolean backwardsCompatible,
+ RoleLocator role, TypeCheckerEnvironment visitor)
+ throws XPathException {
+ action = TypeChecker.staticTypeCheck(action, req, backwardsCompatible, role, visitor);
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+
+ // if this is a construct of the form "let $j := EXP return $j" replace it with EXP
+ // Remarkably, people do write this, and it can also be produced by previous rewrites
+ // Note that type checks will already have been added to the sequence expression
+
+ if (action instanceof VariableReference &&
+ ((VariableReference) action).getBinding() == this) {
+ Expression e2 = visitor.optimize(sequence, contextItemType);
+ opt.trace("Eliminated trivial variable " + getVariableName(), e2);
+ return e2;
+ }
+
+ /**
+ * Unless this has already been done, find and count the references to this variable
+ */
+
+ // if this is an XSLT construct of the form <xsl:variable>text</xsl:variable>, try to replace
+ // it by <xsl:variable select=""/>. This can be done if all the references to the variable use
+ // its value as a string (rather than, say, as a node or as a boolean)
+ if (sequence instanceof DocumentInstr && ((DocumentInstr) sequence).isTextOnly()) {
+ if (allReferencesAreFlattened()) {
+ sequence = ((DocumentInstr) sequence).getStringValueExpression();
+ requiredType = SequenceType.SINGLE_UNTYPED_ATOMIC;
+ adoptChildExpression(sequence);
+ refineTypeInformation(requiredType.getPrimaryType(), requiredType.getCardinality(), null, 0, visitor, this);
+ }
+ }
+
+ // refCount is initialized during the typeCheck() phase
+ if (refCount == 0) {
+ // variable is not used - no need to evaluate it
+ Expression a = visitor.optimize(action, contextItemType);
+ ExpressionTool.copyLocationInfo(this, a);
+ opt.trace("Eliminated unused variable " + getVariableName(), a);
+ return a;
+ }
+
+ // Don't inline context-dependent variables in a streamable template. See strmode011.
+ // The reason for this is that a variable <xsl:variable><xsl:copy-of select="."/></xsl:variable>
+ // can be evaluated in streaming mode, but an arbitrary expression using copy() inline can't (e.g.
+ // if it appears in a path expression or as an operand of an arithmetic expression)
+
+ if (refCount == 1 && ExpressionTool.dependsOnFocus(sequence)) {
+ Container container = getContainer();
+ if (container instanceof Template && ((Template)container).isDeclaredStreamable() && sequence instanceof DocumentInstr) {
+ refCount = 5;
+ }
+ }
+
+ if (refCount == 1 || sequence instanceof Literal) {
+ // Either there's only one reference, and it's not in a loop.
+ // Or the variable is bound to a constant value.
+ // In these two cases we can inline the reference.
+ // That is, we replace "let $x := SEQ return f($x)" by "f(SEQ)".
+ // Note, we rely on the fact that any context-changing expression is treated as a loop,
+ // and generates a refCount greater than one.
+ replaceVariable(opt, sequence);
+ Expression e2 = visitor.optimize(action, contextItemType);
+ opt.trace("Inlined local variable " + getVariableName(), e2);
+ return e2;
+ }
+
+ int tries = 0;
+ while (tries++ < 5) {
+ Expression seq2 = visitor.optimize(sequence, contextItemType);
+ if (seq2 == sequence) {
+ break;
+ }
+ sequence = seq2;
+ adoptChildExpression(sequence);
+ visitor.resetStaticProperties();
+ }
+
+ tries = 0;
+ while (tries++ < 5) {
+ Expression act2 = visitor.optimize(action, contextItemType);
+ if (act2 == action) {
+ break;
+ }
+ action = act2;
+ adoptChildExpression(action);
+ visitor.resetStaticProperties();
+ }
+
+ evaluationMode = (isIndexedVariable() ?
+ ExpressionTool.MAKE_CLOSURE :
+ ExpressionTool.lazyEvaluationMode(sequence));
+ return this;
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (sequence == original) {
+ sequence = replacement;
+ setEvaluationMode(ExpressionTool.eagerEvaluationMode(sequence));
+ found = true;
+ }
+ if (action == original) {
+ action = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Determine whether all references to this variable are using the value either
+ * (a) by atomizing it, or (b) by taking its string value. (This excludes usages
+ * such as testing the existence of a node or taking the effective boolean value).
+ * @return true if all references are known to atomize (or stringify) the value,
+ * false otherwise. The value false may indicate "not known".
+ */
+
+ private boolean allReferencesAreFlattened() {
+ List references = new ArrayList();
+ ExpressionTool.gatherVariableReferences(action, this, references);
+ for (int i=references.size()-1; i>=0; i--) {
+ BindingReference bref = (BindingReference)references.get(i);
+ if (bref instanceof VariableReference) {
+ VariableReference ref = (VariableReference)bref;
+ if (ref.isFlattened()) {
+ // OK, it's a string context
+ } else {
+ return false;
+ }
+
+ } else {
+ // it must be saxon:assign
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Determine whether this is a vacuous expression as defined in the XQuery update specification
+ * @return true if this expression is vacuous
+ */
+
+ public boolean isVacuousExpression() {
+ return action.isVacuousExpression();
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ action.checkPermittedContents(parentType, env, whole);
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ /*@Nullable*/@Override
+ public IntegerValue[] getIntegerBounds() {
+ return action.getIntegerBounds();
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ // Can't follow the W3C rules directly, because we've turned xsl:variable elements into a
+ // let expression. The following logic is believed equivalent
+ int s = sequence.getStreamability(NAVIGATION_CONTEXT, allowExtensions, reasons);
+ int a = action.getStreamability(NAVIGATION_CONTEXT, allowExtensions, reasons);
+ if (s == W3C_MOTIONLESS && a == W3C_MOTIONLESS) {
+ return W3C_MOTIONLESS;
+ }
+ if (s == W3C_FREE_RANGING || a == W3C_FREE_RANGING) {
+ return W3C_FREE_RANGING;
+ }
+ if (s == W3C_MOTIONLESS) {
+ return a;
+ }
+ if (a == W3C_MOTIONLESS) {
+ return s;
+ }
+ reasons.add("Both the initializer of the local variable and the expression using the variable make downwards selections");
+ return W3C_FREE_RANGING;
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public LetExpressionAdjunct getStreamingAdjunct() {
+ return new LetExpressionAdjunct();
+ }
+
+ //#endif
+
+ /**
+ * Iterate over the result of the expression to return a sequence of items
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ // minimize stack consumption by evaluating nested LET expressions iteratively
+ LetExpression let = this;
+ while (true) {
+ Sequence val = let.eval(context);
+ context.setLocalVariable(let.getLocalSlotNumber(), val);
+ if (let.action instanceof LetExpression) {
+ let = (LetExpression) let.action;
+ } else {
+ break;
+ }
+ }
+ return let.action.iterate(context);
+ }
+
+ /**
+ * Iterate over the result of the expression to return a sequence of events
+ */
+
+ public EventIterator iterateEvents(XPathContext context) throws XPathException {
+ // minimize stack consumption by evaluating nested LET expressions iteratively
+ LetExpression let = this;
+ while (true) {
+ Sequence val = let.eval(context);
+ context.setLocalVariable(let.getLocalSlotNumber(), val);
+ if (let.action instanceof LetExpression) {
+ let = (LetExpression) let.action;
+ } else {
+ break;
+ }
+ }
+ return let.action.iterateEvents(context);
+ }
+
+
+ /**
+ * Evaluate the variable.
+ * @param context the dynamic evaluation context
+ * @return the result of evaluating the expression that is bound to the variable
+ */
+
+ public Sequence eval(XPathContext context) throws XPathException {
+ if (evaluationMode == ExpressionTool.UNDECIDED) {
+ evaluationMode = ExpressionTool.lazyEvaluationMode(sequence);
+ }
+ return ExpressionTool.evaluate(sequence, evaluationMode, context, refCount);
+ }
+
+ /**
+ * Evaluate the expression as a singleton
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ // minimize stack consumption by evaluating nested LET expressions iteratively
+ LetExpression let = this;
+ while (true) {
+ Sequence val = let.eval(context);
+ context.setLocalVariable(let.getLocalSlotNumber(), val);
+ if (let.action instanceof LetExpression) {
+ let = (LetExpression) let.action;
+ } else {
+ break;
+ }
+ }
+ return let.action.evaluateItem(context);
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ * @param context The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ // minimize stack consumption by evaluating nested LET expressions iteratively
+ LetExpression let = this;
+ while (true) {
+ Sequence val = let.eval(context);
+ context.setLocalVariable(let.getLocalSlotNumber(), val);
+ if (let.action instanceof LetExpression) {
+ let = (LetExpression) let.action;
+ } else {
+ break;
+ }
+ }
+ return let.action.effectiveBooleanValue(context);
+ }
+
+ /**
+ * Process this expression as an instruction, writing results to the current
+ * outputter
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ // minimize stack consumption by evaluating nested LET expressions iteratively
+ LetExpression let = this;
+ while (true) {
+ Sequence val = let.eval(context);
+ context.setLocalVariable(let.getLocalSlotNumber(), val);
+ if (let.action instanceof LetExpression) {
+ let = (LetExpression) let.action;
+ } else {
+ break;
+ }
+ }
+ let.action.process(context);
+ }
+
+
+ /**
+ * Determine the data type of the items returned by the expression, if possible
+ *
+ * @param th the type hierarchy cache
+ * @return one of the values Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
+ * or Type.ITEM (meaning not known in advance)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return action.getItemType(th);
+ }
+
+ /**
+ * Determine the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ return action.getCardinality();
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ int props = action.getSpecialProperties();
+ int seqProps = sequence.getSpecialProperties();
+ if ((seqProps & StaticProperty.NON_CREATIVE) == 0) {
+ props &= ~StaticProperty.NON_CREATIVE;
+ }
+ return props;
+ }
+
+ /**
+ * Mark tail function calls
+ */
+
+ public int markTailFunctionCalls(StructuredQName qName, int arity) {
+ return ExpressionTool.markTailFunctionCalls(action, qName, arity);
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ // pass the offer on to the sequence expression
+ Expression seq2 = doPromotion(sequence, offer);
+ if (seq2 != sequence) {
+ // if we've extracted a global variable, it may need to be marked indexable
+ if (seq2 instanceof VariableReference) {
+ Binding b = ((VariableReference)seq2).getBinding();
+ if (b instanceof GlobalVariable) {
+ ((GlobalVariable)b).setReferenceCount(refCount < 10 ? 10 : refCount);
+ }
+ }
+ sequence = seq2;
+ }
+ if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES ||
+ offer.action == PromotionOffer.UNORDERED ||
+ offer.action == PromotionOffer.REPLACE_CURRENT ||
+ offer.action == PromotionOffer.EXTRACT_GLOBAL_VARIABLES) {
+ action = doPromotion(action, offer);
+ } else if (offer.action == PromotionOffer.RANGE_INDEPENDENT ||
+ offer.action == PromotionOffer.FOCUS_INDEPENDENT) {
+ // Pass the offer to the action expression after adding the variable bound by this let expression,
+ // so that a subexpression must depend on neither variable if it is to be promoted
+ Binding[] savedBindingList = offer.bindingList;
+ offer.bindingList = extendBindingList(offer.bindingList);
+ action = doPromotion(action, offer);
+ offer.bindingList = savedBindingList;
+ }
+ // if this results in the expression (let $x := $y return Z), replace all references to
+ // to $x by references to $y in the Z part, and eliminate this LetExpression by
+ // returning the action part.
+ if (sequence instanceof VariableReference) {
+ Binding b = ((VariableReference)sequence).getBinding();
+ if (b != null && !b.isAssignable()) {
+ replaceVariable(offer.getOptimizer(), sequence);
+ // defensive programming. If someone in the tree fails to pass this request down,
+ // there will still be a reference to the variable on the tree, which will cause
+ // a crash later. So we'll check that the variable really has gone from the
+ // tree before deleting the variable binding.
+ if (ExpressionTool.dependsOnVariable(action, new Binding[]{this})) {
+ offer.getOptimizer().trace("Failed to eliminate redundant variable $" + getVariableName(), this);
+ } else {
+ return action;
+ }
+ }
+ }
+
+ return this;
+ }
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ LetExpression let = new LetExpression();
+ let.refCount = refCount;
+ let.setVariableQName(variableName);
+ let.setRequiredType(requiredType);
+ let.setSequence(sequence.copy());
+ Expression newAction = action.copy();
+ let.setAction(newAction);
+ ExpressionTool.rebindVariableReferences(newAction, this, let);
+ return let;
+ }
+
+ /**
+ * ProcessLeavingTail: called to do the real work of this instruction.
+ * The results of the instruction are written
+ * to the current Receiver, which can be obtained via the Controller.
+ *
+ * @param context The dynamic context of the transformation, giving access to the current node,
+ * the current variables, etc.
+ * @return null if the instruction has completed execution; or a TailCall indicating
+ * a function call or template call that is delegated to the caller, to be made after the stack has
+ * been unwound so as to save stack space.
+ */
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ // minimize stack consumption by evaluating nested LET expressions iteratively
+ LetExpression let = this;
+ while (true) {
+ Sequence val = let.eval(context);
+ context.setLocalVariable(let.getLocalSlotNumber(), val);
+ if (let.action instanceof LetExpression) {
+ let = (LetExpression) let.action;
+ } else {
+ break;
+ }
+ }
+ if (let.action instanceof TailCallReturner) {
+ return ((TailCallReturner) let.action).processLeavingTail(context);
+ } else {
+ let.action.process(context);
+ return null;
+ }
+ }
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ // minimize stack consumption by evaluating nested LET expressions iteratively
+ LetExpression let = this;
+ while (true) {
+ Sequence val = let.eval(context);
+ context.setLocalVariable(let.getLocalSlotNumber(), val);
+ if (let.action instanceof LetExpression) {
+ let = (LetExpression) let.action;
+ } else {
+ break;
+ }
+ }
+ let.action.evaluatePendingUpdates(context, pul);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Let expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new LetExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ * @return a representation of the expression as a string
+ */
+
+ public String toString() {
+ return "let $" + getVariableEQName() + " := " + sequence.toString() +
+ " return " + ExpressionTool.parenthesize(action);
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("let");
+ out.emitAttribute("variable", getVariableEQName());
+ out.emitAttribute("as", sequence.getItemType(out.getTypeHierarchy()).toString() +
+ Cardinality.getOccurrenceIndicator(sequence.getCardinality()));
+ if (isIndexedVariable()) {
+ out.emitAttribute("indexable", "true");
+ }
+ out.emitAttribute("slot", getLocalSlotNumber()+"");
+ out.startSubsidiaryElement("be");
+ sequence.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("return");
+ action.explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+ public void setEvaluationMode(int evaluationMode) {
+ this.evaluationMode = evaluationMode;
+ }
+
+ public int getEvaluationMode() {
+ return evaluationMode;
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/Literal.java b/sf/saxon/expr/Literal.java
new file mode 100644
index 0000000..8ea4dfc
--- /dev/null
+++ b/sf/saxon/expr/Literal.java
@@ -0,0 +1,621 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.LiteralCompiler;
+import com.saxonica.functions.hof.FunctionLiteral;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.ItemTypePattern;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.ErrorType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.util.List;
+
+
+
+/**
+ * A Literal is an expression whose value is constant: it is a class that implements the {@link Expression}
+ * interface as a wrapper around a {@link GroundedValue}. This may derive from an actual literal in an XPath expression
+ * or query, or it may be the result of evaluating a constant subexpression such as true() or xs:date('2007-01-16')
+ */
+
+public class Literal extends Expression {
+
+ private GroundedValue value;
+
+ /**
+ * Create a literal as a wrapper around a Value
+ * @param value the value of this literal
+ */
+
+ protected Literal(GroundedValue value) {
+ this.value = value.reduce();
+ }
+
+ /**
+ * Get the value represented by this Literal
+ * @return the constant value
+ */
+
+ public GroundedValue getValue() {
+ return value;
+ }
+
+ /**
+ * Simplify an expression
+ * @return for a Value, this always returns the value unchanged
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /**
+ * TypeCheck an expression
+ * @return for a Value, this always returns the value unchanged
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Optimize an expression
+ * @return for a Value, this always returns the value unchanged
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Determine the data type of the items in the expression, if possible
+ * @return for the default implementation: AnyItemType (not known)
+ * @param th The TypeHierarchy. Can be null if the target is an AtomicValue.
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return SequenceTool.getItemType(value, th);
+ }
+
+ /**
+ * Determine the cardinality
+ */
+
+ public int computeCardinality() {
+ if (value instanceof EmptySequence) {
+ return StaticProperty.EMPTY;
+ } else if (value instanceof AtomicValue) {
+ return StaticProperty.EXACTLY_ONE;
+ }
+ try {
+ SequenceIterator iter = value.iterate();
+ Item next = iter.next();
+ if (next == null) {
+ return StaticProperty.EMPTY;
+ } else {
+ if (iter.next() != null) {
+ return StaticProperty.ALLOWS_ONE_OR_MORE;
+ } else {
+ return StaticProperty.EXACTLY_ONE;
+ }
+ }
+ } catch (XPathException err) {
+ // can't actually happen
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ }
+
+ /**
+ * Compute the static properties of this expression (other than its type). For a
+ * Value, the only special property is {@link StaticProperty#NON_CREATIVE}.
+ * @return the value {@link StaticProperty#NON_CREATIVE}
+ */
+
+
+ public int computeSpecialProperties() {
+ if (getValue() instanceof EmptySequence) {
+ // An empty sequence has all special properties except "has side effects".
+ return StaticProperty.SPECIAL_PROPERTY_MASK &~ StaticProperty.HAS_SIDE_EFFECTS;
+ }
+ return StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ /*@Nullable*/@Override
+ public IntegerValue[] getIntegerBounds() {
+ if (value instanceof IntegerValue) {
+ return new IntegerValue[]{(IntegerValue)value, (IntegerValue)value};
+ } else if (value instanceof IntegerRange) {
+ return new IntegerValue[]{
+ Int64Value.makeIntegerValue(((IntegerRange)value).getStart()),
+ Int64Value.makeIntegerValue(((IntegerRange)value).getEnd())};
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Determine whether this is a vacuous expression as defined in the XQuery update specification
+ * @return true if this expression is vacuous
+ */
+
+ public boolean isVacuousExpression() {
+ return value.getLength() == 0;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new Literal(value);
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ if (isEmptySequence(this)) {
+ return new ItemTypePattern(ErrorType.getInstance());
+ } else {
+ return super.toPattern(config, is30);
+ }
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the set of nodes within the path map
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ return pathMapNodeSet;
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as StaticProperty.VARIABLES and
+ * StaticProperty.CURRENT_NODE
+ * @return for a Value, this always returns zero.
+ */
+
+ public final int getDependencies() {
+ return 0;
+ }
+
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ return value.iterate();
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate() throws XPathException {
+ return value.iterate();
+ }
+
+ /**
+ * Evaluate as a singleton item (or empty sequence). Note: this implementation returns
+ * the first item in the sequence. The method should not be used unless appropriate type-checking
+ * has been done to ensure that the value will be a singleton.
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ if (value instanceof Item) {
+ return (Item)value;
+ }
+ return value.iterate().next();
+ }
+
+
+ /**
+ * Process the value as an instruction, without returning any tail calls
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ SequenceIterator iter = value.iterate();
+ SequenceReceiver out = context.getReceiver();
+ while (true) {
+ Item it = iter.next();
+ if (it==null) break;
+ out.append(it, 0, NodeInfo.ALL_NAMESPACES);
+ }
+ }
+
+ /*
+ * Evaluate an expression as a String. This function must only be called in contexts
+ * where it is known that the expression will return a single string (or where an empty sequence
+ * is to be treated as a zero-length string). Implementations should not attempt to convert
+ * the result to a string, other than converting () to "". This method is used mainly to
+ * evaluate expressions produced by compiling an attribute value template.
+ *
+ * @exception net.sf.saxon.trans.XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @exception ClassCastException if the result type of the
+ * expression is not xs:string?
+ * @param context The context in which the expression is to be evaluated
+ * @return the value of the expression, evaluated in the current context.
+ * The expression must return a string or (); if the value of the
+ * expression is (), this method returns "".
+ */
+
+ public CharSequence evaluateAsString(XPathContext context) throws XPathException {
+ AtomicValue value = (AtomicValue) evaluateItem(context);
+ if (value == null) return "";
+ return value.getStringValueCS();
+ }
+
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @exception net.sf.saxon.trans.XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @return the effective boolean value
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ return ExpressionTool.effectiveBooleanValue(value.iterate());
+ }
+
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException. The implementation for a literal representing
+ * an empty sequence, however, is a no-op.
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ if (value instanceof EmptySequence) {
+ // do nothing
+ } else {
+ super.evaluatePendingUpdates(context, pul);
+ }
+ }
+
+ /**
+ * Determine whether two literals are equal, when considered as expressions.
+ * @param obj the other expression
+ * @return true if the two literals are equal. The test here requires (a) identity in the
+ * sense defined by XML Schema (same value in the same value space), and (b) identical type
+ * annotations. For example the literal xs:int(3) is not equal (as an expression) to xs:short(3),
+ * because the two expressions are not interchangeable.
+ */
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Literal)) {
+ return false;
+ }
+ GroundedValue v0 = value;
+ GroundedValue v1 = ((Literal)obj).value;
+ try {
+ SequenceIterator i0 = v0.iterate();
+ SequenceIterator i1 = v1.iterate();
+ while (true) {
+ Item m0 = i0.next();
+ Item m1 = i1.next();
+ if (m0==null && m1==null) {
+ return true;
+ }
+ if (m0==null || m1==null) {
+ return false;
+ }
+ boolean n0 = (m0 instanceof NodeInfo);
+ boolean n1 = (m1 instanceof NodeInfo);
+ if (n0 != n1) {
+ return false;
+ }
+ if (n0 && n1 && !((NodeInfo)m0).isSameNodeInfo((NodeInfo)m1)) {
+ return false;
+ }
+ if(m0 instanceof AtomicValue && m1 instanceof AtomicValue){
+ if ((!((AtomicValue)m0).isIdentical((AtomicValue)m1)) ||
+ ((AtomicValue) m0).getItemType() != ((AtomicValue) m1).getItemType()) {
+ return false;
+ }
+ }
+ }
+ } catch (XPathException err) {
+ return false;
+ }
+ }
+
+ /**
+ * Return a hash code to support the equals() function
+ */
+
+ public int hashCode() {
+ if (value instanceof AtomicSequence) {
+ return ((AtomicSequence)value).getSchemaComparable().hashCode();
+ } else {
+ return super.hashCode();
+ }
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return value.toString();
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("literal");
+ if (value instanceof EmptySequence) {
+ out.emitAttribute("value", "()");
+ } else if (value instanceof AtomicValue) {
+ //noinspection RedundantCast
+ out.emitAttribute("value", ((AtomicValue)value).getStringValue());
+ // cast is needed to tell the compiler there's no exception possible
+ out.emitAttribute("type", ((AtomicValue) value).getItemType().getDisplayName());
+ } else {
+ try {
+ out.emitAttribute("count", value.getLength()+"");
+ if (value.getLength() < 20) {
+ SequenceIterator iter = iterate();
+ while (true) {
+ Item it = iter.next();
+ if (it == null) {
+ break;
+ }
+ if (it instanceof NodeInfo) {
+ out.startElement("node");
+ out.emitAttribute("path", Navigator.getPath(((NodeInfo)it)));
+ out.emitAttribute("uri", ((NodeInfo)it).getSystemId());
+ out.endElement();
+ } else if (it instanceof AtomicValue) {
+ out.startElement("atomicValue");
+ out.emitAttribute("value", it.getStringValue());
+ out.emitAttribute("type", ((AtomicValue) it).getItemType().getDisplayName());
+ out.endElement();
+ } else if (it instanceof FunctionItem) {
+ out.startElement("functionItem");
+ if (((FunctionItem)it).getFunctionName() != null) {
+ out.emitAttribute("name", ((FunctionItem)it).getFunctionName().getDisplayName());
+ }
+ out.emitAttribute("arity", ""+((FunctionItem)it).getArity());
+ out.endElement();
+ } else if (it instanceof ObjectValue) {
+ out.startElement("externalObject");
+ out.emitAttribute("class", ((ObjectValue)it).getObject().getClass().getName());
+ out.endElement();
+ }
+ }
+ }
+ } catch (XPathException err) {
+ //
+ }
+ }
+ out.endElement();
+ }
+
+ /**
+ * Test whether the literal wraps an atomic value. (Note, if this method returns false,
+ * this still leaves the possibility that the literal wraps a sequence that happens to contain
+ * a single atomic value).
+ * @param exp an expression
+ * @return true if the expression is a literal and the literal wraps an AtomicValue
+ */
+
+ public static boolean isAtomic(Expression exp) {
+ return exp instanceof Literal && ((Literal)exp).getValue() instanceof AtomicValue;
+ }
+
+ /**
+ * Test whether the literal explicitly wraps an empty sequence. (Note, if this method returns false,
+ * this still leaves the possibility that the literal wraps a sequence that happens to be empty).
+ * @param exp an expression
+ * @return true if the expression is a literal and the value of the literal is an empty sequence
+ */
+
+ public static boolean isEmptySequence(Expression exp) {
+ return exp instanceof Literal && ((Literal)exp).getValue() instanceof EmptySequence;
+ }
+
+ /**
+ * Test if a literal represents the boolean value true
+ * @param exp an expression
+ * @param value true or false
+ * @return true if the expression is a literal and the literal represents the boolean value given in the
+ * second argument
+ */
+
+ public static boolean isConstantBoolean(Expression exp, boolean value) {
+ if (exp instanceof Literal) {
+ GroundedValue b = ((Literal)exp).getValue();
+ return (b instanceof BooleanValue && ((BooleanValue)b).getBooleanValue() == value);
+ }
+ return false;
+ }
+
+ /**
+ * Test if a literal represents the integer value 1
+ * @param exp an expression
+ * @return true if the expression is a literal and the literal represents the integer value 1
+ */
+
+ public static boolean isConstantOne(Expression exp) {
+ if (exp instanceof Literal) {
+ GroundedValue v = ((Literal)exp).getValue();
+ return (v instanceof Int64Value && ((Int64Value)v).longValue() == 1);
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether the expression can be evaluated without reference to the part of the context
+ * document outside the subtree rooted at the context node.
+ * @return true if the expression has no dependencies on the context node, or if the only dependencies
+ * on the context node are downward selections using the self, child, descendant, attribute, and namespace
+ * axes.
+ */
+
+ public boolean isSubtreeExpression() {
+ return true;
+ }
+
+ /**
+ * Factory method to make an empty-sequence literal
+ * @return a literal whose value is the empty sequence
+ */
+
+ public static Literal makeEmptySequence() {
+ return new Literal(EmptySequence.getInstance());
+ }
+
+ /**
+ * Factory method to create a literal as a wrapper around a Value (factory method)
+ * @param value the value of this literal
+ * @return the Literal
+ */
+
+ public static Literal makeLiteral(GroundedValue value) {
+ value = value.reduce();
+ if (value instanceof StringValue) {
+ return new StringLiteral((StringValue)value);
+//#ifdefined HOF
+ } else if (value instanceof FunctionItem) {
+ return new FunctionLiteral((FunctionItem)value);
+//#endif
+ } else {
+ return new Literal(value);
+ }
+ }
+
+//#ifdefined STREAM
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ if (isEmptySequence(this)) {
+ return new ItemTypePattern(ErrorType.getInstance());
+ } else {
+ return super.toStreamingPattern(config, reasonForFailure);
+ }
+ }
+
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return W3C_MOTIONLESS;
+ }
+
+//#endif
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Literal expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new LiteralCompiler();
+ }
+//#endif
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/LocalVariableReference.java b/sf/saxon/expr/LocalVariableReference.java
new file mode 100644
index 0000000..9d2f7b4
--- /dev/null
+++ b/sf/saxon/expr/LocalVariableReference.java
@@ -0,0 +1,135 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.LocalVariableReferenceCompiler;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Variable reference: a reference to a local variable. This subclass of VariableReference
+ * bypasses the Binding object to get the value directly from the relevant slot in the local
+ * stackframe.
+ */
+
+public class LocalVariableReference extends VariableReference {
+
+ int slotNumber = -999;
+
+ /**
+ * Create a local variable reference. The binding and slot number will be supplied later
+ */
+
+ public LocalVariableReference() {
+ }
+
+ /**
+ * Create a LocalVariableReference bound to a given Binding
+ * @param binding the binding (that is, the declaration of this local variable)
+ */
+
+ public LocalVariableReference(Binding binding) {
+ super(binding);
+ }
+
+ /**
+ * Create a clone copy of this VariableReference
+ * @return the cloned copy
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ if (binding == null) {
+ throw new UnsupportedOperationException("Cannot copy a variable reference whose binding is unknown");
+ }
+ LocalVariableReference ref = new LocalVariableReference();
+ ref.binding = binding;
+ ref.staticType = staticType;
+ ref.slotNumber = slotNumber;
+ ref.constantValue = constantValue;
+ ref.displayName = displayName;
+ binding.addReference(isInLoop());
+ ExpressionTool.copyLocationInfo(this, ref);
+ return ref;
+ }
+
+ /**
+ * Set the slot number for this local variable, that is, its position in the local stack frame
+ * @param slotNumber the slot number to be used
+ */
+
+ public void setSlotNumber(int slotNumber) {
+ this.slotNumber = slotNumber;
+ }
+
+ /**
+ * Get the slot number allocated to this local variable
+ * @return the slot number
+ */
+
+ public int getSlotNumber() {
+ return slotNumber;
+ }
+
+ /**
+ * Return the value of the variable
+ * @param c the XPath dynamic context
+ * @return the value of the variable
+ * @throws XPathException if any dynamic error occurs while evaluating the variable
+ */
+
+ /*@NotNull*/
+ public Sequence evaluateVariable(XPathContext c) throws XPathException {
+ try {
+ return c.getStackFrame().slots[slotNumber];
+ } catch (ArrayIndexOutOfBoundsException err) {
+ if (slotNumber == -999) {
+ if (binding != null) {
+ try {
+ slotNumber = binding.getLocalSlotNumber();
+ return c.getStackFrame().slots[slotNumber];
+ } catch (ArrayIndexOutOfBoundsException err2) {
+ // fall through
+ }
+ }
+ throw new ArrayIndexOutOfBoundsException("Local variable $" + getDisplayName() + " has not been allocated a stack frame slot");
+ } else {
+ int actual = c.getStackFrame().slots.length;
+ throw new ArrayIndexOutOfBoundsException("Local variable $" + getDisplayName() + " uses slot "
+ + slotNumber + " but " + (actual == 0 ? "no" : ("only " + c.getStackFrame().slots.length + " slots")) +
+ " are allocated on the stack frame");
+ }
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the LocalVariableReference expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new LocalVariableReferenceCompiler();
+ }
+//#endif
+
+ /**
+ * Replace this VariableReference where appropriate by a more efficient implementation. This
+ * can only be done after all slot numbers are allocated. The efficiency is gained by binding the
+ * VariableReference directly to a local or global slot, rather than going via the Binding object
+ *
+ * @param parent the parent expression of this variable reference
+ */
+
+// public void refineVariableReference(Expression parent) {
+// // no-op
+// }
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/MappingFunction.java b/sf/saxon/expr/MappingFunction.java
new file mode 100644
index 0000000..8085e57
--- /dev/null
+++ b/sf/saxon/expr/MappingFunction.java
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* MappingFunction is an interface that must be satisfied by an object passed to a
+* MappingIterator. It represents an object which, given an Item, can return a
+* SequenceIterator that delivers a sequence of zero or more Items.
+ *
+ * This is a generic interface, where F represents the type of the input to the mapping
+ * function, and T represents the type of the result
+*/
+
+public interface MappingFunction<F extends Item, T extends Item> {
+
+ /**
+ * Map one item to a sequence.
+ * @param item The item to be mapped.
+ * @return one of the following: (a) a SequenceIterator over the sequence of items that the supplied input
+ * item maps to, or (b) null if it maps to an empty sequence.
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ /*@Nullable*/ public SequenceIterator<? extends T> map(F item) throws XPathException;
+
+}
+
diff --git a/sf/saxon/expr/MappingIterator.java b/sf/saxon/expr/MappingIterator.java
new file mode 100644
index 0000000..e83498a
--- /dev/null
+++ b/sf/saxon/expr/MappingIterator.java
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* MappingIterator merges a sequence of sequences into a single flat
+* sequence. It takes as inputs an iteration, and a mapping function to be
+* applied to each Item returned by that iteration. The mapping function itself
+* returns another iteration. The result is an iteration of the concatenation of all
+* the iterations returned by the mapping function.<p>
+*
+* This is a powerful class. It is used, with different mapping functions,
+* in a great variety of ways. It underpins the way that "for" expressions and
+* path expressions are evaluated, as well as sequence expressions. It is also
+* used in the implementation of the document(), key(), and id() functions.
+*/
+
+public class MappingIterator<F extends Item, T extends Item>
+ implements SequenceIterator<T> {
+
+ private SequenceIterator<F> base;
+ private MappingFunction<F, T> action;
+ /*@Nullable*/ private SequenceIterator<? extends T> results = null;
+ //private boolean atomizing = false;
+ private T current = null;
+ private int position = 0;
+
+ /**
+ * Construct a MappingIterator that will apply a specified MappingFunction to
+ * each Item returned by the base iterator.
+ * @param base the base iterator
+ * @param action the mapping function to be applied
+ */
+
+ public MappingIterator(SequenceIterator<F> base, MappingFunction<F, T> action) {
+ this.base = base;
+ this.action = action;
+ }
+
+ public T next() throws XPathException {
+ T nextItem;
+ while (true) {
+ if (results != null) {
+ nextItem = results.next();
+ if (nextItem != null) {
+ break;
+ } else {
+ results = null;
+ }
+ }
+ F nextSource = base.next();
+ if (nextSource != null) {
+ // Call the supplied mapping function
+ SequenceIterator<? extends T> obj = action.map(nextSource);
+
+ // The result may be null (representing an empty sequence),
+ // or a SequenceIterator (any sequence)
+
+ if (obj != null) {
+ results = obj;
+ nextItem = results.next();
+ if (nextItem == null) {
+ results = null;
+ } else {
+ break;
+ }
+ }
+ // now go round the loop to get the next item from the base sequence
+ } else {
+ results = null;
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+
+ current = nextItem;
+ position++;
+ return nextItem;
+ }
+
+ public T current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ if (results != null) {
+ results.close();
+ }
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<T> getAnother() throws XPathException {
+ SequenceIterator<F> newBase = base.getAnother();
+ MappingFunction<F, T> newAction = action instanceof StatefulMappingFunction ?
+ (MappingFunction<F, T>)((StatefulMappingFunction)action).getAnother() :
+ action;
+ return new MappingIterator<F, T>(newBase, newAction);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link SequenceIterator#GROUNDED}, {@link SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/expr/MonoIterator.java b/sf/saxon/expr/MonoIterator.java
new file mode 100644
index 0000000..d5e24cf
--- /dev/null
+++ b/sf/saxon/expr/MonoIterator.java
@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * An iterator over a single object (typically a sub-expression of an expression)
+ */
+public class MonoIterator<T> implements Iterator<T> {
+
+ private T thing; // the single object in the collection
+ private boolean gone; // true if the single object has already been returned
+
+ /**
+ * Create an iterator of the single object supplied
+ * @param thing the object to be iterated over
+ */
+
+ public MonoIterator(T thing) {
+ gone = false;
+ this.thing = thing;
+ }
+
+ /**
+ * Returns <tt>true</tt> if the iteration has more elements. (In other
+ * words, returns <tt>true</tt> if <tt>next</tt> would return an element
+ * rather than throwing an exception.)
+ *
+ * @return <tt>true</tt> if the iterator has more elements.
+ */
+
+ public boolean hasNext() {
+ return !gone;
+ }
+
+ /**
+ * Returns the next element in the iteration.
+ *
+ * @return the next element in the iteration.
+ * @exception NoSuchElementException iteration has no more elements.
+ */
+
+ public T next() {
+ if (gone) {
+ throw new NoSuchElementException();
+ } else {
+ gone = true;
+ return thing;
+ }
+ }
+
+ /**
+ *
+ * Removes from the underlying collection the last element returned by the
+ * iterator (optional operation). This method can be called only once per
+ * call to <tt>next</tt>. The behavior of an iterator is unspecified if
+ * the underlying collection is modified while the iteration is in
+ * progress in any way other than by calling this method.
+ *
+ * @exception UnsupportedOperationException if the <tt>remove</tt>
+ * operation is not supported by this Iterator (which is the
+ * case for this iterator).
+ */
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/sf/saxon/expr/MultiIterator.java b/sf/saxon/expr/MultiIterator.java
new file mode 100644
index 0000000..f859b9c
--- /dev/null
+++ b/sf/saxon/expr/MultiIterator.java
@@ -0,0 +1,80 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import java.util.Iterator;
+
+/**
+ * An iterator that combines the results of a sequence of iterators
+ */
+public class MultiIterator<T> implements Iterator<T> {
+
+ private Iterator<T>[] array;
+ private int current;
+
+ /**
+ * Create an iterator that concatenates a number of supplied iterators
+ * @param array the iterators to be concatenated
+ */
+
+ public MultiIterator(Iterator<T>[] array) {
+ this.array = array;
+ current = 0;
+ }
+
+ /**
+ * Returns <tt>true</tt> if the iteration has more elements. (In other
+ * words, returns <tt>true</tt> if <tt>next</tt> would return an element
+ * rather than throwing an exception.)
+ *
+ * @return <tt>true</tt> if the iterator has more elements.
+ */
+
+ public boolean hasNext() {
+ while (true) {
+ if (current >= array.length) {
+ return false;
+ }
+ if (array[current].hasNext()) {
+ return true;
+ }
+ current++;
+ }
+ }
+
+ /**
+ * Returns the next element in the iteration.
+ *
+ * @return the next element in the iteration.
+ * @exception java.util.NoSuchElementException iteration has no more elements.
+ */
+ public T next() {
+ return array[current].next();
+ }
+
+ /**
+ *
+ * Removes from the underlying collection the last element returned by the
+ * iterator (optional operation). This method can be called only once per
+ * call to <tt>next</tt>. The behavior of an iterator is unspecified if
+ * the underlying collection is modified while the iteration is in
+ * progress in any way other than by calling this method.
+ *
+ * @exception UnsupportedOperationException if the <tt>remove</tt>
+ * operation is not supported by this Iterator.
+
+ * @exception IllegalStateException if the <tt>next</tt> method has not
+ * yet been called, or the <tt>remove</tt> method has already
+ * been called after the last call to the <tt>next</tt>
+ * method.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/sf/saxon/expr/Negatable.java b/sf/saxon/expr/Negatable.java
new file mode 100644
index 0000000..b04e559
--- /dev/null
+++ b/sf/saxon/expr/Negatable.java
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+
+/**
+ * This interface is implemented by expressions that returns a boolean value, and returns an expression
+ * whose result is the negated boolean value
+ */
+public interface Negatable {
+
+ /**
+ * Check whether this specific instance of the expression is negatable
+ * @return true if it is
+ */
+
+ public boolean isNegatable(ExpressionVisitor visitor);
+
+ /**
+ * Create an expression that returns the negation of this expression
+ * @return the negated expression
+ * @throws IllegalOperationException if isNegatable() returns false
+ */
+
+ public Expression negate();
+}
diff --git a/sf/saxon/expr/NegateExpression.java b/sf/saxon/expr/NegateExpression.java
new file mode 100644
index 0000000..1d9aa0b
--- /dev/null
+++ b/sf/saxon/expr/NegateExpression.java
@@ -0,0 +1,143 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NegateExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.DoubleValue;
+import net.sf.saxon.value.NumericValue;
+import net.sf.saxon.value.SequenceType;
+
+/**
+ * Negate Expression: implements the unary minus operator.
+ * This expression is initially created as an ArithmeticExpression (or in backwards
+ * compatibility mode, an ArithmeticExpression10) to take advantage of the type checking code.
+ * So we don't need to worry about type checking or argument conversion.
+ */
+
+public class NegateExpression extends UnaryExpression {
+
+ private boolean backwardsCompatible;
+
+ /**
+ * Create a NegateExpression
+ * @param base the expression that computes the value whose sign is to be reversed
+ */
+
+ public NegateExpression(Expression base) {
+ super(base);
+ }
+
+ /**
+ * Set whether the expression is to be evaluated in XPath 1.0 compatibility mode
+ * @param compatible true if XPath 1.0 compatibility mode is enabled
+ */
+
+ public void setBackwardsCompatible(boolean compatible) {
+ backwardsCompatible = compatible;
+ }
+
+ /**
+ * Ask whether the expression is to be evaluated in XPath 1.0 compatibility mode
+ * @return true if XPath 1.0 compatibility mode is enabled
+ */
+
+ public boolean isBackwardsCompatible() {
+ return backwardsCompatible;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression oldop = operand;
+ RoleLocator role = new RoleLocator(RoleLocator.UNARY_EXPR, "-", 0);
+ operand = TypeChecker.staticTypeCheck(operand, SequenceType.OPTIONAL_NUMERIC, backwardsCompatible,
+ role, visitor);
+ operand = visitor.typeCheck(operand, contextItemType);
+ if (operand != oldop) {
+ adoptChildExpression(operand);
+ }
+ if (operand instanceof Literal) {
+ GroundedValue v = ((Literal)operand).getValue();
+ if (v instanceof NumericValue) {
+ return Literal.makeLiteral(((NumericValue)v).negate());
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Determine the data type of the expression, if this is known statically
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return operand.getItemType(th);
+ }
+
+ /**
+ * Evaluate the expression.
+ */
+
+ public NumericValue evaluateItem(XPathContext context) throws XPathException {
+
+ NumericValue v1 = (NumericValue) operand.evaluateItem(context);
+ if (v1 == null) {
+ return backwardsCompatible ? DoubleValue.NaN : null;
+ }
+ return v1.negate();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Negate expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NegateExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new NegateExpression(getBaseExpression().copy());
+ }
+
+ protected String displayOperator(Configuration config) {
+ return "-";
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ *
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+ @Override
+ public String getExpressionName() {
+ return "unaryMinus";
+ }
+}
+
diff --git a/sf/saxon/expr/OrExpression.java b/sf/saxon/expr/OrExpression.java
new file mode 100644
index 0000000..2a7ccf4
--- /dev/null
+++ b/sf/saxon/expr/OrExpression.java
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.OrExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.functions.NotFn;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+
+public class OrExpression extends BooleanExpression {
+
+
+ /**
+ * Construct a boolean OR expression
+ * @param p1 the first operand
+ * @param p2 the second operand
+ */
+
+ public OrExpression(Expression p1, Expression p2) {
+ super(p1, Token.OR, p2);
+ }
+
+
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ final Expression e = super.optimize(visitor, contextItemType);
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+
+ if (e != this) {
+ return e;
+ }
+
+ if (Literal.isConstantBoolean(operand0, true) || Literal.isConstantBoolean(operand1, true)) {
+ // A or true() => true()
+ // true() or B => true()
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ } else if (Literal.isConstantBoolean(operand0, false)) {
+ // false() or B => B
+ return forceToBoolean(operand1, th);
+ } else if (Literal.isConstantBoolean(operand1, false)) {
+ // A or false() => A
+ return forceToBoolean(operand0, th);
+ }
+
+ return this;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new OrExpression(operand0.copy(), operand1.copy());
+ }
+
+
+ /**
+ * Return the negation of this boolean expression, that is, an expression that returns true
+ * when this expression returns false, and vice versa
+ *
+ * @return the negation of this expression
+ */
+
+ public Expression negate() {
+ // Apply de Morgan's laws
+ // not(A or B) => not(A) and not(B)
+ NotFn not0 = (NotFn) SystemFunctionCall.makeSystemFunction("not", new Expression[]{operand0});
+ NotFn not1 = (NotFn) SystemFunctionCall.makeSystemFunction("not", new Expression[]{operand1});
+ return new AndExpression(not0, not1);
+ }
+
+
+ /**
+ * Evaluate as a boolean.
+ */
+
+ public boolean effectiveBooleanValue(XPathContext c) throws XPathException {
+ return operand0.effectiveBooleanValue(c) || operand1.effectiveBooleanValue(c);
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Or expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new OrExpressionCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/PJConverter.java b/sf/saxon/expr/PJConverter.java
new file mode 100644
index 0000000..0c7ae09
--- /dev/null
+++ b/sf/saxon/expr/PJConverter.java
@@ -0,0 +1,823 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.lib.ExternalObjectModel;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.wrapper.VirtualNode;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This class together with its embedded subclasses handles conversion from XPath values
+ * to Java values
+ */
+public abstract class PJConverter implements Serializable {
+
+ private static HashMap<Class, SequenceType> jpmap = new HashMap<Class, SequenceType>();
+
+ static {
+ jpmap.put(boolean.class, SequenceType.SINGLE_BOOLEAN);
+ jpmap.put(Boolean.class, SequenceType.OPTIONAL_BOOLEAN);
+ jpmap.put(String.class, SequenceType.OPTIONAL_STRING);
+ jpmap.put(CharSequence.class, SequenceType.OPTIONAL_STRING);
+ // Mappings for long and int are chosen to avoid static type errors when
+ // a Java method expecting long or int is called with an integer literal
+ jpmap.put(long.class, SequenceType.SINGLE_INTEGER);
+ jpmap.put(Long.class, SequenceType.OPTIONAL_INTEGER);
+ jpmap.put(int.class, SequenceType.SINGLE_INTEGER);
+ jpmap.put(Integer.class, SequenceType.OPTIONAL_INTEGER);
+ jpmap.put(short.class, SequenceType.SINGLE_SHORT);
+ jpmap.put(Short.class, SequenceType.OPTIONAL_SHORT);
+ jpmap.put(byte.class, SequenceType.SINGLE_BYTE);
+ jpmap.put(Byte.class, SequenceType.OPTIONAL_BYTE);
+ jpmap.put(float.class, SequenceType.SINGLE_FLOAT);
+ jpmap.put(Float.class, SequenceType.OPTIONAL_FLOAT);
+ jpmap.put(double.class, SequenceType.SINGLE_DOUBLE);
+ jpmap.put(Double.class, SequenceType.OPTIONAL_DOUBLE);
+ jpmap.put(URI.class, SequenceType.OPTIONAL_ANY_URI);
+ jpmap.put(URL.class, SequenceType.OPTIONAL_ANY_URI);
+ jpmap.put(BigInteger.class, SequenceType.OPTIONAL_INTEGER);
+ jpmap.put(BigDecimal.class, SequenceType.OPTIONAL_DECIMAL);
+ }
+
+
+
+ /**
+ * Get the nearest XPath equivalent to a Java class. A function call will
+ * be type-checked against an XPath function signature in which the Java classes
+ * are replaced by their nearest equivalent XPath types
+ * @param javaClass a Java class
+ * @return the nearest equivalent XPath SequenceType
+ */
+
+ public static SequenceType getEquivalentItemType(Class javaClass) {
+ return jpmap.get(javaClass);
+ }
+
+ /**
+ * Convert an XPath value to a Java value of a specified class
+ *
+ * @param value the supplied XPath value
+ * @param targetClass the class of the required Java value
+ * @param context the XPath dynamic context
+ * @return the corresponding Java value, which is guaranteed to be an instance of the
+ * target class (except that an empty sequence is converted to null)
+ * @throws XPathException if the conversion is not possible or fails
+ */
+
+ /*@Nullable*/ public abstract Object convert(Sequence value, Class targetClass, XPathContext context)
+ throws XPathException;
+
+ /**
+ * Factory method to instantiate a converter from a given XPath type to a given Java class
+ * @param config the Saxon Configuration
+ * @param itemType the item type of the XPath value to be converted
+ * @param cardinality the cardinality of the XPath value to be converted
+ * @param targetClass the Java class required for the conversion result
+ * @return a suitable converter
+ * @throws net.sf.saxon.trans.XPathException if no conversion is possible
+ */
+
+ public static PJConverter allocate(Configuration config, ItemType itemType,
+ int cardinality, Class targetClass)
+ throws XPathException {
+ TypeHierarchy th = config.getTypeHierarchy();
+ if (targetClass == SequenceIterator.class) {
+ return ToSequenceIterator.INSTANCE;
+ }
+ if (targetClass == Sequence.class || targetClass == Item.class) {
+ return Identity.INSTANCE;
+ }
+ if (targetClass == GroundedValue.class | targetClass == SequenceExtent.class) {
+ return ToSequenceExtent.INSTANCE;
+ }
+
+ if (!itemType.isPlainType()) {
+ List<ExternalObjectModel> externalObjectModels = config.getExternalObjectModels();
+ for (ExternalObjectModel model : externalObjectModels) {
+ PJConverter converter = model.getPJConverter(targetClass);
+ if (converter != null) {
+ return converter;
+ }
+ }
+
+ if (NodeInfo.class.isAssignableFrom(targetClass)) {
+ return Identity.INSTANCE;
+ }
+ }
+
+ if (Collection.class.isAssignableFrom(targetClass)) {
+ return ToCollection.INSTANCE;
+ }
+ if (targetClass.isArray()) {
+ PJConverter itemConverter =
+ allocate(config, itemType, StaticProperty.EXACTLY_ONE, targetClass.getComponentType());
+ return new ToArray(itemConverter);
+ }
+ if (!Cardinality.allowsMany(cardinality)) {
+ if (itemType.isPlainType()) {
+ if (itemType == ErrorType.getInstance()) {
+ // supplied value is (); we need to convert it to null; this converter does the job.
+ return StringValueToString.INSTANCE;
+ } else if (th.isSubType(itemType, BuiltInAtomicType.STRING)) {
+ if (targetClass == Object.class || targetClass == String.class || targetClass == CharSequence.class) {
+ return StringValueToString.INSTANCE;
+ } else if (targetClass.isAssignableFrom(StringValue.class)) {
+ return Identity.INSTANCE;
+ } else if (targetClass == char.class || targetClass == Character.class) {
+ return StringValueToChar.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (itemType == BuiltInAtomicType.UNTYPED_ATOMIC) {
+ if (targetClass == Object.class || targetClass == String.class || targetClass == CharSequence.class) {
+ return StringValueToString.INSTANCE;
+ } else if (targetClass.isAssignableFrom(UntypedAtomicValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.BOOLEAN)) {
+ if (targetClass == Object.class || targetClass == Boolean.class || targetClass == boolean.class) {
+ return BooleanValueToBoolean.INSTANCE;
+ } else if (targetClass.isAssignableFrom(BooleanValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.INTEGER)) {
+ if (targetClass == Object.class || targetClass == BigInteger.class) {
+ return IntegerValueToBigInteger.INSTANCE;
+ } else if (targetClass == long.class || targetClass == Long.class) {
+ return IntegerValueToLong.INSTANCE;
+ } else if (targetClass == int.class || targetClass == Integer.class) {
+ return IntegerValueToInt.INSTANCE;
+ } else if (targetClass == short.class || targetClass == Short.class) {
+ return IntegerValueToShort.INSTANCE;
+ } else if (targetClass == byte.class || targetClass == Byte.class) {
+ return IntegerValueToByte.INSTANCE;
+ } else if (targetClass == char.class || targetClass == Character.class) {
+ return IntegerValueToChar.INSTANCE;
+ } else if (targetClass == double.class || targetClass == Double.class) {
+ return NumericValueToDouble.INSTANCE;
+ } else if (targetClass == float.class || targetClass == Float.class) {
+ return NumericValueToFloat.INSTANCE;
+ } else if (targetClass == BigDecimal.class) {
+ return NumericValueToBigDecimal.INSTANCE;
+ } else if (targetClass.isAssignableFrom(IntegerValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.DECIMAL)) {
+ if (targetClass == Object.class || targetClass == BigDecimal.class) {
+ return NumericValueToBigDecimal.INSTANCE;
+ } else if (targetClass == double.class || targetClass == Double.class) {
+ return NumericValueToDouble.INSTANCE;
+ } else if (targetClass == float.class || targetClass == Float.class) {
+ return NumericValueToFloat.INSTANCE;
+ } else if (targetClass.isAssignableFrom(DecimalValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.FLOAT)) {
+ if (targetClass == Object.class || targetClass == Float.class || targetClass == float.class) {
+ return NumericValueToFloat.INSTANCE;
+ } else if (targetClass == double.class || targetClass == Double.class) {
+ return NumericValueToDouble.INSTANCE;
+ } else if (targetClass.isAssignableFrom(FloatValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.DOUBLE)) {
+ if (targetClass == Object.class || targetClass == Double.class || targetClass == double.class) {
+ return NumericValueToDouble.INSTANCE;
+ } else if (targetClass.isAssignableFrom(DoubleValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ return Atomic.INSTANCE;
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.ANY_URI)) {
+ if (targetClass == Object.class || URI.class.isAssignableFrom(targetClass)) {
+ return AnyURIValueToURI.INSTANCE;
+ } else if (URL.class.isAssignableFrom(targetClass)) {
+ return AnyURIValueToURL.INSTANCE;
+ } else if (targetClass == String.class || targetClass == CharSequence.class) {
+ return StringValueToString.INSTANCE;
+ } else if (targetClass.isAssignableFrom(AnyURIValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.QNAME)) {
+ if (targetClass == Object.class || targetClass == javax.xml.namespace.QName.class) {
+ // Note JDK1.5 dependency
+ return QualifiedNameValueToQName.INSTANCE;
+ } else if (targetClass.isAssignableFrom(QNameValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.NOTATION)) {
+ if (targetClass == Object.class || targetClass == javax.xml.namespace.QName.class) {
+ // Note JDK1.5 dependency
+ return QualifiedNameValueToQName.INSTANCE;
+ } else if (targetClass.isAssignableFrom(NotationValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.DURATION)) {
+ if (targetClass.isAssignableFrom(DurationValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.DATE_TIME)) {
+ if (targetClass.isAssignableFrom(DateTimeValue.class)) {
+ return Identity.INSTANCE;
+ } else if (targetClass == java.util.Date.class) {
+ return CalendarValueToDate.INSTANCE;
+ } else if (targetClass == java.util.Calendar.class) {
+ return CalendarValueToCalendar.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.DATE)) {
+ if (targetClass.isAssignableFrom(DateValue.class)) {
+ return Identity.INSTANCE;
+ } else if (targetClass == java.util.Date.class) {
+ return CalendarValueToDate.INSTANCE;
+ } else if (targetClass == java.util.Calendar.class) {
+ return CalendarValueToCalendar.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.TIME)) {
+ if (targetClass.isAssignableFrom(TimeValue.class)) {
+ return Identity.INSTANCE;
+ } else if (targetClass == java.util.Date.class) {
+ return CalendarValueToDate.INSTANCE;
+ } else if (targetClass == java.util.Calendar.class) {
+ return CalendarValueToCalendar.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.G_YEAR)) {
+ if (targetClass.isAssignableFrom(GYearValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.G_YEAR_MONTH)) {
+ if (targetClass.isAssignableFrom(GYearMonthValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.G_MONTH)) {
+ if (targetClass.isAssignableFrom(GMonthValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.G_MONTH_DAY)) {
+ if (targetClass.isAssignableFrom(GMonthDayValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.G_DAY)) {
+ if (targetClass.isAssignableFrom(GDayValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.BASE64_BINARY)) {
+ if (targetClass.isAssignableFrom(Base64BinaryValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else if (th.isSubType(itemType, BuiltInAtomicType.HEX_BINARY)) {
+ if (targetClass.isAssignableFrom(HexBinaryValue.class)) {
+ return Identity.INSTANCE;
+ } else {
+ throw cannotConvert(itemType, targetClass, config);
+ }
+ } else {
+ return Atomic.INSTANCE;
+ }
+
+ } else if (itemType instanceof ExternalObjectType) {
+ return UnwrapExternalObject.INSTANCE;
+
+ } else if (itemType instanceof ErrorType) {
+ return ToNull.INSTANCE;
+
+ } else if (itemType instanceof NodeTest) {
+ if (NodeInfo.class.isAssignableFrom(targetClass)) {
+ return Identity.INSTANCE;
+ } else {
+ return General.INSTANCE;
+ }
+
+ } else {
+ // ItemType is item()
+ return General.INSTANCE;
+ }
+ } else {
+ // Cardinality allows many (but target type is not a collection)
+ return General.INSTANCE;
+ }
+ }
+
+ private static XPathException cannotConvert(ItemType source, Class target, Configuration config) {
+ return new XPathException("Cannot convert from " + source.toString() +
+ " to " + target.getName());
+ }
+
+ /**
+ * Static method to get a converter from an XPath sequence of nodes to the representation of a NodeList
+ * in an external object model (this is really a special for DOM, which uses NodeList rather than general
+ * purpose Java collection classes)
+ * @param config the Saxon configuration
+ * @param node an object representing a node in an external model
+ * @return the Java object representing the external node
+ */
+
+ public static PJConverter allocateNodeListCreator(Configuration config, Object node) {
+ List<ExternalObjectModel> externalObjectModels = config.getExternalObjectModels();
+ for (ExternalObjectModel model : externalObjectModels) {
+ PJConverter converter = model.getNodeListCreator(node);
+ if (converter != null) {
+ return converter;
+ }
+ }
+ return ToCollection.INSTANCE;
+ }
+
+ public static class ToSequenceIterator extends PJConverter {
+
+ public static final ToSequenceIterator INSTANCE = new ToSequenceIterator();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ return value.iterate();
+ }
+
+ }
+
+ public static class ToNull extends PJConverter {
+
+ public static final ToNull INSTANCE = new ToNull();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ return null;
+ }
+
+ }
+
+ public static class ToSequenceExtent extends PJConverter {
+
+ public static final ToSequenceExtent INSTANCE = new ToSequenceExtent();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ return SequenceExtent.makeSequenceExtent(value.iterate());
+ }
+
+ }
+
+ /**
+ * Converter for use when the target class is a collection class. Also used when the target
+ * class is Object
+ */
+
+ public static class ToCollection extends PJConverter {
+
+ public static final ToCollection INSTANCE = new ToCollection();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ Collection<Object> list;
+ if (targetClass.isAssignableFrom(ArrayList.class)) {
+ list = new ArrayList<Object>(100);
+ } else {
+ try {
+ list = (Collection<Object>)targetClass.newInstance();
+ } catch (InstantiationException e) {
+ XPathException de = new XPathException("Cannot instantiate collection class " + targetClass);
+ de.setXPathContext(context);
+ throw de;
+ } catch (IllegalAccessException e) {
+ XPathException de = new XPathException("Cannot access collection class " + targetClass);
+ de.setXPathContext(context);
+ throw de;
+ }
+ }
+ Configuration config = context.getConfiguration();
+ SequenceIterator<? extends Item> iter = value.iterate();
+ while (true) {
+ Item it = iter.next();
+ if (it == null) {
+ return list;
+ }
+ if (it instanceof AtomicValue) {
+ PJConverter pj = allocate(
+ config, ((AtomicValue)it).getItemType(), StaticProperty.EXACTLY_ONE, Object.class);
+ list.add(pj.convert(it, Object.class, context));
+ //list.add(((AtomicValue)it).convertToJava(Object.class, context));
+ } else if (it instanceof VirtualNode) {
+ list.add(((VirtualNode)it).getRealNode());
+ } else {
+ list.add(it);
+ }
+ }
+ //return Value.asValue(value).convertToJavaList(list, context);
+ }
+
+ }
+
+ /**
+ * Converter for use when the target class is an array
+ */
+
+ public static class ToArray extends PJConverter {
+
+ private PJConverter itemConverter;
+
+ public ToArray(PJConverter itemConverter) {
+ this.itemConverter = itemConverter;
+ }
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ Class componentClass = targetClass.getComponentType();
+ List<Object> list = new ArrayList<Object>(20);
+ SequenceIterator<? extends Item> iter = value.iterate();
+ while (true) {
+ Item item = iter.next();
+ if (item == null) break;
+ Object obj = itemConverter.convert(item, componentClass, context);
+ if (obj != null) {
+ list.add(obj);
+ }
+ }
+ Object array = Array.newInstance(componentClass, list.size());
+ for (int i=0; i<list.size(); i++) {
+ Array.set(array, i, list.get(i));
+ }
+ return array;
+ //return list.toArray((Object[])array);
+ }
+
+ }
+
+ public static class Identity extends PJConverter {
+
+ public static final Identity INSTANCE = new Identity();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ if (value instanceof Closure) {
+ value = ((Closure)value).reduce();
+ }
+ if (value instanceof SingletonItem) {
+ value = ((SingletonItem)value).asItem();
+ }
+ if (value instanceof VirtualNode) {
+ Object obj = ((VirtualNode)value).getRealNode();
+ if (targetClass.isAssignableFrom(obj.getClass())) {
+ return obj;
+ }
+ }
+ if (targetClass.isAssignableFrom(value.getClass())) {
+ return value;
+ } else {
+ if (value instanceof SingletonItem) {
+ value = ((SingletonItem)value).asItem();
+ }
+ if (targetClass.isAssignableFrom(value.getClass())) {
+ return value;
+ } else if (value instanceof EmptySequence) {
+ return null;
+ } else {
+ throw new XPathException("Cannot convert value " + value.getClass() + " of type " +
+ SequenceTool.getItemType(value, context.getConfiguration().getTypeHierarchy()) +
+ " to class " + targetClass.getName());
+ }
+ }
+ }
+
+ }
+
+ public static class UnwrapExternalObject extends PJConverter {
+
+ public static final UnwrapExternalObject INSTANCE = new UnwrapExternalObject();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ Item head = value.head();
+ if (head == null) {
+ return null;
+ }
+ if (!(head instanceof ObjectValue)) {
+ if (Sequence.class.isAssignableFrom(targetClass)) {
+ head = new ObjectValue(value);
+ } else {
+ throw new XPathException("Expected external object of class " + targetClass.getName() +
+ ", got " + head.getClass());
+ }
+ }
+ Object obj = ((ObjectValue)head).getObject();
+ if (!targetClass.isAssignableFrom(obj.getClass())) {
+ throw new XPathException("External object has wrong class (is "
+ + obj.getClass().getName() + ", expected " + targetClass.getName());
+ }
+ return obj;
+ }
+
+ }
+
+ public static class StringValueToString extends PJConverter {
+
+ public static final StringValueToString INSTANCE = new StringValueToString();
+
+ public String convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ Item first = value.head();
+ return first == null ? null : first.getStringValue();
+ }
+
+ }
+
+ public static class StringValueToChar extends PJConverter {
+
+ public static final StringValueToChar INSTANCE = new StringValueToChar();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ Item first = value.head();
+ if (first == null) {
+ return null;
+ }
+ String str = first.getStringValue();
+ if (str.length() == 1) {
+ return str.charAt(0);
+ } else {
+ XPathException de = new XPathException("Cannot convert xs:string to Java char unless length is 1");
+ de.setXPathContext(context);
+ de.setErrorCode(SaxonErrorCode.SXJE0005);
+ throw de;
+ }
+ }
+
+ }
+
+
+ public static class BooleanValueToBoolean extends PJConverter {
+
+ public static final BooleanValueToBoolean INSTANCE = new BooleanValueToBoolean();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ BooleanValue bv = (BooleanValue)value.head();
+ assert bv != null;
+ return bv.getBooleanValue();
+ }
+
+ }
+
+ public static class IntegerValueToBigInteger extends PJConverter {
+
+ public static final IntegerValueToBigInteger INSTANCE = new IntegerValueToBigInteger();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ IntegerValue val = (IntegerValue)value.head();
+ return (val == null ? null : val.asBigInteger());
+ }
+
+ }
+
+ public static class IntegerValueToLong extends PJConverter {
+
+ public static final IntegerValueToLong INSTANCE = new IntegerValueToLong();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ IntegerValue iv = (IntegerValue)value.head();
+ assert iv != null;
+ return iv.longValue();
+ }
+
+ }
+
+ public static class IntegerValueToInt extends PJConverter {
+
+ public static final IntegerValueToInt INSTANCE = new IntegerValueToInt();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ IntegerValue iv = (IntegerValue)value.head();
+ assert iv != null;
+ return (int) iv.longValue();
+ }
+
+ }
+
+ public static class IntegerValueToShort extends PJConverter {
+
+ public static final IntegerValueToShort INSTANCE = new IntegerValueToShort();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ IntegerValue iv = (IntegerValue)value.head();
+ assert iv != null;
+ return (short) iv.longValue();
+ }
+
+ }
+
+ public static class IntegerValueToByte extends PJConverter {
+
+ public static final IntegerValueToByte INSTANCE = new IntegerValueToByte();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ IntegerValue iv = (IntegerValue)value.head();
+ assert iv != null;
+ return (byte) iv.longValue();
+ }
+
+ }
+
+ public static class IntegerValueToChar extends PJConverter {
+
+ public static final IntegerValueToChar INSTANCE = new IntegerValueToChar();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ IntegerValue iv = (IntegerValue)value.head();
+ assert iv != null;
+ return (char) iv.longValue();
+ }
+
+ }
+
+ public static class NumericValueToBigDecimal extends PJConverter {
+
+ public static final NumericValueToBigDecimal INSTANCE = new NumericValueToBigDecimal();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ NumericValue nv = (NumericValue)value.head();
+ return (nv == null ? null : nv.getDecimalValue());
+ }
+
+ }
+
+ public static class NumericValueToDouble extends PJConverter {
+
+ public static final NumericValueToDouble INSTANCE = new NumericValueToDouble();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ NumericValue nv = (NumericValue)value.head();
+ assert nv != null;
+ return nv.getDoubleValue();
+ }
+
+ }
+
+ public static class NumericValueToFloat extends PJConverter {
+
+ public static final NumericValueToFloat INSTANCE = new NumericValueToFloat();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ NumericValue nv = (NumericValue)value.head();
+ assert nv != null;
+ return nv.getFloatValue();
+ }
+
+ }
+
+ public static class AnyURIValueToURI extends PJConverter {
+
+ public static final AnyURIValueToURI INSTANCE = new AnyURIValueToURI();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ AnyURIValue av = (AnyURIValue)value.head();
+ try {
+ return (av == null ? null : new URI(((AnyURIValue)value).getStringValue()));
+ } catch (URISyntaxException err) {
+ throw new XPathException("The anyURI value '" + value + "' is not an acceptable Java URI");
+ }
+ }
+
+ }
+
+ public static class AnyURIValueToURL extends PJConverter {
+
+ public static final AnyURIValueToURL INSTANCE = new AnyURIValueToURL();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ AnyURIValue av = (AnyURIValue)value.head();
+ try {
+ return (av == null ? null : new URL(((AnyURIValue)value).getStringValue()));
+ } catch (MalformedURLException err) {
+ throw new XPathException("The anyURI value '" + value + "' is not an acceptable Java URL");
+ }
+ }
+
+ }
+
+ public static class QualifiedNameValueToQName extends PJConverter {
+
+ public static final QualifiedNameValueToQName INSTANCE = new QualifiedNameValueToQName();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ QualifiedNameValue qv = (QualifiedNameValue)value.head();
+ return (qv == null ? null : qv.toJaxpQName());
+ }
+
+ }
+
+ public static class CalendarValueToDate extends PJConverter {
+
+ public static final CalendarValueToDate INSTANCE = new CalendarValueToDate();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ CalendarValue cv = (CalendarValue)value.head();
+ return (cv == null ? null : cv.getCalendar().getTime());
+ }
+
+ }
+
+ public static class CalendarValueToCalendar extends PJConverter {
+
+ public static final CalendarValueToCalendar INSTANCE = new CalendarValueToCalendar();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ CalendarValue cv = (CalendarValue)value.head();
+ return (cv == null ? null : cv.getCalendar());
+ }
+
+ }
+
+
+ /**
+ * Converter for use when the source object is an atomic value, but nothing more is known
+ * statically.
+ */
+
+ public static class Atomic extends PJConverter {
+
+ public static final Atomic INSTANCE = new Atomic();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context) throws XPathException {
+ // TODO: not really worth separating from General
+ AtomicValue item = (AtomicValue)value.head();
+ if (item == null) {
+ return null;
+ }
+ Configuration config = context.getConfiguration();
+ PJConverter converter = allocate(
+ config, item.getItemType(), StaticProperty.EXACTLY_ONE, targetClass);
+ return converter.convert(item, targetClass, context);
+ }
+ }
+
+ /**
+ * General-purpose converter when nothing more specific is available.
+ * (Provided largely as a transition aid)
+ */
+
+ public static class General extends PJConverter {
+
+ public static final General INSTANCE = new General();
+
+ public Object convert(Sequence value, Class targetClass, XPathContext context)
+ throws XPathException {
+ Configuration config = context.getConfiguration();
+ GroundedValue gv = SequenceTool.toGroundedValue(value);
+ PJConverter converter = allocate(
+ config, SequenceTool.getItemType(gv, config.getTypeHierarchy()), SequenceTool.getCardinality(gv), targetClass);
+ if (converter instanceof General) {
+ converter = Identity.INSTANCE;
+ }
+ return converter.convert(value, targetClass, context);
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/PairIterator.java b/sf/saxon/expr/PairIterator.java
new file mode 100644
index 0000000..7fbc62a
--- /dev/null
+++ b/sf/saxon/expr/PairIterator.java
@@ -0,0 +1,79 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * An iterator over a pair of objects (typically sub-expressions of an expression)
+ */
+public class PairIterator<T> implements Iterator<T> {
+
+ private T one;
+ private T two;
+ private int pos = 0;
+
+ /**
+ * Create an iterator over two objects
+ * @param one the first object to be returned
+ * @param two the second object to be returned
+ */
+
+ public PairIterator(T one, T two) {
+ this.one = one;
+ this.two = two;
+ }
+
+ /**
+ * Returns <tt>true</tt> if the iteration has more elements. (In other
+ * words, returns <tt>true</tt> if <tt>next</tt> would return an element
+ * rather than throwing an exception.)
+ *
+ * @return <tt>true</tt> if the iterator has more elements.
+ */
+
+ public boolean hasNext() {
+ return pos<2;
+ }
+
+ /**
+ * Returns the next element in the iteration.
+ *
+ * @return the next element in the iteration.
+ * @exception NoSuchElementException iteration has no more elements.
+ */
+ public T next() {
+ switch (pos++) {
+ case 0: return one;
+ case 1: return two;
+ default: throw new NoSuchElementException();
+ }
+ }
+
+ /**
+ *
+ * Removes from the underlying collection the last element returned by the
+ * iterator (optional operation). This method can be called only once per
+ * call to <tt>next</tt>. The behavior of an iterator is unspecified if
+ * the underlying collection is modified while the iteration is in
+ * progress in any way other than by calling this method.
+ *
+ * @exception UnsupportedOperationException if the <tt>remove</tt>
+ * operation is not supported by this Iterator.
+
+ * @exception IllegalStateException if the <tt>next</tt> method has not
+ * yet been called, or the <tt>remove</tt> method has already
+ * been called after the last call to the <tt>next</tt>
+ * method.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/sf/saxon/expr/ParentNodeExpression.java b/sf/saxon/expr/ParentNodeExpression.java
new file mode 100644
index 0000000..ce932d6
--- /dev/null
+++ b/sf/saxon/expr/ParentNodeExpression.java
@@ -0,0 +1,167 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ParentNodeExpressionCompiler;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.List;
+
+/**
+* Class ParentNodeExpression represents the XPath expression ".." or "parent::node()"
+*/
+
+public class ParentNodeExpression extends SingleNodeExpression {
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "parentStep";
+ }
+
+ /**
+ * Customize the error message on type checking
+ */
+
+ protected String noContextMessage() {
+ return "Cannot select the parent of the context node";
+ }
+
+ /**
+ * Return the node selected by this SingleNodeExpression
+ * @param context The context for the evaluation
+ * @return the parent of the current node defined by the context
+ */
+
+ /*@Nullable*/ public NodeInfo getNode(XPathContext context) throws XPathException {
+ Item item = context.getContextItem();
+ if (item==null) {
+ dynamicError("The context item is absent", "XPDY0002", context);
+ }
+ if (item instanceof NodeInfo) {
+ return ((NodeInfo)item).getParent();
+ } else {
+ dynamicError("The context item for the parent axis (..) is not a node", "XPTY0020", context);
+ return null;
+ }
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new ParentNodeExpression();
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ AxisExpression parent = new AxisExpression(AxisInfo.PARENT, AnyNodeTest.getInstance());
+ parent.setContainer(getContainer());
+ return parent.addToPathMap(pathMap, pathMapNodeSet);
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
+ * XPathContext.CURRENT_NODE
+ */
+/*
+ public int getDependencies() {
+ return StaticProperty.CONTEXT_ITEM;
+ }
+*/
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return (other instanceof ParentNodeExpression);
+ }
+
+ /**
+ * get HashCode for comparing two expressions
+ */
+
+ public int hashCode() {
+ return "ParentNodeExpression".hashCode();
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return "..";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement(getExpressionName());
+ destination.endElement();
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ if (syntacticContext == INSPECTION_CONTEXT) {
+ return W3C_MOTIONLESS;
+ } else {
+ if (reasons != null) {
+ reasons.add("Parent axis (when not in inspection context) is free-ranging");
+ }
+ return W3C_FREE_RANGING;
+ }
+ }
+
+
+ /**
+ * Return the compiler of the ParentNode expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ParentNodeExpressionCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/PendingUpdateList.java b/sf/saxon/expr/PendingUpdateList.java
new file mode 100644
index 0000000..cd084e6
--- /dev/null
+++ b/sf/saxon/expr/PendingUpdateList.java
@@ -0,0 +1,48 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.MutableNodeInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.Set;
+
+/**
+ * A PendingUpdateList is created by updating expressions in XQuery Update.
+ *
+ * <p>The implementation of this interface is in Saxon-EE.</p>
+ */
+public interface PendingUpdateList {
+
+ /**
+ * Apply the pending updates
+ * @param context the XPath dynamic evaluation context
+ * @param validationMode the revalidation mode from the static context
+ * @throws XPathException
+ */
+
+ void apply(XPathContext context, int validationMode) throws XPathException;
+
+ /**
+ * Get the root nodes of the trees that are affected by updates in the pending update list
+ * @return the root nodes of affected trees, as a Set
+ */
+
+ Set<MutableNodeInfo> getAffectedTrees();
+
+ /**
+ * Add a put() action to the pending update list
+ * @param node (the first argument of put())
+ * @param uri (the second argument of put())
+ * @param originator the originating put() expression, for diagnostics
+ */
+
+ void addPutAction(NodeInfo node, String uri, Expression originator) throws XPathException;
+}
+
diff --git a/sf/saxon/expr/PositionVariable.java b/sf/saxon/expr/PositionVariable.java
new file mode 100644
index 0000000..60d6e47
--- /dev/null
+++ b/sf/saxon/expr/PositionVariable.java
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+import java.io.Serializable;
+
+/**
+ * Represents the defining occurrence of the position variable in a for expression
+ * within an expression, for example the $p in "for $x at $p in ...".
+ */
+
+public class PositionVariable implements Binding, Serializable {
+
+ private StructuredQName variableName;
+ private int slotNumber = -999;
+
+ /**
+ * Create a RangeVariable
+ */
+
+ public PositionVariable(){}
+
+ /**
+ * Get the name of the variable, as a namepool name code
+ * @return the nameCode
+ */
+
+ public StructuredQName getVariableQName() {
+ return variableName;
+ }
+
+ public void addReference(boolean isLoopingReference) {
+
+ }
+
+ /**
+ * Get the required type (declared type) of the variable
+ * @return the required type
+ */
+
+ public SequenceType getRequiredType() {
+ return SequenceType.SINGLE_INTEGER;
+ }
+
+ /**
+ * If the variable is bound to an integer, get the minimum and maximum possible values.
+ * Return null if unknown or not applicable
+ */
+ public IntegerValue[] getIntegerBoundsForVariable() {
+ return new IntegerValue[]{Int64Value.PLUS_ONE, Expression.MAX_SEQUENCE_LENGTH};
+ }
+
+ /**
+ * Set the name of the variable
+ * @param variableName the name of the variable
+ */
+
+ public void setVariableQName(StructuredQName variableName) {
+ this.variableName = variableName;
+ }
+
+ /**
+ * Set the slot number for the range variable
+ * @param nr the slot number to be used
+ */
+
+ public void setSlotNumber(int nr) {
+ slotNumber = nr;
+ }
+
+ /**
+ * If this is a local variable held on the local stack frame, return the corresponding slot number.
+ * In other cases, return -1.
+ */
+
+ public int getLocalSlotNumber() {
+ return slotNumber;
+ }
+
+ /**
+ * Get the value of the range variable
+ */
+
+ public Sequence evaluateVariable(XPathContext context) throws XPathException {
+ return context.evaluateLocalVariable(slotNumber);
+ }
+
+ /**
+ * Test whether it is permitted to assign to the variable using the saxon:assign
+ * extension element. This will only be for an XSLT global variable where the extra
+ * attribute saxon:assignable="yes" is present.
+ *
+ * @return true if the binding is assignable
+ */
+
+ public boolean isAssignable() {
+ return false;
+ }
+
+ /**
+ * Indicate whether the binding is local or global. A global binding is one that has a fixed
+ * value for the life of a query or transformation; any other binding is local.
+ *
+ * @return true if the binding is global
+ */
+
+ public boolean isGlobal() {
+ return false;
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/QuantifiedExpression.java b/sf/saxon/expr/QuantifiedExpression.java
new file mode 100644
index 0000000..2d58d53
--- /dev/null
+++ b/sf/saxon/expr/QuantifiedExpression.java
@@ -0,0 +1,366 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.QuantifiedExpressionCompiler;
+import com.saxonica.stream.Streamability;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.functions.BooleanFn;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.List;
+
+/**
+* A QuantifiedExpression tests whether some/all items in a sequence satisfy
+* some condition.
+*/
+
+public class QuantifiedExpression extends Assignation {
+
+ private int operator; // Token.SOME or Token.EVERY
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return (operator == Token.SOME ? "some" : "every");
+ }
+
+ /**
+ * Set the operator, either {@link Token#SOME} or {@link Token#EVERY}
+ * @param operator the operator
+ */
+
+ public void setOperator(int operator) {
+ this.operator = operator;
+ }
+
+ /**
+ * Get the operator, either {@link Token#SOME} or {@link Token#EVERY}
+ * @return the operator
+ */
+
+ public int getOperator() {
+ return operator;
+ }
+
+ /**
+ * Determine the static cardinality
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+
+ // The order of events is critical here. First we ensure that the type of the
+ // sequence expression is established. This is used to establish the type of the variable,
+ // which in turn is required when type-checking the action part.
+
+ sequence = visitor.typeCheck(sequence, contextItemType);
+ if (Literal.isEmptySequence(sequence)) {
+ return Literal.makeLiteral(BooleanValue.get(operator != Token.SOME));
+ }
+
+ // "some" and "every" have no ordering constraints
+
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ sequence = ExpressionTool.unsorted(opt, sequence, false);
+
+ SequenceType decl = getRequiredType();
+ SequenceType sequenceType = SequenceType.makeSequenceType(decl.getPrimaryType(),
+ StaticProperty.ALLOWS_ZERO_OR_MORE);
+ RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, getVariableQName(), 0);
+ //role.setSourceLocator(this);
+ sequence = TypeChecker.strictTypeCheck(
+ sequence, sequenceType, role, visitor.getStaticContext());
+ ItemType actualItemType = sequence.getItemType(th);
+ refineTypeInformation(actualItemType,
+ StaticProperty.EXACTLY_ONE,
+ null,
+ sequence.getSpecialProperties(), visitor, this);
+
+ //declaration = null; // let the garbage collector take it
+
+ action = visitor.typeCheck(action, contextItemType);
+ XPathException err = TypeChecker.ebvError(action, visitor.getConfiguration().getTypeHierarchy());
+ if (err != null) {
+ err.setLocator(this);
+ throw err;
+ }
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+
+ sequence = visitor.optimize(sequence, contextItemType);
+ action = visitor.optimize(action, contextItemType);
+ Expression ebv = BooleanFn.rewriteEffectiveBooleanValue(action, visitor, contextItemType);
+ if (ebv != null) {
+ action = ebv;
+ adoptChildExpression(ebv);
+ }
+ PromotionOffer offer = new PromotionOffer(opt);
+ offer.containingExpression = this;
+ offer.action = PromotionOffer.RANGE_INDEPENDENT;
+ offer.bindingList = new Binding[] {this};
+ action = doPromotion(action, offer);
+ if (offer.containingExpression instanceof LetExpression) {
+ offer.containingExpression =
+ visitor.optimize(visitor.typeCheck(offer.containingExpression, contextItemType), contextItemType);
+ }
+ Expression e2 = offer.containingExpression;
+ if (e2 != this) {
+ return e2;
+ }
+
+ // if streaming, convert to an expression that can be streamed
+
+ if (visitor.isOptimizeForStreaming()) {
+ Expression e3 = visitor.getConfiguration().obtainOptimizer().optimizeQuantifiedExpressionForStreaming(this);
+ if (e3 != this) {
+ return visitor.optimize(e3, contextItemType);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Check to ensure that this expression does not contain any updating subexpressions.
+ * This check is overridden for those expressions that permit updating subexpressions.
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression has a non-permitted updateing subexpression
+ */
+
+ public void checkForUpdatingSubexpressions() throws XPathException {
+ sequence.checkForUpdatingSubexpressions();
+ action.checkForUpdatingSubexpressions();
+ }
+
+ /**
+ * Determine whether this is an updating expression as defined in the XQuery update specification
+ * @return true if this is an updating expression
+ */
+
+ public boolean isUpdatingExpression() {
+ return false;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ QuantifiedExpression qe = new QuantifiedExpression();
+ qe.setOperator(operator);
+ qe.setVariableQName(variableName);
+ qe.setRequiredType(requiredType);
+ qe.setSequence(sequence.copy());
+ Expression newAction = action.copy();
+ qe.setAction(newAction);
+ qe.variableName = variableName;
+ qe.slotNumber = slotNumber;
+ ExpressionTool.rebindVariableReferences(newAction, this, qe);
+ return qe;
+ }
+
+
+ /**
+ * Determine the special properties of this expression
+ * @return {@link StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+//#ifdefined STREAM
+
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ int sequenceStreamability = sequence.getStreamability(syntacticContext, allowExtensions, reasons);
+ int actionStreamability = action.getStreamability(syntacticContext, allowExtensions, reasons);
+ if (sequenceStreamability == W3C_MOTIONLESS && actionStreamability == W3C_MOTIONLESS) {
+ return W3C_MOTIONLESS;
+ }
+ if (allowExtensions && !ExpressionTool.dependsOnFocus(action)) {
+ // See if we can rewrite as a streamable filter expression
+ Expression rewrite = Streamability.rewriteQuantifiedExpressionAsFilterExpression(this);
+ if (rewrite != null) {
+ int rs = rewrite.getStreamability(syntacticContext, allowExtensions, reasons);
+ if (rs == W3C_FREE_RANGING) {
+ reasons.add("Equivalent filter expression is not streamable");
+ } else {
+ return rs;
+ }
+ } else {
+ reasons.add("Unable to convert the some/every expression to an equivalent filter expression");
+ }
+ }
+ reasons.add("A some/every expression is free-ranging unless both subexpressions are motionless");
+ return W3C_FREE_RANGING;
+
+ }
+
+
+//#endif
+
+ /**
+ * Evaluate the expression to return a singleton value
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Get the result as a boolean
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+
+ // First create an iteration of the base sequence.
+
+ SequenceIterator base = sequence.iterate(context);
+
+ // Now test to see if some or all of the tests are true. The same
+ // logic is used for the SOME and EVERY operators
+
+ final boolean some = (operator==Token.SOME);
+ int slot = getLocalSlotNumber();
+ while (true) {
+ final Item it = base.next();
+ if (it == null) {
+ break;
+ }
+ context.setLocalVariable(slot, it);
+ if (some == action.effectiveBooleanValue(context)) {
+ base.close();
+ return some;
+ }
+ }
+ return !some;
+ }
+
+
+ /**
+ * Determine the data type of the items returned by the expression
+ * @return Type.BOOLEAN
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the QuantifiedExpression expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new QuantifiedExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ * @return a representation of the expression as a string
+ */
+
+ public String toString() {
+ return (operator==Token.SOME ? "some" : "every") + " $" + getVariableEQName() +
+ " in " + sequence.toString() + " satisfies " +
+ ExpressionTool.parenthesize(action);
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement(Token.tokens[operator]);
+ out.emitAttribute("variable", getVariableName());
+ out.startSubsidiaryElement("in");
+ sequence.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("satisfies");
+ action.explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/RangeExpression.java b/sf/saxon/expr/RangeExpression.java
new file mode 100644
index 0000000..896aa2c
--- /dev/null
+++ b/sf/saxon/expr/RangeExpression.java
@@ -0,0 +1,204 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.RangeExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerRange;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+
+
+/**
+* A RangeExpression is an expression that represents an integer sequence as
+* a pair of end-points (for example "x to y").
+* If the end-points are equal, the sequence is of length one.
+ * <p>From Saxon 7.8, the sequence must be ascending; if the end-point is less
+ * than the start-point, an empty sequence is returned. This is to allow
+ * expressions of the form "for $i in 1 to count($seq) return ...." </p>
+*/
+
+public class RangeExpression extends BinaryExpression {
+
+ /**
+ * Construct a RangeExpression
+ * @param start expression that computes the start of the range
+ * @param op represents the operator "to", needed only because this class is a subclass of
+ * BinaryExpression which needs an operator
+ * @param end expression that computes the end of the range
+ */
+
+ public RangeExpression(Expression start, int op, Expression end) {
+ super(start, op, end);
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand0 = visitor.typeCheck(operand0, contextItemType);
+ operand1 = visitor.typeCheck(operand1, contextItemType);
+
+ boolean backCompat = visitor.getStaticContext().isInBackwardsCompatibleMode();
+ RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, "to", 0);
+ //role0.setSourceLocator(this);
+ operand0 = TypeChecker.staticTypeCheck(
+ operand0, SequenceType.OPTIONAL_INTEGER, backCompat, role0, visitor);
+
+ RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, "to", 1);
+ //role1.setSourceLocator(this);
+ operand1 = TypeChecker.staticTypeCheck(
+ operand1, SequenceType.OPTIONAL_INTEGER, backCompat, role1, visitor);
+
+ return makeConstantRange();
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand0 = visitor.optimize(operand0, contextItemType);
+ operand1 = visitor.optimize(operand1, contextItemType);
+ return makeConstantRange();
+
+ }
+
+ private Expression makeConstantRange() throws XPathException {
+ if (operand0 instanceof Literal && operand1 instanceof Literal) {
+ GroundedValue v0 = ((Literal)operand0).getValue();
+ GroundedValue v1 = ((Literal)operand1).getValue();
+ if (v0 instanceof Int64Value && v1 instanceof Int64Value) {
+ long i0 = ((Int64Value)v0).longValue();
+ long i1 = ((Int64Value)v1).longValue();
+ Literal result;
+ if (i0 > i1) {
+ result = Literal.makeEmptySequence();
+ } else if (i0 == i1) {
+ result = Literal.makeLiteral(Int64Value.makeIntegerValue(i0));
+ } else {
+ if (i1 - i0 > Integer.MAX_VALUE) {
+ throw new XPathException("Maximum length of sequence in Saxon is " + Integer.MAX_VALUE, SaxonErrorCode.SXXP0006);
+ }
+ result = Literal.makeLiteral(new IntegerRange(i0, i1));
+ }
+ ExpressionTool.copyLocationInfo(this, result);
+ return result;
+ }
+ }
+ return this;
+ }
+
+
+ /**
+ * Get the data type of the items returned
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.INTEGER;
+ }
+
+ /**
+ * Determine the static cardinality
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ /*@Nullable*/@Override
+ public IntegerValue[] getIntegerBounds() {
+ IntegerValue[] start = operand0.getIntegerBounds();
+ IntegerValue[] end = operand0.getIntegerBounds();
+ if (start == null || end == null) {
+ return null;
+ } else {
+ // range is from the smallest possible start value to the largest possible end value
+ return new IntegerValue[]{start[0], end[1]};
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new RangeExpression(operand0.copy(), operator, operand1.copy());
+ }
+
+ /**
+ * Return an iteration over the sequence
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ IntegerValue av1 = (IntegerValue)operand0.evaluateItem(context);
+ IntegerValue av2 = (IntegerValue)operand1.evaluateItem(context);
+ return RangeIterator.makeRangeIterator(av1, av2);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Range expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new RangeExpressionCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/RangeIterator.java b/sf/saxon/expr/RangeIterator.java
new file mode 100644
index 0000000..9be0156
--- /dev/null
+++ b/sf/saxon/expr/RangeIterator.java
@@ -0,0 +1,141 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.tree.iter.ReversibleIterator;
+import net.sf.saxon.value.BigIntegerValue;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerRange;
+import net.sf.saxon.value.IntegerValue;
+
+/**
+ * An Iterator that produces numeric values in a monotonic sequence,
+ * ascending or descending. Although a range expression (N to M) is always
+ * in ascending order, applying the reverse() function will produce
+ * a RangeIterator that works in descending order.
+*/
+
+public class RangeIterator implements SequenceIterator<IntegerValue>,
+ ReversibleIterator<IntegerValue>,
+ LastPositionFinder<IntegerValue>,
+ LookaheadIterator<IntegerValue>,
+ GroundedIterator<IntegerValue> {
+
+ long start;
+ long currentValue;
+ long limit;
+
+ public static SequenceIterator makeRangeIterator(IntegerValue start, IntegerValue end) throws XPathException {
+ if (start == null || end == null) {
+ return EmptyIterator.getInstance();
+ } else {
+ if (start.compareTo(end) > 0) {
+ return EmptyIterator.getInstance();
+ }
+ if (start instanceof BigIntegerValue || end instanceof BigIntegerValue) {
+ return new BigRangeIterator(start.asBigInteger(), end.asBigInteger());
+ } else {
+ return new RangeIterator(start.longValue(), end.longValue());
+ }
+ }
+ }
+
+ /**
+ * Create an iterator over a range of monotonically increasing integers
+ * @param start the first integer in the sequence
+ * @param end the last integer in the sequence. Must be >= start.
+ */
+
+ public RangeIterator(long start, long end) throws XPathException {
+ if (end - start > Integer.MAX_VALUE) {
+ throw new XPathException("Saxon limit on sequence length exceeded (2^32-1)");
+ }
+ this.start = start;
+ currentValue = start - 1;
+ limit = end;
+ }
+
+ public boolean hasNext() {
+ return currentValue < limit;
+ }
+
+ /*@Nullable*/ public IntegerValue next() {
+ if (++currentValue > limit) {
+ return null;
+ }
+ return Int64Value.makeIntegerValue(currentValue);
+ }
+
+ public IntegerValue current() {
+ if (currentValue > limit) {
+ return null;
+ } else {
+ return Int64Value.makeIntegerValue(currentValue);
+ }
+ }
+
+ public int position() {
+ if (currentValue > limit) {
+ return -1;
+ } else {
+ return (int)(currentValue - start + 1);
+ }
+ }
+
+ public void close() {
+ }
+
+ public int getLength() {
+ return (int)((limit - start) + 1);
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<IntegerValue> getAnother() throws XPathException {
+ return new RangeIterator(start, limit);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED}, {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD | LAST_POSITION_FINDER | GROUNDED;
+ }
+
+ public SequenceIterator<IntegerValue> getReverseIterator() {
+ try {
+ return new ReverseRangeIterator(limit, start);
+ } catch (XPathException err) {
+ throw new AssertionError(err);
+ }
+ }
+
+ /**
+ * Return a Value containing all the items in the sequence returned by this
+ * SequenceIterator. This should be an "in-memory" value, not a Closure.
+ *
+ * @return the corresponding Value
+ */
+
+ public GroundedValue materialize() {
+ return new IntegerRange(start, limit);
+ }
+}
+
diff --git a/sf/saxon/expr/ReverseRangeIterator.java b/sf/saxon/expr/ReverseRangeIterator.java
new file mode 100644
index 0000000..9126076
--- /dev/null
+++ b/sf/saxon/expr/ReverseRangeIterator.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.tree.iter.ReversibleIterator;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+
+/**
+* Iterator that produces numeric values in a monotonic sequence,
+* ascending or descending. Although a range expression (N to M) is always
+ * in ascending order, applying the reverse() function will produce
+ * a RangeIterator that works in descending order.
+*/
+
+public class ReverseRangeIterator implements SequenceIterator<IntegerValue>,
+ ReversibleIterator<IntegerValue>, LastPositionFinder<IntegerValue>, LookaheadIterator<IntegerValue> {
+
+ long start;
+ long currentValue;
+ long limit;
+
+ /**
+ * Create an iterator over a range of integers in monotonic descending order
+ * @param start the first integer to be delivered (the highest in the range)
+ * @param end the last integer to be delivered (the lowest in the range). Must be <= start
+ */
+
+ public ReverseRangeIterator(long start, long end) throws XPathException {
+ if (start - end > Integer.MAX_VALUE) {
+ throw new XPathException("Saxon limit on sequence length exceeded (2^32-1)");
+ }
+ this.start = start;
+ currentValue = start + 1;
+ limit = end;
+ }
+
+ public boolean hasNext() {
+ return currentValue > limit;
+ }
+
+ /*@Nullable*/ public IntegerValue next() {
+ if (--currentValue < limit) {
+ return null;
+ }
+ return Int64Value.makeIntegerValue(currentValue);
+ }
+
+ public IntegerValue current() {
+ if (currentValue < limit) {
+ return null;
+ } else {
+ return Int64Value.makeIntegerValue(currentValue);
+ }
+ }
+
+ public int position() {
+ if (currentValue < limit) {
+ return -1;
+ } else {
+ return (int)(start - currentValue + 1);
+ }
+ }
+
+ public void close() {
+ }
+
+ public int getLength() {
+ return (int)((start - limit) + 1);
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<IntegerValue> getAnother() throws XPathException {
+ return new ReverseRangeIterator(start, limit);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED}, {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD | LAST_POSITION_FINDER;
+ }
+
+ public SequenceIterator<IntegerValue> getReverseIterator() {
+ try {
+ return new RangeIterator(limit, start);
+ } catch (XPathException err) {
+ throw new AssertionError(err);
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/RootExpression.java b/sf/saxon/expr/RootExpression.java
new file mode 100644
index 0000000..098b3a6
--- /dev/null
+++ b/sf/saxon/expr/RootExpression.java
@@ -0,0 +1,251 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.RootExpressionCompiler;
+import com.saxonica.stream.adjunct.StreamingAdjunct;
+import com.saxonica.stream.adjunct.UnsupportedOperationAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.ItemTypePattern;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.List;
+
+
+/**
+ * An expression whose value is always a set of nodes containing a single node,
+ * the document root. This corresponds to the XPath Expression "/", including the implicit
+ * "/" at the start of a path expression with a leading "/".
+*/
+
+public class RootExpression extends SingleNodeExpression {
+
+ /**
+ * Customize the error message on type checking
+ */
+
+ protected String noContextMessage() {
+ return "Leading '/' cannot select the root node of the tree containing the context item";
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return (other instanceof RootExpression);
+ }
+
+ /**
+ * Specify that the expression returns a singleton
+ */
+
+ public final int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Determine the data type of the items returned by this expression
+ *
+ * @return Type.NODE
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return NodeKindTest.DOCUMENT;
+ }
+
+ /**
+ * get HashCode for comparing two expressions
+ */
+
+ public int hashCode() {
+ return "RootExpression".hashCode();
+ }
+
+ /**
+ * Return the first element selected by this Expression
+ * @param context The evaluation context
+ * @return the NodeInfo of the first selected element, or null if no element
+ * is selected
+ */
+
+ /*@Nullable*/ public NodeInfo getNode(XPathContext context) throws XPathException {
+ Item current = context.getContextItem();
+ if (current==null) {
+ dynamicError("Finding root of tree: the context item is absent", "XPDY0002", context);
+ }
+ if (current instanceof NodeInfo) {
+ DocumentInfo doc = ((NodeInfo)current).getDocumentRoot();
+ if (doc==null) {
+ dynamicError("The root of the tree containing the context item is not a document node", "XPDY0050", context);
+ }
+ return doc;
+ }
+ typeError("Finding root of tree: the context item is not a node", "XPTY0020", context);
+ // dummy return; we never get here
+ return null;
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as StaticProperty.VARIABLES and
+ * StaticProperty.CURRENT_NODE
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CONTEXT_DOCUMENT;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new RootExpression();
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ return new ItemTypePattern(NodeKindTest.DOCUMENT);
+ }
+
+ //#ifdefined STREAM
+
+
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ reasons.add("Root expression '/' is free-ranging");
+ return W3C_FREE_RANGING;
+ }
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ return new ItemTypePattern(NodeKindTest.DOCUMENT);
+ }
+
+ //#endif
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ if (pathMapNodeSet == null) {
+ ContextItemExpression cie = new ContextItemExpression();
+ cie.setContainer(getContainer());
+ pathMapNodeSet = new PathMap.PathMapNodeSet(pathMap.makeNewRoot(cie));
+ }
+ return pathMapNodeSet.createArc(AxisInfo.ANCESTOR_OR_SELF, NodeKindTest.DOCUMENT);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Root expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new RootExpressionCompiler();
+ }
+//#endif
+//#ifdefined STREAM
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public StreamingAdjunct getStreamingAdjunct() {
+ return new UnsupportedOperationAdjunct();
+ }
+
+ //#endif
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return "(/)";
+ }
+
+ @Override
+ public String getExpressionName() {
+ return "(/)";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("root");
+ destination.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/SimpleExpression.java b/sf/saxon/expr/SimpleExpression.java
new file mode 100644
index 0000000..bb3998e
--- /dev/null
+++ b/sf/saxon/expr/SimpleExpression.java
@@ -0,0 +1,307 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+
+/**
+ * An abstract implementation of Expression designed to make it easy to implement new expressions,
+ * in particular, expressions to support extension instructions.
+ *
+ * <p>An implementation of this class must supply the {@link Callable#call(XPathContext, net.sf.saxon.om.Sequence[])}
+ * method to evaluate the expression and return its result.</p>
+ */
+
+public abstract class SimpleExpression extends Expression implements Callable {
+
+ public static final Expression[] NO_ARGUMENTS = new Expression[0];
+
+ protected Expression[] arguments = NO_ARGUMENTS;
+
+ /**
+ * Constructor
+ */
+
+ public SimpleExpression() {
+ }
+
+ /**
+ * Set the immediate sub-expressions of this expression.
+ *
+ * @param sub an array containing the sub-expressions of this expression
+ */
+
+ public void setArguments(Expression[] sub) {
+ arguments = sub;
+ for (Expression aSub : sub) {
+ adoptChildExpression(aSub);
+ }
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression.
+ *
+ * @return an array containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return Arrays.asList(arguments).iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ for (int i = 0; i < arguments.length; i++) {
+ if (arguments[i] == original) {
+ arguments[i] = replacement;
+ found = true;
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Simplify the expression
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ for (int i = 0; i < arguments.length; i++) {
+ if (arguments[i] != null) {
+ arguments[i] = visitor.simplify(arguments[i]);
+ }
+ }
+ return this;
+ }
+
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ for (int i = 0; i < arguments.length; i++) {
+ if (arguments[i] != null) {
+ arguments[i] = visitor.typeCheck(arguments[i], contextItemType);
+ }
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ for (int i = 0; i < arguments.length; i++) {
+ if (arguments[i] != null) {
+ arguments[i] = visitor.optimize(arguments[i], contextItemType);
+ }
+ }
+ return this;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ throw new UnsupportedOperationException("SimpleExpression.copy()");
+ }
+
+ /**
+ * Offer promotion for this subexpression. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent the parent of this expression
+ * @return if the offer is not accepted, return this expression unchanged.
+ * Otherwise return the result of rewriting the expression to promote
+ * this subexpression
+ * @throws XPathException if any error is detected
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ for (int i = 0; i < arguments.length; i++) {
+ if (arguments[i] != null) {
+ arguments[i] = doPromotion(arguments[i], offer);
+ }
+ }
+ return this;
+ }
+
+
+ /**
+ * Determine the data type of the items returned by this expression. This implementation
+ * returns "item()", which can be overridden in a subclass.
+ *
+ * @param th the type hierarchy cache
+ * @return the data type
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return Type.ITEM_TYPE;
+ }
+
+ /**
+ * Determine the static cardinality of the expression. This implementation
+ * returns "zero or more", which can be overridden in a subclass.
+ */
+
+ public int computeCardinality() {
+ if ((getImplementationMethod() & Expression.EVALUATE_METHOD) == 0) {
+ return StaticProperty.ALLOWS_ONE_OR_MORE;
+ } else {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+ }
+
+ /**
+ * Compute the dependencies of an expression, as the union of the
+ * dependencies of its subexpressions. (This is overridden for path expressions
+ * and filter expressions, where the dependencies of a subexpression are not all
+ * propogated). This method should be called only once, to compute the dependencies;
+ * after that, getDependencies should be used.
+ *
+ * @return the depencies, as a bit-mask
+ */
+
+ public int computeDependencies() {
+ return super.computeDependencies();
+ }
+
+ /**
+ * Evaluate an expression as a single item. This always returns either a single Item or
+ * null (denoting the empty sequence). No conversion is done. This method should not be
+ * used unless the static type of the expression is a subtype of "item" or "item?": that is,
+ * it should not be called if the expression may return a sequence. There is no guarantee that
+ * this condition will be detected.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the node or atomic value that results from evaluating the
+ * expression; or null to indicate that the result is an empty
+ * sequence
+ * @throws XPathException if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public final Item evaluateItem(XPathContext context) throws XPathException {
+ return call(context, evaluateArguments(context)).head();
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws XPathException if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ public final SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ return call(context, evaluateArguments(context)).iterate();
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public final void process(XPathContext context) throws XPathException {
+ SequenceIterator iter = call(context, evaluateArguments(context)).iterate();
+ Item it;
+ while ((it = iter.next()) != null) {
+ context.getReceiver().append(it, locationId, NodeInfo.ALL_NAMESPACES);
+ }
+ }
+
+ /**
+ * Internal method to evaluate the arguments prior to calling the generic call() method
+ * @param context the XPath dynamic context
+ * @return the values of the (evaluated) arguments
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ private Sequence[] evaluateArguments(XPathContext context) throws XPathException {
+ Sequence[] iters = new Sequence[arguments.length];
+ for (int i = 0; i < arguments.length; i++) {
+ iters[i] = SequenceTool.toLazySequence(arguments[i].iterate(context));
+ }
+ return iters;
+ }
+
+ /**
+ * Get the subexpressions (arguments to this expression)
+ *
+ * @return the arguments, as an array
+ */
+ public Expression[] getArguments() {
+ return arguments;
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("userExpression");
+ destination.emitAttribute("class", getExpressionType());
+ for (Expression argument : arguments) {
+ argument.explain(destination);
+ }
+ destination.endElement();
+ }
+
+ /**
+ * Return a distinguishing name for the expression, for use in diagnostics.
+ * By default the class name is used.
+ *
+ * @return a distinguishing name for the expression (defaults to the name of the implementation class)
+ */
+
+ public String getExpressionType() {
+ return getClass().getName();
+ }
+
+}
+
diff --git a/sf/saxon/expr/SimpleStepExpression.java b/sf/saxon/expr/SimpleStepExpression.java
new file mode 100644
index 0000000..0e6f769
--- /dev/null
+++ b/sf/saxon/expr/SimpleStepExpression.java
@@ -0,0 +1,106 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SimpleStepExpressionCompiler;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.type.TypeHierarchy;
+
+
+/**
+ * An SimpleStepExpression is a special case of a SlashExpression in which the
+ * start expression selects a single item (or nothing), and the step expression is
+ * a simple AxisExpression. This is designed to avoid the costs of creating a new
+ * dynamic context for expressions (common in XQuery) such as
+ * for $b in EXPR return $b/title
+ *
+*/
+
+public final class SimpleStepExpression extends SlashExpression {
+
+ public SimpleStepExpression(Expression start, Expression step) {
+ super(start, step);
+ if (!(step instanceof AxisExpression)) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /*@NotNull*/
+ @Override
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression start2 = visitor.typeCheck(start, contextItemType);
+ if (start2 != start) {
+ setStartExpression(start2);
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Expression step2 = visitor.typeCheck(step, new ExpressionVisitor.ContextItemType(start2.getItemType(th), false));
+ if (step2 != step) {
+ setStepExpression(step2);
+ }
+ if (!(step instanceof AxisExpression)) {
+ SlashExpression se = new SlashExpression(start, step);
+ ExpressionTool.copyLocationInfo(this, se);
+ return se;
+ }
+ if (start instanceof ContextItemExpression && AxisInfo.isForwards[((AxisExpression)step).getAxis()]) {
+ return step;
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ @Override
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+ /*@NotNull*/
+ @Override
+ public Expression copy() {
+ return new SimpleStepExpression(start.copy(), step.copy());
+ }
+
+ /**
+ * Evaluate the expression, returning an iterator over the result
+ * @param context the evaluation context
+ */
+ /*@NotNull*/
+ @Override
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ Item origin = start.evaluateItem(context);
+ if (origin == null) {
+ return EmptyIterator.getInstance();
+ }
+ return ((AxisExpression)step).iterate(origin);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the SimpleStep expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SimpleStepExpressionCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/SingleItemFilter.java b/sf/saxon/expr/SingleItemFilter.java
new file mode 100644
index 0000000..217a462
--- /dev/null
+++ b/sf/saxon/expr/SingleItemFilter.java
@@ -0,0 +1,73 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Cardinality;
+
+/**
+* A SingleItemFilter is an expression that selects zero or one items from a supplied sequence
+*/
+
+public abstract class SingleItemFilter extends UnaryExpression {
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expresion visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.optimize(operand, contextItemType);
+ if (!Cardinality.allowsMany(operand.getCardinality())) {
+ return operand;
+ }
+ return super.optimize(visitor, contextItemType);
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ /*@Nullable*/ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ if (offer.action != PromotionOffer.UNORDERED) {
+ // we can't push the UNORDERED property down to the operand, because order is significant
+ operand = doPromotion(operand, offer);
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Get the static cardinality: this implementation is appropriate for [1] and [last()] which will always
+ * return something if the input is non-empty
+ */
+
+ public int computeCardinality() {
+ return operand.getCardinality() & ~StaticProperty.ALLOWS_MANY;
+ }
+
+}
+
diff --git a/sf/saxon/expr/SingleNodeExpression.java b/sf/saxon/expr/SingleNodeExpression.java
new file mode 100644
index 0000000..d3e709b
--- /dev/null
+++ b/sf/saxon/expr/SingleNodeExpression.java
@@ -0,0 +1,152 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+
+/**
+* A node set expression that will always return zero or one nodes
+*/
+
+public abstract class SingleNodeExpression extends Expression {
+
+ private boolean contextMaybeUndefined = true;
+
+ /**
+ * Type-check the expression.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (contextItemType == null || contextItemType.itemType == null) {
+ XPathException err = new XPathException(noContextMessage() + ": the context item is absent");
+ err.setErrorCode("XPDY0002");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }else {
+ contextMaybeUndefined = contextItemType.contextMaybeUndefined;
+ }
+ if (contextItemType.itemType.isPlainType()) {
+ XPathException err = new XPathException(noContextMessage() + ": the context item is an atomic value");
+ err.setErrorCode("XPTY0020");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ return this;
+ }
+
+ /**
+ * Customize the error message on type checking
+ */
+
+ protected abstract String noContextMessage();
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ // repeat the check: in XSLT insufficient information is available the first time
+ return typeCheck(visitor, contextItemType);
+ }
+
+
+ /**
+ * Specify that the expression returns a singleton
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+
+ /**
+ * Determine the data type of the items returned by this expression
+ * @return Type.NODE
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return AnyNodeTest.getInstance();
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as StaticProperty.VARIABLES and
+ * StaticProperty.CURRENT_NODE
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ }
+
+ public int computeSpecialProperties() {
+ return StaticProperty.ORDERED_NODESET |
+ StaticProperty.CONTEXT_DOCUMENT_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET |
+ StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Get the single node to which this expression refers. Returns null if the node-set is empty
+ */
+
+ public abstract NodeInfo getNode(XPathContext context) throws XPathException;
+
+ /**
+ * Ask whether there is a possibility that the context item will be undefined
+ * @return true if this is a possibility
+ */
+
+ public boolean isContextPossiblyUndefined() {
+ return contextMaybeUndefined;
+ }
+
+ /**
+ * Evaluate the expression in a given context to return an iterator
+ * @param context the evaluation context
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ return SingletonIterator.makeIterator(getNode(context));
+ }
+
+ public NodeInfo evaluateItem(XPathContext context) throws XPathException {
+ return getNode(context);
+ }
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ return getNode(context) != null;
+ }
+
+}
+
diff --git a/sf/saxon/expr/SingletonAtomizer.java b/sf/saxon/expr/SingletonAtomizer.java
new file mode 100644
index 0000000..f7d882c
--- /dev/null
+++ b/sf/saxon/expr/SingletonAtomizer.java
@@ -0,0 +1,333 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SingletonAtomizerCompiler;
+import com.saxonica.stream.adjunct.SingletonAtomizerAdjunct;
+import com.saxonica.stream.adjunct.StreamingAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.ObjectValue;
+import net.sf.saxon.value.StringValue;
+
+import java.util.List;
+
+/**
+* A SingletonAtomizer combines the functions of an Atomizer and a CardinalityChecker: it is used to
+ * atomize a sequence of nodes, checking that the result of the atomization contains zero or one atomic
+ * values. Note that the input may be a sequence of nodes or atomic values, even though the result must
+ * contain at most one atomic value.
+*/
+
+public final class SingletonAtomizer extends UnaryExpression {
+
+ private boolean allowEmpty;
+ private RoleLocator role;
+
+ /**
+ * Constructor
+ * @param sequence the sequence to be atomized
+ * @param role contains information about where the expression appears, for use in any error message
+ * @param allowEmpty true if the result sequence is allowed to be empty.
+ */
+
+ public SingletonAtomizer(Expression sequence, RoleLocator role, boolean allowEmpty) {
+ super(sequence);
+ this.allowEmpty = allowEmpty;
+ this.role = role;
+ }
+
+ /**
+ * Simplify an expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ operand = visitor.simplify(operand);
+ if (operand instanceof Literal && ((Literal)(operand)).getValue() instanceof AtomicValue) {
+ return operand;
+ }
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ visitor.resetStaticProperties();
+ if (Literal.isEmptySequence(operand)) {
+ if (!allowEmpty) {
+ typeError("An empty sequence is not allowed as the " +
+ role.getMessage(), role.getErrorCode(), null);
+ }
+ return operand;
+ }
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType operandType = operand.getItemType(th);
+ if (operandType.isPlainType()) {
+ return operand;
+ }
+ if (!operandType.isAtomizable()) {
+ XPathException err;
+ if (operandType instanceof FunctionItemType) {
+ err = new XPathException(
+ "Cannot atomize a function item", "FOTY0013");
+ } else {
+ err = new XPathException(
+ "Cannot atomize an element that is defined in the schema to have element-only content", "FOTY0012");
+ }
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ return this;
+ }
+
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression exp = super.optimize(visitor, contextItemType);
+ if (exp == this) {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+
+ if (operand.getItemType(th).isPlainType()) {
+ return operand;
+ }
+ return this;
+ } else {
+ return exp;
+ }
+ }
+
+ /**
+ * Determine the special properties of this expression
+ * @return {@link StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new SingletonAtomizer(getBaseExpression().copy(), role, allowEmpty);
+ }
+
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return operand.getStreamability(NODE_VALUE_CONTEXT, allowExtensions, null);
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public StreamingAdjunct getStreamingAdjunct() {
+ return new SingletonAtomizerAdjunct();
+ }
+
+ //#endif
+
+ /**
+ * Get the RoleLocator (used to construct error messages)
+ * @return the role locator
+ */
+
+ public RoleLocator getRole() {
+ return role;
+ }
+
+
+ /*@Nullable*/ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet result = operand.addToPathMap(pathMap, pathMapNodeSet);
+ if (result != null) {
+ TypeHierarchy th = getExecutable().getConfiguration().getTypeHierarchy();
+ ItemType operandItemType = operand.getItemType(th);
+ if (th.relationship(NodeKindTest.ELEMENT, operandItemType) != TypeHierarchy.DISJOINT ||
+ th.relationship(NodeKindTest.DOCUMENT, operandItemType) != TypeHierarchy.DISJOINT) {
+ result.setAtomized();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Evaluate as an Item. This should only be called if a singleton or empty sequence is required;
+ * it throws a type error if the underlying sequence is multi-valued.
+ */
+
+ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+ int found = 0;
+ AtomicValue result = null;
+ SequenceIterator iter = operand.iterate(context);
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (item instanceof AtomicValue) {
+ if (found++ > 0) {
+ typeError(
+ "A sequence of more than one item is not allowed as the " +
+ role.getMessage(), role.getErrorCode(), context);
+ }
+ result = (AtomicValue)item;
+ } else if (item instanceof FunctionItem) {
+ typeError("A function item cannot appear as the " +
+ role.getMessage(), "FOTY0013", context);
+ } else if (item instanceof ObjectValue) {
+ return new StringValue(item.getStringValue());
+ } else {
+ AtomicSequence value = ((NodeInfo)item).atomize();
+ found += SequenceTool.getLength(value);
+ if (found > 1) {
+ typeError(
+ "A sequence of more than one item is not allowed as the " +
+ role.getMessage(), role.getErrorCode(), context);
+ }
+ result = value.head();
+ }
+ }
+ if (found == 0 && !allowEmpty) {
+ typeError("An empty sequence is not allowed as the " +
+ role.getMessage(), role.getErrorCode(), null);
+ }
+ return result;
+ }
+
+ /**
+ * Determine the data type of the items returned by the expression, if possible
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER. For this class, the
+ * result is always an atomic type, but it might be more specific.
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ boolean isSchemaAware = true;
+ try {
+ isSchemaAware = getExecutable().isSchemaAware();
+ } catch (NullPointerException err) {
+ // ultra-cautious code in case expression container has not been set
+ if (!th.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.SCHEMA_VALIDATION)) {
+ isSchemaAware = false;
+ }
+ }
+ ItemType in = operand.getItemType(th);
+ if (in.isPlainType()) {
+ return in;
+ } else if (in instanceof NodeTest) {
+
+ if (in instanceof ErrorType) {
+ return in;
+ }
+
+ int kinds = ((NodeTest)in).getNodeKindMask();
+ if (!isSchemaAware) {
+ // Some node-kinds always have a typed value that's a string
+
+ if ((kinds | STRING_KINDS) == STRING_KINDS) {
+ return BuiltInAtomicType.STRING;
+ }
+ // Some node-kinds are always untyped atomic; some are untypedAtomic provided that the configuration
+ // is untyped
+
+ if ((kinds | UNTYPED_IF_UNTYPED_KINDS) == UNTYPED_IF_UNTYPED_KINDS) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ } else {
+ if ((kinds | UNTYPED_KINDS) == UNTYPED_KINDS) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ }
+
+ return in.getAtomizedItemType();
+ } else if (in instanceof ExternalObjectType) {
+ return in.getAtomizedItemType();
+ }
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+
+ /**
+ * Node kinds whose typed value is always a string
+ */
+ private static final int STRING_KINDS =
+ (1<<Type.NAMESPACE) | (1<<Type.COMMENT) | (1<<Type.PROCESSING_INSTRUCTION);
+
+ /**
+ * Node kinds whose typed value is always untypedAtomic
+ */
+
+ private static final int UNTYPED_KINDS =
+ (1<<Type.TEXT) | (1<<Type.DOCUMENT);
+
+ /**
+ * Node kinds whose typed value is untypedAtomic if the configuration is untyped
+ */
+
+ private static final int UNTYPED_IF_UNTYPED_KINDS =
+ (1<<Type.TEXT) | (1<<Type.ELEMENT) | (1<<Type.DOCUMENT) | (1<<Type.ATTRIBUTE);
+
+ /**
+ * Determine the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ if (allowEmpty) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ } else {
+ return StaticProperty.EXACTLY_ONE;
+ }
+ }
+
+ /**
+ * Give a string representation of the expression name for use in diagnostics
+ * @return the expression name, as a string
+ */
+
+ public String getExpressionName() {
+ return "atomizeSingleton";
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Cast expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SingletonAtomizerCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/SingletonIntersectExpression.java b/sf/saxon/expr/SingletonIntersectExpression.java
new file mode 100644
index 0000000..b41339a
--- /dev/null
+++ b/sf/saxon/expr/SingletonIntersectExpression.java
@@ -0,0 +1,108 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+
+/**
+ * This expression is equivalent to (A intersect B) in the case where A has cardinality
+ * zero-or-one. This is handled as a special case because the standard sort-merge algorithm
+ * involves an unnecessary sort on B.
+ */
+public class SingletonIntersectExpression extends VennExpression {
+
+ /**
+ * Special case of an intersect expression where the first argument is a singleton
+ * @param p1 the first argument, always a singleton
+ * @param op the operator, always Token.INTERSECT
+ * @param p2 the second argument
+ */
+
+ public SingletonIntersectExpression(final Expression p1, final int op, final Expression p2) {
+ super(p1, op, p2);
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ @Override
+ public Expression copy() {
+ return new SingletonIntersectExpression(operand0.copy(), operator, operand1.copy());
+ }
+
+ /**
+ * Iterate over the value of the expression. The result will always be sorted in document order,
+ * with duplicates eliminated
+ * @param c The context for evaluation
+ * @return a SequenceIterator representing the union of the two operands
+ */
+
+ /*@NotNull*/
+ @Override
+ public SequenceIterator iterate(XPathContext c) throws XPathException {
+ NodeInfo m = (NodeInfo)operand0.evaluateItem(c);
+ if (m == null) {
+ return EmptyIterator.getInstance();
+ }
+ SequenceIterator iter = operand1.iterate(c);
+ while (true) {
+ NodeInfo n = (NodeInfo)iter.next();
+ if (n == null) {
+ return EmptyIterator.getInstance();
+ }
+ if (n.isSameNodeInfo(m)) {
+ return SingletonIterator.makeIterator(m);
+ }
+ }
+ }
+
+ /**
+ * Get the effective boolean value. In the case of a union expression, this
+ * is reduced to an OR expression, for efficiency
+ */
+
+ @Override
+ public boolean effectiveBooleanValue(XPathContext c) throws XPathException {
+ NodeInfo m = (NodeInfo)operand0.evaluateItem(c);
+ if (m == null) {
+ return false;
+ }
+ SequenceIterator iter = operand1.iterate(c);
+ while (true) {
+ NodeInfo n = (NodeInfo)iter.next();
+ if (n == null) {
+ return false;
+ }
+ if (n.isSameNodeInfo(m)) {
+ return true;
+ }
+ }
+ }
+
+ public String getExpressionName() {
+ return "singleton-intersect";
+ }
+
+/**
+ * Display the operator used by this binary expression
+ * @return String representation of the operator (for diagnostic display only)
+ */
+
+ protected String displayOperator() {
+ return "singleton-intersect";
+ }
+
+}
+
diff --git a/sf/saxon/expr/SlashExpression.java b/sf/saxon/expr/SlashExpression.java
new file mode 100644
index 0000000..1b99247
--- /dev/null
+++ b/sf/saxon/expr/SlashExpression.java
@@ -0,0 +1,1136 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SlashExpressionCompiler;
+import com.saxonica.stream.adjunct.ForEachAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.instruct.CopyOf;
+import net.sf.saxon.expr.instruct.ForEach;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.expr.sort.DocumentSorter;
+import net.sf.saxon.functions.Reverse;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ErrorType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+
+
+/**
+ * A slash expression is any expression using the binary slash operator "/". The parser initially generates a slash
+ * expression for all occurrences of the binary "/" operator. Subsequently, as a result of type inferencing, the
+ * majority of slash expressions will be rewritten as instances of PathExpression (returning nodes) or
+ * ForEach instructions (when they return atomic values). However, in the rare case where it is not possible to determine
+ * statically whether the rh operand returns nodes or atomic values, instances of this class may need to be interpreted
+ * directly at run time.
+ */
+
+public class SlashExpression extends Expression
+ implements ContextSwitchingExpression, ContextMappingFunction<Item> {
+
+ Expression start;
+ Expression step;
+
+ /**
+ * Constructor
+ *
+ * @param start The left hand operand (which must always select a sequence of nodes).
+ * @param step The step to be followed from each node in the start expression to yield a new
+ * sequence; this may return either nodes or atomic values (but not a mixture of the two)
+ */
+
+ public SlashExpression(Expression start, Expression step) {
+ this.start = start;
+ this.step = step;
+ adoptChildExpression(start);
+ adoptChildExpression(step);
+ }
+
+ public void setStartExpression(Expression start2) {
+ if (start != start2) {
+ start = start2;
+ adoptChildExpression(start);
+ }
+ }
+
+ public void setStepExpression(Expression step2) {
+ if (step != step2) {
+ step = step2;
+ adoptChildExpression(step);
+ }
+ }
+
+ /**
+ * Get the start expression (the left-hand operand)
+ *
+ * @return the first operand
+ */
+
+ public Expression getControllingExpression() {
+ return start;
+ }
+
+ /**
+ * Get the step expression (the right-hand operand)
+ *
+ * @return the second operand
+ */
+
+ public Expression getControlledExpression() {
+ return step;
+ }
+
+ /**
+ * Simplify an expression
+ *
+ * @param visitor the expression visitor
+ * @return the simplified expression
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ setStartExpression(visitor.simplify(start));
+ setStepExpression(visitor.simplify(step));
+ return this;
+ }
+
+ /**
+ * Determine the data type of the items returned by this exprssion
+ *
+ * @param th the type hierarchy cache
+ * @return the type of the step
+ */
+
+ /*@NotNull*/
+ public final ItemType getItemType(TypeHierarchy th) {
+ return step.getItemType(th);
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return step.getIntegerBounds();
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+
+ Expression start2 = visitor.typeCheck(start, contextItemType);
+
+ // The first operand must be of type node()*
+
+ RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, "/", 0);
+ role0.setErrorCode("XPTY0019");
+ setStartExpression(
+ TypeChecker.staticTypeCheck(start2, SequenceType.NODE_SEQUENCE, false, role0, visitor));
+
+ // Now check the second operand
+
+ setStepExpression(visitor.typeCheck(step, new ExpressionVisitor.ContextItemType(start.getItemType(th), false)));
+
+ // If the expression has the form (a//descendant-or-self::node())/b, try to simplify it to
+ // use the descendant axis
+
+ Expression e2 = simplifyDescendantPath(visitor.getStaticContext());
+ if (e2 != null) {
+ return e2.typeCheck(visitor, contextItemType);
+ }
+
+ if (start instanceof ContextItemExpression &&
+ ((step.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0)) {
+ return step;
+ }
+
+ if (step instanceof ContextItemExpression &&
+ ((start.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0)) {
+ return start;
+ }
+
+ return this;
+ }
+
+ // Simplify an expression of the form a//b, where b has no positional filters.
+ // This comes out of the constructor above as (a/descendent-or-self::node())/child::b,
+ // but it is equivalent to a/descendant::b; and the latter is better as it
+ // doesn't require sorting. Note that we can't do this until type information is available,
+ // as we need to know whether any filters are positional or not.
+
+ private SlashExpression simplifyDescendantPath(StaticContext env) {
+
+ Expression st = start;
+
+ // detect .//x as a special case; this will appear as descendant-or-self::node()/x
+
+ if (start instanceof AxisExpression) {
+ AxisExpression stax = (AxisExpression) start;
+ if (stax.getAxis() != AxisInfo.DESCENDANT_OR_SELF) {
+ return null;
+ }
+ ContextItemExpression cie = new ContextItemExpression();
+ ExpressionTool.copyLocationInfo(this, cie);
+ st = ExpressionTool.makePathExpression(cie, stax, false);
+ ExpressionTool.copyLocationInfo(this, st);
+ }
+
+ if (!(st instanceof SlashExpression)) {
+ return null;
+ }
+
+ SlashExpression startPath = (SlashExpression) st;
+ if (!(startPath.step instanceof AxisExpression)) {
+ return null;
+ }
+
+ AxisExpression mid = (AxisExpression) startPath.step;
+ if (mid.getAxis() != AxisInfo.DESCENDANT_OR_SELF) {
+ return null;
+ }
+
+
+ NodeTest test = mid.getNodeTest();
+ if (!(test == null || test instanceof AnyNodeTest)) {
+ return null;
+ }
+
+ Expression underlyingStep = step;
+ while (underlyingStep instanceof FilterExpression) {
+ if (((FilterExpression) underlyingStep).isPositional(env.getConfiguration().getTypeHierarchy())) {
+ return null;
+ }
+ underlyingStep = ((FilterExpression) underlyingStep).getControllingExpression();
+ }
+
+ if (!(underlyingStep instanceof AxisExpression)) {
+ return null;
+ }
+
+ byte underlyingAxis = ((AxisExpression) underlyingStep).getAxis();
+ if (underlyingAxis == AxisInfo.CHILD ||
+ underlyingAxis == AxisInfo.DESCENDANT ||
+ underlyingAxis == AxisInfo.DESCENDANT_OR_SELF) {
+ byte newAxis = (underlyingAxis == AxisInfo.DESCENDANT_OR_SELF ? AxisInfo.DESCENDANT_OR_SELF : AxisInfo.DESCENDANT);
+ Expression newStep =
+ new AxisExpression(newAxis,
+ ((AxisExpression) underlyingStep).getNodeTest());
+ ExpressionTool.copyLocationInfo(this, newStep);
+
+ underlyingStep = step;
+ // Add any filters to the new expression. We know they aren't
+ // positional, so the order of the filters doesn't technically matter
+ // (XPath section 2.3.4 explicitly allows us to change it.)
+ // However, in the interests of predictable execution, hand-optimization, and
+ // diagnosable error behaviour, we retain the original order.
+ Stack<Expression> filters = new Stack<Expression>();
+ while (underlyingStep instanceof FilterExpression) {
+ filters.add(((FilterExpression) underlyingStep).getFilter());
+ underlyingStep = ((FilterExpression) underlyingStep).getControllingExpression();
+ }
+ while (!filters.isEmpty()) {
+ newStep = new FilterExpression(newStep, filters.pop());
+ ExpressionTool.copyLocationInfo(step, newStep);
+ }
+
+ //System.err.println("Simplified this:");
+ // display(10);
+ //System.err.println("as this:");
+ // new PathExpression(startPath.start, newStep).display(10);
+
+ Expression newPath = ExpressionTool.makePathExpression(startPath.start, newStep, false);
+ if (!(newPath instanceof SlashExpression)) {
+ return null;
+ }
+ ExpressionTool.copyLocationInfo(this, newPath);
+ return (SlashExpression) newPath;
+ }
+
+ if (underlyingAxis == AxisInfo.ATTRIBUTE) {
+
+ // turn the expression a//@b into a/descendant-or-self::*/@b
+
+ Expression newStep =
+ new AxisExpression(AxisInfo.DESCENDANT_OR_SELF, NodeKindTest.ELEMENT);
+ ExpressionTool.copyLocationInfo(this, newStep);
+ Expression e2 = ExpressionTool.makePathExpression(startPath.start, newStep, false);
+ Expression e3 = ExpressionTool.makePathExpression(e2, step, false);
+ if (!(e3 instanceof SlashExpression)) {
+ return null;
+ }
+ ExpressionTool.copyLocationInfo(this, e3);
+ return (SlashExpression) e3;
+ }
+
+ return null;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+
+ setStartExpression(visitor.optimize(start, contextItemType));
+ setStepExpression(step.optimize(visitor, new ExpressionVisitor.ContextItemType(start.getItemType(th), false)));
+
+ if (Literal.isEmptySequence(start) || Literal.isEmptySequence(step)) {
+ return Literal.makeEmptySequence();
+ }
+
+ if (start instanceof RootExpression && th.isSubType(contextItemType.itemType, NodeKindTest.DOCUMENT)) {
+ // remove unnecessary leading "/" - helps streaming
+ return step;
+ }
+
+ // Rewrite a/b[filter] as (a/b)[filter] to improve the chance of indexing
+
+ Expression lastStep = getLastStep();
+ if (lastStep instanceof FilterExpression && !((FilterExpression) lastStep).isPositional(th)) {
+ Expression leading = getLeadingSteps();
+ Expression p2 = ExpressionTool.makePathExpression(leading, ((FilterExpression) lastStep).getControllingExpression(), false);
+ Expression f2 = new FilterExpression(p2, ((FilterExpression) lastStep).getFilter());
+ return f2.optimize(visitor, contextItemType);
+ }
+
+ if (!visitor.isOptimizeForStreaming()) {
+ Expression k = opt.convertPathExpressionToKey(this, visitor);
+ if (k != null) {
+ return k.typeCheck(visitor, contextItemType).optimize(visitor, contextItemType);
+ }
+ }
+
+ // Replace //x/y by descendant::y[parent::x] to eliminate the need for sorting
+ // into document order, and to make the expression streamable
+
+ if (start instanceof AxisExpression && ((AxisExpression)start).getAxis() == AxisInfo.DESCENDANT &&
+ step instanceof AxisExpression && ((AxisExpression)step).getAxis() == AxisInfo.CHILD) {
+ // TODO: we could be more ambitious and attempt this in the presence of non-positional filters;
+ Expression k = new FilterExpression(
+ new AxisExpression(AxisInfo.DESCENDANT, ((AxisExpression)step).getNodeTest()),
+ new AxisExpression(AxisInfo.PARENT, ((AxisExpression)start).getNodeTest()));
+ // If we're not starting at the root, ensure we go down at least one level
+ if (!th.isSubType(contextItemType.itemType, NodeKindTest.DOCUMENT)) {
+ k = new SlashExpression(new AxisExpression(AxisInfo.CHILD, NodeKindTest.ELEMENT), k);
+ }
+ opt.trace("Rewrote descendant::X/child::Y as descendant::Y[parent::X]", k);
+ return k;
+ }
+
+ // Replace $x/child::abcd by a SimpleStepExpression, to avoid the need for creating
+ // a new dynamic context at run-time.
+
+ if (step instanceof AxisExpression && !Cardinality.allowsMany(start.getCardinality())) {
+ SimpleStepExpression sse = new SimpleStepExpression(start, step);
+ ExpressionTool.copyLocationInfo(this, sse);
+ return sse;
+ }
+
+ Expression k = promoteFocusIndependentSubexpressions(visitor, contextItemType);
+ if (k != this) {
+ return k;
+ }
+
+ if (visitor.isOptimizeForStreaming()) {
+ // rewrite a/copy-of(.) as copy-of(a)
+ Expression rawStep = ExpressionTool.unfilteredExpression(step);
+ if (rawStep instanceof CopyOf && ((CopyOf)rawStep).getSelectExpression() instanceof ContextItemExpression) {
+ ((CopyOf)rawStep).setSelectExpression(start);
+ rawStep.resetLocalStaticProperties();
+ step.resetLocalStaticProperties();
+ return step;
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Test whether a path expression is an absolute path - that is, a path whose first step selects a
+ * document node; if not, see if it can be converted to an absolute path. This is possible in cases where
+ * the path expression has the form a/b/c and it is known that the context item is a document node; in this
+ * case it is safe to change the path expression to /a/b/c
+ *
+ * @param th the type hierarchy cache
+ * @return the path expression if it is absolute; the converted path expression if it can be made absolute;
+ * or null if neither condition applies.
+ */
+
+ public SlashExpression tryToMakeAbsolute(TypeHierarchy th) {
+ Expression first = getFirstStep();
+ if (first.getItemType(th).getPrimitiveType() == Type.DOCUMENT) {
+ return this;
+ }
+ if (first instanceof AxisExpression) {
+ // This second test allows keys to be built. See XMark q9.
+ ItemType contextItemType = ((AxisExpression) first).getContextItemType();
+ if (contextItemType != null && contextItemType.getPrimitiveType() == Type.DOCUMENT) {
+ RootExpression root = new RootExpression();
+ ExpressionTool.copyLocationInfo(this, root);
+ Expression path = ExpressionTool.makePathExpression(root, this, false);
+ ExpressionTool.copyLocationInfo(this, path);
+ return (SlashExpression) path;
+ }
+ }
+ if (first instanceof DocumentSorter && ((DocumentSorter) first).getBaseExpression() instanceof SlashExpression) {
+ // see test case filter-001 in xqts-extra
+ SlashExpression se = (SlashExpression) ((DocumentSorter) first).getBaseExpression();
+ SlashExpression se2 = se.tryToMakeAbsolute(th);
+ if (se2 != null) {
+ if (se2 == se) {
+ return this;
+ } else {
+ Expression rest = getRemainingSteps();
+ DocumentSorter ds = new DocumentSorter(se2);
+ return new SlashExpression(ds, rest);
+ }
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * If any subexpressions within the step are not dependent on the focus,
+ * and if they are not "creative" expressions (expressions that can create new nodes), then
+ * promote them: this causes them to be evaluated once, outside the path expression
+ *
+ * @param visitor the expression visitor
+ * @param contextItemType the type of the context item for evaluating the start expression
+ * @return the rewritten expression, or the original expression if no rewrite was possible
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error is detected
+ */
+
+ protected Expression promoteFocusIndependentSubexpressions(
+ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+
+ PromotionOffer offer = new PromotionOffer(opt);
+ offer.action = PromotionOffer.FOCUS_INDEPENDENT;
+ offer.promoteDocumentDependent = (start.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0;
+ offer.containingExpression = this;
+
+ setStepExpression(doPromotion(step, offer));
+ visitor.resetStaticProperties();
+ if (offer.containingExpression != this) {
+ offer.containingExpression =
+ visitor.optimize(visitor.typeCheck(offer.containingExpression, contextItemType), contextItemType);
+ return offer.containingExpression;
+ }
+ return this;
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ setStartExpression(doPromotion(start, offer));
+ if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES ||
+ offer.action == PromotionOffer.REPLACE_CURRENT) {
+ // Don't pass on other requests. We could pass them on, but only after augmenting
+ // them to say we are interested in subexpressions that don't depend on either the
+ // outer context or the inner context.
+ setStepExpression(doPromotion(step, offer));
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Get the immediate subexpressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(start, step);
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ SubExpressionInfo selectInfo = new SubExpressionInfo(start, true, false, INSPECTION_CONTEXT);
+ SubExpressionInfo actionInfo = new SubExpressionInfo(step, false, true, INHERITED_CONTEXT);
+ return new PairIterator<SubExpressionInfo>(selectInfo, actionInfo);
+
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (start == original) {
+ setStartExpression(replacement);
+ found = true;
+ }
+ if (step == original) {
+ setStepExpression(replacement);
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet target = start.addToPathMap(pathMap, pathMapNodeSet);
+ return step.addToPathMap(pathMap, target);
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
+ * XPathContext.CURRENT_NODE
+ */
+
+ public int computeDependencies() {
+ return start.getDependencies() |
+ // not all dependencies in the step matter, because the context node, etc,
+ // are not those of the outer expression
+ (step.getDependencies() &
+ (StaticProperty.DEPENDS_ON_XSLT_CONTEXT |
+ StaticProperty.DEPENDS_ON_LOCAL_VARIABLES |
+ StaticProperty.DEPENDS_ON_USER_FUNCTIONS));
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return ExpressionTool.makePathExpression(start.copy(), step.copy(), false);
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ int startProperties = start.getSpecialProperties();
+ int stepProperties = step.getSpecialProperties();
+
+ int p = 0;
+ if (!Cardinality.allowsMany(start.getCardinality())) {
+ startProperties |= StaticProperty.ORDERED_NODESET |
+ StaticProperty.PEER_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET;
+ }
+ if (!Cardinality.allowsMany(step.getCardinality())) {
+ stepProperties |= StaticProperty.ORDERED_NODESET |
+ StaticProperty.PEER_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET;
+ }
+
+ if ((startProperties & stepProperties & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
+ p |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
+ }
+ if (((startProperties & StaticProperty.SINGLE_DOCUMENT_NODESET) != 0) &&
+ ((stepProperties & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0)) {
+ p |= StaticProperty.SINGLE_DOCUMENT_NODESET;
+ }
+ if ((startProperties & stepProperties & StaticProperty.PEER_NODESET) != 0) {
+ p |= StaticProperty.PEER_NODESET;
+ }
+ if ((startProperties & stepProperties & StaticProperty.SUBTREE_NODESET) != 0) {
+ p |= StaticProperty.SUBTREE_NODESET;
+ }
+
+ if (testNaturallySorted(startProperties, stepProperties)) {
+ p |= StaticProperty.ORDERED_NODESET;
+ }
+
+ if (testNaturallyReverseSorted()) {
+ p |= StaticProperty.REVERSE_DOCUMENT_ORDER;
+ }
+
+ if ((startProperties & stepProperties & StaticProperty.NON_CREATIVE) != 0) {
+ p |= StaticProperty.NON_CREATIVE;
+ }
+
+ return p;
+ }
+
+ /**
+ * Determine if we can guarantee that the nodes are delivered in document order.
+ * This is true if the start nodes are sorted peer nodes
+ * and the step is based on an Axis within the subtree rooted at each node.
+ * It is also true if the start is a singleton node and the axis is sorted.
+ *
+ * @param startProperties the properties of the left-hand expression
+ * @param stepProperties the properties of the right-hand expression
+ * @return true if the natural nested-loop evaluation strategy for the expression
+ * is known to deliver results with no duplicates and in document order, that is,
+ * if no additional sort is required
+ */
+
+ private boolean testNaturallySorted(int startProperties, int stepProperties) {
+
+ // System.err.println("**** Testing pathExpression.isNaturallySorted()");
+ // display(20);
+ // System.err.println("Start is ordered node-set? " + start.isOrderedNodeSet());
+ // System.err.println("Start is naturally sorted? " + start.isNaturallySorted());
+ // System.err.println("Start is singleton? " + start.isSingleton());
+
+ if ((stepProperties & StaticProperty.ORDERED_NODESET) == 0) {
+ return false;
+ }
+ if (Cardinality.allowsMany(start.getCardinality())) {
+ if ((startProperties & StaticProperty.ORDERED_NODESET) == 0) {
+ return false;
+ }
+ } else {
+ //if ((stepProperties & StaticProperty.ORDERED_NODESET) != 0) {
+ return true;
+ //}
+ }
+
+ // We know now that both the start and the step are sorted. But this does
+ // not necessarily mean that the combination is sorted.
+
+ // The result is sorted if the start is sorted and the step selects attributes
+ // or namespaces
+
+ if ((stepProperties & StaticProperty.ATTRIBUTE_NS_NODESET) != 0) {
+ return true;
+ }
+
+ // The result is sorted if the step is creative (e.g. a call to copy-of())
+
+ if ((stepProperties & StaticProperty.NON_CREATIVE) == 0) {
+ return true;
+ }
+
+ // The result is sorted if the start selects "peer nodes" (that is, a node-set in which
+ // no node is an ancestor of another) and the step selects within the subtree rooted
+ // at the context node
+
+ return ((startProperties & StaticProperty.PEER_NODESET) != 0) &&
+ ((stepProperties & StaticProperty.SUBTREE_NODESET) != 0);
+
+ }
+
+ /**
+ * Determine if the path expression naturally returns nodes in reverse document order
+ *
+ * @return true if the natural nested-loop evaluation strategy returns nodes in reverse
+ * document order
+ */
+
+ private boolean testNaturallyReverseSorted() {
+
+ // Some examples of path expressions that are naturally reverse sorted:
+ // ancestor::*/@x
+ // ../preceding-sibling::x
+ // $x[1]/preceding-sibling::node()
+
+ // This information is used to do a simple reversal of the nodes
+ // instead of a full sort, which is significantly cheaper, especially
+ // when using tree models (such as DOM and JDOM) in which comparing
+ // nodes in document order is an expensive operation.
+
+
+ if (!Cardinality.allowsMany(start.getCardinality()) &&
+ (step instanceof AxisExpression)) {
+ return !AxisInfo.isForwards[((AxisExpression) step).getAxis()];
+ }
+
+ return !Cardinality.allowsMany(step.getCardinality()) &&
+ (start instanceof AxisExpression) &&
+ !AxisInfo.isForwards[((AxisExpression) start).getAxis()];
+
+ }
+
+
+ /**
+ * Determine the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ int c1 = start.getCardinality();
+ int c2 = step.getCardinality();
+ return Cardinality.multiply(c1, c2);
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ Expression head = getLeadingSteps();
+ Expression tail = getLastStep();
+ if (head instanceof ItemChecker) {
+ // No need to typecheck the context item
+ ItemChecker checker = (ItemChecker) head;
+ if (checker.getBaseExpression() instanceof ContextItemExpression) {
+ return tail.toPattern(config, is30);
+ }
+ }
+ Pattern tailPattern = tail.toPattern(config, is30);
+ if (tailPattern instanceof ItemTypePattern) {
+ if (tailPattern.getItemType() instanceof ErrorType) {
+ return tailPattern;
+ }
+ }
+
+ byte axis = AxisInfo.PARENT;
+ Pattern headPattern = null;
+ if (head instanceof SlashExpression) {
+ SlashExpression start = (SlashExpression) head;
+ if (start.getControlledExpression() instanceof AxisExpression) {
+ AxisExpression mid = (AxisExpression) start.getControlledExpression();
+ if (mid.getAxis() == AxisInfo.DESCENDANT_OR_SELF &&
+ (mid.getNodeTest() == null || mid.getNodeTest() instanceof AnyNodeTest)) {
+ axis = AxisInfo.ANCESTOR;
+ headPattern = start.getControllingExpression().toPattern(config, is30);
+ }
+ }
+ }
+ if (headPattern == null) {
+ axis = PatternMaker.getAxisForPathStep(tail);
+ headPattern = head.toPattern(config, is30);
+ }
+ return new AncestorQualifiedPattern(tailPattern, headPattern, axis);
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ if (!(other instanceof SlashExpression)) {
+ return false;
+ }
+ SlashExpression p = (SlashExpression) other;
+ return (start.equals(p.start) && step.equals(p.step));
+ }
+
+ /**
+ * get HashCode for comparing two expressions
+ */
+
+ public int hashCode() {
+ return "SlashExpression".hashCode() + start.hashCode() + step.hashCode();
+ }
+
+ /**
+ * Iterate the path-expression in a given context
+ *
+ * @param context the evaluation context
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(final XPathContext context) throws XPathException {
+
+ // This class delivers the result of the path expression in unsorted order,
+ // without removal of duplicates. If sorting and deduplication are needed,
+ // this is achieved by wrapping the path expression in a DocumentSorter
+
+ SequenceIterator result = start.iterate(context);
+ XPathContext context2 = context.newMinorContext();
+ context2.setCurrentIterator(result);
+ return new ContextMappingIterator<Item>(this, context2);
+ }
+
+ /**
+ * Mapping function, from a node returned by the start iteration, to a sequence
+ * returned by the child.
+ */
+
+ public SequenceIterator map(XPathContext context) throws XPathException {
+ return step.iterate(context);
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("slash");
+ if (this instanceof SimpleStepExpression) {
+ destination.emitAttribute("simple-step", "true");
+ }
+ start.explain(destination);
+ step.explain(destination);
+ destination.endElement();
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ *
+ * @return a representation of the expression as a string
+ */
+
+ public String toString() {
+ return ExpressionTool.parenthesize(start) + "/" + ExpressionTool.parenthesize(step);
+ }
+
+ /**
+ * Get the first step in this expression. A path expression A/B/C is represented as (A/B)/C, but
+ * the first step is A
+ *
+ * @return the first step in the expression, after expanding any nested path expressions
+ */
+
+ public Expression getFirstStep() {
+ if (start instanceof SlashExpression) {
+ return ((SlashExpression) start).getFirstStep();
+ } else {
+ return start;
+ }
+ }
+
+ /**
+ * Get step of the path expression
+ *
+ * @return the step expression
+ */
+
+ public Expression getStep() {
+ return step;
+ }
+
+ /**
+ * Get all steps after the first.
+ * This is complicated by the fact that A/B/C is represented as ((A/B)/C; we are required
+ * to return B/C
+ *
+ * @return a path expression containing all steps in this path expression other than the first,
+ * after expanding any nested path expressions
+ */
+
+ public Expression getRemainingSteps() {
+ if (start instanceof SlashExpression) {
+ SlashExpression rem =
+ new SlashExpression(((SlashExpression) start).getRemainingSteps(), step);
+ ExpressionTool.copyLocationInfo(start, rem);
+ return rem;
+ } else {
+ return step;
+ }
+ }
+
+ /**
+ * Get the last step of the path expression
+ *
+ * @return the last step in the expression, after expanding any nested path expressions
+ */
+
+ public Expression getLastStep() {
+ if (step instanceof SlashExpression) {
+ return ((SlashExpression) step).getLastStep();
+ } else {
+ return step;
+ }
+ }
+
+ /**
+ * Get a path expression consisting of all steps except the last
+ *
+ * @return a path expression containing all steps in this path expression other than the last,
+ * after expanding any nested path expressions
+ */
+
+ public Expression getLeadingSteps() {
+ if (step instanceof SlashExpression) {
+ SlashExpression rem =
+ new SlashExpression(start, ((SlashExpression) step).getLeadingSteps());
+ ExpressionTool.copyLocationInfo(start, rem);
+ return rem;
+ } else {
+ return start;
+ }
+ }
+
+ /**
+ * Test whether a path expression is an absolute path - that is, a path whose first step selects a
+ * document node
+ *
+ * @param th the type hierarchy cache
+ * @return true if the first step in this path expression selects a document node
+ */
+
+ public boolean isAbsolute(TypeHierarchy th) {
+ Expression first = getFirstStep();
+ if (first.getItemType(th).getPrimitiveType() == Type.DOCUMENT) {
+ return true;
+ }
+ // This second test allows keys to be built. See XMark q9.
+// if (first instanceof AxisExpression && ((AxisExpression)first).getContextItemType().getPrimitiveType() == Type.DOCUMENT) {
+// return true;
+// };
+ return false;
+ }
+
+
+//#ifdefined STREAM
+ /**
+ * Tests whether a path selects attributes of ancestor elements: this has special significance for streamability
+ * analysis. More specifically, if a path consists entirely of steps using the parent, ancestor, self, ancestor-or-self,
+ * attribute and namespace axes, optionally filtered by motionless predicates,
+ * then the expression is motionless provided that it is used in a boolean, atomizing,
+ * or inspection context (to ensure that the selected nodes cannot be used as the basis for further navigation)
+ *
+ * @param exp the expression being tested
+ * @return true if this is a path using the permitted axes together with motionless predicates
+ */
+
+ public static boolean isInheritedAttributePath(Expression exp) {
+ return isPermittedStep(exp) ||
+ (exp instanceof SlashExpression &&
+ isPermittedStep(((SlashExpression) exp).getLastStep()) &&
+ isInheritedAttributePath(((SlashExpression) exp).getLeadingSteps())) ||
+ (exp instanceof ForEach &&
+ isPermittedStep(((ForEach) exp).getActionExpression()) &&
+ isInheritedAttributePath(((ForEach) exp).getSelectExpression()));
+ }
+
+ private static boolean isPermittedStep(Expression exp) {
+ return exp instanceof ParentNodeExpression ||
+ exp instanceof ContextItemExpression ||
+ (exp instanceof AxisExpression && isPermittedAxis(((AxisExpression) exp).getAxis())) ||
+ (exp instanceof FilterExpression &&
+ isPermittedStep(((FilterExpression) exp).getControllingExpression()) &&
+ ((FilterExpression) exp).getControlledExpression().getStreamability(
+ INSPECTION_CONTEXT, false, null) == W3C_MOTIONLESS) ||
+ (exp instanceof Reverse && isInheritedAttributePath(((Reverse) exp).getArguments()[0]));
+ }
+
+ public static boolean isPermittedAxis(byte axis) {
+ return axis == AxisInfo.PARENT || axis == AxisInfo.ANCESTOR || axis == AxisInfo.ANCESTOR_OR_SELF || axis == AxisInfo.ATTRIBUTE ||
+ axis == AxisInfo.SELF || axis == AxisInfo.NAMESPACE;
+ }
+
+
+
+
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * constructs streamable that would not be so according to W3C rules.
+ * @param reasons
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+
+ return ForEachAdjunct.getStreamability(this, syntacticContext, allowExtensions, reasons);
+
+// // Test for expressions that select attributes of ancestors, etc
+// if (allowExtensions &&
+// syntacticContext != Expression.NAVIGATION_CONTEXT &&
+// SlashExpression.isInheritedAttributePath(this)) {
+// return Expression.W3C_MOTIONLESS;
+// }
+//
+// Expression start = getFirstStep();
+// if (start instanceof ItemChecker) {
+// start = ((ItemChecker)start).getBaseExpression();
+// }
+// if (start instanceof ContextItemExpression) {
+// return getRemainingSteps().getStreamability(syntacticContext, allowExtensions, reasons);
+// }
+// if ((start.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) == 0) {
+// return W3C_MOTIONLESS;
+// }
+// int ss = start.getStreamability(INSPECTION_CONTEXT, allowExtensions, reasons);
+// if (ss == W3C_MOTIONLESS || ss == W3C_FREE_RANGING) {
+// // This is what the spec says, but I'm not sure it's safe - MHK 2012-10-23
+// return ss;
+// }
+// Expression last = getLastStep();
+// if (last instanceof ItemChecker) {
+// last = ((ItemChecker)last).getBaseExpression();
+// }
+// if (last instanceof ContextItemExpression) {
+// return getLeadingSteps().getStreamability(syntacticContext, allowExtensions, reasons);
+// }
+// if ((last.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) == 0) {
+// return getLeadingSteps().getStreamability(INSPECTION_CONTEXT, allowExtensions, reasons);
+// }
+// if (syntacticContext == NODE_VALUE_CONTEXT || syntacticContext == INSPECTION_CONTEXT) {
+// if (isInheritedAttributePath(this)) {
+// return W3C_MOTIONLESS;
+// }
+// if (Streamability.isIncrementallyConsuming(this)) {
+// return W3C_CONSUMING;
+// }
+// if (allowExtensions) {
+// if (reasons == null) {
+// reasons = new ArrayList<String>();
+// }
+// Pattern selection = StreamingPatternMaker.makeStreamingPattern(this, getExecutable().getConfiguration(), reasons);
+// if (selection != null) {
+// return W3C_CONSUMING;
+// }
+// }
+// } else if (syntacticContext == NAVIGATION_CONTEXT) {
+// // this bit isn't in the spec. We're OK provided that we don't select a node from the streamed document
+// TypeHierarchy th = getExecutable().getConfiguration().getTypeHierarchy();
+// if (th.relationship(getItemType(th), AnyNodeTest.getInstance()) == TypeHierarchy.DISJOINT) {
+// return getStreamability(INSPECTION_CONTEXT, allowExtensions, reasons);
+// }
+// if ((step.getSpecialProperties() & StaticProperty.NON_CREATIVE) == 0) {
+// // the step creates new nodes, e.g. copy-of() or snapshot
+// return start.getStreamability(INSPECTION_CONTEXT, allowExtensions, reasons);
+// }
+// }
+// if (reasons != null) {
+// reasons.add("Path expression {" + this + "} is free-ranging");
+// }
+// return W3C_FREE_RANGING;
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public ForEachAdjunct getStreamingAdjunct() {
+ return new ForEachAdjunct(); //sic
+ }
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ Expression head = getLeadingSteps();
+ if (head instanceof RootExpression) {
+ reasonForFailure.add("A streamable pattern must not start with '/' or '//'");
+ return null;
+ }
+ if (head instanceof ItemChecker) {
+ head = ((ItemChecker)head).getBaseExpression();
+ }
+ Expression tail = getLastStep();
+ Pattern tailPattern = tail.toStreamingPattern(config, reasonForFailure);
+ if (tailPattern instanceof AncestorQualifiedPattern &&
+ ((AncestorQualifiedPattern)tailPattern).getUpperPattern() instanceof AnchorPattern) {
+ tailPattern = ((AncestorQualifiedPattern)tailPattern).getBasePattern();
+ }
+
+ byte axis;
+ try {
+ if (tailPattern instanceof SimplePositionalPattern) {
+ axis = AxisInfo.PARENT;
+ } else {
+ axis = PatternMaker.getAxisForPathStep(tail);
+ }
+ } catch (XPathException e) {
+ reasonForFailure.add(e.getMessage());
+ return null;
+ }
+ Pattern headPattern = head.toStreamingPattern(config, reasonForFailure);
+ if (!reasonForFailure.isEmpty()) {
+ return null;
+ }
+ return new AncestorQualifiedPattern(tailPattern, headPattern, axis);
+ }
+
+//#endif
+//#ifdefined BYTECODE
+
+ /**
+ * Return the compiler of the Slash expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SlashExpressionCompiler();
+ }
+//#endif
+
+
+}
+
diff --git a/sf/saxon/expr/StackFrame.java b/sf/saxon/expr/StackFrame.java
new file mode 100644
index 0000000..d668a15
--- /dev/null
+++ b/sf/saxon/expr/StackFrame.java
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.om.Sequence;
+
+import java.util.Stack;
+
+/**
+ * This class represents a stack frame holding details of the variables used in a function or in
+ * an XSLT template.
+ */
+
+public class StackFrame {
+ protected SlotManager map;
+ protected Sequence[] slots;
+ protected Stack<Sequence> dynamicStack;
+
+ public static final StackFrame EMPTY = new StackFrame(SlotManager.EMPTY, new Sequence[0]);
+
+ public StackFrame (SlotManager map, Sequence[] slots) {
+ this.map = map;
+ this.slots = slots;
+ }
+
+ public SlotManager getStackFrameMap() {
+ return map;
+ }
+
+ public Sequence[] getStackFrameValues() {
+ return slots;
+ }
+
+ public void setStackFrameValues(Sequence[] values) {
+ slots = values;
+ }
+
+ public StackFrame copy() {
+ Sequence[] v2 = new Sequence[slots.length];
+ System.arraycopy(slots, 0, v2, 0, slots.length);
+ StackFrame s = new StackFrame(map, v2);
+ if (dynamicStack != null) {
+ s.dynamicStack = new Stack<Sequence>();
+ s.dynamicStack.addAll(dynamicStack);
+ }
+ return s;
+ }
+
+ public void pushDynamicValue(Sequence value) {
+ if (dynamicStack == null) {
+ dynamicStack = new Stack<Sequence>();
+ }
+ dynamicStack.push(value);
+ }
+
+ public Sequence popDynamicValue() {
+ return dynamicStack.pop();
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/StatefulMappingFunction.java b/sf/saxon/expr/StatefulMappingFunction.java
new file mode 100644
index 0000000..c31b18d
--- /dev/null
+++ b/sf/saxon/expr/StatefulMappingFunction.java
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.Item;
+
+/**
+ * MappingFunction is an interface that must be satisfied by an object passed to a
+ * MappingIterator. StatefulMappingFunction is a sub-interface representing a mapping
+ * function that maintains state information, and which must therefore be cloned
+ * when the mapping iterator is cloned.
+*/
+
+public interface StatefulMappingFunction<F extends Item, T extends Item> {
+
+ /**
+ * Return a clone of this MappingFunction, with the state reset to its state at the beginning
+ * of the underlying iteration
+ * @return a clone of this MappingFunction
+ */
+
+ public StatefulMappingFunction<F, T> getAnother();
+
+}
+
diff --git a/sf/saxon/expr/StaticContext.java b/sf/saxon/expr/StaticContext.java
new file mode 100644
index 0000000..fd5e0b3
--- /dev/null
+++ b/sf/saxon/expr/StaticContext.java
@@ -0,0 +1,241 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.instruct.LocationMap;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.DecimalFormatManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.DecimalValue;
+
+import javax.xml.transform.SourceLocator;
+import java.util.Set;
+
+/**
+* A StaticContext contains the information needed while an expression or pattern
+* is being parsed. The information is also sometimes needed at run-time.
+*/
+
+public interface StaticContext {
+
+ /**
+ * Get the system configuration
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration();
+
+ /**
+ * Ask whether expressions compiled under this static context are schema-aware.
+ * They must be schema-aware if the expression is to handle typed (validated) nodes
+ * @return true if expressions are schema-aware
+ */
+
+ public boolean isSchemaAware();
+
+
+ /**
+ * Construct a dynamic context for early evaluation of constant subexpressions.
+ * @return a newly constructed dynamic context
+ */
+
+ public XPathContext makeEarlyEvaluationContext();
+
+ /**
+ * Get the location map. This is a mapping from short location ids held with each expression or
+ * subexpression, to a fully-resolved location in a source stylesheet or query.
+ * @return the location map
+ */
+
+ public LocationMap getLocationMap();
+
+ /**
+ * Issue a compile-time warning.
+ * @param message The warning message. This should not contain any prefix such as "Warning".
+ * @param locator the location of the construct in question. May be null.
+ */
+
+ public void issueWarning(String message, /*@Nullable*/ SourceLocator locator);
+
+ /**
+ * Get the System ID of the container of the expression. This is the containing
+ * entity (file) and is therefore useful for diagnostics. Use getBaseURI() to get
+ * the base URI, which may be different.
+ * @return the system ID
+ */
+
+ public String getSystemId();
+
+ /**
+ * Get the line number of the expression within its containing entity
+ * Returns -1 if no line number is available
+ * @return the line number, or -1 if not available
+ */
+
+ public int getLineNumber();
+
+ /**
+ * Get the Base URI of the stylesheet element, for resolving any relative URI's used
+ * in the expression.
+ * Used by the document(), doc(), resolve-uri(), and base-uri() functions.
+ * May return null if the base URI is not known.
+ * @return the static base URI, or null if not known
+ */
+
+ public String getBaseURI();
+
+ /**
+ * Get the URI for a namespace prefix. The default namespace is NOT used
+ * when the prefix is empty.
+ * @param prefix The namespace prefix.
+ * @return the corresponding namespace URI
+ * @throws net.sf.saxon.trans.XPathException if the prefix is not declared; the
+ * associated error code should be XPST0081
+ */
+
+ public String getURIForPrefix(String prefix) throws XPathException;
+
+ /**
+ * Get the NamePool used for compiling expressions
+ * @return the name pool
+ */
+
+ public NamePool getNamePool();
+
+ /**
+ * Bind a variable used in this element to the XSLVariable element in which it is declared
+ * @param qName The name of the variable
+ * @return an expression representing the variable reference, This will often be
+ * a {@link VariableReference}, suitably initialized to refer to the corresponding variable declaration,
+ * but in general it can be any expression which returns the variable's value when evaluated.
+ */
+
+ public Expression bindVariable(StructuredQName qName) throws XPathException;
+
+ /**
+ * Get the function library containing all the in-scope functions available in this static
+ * context
+ * @return the function library
+ */
+
+ public FunctionLibrary getFunctionLibrary();
+
+ /**
+ * Get a named collation.
+ * @param name The name of the required collation. Supply null to get the default collation.
+ * @return the collation; or null if the required collation is not found.
+ */
+
+ public StringCollator getCollation(String name);
+
+ /**
+ * Get the name of the default collation.
+ * @return the name of the default collation; or the name of the codepoint collation
+ * if no default collation has been defined
+ */
+
+ public String getDefaultCollationName();
+
+ /**
+ * Get the default XPath namespace for elements and types
+ * @return the default namespace, or NamespaceConstant.NULL for the non-namespace
+ */
+
+ public String getDefaultElementNamespace();
+
+ /**
+ * Get the default function namespace
+ * @return the default namespace for function names
+ */
+
+ public String getDefaultFunctionNamespace();
+
+ /**
+ * Determine whether backwards compatibility mode is used
+ * @return true if 1.0 compaibility mode is in force.
+ */
+
+ public boolean isInBackwardsCompatibleMode();
+
+ /**
+ * Ask whether a Schema for a given target namespace has been imported. Note that the
+ * in-scope element declarations, attribute declarations and schema types are the types registered
+ * with the (schema-aware) configuration, provided that their namespace URI is registered
+ * in the static context as being an imported schema namespace. (A consequence of this is that
+ * within a Configuration, there can only be one schema for any given namespace, including the
+ * null namespace).
+ * @param namespace the target namespace in question
+ * @return true if the given namespace has been imported
+ */
+
+ public boolean isImportedSchema(String namespace);
+
+ /**
+ * Get the set of imported schemas
+ * @return a Set, the set of URIs representing the target namespaces of imported schemas,
+ * using the zero-length string to denote the "null" namespace.
+ */
+
+ public Set<String> getImportedSchemaNamespaces();
+
+ /**
+ * Ask whether a built-in type is available in this context. This method caters for differences
+ * between host languages as to which set of types are built in.
+ * @param type the supposedly built-in type. This will always be a type in the
+ * XS namespace.
+ * @return true if this type can be used in this static context
+ */
+
+ public boolean isAllowedBuiltInType(BuiltInAtomicType type);
+
+ /**
+ * Get a namespace resolver to resolve the namespaces declared in this static context.
+ * @return a namespace resolver.
+ */
+
+ public NamespaceResolver getNamespaceResolver();
+
+ /**
+ * Get the required type of the context item. If no type has been explicitly declared for the context
+ * item, an instance of AnyItemType (representing the type item()) is returned.
+ * @return the required type of the context item
+ * @since 9.3
+ */
+
+ public ItemType getRequiredContextItemType();
+
+ /**
+ * Get a DecimalFormatManager to resolve the names of decimal formats used in calls
+ * to the format-number() function.
+ * @return the decimal format manager for this static context, or null if no named decimal
+ * formats are available in this environment.
+ * @since 9.2
+ */
+
+ public DecimalFormatManager getDecimalFormatManager();
+
+ /**
+ * Get the XPath language level supported, as a decimal value.
+ * The current levels supported are 2.0, and 3.0. The default is 2.0.
+ * If running XQuery 1.0, the value is 2.0; if running XQuery 3.0, it is 3.0.
+ * @return the XPath language level; the return value will be either
+ * {@link net.sf.saxon.value.DecimalValue#TWO} or {@link net.sf.saxon.value.DecimalValue#THREE}
+ * @since 9.3
+ */
+
+ public DecimalValue getXPathLanguageLevel();
+
+}
+
diff --git a/sf/saxon/expr/StaticProperty.java b/sf/saxon/expr/StaticProperty.java
new file mode 100644
index 0000000..1a75f97
--- /dev/null
+++ b/sf/saxon/expr/StaticProperty.java
@@ -0,0 +1,335 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+/**
+* This class contains constants identifying dependencies that an XPath expression
+* might have on its context.
+*/
+
+public abstract class StaticProperty {
+
+ /**
+ * Bit setting: Expression depends on current() item
+ */
+
+ public static final int DEPENDS_ON_CURRENT_ITEM = 1;
+
+ /**
+ * Bit setting: Expression depends on context item
+ */
+
+ public static final int DEPENDS_ON_CONTEXT_ITEM = 1<<1;
+
+ /**
+ * Bit setting: Expression depends on position()
+ */
+
+ public static final int DEPENDS_ON_POSITION = 1<<2;
+
+ /**
+ * Bit setting: Expression depends on last()
+ */
+
+ public static final int DEPENDS_ON_LAST = 1<<3;
+
+ /**
+ * Bit setting: Expression depends on the document containing the context node
+ */
+
+ public static final int DEPENDS_ON_CONTEXT_DOCUMENT = 1<<4;
+
+ /**
+ * Bit setting: Expression depends on current-group() and/or current-grouping-key()
+ */
+
+ public static final int DEPENDS_ON_CURRENT_GROUP = 1<<5;
+
+ /**
+ * Bit setting: Expression depends on regex-group()
+ */
+
+ public static final int DEPENDS_ON_REGEX_GROUP = 1<<6;
+
+
+ /**
+ * Bit setting: Expression depends on local variables
+ */
+
+ public static final int DEPENDS_ON_LOCAL_VARIABLES = 1<<7;
+
+ /**
+ * Bit setting: Expression depends on user-defined functions
+ */
+
+ public static final int DEPENDS_ON_USER_FUNCTIONS = 1<<8;
+
+ /**
+ * Bit setting: Expression depends on assignable global variables
+ */
+
+ public static final int DEPENDS_ON_ASSIGNABLE_GLOBALS = 1<<9;
+
+ /**
+ * Bit setting: Expression can't be evaluated at compile time for reasons other than the above
+ */
+
+ public static final int DEPENDS_ON_RUNTIME_ENVIRONMENT = 1<<10;
+
+ /**
+ * Combination of bits representing dependencies on the XSLT context
+ */
+
+ public static final int DEPENDS_ON_XSLT_CONTEXT =
+ DEPENDS_ON_CURRENT_ITEM |
+ DEPENDS_ON_CURRENT_GROUP |
+ DEPENDS_ON_REGEX_GROUP |
+ DEPENDS_ON_ASSIGNABLE_GLOBALS;
+
+ /**
+ * Combination of bits representing dependencies on the focus
+ */
+
+ public static final int DEPENDS_ON_FOCUS =
+ DEPENDS_ON_CONTEXT_ITEM |
+ DEPENDS_ON_POSITION |
+ DEPENDS_ON_LAST |
+ DEPENDS_ON_CONTEXT_DOCUMENT;
+
+ /**
+ * Combination of bits representing dependencies on the focus, but excluding dependencies
+ * on the current document
+ */
+
+ public static final int DEPENDS_ON_NON_DOCUMENT_FOCUS =
+ DEPENDS_ON_CONTEXT_ITEM |
+ DEPENDS_ON_POSITION |
+ DEPENDS_ON_LAST;
+
+ /*
+ * Bit set if an empty sequence is allowed
+ */
+
+ public static final int ALLOWS_ZERO = 1<<13;
+
+ /**
+ * Bit set if a single value is allowed
+ */
+
+ public static final int ALLOWS_ONE = 1<<14;
+
+ /**
+ * Bit set if multiple values are allowed
+ */
+
+ public static final int ALLOWS_MANY = 1<<15;
+
+ /**
+ * Mask for all cardinality bits
+ */
+
+ public static final int CARDINALITY_MASK =
+ ALLOWS_ZERO | ALLOWS_ONE | ALLOWS_MANY;
+
+ /**
+ * Occurence indicator for "one or more" (+)
+ */
+
+ public static final int ALLOWS_ONE_OR_MORE =
+ ALLOWS_ONE | ALLOWS_MANY;
+
+ /**
+ * Occurence indicator for "zero or more" (*)
+ */
+
+ public static final int ALLOWS_ZERO_OR_MORE =
+ ALLOWS_ZERO | ALLOWS_ONE | ALLOWS_MANY;
+
+ /**
+ * Occurence indicator for "zero or one" (?)
+ */
+
+ public static final int ALLOWS_ZERO_OR_ONE =
+ ALLOWS_ZERO | ALLOWS_ONE;
+
+ /**
+ * Occurence indicator for "exactly one" (default occurrence indicator)
+ */
+
+ public static final int EXACTLY_ONE = ALLOWS_ONE;
+
+ /**
+ * Occurence indicator when an empty sequence is required
+ */
+
+ public static final int EMPTY = ALLOWS_ZERO;
+
+ /**
+ * Reduce the cardinality value to an integer in the range 0-7
+ * @param cardinality the result of calling getCardinality() on an expression
+ * @return the cardinality code
+ */
+
+ public static int getCardinalityCode(int cardinality) {
+ return (cardinality & CARDINALITY_MASK) >> 13;
+ }
+
+ /**
+ * Expression property: this bit is set by getProperties() in the case of
+ * an expression whose item type is node, when the nodes in the result are
+ * guaranteed all to be in the same document as the context node. For
+ * expressions that return values other than nodes, the setting is undefined.
+ */
+
+ public static final int CONTEXT_DOCUMENT_NODESET = 1<<16;
+
+ /**
+ * Expression property: this bit is set by getProperties() in the case of
+ * an expression whose item type is node, when the nodes in the result are
+ * in document order.
+ */
+
+ public static final int ORDERED_NODESET = 1<<17;
+
+ /**
+ * Expression property: this bit is set by getProperties() in the case of
+ * an expression that delivers items in the reverse of the correct order, when unordered
+ * retrieval is requested.
+ */
+
+ public static final int REVERSE_DOCUMENT_ORDER = 1<<18;
+
+ /**
+ * Expression property: this bit is set by getProperties() in the case of
+ * an expression that delivers a set of nodes with the guarantee that no node in the
+ * set will be an ancestor of any other. This property is useful in deciding whether the
+ * results of a path expression are pre-sorted. The property is only used in the case where
+ * the ORDERED_NODESET property is true, so there is no point in setting it in other cases.
+ */
+
+ public static final int PEER_NODESET = 1<<19;
+
+ /**
+ * Expression property: this bit is set by getProperties() in the case of
+ * an expression that delivers a set of nodes with the guarantee that every node in the
+ * result will be a descendant or self, or attribute or namespace, of the context node
+ */
+
+ public static final int SUBTREE_NODESET = 1<<20;
+
+ /**
+ * Expression property: this bit is set by getProperties() in the case of
+ * an expression that delivers a set of nodes with the guarantee that every node in the
+ * result will be an attribute or namespace of the context node
+ */
+
+ public static final int ATTRIBUTE_NS_NODESET = 1<<21;
+
+ /**
+ * Expression property: this bit is set in the case of an expression that will
+ * never return newly created nodes, nor a value that depends on the identity
+ * of newly created nodes (for example generate-id(new-node())). Expressions
+ * that do create new nodes cannot be moved out of loops as this could cause
+ * too few nodes to be created: for example if f() creates a new node, then
+ * count(for $i in 1 to 5 return f()) must be 5.
+ */
+
+ public static final int NON_CREATIVE = 1<<22;
+
+ /**
+ * Expression property: this bit is set in the case of an expression that delivers
+ * a set of nodes that are all in the same document (not necessarily the same
+ * document as the context node).
+ */
+
+ public static final int SINGLE_DOCUMENT_NODESET = 1<<23;
+
+ /**
+ * Expression property: this bit indicates that an expression has (or might have)
+ * side-effects. This property is applied to calls on extension functions and to
+ * certain instructions such as xsl:result-document and xsl:message.
+ */
+
+ public static final int HAS_SIDE_EFFECTS = 1<<24;
+
+ /**
+ * Expression property: this bit indicates that although the static type of the expression
+ * permits untyped atomic values, it is known that the value will not be untyped atomic.
+ */
+
+ public static final int NOT_UNTYPED_ATOMIC = 1<<25;
+
+ /**
+ * Expression property: this bit indicates that in the result of an expression,
+ * any element and attribute nodes that are present will have type annotation xs:untyped or
+ * xs:untypedAtomic respectively, and that any document nodes that are present will have
+ * no element children whose type annotation is anything other than xs:untyped
+ */
+
+ public static final int ALL_NODES_UNTYPED = 1<<26;
+
+ /**
+ * Expression property: this bit indicates that the expression is required during evaluation to maintain
+ * the values of position() and last() because they might be required.
+ */
+
+ public static final int MUST_MAINTAIN_POSITION = 1<<27;
+
+ /**
+ * Mask to select all the dependency bits
+ */
+
+ public static final int DEPENDENCY_MASK =
+ DEPENDS_ON_CONTEXT_DOCUMENT |
+ DEPENDS_ON_CONTEXT_ITEM |
+ DEPENDS_ON_CURRENT_GROUP |
+ DEPENDS_ON_REGEX_GROUP |
+ DEPENDS_ON_CURRENT_ITEM |
+ DEPENDS_ON_FOCUS |
+ DEPENDS_ON_LOCAL_VARIABLES |
+ DEPENDS_ON_USER_FUNCTIONS |
+ DEPENDS_ON_ASSIGNABLE_GLOBALS |
+ DEPENDS_ON_RUNTIME_ENVIRONMENT |
+ HAS_SIDE_EFFECTS;
+
+ /**
+ * Mask for "special properties": that is, all properties other than cardinality
+ * and dependencies
+ */
+
+ public static final int SPECIAL_PROPERTY_MASK =
+ CONTEXT_DOCUMENT_NODESET |
+ ORDERED_NODESET |
+ REVERSE_DOCUMENT_ORDER |
+ PEER_NODESET |
+ SUBTREE_NODESET |
+ ATTRIBUTE_NS_NODESET |
+ SINGLE_DOCUMENT_NODESET |
+ NON_CREATIVE |
+ HAS_SIDE_EFFECTS |
+ NOT_UNTYPED_ATOMIC |
+ ALL_NODES_UNTYPED |
+ MUST_MAINTAIN_POSITION;
+
+ /**
+ * Mask for nodeset-related properties
+ */
+
+ public static final int NODESET_PROPERTIES =
+ CONTEXT_DOCUMENT_NODESET |
+ ORDERED_NODESET |
+ REVERSE_DOCUMENT_ORDER |
+ PEER_NODESET |
+ SUBTREE_NODESET |
+ ATTRIBUTE_NS_NODESET |
+ SINGLE_DOCUMENT_NODESET |
+ ALL_NODES_UNTYPED;
+
+ // This class is not instantiated
+ private StaticProperty() {}
+}
diff --git a/sf/saxon/expr/StringLiteral.java b/sf/saxon/expr/StringLiteral.java
new file mode 100644
index 0000000..ac9c5e5
--- /dev/null
+++ b/sf/saxon/expr/StringLiteral.java
@@ -0,0 +1,53 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * Subclass of Literal used specifically for string literals, as this is a common case
+ */
+public class StringLiteral extends Literal {
+
+ /**
+ * Create a StringLiteral that wraps a StringValue
+ * @param value the StringValue
+ */
+
+ public StringLiteral(StringValue value) {
+ super(value);
+ }
+
+ /**
+ * Create a StringLiteral that wraps any CharSequence (including, of course, a String)
+ * @param value the CharSequence to be wrapped
+ */
+
+ public StringLiteral(CharSequence value) {
+ super(StringValue.makeStringValue(value));
+ }
+
+ /**
+ * Get the string represented by this StringLiteral
+ * @return the underlying string
+ */
+
+ public String getStringValue() {
+ //noinspection RedundantCast
+ return ((StringValue)getValue()).getStringValue();
+ }
+
+ /*@NotNull*/
+ public Expression copy() {
+ StringLiteral stringLiteral = new StringLiteral((StringValue)getValue());
+ ExpressionTool.copyLocationInfo(this, stringLiteral);
+ return stringLiteral;
+ }
+}
+
diff --git a/sf/saxon/expr/StringTokenIterator.java b/sf/saxon/expr/StringTokenIterator.java
new file mode 100644
index 0000000..f410aba
--- /dev/null
+++ b/sf/saxon/expr/StringTokenIterator.java
@@ -0,0 +1,101 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.value.StringValue;
+
+import java.util.StringTokenizer;
+
+/**
+* StringTokenIterator: breaks a string up into tokens,
+* and returns the tokens as a sequence of strings.
+*/
+
+public class StringTokenIterator implements UnfailingIterator<StringValue> {
+
+ private String theString;
+ /*@Nullable*/ private String delimiters; // null implies use whitespace as delimiter
+ private StringTokenizer tokenizer;
+ private String current;
+ private int position = 0;
+
+ /**
+ * Construct a StringTokenIterator that will break the supplied
+ * string into tokens at whitespace boundaries
+ * @param string the string to be tokenized
+ */
+
+ public StringTokenIterator (String string) {
+ theString = string;
+ delimiters = null;
+ tokenizer = new StringTokenizer(string, " \t\n\r", false);
+ }
+
+ /**
+ * Construct a StringTokenIterator that will break the supplied
+ * string into tokens at any of the delimiter characters included in the
+ * delimiter string.
+ * @param string the string to be tokenized
+ * @param delimiters the characters that are recognized as token separators
+ */
+
+ public StringTokenIterator (String string, String delimiters) {
+ theString = string;
+ this.delimiters = delimiters;
+ tokenizer = new StringTokenizer(string, delimiters, false);
+ }
+
+ public StringValue next() {
+ if (tokenizer.hasMoreElements()) {
+ current = (String)tokenizer.nextElement();
+ position++;
+ return new StringValue(current);
+ } else {
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+
+ public StringValue current() {
+ return (current == null ? null : new StringValue(current));
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/
+ public StringTokenIterator getAnother() {
+ if (delimiters==null) {
+ return new StringTokenIterator(theString);
+ } else {
+ return new StringTokenIterator(theString, delimiters);
+ }
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link SequenceIterator#GROUNDED}, {@link SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+}
+
diff --git a/sf/saxon/expr/SubExpressionInfo.java b/sf/saxon/expr/SubExpressionInfo.java
new file mode 100644
index 0000000..c55daa5
--- /dev/null
+++ b/sf/saxon/expr/SubExpressionInfo.java
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+/**
+ * Information about a sub-expression and its relationship to the parent expression
+ */
+
+public class SubExpressionInfo {
+
+ public Expression expression;
+ public boolean hasSameFocus;
+ public boolean isEvaluatedRepeatedly;
+ public int syntacticContext;
+
+ public SubExpressionInfo(Expression child, boolean hasSameFocus, boolean isEvaluatedRepeatedly, int syntacticContext) {
+ this.expression = child;
+ this.hasSameFocus = hasSameFocus;
+ this.isEvaluatedRepeatedly = isEvaluatedRepeatedly;
+ this.syntacticContext = syntacticContext;
+
+ }
+
+}
+
diff --git a/sf/saxon/expr/SubscriptExpression.java b/sf/saxon/expr/SubscriptExpression.java
new file mode 100644
index 0000000..2b79dd8
--- /dev/null
+++ b/sf/saxon/expr/SubscriptExpression.java
@@ -0,0 +1,260 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SubscriptExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.pattern.SimplePositionalPattern;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.MemoClosure;
+import net.sf.saxon.value.NumericValue;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A SubscriptExpression represents a FilterExpression of the form EXPR[n]
+ * where n is known to be singleton numeric and to be independent of the focus; it does not need to be constant
+ */
+public class SubscriptExpression extends SingleItemFilter {
+
+ Expression subscript;
+
+ /**
+ * Construct a SubscriptExpression
+ * @param base the expression to be filtered
+ * @param subscript the positional subscript filter
+ */
+
+ public SubscriptExpression(Expression base, Expression subscript) {
+ this.operand = base;
+ this.subscript = subscript;
+ adoptChildExpression(base);
+ adoptChildExpression(subscript);
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ subscript = visitor.typeCheck(subscript, contextItemType);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.optimize(operand, contextItemType);
+ subscript = visitor.optimize(subscript, contextItemType);
+ if (Literal.isConstantOne(subscript)) {
+ return FirstItemExpression.makeFirstItemExpression(operand);
+ }
+ return this;
+ }
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ if (offer.action != PromotionOffer.UNORDERED) {
+ operand = doPromotion(operand, offer);
+ subscript = doPromotion(subscript, offer);
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new SubscriptExpression(operand.copy(), subscript.copy());
+ }
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(operand, subscript);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (operand == original) {
+ operand = replacement;
+ found = true;
+ } else if (subscript == original) {
+ subscript = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Get the subscript expression
+ * @return the expression used to compute the one-based start offset
+ */
+
+ public Expression getSubscriptExpression() {
+ return subscript;
+ }
+
+ /**
+ * Compare two expressions to see if they are equal
+ * @param other the other expression
+ * @return true if the expressions are equivalent
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof SubscriptExpression &&
+ operand.equals(((SubscriptExpression)other).operand) &&
+ subscript == ((SubscriptExpression)other).subscript;
+ }
+
+ public int hashCode() {
+ return operand.hashCode() ^ subscript.hashCode();
+ }
+
+ /**
+ * Get the static cardinality: this implementation is appropriate for [1] and [last()] which will always
+ * return something if the input is non-empty
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+
+
+//#ifdefined STREAM
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ TypeHierarchy th = config.getTypeHierarchy();
+ Expression base = getBaseExpression();
+
+ if (base instanceof AxisExpression &&
+ ((AxisExpression)base).getAxis() == AxisInfo.CHILD &&
+ base.getItemType(th).getPrimitiveType() == Type.ELEMENT) {
+ return new SimplePositionalPattern(
+ (NodeTest)base.getItemType(th),
+ this,
+ Token.FEQ);
+ } else {
+ return super.toStreamingPattern(config, reasonForFailure);
+ }
+
+ }
+
+
+//#endif
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ NumericValue index = (NumericValue)subscript.evaluateItem(context);
+ if (index == null) {
+ return null;
+ }
+ if (index.compareTo(Integer.MAX_VALUE) <= 0 && index.isWholeNumber()) {
+ int intindex = (int)index.longValue();
+ if (intindex < 1) {
+ return null;
+ }
+ Item item;
+ SequenceIterator iter = operand.iterate(context);
+ if (intindex == 1) {
+ item = iter.next();
+ } else if (iter instanceof MemoClosure.ProgressiveIterator) {
+ item = ((MemoClosure.ProgressiveIterator)iter).itemAt(intindex - 1);
+ } else if ((iter.getProperties() & SequenceIterator.GROUNDED) != 0) {
+ GroundedValue value = ((GroundedIterator)iter).materialize();
+ item = value.itemAt(intindex-1);
+ } else {
+ SequenceIterator tail = TailIterator.make(iter, intindex);
+ item = tail.next();
+ tail.close();
+ }
+ return item;
+ } else {
+ // there is no item at the required position
+ return null;
+ }
+ }
+
+
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the TailExpression expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SubscriptExpressionCompiler();
+ }
+//#endif
+
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("subscript");
+ operand.explain(destination);
+ subscript.explain(destination);
+ destination.endElement();
+ }
+
+ /**
+ * <p>The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form.</p>
+ * <p/>
+ * <p>For subclasses of Expression that represent XPath expressions, the result should always be a string that
+ * parses as an XPath 3.0 expression.</p>
+ *
+ * @return a representation of the expression as a string
+ */
+ @Override
+ public String toString() {
+ return ExpressionTool.parenthesize(operand) + "[" + subscript.toString() + "]";
+ }
+}
+
diff --git a/sf/saxon/expr/SubsequenceIterator.java b/sf/saxon/expr/SubsequenceIterator.java
new file mode 100644
index 0000000..4bae04a
--- /dev/null
+++ b/sf/saxon/expr/SubsequenceIterator.java
@@ -0,0 +1,165 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ArrayIterator;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+
+
+/**
+* A SubsequenceIterator selects a subsequence of a sequence
+*/
+
+public class SubsequenceIterator implements SequenceIterator, LastPositionFinder, LookaheadIterator {
+
+ private SequenceIterator base;
+ private int position = 0;
+ private int min;
+ private int max;
+ /*@Nullable*/ private Item nextItem = null;
+ private Item current = null;
+
+ /**
+ * Private Constructor: use the factory method instead!
+ * @param base An iteration of the items to be filtered
+ * @param min The position of the first item to be included (1-based)
+ * @param max The position of the last item to be included (1-based)
+ */
+
+ private SubsequenceIterator(SequenceIterator base, int min, int max) throws XPathException {
+ this.base = base;
+ this.min = min;
+ if (min<1) min=1;
+ this.max = max;
+ if (max<min) {
+ nextItem = null;
+ return;
+ }
+ int i=1;
+ while ( i++ <= min ) {
+ nextItem = base.next();
+ if (nextItem == null) {
+ break;
+ }
+ }
+ current = nextItem;
+ }
+
+ /**
+ * Static factory method. Creates a SubsequenceIterator, unless for example the base Iterator is an
+ * ArrayIterator, in which case it optimizes by creating a new ArrayIterator directly over the
+ * underlying array. This optimization is important when doing recursion over a node-set using
+ * repeated calls of $nodes[position()>1]
+ * @param base An iteration of the items to be filtered
+ * @param min The position of the first item to be included (base 1)
+ * @param max The position of the last item to be included (base 1)
+ * @return an iterator over the requested subsequence
+ */
+
+ public static SequenceIterator make(SequenceIterator base, int min, int max) throws XPathException {
+ if (base instanceof ArrayIterator) {
+ return ((ArrayIterator)base).makeSliceIterator(min, max);
+ } else if (max == Integer.MAX_VALUE) {
+ return TailIterator.make(base, min);
+ } else if ((base.getProperties() & SequenceIterator.GROUNDED) != 0 && min > 4) {
+ GroundedValue value = ((GroundedIterator)base).materialize();
+ value = value.subsequence(min-1, max-min+1);
+ return value.iterate();
+ } else {
+ return new SubsequenceIterator(base, min, max);
+ }
+ }
+
+ /**
+ * Test whether there are any more items available in the sequence
+ */
+
+ public boolean hasNext() {
+ return nextItem != null;
+ }
+
+ /**
+ * Get the next item if there is one
+ */
+
+ public Item next() throws XPathException {
+ if (nextItem == null) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ current = nextItem;
+ position++;
+ if (base.position() < max) {
+ nextItem = base.next();
+ } else {
+ nextItem = null;
+ base.close();
+ }
+ return current;
+ }
+
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Get another iterator to return the same nodes
+ */
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new SubsequenceIterator(base.getAnother(), min, max);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ int p = LOOKAHEAD;
+ p |= (base.getProperties() & LAST_POSITION_FINDER);
+ return p;
+ }
+
+ /**
+ * Get the last position (that is, the number of items in the sequence). This method is
+ * non-destructive: it does not change the state of the iterator.
+ * The result is undefined if the next() method of the iterator has already returned null.
+ * This method must not be called unless the result of getProperties() on the iterator
+ * includes the bit setting {@link #LAST_POSITION_FINDER}
+ */
+
+ public int getLength() throws XPathException {
+ int lastBase = ((LastPositionFinder)base).getLength();
+ int z = Math.min(lastBase, max);
+ return Math.max(z - min + 1, 0);
+ }
+
+}
+
diff --git a/sf/saxon/expr/SuppliedParameterReference.java b/sf/saxon/expr/SuppliedParameterReference.java
new file mode 100644
index 0000000..6cc27d5
--- /dev/null
+++ b/sf/saxon/expr/SuppliedParameterReference.java
@@ -0,0 +1,213 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SuppliedParameterReferenceCompiler;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.StandardErrorListener;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceType;
+
+/**
+ * Supplied parameter reference: this is an internal expression used to refer to
+ * the value of the n'th parameter supplied on a template call or a call to an inline function.
+ * It is used within a type-checking expression designed to check the consistency
+ * of the supplied value with the required type. This type checking is all done
+ * at run-time, because the binding of apply-templates to actual template rules
+ * is entirely dynamic.
+ */
+
+public class SuppliedParameterReference extends Expression {
+
+ int slotNumber;
+ SequenceType type;
+
+ /**
+ * Constructor
+ * @param slot identifies this parameter. The value -1 indicates that the value is to be obtained
+ * from the dynamic stack held in the context object.
+ */
+
+ public SuppliedParameterReference(int slot) {
+ slotNumber = slot;
+ }
+
+ /**
+ * Get the slot number
+ * @return the slot number
+ */
+
+ public int getSlotNumber() {
+ return slotNumber;
+ }
+
+ /**
+ * Set the type of the supplied value if known
+ * @param type of the supplied value
+ */
+
+ public void setSuppliedType(SequenceType type) {
+ this.type = type;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Determine the data type of the expression, if possible.
+ * @return Type.ITEM, because we don't know the type of the supplied value
+ * in advance.
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (type != null) {
+ return type.getPrimaryType();
+ } else {
+ return AnyItemType.getInstance();
+ }
+ }
+
+ /**
+ * Determine the intrinsic dependencies of an expression, that is, those which are not derived
+ * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
+ * on the context position, while (position()+1) does not. The default implementation
+ * of the method returns 0, indicating "no dependencies".
+ * @return a set of bit-significant flags identifying the "intrinsic"
+ * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_LOCAL_VARIABLES;
+ }
+
+ /**
+ * Get the static cardinality
+ * @return ZERO_OR_MORE, unless we know the type of the supplied value
+ * in advance.
+ */
+
+ public int computeCardinality() {
+ if (type != null) {
+ return type.getCardinality();
+ } else {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new SuppliedParameterReference(slotNumber);
+ }
+
+ /**
+ * Get the value of this expression in a given context.
+ * @param c the XPathContext which contains the relevant variable bindings
+ * @return the value of the variable, if it is defined
+ * @throws XPathException if the variable is undefined
+ */
+
+ public Sequence evaluateVariable(XPathContext c) throws XPathException {
+ if (slotNumber == -1) {
+ return c.getStackFrame().popDynamicValue();
+ }
+ try {
+ return c.evaluateLocalVariable(slotNumber);
+ } catch (AssertionError e) {
+ StandardErrorListener.printStackTrace(c.getConfiguration().getStandardErrorOutput(), c);
+ throw new AssertionError(e.getMessage() + ". No value has been set for parameter " + slotNumber);
+ }
+ }
+
+ /**
+ * Get the value of this expression in a given context.
+ * @param context the XPathContext which contains the relevant variable bindings
+ * @return the value of the variable, if it is defined
+ * @throws XPathException if the variable is undefined
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ return evaluateVariable(context).iterate();
+ }
+
+ /**
+ * Evaluate an expression as a single item. This always returns either a single Item or
+ * null (denoting the empty sequence). No conversion is done. This method should not be
+ * used unless the static type of the expression is a subtype of "item" or "item?": that is,
+ * it should not be called if the expression may return a sequence. There is no guarantee that
+ * this condition will be detected.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @exception net.sf.saxon.trans.XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @return the node or atomic value that results from evaluating the
+ * expression; or null to indicate that the result is an empty
+ * sequence
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ return evaluateVariable(context).head();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the SuppliedParameterReference expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SuppliedParameterReferenceCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("suppliedParam");
+ destination.emitAttribute("slot", slotNumber+"");
+ destination.endElement();
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ * @return a representation of the expression as a string
+ */
+
+ public String toString() {
+ return "suppliedParam(" + slotNumber + ")";
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/TailCallLoop.java b/sf/saxon/expr/TailCallLoop.java
new file mode 100644
index 0000000..004ca5f
--- /dev/null
+++ b/sf/saxon/expr/TailCallLoop.java
@@ -0,0 +1,199 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.TailCallLoopCompiler;
+import net.sf.saxon.expr.instruct.UserFunction;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceExtent;
+
+/**
+* A TailCallLoop wraps the body of a function that contains tail-recursive function calls. On completion
+ * of the "real" body of the function it tests whether the function has executed a tail call, and if so,
+ * iterates to evaluate the tail call.
+*/
+
+public final class TailCallLoop extends UnaryExpression {
+
+ UserFunction containingFunction;
+
+ /**
+ * Constructor - create a TailCallLoop
+ * @param function the function in which this tail call loop appears
+ */
+
+ public TailCallLoop(UserFunction function) {
+ super(function.getBody());
+ containingFunction = function;
+ }
+
+ /**
+ * Get the containing function
+ * @return the containing function
+ */
+
+ public UserFunction getContainingFunction() {
+ return containingFunction;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ return this;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided. This implementation provides both iterate() and
+ * process() methods natively.
+ */
+
+ public int getImplementationMethod() {
+ return operand.getImplementationMethod();
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ throw new UnsupportedOperationException("TailCallLoop.copy()");
+ }
+
+ /**
+ * Iterate over the sequence of values
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ final XPathContextMajor cm = (XPathContextMajor)context;
+ while (true) {
+ SequenceIterator iter = operand.iterate(cm);
+ Sequence extent = SequenceExtent.makeSequenceExtent(iter);
+ UserFunction fn = cm.getTailCallFunction();
+ if (fn == null) {
+ return extent.iterate();
+ }
+ if (fn != containingFunction) {
+ return tailCallDifferentFunction(fn, cm).iterate();
+ }
+ // otherwise, loop round to execute the tail call
+ }
+ }
+
+ /**
+ * Evaluate as an Item.
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ final XPathContextMajor cm = (XPathContextMajor)context;
+ while (true) {
+ Item item = operand.evaluateItem(context);
+ UserFunction fn = cm.getTailCallFunction();
+ if (fn == null) {
+ return item;
+ }
+ if (fn != containingFunction) {
+ return tailCallDifferentFunction(fn, cm).head();
+ }
+ // otherwise, loop round to execute the tail call
+ }
+ }
+
+ /**
+ * Process the function body
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ final XPathContextMajor cm = (XPathContextMajor)context;
+ while (true) {
+ operand.process(context);
+ UserFunction fn = cm.getTailCallFunction();
+ if (fn == null) {
+ return;
+ }
+ if (fn != containingFunction) {
+ SequenceTool.process(tailCallDifferentFunction(fn, cm), cm, operand.getLocationId());
+ return;
+ }
+ // otherwise, loop round to execute the tail call
+ }
+ }
+
+ /**
+ * Make a tail call on a different function. This reuses the context object and the stack frame array
+ * where possible, but it does consume some Java stack space. It's still worth it, because we don't use
+ * as much stack as we would if we didn't return down to the TailCallLoop level.
+ * @param fn the function to be called
+ * @param cm the dynamic context
+ * @return the result of calling the other function
+ * @throws XPathException if the called function fails
+ */
+
+ /*@Nullable*/ private Sequence tailCallDifferentFunction(UserFunction fn, XPathContextMajor cm) throws XPathException {
+ cm.resetStackFrameMap(fn.getStackFrameMap(), fn.getNumberOfArguments());
+ try {
+ return ExpressionTool.evaluate(fn.getBody(), fn.getEvaluationMode(), cm, 1);
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ err.maybeSetContext(cm);
+ throw err;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the TailCallLoop expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new TailCallLoopCompiler();
+ }
+//#endif
+
+
+ /**
+ * Determine the data type of the items returned by the expression
+ * @param th The type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return operand.getItemType(th);
+ }
+
+ /**
+ * Give a string representation of the expression name for use in diagnostics
+ * @return the expression name, as a string
+ */
+
+ public String getExpressionName() {
+ return "tailCallLoop";
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/TailExpression.java b/sf/saxon/expr/TailExpression.java
new file mode 100644
index 0000000..fef7b81
--- /dev/null
+++ b/sf/saxon/expr/TailExpression.java
@@ -0,0 +1,244 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.TailExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.pattern.SimplePositionalPattern;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A TailExpression represents a FilterExpression of the form EXPR[position() > n]
+ * Here n is usually 2, but we allow other values
+ */
+public class TailExpression extends Expression {
+
+ /*@Nullable*/ Expression base;
+ int start; // 1-based offset of first item from base expression
+ // to be included
+
+ /**
+ * Construct a TailExpression, representing a filter expression of the form
+ * $base[position() >= $start]
+ * @param base the expression to be filtered
+ * @param start the position (1-based) of the first item to be included
+ */
+
+ public TailExpression(Expression base, int start) {
+ this.base = base;
+ this.start = start;
+ adoptChildExpression(base);
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ base = visitor.typeCheck(base, contextItemType);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ base = visitor.optimize(base, contextItemType);
+ if (base instanceof Literal) {
+ GroundedValue value =
+ SequenceExtent.makeSequenceExtent(iterate(visitor.getStaticContext().makeEarlyEvaluationContext()));
+ return Literal.makeLiteral(value);
+ }
+ return this;
+ }
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ if (offer.action != PromotionOffer.UNORDERED) {
+ base = doPromotion(base, offer);
+ }
+ return this;
+ }
+ }
+
+ public int computeSpecialProperties() {
+ return base.getSpecialProperties();
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new TailExpression(base.copy(), start);
+ }
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return base.getItemType(th);
+ }
+
+ public int computeCardinality() {
+ return base.getCardinality() | StaticProperty.ALLOWS_ZERO;
+ }
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator(base);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (base == original) {
+ base = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Get the base expression (of which this expression returns the tail part of the value)
+ * @return the base expression
+ */
+
+ public Expression getBaseExpression() {
+ return base;
+ }
+
+ /**
+ * Get the start offset
+ * @return the one-based start offset (returns 2 if all but the first item is being selected)
+ */
+
+ public int getStart() {
+ return start;
+ }
+
+ /**
+ * Compare two expressions to see if they are equal
+ * @param other the other expression
+ * @return true if the expressions are equivalent
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof TailExpression &&
+ base.equals(((TailExpression)other).base) &&
+ start == ((TailExpression)other).start;
+ }
+
+ public int hashCode() {
+ return base.hashCode();
+ }
+
+//#ifdefined STREAM
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ TypeHierarchy th = config.getTypeHierarchy();
+ Expression base = getBaseExpression();
+
+ if (base instanceof AxisExpression &&
+ ((AxisExpression)base).getAxis() == AxisInfo.CHILD &&
+ base.getItemType(th).getPrimitiveType() == Type.ELEMENT) {
+ return new SimplePositionalPattern(
+ (NodeTest)base.getItemType(th),
+ Literal.makeLiteral(Int64Value.makeIntegerValue(getStart())),
+ Token.FGE);
+ } else {
+ return super.toStreamingPattern(config, reasonForFailure);
+ }
+ }
+
+//#endif
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ SequenceIterator baseIter = base.iterate(context);
+ return TailIterator.make(baseIter, start);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the TailExpression expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new TailExpressionCompiler();
+ }
+//#endif
+
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("tail");
+ destination.emitAttribute("start", start+"");
+ base.explain(destination);
+ destination.endElement();
+ }
+
+ /**
+ * <p>The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form.</p>
+ * <p/>
+ * <p>For subclasses of Expression that represent XPath expressions, the result should always be a string that
+ * parses as an XPath 3.0 expression.</p>
+ *
+ * @return a representation of the expression as a string
+ */
+ @Override
+ public String toString() {
+ if (start == 2) {
+ return "tail(" + base.toString() + ")";
+ } else {
+ return ExpressionTool.parenthesize(base) + "[position() ge " + start + "]";
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/TailIterator.java b/sf/saxon/expr/TailIterator.java
new file mode 100644
index 0000000..d80ad1f
--- /dev/null
+++ b/sf/saxon/expr/TailIterator.java
@@ -0,0 +1,131 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ArrayIterator;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+ * TailIterator iterates over a base sequence starting at an element other than the first.
+ * The base sequence is represented by an iterator which is consumed in the process
+ */
+
+public class TailIterator<T extends Item>
+ implements SequenceIterator<T>, LastPositionFinder<T>, LookaheadIterator<T> {
+
+ private SequenceIterator<T> base;
+ private int start;
+
+ /**
+ * Private constructor: external callers should use the public factory method.
+ * Create a TailIterator, an iterator that starts at position N in a sequence and iterates
+ * to the end of the sequence
+ * @param base the base sequence of which we want to select the tail. Unusually, this iterator
+ * should be supplied pre-positioned so that the next call on next() returns the first item to
+ * be returned by the TailIterator
+ * @param start the index of the first required item in the sequence, starting from one. To
+ * include all items in the sequence except the first, set start = 2. This value is used only
+ * when cloning the iterator or when calculating the value of last().
+ */
+
+ private TailIterator(SequenceIterator<T> base, int start) {
+ this.base = base;
+ this.start = start;
+ }
+
+ /**
+ * Static factory method. Creates a TailIterator, unless the base Iterator is an
+ * ArrayIterator, in which case it optimizes by creating a new ArrayIterator directly over the
+ * underlying array. This optimization is important when doing recursion over a node-set using
+ * repeated calls of $nodes[position()>1]
+ * @param base An iteration of the items to be filtered
+ * @param start The position of the first item to be included (base 1). If <= 1, the whole of the
+ * base sequence is returned
+ * @return an iterator over the items in the sequence from the start item to the end of the sequence.
+ * The returned iterator will not necessarily be an instance of this class.
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ public static <T extends Item> SequenceIterator<T> make(SequenceIterator<T> base, int start) throws XPathException {
+ if (start <= 1) {
+ return base;
+ } else if (base instanceof ArrayIterator) {
+ return ((ArrayIterator<T>)base).makeSliceIterator(start, Integer.MAX_VALUE);
+ } else if ((base.getProperties() & SequenceIterator.GROUNDED) != 0) {
+ GroundedValue value = ((GroundedIterator<T>)base).materialize();
+ if (value == EmptySequence.getInstance()) {
+ return EmptyIterator.emptyIterator();
+ } else {
+ return new ValueTailIterator<T>(value, start-1);
+ }
+ } else {
+ // discard the first n-1 items from the underlying iterator
+ for (int i=0; i < start-1; i++) {
+ Item b = base.next();
+ if (b == null) {
+ return EmptyIterator.emptyIterator();
+ }
+ }
+ return new TailIterator<T>(base, start);
+ }
+ }
+
+
+ public T next() throws XPathException {
+ return base.next();
+ }
+
+ public T current() {
+ return base.current();
+ }
+
+ public int position() {
+ int bp = base.position();
+ return (bp > 0 ? (base.position() - start + 1) : bp);
+ }
+
+ public boolean hasNext() {
+ return ((LookaheadIterator)base).hasNext();
+ }
+
+ public int getLength() throws XPathException {
+ int bl = ((LastPositionFinder)base).getLength() - start + 1;
+ return (bl > 0 ? bl : 0);
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<T> getAnother() throws XPathException {
+ return make(base.getAnother(), start);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return base.getProperties() & (LAST_POSITION_FINDER | LOOKAHEAD);
+ }
+}
+
diff --git a/sf/saxon/expr/TreatExpression.java b/sf/saxon/expr/TreatExpression.java
new file mode 100644
index 0000000..d7ce45e
--- /dev/null
+++ b/sf/saxon/expr/TreatExpression.java
@@ -0,0 +1,51 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.value.SequenceType;
+
+/**
+* Treat Expression: implements "treat as data-type ( expression )". This is a factory class only.
+*/
+
+public abstract class TreatExpression {
+
+ /**
+ * This class is never instantiated
+ */
+ private TreatExpression() {
+ }
+
+ /**
+ * Make a treat expression with error code XPDY0050
+ * @param sequence the expression whose result is to be checked
+ * @param type the type against which the result is to be checked
+ * @return the expression
+ */
+
+ public static Expression make(Expression sequence, SequenceType type) {
+ return make(sequence, type, "XPDY0050");
+ }
+
+ /**
+ * Make a treat expression with a non-standard error code
+ * @param sequence the expression whose result is to be checked
+ * @param type the type against which the result is to be checked
+ * @param errorCode the error code to be returned if the check fails
+ * @return the expression
+ */
+
+ public static Expression make(Expression sequence, SequenceType type, String errorCode) {
+ RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP, "treat as", 0);
+ role.setErrorCode(errorCode);
+ Expression e = CardinalityChecker.makeCardinalityChecker(sequence, type.getCardinality(), role);
+ return new ItemChecker(e, type.getPrimaryType(), role);
+ }
+
+}
+
diff --git a/sf/saxon/expr/UnaryExpression.java b/sf/saxon/expr/UnaryExpression.java
new file mode 100644
index 0000000..2127048
--- /dev/null
+++ b/sf/saxon/expr/UnaryExpression.java
@@ -0,0 +1,242 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.Iterator;
+
+/**
+* Unary Expression: an expression taking a single operand expression
+*/
+
+public abstract class UnaryExpression extends Expression {
+
+ /*@NotNull*/ protected Expression operand;
+
+ public UnaryExpression() {}
+
+ public UnaryExpression(/*@NotNull*/ Expression p0) {
+ operand = p0;
+ adoptChildExpression(p0);
+ }
+
+ /*@NotNull*/
+ public Expression getBaseExpression() {
+ return operand;
+ }
+
+ /**
+ * Simplify an expression
+ * @return the simplified expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ operand = visitor.simplify(operand);
+ return this;
+ }
+
+ /**
+ * Type-check the expression. Default implementation for unary operators that accept
+ * any kind of operand
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.typeCheck(operand, contextItemType);
+ // if the operand value is known, pre-evaluate the expression
+ try {
+ if (operand instanceof Literal) {
+ return Literal.makeLiteral(
+ SequenceExtent.makeSequenceExtent(
+ iterate(visitor.getStaticContext().makeEarlyEvaluationContext())));
+ }
+ //return (Value)ExpressionTool.eagerEvaluate(this, env.makeEarlyEvaluationContext());
+ } catch (XPathException err) {
+ // if early evaluation fails, suppress the error: the value might
+ // not be needed at run-time
+ }
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.optimize(operand, contextItemType);
+ // if the operand value is known, pre-evaluate the expression
+ try {
+ if (operand instanceof Literal) {
+ return Literal.makeLiteral(
+ SequenceExtent.makeSequenceExtent(
+ iterate(visitor.getStaticContext().makeEarlyEvaluationContext())));
+ }
+ } catch (XPathException err) {
+ // if early evaluation fails, suppress the error: the value might
+ // not be needed at run-time
+ }
+ return this;
+ }
+
+
+ /**
+ * Promote this expression if possible
+ */
+
+ /*@Nullable*/
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ operand = doPromotion(operand, offer);
+ return this;
+ }
+ }
+
+ /**
+ * Get the immediate subexpressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(operand);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (operand == original) {
+ operand = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ return operand.getSpecialProperties();
+ }
+
+ /**
+ * Determine the static cardinality. Default implementation returns the cardinality of the operand
+ */
+
+ public int computeCardinality() {
+ return operand.getCardinality();
+ }
+
+ /**
+ * Determine the data type of the expression, if possible. The default
+ * implementation for unary expressions returns the item type of the operand
+ * @return the item type of the items in the result sequence, insofar as this
+ * is known statically.
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return operand.getItemType(th);
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(/*@Nullable*/ Object other) {
+ return other != null && this.getClass().equals(other.getClass()) &&
+ this.operand.equals(((UnaryExpression)other).operand);
+ }
+
+ /**
+ * get HashCode for comparing two expressions. Note that this hashcode gives the same
+ * result for (A op B) and for (B op A), whether or not the operator is commutative.
+ */
+
+ public int hashCode() {
+ return ("UnaryExpression " + getClass()).hashCode() ^ operand.hashCode();
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ return getExpressionName() + "(" + operand.toString() + ")";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ String name = getExpressionName();
+ if (name == null) {
+ out.startElement("unaryOperator");
+ String op = displayOperator(out.getConfiguration());
+ if (op != null) {
+ out.emitAttribute("op", op);
+ }
+ } else {
+ out.startElement(name);
+ }
+ operand.explain(out);
+ out.endElement();
+ }
+
+ /**
+ * Give a string representation of the operator for use in diagnostics
+ * @return the operator, as a string
+ * @param config the Saxon configuration
+ */
+
+ /*@Nullable*/ protected String displayOperator(Configuration config) {
+ return null;
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/UnionEnumeration.java b/sf/saxon/expr/UnionEnumeration.java
new file mode 100644
index 0000000..d397c88
--- /dev/null
+++ b/sf/saxon/expr/UnionEnumeration.java
@@ -0,0 +1,141 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.sort.ItemOrderComparer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+
+/**
+* An enumeration representing a nodeset that is a union of two other NodeSets.
+*/
+
+public class UnionEnumeration implements SequenceIterator, LookaheadIterator {
+
+ private SequenceIterator e1;
+ private SequenceIterator e2;
+ /*@Nullable*/ private NodeInfo nextNode1 = null;
+ private NodeInfo nextNode2 = null;
+ private ItemOrderComparer comparer;
+ private NodeInfo current = null;
+ private int position = 0;
+
+ /**
+ * Create the iterator. The two input iterators must return nodes in document
+ * order for this to work.
+ * @param p1 iterator over the first operand sequence (in document order)
+ * @param p2 iterator over the second operand sequence
+ * @param comparer used to test whether nodes are in document order. Different versions
+ * are used for intra-document and cross-document operations
+ */
+
+ public UnionEnumeration(SequenceIterator p1, SequenceIterator p2,
+ ItemOrderComparer comparer) throws XPathException {
+ this.e1 = p1;
+ this.e2 = p2;
+ this.comparer = comparer;
+
+ nextNode1 = next(e1);
+ nextNode2 = next(e2);
+ }
+
+ /**
+ * Get the next item from one of the input sequences,
+ * checking that it is a node.
+ * @param iter the sequence from which a node is to be read
+ * @return the node that was read
+ */
+
+ private NodeInfo next(SequenceIterator iter) throws XPathException {
+ return (NodeInfo)iter.next();
+ // we rely on the type-checking mechanism to prevent a ClassCastException here
+ }
+
+ public boolean hasNext() {
+ return nextNode1!=null || nextNode2!=null;
+ }
+
+ public Item next() throws XPathException {
+
+ // main merge loop: take a value from whichever set has the lower value
+
+ position++;
+ if (nextNode1 != null && nextNode2 != null) {
+ int c = comparer.compare(nextNode1, nextNode2);
+ if (c<0) {
+ current = nextNode1;
+ nextNode1 = next(e1);
+ return current;
+
+ } else if (c>0) {
+ current = nextNode2;
+ nextNode2 = next(e2);
+ return current;
+
+ } else {
+ current = nextNode2;
+ nextNode2 = next(e2);
+ nextNode1 = next(e1);
+ return current;
+ }
+ }
+
+ // collect the remaining nodes from whichever set has a residue
+
+ if (nextNode1!=null) {
+ current = nextNode1;
+ nextNode1 = next(e1);
+ return current;
+ }
+ if (nextNode2!=null) {
+ current = nextNode2;
+ nextNode2 = next(e2);
+ return current;
+ }
+ current = null;
+ position = -1;
+ return null;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ e1.close();
+ e2.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new UnionEnumeration(e1.getAnother(), e2.getAnother(), comparer);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+
+}
+
diff --git a/sf/saxon/expr/UntypedSequenceConverter.java b/sf/saxon/expr/UntypedSequenceConverter.java
new file mode 100644
index 0000000..1ae22ae
--- /dev/null
+++ b/sf/saxon/expr/UntypedSequenceConverter.java
@@ -0,0 +1,236 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+/**
+ * An UntypedSequenceConverter is an expression that performs a cast on each member of
+ * a supplied sequence that is an untypedAtomic value, while leaving other items unchanged
+ */
+
+public final class UntypedSequenceConverter extends AtomicSequenceConverter {
+
+ /**
+ * Constructor
+ *
+ * @param sequence this must be a sequence of atomic values. This is not checked; a ClassCastException
+ * will occur if the precondition is not satisfied.
+ * @param requiredItemType the item type to which all items in the sequence should be converted,
+ * using the rules for "cast as".
+ */
+
+ public UntypedSequenceConverter(Expression sequence, PlainType requiredItemType) {
+ super(sequence, requiredItemType);
+ }
+
+ /**
+ * Create an AtomicSequenceConverter that converts all untypedAtomic values in the input sequence to
+ * a specified target type, while leaving items other than untypedAtomic unchanged
+ * @param config the Saxon configuration
+ * @param operand the expression that delivers the input sequence
+ * @param requiredItemType the type to which untypedAtomic values should be cast, which must either be an
+ * atomic type or a "plain" union type
+ * @return an AtomicSequenceConverter that performs the required conversion
+ * @throws net.sf.saxon.trans.XPathException if an error occurs, for example if the target type is namespace-sensitive
+ */
+
+
+ public static UntypedSequenceConverter makeUntypedSequenceConverter(Configuration config, Expression operand, PlainType requiredItemType)
+ throws XPathException {
+ TypeHierarchy th = config.getTypeHierarchy();
+ UntypedSequenceConverter atomicSeqConverter =
+ new UntypedSequenceConverter(operand, requiredItemType);
+ final ConversionRules rules = config.getConversionRules();
+ final Converter untypedConverter;
+ if (((SimpleType)requiredItemType).isNamespaceSensitive()) {
+ throw new XPathException("Cannot convert untyped atomic values to a namespace-sensitive type", "XPTY0117");
+ }
+ if (requiredItemType.isAtomicType()) {
+ untypedConverter = rules.getConverter(BuiltInAtomicType.UNTYPED_ATOMIC, (AtomicType)requiredItemType);
+ } else {
+ untypedConverter = new StringConverter.StringToUnionConverter(requiredItemType, rules);
+ }
+ // source type not known statically; create a converter that decides at run-time
+ Converter converter = new UntypedConverter(rules, untypedConverter);
+ atomicSeqConverter.setConverter(converter);
+ return atomicSeqConverter;
+ }
+
+ /**
+ * A Converter that converts untyped atomic values to the required type, while
+ * leaving other values unchanged
+ */
+
+ public static class UntypedConverter extends Converter {
+ Converter untypedConverter = null;
+
+ /**
+ * Create an UntypedConverter
+ * @param rules the conversion rules
+ * @param converter the converter to be used in the case where the supplied
+ * value is untypedAtomic
+ */
+
+ public UntypedConverter(ConversionRules rules, Converter converter) {
+ super(rules);
+ untypedConverter = converter;
+ //untypedConverter.setConversionRules(rules);
+
+ }
+
+ /*@NotNull*/
+ @Override
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ if (input instanceof UntypedAtomicValue) {
+ return untypedConverter.convert(input);
+ } else {
+ return input;
+ }
+ }
+ }
+
+ public static UntypedSequenceConverter makeUntypedSequenceRejector(Configuration config, final Expression operand, final PlainType requiredItemType) {
+ UntypedSequenceConverter atomicSeqConverter = new UntypedSequenceConverter(operand, requiredItemType);
+ final ConversionRules rules = config.getConversionRules();
+ final Converter untypedConverter = new Converter() {
+ // called when an untyped atomic value is encountered
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ ValidationFailure vf = new ValidationFailure(
+ "Implicit conversion of untypedAtomic value to " + requiredItemType.toString() + " is not allowed");
+ vf.setErrorCode("XPTY0117");
+ vf.setLocator(operand);
+ return vf;
+ }
+ };
+
+ // source type not known statically; create a converter that decides at run-time
+ Converter converter = new UntypedConverter(rules, untypedConverter);
+ atomicSeqConverter.setConverter(converter);
+ return atomicSeqConverter;
+ }
+
+ @Override
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e2 = super.typeCheck(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (th.relationship(operand.getItemType(th), BuiltInAtomicType.UNTYPED_ATOMIC) == TypeHierarchy.DISJOINT) {
+ // operand cannot return untyped atomic values, so there's nothing to convert
+ return operand;
+ }
+ return this;
+ }
+
+ /**
+ * Determine the special properties of this expression
+ *
+ * @return {@link net.sf.saxon.expr.StaticProperty#NON_CREATIVE}.
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p | StaticProperty.NON_CREATIVE | StaticProperty.NOT_UNTYPED_ATOMIC;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ UntypedSequenceConverter atomicConverter = new UntypedSequenceConverter(getBaseExpression().copy(), getRequiredItemType());
+ atomicConverter.setConverter(converter);
+ return atomicConverter;
+ }
+
+
+ /**
+ * Determine the data type of the items returned by the expression, if possible
+ *
+ * @param th the type hierarchy cache
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
+ * or Type.ITEM (meaning not known in advance)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (operand.getItemType(th) == BuiltInAtomicType.UNTYPED_ATOMIC) {
+ return getRequiredItemType();
+ } else {
+ return Type.getCommonSuperType(getRequiredItemType(), operand.getItemType(th), th);
+ }
+ }
+
+ /**
+ * Determine the static cardinality of the expression
+ */
+
+ public int computeCardinality() {
+ return operand.getCardinality();
+ }
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof UntypedSequenceConverter && super.equals(other);
+ }
+
+ /**
+ * get HashCode for comparing two expressions.
+ */
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ protected String displayOperator(Configuration config) {
+ return "convertUntyped";
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ *
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+ @Override
+ public String getExpressionName() {
+ return "convertUntyped";
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+ destination.startElement("convertUntyped");
+ destination.emitAttribute("to", getRequiredItemType().toString());
+ operand.explain(destination);
+ destination.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/UserFunctionCall.java b/sf/saxon/expr/UserFunctionCall.java
new file mode 100644
index 0000000..d3e574a
--- /dev/null
+++ b/sf/saxon/expr/UserFunctionCall.java
@@ -0,0 +1,648 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.UserFunctionCallCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.evpull.EmptyEventIterator;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.UserFunction;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * This class represents a call to a user-defined function in the stylesheet or query.
+ */
+
+public class UserFunctionCall extends FunctionCall implements UserFunctionReference {
+
+ private SequenceType staticType;
+ private UserFunction function;
+ private int tailCall = NOT_TAIL_CALL;
+
+ /*@Nullable*/ private int[] argumentEvaluationModes = null;
+
+ public static final int NOT_TAIL_CALL = 0;
+ public static final int FOREIGN_TAIL_CALL = 1;
+ public static final int SELF_TAIL_CALL = 2;
+
+ /**
+ * Create a function call to a user-written function in a query or stylesheet
+ */
+
+ public UserFunctionCall() {
+ }
+
+ /**
+ * Set the static type
+ *
+ * @param type the static type of the result of the function call
+ */
+
+ public void setStaticType(SequenceType type) {
+ staticType = type;
+ }
+
+ /**
+ * Create the reference to the function to be called
+ *
+ * @param compiledFunction the function being called
+ */
+
+ public void setFunction(UserFunction compiledFunction) {
+ function = compiledFunction;
+ }
+
+ /**
+ * Check the function call against the declared function signature
+ *
+ * @param compiledFunction the function being called
+ * @param visitor an expression visitor
+ */
+
+ public void checkFunctionCall(UserFunction compiledFunction,
+ ExpressionVisitor visitor) throws XPathException {
+ Executable executable = visitor.getExecutable();
+ boolean isXSLT = executable != null && executable.getHostLanguage() == Configuration.XSLT;
+ int n = compiledFunction.getNumberOfArguments();
+ for (int i = 0; i < n; i++) {
+ Object name = compiledFunction.getFunctionName();
+ if (name == null) {
+ name = "";
+ }
+ RoleLocator role = new RoleLocator(
+ RoleLocator.FUNCTION, name, i);
+ if (isXSLT) {
+ role.setErrorCode("XTTE0790");
+ }
+ //role.setSourceLocator(this);
+ argument[i] = TypeChecker.staticTypeCheck(
+ argument[i],
+ compiledFunction.getArgumentType(i),
+ false,
+ role, visitor);
+ }
+ }
+
+
+ /**
+ * Get the function that is being called by this function call
+ *
+ * @return the function being called
+ */
+
+ public UserFunction getFunction() {
+ return function;
+ }
+
+ /**
+ * Determine whether this is a tail call (not necessarily a recursive tail call)
+ *
+ * @return true if this function call is a tail call
+ */
+
+ public boolean isTailCall() {
+ return tailCall != NOT_TAIL_CALL;
+ }
+
+ public boolean isRecursiveTailCall() {
+ return tailCall == SELF_TAIL_CALL;
+ }
+
+ /**
+ * Method called during the type checking phase
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ // these checks are now done in setFunction(), at the time when the function
+ // call is bound to an actual function
+ }
+
+ /**
+ * Get the qualified of the function being called
+ *
+ * @return the qualified name
+ */
+
+ public final StructuredQName getFunctionName() {
+ StructuredQName n = super.getFunctionName();
+ if (n == null) {
+ return function.getFunctionName();
+ } else {
+ return n;
+ }
+ }
+
+ /**
+ * Get the evaluation modes that have been determined for each of the arguments
+ *
+ * @return an array of integers representing the evaluation modes, one for each argument
+ */
+
+ public int[] getArgumentEvaluationModes() {
+ return argumentEvaluationModes;
+ }
+
+ /**
+ * Pre-evaluate a function at compile time. This version of the method suppresses
+ * early evaluation by doing nothing.
+ *
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Determine the data type of the expression, if possible
+ *
+ * @param th the type hierarchy cache
+ * @return Type.ITEM (meaning not known in advance)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (staticType == null) {
+ // the actual type is not known yet, so we return an approximation
+ return AnyItemType.getInstance();
+ } else {
+ return staticType.getPrimaryType();
+ }
+ }
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_USER_FUNCTIONS;
+ }
+
+ /**
+ * Determine whether this is an updating expression as defined in the XQuery update specification
+ *
+ * @return true if this is an updating expression
+ */
+
+ public boolean isUpdatingExpression() {
+ return function.isUpdating();
+ }
+
+ /**
+ * Compute the special properties of this expression. These properties are denoted by a bit-significant
+ * integer, possible values are in class {@link net.sf.saxon.expr.StaticProperty}. The "special" properties are properties
+ * other than cardinality and dependencies, and most of them relate to properties of node sequences, for
+ * example whether the nodes are in document order.
+ *
+ * @return the special properties, as a bit-significant integer
+ */
+
+ protected int computeSpecialProperties() {
+ // Inherit the properties of the function being called if possible. But we have to prevent
+ // looping when the function is recursive. For safety, we only consider the properties of the
+ // function body if it contains no further function calls.
+ if (function == null) {
+ return super.computeSpecialProperties();
+ } else {
+ List calledFunctions = new ArrayList();
+ ExpressionTool.gatherCalledFunctions(function.getBody(), calledFunctions);
+ if (calledFunctions.isEmpty()) {
+ return function.getBody().computeSpecialProperties();
+ } else {
+ return super.computeSpecialProperties();
+ }
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ if (function == null) {
+ // not bound yet, we have no way to register the new copy with the XSLFunction
+ throw new UnsupportedOperationException("UserFunctionCall.copy()");
+ }
+ UserFunctionCall ufc = new UserFunctionCall();
+ ufc.setFunction(function);
+ ufc.setStaticType(staticType);
+ Expression[] a2 = new Expression[argument.length];
+ for (int i = 0; i < argument.length; i++) {
+ a2[i] = argument[i].copy();
+ }
+ ufc.argument = a2;
+ if (argumentEvaluationModes != null) {
+ int[] am2 = new int[argumentEvaluationModes.length];
+ System.arraycopy(argumentEvaluationModes, 0, am2, 0, am2.length);
+ ufc.argumentEvaluationModes = am2;
+ }
+ return ufc;
+ }
+
+ /**
+ * Determine the cardinality of the result
+ */
+
+ public int computeCardinality() {
+ if (staticType == null) {
+ // the actual type is not known yet, so we return an approximation
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ } else {
+ return staticType.getCardinality();
+ }
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.typeCheck(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+ if (function != null) {
+ if (function.getFunctionName() == null) {
+ // This is an inline function item; add type-checking for the argument types and the result type
+ checkFunctionCall(function, visitor);
+ Expression body = function.getBody();
+ body = body.typeCheck(visitor, null);
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ RoleLocator role = new RoleLocator(RoleLocator.FUNCTION_RESULT, "", 0);
+ body = TypeChecker.staticTypeCheck(body, function.getResultType(th), visitor.getStaticContext().isInBackwardsCompatibleMode(), role, visitor);
+ function.setBody(body);
+ }
+ computeArgumentEvaluationModes();
+ if (staticType == SequenceType.ANY_SEQUENCE) {
+ // try to get a better type
+ staticType = function.getResultType(visitor.getConfiguration().getTypeHierarchy());
+ }
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e == this && function != null) {
+ computeArgumentEvaluationModes();
+ Expression e2 = visitor.getConfiguration().obtainOptimizer().tryInlineFunctionCall(
+ this, visitor, contextItemType);
+ if (e2 != this) {
+ return visitor.optimize(e2, contextItemType);
+ }
+ return e2;
+ }
+ return e;
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ boolean changed = false;
+ if (offer.action != PromotionOffer.UNORDERED) {
+ for (int i = 0; i < argument.length; i++) {
+ Expression a2 = doPromotion(argument[i], offer);
+ changed |= (a2 != argument[i]);
+ argument[i] = a2;
+ }
+ }
+ if (changed && function != null) {
+ computeArgumentEvaluationModes();
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Compute the evaluation mode of each argument
+ */
+
+ public void computeArgumentEvaluationModes() {
+ argumentEvaluationModes = new int[argument.length];
+ for (int i = 0; i < argument.length; i++) {
+ int refs = function.getParameterDefinitions()[i].getReferenceCount();
+ if (refs == 0) {
+ // the argument is never referenced, so don't evaluate it
+ argumentEvaluationModes[i] = ExpressionTool.RETURN_EMPTY_SEQUENCE;
+ } else if (function.getParameterDefinitions()[i].isIndexedVariable()) {
+ argumentEvaluationModes[i] = ExpressionTool.MAKE_INDEXED_VARIABLE;
+ } else if ((argument[i].getDependencies() & StaticProperty.DEPENDS_ON_USER_FUNCTIONS) != 0) {
+ // if the argument contains a call to a user-defined function, then it might be a recursive call.
+ // It's better to evaluate it now, rather than waiting until we are on a new stack frame, as
+ // that can blow the stack if done repeatedly. (See test func42)
+ argumentEvaluationModes[i] = ExpressionTool.eagerEvaluationMode(argument[i]);
+ } else {
+ int m = ExpressionTool.lazyEvaluationMode(argument[i]);
+ if (m == ExpressionTool.MAKE_CLOSURE && refs > 1) {
+ m = ExpressionTool.MAKE_MEMO_CLOSURE;
+ }
+ argumentEvaluationModes[i] = m;
+ }
+ }
+ }
+
+ @Override
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = super.replaceSubExpression(original, replacement);
+ if (found) {
+ computeArgumentEvaluationModes();
+ }
+ return found;
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ return addExternalFunctionCallToPathMap(pathMap, pathMapNodeSet);
+ }
+
+ /**
+ * Mark tail-recursive calls on stylesheet functions. This marks the function call as tailRecursive if
+ * if is a call to the containing function, and in this case it also returns "true" to the caller to indicate
+ * that a tail call was found.
+ */
+
+ public int markTailFunctionCalls(StructuredQName qName, int arity) {
+ tailCall = (getFunctionName().equals(qName) &&
+ arity == getNumberOfArguments() ? SELF_TAIL_CALL : FOREIGN_TAIL_CALL);
+ return tailCall;
+ }
+
+ public int getImplementationMethod() {
+ if (Cardinality.allowsMany(getCardinality())) {
+ return ITERATE_METHOD | PROCESS_METHOD;
+ } else {
+ return EVALUATE_METHOD;
+ }
+ }
+
+ /**
+ * Call the function, returning the value as an item. This method will be used
+ * only when the cardinality is zero or one. If the function is tail recursive,
+ * it returns an Object representing the arguments to the next (recursive) call
+ */
+
+ public Item evaluateItem(XPathContext c) throws XPathException {
+ return callFunction(c).head();
+ }
+
+ /**
+ * Call the function, returning an iterator over the results. (But if the function is
+ * tail recursive, it returns an iterator over the arguments of the recursive call)
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext c) throws XPathException {
+ return callFunction(c).iterate();
+ }
+
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ Sequence[] actualArgs = evaluateArguments(context);
+ XPathContextMajor c2 = context.newCleanContext();
+ c2.setOrigin(this);
+ function.callUpdating(actualArgs, c2, pul);
+ }
+
+ /**
+ * This is the method that actually does the function call
+ *
+ * @param c the dynamic context
+ * @return the result of the function
+ * @throws XPathException if dynamic errors occur
+ */
+ private Sequence callFunction(XPathContext c) throws XPathException {
+ Sequence[] actualArgs = evaluateArguments(c);
+
+ if (isTailCall()) {
+ ((XPathContextMajor) c).requestTailCall(function, actualArgs);
+ return EmptySequence.getInstance();
+ }
+
+ XPathContextMajor c2 = c.newCleanContext();
+ c2.setOrigin(this);
+ c2.setTemporaryOutputState(true);
+ try {
+ return function.call(c2, actualArgs);
+ } catch (StackOverflowError err) {
+ throw new XPathException("Too many nested function calls. May be due to infinite recursion.", this);
+ }
+ }
+
+ /**
+ * Process the function call in push mode
+ *
+ * @param context the XPath dynamic context
+ * @throws XPathException
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ Sequence[] actualArgs = evaluateArguments(context);
+ if (isTailCall()) {
+ ((XPathContextMajor) context).requestTailCall(function, actualArgs);
+ } else {
+ SequenceReceiver out = context.getReceiver();
+ XPathContextMajor c2 = context.newCleanContext();
+ c2.setReceiver(out);
+ c2.setTemporaryOutputState(true);
+ c2.setOrigin(this);
+ function.process(actualArgs, c2);
+ }
+ }
+
+ /**
+ * Process the function call in pull mode
+ *
+ * @param context the XPath dynamic context
+ * @throws XPathException
+ */
+
+ public EventIterator iterateEvents(XPathContext context) throws XPathException {
+ Sequence[] actualArgs = evaluateArguments(context);
+ if (isTailCall()) {
+ ((XPathContextMajor) context).requestTailCall(function, actualArgs);
+ return EmptyEventIterator.getInstance();
+ } else {
+ SequenceReceiver out = context.getReceiver();
+ XPathContextMajor c2 = context.newCleanContext();
+ c2.setReceiver(out);
+ c2.setOrigin(this);
+ return function.iterateEvents(actualArgs, c2);
+ }
+ }
+
+
+ public Sequence[] evaluateArguments(XPathContext c) throws XPathException {
+ int numArgs = argument.length;
+ Sequence[] actualArgs = new Sequence[numArgs];
+ if (argumentEvaluationModes == null) {
+ // should have been done at compile time
+ computeArgumentEvaluationModes();
+ }
+ for (int i = 0; i < numArgs; i++) {
+
+ int refs = function.getParameterDefinitions()[i].getReferenceCount();
+ actualArgs[i] = ExpressionTool.evaluate(argument[i], argumentEvaluationModes[i], c, refs);
+
+ if (actualArgs[i] == null) {
+ actualArgs[i] = EmptySequence.getInstance();
+ }
+ // If the argument has come in as a (non-memo) closure but there are multiple references to it,
+ // then we materialize it in memory now. This shouldn't really happen but it does (tour.xq)
+ if (refs > 1 && actualArgs[i] instanceof Closure) {
+ actualArgs[i] = ((Closure) actualArgs[i]).reduce();
+ }
+ }
+ return actualArgs;
+ }
+
+ /**
+ * Call the function dynamically. For this to be possible, the static arguments of the function call
+ * must have been set up as {@link SuppliedParameterReference} objects. The actual arguments are placed on the
+ * callee's stack, and the type conversion takes place "in situ".
+ *
+ * @param suppliedArguments the values to be used for the arguments of the function
+ * @param context the dynamic evaluation context
+ * @return the result of evaluating the function
+ */
+
+ public Sequence dynamicCall(Sequence[] suppliedArguments, XPathContext context) throws XPathException {
+ Sequence[] convertedArgs = new Sequence[suppliedArguments.length];
+ XPathContextMajor c2 = context.newCleanContext();
+ c2.setOrigin(this);
+ c2.setCaller(context);
+ c2.openStackFrame(suppliedArguments.length);
+ for (int i = 0; i < suppliedArguments.length; i++) {
+ c2.setLocalVariable(i, suppliedArguments[i]);
+ convertedArgs[i] = ExpressionTool.lazyEvaluate(argument[i], c2, 10);
+ }
+ XPathContextMajor c3 = c2.newCleanContext();
+ c3.setOrigin(this);
+ return function.call(c3, convertedArgs);
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("functionCall");
+ if (getFunctionName() != null) {
+ out.emitAttribute("name", getDisplayName());
+ out.emitAttribute("tailCall",
+ ((tailCall == NOT_TAIL_CALL ? "false" : (tailCall == SELF_TAIL_CALL ? "self" : "foreign"))));
+ }
+ for (int a = 0; a < argument.length; a++) {
+ argument[a].explain(out);
+ }
+ if (getFunctionName() == null) {
+ out.startElement("body");
+ function.getBody().explain(out);
+ out.endElement();
+ }
+ out.endElement();
+ }
+
+ public int getConstructType() {
+ return Location.FUNCTION_CALL;
+ }
+
+ public Object getProperty(String name) {
+ if (name.equals("target")) {
+ return function;
+ }
+ return super.getProperty(name);
+ }
+
+ public StructuredQName getObjectName() {
+ return getFunctionName();
+ }
+
+
+ /**
+ * Get the line number within the document or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the line number within the document or module.
+ */
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ /**
+ * Get the URI of the document or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the URI of the document or module.
+ */
+
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the UserFunctionCall expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new UserFunctionCallCompiler();
+ }
+//#endif
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/UserFunctionReference.java b/sf/saxon/expr/UserFunctionReference.java
new file mode 100644
index 0000000..ffb6601
--- /dev/null
+++ b/sf/saxon/expr/UserFunctionReference.java
@@ -0,0 +1,20 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.expr.instruct.UserFunction;
+
+/**
+ * A reference to a function; typically a forwards reference to a user-defined function in XQuery
+ * that has not yet been compiled.
+ */
+public interface UserFunctionReference {
+
+ public void setFunction(UserFunction function);
+}
+
diff --git a/sf/saxon/expr/ValueComparison.java b/sf/saxon/expr/ValueComparison.java
new file mode 100644
index 0000000..de28042
--- /dev/null
+++ b/sf/saxon/expr/ValueComparison.java
@@ -0,0 +1,831 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ValueComparisonCompiler;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.CodepointCollatingComparer;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.functions.*;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+
+/**
+ * ValueComparison: a boolean expression that compares two atomic values
+ * for equals, not-equals, greater-than or less-than. Implements the operators
+ * eq, ne, lt, le, gt, ge
+ */
+
+public final class ValueComparison extends BinaryExpression implements ComparisonExpression, Negatable {
+
+ private AtomicComparer comparer;
+ /*@Nullable*/ private BooleanValue resultWhenEmpty = null;
+ private boolean needsRuntimeCheck;
+
+ /**
+ * Create a comparison expression identifying the two operands and the operator
+ *
+ * @param p1 the left-hand operand
+ * @param op the operator, as a token returned by the Tokenizer (e.g. Token.LT)
+ * @param p2 the right-hand operand
+ */
+
+ public ValueComparison(Expression p1, int op, Expression p2) {
+ super(p1, op, p2);
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "ValueComparison";
+ }
+
+ /**
+ * Deserialization method ensures that there is only one BooleanValue.TRUE and only one BooleanValue.FALSE
+ * @param in the input stream
+ */
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ if (resultWhenEmpty != null) {
+ resultWhenEmpty = (resultWhenEmpty.getBooleanValue() ? BooleanValue.TRUE : BooleanValue.FALSE);
+ }
+ }
+
+ /**
+ * Set the AtomicComparer used to compare atomic values
+ * @param comparer the AtomicComparer
+ */
+
+ public void setAtomicComparer(AtomicComparer comparer) {
+ this.comparer = comparer;
+ }
+
+ /**
+ * Get the AtomicComparer used to compare atomic values. This encapsulates any collation that is used.
+ * Note that the comparer is always known at compile time.
+ */
+
+ public AtomicComparer getAtomicComparer() {
+ return comparer;
+ }
+
+ /**
+ * Get the primitive (singleton) operator used: one of Token.FEQ, Token.FNE, Token.FLT, Token.FGT,
+ * Token.FLE, Token.FGE
+ */
+
+ public int getSingletonOperator() {
+ return operator;
+ }
+
+ /**
+ * Determine whether untyped atomic values should be converted to the type of the other operand
+ *
+ * @return true if untyped values should be converted to the type of the other operand, false if they
+ * should be converted to strings.
+ */
+
+ public boolean convertsUntypedToOther() {
+ return false;
+ }
+
+ /**
+ * Set the result to be returned if one of the operands is an empty sequence
+ * @param value the result to be returned if an operand is empty. Supply null to mean the empty sequence.
+ */
+
+ public void setResultWhenEmpty(BooleanValue value) {
+ resultWhenEmpty = value;
+ }
+
+ /**
+ * Get the result to be returned if one of the operands is an empty sequence
+ * @return BooleanValue.TRUE, BooleanValue.FALSE, or null (meaning the empty sequence)
+ */
+
+ public BooleanValue getResultWhenEmpty() {
+ return resultWhenEmpty;
+ }
+
+ /**
+ * Determine whether a run-time check is needed to check that the types of the arguments
+ * are comparable
+ * @return true if a run-time check is needed
+ */
+
+ public boolean needsRuntimeComparabilityCheck() {
+ return needsRuntimeCheck;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ StaticContext env = visitor.getStaticContext();
+
+ operand0 = visitor.typeCheck(operand0, contextItemType);
+ if (Literal.isEmptySequence(operand0)) {
+ return (resultWhenEmpty == null ? operand0 : Literal.makeLiteral(resultWhenEmpty));
+ }
+
+ operand1 = visitor.typeCheck(operand1, contextItemType);
+ if (Literal.isEmptySequence(operand1)) {
+ return (resultWhenEmpty == null ? operand1 : Literal.makeLiteral(resultWhenEmpty));
+ }
+
+ final SequenceType optionalAtomic = SequenceType.OPTIONAL_ATOMIC;
+
+ RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0);
+ operand0 = TypeChecker.staticTypeCheck(operand0, optionalAtomic, false, role0, visitor);
+
+ RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1);
+ operand1 = TypeChecker.staticTypeCheck(operand1, optionalAtomic, false, role1, visitor);
+
+ PlainType t0 = operand0.getItemType(th).getAtomizedItemType();
+ PlainType t1 = operand1.getItemType(th).getAtomizedItemType();
+
+ if (t0.isExternalType() || t1.isExternalType()) {
+ XPathException err = new XPathException("Cannot perform comparisons involving external objects");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ throw err;
+ }
+
+ BuiltInAtomicType p0 = (BuiltInAtomicType)t0.getPrimitiveItemType();
+ if (p0.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ p0 = BuiltInAtomicType.STRING;
+ }
+ BuiltInAtomicType p1 = (BuiltInAtomicType)t1.getPrimitiveItemType();
+ if (p1.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ p1 = BuiltInAtomicType.STRING;
+ }
+
+ needsRuntimeCheck =
+ p0.equals(BuiltInAtomicType.ANY_ATOMIC) || p1.equals(BuiltInAtomicType.ANY_ATOMIC);
+
+ if (!needsRuntimeCheck && !Type.isPossiblyComparable(p0, p1, Token.isOrderedOperator(operator))) {
+ boolean opt0 = Cardinality.allowsZero(operand0.getCardinality());
+ boolean opt1 = Cardinality.allowsZero(operand1.getCardinality());
+ if (opt0 || opt1) {
+ // This is a comparison such as (xs:integer? eq xs:date?). This is almost
+ // certainly an error, but we need to let it through because it will work if
+ // one of the operands is an empty sequence.
+
+ String which = null;
+ if (opt0) which = "the first operand is";
+ if (opt1) which = "the second operand is";
+ if (opt0 && opt1) which = "one or both operands are";
+
+ visitor.getStaticContext().issueWarning("Comparison of " + t0.toString() +
+ (opt0 ? "?" : "") + " to " + t1.toString() +
+ (opt1 ? "?" : "") + " will fail unless " + which + " empty", this);
+ needsRuntimeCheck = true;
+ } else {
+ XPathException err = new XPathException("Cannot compare " + t0.toString() +
+ " to " + t1.toString());
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ if (!(operator == Token.FEQ || operator == Token.FNE)) {
+ if (!p0.isOrdered(true)) {
+ XPathException err = new XPathException("Type " + t0.toString() + " is not an ordered type");
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ if (!p1.isOrdered(true)) {
+ XPathException err = new XPathException("Type " + t1.toString() + " is not an ordered type");
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ }
+
+ if (comparer == null) {
+ // In XSLT, only do this the first time through, otherwise the default-collation attribute may be missed
+ final String defaultCollationName = env.getDefaultCollationName();
+ StringCollator comp = env.getCollation(defaultCollationName);
+ if (comp == null) {
+ comp = CodepointCollator.getInstance();
+ }
+ comparer = GenericAtomicComparer.makeAtomicComparer(
+ p0, p1, comp, env.getConfiguration().getConversionContext());
+ }
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+
+ operand0 = visitor.optimize(operand0, contextItemType);
+ operand1 = visitor.optimize(operand1, contextItemType);
+
+ Sequence value0 = null;
+ Sequence value1 = null;
+
+ if (operand0 instanceof Literal) {
+ value0 = ((Literal)operand0).getValue();
+ }
+
+ if (operand1 instanceof Literal) {
+ value1 = ((Literal)operand1).getValue();
+ }
+
+ // evaluate the expression now if both arguments are constant
+
+ if ((value0 != null) && (value1 != null)) {
+ try {
+ AtomicValue r = (AtomicValue)evaluateItem(visitor.getStaticContext().makeEarlyEvaluationContext());
+ //noinspection RedundantCast
+ return Literal.makeLiteral(r == null ? (GroundedValue)EmptySequence.getInstance() : (GroundedValue)r);
+ } catch (NoDynamicContextException e) {
+ // early evaluation failed, typically because the implicit context isn't available.
+ // Try again at run-time
+ return this;
+ }
+ }
+
+ // optimise count(x) eq N (or gt N, ne N, eq N, etc)
+
+ if (operand0 instanceof Count && Literal.isAtomic(operand1)) {
+ Expression e2 = optimizeCount(visitor, contextItemType, false);
+ if (e2 != null) {
+ return visitor.optimize(e2, contextItemType);
+ }
+ } else if (operand1 instanceof Count && Literal.isAtomic(operand0)) {
+ Expression e2 = optimizeCount(visitor, contextItemType, true);
+ if (e2 != null) {
+ return visitor.optimize(e2, contextItemType);
+ }
+ }
+
+
+ // optimise string-length(x) = 0, >0, !=0 etc
+
+ if ((operand0 instanceof StringLength) &&
+ (((StringLength) operand0).getNumberOfArguments() == 1) &&
+ isZero(value1)) {
+ Expression arg = (((StringLength)operand0).getArguments()[0]);
+ switch (operator) {
+ case Token.FEQ:
+ case Token.FLE:
+ return SystemFunctionCall.makeSystemFunction("not", new Expression[]{arg});
+ case Token.FNE:
+ case Token.FGT:
+ return SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{arg});
+ case Token.FGE:
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ case Token.FLT:
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+ }
+
+ // optimise (0 = string-length(x)), etc
+
+ if ((operand1 instanceof StringLength) &&
+ (((StringLength) operand1).getNumberOfArguments() == 1) &&
+ isZero(value0)) {
+ Expression arg = (((StringLength)operand1).getArguments()[0]);
+ switch (operator) {
+ case Token.FEQ:
+ case Token.FGE:
+ return SystemFunctionCall.makeSystemFunction("not", new Expression[]{arg});
+ case Token.FNE:
+ case Token.FLT:
+ return SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{arg});
+ case Token.FLE:
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ case Token.FGT:
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+ }
+
+ // optimise string="" etc
+ // Note we can change S!="" to boolean(S) for cardinality zero-or-one, but we can only
+ // change S="" to not(S) for cardinality exactly-one.
+
+ int p0 = operand0.getItemType(th).getPrimitiveType();
+ if ((p0 == StandardNames.XS_STRING ||
+ p0 == StandardNames.XS_ANY_URI ||
+ p0 == StandardNames.XS_UNTYPED_ATOMIC) &&
+ operand1 instanceof Literal &&
+ ((Literal)operand1).getValue() instanceof StringValue &&
+ ((StringValue)((Literal)operand1).getValue()).isZeroLength() &&
+ comparer instanceof CodepointCollatingComparer) {
+
+ switch (operator) {
+ case Token.FNE:
+ case Token.FGT:
+ return SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{operand0});
+ case Token.FEQ:
+ case Token.FLE:
+ if (operand0.getCardinality() == StaticProperty.EXACTLY_ONE) {
+ return SystemFunctionCall.makeSystemFunction("not", new Expression[]{operand0});
+ }
+ }
+ }
+
+ // optimize "" = string etc
+
+ int p1 = operand1.getItemType(th).getPrimitiveType();
+ if ((p1 == StandardNames.XS_STRING ||
+ p1 == StandardNames.XS_ANY_URI ||
+ p1 == StandardNames.XS_UNTYPED_ATOMIC) &&
+ operand0 instanceof Literal &&
+ ((Literal)operand0).getValue() instanceof StringValue &&
+ ((StringValue)((Literal)operand0).getValue()).isZeroLength() &&
+ comparer instanceof CodepointCollatingComparer) {
+
+ switch (operator) {
+ case Token.FNE:
+ case Token.FLT:
+ return SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{operand1});
+ case Token.FEQ:
+ case Token.FGE:
+ if (operand1.getCardinality() == StaticProperty.EXACTLY_ONE) {
+ return SystemFunctionCall.makeSystemFunction("not", new Expression[]{operand1});
+ }
+ }
+ }
+
+
+ // optimise [position()=last()] etc
+
+ if ((operand0 instanceof Position) && (operand1 instanceof Last)) {
+ switch (operator) {
+ case Token.FEQ:
+ case Token.FGE:
+ IsLastExpression iletrue = new IsLastExpression(true);
+ ExpressionTool.copyLocationInfo(this, iletrue);
+ return iletrue;
+ case Token.FNE:
+ case Token.FLT:
+ IsLastExpression ilefalse = new IsLastExpression(false);
+ ExpressionTool.copyLocationInfo(this, ilefalse);
+ return ilefalse;
+ case Token.FGT:
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ case Token.FLE:
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ }
+ }
+ if ((operand0 instanceof Last) && (operand1 instanceof Position)) {
+ switch (operator) {
+ case Token.FEQ:
+ case Token.FLE:
+ IsLastExpression iletrue = new IsLastExpression(true);
+ ExpressionTool.copyLocationInfo(this, iletrue);
+ return iletrue;
+ case Token.FNE:
+ case Token.FGT:
+ IsLastExpression ilefalse = new IsLastExpression(false);
+ ExpressionTool.copyLocationInfo(this, ilefalse);
+ return ilefalse;
+ case Token.FLT:
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ case Token.FGE:
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ }
+ }
+
+ // optimize comparison against an integer constant
+
+ if (value1 instanceof Int64Value &&
+ operand0.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ th.isSubType(operand0.getItemType(th), BuiltInAtomicType.NUMERIC)) {
+ return new CompareToIntegerConstant(operand0, operator, ((Int64Value)value1).longValue());
+ }
+
+ if (value0 instanceof Int64Value &&
+ operand1.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ th.isSubType(operand1.getItemType(th), BuiltInAtomicType.NUMERIC)) {
+ return new CompareToIntegerConstant(operand1, Token.inverse(operator), ((Int64Value) value0).longValue());
+ }
+
+ // optimize (boolean expression) == (boolean literal)
+
+ if (p0 == StandardNames.XS_BOOLEAN &&
+ p1 == StandardNames.XS_BOOLEAN &&
+ (operator == Token.FEQ || operator == Token.FNE) &&
+ operand0.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ operand1.getCardinality() == StaticProperty.EXACTLY_ONE &&
+ (operand0 instanceof Literal || operand1 instanceof Literal)) {
+ Literal literal = (Literal)(operand0 instanceof Literal ? operand0 : operand1);
+ Expression other = (operand0 instanceof Literal ? operand1 : operand0);
+ boolean negate = ((operator == Token.FEQ) != (((BooleanValue)literal.getValue()).getBooleanValue()));
+ if (negate) {
+ NotFn fn = (NotFn) SystemFunctionCall.makeSystemFunction("not", new Expression[]{other});
+ ExpressionTool.copyLocationInfo(this, fn);
+ return fn.optimize(visitor, contextItemType);
+ } else {
+ return other;
+ }
+ }
+
+
+ // optimize generate-id(X) = generate-id(Y) as "X is Y"
+ // This construct is often used in XSLT 1.0 stylesheets.
+ // Only do this if we know the arguments are singletons, because "is" doesn't
+ // do first-value extraction.
+
+ if (operand0 instanceof GenerateId && operand1 instanceof GenerateId) {
+ GenerateId f0 = (GenerateId) operand0;
+ GenerateId f1 = (GenerateId) operand1;
+ if (!Cardinality.allowsMany(f0.argument[0].getCardinality()) &&
+ !Cardinality.allowsMany(f1.argument[0].getCardinality()) &&
+ (operator == Token.FEQ)) {
+ IdentityComparison id =
+ new IdentityComparison(f0.argument[0],
+ Token.IS,
+ f1.argument[0]);
+ id.setGenerateIdEmulation(true);
+ ExpressionTool.copyLocationInfo(this, id);
+ return visitor.optimize(visitor.typeCheck(visitor.simplify(id), contextItemType), contextItemType);
+ }
+ }
+
+ return this;
+ }
+
+ /**
+ * Optimize comparisons of count(X) to a literal value. The objective here is to count items in the sequence only
+ * until the result of the comparison is deducible; for example to evaluate (count(X)>2) we can stop at the third
+ * item in the sequence.
+ * @param visitor the expression visitor
+ * @param contextItemType the context item type
+ * @param inverted true if the call to count(X) is the right-hand operand
+ * @return the optimized expression, or null if no optimization is possible
+ * @throws XPathException if an error occurs
+ */
+
+ /*@Nullable*/
+ private Expression optimizeCount(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType, boolean inverted) throws XPathException {
+ Count countFn = (Count)(inverted ? operand1 : operand0);
+ Expression sequence = countFn.argument[0];
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ sequence = ExpressionTool.unsorted(opt, sequence, false);
+
+ AtomicValue literalOperand = (AtomicValue)((Literal)(inverted ? operand0 : operand1)).getValue();
+ int op = (inverted ? Token.inverse(operator) : operator);
+
+ if (isZero(literalOperand)) {
+ if (op == Token.FEQ || op == Token.FLE) {
+ // rewrite count(x)=0 as empty(x)
+ Expression result = SystemFunctionCall.makeSystemFunction(
+ "empty", new Expression[]{sequence});
+ opt.trace("Rewrite count()=0 as:", result);
+ return result;
+ } else if (op == Token.FNE || op == Token.FGT) {
+ // rewrite count(x)!=0, count(x)>0 as exists(x)
+ Expression result = SystemFunctionCall.makeSystemFunction("exists", new Expression[]{sequence});
+ opt.trace("Rewrite count()>0 as:", result);
+ return result;
+ } else if (op == Token.FGE) {
+ // rewrite count(x)>=0 as true()
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ } else { // singletonOperator == Token.FLT
+ // rewrite count(x)<0 as false()
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+ } else if (literalOperand instanceof NumericValue) {
+ long operand;
+ if (literalOperand instanceof IntegerValue) {
+ operand = ((IntegerValue) literalOperand).longValue();
+ } else if (literalOperand.isNaN()) {
+ return Literal.makeLiteral(BooleanValue.get(op == Token.FNE));
+ } else if (((NumericValue) literalOperand).isWholeNumber()) {
+ operand = ((NumericValue) literalOperand).longValue();
+ } else if (op == Token.FEQ) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ } else if (op == Token.FNE) {
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ } else if (op == Token.FGT || op == Token.FGE) {
+ operand = ((NumericValue) literalOperand).ceiling().longValue();
+ op = Token.FGE;
+ } else /*if (op == Token.FLT || op == Token.FLE)*/ {
+ operand = ((NumericValue) literalOperand).floor().longValue();
+ op = Token.FLE;
+ }
+ if (operand < 0) {
+ switch (op) {
+ case Token.FEQ:
+ case Token.FLT:
+ case Token.FLE:
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ default:
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ }
+ }
+ if (operand > Integer.MAX_VALUE) {
+ switch (op) {
+ case Token.FEQ:
+ case Token.FGT:
+ case Token.FGE:
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ default:
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ }
+ }
+ if (sequence instanceof TailExpression || sequence instanceof Subsequence) {
+ // it's probably the result of a previous optimization
+ return null;
+ }
+ switch (op) {
+ case Token.FEQ:
+ case Token.FNE:
+ case Token.FLE:
+ case Token.FLT:
+ // rewrite count(E) op N as count(subsequence(E, 1, N+1)) op N
+ Subsequence ss = (Subsequence) SystemFunctionCall.makeSystemFunction("subsequence",
+ new Expression[]{
+ sequence,
+ Literal.makeLiteral(IntegerValue.PLUS_ONE),
+ Literal.makeLiteral(Int64Value.makeIntegerValue(operand + 1))});
+ Count ct = (Count) SystemFunctionCall.makeSystemFunction("count", new Expression[]{ss});
+ CompareToIntegerConstant ctic = new CompareToIntegerConstant(ct, op, operand);
+ opt.trace("Rewrite count()~N as:", ctic);
+ ExpressionTool.copyLocationInfo(this, ctic);
+ return ctic;
+ case Token.FGE:
+ case Token.FGT:
+ // rewrite count(x) gt n as exists(x[n+1])
+ // and count(x) ge n as exists(x[n])
+ TailExpression tail = new TailExpression(sequence, (int)(op == Token.FGE ? operand : operand+1));
+ ExpressionTool.copyLocationInfo(this, tail);
+ Expression result = SystemFunctionCall.makeSystemFunction("exists", new Expression[]{tail});
+ ExpressionTool.copyLocationInfo(this, result);
+ opt.trace("Rewrite count()>=N as:", result);
+ return result;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Check whether this specific instance of the expression is negatable
+ *
+ * @return true if it is
+ */
+
+ public boolean isNegatable(ExpressionVisitor visitor) {
+ // Expression is not negatable if it might involve NaN
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ return !maybeNaN(operand0, th) && !maybeNaN(operand1, th);
+ }
+
+ private boolean maybeNaN(Expression exp, TypeHierarchy th) {
+ return th.relationship(exp.getItemType(th), BuiltInAtomicType.DOUBLE) != TypeHierarchy.DISJOINT ||
+ th.relationship(exp.getItemType(th), BuiltInAtomicType.FLOAT) != TypeHierarchy.DISJOINT;
+ }
+
+ /**
+ * Return the negation of this value comparison: that is, a value comparison that returns true()
+ * if and only if the original returns false(). The result must be the same as not(this) even in the
+ * case where one of the operands is ().
+ * @return the inverted comparison
+ */
+
+ public Expression negate() {
+ ValueComparison vc = new ValueComparison(operand0, Token.negate(operator), operand1);
+ vc.comparer = comparer;
+ if (resultWhenEmpty == null || resultWhenEmpty == BooleanValue.FALSE) {
+ vc.resultWhenEmpty = BooleanValue.TRUE;
+ } else {
+ vc.resultWhenEmpty = BooleanValue.FALSE;
+ }
+ ExpressionTool.copyLocationInfo(this, vc);
+ return vc;
+ }
+
+
+ /**
+ * Test whether an expression is constant zero
+ * @param v the value to be tested
+ * @return true if the operand is the constant zero (of any numeric data type)
+ */
+
+ private static boolean isZero(Sequence v) {
+ return v instanceof NumericValue && ((NumericValue)v).compareTo(0) == 0;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ValueComparison vc = new ValueComparison(operand0.copy(), operator, operand1.copy());
+ vc.comparer = comparer;
+ vc.resultWhenEmpty = resultWhenEmpty;
+ vc.needsRuntimeCheck = needsRuntimeCheck;
+ return vc;
+ }
+
+ /**
+ * Evaluate the effective boolean value of the expression
+ *
+ * @param context the given context for evaluation
+ * @return a boolean representing the result of the comparison of the two operands
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ try {
+ AtomicValue v0 = ((AtomicValue) operand0.evaluateItem(context));
+ if (v0 == null) {
+ return (resultWhenEmpty == BooleanValue.TRUE); // normally false
+ }
+ AtomicValue v1 = ((AtomicValue) operand1.evaluateItem(context));
+ if (v1 == null) {
+ return (resultWhenEmpty == BooleanValue.TRUE); // normally false
+ }
+ return compare(v0, operator, v1, comparer.provideContext(context), needsRuntimeCheck);
+ } catch (XPathException e) {
+ // re-throw the exception with location information added
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+ /**
+ * Compare two atomic values, using a specified operator and collation
+ *
+ * @param v0 the first operand
+ * @param op the operator, as defined by constants such as {@link net.sf.saxon.expr.parser.Token#FEQ} or
+ * {@link net.sf.saxon.expr.parser.Token#FLT}
+ * @param v1 the second operand
+ * @param comparer the Collator to be used when comparing strings
+ * @param checkTypes
+ * @return the result of the comparison: -1 for LT, 0 for EQ, +1 for GT
+ * @throws XPathException if the values are not comparable
+ */
+
+ static boolean compare(AtomicValue v0, int op, AtomicValue v1, AtomicComparer comparer, boolean checkTypes)
+ throws XPathException {
+ if (checkTypes &&
+ !Type.isGuaranteedComparable(v0.getPrimitiveType(), v1.getPrimitiveType(), Token.isOrderedOperator(op))) {
+ XPathException e2 = new XPathException("Cannot compare " + Type.displayTypeName(v0) +
+ " to " + Type.displayTypeName(v1));
+ e2.setErrorCode("XPTY0004");
+ e2.setIsTypeError(true);
+ throw e2;
+ }
+ if (v0.isNaN() || v1.isNaN()) {
+ return (op == Token.FNE);
+ }
+ try {
+ switch (op) {
+ case Token.FEQ:
+ return comparer.comparesEqual(v0, v1);
+ case Token.FNE:
+ return !comparer.comparesEqual(v0, v1);
+ case Token.FGT:
+ return comparer.compareAtomicValues(v0, v1) > 0;
+ case Token.FLT:
+ return comparer.compareAtomicValues(v0, v1) < 0;
+ case Token.FGE:
+ return comparer.compareAtomicValues(v0, v1) >= 0;
+ case Token.FLE:
+ return comparer.compareAtomicValues(v0, v1) <= 0;
+ default:
+ throw new UnsupportedOperationException("Unknown operator " + op);
+ }
+ } catch (ClassCastException err) {
+ XPathException e2 = new XPathException("Cannot compare " + Type.displayTypeName(v0) +
+ " to " + Type.displayTypeName(v1));
+ e2.setErrorCode("XPTY0004");
+ e2.setIsTypeError(true);
+ throw e2;
+ }
+ }
+
+ /**
+ * Evaluate the expression in a given context
+ *
+ * @param context the given context for evaluation
+ * @return a BooleanValue representing the result of the numeric comparison of the two operands,
+ * or null representing the empty sequence
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ try {
+ AtomicValue v0 = (AtomicValue) operand0.evaluateItem(context);
+ if (v0 == null) {
+ return resultWhenEmpty;
+ }
+ AtomicValue v1 = (AtomicValue) operand1.evaluateItem(context);
+ if (v1 == null) {
+ return resultWhenEmpty;
+ }
+ return BooleanValue.get(compare(v0, operator, v1, comparer.provideContext(context), needsRuntimeCheck));
+ } catch (XPathException e) {
+ // re-throw the exception with location information added
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+
+ /**
+ * Determine the data type of the expression
+ *
+ * @param th the type hierarchy cache
+ * @return Type.BOOLEAN
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Determine the static cardinality.
+ */
+
+ public int computeCardinality() {
+ if (resultWhenEmpty != null) {
+ return StaticProperty.EXACTLY_ONE;
+ } else {
+ return super.computeCardinality();
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ValueComparison expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ValueComparisonCompiler();
+ }
+//#endif
+
+// protected String displayOperator() {
+// return Token.tokens[operator] +
+// (resultWhenEmpty == null ? "" : " (on empty return " + resultWhenEmpty + ')');
+// }
+
+ protected void explainExtraAttribute(ExpressionPresenter out) {
+ out.emitAttribute("on-empty", resultWhenEmpty.toString());
+ }
+
+}
+
diff --git a/sf/saxon/expr/ValueTailIterator.java b/sf/saxon/expr/ValueTailIterator.java
new file mode 100644
index 0000000..a2f4390
--- /dev/null
+++ b/sf/saxon/expr/ValueTailIterator.java
@@ -0,0 +1,95 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+
+/**
+ * ValueTailIterator iterates over a base sequence starting at an element other than the first.
+ * It is used in the case where the base sequence is "grounded", that is, it exists in memory and
+ * supports efficient direct addressing.
+ */
+
+public class ValueTailIterator<T extends Item>
+ implements SequenceIterator<T>, GroundedIterator<T>, LookaheadIterator<T> {
+
+ private GroundedValue baseValue;
+ private int start; // zero-based
+ private int pos = 0;
+
+ /**
+ * Construct a ValueTailIterator
+ * @param base The items to be filtered
+ * @param start The position of the first item to be included (zero-based)
+ */
+
+ public ValueTailIterator(GroundedValue base, int start) {
+ baseValue = base;
+ this.start = start;
+ pos = 0;
+ }
+
+ public T next() throws XPathException {
+ return (T)baseValue.itemAt(start + pos++);
+ }
+
+ public T current() {
+ return (T)baseValue.itemAt(start + pos - 1);
+ }
+
+ public int position() {
+ return pos;
+ }
+
+ public boolean hasNext() {
+ return baseValue.itemAt(start + pos) != null;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/
+ public ValueTailIterator<T> getAnother() throws XPathException {
+ return new ValueTailIterator<T>(baseValue, start);
+ }
+
+ /**
+ * Return a Value containing all the items in the sequence returned by this
+ * SequenceIterator. This should be an "in-memory" value, not a Closure.
+ *
+ * @return the corresponding Value
+ */
+
+ public GroundedValue materialize() {
+ if (start == 0) {
+ return baseValue;
+ } else {
+ return baseValue.subsequence(start, Integer.MAX_VALUE);
+ }
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return GROUNDED | LOOKAHEAD;
+ }
+}
+
diff --git a/sf/saxon/expr/VariableReference.java b/sf/saxon/expr/VariableReference.java
new file mode 100644
index 0000000..0e1ac16
--- /dev/null
+++ b/sf/saxon/expr/VariableReference.java
@@ -0,0 +1,600 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.VariableReferenceCompiler;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.GlobalParam;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.lib.StandardErrorListener;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.List;
+
+/**
+ * Variable reference: a reference to a variable. This may be an XSLT-defined variable, a range
+ * variable defined within the XPath expression, or a variable defined in some other static context.
+ */
+
+public class VariableReference extends Expression implements BindingReference {
+
+ /*@Nullable*/ protected Binding binding = null; // This will be null until fixup() is called; it will also be null
+ // if the variable reference has been inlined
+ protected SequenceType staticType = null;
+ protected GroundedValue constantValue = null;
+ transient String displayName = null;
+ private boolean flattened = false;
+ private boolean inLoop = true;
+ private boolean filtered = false;
+
+ /**
+ * Create a Variable Reference
+ */
+
+ public VariableReference() {
+ //System.err.println("Creating varRef");
+ }
+
+ /**
+ * Create a Variable Reference
+ * @param binding the variable binding to which this variable refers
+ */
+
+ public VariableReference(Binding binding) {
+ //System.err.println("Creating varRef1");
+ displayName = binding.getVariableQName().getDisplayName();
+ fixup(binding);
+ }
+
+ /**
+ * Create a clone copy of this VariableReference
+ * @return the cloned copy
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ if (binding == null) {
+ //System.err.println("copy unbound variable " + this);
+ throw new UnsupportedOperationException("Cannot copy a variable reference whose binding is unknown");
+ }
+ VariableReference ref = new VariableReference();
+ ref.binding = binding;
+ ref.staticType = staticType;
+ ref.constantValue = constantValue;
+ ref.displayName = displayName;
+ binding.addReference(inLoop);
+ ExpressionTool.copyLocationInfo(this, ref);
+ return ref;
+ }
+
+ /**
+ * Set static type. This is a callback from the variable declaration object. As well
+ * as supplying the static type, it may also supply a compile-time value for the variable.
+ * As well as the type information, other static properties of the value are supplied:
+ * for example, whether the value is an ordered node-set.
+ * @param type the static type of the variable
+ * @param value the value of the variable if this is a compile-time constant, or null otherwise
+ * @param properties static properties of the expression to which the variable is bound
+ */
+
+ public void setStaticType(SequenceType type, /*@Nullable*/ GroundedValue value, int properties) {
+ // System.err.println(this + " Set static type = " + type);
+ if (type == null) {
+ type = SequenceType.ANY_SEQUENCE;
+ }
+ staticType = type;
+ constantValue = value;
+ // Although the variable may be a context document node-set at the point it is defined,
+ // the context at the point of use may be different, so this property cannot be transferred.
+ int dependencies = getDependencies();
+ staticProperties = (properties & ~StaticProperty.CONTEXT_DOCUMENT_NODESET) |
+ StaticProperty.NON_CREATIVE |
+ type.getCardinality() |
+ dependencies;
+ }
+
+ /**
+ * Mark an expression as being "flattened". This is a collective term that includes extracting the
+ * string value or typed value, or operations such as simple value construction that concatenate text
+ * nodes before atomizing. The implication of all of these is that although the expression might
+ * return nodes, the identity of the nodes has no significance. This is called during type checking
+ * of the parent expression. At present, only variable references take any notice of this notification.
+ */
+
+ public void setFlattened(boolean flattened) {
+ this.flattened = flattened;
+ }
+
+ /**
+ * Test whether this variable reference is flattened - that is, whether it is atomized etc
+ * @return true if the value of the variable is atomized, or converted to a string or number
+ */
+
+ public boolean isFlattened() {
+ return flattened;
+ }
+
+ /**
+ * Mark an expression as filtered: that is, it appears as the base expression in a filter expression.
+ * This notification currently has no effect except when the expression is a variable reference.
+ */
+
+ public void setFiltered(boolean filtered) {
+ this.filtered = filtered;
+ }
+
+ /**
+ * Determine whether this variable reference is filtered
+ * @return true if the value of the variable is filtered by a predicate
+ */
+
+ public boolean isFiltered() {
+ return filtered;
+ }
+
+ /**
+ * Determine whether this variable reference appears in a loop relative to its declaration.
+ * By default, when in doubt, returns true. This is calculated during type-checking.
+ * @return true if this variable reference occurs in a loop, where the variable declaration is
+ * outside the loop
+ */
+
+ public boolean isInLoop() {
+ return inLoop;
+ }
+
+ /**
+ * Type-check the expression. At this stage details of the static type must be known.
+ * If the variable has a compile-time value, this is substituted for the variable reference
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (constantValue != null) {
+ binding = null;
+ return Literal.makeLiteral(constantValue);
+ }
+// if (staticType == null) {
+// throw new IllegalStateException("Variable $" + getDisplayName() + " has not been fixed up");
+// }
+
+
+ inLoop = visitor.isLoopingReference(binding, this);
+// following code removed because it causes error181 to blow the stack - need to check for circularities well
+// if (binding instanceof GlobalVariable) {
+// ((GlobalVariable)binding).typeCheck(visitor, AnyItemType.getInstance());
+// }
+
+ if (binding != null) {
+ binding.addReference(inLoop);
+ }
+
+ return this;
+ }
+
+ /**
+ * Type-check the expression. At this stage details of the static type must be known.
+ * If the variable has a compile-time value, this is substituted for the variable reference
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (constantValue != null) {
+ binding = null;
+ return Literal.makeLiteral(constantValue);
+ }
+ return this;
+ }
+
+
+
+ /**
+ * Fix up this variable reference to a Binding object, which enables the value of the variable
+ * to be located at run-time.
+ */
+
+ public void fixup(Binding binding) {
+ this.binding = binding;
+ resetLocalStaticProperties();
+ }
+
+ /**
+ * Provide additional information about the type of the variable, typically derived by analyzing
+ * the initializer of the variable binding
+ * @param type the item type of the variable
+ * @param cardinality the cardinality of the variable
+ * @param constantValue the actual value of the variable, if this is known statically, otherwise null
+ * @param properties additional static properties of the variable's initializer
+ * @param visitor an ExpressionVisitor
+ */
+
+ public void refineVariableType(
+ ItemType type, int cardinality, /*@Nullable*/ GroundedValue constantValue, int properties, ExpressionVisitor visitor) {
+ Executable exec = visitor.getExecutable();
+ if (exec == null) {
+ // happens during use-when evaluation
+ return;
+ }
+ TypeHierarchy th = exec.getConfiguration().getTypeHierarchy();
+ ItemType oldItemType = getItemType(th);
+ ItemType newItemType = oldItemType;
+ if (th.isSubType(type, oldItemType)) {
+ newItemType = type;
+ }
+ if (oldItemType instanceof NodeTest && type instanceof AtomicType) {
+ // happens when all references are flattened
+ newItemType = type;
+ }
+ int newcard = cardinality & getCardinality();
+ if (newcard==0) {
+ // this will probably lead to a type error later
+ newcard = getCardinality();
+ }
+ SequenceType seqType = SequenceType.makeSequenceType(newItemType, newcard);
+ setStaticType(seqType, constantValue, properties);
+ }
+
+ /**
+ * Determine the data type of the expression, if possible
+ *
+ * @param th the type hierarchy cache
+ * @return the type of the variable, if this can be determined statically;
+ * otherwise Type.ITEM (meaning not known in advance)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (staticType == null || staticType.getPrimaryType() == AnyItemType.getInstance()) {
+ if (binding != null) {
+ return binding.getRequiredType().getPrimaryType();
+ }
+ return AnyItemType.getInstance();
+ } else {
+ return staticType.getPrimaryType();
+ }
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ if (binding != null) {
+ return binding.getIntegerBoundsForVariable();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the static cardinality
+ */
+
+ public int computeCardinality() {
+ if (staticType == null) {
+ if (binding == null) {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ } else if (binding instanceof LetExpression) {
+ return binding.getRequiredType().getCardinality();
+ } else if (binding instanceof Assignation) {
+ return StaticProperty.EXACTLY_ONE;
+ } else {
+ return binding.getRequiredType().getCardinality();
+ }
+ } else {
+ return staticType.getCardinality();
+ }
+ }
+
+ /**
+ * Determine the special properties of this expression
+ *
+ * @return {@link StaticProperty#NON_CREATIVE} (unless the variable is assignable using saxon:assign)
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ if (binding == null || !binding.isAssignable()) {
+ // if the variable reference is assignable, we mustn't move it, or any expression that contains it,
+ // out of a loop. The way to achieve this is to treat it as a "creative" expression, because the
+ // optimizer recognizes such expressions and handles them with care...
+ p |= StaticProperty.NON_CREATIVE;
+ }
+ if (binding instanceof Assignation) {
+ Expression exp = ((Assignation)binding).getSequence();
+ if (exp != null) {
+ p |= (exp.getSpecialProperties() & StaticProperty.NOT_UNTYPED_ATOMIC);
+ }
+ }
+ if (staticType != null &&
+ !Cardinality.allowsMany(staticType.getCardinality()) &&
+ staticType.getPrimaryType() instanceof NodeTest) {
+ p |= StaticProperty.SINGLE_DOCUMENT_NODESET;
+ }
+ return p;
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return W3C_MOTIONLESS;
+ }
+//#endif
+
+ /**
+ * Test if this expression is the same as another expression.
+ * (Note, we only compare expressions that
+ * have the same static and dynamic context).
+ */
+
+ public boolean equals(Object other) {
+ return (other instanceof VariableReference &&
+ binding == ((VariableReference) other).binding &&
+ binding != null);
+ }
+
+ /**
+ * get HashCode for comparing two expressions
+ */
+
+ public int hashCode() {
+ return binding == null ? 73619830 : binding.hashCode();
+ }
+
+
+ public int getIntrinsicDependencies() {
+ int d = 0;
+ if (binding == null) {
+ // assume the worst
+ d |= (StaticProperty.DEPENDS_ON_LOCAL_VARIABLES |
+ StaticProperty.DEPENDS_ON_ASSIGNABLE_GLOBALS |
+ StaticProperty.DEPENDS_ON_RUNTIME_ENVIRONMENT);
+ } else if (binding.isGlobal()) {
+ if (binding.isAssignable()) {
+ d |= StaticProperty.DEPENDS_ON_ASSIGNABLE_GLOBALS;
+ }
+ if (binding instanceof GlobalParam) {
+ d |= StaticProperty.DEPENDS_ON_RUNTIME_ENVIRONMENT;
+ }
+ } else {
+ d |= StaticProperty.DEPENDS_ON_LOCAL_VARIABLES;
+ }
+ return d;
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES) {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ // Replace the variable reference with the given expression.
+ offer.accepted = true;
+ return exp;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided. This implementation provides both all three methods
+ * natively.
+ */
+
+ public int getImplementationMethod() {
+ return (Cardinality.allowsMany(getCardinality()) ? 0 : EVALUATE_METHOD)
+ | ITERATE_METHOD | PROCESS_METHOD;
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ return pathMap.getPathForVariable(getBinding());
+ }
+
+ /**
+ * Get the value of this variable in a given context.
+ *
+ * @param c the XPathContext which contains the relevant variable bindings
+ * @return the value of the variable, if it is defined
+ * @throws XPathException if the variable is undefined
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext c) throws XPathException {
+ try {
+ Sequence actual = evaluateVariable(c);
+ return actual.iterate();
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ throw err;
+ } catch (AssertionError err) {
+ err.printStackTrace();
+ String msg = err.getMessage() + ". Variable reference $" + getDisplayName() +
+ " at line " + getLineNumber() + (getSystemId() == null ? "" : " of " + getSystemId());
+ StandardErrorListener.printStackTrace(System.err, c);
+ throw new AssertionError(msg);
+ }
+ }
+
+ public Item evaluateItem(XPathContext c) throws XPathException {
+ try {
+ Sequence actual = evaluateVariable(c);
+ return actual.head();
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+ public void process(XPathContext c) throws XPathException {
+ try {
+ SequenceIterator iter = evaluateVariable(c).iterate();
+ SequenceReceiver out = c.getReceiver();
+ int loc = getLocationId();
+ while (true) {
+ Item it = iter.next();
+ if (it==null) {
+ break;
+ }
+ out.append(it, loc, NodeInfo.ALL_NAMESPACES);
+ }
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+ /**
+ * Evaluate this variable
+ * @param c the XPath dynamic context
+ * @return the value of the variable
+ * @throws XPathException if any error occurs
+ */
+
+ /*@NotNull*/
+ public Sequence evaluateVariable(XPathContext c) throws XPathException {
+ try {
+ return binding.evaluateVariable(c);
+ } catch (NullPointerException err) {
+ if (binding == null) {
+ throw new IllegalStateException("Variable $" + displayName + " has not been fixed up");
+ } else {
+ throw err;
+ }
+ }
+ }
+
+ /**
+ * Get the object bound to the variable
+ * @return the Binding which declares this variable and associates it with a value
+ */
+
+ public Binding getBinding() {
+ return binding;
+ }
+
+ /**
+ * Get the display name of the variable. This is taken from the variable binding if possible
+ * @return the display name (a lexical QName
+ */
+
+ public String getDisplayName() {
+ if (binding != null) {
+ return binding.getVariableQName().getDisplayName();
+ } else {
+ return displayName;
+ }
+ }
+
+ /**
+ * Get the EQName of the variable. This is taken from the variable binding if possible.
+ * The returned name is in the format Q{uri}local if in a namespace, or the local name
+ * alone if not.
+ * @return the EQName, or the local name if not in a namespace
+ */
+
+ public String getEQName() {
+ if (binding != null) {
+ StructuredQName q = binding.getVariableQName();
+ if (q.isInNamespace("")) {
+ return q.getLocalPart();
+ } else {
+ return q.getEQName();
+ }
+ } else {
+ return displayName;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the VariableReference expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new VariableReferenceCompiler();
+ }
+//#endif
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ */
+
+ public String toString() {
+ String d = getEQName();
+ return "$" + (d == null ? "$" : d);
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter destination) {
+
+ destination.startElement("variableReference");
+ String d = getEQName();
+ destination.emitAttribute("name", (d == null ? "null" : d));
+ destination.emitAttribute("slot", ""+binding.getLocalSlotNumber());
+ destination.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/VennExpression.java b/sf/saxon/expr/VennExpression.java
new file mode 100644
index 0000000..8f19dae
--- /dev/null
+++ b/sf/saxon/expr/VennExpression.java
@@ -0,0 +1,683 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.VennExpressionCompiler;
+import com.saxonica.stream.StreamingPatternMaker;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.instruct.Block;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.expr.sort.DocumentOrderIterator;
+import net.sf.saxon.expr.sort.DocumentSorter;
+import net.sf.saxon.expr.sort.GlobalOrderComparer;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.*;
+
+
+/**
+* An expression representing a nodeset that is a union, difference, or
+* intersection of two other NodeSets
+*/
+
+public class VennExpression extends BinaryExpression {
+
+ /**
+ * Constructor
+ * @param p1 the left-hand operand
+ * @param op the operator (union, intersection, or difference)
+ * @param p2 the right-hand operand
+ */
+
+ public VennExpression(final Expression p1, final int op, final Expression p2) {
+ super(p1, op, p2);
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ switch (operator) {
+ case Token.UNION: return "union";
+ case Token.INTERSECT: return "intersect";
+ case Token.EXCEPT: return "except";
+ default: return "unknown";
+ }
+ }
+
+ /**
+ * Determine the data type of the items returned by this expression
+ * @return the data type
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public final ItemType getItemType(TypeHierarchy th) {
+ final ItemType t1 = operand0.getItemType(th);
+ if (operator == Token.UNION) {
+ final ItemType t2 = operand1.getItemType(th);
+ return Type.getCommonSuperType(t1, t2, th);
+ } else {
+ return t1;
+ }
+ }
+
+ /**
+ * Determine the static cardinality of the expression
+ */
+
+ public final int computeCardinality() {
+ final int c1 = operand0.getCardinality();
+ final int c2 = operand1.getCardinality();
+ switch (operator) {
+ case Token.UNION:
+ if (Literal.isEmptySequence(operand0)) return c2;
+ if (Literal.isEmptySequence(operand1)) return c1;
+ return c1 | c2 | StaticProperty.ALLOWS_ONE | StaticProperty.ALLOWS_MANY;
+ // allows ZERO only if one operand allows ZERO
+ case Token.INTERSECT:
+ if (Literal.isEmptySequence(operand0)) return StaticProperty.EMPTY;
+ if (Literal.isEmptySequence(operand1)) return StaticProperty.EMPTY;
+ return (c1 & c2) | StaticProperty.ALLOWS_ZERO | StaticProperty.ALLOWS_ONE;
+ // allows MANY only if both operands allow MANY
+ case Token.EXCEPT:
+ if (Literal.isEmptySequence(operand0)) return StaticProperty.EMPTY;
+ if (Literal.isEmptySequence(operand1)) return c1;
+ return c1 | StaticProperty.ALLOWS_ZERO | StaticProperty.ALLOWS_ONE;
+ // allows MANY only if first operand allows MANY
+ }
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ final int prop0 = operand0.getSpecialProperties();
+ final int prop1 = operand1.getSpecialProperties();
+ int props = StaticProperty.ORDERED_NODESET;
+ if (testContextDocumentNodeSet(prop0, prop1)) {
+ props |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
+ }
+ if (testSubTree(prop0, prop1)) {
+ props |= StaticProperty.SUBTREE_NODESET;
+ }
+ if (!testCreative(prop0, prop1)) {
+ props |= StaticProperty.NON_CREATIVE;
+ }
+ return props;
+ }
+
+ /**
+ * Determine whether all the nodes in the node-set are guaranteed to
+ * come from the same document as the context node. Used for optimization.
+ * @param prop0 contains the Context Document Nodeset property of the first operand
+ * @param prop1 contains the Context Document Nodeset property of the second operand
+ * @return true if all the nodes come from the context document
+ */
+
+ private boolean testContextDocumentNodeSet(final int prop0, final int prop1) {
+ switch (operator) {
+ case Token.UNION:
+ return (prop0 & prop1 & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0;
+ case Token.INTERSECT:
+ return ((prop0 | prop1) & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0;
+ case Token.EXCEPT:
+ return (prop0 & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0;
+ }
+ return false;
+ }
+
+ /**
+ * Gather the component operands of a union or intersect expression
+ * @param operator union or intersect
+ * @param set the set into which the components are to be gathered. If the operator
+ * is union, this follows the tree gathering all operands of union expressions. Ditto,
+ * mutatis mutandis, for intersect expressions.
+ */
+
+ public void gatherComponents(int operator, Set set) {
+ if (operand0 instanceof VennExpression && ((VennExpression)operand0).operator == operator) {
+ ((VennExpression)operand0).gatherComponents(operator, set);
+ } else {
+ set.add(operand0);
+ }
+ if (operand1 instanceof VennExpression && ((VennExpression)operand1).operator == operator) {
+ ((VennExpression)operand1).gatherComponents(operator, set);
+ } else {
+ set.add(operand1);
+ }
+ }
+
+ /**
+ * Determine whether all the nodes in the node-set are guaranteed to
+ * come from a subtree rooted at the context node. Used for optimization.
+ * @param prop0 contains the SubTree property of the first operand
+ * @param prop1 contains the SubTree property of the second operand
+ * @return true if all the nodes come from the tree rooted at the context node
+ */
+
+ private boolean testSubTree(final int prop0, final int prop1) {
+ switch (operator) {
+ case Token.UNION:
+ return (prop0 & prop1 & StaticProperty.SUBTREE_NODESET) != 0;
+ case Token.INTERSECT:
+ return ((prop0 | prop1) & StaticProperty.SUBTREE_NODESET) != 0;
+ case Token.EXCEPT:
+ return (prop0 & StaticProperty.SUBTREE_NODESET) != 0;
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether the expression can create new nodes
+ * @param prop0 contains the noncreative property of the first operand
+ * @param prop1 contains the noncreative property of the second operand
+ * @return true if the expression can create new nodes
+ */
+
+ private boolean testCreative(final int prop0, final int prop1) {
+ return !(((prop0 & StaticProperty.NON_CREATIVE) != 0) &&
+ ((prop1 & StaticProperty.NON_CREATIVE) != 0));
+ }
+
+
+ /**
+ * Simplify the expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ operand0 = visitor.simplify(operand0);
+ operand1 = visitor.simplify(operand1);
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, final ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ operand0 = visitor.typeCheck(operand0, contextItemType);
+ operand1 = visitor.typeCheck(operand1, contextItemType);
+
+ if (!(operand0 instanceof PatternSponsor)) {
+ final RoleLocator role0 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 0);
+ operand0 = TypeChecker.staticTypeCheck(operand0, SequenceType.NODE_SEQUENCE, false, role0, visitor);
+ }
+
+ if (!(operand1 instanceof PatternSponsor)) {
+ final RoleLocator role1 = new RoleLocator(RoleLocator.BINARY_EXPR, Token.tokens[operator], 1);
+ operand1 = TypeChecker.staticTypeCheck(operand1, SequenceType.NODE_SEQUENCE, false, role1, visitor);
+ }
+
+ // For the intersect and except operators, if the types are disjoint then we can simplify
+ if (operator != Token.UNION) {
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType t0 = operand0.getItemType(th);
+ ItemType t1 = operand1.getItemType(th);
+ if (th.relationship(t0, t1) == TypeHierarchy.DISJOINT) {
+ if (operator == Token.INTERSECT) {
+ return Literal.makeEmptySequence();
+ } else {
+ if ((operand0.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) {
+ return operand0;
+ } else {
+ return new DocumentSorter(operand0);
+ }
+ }
+ }
+ }
+
+ return this;
+ }
+
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+
+ final Configuration config = visitor.getConfiguration();
+ final TypeHierarchy th = config.getTypeHierarchy();
+
+ // If either operand is an empty sequence, simplify the expression. This can happen
+ // after reduction with constructs of the form //a[condition] | //b[not(condition)],
+ // common in XPath 1.0 because there were no conditional expressions.
+
+ switch (operator) {
+ case Token.UNION:
+ if (Literal.isEmptySequence(operand0) &&
+ (operand1.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) return operand1;
+ if (Literal.isEmptySequence(operand1) &&
+ (operand0.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) return operand0;
+ break;
+ case Token.INTERSECT:
+ if (Literal.isEmptySequence(operand0)) return operand0;
+ if (Literal.isEmptySequence(operand1)) return operand1;
+ break;
+ case Token.EXCEPT:
+ if (Literal.isEmptySequence(operand0)) return operand0;
+ if (Literal.isEmptySequence(operand1) &&
+ (operand0.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) return operand0;
+ break;
+ }
+
+ // If both are axis expressions on the same axis, merge them
+ // ie. rewrite (axis::test1 | axis::test2) as axis::(test1 | test2)
+
+ if (operand0 instanceof AxisExpression && operand1 instanceof AxisExpression) {
+ final AxisExpression a1 = (AxisExpression)operand0;
+ final AxisExpression a2 = (AxisExpression)operand1;
+ if (a1.getAxis() == a2.getAxis()) {
+ AxisExpression ax = new AxisExpression(a1.getAxis(),
+ new CombinedNodeTest(a1.getNodeTest(),
+ operator,
+ a2.getNodeTest()));
+ ExpressionTool.copyLocationInfo(this, ax);
+ return ax;
+ }
+ }
+
+ // If both are path expressions starting the same way, merge them
+ // i.e. rewrite (/X | /Y) as /(X|Y). This applies recursively, so that
+ // /A/B/C | /A/B/D becomes /A/B/child::(C|D)
+
+ // This optimization was previously done for all three operators. However, it's not safe for "except":
+ // A//B except A//C//B cannot be rewritten as A/descendant-or-self::node()/(B except C//B). As a quick
+ // fix, the optimization has been retained for "union" but dropped for "intersect" and "except". Need to
+ // do a more rigorous analysis of the conditions under which it is safe.
+
+ // TODO: generalize this code to handle all distributive operators
+
+ if (operand0 instanceof SlashExpression && operand1 instanceof SlashExpression && operator==Token.UNION) {
+ final SlashExpression path1 = (SlashExpression)operand0;
+ final SlashExpression path2 = (SlashExpression)operand1;
+
+ if (path1.getFirstStep().equals(path2.getFirstStep())) {
+ final VennExpression venn = new VennExpression(
+ path1.getRemainingSteps(),
+ operator,
+ path2.getRemainingSteps());
+ ExpressionTool.copyLocationInfo(this, venn);
+ final Expression path = ExpressionTool.makePathExpression(path1.getFirstStep(), venn, false);
+ ExpressionTool.copyLocationInfo(this, path);
+ return visitor.optimize(path, contextItemType);
+ }
+ }
+
+ // Try merging two non-positional filter expressions:
+ // A[exp0] | A[exp1] becomes A[exp0 or exp1]
+
+ if (operand0 instanceof FilterExpression && operand1 instanceof FilterExpression) {
+ final FilterExpression exp0 = (FilterExpression)operand0;
+ final FilterExpression exp1 = (FilterExpression)operand1;
+
+ if (!exp0.isPositional(th) &&
+ !exp1.isPositional(th) &&
+ exp0.getControllingExpression().equals(exp1.getControllingExpression())) {
+ final Expression filter;
+ switch (operator) {
+ case Token.UNION:
+ filter = new OrExpression(exp0.getFilter(),
+ exp1.getFilter());
+ break;
+ case Token.INTERSECT:
+ filter = new AndExpression(exp0.getFilter(),
+ exp1.getFilter());
+ break;
+ case Token.EXCEPT:
+ final FunctionCall negate2 = SystemFunctionCall.makeSystemFunction(
+ "not", new Expression[]{exp1.getFilter()});
+ filter = new AndExpression(exp0.getFilter(),
+ negate2);
+ break;
+ default:
+ throw new AssertionError("Unknown operator " + operator);
+ }
+ ExpressionTool.copyLocationInfo(this, filter);
+ FilterExpression f = new FilterExpression(exp0.getControllingExpression(), filter);
+ ExpressionTool.copyLocationInfo(this, f);
+ return f.simplify(visitor).typeCheck(visitor, contextItemType).optimize(visitor, contextItemType);
+ }
+ }
+
+ // Convert @*|node() into @*,node() to eliminate the sorted merge operation
+ // Avoid doing this when streaming because xsl:value-of select="@*,node()" is not currently streamable
+ if (!visitor.isOptimizeForStreaming() && operator == Token.UNION &&
+ operand0 instanceof AxisExpression && operand1 instanceof AxisExpression) {
+ AxisExpression a0 = (AxisExpression)operand0;
+ AxisExpression a1 = (AxisExpression)operand1;
+ if (a0.getAxis() == AxisInfo.ATTRIBUTE && a1.getAxis() == AxisInfo.CHILD) {
+ Block b = new Block();
+ b.setChildren(new Expression[]{operand0, operand1});
+ return b;
+ } else if (a1.getAxis() == AxisInfo.ATTRIBUTE && a0.getAxis() == AxisInfo.CHILD) {
+ Block b = new Block();
+ b.setChildren(new Expression[]{operand1, operand0});
+ return b;
+ }
+ }
+
+ // Convert (A intersect B) to use a serial search where one operand is a singleton
+ if (operator == Token.INTERSECT && !Cardinality.allowsMany(operand0.getCardinality())) {
+ return new SingletonIntersectExpression(operand0, operator,
+ ExpressionTool.unsorted(config.obtainOptimizer(), operand1, false));
+ }
+ if (operator == Token.INTERSECT && !Cardinality.allowsMany(operand1.getCardinality())) {
+ return new SingletonIntersectExpression(operand1, operator,
+ ExpressionTool.unsorted(config.obtainOptimizer(), operand0, false));
+ }
+
+ // If the types of the operands are disjoint, simplify "intersect" and "except"
+ if (operandsAreDisjoint(th)) {
+ if (operator == Token.INTERSECT) {
+ return Literal.makeEmptySequence();
+ } else if (operator == Token.EXCEPT) {
+ if ((operand0.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) {
+ return operand0;
+ } else {
+ return new DocumentSorter(operand0);
+ }
+ }
+ }
+ return this;
+ }
+
+ private boolean operandsAreDisjoint(TypeHierarchy th) {
+ return th.relationship(operand0.getItemType(th), operand1.getItemType(th)) == TypeHierarchy.DISJOINT;
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ if (offer.action == PromotionOffer.UNORDERED) {
+ if (operator == Token.UNION && operandsAreDisjoint(offer.getOptimizer().getConfiguration().getTypeHierarchy())) {
+ // replace union operator by comma operator to avoid cost of sorting into document order. See XMark q7
+ Block block = new Block();
+ block.setChildren(new Expression[]{operand0, operand1});
+ ExpressionTool.copyLocationInfo(this, block);
+ return block;
+ }
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ if (offer.action == PromotionOffer.UNORDERED) {
+ operand0 = doPromotion(operand0, offer);
+ operand1 = doPromotion(operand1, offer);
+ }
+ return this;
+ }
+ } else {
+ return super.promote(offer, parent);
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new VennExpression(operand0.copy(), operator, operand1.copy());
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ SubExpressionInfo i0 = new SubExpressionInfo(operand0, true, false, INHERITED_CONTEXT);
+ SubExpressionInfo i1 = new SubExpressionInfo(operand1, true, false, INHERITED_CONTEXT);
+ return new PairIterator<SubExpressionInfo>(i0, i1);
+ }
+
+
+
+ /**
+ * Is this expression the same as another expression?
+ */
+
+ public boolean equals(Object other) {
+ // NOTE: it's possible that the method in the superclass is already adequate for this
+ if (other instanceof VennExpression) {
+ VennExpression b = (VennExpression)other;
+ if (operator != b.operator) {
+ return false;
+ }
+ if (operand0.equals(b.operand0) && operand1.equals(b.operand1)) {
+ return true;
+ }
+ if (operator == Token.UNION || operator == Token.INTERSECT) {
+ // These are commutative and associative, so for example (A|B)|C equals B|(A|C)
+ Set s0 = new HashSet(10);
+ gatherComponents(operator, s0);
+ Set s1 = new HashSet(10);
+ ((VennExpression)other).gatherComponents(operator, s1);
+ return s0.equals(s1);
+ }
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return operand0.hashCode() ^ operand1.hashCode();
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ if (operator == Token.UNION) {
+ return new UnionPattern(
+ operand0.toPattern(config, is30),
+ operand1.toPattern(config, is30));
+ } else if (is30) {
+ if (operator == Token.EXCEPT) {
+ return new ExceptPattern(
+ operand0.toPattern(config, is30),
+ operand1.toPattern(config, is30));
+ } else {
+ return new IntersectPattern(
+ operand0.toPattern(config, is30),
+ operand1.toPattern(config, is30));
+ }
+ } else {
+ throw new XPathException("Cannot use intersect or except in an XSLT 2.0 pattern");
+ }
+ }
+
+//#ifdefined STREAM
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ Pattern result;
+ if (operator == Token.UNION) {
+ result = new UnionPattern(
+ operand0.toStreamingPattern(config, reasonForFailure),
+ operand1.toStreamingPattern(config, reasonForFailure));
+ } else if (operator == Token.EXCEPT) {
+ result = new ExceptPattern(
+ operand0.toStreamingPattern(config, reasonForFailure),
+ operand1.toStreamingPattern(config, reasonForFailure));
+ } else {
+ result = new IntersectPattern(
+ operand0.toStreamingPattern(config, reasonForFailure),
+ operand1.toStreamingPattern(config, reasonForFailure));
+ }
+ if (!reasonForFailure.isEmpty()) {
+ return null;
+ } else {
+ return result;
+ }
+ }
+
+//#endif
+
+ /**
+ * Iterate over the value of the expression. The result will always be sorted in document order,
+ * with duplicates eliminated
+ * @param c The context for evaluation
+ * @return a SequenceIterator representing the union of the two operands
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(final XPathContext c) throws XPathException {
+ SequenceIterator i1 = operand0.iterate(c);
+ //return Type.isNodeType(getItemType()) && isSingleton();
+ // this is a sufficient condition, but other expressions override this method
+ if ((operand0.getSpecialProperties() & StaticProperty.ORDERED_NODESET) == 0) {
+ i1 = new DocumentOrderIterator(i1, GlobalOrderComparer.getInstance());
+ }
+ SequenceIterator i2 = operand1.iterate(c);
+ //return Type.isNodeType(getItemType()) && isSingleton();
+ // this is a sufficient condition, but other expressions override this method
+ if ((operand1.getSpecialProperties() & StaticProperty.ORDERED_NODESET) == 0) {
+ i2 = new DocumentOrderIterator(i2, GlobalOrderComparer.getInstance());
+ }
+ switch (operator) {
+ case Token.UNION:
+ return new UnionEnumeration(i1, i2,
+ GlobalOrderComparer.getInstance());
+ case Token.INTERSECT:
+ return new IntersectionEnumeration(i1, i2,
+ GlobalOrderComparer.getInstance());
+ case Token.EXCEPT:
+ return new DifferenceEnumeration(i1, i2,
+ GlobalOrderComparer.getInstance());
+ }
+ throw new UnsupportedOperationException("Unknown operator in Venn Expression");
+ }
+
+ /**
+ * Get the effective boolean value. In the case of a union expression, this
+ * is reduced to an OR expression, for efficiency
+ */
+
+ public boolean effectiveBooleanValue(final XPathContext context) throws XPathException {
+ if (operator == Token.UNION) {
+ // NOTE: this optimization was probably already done statically
+ return operand0.effectiveBooleanValue(context) || operand1.effectiveBooleanValue(context);
+ } else {
+ return super.effectiveBooleanValue(context);
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ if (allowExtensions) {
+ // TODO: avoid dependency on Saxonica code
+ if (reasons == null) {
+ reasons = new ArrayList<String>();
+ }
+ Pattern selection = StreamingPatternMaker.makeStreamingPattern(this, getExecutable().getConfiguration(), reasons);
+ if (selection != null) {
+ return W3C_CONSUMING;
+ }
+ }
+ return super.getStreamability(syntacticContext, allowExtensions, reasons);
+ }
+
+
+ /**
+ * Return the compiler of the Venn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new VennExpressionCompiler();
+ }
+//#endif
+
+
+}
+
diff --git a/sf/saxon/expr/XPathContext.java b/sf/saxon/expr/XPathContext.java
new file mode 100644
index 0000000..a2eafa8
--- /dev/null
+++ b/sf/saxon/expr/XPathContext.java
@@ -0,0 +1,307 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.instruct.ParameterSet;
+import net.sf.saxon.expr.sort.GroupIterator;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.regex.RegexIterator;
+import net.sf.saxon.trans.Mode;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.Rule;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.DateTimeValue;
+
+import java.util.Iterator;
+
+/**
+* This class represents a context in which an XPath expression is evaluated.
+*/
+
+public interface XPathContext {
+
+
+ /**
+ * Construct a new context as a copy of another. The new context is effectively added
+ * to the top of a stack, and contains a pointer to the previous context
+ * @return a new context, created as a copy of this context
+ */
+
+ public XPathContextMajor newContext();
+
+ /**
+ * Construct a new context without copying (used for the context in a function call)
+ * @return a new clean context
+ */
+
+ public XPathContextMajor newCleanContext();
+
+ /**
+ * Construct a new minor context. A minor context can only hold new values of the focus
+ * (currentIterator) and current output destination.
+ * @return a new minor context
+ */
+
+ public XPathContextMinor newMinorContext();
+
+ /**
+ * Get the local (non-tunnel) parameters that were passed to the current function or template
+ * @return a ParameterSet containing the local parameters
+ */
+
+ public ParameterSet getLocalParameters();
+
+ /**
+ * Get the tunnel parameters that were passed to the current function or template. This includes all
+ * active tunnel parameters whether the current template uses them or not.
+ * @return a ParameterSet containing the tunnel parameters
+ */
+
+ public ParameterSet getTunnelParameters();
+
+ /**
+ * Get the Controller. May return null when running outside XSLT or XQuery
+ * @return the controller for this query or transformation
+ */
+
+ /*@Nullable*/
+ public Controller getController();
+
+ /**
+ * Get the Configuration
+ * @return the Saxon configuration object
+ */
+
+ public Configuration getConfiguration();
+
+ /**
+ * Get the Name Pool
+ * @return the name pool
+ */
+
+ public NamePool getNamePool();
+
+ /**
+ * Set the calling XPathContext
+ * @param caller the XPathContext of the calling expression
+ */
+
+ public void setCaller(XPathContext caller);
+
+ /**
+ * Get the calling XPathContext (the next one down the stack). This will be null if unknown, or
+ * if the bottom of the stack has been reached.
+ * @return the XPathContext of the calling expression
+ */
+
+ public XPathContext getCaller();
+
+ /**
+ * Set a new sequence iterator.
+ * @param iter the current iterator. The context item, position, and size are determined by reference
+ * to the current iterator.
+ */
+
+ public void setCurrentIterator(SequenceIterator iter);
+
+ /**
+ * Get the current iterator.
+ * This encapsulates the context item, context position, and context size.
+ * @return the current iterator, or null if there is no current iterator
+ * (which means the context item, position, and size are undefined).
+ */
+
+ public SequenceIterator getCurrentIterator();
+
+ /**
+ * Get the context item
+ * @return the context item, or null if the context item is undefined
+ */
+
+ public Item getContextItem();
+ /**
+ * Get the context size (the position of the last item in the current node list)
+ * @return the context size
+ * @throws XPathException if the context position is undefined
+ */
+
+ public int getLast() throws XPathException;
+
+ /**
+ * Determine whether the context position is the same as the context size
+ * that is, whether position()=last(). In many cases this has better performance
+ * than a direct comparison, because it does not require reading to the end of the
+ * sequence.
+ * @return true if the context position is the same as the context size.
+ */
+
+ public boolean isAtLast() throws XPathException;
+
+ /**
+ * Get a named collation.
+ * <p>Note: although collations are defined in the specification as being part of the static
+ * context, Saxon assumes that all available collations have global scope for a transformation
+ * or query, so that a collation URI can be translated into an actual collation without knowing
+ * where in the source code the collation URI was used. The default collation, however, can vary
+ * in different parts of a query or stylesheet, and expressions using the default collation must
+ * therefore get it from the static context.</p>
+ * @param name the name (URI) of the required collation
+ * @return a StringCollator representing the collation
+ * @throws XPathException if the collation is not recognized
+ */
+
+ public StringCollator getCollation(String name) throws XPathException;
+
+ /**
+ * Use local parameter. This is called when a local xsl:param element is processed.
+ * If a parameter of the relevant name was supplied, it is bound to the xsl:param element.
+ * Otherwise the method returns false, so the xsl:param default will be evaluated
+ * @param parameterId Globally-unique parameter identifier
+ * @param slotNumber Slot number of the parameter within the stack frame of the called template
+ * @param isTunnel True if a tunnel parameter is required, else false
+ * @return ParameterSet.NOT_SUPPLIED, ParameterSet.SUPPLIED, or ParameterSet.SUPPLIED_AND_CHECKED
+ */
+
+ public int useLocalParameter(
+ int parameterId, int slotNumber, boolean isTunnel) throws XPathException;
+
+ /**
+ * Get a reference to the local stack frame for variables. Note that it's
+ * the caller's job to make a local copy of this. This is used for creating
+ * a Closure containing a retained copy of the variables for delayed evaluation.
+ * @return array of variables.
+ */
+
+ public StackFrame getStackFrame();
+
+ /**
+ * Get the value of a local variable, identified by its slot number
+ * @param slotnumber the slot number allocated at compile time to the variable,
+ * which identifies its position within the local stack frame
+ * @return the value of the variable.
+ */
+
+ public Sequence evaluateLocalVariable(int slotnumber);
+
+ /**
+ * Set the value of a local variable, identified by its slot number
+ * @param slotnumber the slot number allocated at compile time to the variable,
+ * which identifies its position within the local stack frame
+ * @param value the value of the variable
+ */
+
+ public void setLocalVariable(int slotnumber, Sequence value);
+
+ /**
+ * Set a new output destination, supplying the output format details. <BR>
+ * Note that it is the caller's responsibility to close the Writer after use.
+ *
+ * @throws XPathException if any dynamic error occurs; and
+ * specifically, if an attempt is made to switch to a final output
+ * destination while writing a temporary tree or sequence
+ * @param receiver the new output destination
+ * @param options options for schema-validation of the output stream. May be null
+ * if validation is not required (i.e. Validation.PRESERVE)
+ */
+
+ public void changeOutputDestination(Receiver receiver, /*@Nullable*/ ParseOptions options) throws XPathException;
+
+
+ /**
+ * Ask whether the XSLT output state is "temporary" or "final"
+ * @return true to set temporary output state; false to set final output state
+ */
+
+ public boolean isTemporaryOutputState();
+
+ /**
+ * Change the SequenceReceiver to which output is written
+ * @param receiver the SequenceReceiver to be used
+ */
+
+ public void setReceiver(SequenceReceiver receiver);
+
+ /**
+ * Get the Receiver to which output is currently being written.
+ * @return the current SequenceReceiver
+ */
+ public SequenceReceiver getReceiver();
+
+ /**
+ * Get the current mode.
+ * @return the current mode
+ */
+
+ public Mode getCurrentMode();
+
+ /**
+ * Get the current template rule. This is used to support xsl:apply-imports and xsl:next-match
+ * @return the current template rule
+ */
+
+ public Rule getCurrentTemplateRule();
+
+ /**
+ * Get the current group iterator. This supports the current-group() and
+ * current-grouping-key() functions in XSLT 2.0
+ * @return the current grouped collection
+ */
+
+ public GroupIterator getCurrentGroupIterator();
+
+ /**
+ * Get the current regex iterator. This supports the functionality of the regex-group()
+ * function in XSLT 2.0.
+ * @return the current regular expressions iterator
+ */
+
+ public RegexIterator getCurrentRegexIterator();
+
+ /**
+ * Get the current date and time
+ * @return the current date and time. All calls within a single query or transformation
+ * will return the same value
+ */
+
+ public DateTimeValue getCurrentDateTime() throws NoDynamicContextException;
+
+ /**
+ * Get the implicit timezone
+ * @return the implicit timezone. This will be the timezone of the current date and time, and
+ * all calls within a single query or transformation will return the same value. The result is
+ * expressed as an offset from UTC in minutes.
+ */
+
+ public int getImplicitTimezone() throws NoDynamicContextException;
+
+ /**
+ * Get the context stack. This method returns an iterator whose items are instances of
+ * {@link net.sf.saxon.trace.ContextStackFrame}, starting with the top-most stackframe and
+ * ending at the point the query or transformation was invoked by a calling application.
+ * @return an iterator over a copy of the run-time call stack
+ */
+
+ public Iterator iterateStackFrames();
+
+ /**
+ * Get the current exception (in saxon:catch)
+ * @return the current exception, or null if there is none defined
+ */
+
+ public XPathException getCurrentException();
+
+ public void notifyChildThreads() throws XPathException;
+
+}
+
diff --git a/sf/saxon/expr/XPathContextMajor.java b/sf/saxon/expr/XPathContextMajor.java
new file mode 100644
index 0000000..ac0047e
--- /dev/null
+++ b/sf/saxon/expr/XPathContextMajor.java
@@ -0,0 +1,662 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.ParameterSet;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.instruct.UserFunction;
+import net.sf.saxon.expr.sort.GroupIterator;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.regex.RegexIterator;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.Mode;
+import net.sf.saxon.trans.Rule;
+import net.sf.saxon.trans.RuleManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * This class represents a "major context" in which an XPath expression is evaluated:
+ * a "major context" object allows all aspects of the dynamic context to change, whereas
+ * a "minor context" only allows changes to the focus and the destination for push output.
+ */
+
+public class XPathContextMajor extends XPathContextMinor {
+
+ private ParameterSet localParameters;
+ private ParameterSet tunnelParameters;
+ /*@Nullable*/ private UserFunction tailCallFunction;
+ private Mode currentMode;
+ /*@Nullable*/ private Rule currentTemplate;
+ private GroupIterator currentGroupIterator;
+ private RegexIterator currentRegexIterator;
+ boolean isTemporaryDestination = false;
+ Object origin;
+ private ThreadManager threadManager = null;
+
+ public class ThreadManager {
+ List<Thread> threads = new ArrayList<Thread>();
+ Exception errFound;
+
+ public void addThread(Thread t) {
+ threads.add(t);
+ }
+
+ public List<Thread> getChildThreads() {
+ return threads;
+ }
+
+
+ public void setException(Exception err) {
+ errFound = err;
+ }
+
+ public XPathException getException() {
+ if (errFound instanceof XPathException) {
+ return (XPathException) errFound;
+ }
+ throw new AssertionError(errFound);
+ }
+
+ }
+
+ /**
+ * Add a new thread to the list of child threads
+ * for this context. Create threadManager if null.
+ *
+ * @param t the Thread
+ */
+
+ public void addChildThread(Thread t) {
+ if (threadManager == null) {
+ threadManager = new ThreadManager();
+ }
+ synchronized (threadManager.threads) {
+ threadManager.addThread(t);
+ }
+ }
+
+
+ public ThreadManager getThreadManager() {
+ return threadManager;
+ }
+
+ public void createThreadManager() {
+ threadManager = new ThreadManager();
+ }
+
+ /**
+ * Check for running child threads and wait for them
+ * to complete and join to main thread.
+ * Throw any exception caught in any of the running threads
+ */
+ public void notifyChildThreads() throws XPathException {
+// if (getCaller() != null) {
+// getCaller().notifyChildThreads();
+// }
+
+ if (threadManager != null) {
+
+ List<Thread> childThreads = threadManager.getChildThreads();
+ while (!childThreads.isEmpty()) {
+
+ try {
+
+ if (threadManager.errFound != null) {
+ throw threadManager.getException();
+ }
+
+ childThreads.get(0).join();
+ synchronized (threadManager.threads) {
+ childThreads.remove(0);
+ }
+
+ } catch (InterruptedException e) {
+ throw new AssertionError(e);
+ } catch (IndexOutOfBoundsException e) {
+
+ }
+
+
+ }
+ if (threadManager.errFound != null) {
+ XPathException err = threadManager.getException();
+ threadManager.errFound = null;
+ throw err;
+ }
+ // threadManager = null;
+ }
+ }
+
+
+ /**
+ * Constructor should only be called by the Controller,
+ * which acts as a XPathContext factory.
+ *
+ * @param controller the Controller
+ */
+
+ public XPathContextMajor(Controller controller) {
+ this.controller = controller;
+ stackFrame = StackFrame.EMPTY;
+ origin = controller;
+ }
+
+
+ /**
+ * Private Constructor
+ */
+
+ private XPathContextMajor() {
+ }
+
+ /**
+ * Constructor for use in free-standing Java applications.
+ *
+ * @param item the item to use as the initial context item. If this is null,
+ * the comtext item is initially undefined (which will cause a dynamic error
+ * if it is referenced).
+ * @param exec the Executable
+ */
+
+ public XPathContextMajor(Item item, Executable exec) {
+ controller = new Controller(exec.getConfiguration(), exec);
+ if (item != null) {
+ UnfailingIterator iter = SingletonIterator.makeIterator(item);
+ iter.next();
+ currentIterator = iter;
+ last = new LastValue(1);
+ }
+ origin = controller;
+ }
+
+ /**
+ * Constructor for use in free-standing Java applications.
+ *
+ * @param item the item to use as the initial context item. If this is null,
+ * the comtext item is initially undefined (which will cause a dynamic error
+ * if it is referenced).
+ * @param config the Saxon Configuration
+ * @deprecated since 9.0 - use {@link #XPathContextMajor(Item, Executable)}
+ */
+
+ public XPathContextMajor(Item item, Configuration config) {
+ // No longer used internally but retained for backwards compatibility (8.8)
+ Executable exec = new Executable(config);
+ exec.setHostLanguage(Configuration.JAVA_APPLICATION, true);
+ controller = new Controller(config, exec);
+ if (item != null) {
+ UnfailingIterator iter = SingletonIterator.makeIterator(item);
+ iter.next();
+ currentIterator = iter;
+ last = new LastValue(1);
+ }
+ origin = controller;
+ }
+
+ /**
+ * Construct a new context as a copy of another. The new context is effectively added
+ * to the top of a stack, and contains a pointer to the previous context. The
+ */
+
+ public XPathContextMajor newContext() {
+ XPathContextMajor c = new XPathContextMajor();
+ c.controller = controller;
+ c.currentIterator = currentIterator;
+ c.stackFrame = stackFrame;
+ c.localParameters = localParameters;
+ c.tunnelParameters = tunnelParameters;
+ c.last = last;
+ c.currentReceiver = currentReceiver;
+ c.isTemporaryDestination = isTemporaryDestination;
+ c.currentMode = currentMode;
+ c.currentTemplate = currentTemplate;
+ c.currentRegexIterator = currentRegexIterator;
+ c.currentGroupIterator = currentGroupIterator;
+ c.caller = this;
+ c.tailCallFunction = null;
+ c.threadManager = threadManager;
+ return c;
+ }
+
+ /**
+ * Create a new "major" context (one that is capable of holding a stack frame with local variables
+ *
+ * @param prev the previous context (the one causing the new context to be created)
+ * @return the new major context
+ */
+
+ public static XPathContextMajor newContext(XPathContextMinor prev) {
+ XPathContextMajor c = new XPathContextMajor();
+ XPathContext p = prev;
+ while (!(p instanceof XPathContextMajor)) {
+ p = p.getCaller();
+ }
+ c.controller = p.getController();
+ c.currentIterator = prev.getCurrentIterator();
+ c.stackFrame = prev.getStackFrame();
+ c.localParameters = p.getLocalParameters();
+ c.tunnelParameters = p.getTunnelParameters();
+ c.last = prev.last;
+ c.currentReceiver = prev.currentReceiver;
+ c.isTemporaryDestination = ((XPathContextMajor) p).isTemporaryDestination;
+ c.currentMode = p.getCurrentMode();
+ c.currentTemplate = p.getCurrentTemplateRule();
+ c.currentRegexIterator = p.getCurrentRegexIterator();
+ c.currentGroupIterator = p.getCurrentGroupIterator();
+ c.caller = prev;
+ c.tailCallFunction = null;
+ c.threadManager = ((XPathContextMajor) p).threadManager;
+ return c;
+ }
+
+ /**
+ * Make a copy of the supplied context for use in a new thread (typically for
+ * an asynchronous xsl:result-document
+ * @param prev the context to be copied
+ * @return the copy of the context
+ */
+
+ public static XPathContextMajor newThreadContext(XPathContextMinor prev) {
+ XPathContextMajor c = newContext(prev);
+ c.stackFrame = prev.stackFrame.copy();
+ return c;
+ }
+
+ /**
+ * Get the local parameters for the current template call.
+ *
+ * @return the supplied parameters
+ */
+
+ public ParameterSet getLocalParameters() {
+ if (localParameters == null) {
+ localParameters = new ParameterSet();
+ }
+ return localParameters;
+ }
+
+ /**
+ * Set the local parameters for the current template call.
+ *
+ * @param localParameters the supplied parameters
+ */
+
+ public void setLocalParameters(ParameterSet localParameters) {
+ this.localParameters = localParameters;
+ }
+
+ /**
+ * Get the tunnel parameters for the current template call.
+ *
+ * @return the supplied tunnel parameters
+ */
+
+ public ParameterSet getTunnelParameters() {
+ return tunnelParameters;
+ }
+
+ /**
+ * Set the tunnel parameters for the current template call.
+ *
+ * @param tunnelParameters the supplied tunnel parameters
+ */
+
+ public void setTunnelParameters(ParameterSet tunnelParameters) {
+ this.tunnelParameters = tunnelParameters;
+ }
+
+ /**
+ * Set the creating expression (for use in diagnostics). The origin is generally set to "this" by the
+ * object that creates the new context. It's up to the debugger to determine whether this information
+ * is useful. The object will either be an {@link Expression}, allowing information
+ * about the calling instruction to be obtained, or null.
+ */
+
+ public void setOrigin(InstructionInfo expr) {
+ origin = expr;
+ }
+
+ /**
+ * Set the type of creating expression (for use in diagnostics). When a new context is created, either
+ * this method or {@link XPathContextMajor#setOrigin} should be called.
+ *
+ * @param loc The originating location: the argument must be one of the integer constants in class
+ * {@link net.sf.saxon.trace.Location}
+ */
+
+ public void setOriginatingConstructType(int loc) {
+ origin = Integer.valueOf(loc);
+ }
+
+ /**
+ * Get the type of location from which this context was created.
+ */
+
+ public int getOriginatingConstructType() {
+ if (origin == null) {
+ return -1;
+ }
+ if (origin instanceof Expression) {
+ if (origin instanceof SlashExpression) {
+ return Location.PATH_EXPRESSION;
+ }
+ return ((Expression) origin).getConstructType();
+ } else if (origin instanceof Integer) {
+ return ((Integer) origin).intValue();
+ } else if (origin instanceof InstructionInfo) {
+ return ((InstructionInfo) origin).getConstructType();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Get information about the creating expression or other construct.
+ */
+
+ public InstructionInfo getOrigin() {
+ if (origin instanceof InstructionInfo) {
+ return (InstructionInfo) origin;
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Set the local stack frame. This method is used when creating a Closure to support
+ * delayed evaluation of expressions. The "stack frame" is actually on the Java heap, which
+ * means it can survive function returns and the like.
+ *
+ * @param map the SlotManager, which holds static details of the allocation of variables to slots
+ * @param variables the array of "slots" to hold the actual variable values. This array will be
+ * copied if it is too small to hold all the variables defined in the SlotManager
+ */
+
+ public void setStackFrame(SlotManager map, Sequence[] variables) {
+ stackFrame = new StackFrame(map, variables);
+ if (map != null && variables.length != map.getNumberOfVariables()) {
+ if (variables.length > map.getNumberOfVariables()) {
+ throw new IllegalStateException(
+ "Attempting to set more local variables (" + variables.length +
+ ") than the stackframe can accommodate (" + map.getNumberOfVariables() + ")");
+ }
+ stackFrame.slots = new Sequence[map.getNumberOfVariables()];
+ System.arraycopy(variables, 0, stackFrame.slots, 0, variables.length);
+ }
+ }
+
+ /**
+ * Reset the stack frame variable map, while reusing the StackFrame object itself. This
+ * is done on a tail call to a different function
+ *
+ * @param map the SlotManager representing the stack frame contents
+ * @param numberOfParams the number of parameters required on the new stack frame
+ */
+
+ public void resetStackFrameMap(SlotManager map, int numberOfParams) {
+ stackFrame.map = map;
+ if (stackFrame.slots.length != map.getNumberOfVariables()) {
+ Sequence[] v2 = new Sequence[map.getNumberOfVariables()];
+ System.arraycopy(stackFrame.slots, 0, v2, 0, numberOfParams);
+ stackFrame.slots = v2;
+ } else {
+ // not strictly necessary
+ Arrays.fill(stackFrame.slots, numberOfParams, stackFrame.slots.length, null);
+ }
+ }
+
+ /**
+ * Get a all the variables in the stack frame
+ *
+ * @return an array holding all the variables, each referenceable by its slot number
+ */
+
+ public Sequence[] getAllVariableValues() {
+ return stackFrame.getStackFrameValues();
+ }
+
+ /**
+ * Overwrite all the variables in the stack frame
+ *
+ * @param values an array holding all the variables, each referenceable by its slot number;
+ * the caller must ensure this is the correct length (and valid in other ways)
+ */
+
+ public void resetAllVariableValues(Sequence[] values) {
+ stackFrame.setStackFrameValues(values);
+ }
+
+ /**
+ * Overwrite all the parameters in the stack frame. (Used from compiled bytecode)
+ *
+ * @param values an array holding all the parameters, each referenceable by its slot number;
+ * the caller must ensure this is the correct length (and valid in other ways)
+ */
+
+ public void resetParameterValues(Sequence[] values) {
+ System.arraycopy(values, 0, stackFrame.slots, 0, values.length);
+ }
+
+ /**
+ * Reset the local stack frame. This method is used when processing a tail-recursive function.
+ * Instead of the function being called recursively, the parameters are set to new values and the
+ * function body is evaluated repeatedly
+ *
+ * @param fn the user function being called using tail recursion
+ * @param variables the parameter to be supplied to the user function
+ */
+
+ public void requestTailCall(UserFunction fn, Sequence[] variables) {
+ if (variables.length > stackFrame.slots.length) {
+ Sequence[] v2 = new Sequence[fn.getStackFrameMap().getNumberOfVariables()];
+ System.arraycopy(variables, 0, v2, 0, variables.length);
+ stackFrame.slots = v2;
+ } else {
+ System.arraycopy(variables, 0, stackFrame.slots, 0, variables.length);
+ }
+ tailCallFunction = fn;
+ }
+
+
+ /**
+ * Determine whether the body of a function is to be repeated, due to tail-recursive function calls
+ *
+ * @return null if no tail call has been requested, or the name of the function to be called otherwise
+ */
+
+ public UserFunction getTailCallFunction() {
+ UserFunction fn = tailCallFunction;
+ tailCallFunction = null;
+ return fn;
+ }
+
+ /**
+ * Create a new stack frame for local variables, using the supplied SlotManager to
+ * define the allocation of slots to individual variables
+ *
+ * @param map the SlotManager for the new stack frame
+ */
+ public void openStackFrame(SlotManager map) {
+ int numberOfSlots = map.getNumberOfVariables();
+ if (numberOfSlots == 0) {
+ stackFrame = StackFrame.EMPTY;
+ } else {
+ stackFrame = new StackFrame(map, new Sequence[numberOfSlots]);
+ }
+ }
+
+ /**
+ * Create a new stack frame large enough to hold a given number of local variables,
+ * for which no stack frame map is available. This is used in particular when evaluating
+ * match patterns of template rules.
+ *
+ * @param numberOfVariables The number of local variables to be accommodated.
+ */
+
+ public void openStackFrame(int numberOfVariables) {
+ stackFrame = new StackFrame(new SlotManager(numberOfVariables),
+ new Sequence[numberOfVariables]);
+ }
+
+ /**
+ * Set the current mode.
+ *
+ * @param mode the new current mode
+ */
+
+ public void setCurrentMode(Mode mode) {
+ this.currentMode = mode;
+ }
+
+ /**
+ * Get the current mode.
+ *
+ * @return the current mode. May return null if the current mode is the default mode.
+ */
+
+ public Mode getCurrentMode() {
+ Mode m = currentMode;
+ if (m == null) {
+ RuleManager rm = getController().getRuleManager();
+ if (rm != null) {
+ return rm.getUnnamedMode();
+ } else {
+ return null;
+ }
+ } else {
+ return m;
+ }
+ }
+
+ /**
+ * Set the current template. This is used to support xsl:apply-imports. The caller
+ * is responsible for remembering the previous current template and resetting it
+ * after use.
+ *
+ * @param rule the current template rule, or null to indicate that there is no current template rule
+ */
+
+ public void setCurrentTemplateRule(/*@Nullable*/ Rule rule) {
+ this.currentTemplate = rule;
+ }
+
+ /**
+ * Get the current template. This is used to support xsl:apply-imports
+ *
+ * @return the current template
+ */
+
+ public Rule getCurrentTemplateRule() {
+ return currentTemplate;
+ }
+
+ /**
+ * Set the current grouping iterator. This supports the current-group() and
+ * current-grouping-key() functions in XSLT 2.0
+ *
+ * @param iterator the new current GroupIterator
+ */
+
+ public void setCurrentGroupIterator(GroupIterator iterator) {
+ this.currentGroupIterator = iterator;
+ }
+
+ /**
+ * Get the current group iterator. This supports the current-group() and
+ * current-grouping-key() functions in XSLT 2.0
+ *
+ * @return the current grouped collection
+ */
+
+ public GroupIterator getCurrentGroupIterator() {
+ return currentGroupIterator;
+ }
+
+ /**
+ * Set the current regex iterator. This supports the functionality of the regex-group()
+ * function in XSLT 2.0.
+ *
+ * @param currentRegexIterator the current regex iterator
+ */
+
+ public void setCurrentRegexIterator(RegexIterator currentRegexIterator) {
+ this.currentRegexIterator = currentRegexIterator;
+ }
+
+ /**
+ * Get the current regex iterator. This supports the functionality of the regex-group()
+ * function in XSLT 2.0.
+ *
+ * @return the current regular expressions iterator
+ */
+
+ public RegexIterator getCurrentRegexIterator() {
+ return currentRegexIterator;
+ }
+
+ /**
+ * Use local parameter. This is called when a local xsl:param element is processed.
+ * If a parameter of the relevant name was supplied, it is bound to the xsl:param element.
+ * Otherwise the method returns false, so the xsl:param default will be evaluated
+ *
+ * @param parameterId
+ * @param slotNumber
+ * @param isTunnel True if a tunnel parameter is required, else false @return ParameterSet.NOT_SUPPLIED, ParameterSet.SUPPLIED, or ParameterSet.SUPPLIED_AND_CHECKED
+ */
+
+ public int useLocalParameter(
+ int parameterId, int slotNumber, boolean isTunnel) throws XPathException {
+
+ ParameterSet params = (isTunnel ? getTunnelParameters() : localParameters);
+ if (params == null) {
+ return ParameterSet.NOT_SUPPLIED;
+ }
+ int index = params.getIndex(parameterId);
+ if (index < 0) {
+ return ParameterSet.NOT_SUPPLIED;
+ }
+ Sequence val = params.getValue(index);
+ stackFrame.slots[slotNumber] = val;
+ boolean checked = params.isTypeChecked(index);
+ return (checked ? ParameterSet.SUPPLIED_AND_CHECKED : ParameterSet.SUPPLIED);
+ }
+
+ /**
+ * Set the XSLT output state to "temporary" or "final"
+ *
+ * @param temporary set to true to set temporary output state; false to set final output state
+ */
+
+ public void setTemporaryOutputState(boolean temporary) {
+ isTemporaryDestination = temporary;
+ }
+
+ /**
+ * Ask whether the XSLT output state is "temporary" or "final"
+ *
+ * @return true to set temporary output state; false to set final output state
+ */
+
+ public boolean isTemporaryOutputState() {
+ return isTemporaryDestination;
+ }
+
+}
+
diff --git a/sf/saxon/expr/XPathContextMinor.java b/sf/saxon/expr/XPathContextMinor.java
new file mode 100644
index 0000000..1bb0867
--- /dev/null
+++ b/sf/saxon/expr/XPathContextMinor.java
@@ -0,0 +1,501 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.*;
+import net.sf.saxon.expr.instruct.ParameterSet;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.GroupIterator;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.regex.RegexIterator;
+import net.sf.saxon.trace.ContextStackIterator;
+import net.sf.saxon.trans.Mode;
+import net.sf.saxon.trans.Rule;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.value.DateTimeValue;
+
+import java.util.Iterator;
+
+/**
+ * This class represents a minor change in the dynamic context in which an XPath expression is evaluated:
+ * a "major context" object allows all aspects of the dynamic context to change, whereas
+ * a "minor context" only allows changes to the focus and the destination for push output.
+*/
+
+public class XPathContextMinor implements XPathContext {
+
+ Controller controller;
+ SequenceIterator currentIterator;
+ /*@Nullable*/ LastValue last = null;
+ SequenceReceiver currentReceiver;
+
+ XPathContext caller = null;
+ protected StackFrame stackFrame;
+ //Object origin = null;
+ XPathException currentException;
+
+ /**
+ * Private Constructor
+ */
+
+ protected XPathContextMinor() {
+ }
+
+ /**
+ * Construct a new context as a copy of another. The new context is effectively added
+ * to the top of a stack, and contains a pointer to the previous context
+ */
+
+ public XPathContextMajor newContext() {
+ return XPathContextMajor.newContext(this);
+ }
+
+ /**
+ * Construct a new context as a copy of another. The new context is effectively added
+ * to the top of a stack, and contains a pointer to the previous context
+ */
+
+ public XPathContextMinor newMinorContext() {
+ XPathContextMinor c = new XPathContextMinor();
+ //System.err.println("NEW MINOR CONTEXT " + c);
+ c.controller = controller;
+ c.caller = this;
+ c.currentIterator = currentIterator;
+ c.currentReceiver = currentReceiver;
+ c.last = last;
+ c.stackFrame = stackFrame;
+ c.currentException = currentException;
+ return c;
+ }
+
+ /**
+ * Set the calling XPathContext
+ */
+
+ public void setCaller(XPathContext caller) {
+ this.caller = caller;
+ }
+
+ /**
+ * Construct a new context without copying (used for the context in a function call)
+ */
+
+ public XPathContextMajor newCleanContext() {
+ XPathContextMajor c = new XPathContextMajor(getController());
+ c.setCaller(this);
+ return c;
+ }
+
+ /**
+ * Get the local parameters for the current template call.
+ * @return the supplied parameters
+ */
+
+ public ParameterSet getLocalParameters() {
+ return getCaller().getLocalParameters();
+ }
+
+ /**
+ * Get the tunnel parameters for the current template call.
+ * @return the supplied tunnel parameters
+ */
+
+ public ParameterSet getTunnelParameters() {
+ return getCaller().getTunnelParameters();
+ }
+
+ /**
+ * Get the Controller. May return null when running outside XSLT or XQuery
+ */
+
+ public final Controller getController() {
+ return controller;
+ }
+
+ /**
+ * Get the Configuration
+ */
+
+ public final Configuration getConfiguration() {
+ return controller.getConfiguration();
+ }
+
+ /**
+ * Get the Name Pool
+ */
+
+ public final NamePool getNamePool() {
+ return controller.getNamePool();
+ }
+
+ /**
+ * Get a NameChecker for checking names against the XML 1.0 or XML 1.1 specification as appropriate
+ * @return the appropriate name checker
+ */
+
+ public final NameChecker getNameChecker() {
+ return controller.getConfiguration().getNameChecker();
+ }
+
+ /**
+ * Get the calling XPathContext (the next one down the stack). This will be null if unknown, or
+ * if the bottom of the stack has been reached.
+ */
+
+ public final XPathContext getCaller() {
+ return caller;
+ }
+
+ /**
+ * Set a new sequence iterator.
+ */
+
+ public void setCurrentIterator(SequenceIterator iter) {
+ currentIterator = iter;
+ last = new LastValue(-1);
+ }
+
+ /**
+ * Get the current iterator.
+ * This encapsulates the context item, context position, and context size.
+ * @return the current iterator, or null if there is no current iterator
+ * (which means the context item, position, and size are undefined).
+ */
+
+ public final SequenceIterator getCurrentIterator() {
+ return currentIterator;
+ }
+
+ /**
+ * Get the context item
+ * @return the context item, or null if the context item is undefined
+ */
+
+ public final Item getContextItem() {
+ if (currentIterator==null) {
+ return null;
+ }
+ return currentIterator.current();
+ }
+
+ /**
+ * Get the context size (the position of the last item in the current node list)
+ * @return the context size
+ * @throws XPathException if the context position is undefined
+ */
+
+ public final int getLast() throws XPathException {
+ if (currentIterator == null) {
+ XPathException e = new XPathException("The context item is absent, so last() is undefined");
+ e.setXPathContext(this);
+ e.setErrorCode("XPDY0002");
+ throw e;
+ }
+ if (last.value >= 0) {
+ return last.value;
+ }
+ if ((currentIterator.getProperties() & SequenceIterator.LAST_POSITION_FINDER) == 0) {
+ SequenceIterator another = currentIterator.getAnother();
+ int count = 0;
+ while (another.next() != null) {
+ count++;
+ }
+ return (last.value = count);
+ } else {
+ return (last.value = ((LastPositionFinder)currentIterator).getLength());
+ }
+ }
+
+ /**
+ * Determine whether the context position is the same as the context size
+ * that is, whether position()=last()
+ */
+
+ public final boolean isAtLast() throws XPathException {
+ if ((currentIterator.getProperties() & SequenceIterator.LOOKAHEAD) != 0) {
+ return !((LookaheadIterator)currentIterator).hasNext();
+ }
+ return currentIterator.position() == getLast();
+ }
+
+ /**
+ * Get a named collation
+ * @throws XPathException if the collation is not recognized
+ */
+
+ public final StringCollator getCollation(String name) throws XPathException {
+ if (name.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
+ return CodepointCollator.getInstance();
+ }
+ StringCollator collation = null;
+ if (controller != null) {
+ collation = controller.getExecutable().getNamedCollation(name);
+
+ if (collation == null) {
+ Configuration config = controller.getConfiguration();
+ collation = config.getCollationURIResolver().resolve(name, null, config);
+ }
+ }
+ if (collation==null) {
+ XPathException e = new XPathException("Unknown collation " + name);
+ e.setErrorCode("FOCH0002"); // Caller may have to change this
+ e.setXPathContext(this);
+ throw e;
+ }
+ return collation;
+ }
+
+ /**
+ * Get a reference to the local stack frame for variables. Note that it's
+ * the caller's job to make a local copy of this. This is used for creating
+ * a Closure containing a retained copy of the variables for delayed evaluation.
+ * @return array of variables.
+ */
+
+ public StackFrame getStackFrame() {
+ return stackFrame;
+ }
+
+
+ /**
+ * Get the value of a local variable, identified by its slot number
+ */
+
+ public final Sequence evaluateLocalVariable(int slotnumber) {
+ return stackFrame.slots[slotnumber];
+ }
+
+ /**
+ * Set the value of a local variable, identified by its slot number
+ */
+
+ public final void setLocalVariable(int slotnumber, Sequence value) {
+ try {
+ stackFrame.slots[slotnumber] = value;
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new AssertionError("Internal error: invalid slot number for local variable " +
+ (slotnumber == -999 ? "(No slot allocated)" : "(" + slotnumber + ")"));
+ }
+ }
+
+ public synchronized void notifyChildThreads() throws XPathException {
+ getCaller().notifyChildThreads();
+ }
+
+ /**
+ * Set a new output destination, supplying the output format details. <BR>
+ * This affects all further output until resetOutputDestination() is called. Note that
+ * it is the caller's responsibility to close the Writer after use.
+ *
+ * @exception XPathException if any dynamic error occurs; and
+ * specifically, if an attempt is made to switch to a final output
+ * destination while writing a temporary tree or sequence @param isFinal true if the destination is a final result tree
+ * (either the principal output or a secondary result tree); false if @param validation Validation to be performed on the output document
+ * @param options options for validating the output stream; may be null
+ */
+
+ public void changeOutputDestination(Receiver receiver, ParseOptions options)
+ throws XPathException {
+// System.err.println("CHANGE OUTPUT DESTINATION new=" + receiver);
+ PipelineConfiguration pipe = receiver.getPipelineConfiguration();
+ ComplexContentOutputter out = new ComplexContentOutputter(pipe);
+ out.setHostLanguage(pipe.getHostLanguage());
+
+ // add a filter to remove duplicate namespaces
+
+ NamespaceReducer ne = new NamespaceReducer(receiver);
+ ne.setSystemId(receiver.getSystemId());
+ receiver = ne;
+
+ // add a validator to the pipeline if required
+
+ if (options != null && options.getSchemaValidationMode() != Validation.PRESERVE) {
+ Configuration config = controller.getConfiguration();
+ receiver = config.getDocumentValidator(ne, receiver.getSystemId(), options);
+ }
+
+ out.setReceiver(receiver);
+ currentReceiver = out;
+ }
+
+ /**
+ * Change the Receiver to which output is written
+ */
+
+ public void setReceiver(SequenceReceiver receiver) {
+// System.err.println("SET RECEIVER new=" + receiver);
+ currentReceiver = receiver;
+ }
+
+ /**
+ * Get the Receiver to which output is currently being written.
+ *
+ * @return the current Receiver
+ */
+ public final SequenceReceiver getReceiver() {
+// System.err.println("GET RECEIVER return=" + currentReceiver);
+ return currentReceiver;
+ }
+
+ /**
+ * Ask whether the XSLT output state is "temporary" or "final"
+ *
+ * @return true to set temporary output state; false to set final output state
+ */
+ public boolean isTemporaryOutputState() {
+ return caller.isTemporaryOutputState();
+ }
+
+ /**
+ * Use local parameter. This is called when a local xsl:param element is processed.
+ * If a parameter of the relevant name was supplied, it is bound to the xsl:param element.
+ * Otherwise the method returns false, so the xsl:param default will be evaluated
+ * @param parameterId
+ * @param slotNumber
+ * @param isTunnel True if a tunnel parameter is required, else false @return ParameterSet.NOT_SUPPLIED, ParameterSet.SUPPLIED, or ParameterSet.SUPPLIED_AND_CHECKED
+ */
+
+ public int useLocalParameter(
+ int parameterId, int slotNumber, boolean isTunnel) throws XPathException {
+ return getCaller().useLocalParameter(parameterId, slotNumber, isTunnel);
+ }
+
+ /**
+ * Get the current mode.
+ * @return the current mode
+ */
+
+ public Mode getCurrentMode() {
+ return getCaller().getCurrentMode();
+ }
+
+ /**
+ * Get the current template. This is used to support xsl:apply-imports
+ *
+ * @return the current template
+ */
+
+ public Rule getCurrentTemplateRule() {
+ // In a minor context, the current template rule is always null. This is a consequence
+ // of the way they are used.
+ return null;
+ //return getCaller().getCurrentTemplateRule();
+ }
+
+ /**
+ * Get the current group iterator. This supports the current-group() and
+ * current-grouping-key() functions in XSLT 2.0
+ * @return the current grouped collection
+ */
+
+ public GroupIterator getCurrentGroupIterator() {
+ return getCaller().getCurrentGroupIterator();
+ }
+
+ /**
+ * Get the current regex iterator. This supports the functionality of the regex-group()
+ * function in XSLT 2.0.
+ * @return the current regular expressions iterator
+ */
+
+ public RegexIterator getCurrentRegexIterator() {
+ return getCaller().getCurrentRegexIterator();
+ }
+
+ /**
+ * Get the current date and time for this query or transformation.
+ * All calls during one transformation return the same answer.
+ *
+ * @return Get the current date and time. This will deliver the same value
+ * for repeated calls within the same transformation
+ */
+
+ public DateTimeValue getCurrentDateTime() {
+ return controller.getCurrentDateTime();
+ }
+
+ /**
+ * Get the implicit timezone, as a positive or negative offset from UTC in minutes.
+ * The range is -14hours to +14hours
+ * @return the implicit timezone as an offset from UTC in minutes
+ */
+
+ public final int getImplicitTimezone() {
+ return controller.getImplicitTimezone();
+ }
+
+ /**
+ * Get the context stack. This method returns an iterator whose items are instances of
+ * {@link net.sf.saxon.trace.ContextStackFrame}, starting with the top-most stackframe and
+ * ending at the point the query or transformation was invoked by a calling application.
+ *
+ * @return an iterator over a copy of the run-time call stack
+ */
+
+ public Iterator iterateStackFrames() {
+ return new ContextStackIterator(this);
+ }
+
+ /**
+ * Set the current exception (in saxon:catch)
+ * @param exception the current exception
+ */
+
+ public void setCurrentException(XPathException exception) {
+ currentException = exception;
+ }
+
+ /**
+ * Get the current exception (in saxon:catch)
+ * @return the current exception, or null if there is none defined
+ */
+
+ public XPathException getCurrentException() {
+ return currentException;
+ }
+
+ // Note: consider eliminating this class. A new XPathContextMinor is created under two circumstances,
+ // (a) when the focus changes (i.e., a new current iterator), and (b) when the current
+ // receiver changes. We could handle these by maintaining a stack of iterators and a stack of
+ // receivers in the XPathContextMajor object. Adding a new iterator or receiver to the stack would
+ // generally be cheaper than creating the new XPathContextMinor object. The main difficulty (in the
+ // case of iterators) is knowing when to pop the stack: currently we rely on the garbage collector.
+ // We can only really do this when the iterator comes to its end, which is difficult to detect.
+ // Perhaps we should try to do static allocation, so that fixed slots are allocated for different
+ // minor-contexts within a Procedure, and a compiled expression that uses the focus knows which
+ // slot to look in.
+
+ // Investigated the above Sept 2008. On xmark, with a 100Mb input, the path expression
+ // count(site/people/person/watches/watch) takes just 13ms to execute (compared with 6500ms for building
+ // the tree). Only 6 context objects are created while doing this. This doesn't appear to be a productive
+ // area to look for new optimizations.
+
+ /**
+ * Container for cached value of the last() function.
+ * This is shared between all context objects that share the same current iterator.
+ * Introduced in 9.2 to handle the case where a new context is introduced when the current
+ * outputter changes, without changing the current iterator: in this case the cached value
+ * was being lost because each call on last() used a different context object.
+ */
+
+ protected static class LastValue {
+ public int value = 0;
+
+ public LastValue(int count) {
+ value = count;
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/Clause.java b/sf/saxon/expr/flwor/Clause.java
new file mode 100644
index 0000000..b74888b
--- /dev/null
+++ b/sf/saxon/expr/flwor/Clause.java
@@ -0,0 +1,166 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Binding;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.VariableReference;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.List;
+
+/**
+ * A "Clause" refers specifically to one of the clauses of a FLWOR expression, for example the "for"
+ * clause, the "let" clause, the "where" or "order by" clause. (The "return" clause, however, is not
+ * modelled as a Clause).
+ */
+public abstract class Clause {
+
+ public static final int FOR = 0;
+ public static final int LET = 1;
+ public static final int WINDOW = 2;
+ public static final int GROUPBYCLAUSE = 3;
+ public static final int COUNT = 4;
+ public static final int ORDERBYCLAUSE = 5;
+ public static final int WHERE = 6;
+ public static final int TRACE = 7;
+
+ private int locationId;
+
+ /**
+ * Get the location ID, which can be used in conjunction with a LocationProvider to determine
+ * the system ID and line number of the clause
+ * @return the location ID
+ */
+ public int getLocationId() {
+ return locationId;
+ }
+
+ /**
+ * Set the location ID, which can be used in conjunction with a LocationProvider to determine
+ * the system ID and line number of the clause
+ * @param locationId the location ID
+ */
+
+ public void setLocationId(int locationId) {
+ this.locationId = locationId;
+ }
+
+ /**
+ * Create a copy of this clause
+ * @return the copied clause
+ */
+
+ public abstract Clause copy();
+
+ /**
+ * Optimize any expressions contained within this clause
+ * @param visitor the ExpressionVisitor, providing access to static context information
+ * @param contextItemType the type of the context item
+ * @throws XPathException if any error is detected
+ */
+ public void optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {}
+
+ /**
+ * Type-check any expression contained within this clause
+ * @param visitor the ExpressionVisitor, providing access to static context information
+ * @throws XPathException if any error is detected
+ */
+
+ public void typeCheck(ExpressionVisitor visitor) throws XPathException {
+ }
+
+ /**
+ * Get a pull-mode tuple stream that implements the functionality of this clause, taking its
+ * input from another tuple stream which this clause modifies
+ *
+ * @param base the input tuple stream
+ * @param context the dynamic evaluation context
+ * @return the output tuple stream
+ */
+
+ public abstract TuplePull getPullStream(TuplePull base, XPathContext context);
+
+ /**
+ * Get a push-mode tuple stream that implements the functionality of this clause, supplying its
+ * output to another tuple stream
+ *
+ * @param destination the output tuple stream
+ * @param context the dynamic evaluation context
+ * @return the push tuple stream that implements the functionality of this clause of the FLWOR
+ * expression
+ */
+
+ public abstract TuplePush getPushStream(TuplePush destination, XPathContext context);
+
+ /**
+ * Process the subexpressions of this clause
+ * @param processor the expression processor used to process the subexpressions
+ * @throws XPathException if any error is detected
+ */
+
+ public abstract void processSubExpressions(ExpressionProcessor processor) throws XPathException;
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+
+ public abstract void explain(ExpressionPresenter out);
+
+ /**
+ * Get the variables bound by this clause
+ * @return the variable bindings
+ */
+
+ public LocalVariableBinding[] getRangeVariables() {
+ return new LocalVariableBinding[0];
+ }
+
+ /**
+ * Build a list of all references to a variables declared in this clause
+ * @param visitor the expression visitor
+ * @param binding a variable declared in this clause
+ * @param refs the list of variable references, initially empty, to which the method will append
+ */
+
+ public void gatherVariableReferences(final ExpressionVisitor visitor, Binding binding, List<VariableReference> refs){}
+
+ /**
+ * Determine whether the clause contains a reference to a local variable binding that cannot be inlined
+ * @param binding the binding for the local variable in question
+ * @return true if this clause uses the variable in a way that does not permit inlining
+ */
+
+ public boolean containsNonInlineableVariableReference(Binding binding) {
+ return false;
+ }
+
+ /**
+ * Supply improved type information to the expressions that contain references to the variables declared in this clause
+ * @param visitor the expression visitor
+ * @param references the list of variable references
+ * @param returnExpr the expression in the return clause
+ */
+
+ public void refineVariableType(final ExpressionVisitor visitor, List<VariableReference> references, Expression returnExpr){}
+
+ /**
+ * Get a keyword identifying what kind of clause this is
+ * @return the kind of clause
+ */
+
+ public abstract int getClauseKey();
+}
+
+
diff --git a/sf/saxon/expr/flwor/ClauseInfo.java b/sf/saxon/expr/flwor/ClauseInfo.java
new file mode 100644
index 0000000..c8a190f
--- /dev/null
+++ b/sf/saxon/expr/flwor/ClauseInfo.java
@@ -0,0 +1,212 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trace.Location;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A "trace" clause in a FLWOR expression, added by a TraceCodeInjector
+ */
+public class ClauseInfo implements InstructionInfo {
+
+ private Clause clause;
+ private Container container;
+ private NamespaceResolver nsResolver;
+
+ public ClauseInfo(Clause clause, Container container) {
+ this.clause = clause;
+ this.container = container;
+ }
+
+ /**
+ * Get the clause being traced
+ * @return the clause in the FLWOR expression to which this ClauseInfo relates
+ */
+
+ public Clause getClause() {
+ return clause;
+ }
+
+ /**
+ * Get the type of construct. This will either be the fingerprint of a standard XSLT instruction name
+ * (values in {@link net.sf.saxon.om.StandardNames}: all less than 1024)
+ * or it will be a constant in class {@link net.sf.saxon.trace.Location}.
+ *
+ * @return an integer identifying the kind of construct
+ */
+ public int getConstructType() {
+ return Location.CLAUSE_BASE + clause.getClauseKey();
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ *
+ * @return the QName of the object declared or manipulated by this instruction or expression
+ */
+ public StructuredQName getObjectName() {
+ LocalVariableBinding[] vars = clause.getRangeVariables();
+ if (vars != null && vars.length > 0) {
+ return vars[0].getVariableQName();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the namespace bindings from the static context of the clause
+ * @return a namespace resolver that reflects the in scope namespaces of the clause
+ */
+
+ public NamespaceResolver getNamespaceResolver() {
+ return nsResolver;
+ }
+
+ /**
+ * Set the namespace bindings from the static context of the clause
+ * @param nsResolver a namespace resolver that reflects the in scope namespaces of the clause
+ */
+
+ public void setNamespaceResolver(NamespaceResolver nsResolver) {
+ this.nsResolver = nsResolver;
+ }
+
+ /**
+ * Get the system identifier (URI) of the source stylesheet or query module containing
+ * the instruction. This will generally be an absolute URI. If the system
+ * identifier is not known, the method may return null. In some cases, for example
+ * where XML external entities are used, the correct system identifier is not
+ * always retained.
+ *
+ * @return the URI of the containing module
+ */
+ public String getSystemId() {
+ return container.getLocationProvider().getSystemId(clause.getLocationId());
+ }
+
+ /**
+ * Get the line number of the instruction in the source stylesheet module.
+ * If this is not known, or if the instruction is an artificial one that does
+ * not relate to anything in the source code, the value returned may be -1.
+ *
+ * @return the line number of the expression within the containing module
+ */
+ public int getLineNumber() {
+ return container.getLocationProvider().getLineNumber(clause.getLocationId());
+ }
+
+ /**
+ * Get the value of a particular property of the instruction. Properties
+ * of XSLT instructions are generally known by the name of the stylesheet attribute
+ * that defines them.
+ *
+ * @param name The name of the required property
+ * @return The value of the requested property, or null if the property is not available
+ */
+ public Object getProperty(String name) {
+ return null;
+ }
+
+ /**
+ * Get an iterator over all the properties available. The values returned by the iterator
+ * will be of type String, and each string can be supplied as input to the getProperty()
+ * method to retrieve the value of the property. The iterator may return properties whose
+ * value is null.
+ *
+ * @return an iterator over the properties.
+ */
+ public Iterator<String> getProperties() {
+ List<String> ls = Collections.emptyList();
+ return ls.iterator();
+ }
+
+ /**
+ * Get the URI of the document, entity, or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the URI of the document, XML entity or module. For a SourceLocationProvider this will
+ * be the URI of the document or entity (the URI that would be the base URI if there were no
+ * xml:base attributes). In other cases it may identify the query or stylesheet module currently
+ * being executed.
+ */
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ /**
+ * Get the line number within the document, entity or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the line number within the document, entity or module, or -1 if no information is available.
+ */
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ /**
+ * Get the column number within the document, entity, or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the column number within the document, entity, or module, or -1 if this is not available
+ */
+ public int getColumnNumber(long locationId) {
+ return -1;
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ * <p/>
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup
+ * triggering the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Return the column number where the current document event ends.
+ * This is one-based number of Java <code>char</code> values since
+ * the last line end.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of diagnostics;
+ * it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.
+ * For example, when lines contain combining character sequences, wide
+ * characters, surrogate pairs, or bi-directional text, the value may
+ * not correspond to the column in a text editor's display. </p>
+ * <p/>
+ * <p>The return value is an approximation of the column number
+ * in the document entity or external parsed entity where the
+ * markup triggering the event appears.</p>
+ * <p/>
+ * <p>If possible, the SAX driver should provide the line position
+ * of the first character after the text associated with the document
+ * event. The first column in each line is column 1.</p>
+ *
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return -1;
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/ExpressionProcessor.java b/sf/saxon/expr/flwor/ExpressionProcessor.java
new file mode 100644
index 0000000..34731e0
--- /dev/null
+++ b/sf/saxon/expr/flwor/ExpressionProcessor.java
@@ -0,0 +1,21 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ *
+ */
+public interface ExpressionProcessor {
+
+ public Expression processExpression(Expression expr) throws XPathException;
+
+}
+
diff --git a/sf/saxon/expr/flwor/FLWORExpression.java b/sf/saxon/expr/flwor/FLWORExpression.java
new file mode 100644
index 0000000..a1b9aad
--- /dev/null
+++ b/sf/saxon/expr/flwor/FLWORExpression.java
@@ -0,0 +1,962 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.FLWORExpressionCompiler;
+import net.sf.saxon.Controller;
+import net.sf.saxon.TypeCheckerEnvironment;
+import net.sf.saxon.event.SequenceOutputter;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.query.QueryModule;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.*;
+
+/**
+ * This class represents a FLWOR expression, evaluated using tuple streams
+ */
+public class FLWORExpression extends Expression {
+
+ /*@NotNull*/
+ public List<Clause> clauses;
+ /*@NotNull*/
+ public Expression returnClause;
+
+ public FLWORExpression(/*@NotNull*/ List<Clause> clauses, /*@NotNull*/ Expression returnClause) {
+ this.clauses = clauses;
+ this.returnClause = returnClause;
+ }
+
+ /**
+ * Get the list of clauses of the FLWOR expression, in the order they are written.
+ * This excludes the return clause
+ *
+ * @return the list of clauses
+ */
+ /*@NotNull*/
+ public List<Clause> getClauseList() {
+ return clauses;
+ }
+
+ private static boolean isLoopingClause(Clause c) {
+ return c.getClauseKey() == Clause.FOR || c.getClauseKey() == Clause.GROUPBYCLAUSE || c.getClauseKey() == Clause.WINDOW;
+ }
+
+ /**
+ * Get the return clause of the FLWOR expression
+ *
+ * @return the expression contained in the return clause
+ */
+
+ /*@NotNull*/
+ public Expression getReturnClause() {
+ return returnClause;
+ }
+
+ /**
+ * Determine whether a given variable binding belongs to this FLWOR expression
+ *
+ * @param binding the binding being sought
+ * @return true if this binding belongs to one of the clauses of this FLWOR expression
+ */
+
+ public boolean hasVariableBinding(Binding binding) {
+ for (Clause c : clauses) {
+ if (clauseHasBinding(c, binding)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean clauseHasBinding(Clause c, Binding binding) {
+ for (Binding b : c.getRangeVariables()) {
+ if (b == binding) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression
+ * rewriting
+ */
+ /*@NotNull*/
+ @Override
+ public Expression simplify(final ExpressionVisitor visitor) throws XPathException {
+ ExpressionProcessor simplifier = new ExpressionProcessor() {
+ public Expression processExpression(Expression expr) throws XPathException {
+ return visitor.simplify(expr);
+ }
+ };
+ for (Clause c : clauses) {
+ c.processSubExpressions(simplifier);
+ }
+ returnClause = visitor.simplify(returnClause);
+ return this;
+ }
+
+ /**
+ * Perform type checking of an expression and its subexpressions. This is the second phase of
+ * static optimization.
+ * <p/>
+ * <p>This checks statically that the operands of the expression have
+ * the correct type; if necessary it generates code to do run-time type checking or type
+ * conversion. A static type error is reported only if execution cannot possibly succeed, that
+ * is, if a run-time type error is inevitable. The call may return a modified form of the expression.</p>
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable. However, the types of such functions and
+ * variables may not be accurately known if they have not been explicitly declared.</p>
+ * <p/>
+ * <p>If the implementation returns a value other than "this", then it is required to ensure that
+ * the location information in the returned expression have been set up correctly.
+ * It should not rely on the caller to do this, although for historical reasons many callers do so.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten to perform necessary run-time type checks,
+ * and to perform other type-related optimizations
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+ /*@NotNull*/
+ @Override
+ public Expression typeCheck(final ExpressionVisitor visitor, final ExpressionVisitor.ContextItemType contextItemType)
+ throws XPathException {
+
+ ExpressionProcessor typeChecker = new ExpressionProcessor() {
+ public Expression processExpression(Expression expr) throws XPathException {
+ return visitor.typeCheck(expr, contextItemType);
+ }
+ };
+ for (int i = 0; i < clauses.size(); i++) {
+ clauses.get(i).processSubExpressions(typeChecker);
+ clauses.get(i).typeCheck(visitor);
+ LocalVariableBinding[] bindings = clauses.get(i).getRangeVariables();
+
+ for (Binding b : bindings) {
+ List references = new ArrayList();
+ for (int j = i; j < clauses.size(); j++) {
+ clauses.get(j).gatherVariableReferences(visitor, b, references);
+ }
+ ExpressionTool.gatherVariableReferences(returnClause, b, references);
+ clauses.get(i).refineVariableType(visitor, references, returnClause);
+ }
+ }
+ returnClause = visitor.typeCheck(returnClause, contextItemType);
+ return this;
+ }
+
+ /**
+ * Determine whether this expression implements its own method for static type checking
+ *
+ * @return true - this expression has a non-trivial implementation of the staticTypeCheck()
+ * method
+ */
+
+ public boolean implementsStaticTypeCheck() {
+ for (Clause c : clauses) {
+ switch (c.getClauseKey()) {
+ case Clause.LET:
+ case Clause.WHERE:
+ continue;
+ default:
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Static type checking for let expressions is delegated to the expression itself,
+ * and is performed on the "return" expression, to allow further delegation to the branches
+ * of a conditional
+ * @param req the required type
+ * @param backwardsCompatible true if backwards compatibility mode applies
+ * @param role the role of the expression in relation to the required type
+ * @param visitor an expression visitor
+ * @return the expression after type checking (perhaps augmented with dynamic type checking code)
+ * @throws XPathException if failures occur, for example if the static type of one branch of the conditional
+ * is incompatible with the required type
+ */
+
+ public Expression staticTypeCheck(SequenceType req,
+ boolean backwardsCompatible,
+ RoleLocator role, TypeCheckerEnvironment visitor)
+ throws XPathException {
+ // only called if implementsStaticTypeCheck() returns true
+ returnClause = TypeChecker.staticTypeCheck(returnClause, req, backwardsCompatible, role, visitor);
+ return this;
+ }
+
+ /**
+ * This method is required to refine the variabletype of the returnClause
+ * Repeated code in the Clause sub-classes. Room here for commoning-up code
+ * */
+
+
+ /**
+ * Determine the data type of the items returned by the expression.
+ *
+ * @param th the type hierarchy cache
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER,
+ * Type.NODE, or Type.ITEM (meaning not known at compile time)
+ */
+ /*@NotNull*/
+ @Override
+ public ItemType getItemType(TypeHierarchy th) {
+ return returnClause.getItemType(th);
+ }
+
+ /**
+ * Compute the static cardinality of this expression
+ *
+ * @return the computed cardinality, as one of the values {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_ONE},
+ * {@link net.sf.saxon.expr.StaticProperty#EXACTLY_ONE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ONE_OR_MORE},
+ * {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_MORE}
+ */
+ @Override
+ protected int computeCardinality() {
+ // Assume that simple cases, like a FLWOR whose clauses are all "let" clauses, will have been converted into something else.
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ /*@NotNull*/
+ @Override
+ public Iterator<Expression> iterateSubExpressions() {
+ final List<Expression> list = new ArrayList<Expression>(5);
+ ExpressionProcessor processor = new ExpressionProcessor() {
+ public Expression processExpression(Expression expr) {
+ list.add(expr);
+ return expr;
+ }
+ };
+ try {
+ for (Clause c : clauses) {
+ c.processSubExpressions(processor);
+ }
+ } catch (XPathException e) {
+ throw new IllegalStateException(e);
+ }
+ list.add(returnClause);
+ return list.iterator();
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ final List<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>(5);
+ ExpressionProcessor processor = new ExpressionProcessor() {
+ public Expression processExpression(Expression expr) {
+ list.add(new SubExpressionInfo(expr, true, false, NAVIGATION_CONTEXT));
+ return expr;
+ }
+ };
+ boolean foundLoopClause = false;
+ try {
+ for (Clause c : clauses) {
+ if (isLoopingClause(c)) {
+ foundLoopClause = true;
+ }
+ c.processSubExpressions(processor);
+ }
+ } catch (XPathException e) {
+ throw new IllegalStateException(e);
+ }
+ list.add(new SubExpressionInfo(returnClause, true, foundLoopClause, INHERITED_CONTEXT));
+ return list.iterator();
+
+ }
+
+ /**
+ * Check to ensure that this expression does not contain any inappropriate updating subexpressions.
+ * This check is overridden for those expressions that permit updating subexpressions.
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression has a non-permitted updateing subexpression
+ */
+
+ public void checkForUpdatingSubexpressions() throws XPathException {
+ ExpressionProcessor processor = new ExpressionProcessor() {
+ public Expression processExpression(Expression expr) throws XPathException {
+ expr.checkForUpdatingSubexpressions();
+ if (expr.isUpdatingExpression()) {
+ throw new XPathException(
+ "An updating expression cannot be used in a clause of a FLWOR expression", "XUST0001");
+ }
+ return expr;
+ }
+ };
+ for (Clause c : clauses) {
+ c.processSubExpressions(processor);
+ }
+ returnClause.checkForUpdatingSubexpressions();
+ }
+
+ /**
+ * Determine whether this is an updating expression as defined in the XQuery update specification
+ *
+ * @return true if this is an updating expression
+ */
+
+ @Override
+ public boolean isUpdatingExpression() {
+ return returnClause.isUpdatingExpression();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ @Override
+ public boolean replaceSubExpression(final Expression original, final Expression replacement) {
+ final List<Boolean> changed = new ArrayList<Boolean>();
+ ExpressionProcessor processor = new ExpressionProcessor() {
+ public Expression processExpression(Expression expr) {
+ if (expr == original) {
+ changed.add(Boolean.TRUE);
+ return replacement;
+ }
+ return expr;
+ }
+ };
+ try {
+ for (Clause c : clauses) {
+ c.processSubExpressions(processor);
+ }
+ } catch (XPathException e) {
+ throw new IllegalStateException(e);
+ }
+ if (returnClause == original) {
+ returnClause = replacement;
+ return true;
+ }
+ return !changed.isEmpty();
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+ @Override
+ public void explain(ExpressionPresenter out) {
+ out.startElement("FLWOR");
+ for (Clause c : clauses) {
+ c.explain(out);
+ }
+ out.startSubsidiaryElement("return");
+ returnClause.explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+ /*@NotNull*/
+ @Override
+ public Expression copy() {
+ List<Clause> newClauses = new ArrayList<Clause>();
+ List<LocalVariableBinding> oldBindings = new ArrayList<LocalVariableBinding>();
+ List<LocalVariableBinding> newBindings = new ArrayList<LocalVariableBinding>();
+ for (Clause c : clauses) {
+ Clause c2 = c.copy();
+ oldBindings.addAll(Arrays.asList(c.getRangeVariables()));
+ newBindings.addAll(Arrays.asList(c2.getRangeVariables()));
+ newClauses.add(c2);
+ }
+ FLWORExpression f2 = new FLWORExpression(newClauses, returnClause.copy());
+ ExpressionTool.copyLocationInfo(this, f2);
+ for (int i = 0; i < oldBindings.size(); i++) {
+ ExpressionTool.rebindVariableReferences(f2, oldBindings.get(i), newBindings.get(i));
+ }
+ return f2;
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(final PromotionOffer offer, final Expression parent) throws XPathException {
+ ExpressionProcessor processor = new ExpressionProcessor() {
+ /*@Nullable*/
+ public Expression processExpression(Expression expr) throws XPathException {
+ return doPromotion(expr, offer);
+ }
+ };
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else if (offer.action == PromotionOffer.RANGE_INDEPENDENT ||
+ offer.action == PromotionOffer.FOCUS_INDEPENDENT) {
+ // Pass the offer to the action expression only if the action isn't dependent on a
+ // variable bound within the FLWOR expression
+ Binding[] savedBindingList = offer.bindingList;
+ for (Clause c : clauses) {
+ offer.bindingList = extendBindingList(offer.bindingList, c.getRangeVariables());
+ c.processSubExpressions(processor);
+ }
+ offer.bindingList = savedBindingList;
+ return this;
+ } else {
+
+ try {
+ for (Clause c : clauses) {
+ c.processSubExpressions(processor);
+ }
+ } catch (XPathException e) {
+ throw new IllegalStateException(e);
+ }
+ returnClause.promote(offer, this);
+ return this;
+ }
+ }
+
+
+ /*@Nullable*/
+ private Binding[] extendBindingList(/*@Nullable*/ Binding[] bindings, /*@Nullable*/ LocalVariableBinding[] moreBindings) {
+ if (bindings == null) {
+ bindings = new Binding[0];
+ }
+ if (moreBindings == null || moreBindings.length == 0) {
+ return bindings;
+ } else {
+ Binding[] b2 = new Binding[bindings.length + moreBindings.length];
+ System.arraycopy(bindings, 0, b2, 0, bindings.length);
+ System.arraycopy(moreBindings, 0, b2, bindings.length, moreBindings.length);
+ return b2;
+ }
+ }
+
+ @Override
+ public int getEvaluationMethod() {
+ return Expression.PROCESS_METHOD;
+ }
+
+
+ /*@NotNull*/
+ public Expression optimize(
+ final ExpressionVisitor visitor,
+ final ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ // Optimize all the subexpressions
+ for (Clause c : clauses) {
+ c.processSubExpressions(new ExpressionProcessor() {
+ public Expression processExpression(Expression expr) throws XPathException {
+ return visitor.optimize(expr, contextItemType);
+ }
+ });
+ c.optimize(visitor, contextItemType);
+ }
+
+ // Optimize the return expression
+ returnClause = returnClause.optimize(visitor, contextItemType);
+
+ // If any 'let' clause declares a variable that is used only once, then inline it. If the variable
+ // is not used at all, then eliminate it
+
+ boolean tryAgain;
+ boolean changed = false;
+ do {
+ tryAgain = false;
+ for (Clause c : clauses) {
+ if (c.getClauseKey() == Clause.LET) {
+ LetClause lc = (LetClause)c;
+ if (!ExpressionTool.dependsOnVariable(this, new Binding[]{lc.getRangeVariable()})) {
+ clauses.remove(c);
+ tryAgain = true;
+ break;
+ }
+ boolean suppressInlining = false;
+ for (Clause c2 : clauses) {
+ if (c2.containsNonInlineableVariableReference(lc.getRangeVariable())) {
+ suppressInlining = true;
+ break;
+ }
+ }
+ if (!suppressInlining) {
+ if (lc.getRangeVariable().getNominalReferenceCount() == 1 ||
+ lc.getSequence() instanceof VariableReference ||
+ lc.getSequence() instanceof Literal) {
+ ExpressionTool.replaceVariableReferences(this, lc.getRangeVariable(), lc.getSequence().copy());
+ clauses.remove(c);
+ if (clauses.isEmpty()) {
+ return returnClause;
+ }
+ tryAgain = true;
+ break;
+ }
+ }
+ }
+ }
+ changed |= tryAgain;
+ } while (tryAgain);
+
+ // If changed, remove any redundant trace clauses
+ for (int i=clauses.size()-1; i>=1; i--) {
+ if (clauses.get(i).getClauseKey() == Clause.TRACE && clauses.get(i-1).getClauseKey() == Clause.TRACE) {
+ clauses.remove(i);
+ }
+ }
+
+ // If any 'where' clause depends on the context item, remove this dependency, because it makes
+ // it easier to rearrange where clauses as predicates
+ boolean depends = false;
+ for (Clause w : clauses) {
+ if (w instanceof WhereClause && ExpressionTool.dependsOnFocus(((WhereClause) w).getPredicate())) {
+ depends = true;
+ break;
+ }
+ }
+ if (depends) {
+ Expression expr1 = ExpressionTool.tryToFactorOutDot(this, contextItemType.itemType);
+ if (expr1 == null || expr1 == this) {
+ //no optimisation possible
+ return this;
+ }
+ resetLocalStaticProperties();
+ return expr1.optimize(visitor, contextItemType);
+ }
+
+ // Now convert any terms within WHERE clauses where possible into predicates on the appropriate
+ // expression bound to a variable on a for clause. This enables the resulting filter expression
+ // to be handled using indexing (in Saxon-EE), and it also reduces the number of items that need
+ // to be tested against the predicate
+
+ Expression expr2 = rewriteWhereClause(visitor, contextItemType);
+ if (expr2 != null && expr2 != this) {
+ return expr2.optimize(visitor, contextItemType);
+ }
+
+ // If the FLWOR expression consists entirely of FOR and LET clauses, convert it to a ForExpression
+ // or LetExpression. This is largely to take advantage of existing optimizations implemented for those
+ // expressions.
+
+ boolean allForOrLetExpr = true;
+ for (Clause c : clauses) {
+ if (!((c instanceof ForClause) || (c instanceof LetClause))) {
+ allForOrLetExpr = false;
+ break;
+ }
+ }
+
+ if (allForOrLetExpr) {
+ return rewriteForOrLet(visitor, contextItemType);
+ }
+
+ return this;
+ }
+
+ /**
+ * @param visitor the expression visitor
+ * @param contextItemType the type of the context item
+ * @return We return this expression, with WhereClauses moved up as far as possible in the list of clauses.
+ * A Where clause cannot move above a Count clause because it changes the number of tuples in the tuple stream.
+ * Alternatively, return null if no rewriting is possible.
+ * @throws XPathException if the rewrite fails for any reason
+ */
+
+ /*@Nullable*/
+ private Expression rewriteWhereClause(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType)
+ throws XPathException {
+ WhereClause whereClause;
+ int whereIndex = 0;
+ class WhereClauseStruct {
+ int whereIndex = 0;
+ WhereClause whereClause;
+ }
+ List<WhereClauseStruct> whereList = new ArrayList<WhereClauseStruct>();
+
+ for (Clause c : clauses) {
+ if (c instanceof WhereClause) {
+ WhereClauseStruct wStruct = new WhereClauseStruct();
+ wStruct.whereClause = (WhereClause) c;
+
+ //keep track of whereclause from the end of the list of clauses.
+ //We are always attempting to rewrite whereclauses from left to right,
+ // therefore index will always be in snyc
+ wStruct.whereIndex = clauses.size() - whereIndex;
+ whereList.add(wStruct);
+ }
+ whereIndex++;
+ }
+
+ if (whereList.size() == 0) {
+ return null;
+ }
+
+ while (!whereList.isEmpty()) {
+ whereClause = whereList.get(0).whereClause;
+ whereIndex = whereList.get(0).whereIndex;
+ Expression condition = whereClause.getPredicate();
+ List<Expression> list = new ArrayList<Expression>(5);
+ BooleanExpression.listAndComponents(condition, list);
+ for (int i = list.size() - 1; i >= 0; i--) {
+ Expression term = list.get(i);
+ for (int c = clauses.size() - whereIndex - 1; c >= 0; c--) {
+ Clause clause = clauses.get(c);
+ Binding[] bindingList = clause.getRangeVariables();
+
+ // Find the first clause prior to the where clause that declares variables on which the
+ // term of the where clause depends
+
+ if (ExpressionTool.dependsOnVariable(term, bindingList) || clause.getClauseKey() == Clause.COUNT) {
+ // remove this term from the where clause
+ Expression removedExpr = list.remove(i);
+ if (list.isEmpty()) {
+ // the where clause has no terms left, so remove the clause
+ clauses.remove(clauses.size() - whereIndex);
+ } else {
+ // change the predicate of the where clause to use only those terms that remain
+ whereClause.setPredicate(makeAndCondition(list));
+ }
+ if ((clause instanceof ForClause) && !(((ForClause) clause).isAllowingEmpty())) {
+ // if the clause is a "for" clause, try to add the term as a predicate
+ boolean added = ((ForClause) clause).addPredicate(this, visitor, contextItemType, term);
+ //If we cannot add the WhereClause term as a predicate then put it back into the list of clauses
+ if (!added) {
+ clauses.add(c + 1, new WhereClause(removedExpr));
+ }
+ } else {
+ // the clause is not a "for" clause, so just move the "where" to this place in the list of clauses
+ WhereClause newWhere = new WhereClause(term);
+ clauses.add(c + 1, newWhere);
+ }
+ // we found a variable on which the term depends so we can't move it any further
+ break;
+ }
+ }
+ if (list.size() - 1 == i) {
+ list.remove(i);
+ if (list.isEmpty()) {
+ clauses.remove(clauses.size() - whereIndex);
+ } else {
+ whereClause.setPredicate(makeAndCondition(list));
+ }
+ WhereClause newWhere = new WhereClause(term);
+ clauses.add(0, newWhere);
+ }
+ }
+
+ whereList.remove(0);
+ }
+ return this;
+ }
+
+ /**
+ * Recursive method to make a list of expressions into a AndExpression
+ *
+ * @param list of Expression
+ * @return And Expression of list of expressions
+ */
+ private Expression makeAndCondition(List<Expression> list) {
+ if (list.size() == 1) {
+ return list.get(0);
+ } else {
+ return new AndExpression(list.get(0), makeAndCondition(list.subList(1, list.size())));
+ }
+ }
+
+ /**
+ * Rewrite a FLWOR expression that consists entirely of "for" and "let" clauses as
+ * a LetExpression or ForExpression
+ *
+ * @param visitor - ExpressionVisitor
+ * @param contextItemType - ExpressionVisitor.ContextItemTyp
+ * @return the rewritten expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error occurs
+ */
+ /*@NotNull*/
+ private Expression rewriteForOrLet(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ Expression action = returnClause;
+ CodeInjector injector = null;
+ if (visitor.getStaticContext() instanceof QueryModule) {
+ injector = ((QueryModule)visitor.getStaticContext()).getCodeInjector();
+ }
+
+ for (int i = clauses.size() - 1; i >= 0; i--) {
+
+ if (clauses.get(i) instanceof ForClause) {
+ ForClause forClause = (ForClause) clauses.get(i);
+ ForExpression forExpr;
+ if (forClause.isAllowingEmpty()) {
+ forExpr = (ForExpression) visitor.getConfiguration().makeOuterForExpression();
+ } else {
+ forExpr = new ForExpression();
+ }
+
+ forExpr.setLocationId(forClause.getLocationId());
+ forExpr.setAction(action);
+
+ forExpr.setSequence(forClause.getSequence());
+ forExpr.setVariableQName(forClause.getRangeVariable().getVariableQName());
+ forExpr.setRequiredType(forClause.getRangeVariable().getRequiredType());
+ ExpressionTool.rebindVariableReferences(action, forClause.getRangeVariable(), forExpr);
+ if (forClause.getPositionVariable() != null) {
+ PositionVariable posVar = new PositionVariable();
+ posVar.setVariableQName(forClause.getPositionVariable().getVariableQName());
+ ExpressionTool.rebindVariableReferences(action, forClause.getPositionVariable(), posVar);
+ forExpr.setPositionVariable(posVar);
+ }
+ action = forExpr;
+
+ if (injector != null) {
+ action = injector.inject(action, visitor.getStaticContext(), Location.FOR_EXPRESSION, forExpr.getVariableQName());
+ }
+
+
+ } else {
+ LetClause letClause = (LetClause) clauses.get(i);
+ LetExpression letExpr = new LetExpression();
+ letExpr.setLocationId(letClause.getLocationId());
+ letExpr.setAction(action);
+ letExpr.setSequence(letClause.getSequence());
+ letExpr.setVariableQName(letClause.getRangeVariable().getVariableQName());
+ letExpr.setRequiredType(letClause.getRangeVariable().getRequiredType());
+ letExpr.setRefCount(letClause.getRangeVariable().getNominalReferenceCount());
+ ExpressionTool.rebindVariableReferences(action, letClause.getRangeVariable(), letExpr);
+ action = letExpr;
+
+ if (injector != null) {
+ action = injector.inject(action, visitor.getStaticContext(), Location.LET_EXPRESSION, letExpr.getVariableQName());
+ }
+ }
+
+ }
+ action = action.optimize(visitor, contextItemType);
+ return action;
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+ /*@NotNull*/
+ @Override
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ //force push mode if there is a window clause that is not the first clause
+ // TODO: this is a temporary solution (pre 9.4 release) because WindowPull fails to call pullTuple
+ // on the previous clause in the FLWOR expression
+ for (int i = 1; i < clauses.size(); i++) {
+ if (clauses.get(i).getClauseKey() == Clause.WINDOW) {
+ Controller controller = context.getController();
+ SequenceReceiver saved = context.getReceiver();
+ SequenceOutputter seq = controller.allocateSequenceOutputter(20);
+ seq.getPipelineConfiguration().setHostLanguage(getHostLanguage());
+ context.setReceiver(seq);
+ process(context);
+ context.setReceiver(saved);
+ seq.close();
+ return seq.iterate();
+ }
+ }
+
+ TuplePull stream = new SingularityPull();
+ for (Clause c : clauses) {
+ stream = c.getPullStream(stream, context);
+ }
+ return new ReturnClauseIterator(stream, this, context);
+ }
+
+
+ /**
+ * Process the instruction, without returning any tail calls
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ * @throws XPathException if a dynamic error occurs
+ */
+ @Override
+ public void process(XPathContext context) throws XPathException {
+ TuplePush destination = new ReturnClausePush(returnClause);
+ for (int i = clauses.size() - 1; i >= 0; i--) {
+ Clause c = clauses.get(i);
+ destination = c.getPushStream(destination, context);
+ }
+ destination.processTuple(context);
+ destination.close();
+ }
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ * @throws net.sf.saxon.trans.XPathException
+ * if evaluation fails
+ * @throws UnsupportedOperationException if the expression is not an updating expression
+ */
+
+
+ @Override
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ TuplePull stream = new SingularityPull();
+ for (Clause c : clauses) {
+ stream = c.getPullStream(stream, context);
+ }
+ while (stream.nextTuple(context)) {
+ returnClause.evaluatePendingUpdates(context, pul);
+ }
+ }
+
+ /**
+ * Display the expression as a string
+ */
+
+ public String toString() {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ for (Clause c : clauses) {
+ sb.append(c.toString());
+ sb.append(' ');
+ }
+ sb.append(" return ");
+ sb.append(returnClause.toString());
+ return sb.toString();
+ }
+
+ /**
+ * Determine whether a variable reference found within a clause of a FLWOR expression is a looping
+ * reference, that is, whether the variable is used more than once
+ * @param binding the variable binding, which may be bound in a clause of the same FLWOR expression,
+ * or in some containing expression
+ * @return true if a reference to the variable occurs within a loop relative to the binding, that is, if the
+ * variable's value is used more than once. Note that this method only detects a loop that is due to the clauses
+ * of this FLWOR expression itself. A loop in an inner expression or outer expression of the FLWOR expression must
+ * be detected by the caller.
+ */
+
+ public boolean hasLoopingVariableReference(final Binding binding) {
+
+ // Determine the clause that binds the variable (if any)
+
+ int bindingClause = -1;
+ for (int i=0; i<clauses.size(); i++) {
+ if (clauseHasBinding(clauses.get(i), binding)) {
+ bindingClause = i;
+ break;
+ }
+ }
+
+ boolean boundOutside = bindingClause < 0;
+ if (boundOutside) {
+ bindingClause = 0;
+ }
+
+ // Determine the last clause that contains a reference to the variable.
+ // (If any reference to the variable is a looping reference, then the last one will be)
+
+ int lastReferencingClause = clauses.size(); // indicates the return clause
+ if (!ExpressionTool.dependsOnVariable(returnClause, new Binding[]{binding})) {
+ // artifice to get a response value from the generic processExpression() method
+ final List<Boolean> response = new ArrayList<Boolean>();
+ ExpressionProcessor checker = new ExpressionProcessor() {
+ public Expression processExpression(Expression expr) throws XPathException {
+ if (response.isEmpty() && ExpressionTool.dependsOnVariable(expr, new Binding[]{binding})) {
+ response.add(true);
+ }
+ return expr;
+ }
+ };
+ for (int i=clauses.size()-1; i>=0; i--) {
+ try {
+ clauses.get(i).processSubExpressions(checker);
+ if (!response.isEmpty()) {
+ lastReferencingClause = i;
+ break;
+ }
+ } catch (XPathException e) {
+ assert false;
+ }
+ }
+ }
+
+ // If any clause between the binding clause and the last referencing clause is a looping clause,
+ // then the variable is used within a loop
+
+ for (int i=lastReferencingClause - 1; i>=bindingClause; i--) {
+ if (isLoopingClause(clauses.get(i))) {
+ return true;
+ }
+ }
+
+ // otherwise there is no loop caused by the clauses of the FLWOR expression itself.
+
+ return false;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the FLWOR expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new FLWORExpressionCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/flwor/ForClause.java b/sf/saxon/expr/flwor/ForClause.java
new file mode 100644
index 0000000..04b8b71
--- /dev/null
+++ b/sf/saxon/expr/flwor/ForClause.java
@@ -0,0 +1,435 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.functions.KeyFn;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A "for" clause in a FLWOR expression
+ */
+public class ForClause extends Clause {
+
+ private LocalVariableBinding rangeVariable;
+ /*@Nullable*/
+ private LocalVariableBinding positionVariable;
+ private Expression sequence;
+ private boolean allowsEmpty;
+
+ @Override
+ public int getClauseKey() {
+ return FOR;
+ }
+
+ public ForClause copy() {
+ ForClause f2 = new ForClause();
+ f2.setLocationId(getLocationId());
+ f2.rangeVariable = rangeVariable.copy();
+ if (positionVariable != null) {
+ f2.positionVariable = positionVariable.copy();
+ }
+ f2.sequence = sequence.copy();
+ f2.allowsEmpty = allowsEmpty;
+ return f2;
+ }
+
+ /**
+ * Set the expression over which the "for" variable iterates
+ *
+ * @param sequence the expression over which the variable ranges
+ */
+ public void setSequence(Expression sequence) {
+ this.sequence = sequence;
+ }
+
+ /**
+ * Get the expression over which the "for" variable iterates
+ *
+ * @return the expression over which the variable ranges
+ */
+
+ public Expression getSequence() {
+ return sequence;
+ }
+
+ /**
+ * Set the range variable (the primary variable bound by this clause)
+ *
+ * @param binding the range variable
+ */
+
+ public void setRangeVariable(LocalVariableBinding binding) {
+ rangeVariable = binding;
+ }
+
+ /**
+ * Get the range variable (the primary variable bound by this clause)
+ *
+ * @return the range variable
+ */
+
+ public LocalVariableBinding getRangeVariable() {
+ return rangeVariable;
+ }
+
+ /**
+ * Set the position variable (the variable bound by the "at" clause)
+ *
+ * @param binding the position variable
+ */
+
+ public void setPositionVariable(/*@Nullable*/ LocalVariableBinding binding) {
+ positionVariable = binding;
+ }
+
+ /**
+ * Get the position variable (the variable bound by the "at" clause)
+ *
+ * @return the position variable
+ */
+
+ /*@Nullable*/
+ public LocalVariableBinding getPositionVariable() {
+ return positionVariable;
+ }
+
+ /**
+ * Get the number of variables bound by this clause
+ *
+ * @return the number of variable bindings (1 or 2 depending on whether there is a position variable)
+ */
+ @Override
+ public LocalVariableBinding[] getRangeVariables() {
+ if (positionVariable == null) {
+ return new LocalVariableBinding[]{rangeVariable};
+ } else {
+ return new LocalVariableBinding[]{rangeVariable, positionVariable};
+ }
+ }
+
+ /**
+ * Say whether the "allowing empty" option is present
+ *
+ * @param option true if the "allowing empty" option is present
+ */
+
+ public void setAllowingEmpty(boolean option) {
+ allowsEmpty = option;
+ }
+
+ /**
+ * Ask whether the "allowing empty" option is present
+ *
+ * @return true if the "allowing empty" option is present
+ */
+
+ public boolean isAllowingEmpty() {
+ return allowsEmpty;
+ }
+
+ /**
+ * Type-check the expression
+ */
+ @Override
+ public void typeCheck(ExpressionVisitor visitor) throws XPathException {
+ SequenceType decl = rangeVariable.getRequiredType();
+ SequenceType sequenceType = SequenceType.makeSequenceType(
+ decl.getPrimaryType(), StaticProperty.ALLOWS_ZERO_OR_MORE);
+ RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, rangeVariable.getVariableQName(), 0
+ );
+ //role.setSourceLocator(this);
+ sequence = TypeChecker.strictTypeCheck(
+ sequence, sequenceType, role, visitor.getStaticContext());
+ // TODO: refine the type information for the variable
+ }
+
+ /**
+ * Get a tuple stream that implements the functionality of this clause, taking its
+ * input from another tuple stream which this clause modifies
+ *
+ * @param base the input tuple stream
+ * @param context the XPath dynamic context
+ * @return the output tuple stream
+ */
+ @Override
+ public TuplePull getPullStream(TuplePull base, XPathContext context) {
+ if (allowsEmpty) {
+ return new ForClauseOuterPull(base, this);
+ } else {
+ return new ForClausePull(base, this);
+ }
+ }
+
+ /**
+ * Get a push-mode tuple stream that implements the functionality of this clause, supplying its
+ * output to another tuple stream
+ *
+ * @param destination the output tuple stream
+ * @param context the dynamic evaluation context
+ * @return the push tuple stream that implements the functionality of this clause of the FLWOR
+ * expression
+ */
+ @Override
+ public TuplePush getPushStream(TuplePush destination, XPathContext context) {
+ if (allowsEmpty) {
+ return new ForClauseOuterPush(destination, this);
+ } else {
+ return new ForClausePush(destination, this);
+ }
+ }
+
+ /**
+ * Convert where clause to a predicate.
+ *
+ * @param flwor the FLWOR expression (sans the relevant part of the where clause)
+ * @param visitor the expression visitor
+ * @param contextItemType the item type of the context item
+ * @param condition the predicate to be added. This will always be a single term (never a composite condition
+ * using "and"), as the where clause is split into separate terms before calling this method
+ * @return true if the expression has been changed, that is, if the where clause has been converted
+ * @throws XPathException if an error is encountered
+ */
+
+ public boolean addPredicate(FLWORExpression flwor, ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType, Expression condition) throws XPathException {
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ boolean debug = opt.getConfiguration().getBooleanProperty(FeatureKeys.TRACE_OPTIMIZER_DECISIONS);
+
+ // assert: condition has no dependency on context item. We removed any such dependency before we got here.
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Expression head = null;
+ Expression selection = sequence;
+ ItemType selectionContextItemType = (contextItemType == null ? null : contextItemType.itemType);
+ if (sequence instanceof SlashExpression) {
+ if (((SlashExpression) sequence).isAbsolute(th)) {
+ head = ((SlashExpression) sequence).getFirstStep();
+ selection = ((SlashExpression) sequence).getRemainingSteps();
+ selectionContextItemType = head.getItemType(th);
+ } else {
+ SlashExpression p = ((SlashExpression) sequence).tryToMakeAbsolute(th);
+ if (p != null) {
+ sequence = p;
+ head = ((SlashExpression) sequence).getFirstStep();
+ selection = ((SlashExpression) sequence).getRemainingSteps();
+ selectionContextItemType = head.getItemType(th);
+ }
+ }
+ }
+
+ boolean changed = false;
+
+ // Process each term in the where clause independently
+ Expression term = condition;
+
+ if (positionVariable != null &&
+ (term instanceof ValueComparison || term instanceof GeneralComparison || term instanceof CompareToIntegerConstant) &&
+ ExpressionTool.dependsOnVariable(term, new Binding[]{positionVariable})) {
+ ComparisonExpression comp = (ComparisonExpression) term;
+ Expression[] operands = comp.getOperands();
+
+ if (ExpressionTool.dependsOnVariable(flwor, new Binding[]{positionVariable})) {
+ // cannot convert a positional where clause into a positional predicate if there are
+ // other references to the position variable
+ return false;
+ }
+
+ for (int op = 0; op < 2; op++) {
+
+ // If the where clause is a simple test on the position variable, for example
+ // for $x at $p in EXPR where $p = 5 return A
+ // then absorb the where condition into a predicate, rewriting it as
+ // for $x in EXPR[position() = 5] return A
+ // This takes advantage of the optimizations applied to positional filter expressions
+ // Only do this if the sequence expression has not yet been changed, because
+ // the position in a predicate after the first is different. And only do it if this
+ // is the only reference to the position variable, because if there are other references,
+ // the existence of the predicate will change the values of the position variable.
+ Binding[] thisVar = {this.getRangeVariable()};
+ if (positionVariable != null && operands[op] instanceof VariableReference && !changed) {
+ List varRefs = new ArrayList();
+ ExpressionTool.gatherVariableReferences(condition, positionVariable, varRefs);
+ if (varRefs.size() == 1 && varRefs.get(0) == operands[op] &&
+ !ExpressionTool.dependsOnFocus(operands[1 - op]) &&
+ !ExpressionTool.dependsOnVariable(operands[1 - op], thisVar)) {
+ FunctionCall position =
+ SystemFunctionCall.makeSystemFunction("position", SimpleExpression.NO_ARGUMENTS);
+ Expression predicate = term.copy();
+ predicate.replaceSubExpression(((ComparisonExpression) predicate).getOperands()[op], position);
+ if (debug) {
+ opt.trace("Replaced positional variable in predicate by position()");
+ }
+ selection = new FilterExpression(selection, predicate);
+ ExpressionTool.copyLocationInfo(predicate, selection);
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(selectionContextItemType, true);
+ selection = visitor.typeCheck(selection, cit);
+ if (!ExpressionTool.dependsOnVariable(flwor, new Binding[]{positionVariable})) {
+ positionVariable = null;
+ }
+ changed = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (positionVariable == null) {
+ Binding[] thisVar = {this.getRangeVariable()};
+ if (opt.isVariableReplaceableByDot(term, thisVar)) {
+ boolean useDotDirectly = opt.isVariableReplaceableByDot(term, thisVar);
+ Expression replacement;
+ // When rewriting the where expression as a filter, we have to replace references to the
+ // range variable by references to the context item. If we can do this directly, we do. But
+ // if the reference to the range variable occurs inside a predicate, or on the rhs of slash,
+ // we have to bind a new variable to the context item. So for example "for $x in S where
+ // T[abc = $x]" gets rewritten as "for $x in S[let $dot := . return T[abc = $dot]]"
+ if (useDotDirectly) {
+ replacement = new ContextItemExpression();
+ } else {
+ LetExpression let = new LetExpression();
+ let.setVariableQName(
+ new StructuredQName("saxon", NamespaceConstant.SAXON, "dot" + hashCode()));
+ let.setRequiredType(SequenceType.makeSequenceType(contextItemType.itemType, StaticProperty.EXACTLY_ONE));
+ let.setSequence(new ContextItemExpression());
+ let.setAction(term);
+ term = let;
+ replacement = new VariableReference(let);
+ }
+
+ PromotionOffer offer = new PromotionOffer(visitor.getConfiguration().obtainOptimizer());
+ offer.action = PromotionOffer.INLINE_VARIABLE_REFERENCES;
+ offer.bindingList = thisVar;
+ offer.containingExpression = replacement;
+ Expression newTerm = term.promote(offer, sequence);
+ if (newTerm != null && offer.accepted) {
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(sequence.getItemType(th), true);
+ Expression predicate = visitor.typeCheck(newTerm, cit);
+ // If the result of the predicate might be a number, wrap it in a call of boolean()
+ int rel = th.relationship(predicate.getItemType(th), BuiltInAtomicType.INTEGER);
+ if (rel != TypeHierarchy.DISJOINT) {
+ predicate = SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{predicate});
+ assert predicate != null;
+ }
+ selection = new FilterExpression(selection, predicate);
+ ExpressionTool.copyLocationInfo(predicate, selection);
+ cit = new ExpressionVisitor.ContextItemType(selectionContextItemType, true);
+ selection = visitor.typeCheck(selection, cit);
+ changed = true;
+ }
+ }
+
+ }
+ if (changed) {
+
+ if (head == null) {
+ sequence = selection;
+ } else if (head instanceof RootExpression && selection instanceof KeyFn) {
+ sequence = selection;
+ } else {
+ Expression path = ExpressionTool.makePathExpression(head, selection, false);
+ if (!(path instanceof SlashExpression)) {
+ return changed;
+ }
+ ExpressionTool.copyLocationInfo(condition, path);
+ Expression k = visitor.getConfiguration().obtainOptimizer().convertPathExpressionToKey((SlashExpression) path, visitor);
+ if (k == null) {
+ sequence = path;
+ } else {
+ sequence = k;
+ }
+ sequence = visitor.optimize(visitor.typeCheck(visitor.simplify(sequence), contextItemType), contextItemType);
+ }
+ }
+ return changed;
+ }
+
+ /**
+ * Process the subexpressions of this clause
+ *
+ * @param processor the expression processor used to process the subexpressions
+ */
+ @Override
+ public void processSubExpressions(ExpressionProcessor processor) throws XPathException {
+ sequence = processor.processExpression(sequence);
+ }
+
+ public void gatherVariableReferences(List references) {
+ if (positionVariable != null) {
+ ExpressionTool.gatherVariableReferences(sequence, (Binding) positionVariable, references);
+ }
+ ExpressionTool.gatherVariableReferences(sequence, (Binding) rangeVariable, references);
+ }
+
+ @Override
+ public void gatherVariableReferences(final ExpressionVisitor visitor, Binding binding, List<VariableReference> references) {
+ ExpressionTool.gatherVariableReferences(sequence, binding, references);
+ }
+
+ @Override
+ public void refineVariableType(ExpressionVisitor visitor, List<VariableReference> references, Expression returnExpr) {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType actualItemType = sequence.getItemType(th);
+ if(actualItemType instanceof ErrorType) {
+ actualItemType = AnyItemType.getInstance();
+ }
+ for (VariableReference ref : references) {
+ ref.refineVariableType(actualItemType,
+ (allowsEmpty ? StaticProperty.ALLOWS_ZERO_OR_ONE : StaticProperty.EXACTLY_ONE),
+ null, sequence.getSpecialProperties(), visitor);
+ }
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+ @Override
+ public void explain(ExpressionPresenter out) {
+ out.startElement("for");
+ out.emitAttribute("var", getRangeVariable().getVariableQName().getDisplayName());
+ out.emitAttribute("slot", getRangeVariable().getLocalSlotNumber()+"");
+ LocalVariableBinding posVar = getPositionVariable();
+ if (posVar != null) {
+ out.emitAttribute("at", posVar.getVariableQName().getDisplayName());
+ out.emitAttribute("at-slot", posVar.getLocalSlotNumber()+"");
+ }
+ sequence.explain(out);
+ out.endElement();
+ }
+
+ public String toString() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ fsb.append("for $");
+ fsb.append(rangeVariable.getVariableQName().getDisplayName());
+ fsb.append(' ');
+ LocalVariableBinding posVar = getPositionVariable();
+ if (posVar != null) {
+ fsb.append("at $");
+ fsb.append(posVar.getVariableQName().getDisplayName());
+ fsb.append(' ');
+ }
+ fsb.append("in ");
+ fsb.append(sequence.toString());
+ return fsb.toString();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/ForClauseOuterPull.java b/sf/saxon/expr/flwor/ForClauseOuterPull.java
new file mode 100644
index 0000000..636cdb7
--- /dev/null
+++ b/sf/saxon/expr/flwor/ForClauseOuterPull.java
@@ -0,0 +1,84 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.Int64Value;
+
+/**
+ * This class represents the tuple stream returned by a "for" clause in a FLWOR expression
+ */
+public class ForClauseOuterPull extends ForClausePull {
+
+ public ForClauseOuterPull(TuplePull base, ForClause forClause) {
+ super(base, forClause);
+ }
+
+ /**
+ * Deliver the next output tuple. Before returning, this method must set all the variables corresponding
+ * to the output tuple in the local stack frame associated with the context object
+ *
+ * @param context the dynamic evaluation context
+ * @return true if another tuple has been generated; false if the tuple stream is exhausted. If the
+ * method returns false, the values of the local variables corresponding to this tuple stream
+ * are undefined.
+ */
+ @Override
+ public boolean nextTuple(XPathContext context) throws XPathException {
+ while (true) {
+ Item next;
+ if (currentIteration == null) {
+ if (!base.nextTuple(context)) {
+ return false;
+ }
+ currentIteration = forClause.getSequence().iterate(context);
+ next = currentIteration.next();
+ if (next == null) {
+ context.setLocalVariable(forClause.getRangeVariable().getLocalSlotNumber(),
+ EmptySequence.getInstance());
+ if (forClause.getPositionVariable() != null) {
+ context.setLocalVariable(
+ forClause.getPositionVariable().getLocalSlotNumber(),
+ Int64Value.ZERO);
+ }
+ currentIteration = null;
+ return true;
+ }
+ } else {
+ next = currentIteration.next();
+ }
+ if (next != null) {
+ context.setLocalVariable(forClause.getRangeVariable().getLocalSlotNumber(), next);
+ if (forClause.getPositionVariable() != null) {
+ context.setLocalVariable(
+ forClause.getPositionVariable().getLocalSlotNumber(),
+ new Int64Value(currentIteration.position()));
+ }
+ return true;
+ } else {
+ currentIteration = null;
+ }
+ }
+ }
+
+ /**
+ * Close the tuple stream, indicating that although not all tuples have been read,
+ * no further tuples are required and resources can be released
+ */
+ @Override
+ public void close() {
+ base.close();
+ if (currentIteration != null) {
+ currentIteration.close();
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/ForClauseOuterPush.java b/sf/saxon/expr/flwor/ForClauseOuterPush.java
new file mode 100644
index 0000000..af047f9
--- /dev/null
+++ b/sf/saxon/expr/flwor/ForClauseOuterPush.java
@@ -0,0 +1,69 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.Int64Value;
+
+/**
+ * This class implements the changes to the tuple stream effected by a "for" clause in a FLWOR expression
+ * where "allowing empty" is specified
+ */
+public class ForClauseOuterPush extends TuplePush {
+
+ protected TuplePush destination;
+ protected ForClause forClause;
+
+ public ForClauseOuterPush(TuplePush destination, ForClause forClause) {
+ this.destination = destination;
+ this.forClause = forClause;
+ }
+
+ /*
+ * Process the next tuple.
+ */
+ @Override
+ public void processTuple(XPathContext context) throws XPathException {
+ SequenceIterator iter = forClause.getSequence().iterate(context);
+ Item next = iter.next();
+ if (next == null) {
+ context.setLocalVariable(forClause.getRangeVariable().getLocalSlotNumber(), EmptySequence.getInstance());
+ if (forClause.getPositionVariable() != null) {
+ context.setLocalVariable(forClause.getPositionVariable().getLocalSlotNumber(), Int64Value.ZERO);
+ }
+ destination.processTuple(context);
+ } else {
+ while (true) {
+ context.setLocalVariable(forClause.getRangeVariable().getLocalSlotNumber(), next);
+ if (forClause.getPositionVariable() != null) {
+ context.setLocalVariable(
+ forClause.getPositionVariable().getLocalSlotNumber(),
+ new Int64Value(iter.position()));
+ }
+ destination.processTuple(context);
+ next = iter.next();
+ if (next == null) {
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Close the tuple stream
+ */
+ @Override
+ public void close() throws XPathException {
+ destination.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/ForClausePull.java b/sf/saxon/expr/flwor/ForClausePull.java
new file mode 100644
index 0000000..28905ff
--- /dev/null
+++ b/sf/saxon/expr/flwor/ForClausePull.java
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Int64Value;
+
+/**
+ * This class implements the changes to the tuple stream effected by a "for" clause in a FLWOR expression
+ */
+public class ForClausePull extends TuplePull {
+
+ protected TuplePull base;
+ protected ForClause forClause;
+ /*@Nullable*/ protected SequenceIterator currentIteration;
+
+ public ForClausePull(TuplePull base, ForClause forClause) {
+ this.base = base;
+ this.forClause = forClause;
+ }
+
+ /**
+ * Move on to the next tuple. Before returning, this method must set all the variables corresponding
+ * to the "returned" tuple in the local stack frame associated with the context object
+ *
+ * @param context the dynamic evaluation context
+ * @return true if another tuple has been generated; false if the tuple stream is exhausted. If the
+ * method returns false, the values of the local variables corresponding to this tuple stream
+ * are undefined.
+ */
+ @Override
+ public boolean nextTuple(XPathContext context) throws XPathException {
+ while (true) {
+ if (currentIteration == null) {
+ if (!base.nextTuple(context)) {
+ return false;
+ }
+ currentIteration = forClause.getSequence().iterate(context);
+ }
+ Item next = currentIteration.next();
+ if (next != null) {
+ context.setLocalVariable(forClause.getRangeVariable().getLocalSlotNumber(), next);
+ if (forClause.getPositionVariable() != null) {
+ context.setLocalVariable(
+ forClause.getPositionVariable().getLocalSlotNumber(),
+ new Int64Value(currentIteration.position()));
+ }
+ return true;
+ } else {
+ currentIteration = null;
+ }
+ }
+ }
+
+ /**
+ * Close the tuple stream, indicating that although not all tuples have been read,
+ * no further tuples are required and resources can be released
+ */
+ @Override
+ public void close() {
+ base.close();
+ if (currentIteration != null) {
+ currentIteration.close();
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/ForClausePush.java b/sf/saxon/expr/flwor/ForClausePush.java
new file mode 100644
index 0000000..a8e9974
--- /dev/null
+++ b/sf/saxon/expr/flwor/ForClausePush.java
@@ -0,0 +1,59 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Int64Value;
+
+/**
+ * This class implements the changes to the tuple stream effected by a "for" clause in a FLWOR expression
+ */
+public class ForClausePush extends TuplePush {
+
+ protected TuplePush destination;
+ protected ForClause forClause;
+
+ public ForClausePush(TuplePush destination, ForClause forClause) {
+ this.destination = destination;
+ this.forClause = forClause;
+ }
+
+ /*
+ * Process the next tuple.
+ */
+ @Override
+ public void processTuple(XPathContext context) throws XPathException {
+ SequenceIterator iter = forClause.getSequence().iterate(context);
+ while (true) {
+ Item next = iter.next();
+ if (next != null) {
+ context.setLocalVariable(forClause.getRangeVariable().getLocalSlotNumber(), next);
+ if (forClause.getPositionVariable() != null) {
+ context.setLocalVariable(
+ forClause.getPositionVariable().getLocalSlotNumber(),
+ new Int64Value(iter.position()));
+ }
+ destination.processTuple(context);
+ } else {
+ break;
+ }
+ }
+ }
+
+ /*
+ * Close the tuple stream
+ */
+ @Override
+ public void close() throws XPathException {
+ destination.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/LetClause.java b/sf/saxon/expr/flwor/LetClause.java
new file mode 100644
index 0000000..33a17e0
--- /dev/null
+++ b/sf/saxon/expr/flwor/LetClause.java
@@ -0,0 +1,196 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.List;
+
+/**
+ * A "let" clause in a FLWOR expression
+ */
+public class LetClause extends Clause {
+
+ private LocalVariableBinding rangeVariable;
+ private Expression sequence;
+
+ @Override
+ public int getClauseKey() {
+ return LET;
+ }
+
+ public LetClause copy() {
+ LetClause let2 = new LetClause();
+ let2.setLocationId(getLocationId());
+ let2.rangeVariable = rangeVariable.copy();
+ let2.sequence = sequence.copy();
+ return let2;
+ }
+
+ public void setSequence(Expression sequence) {
+ this.sequence = sequence;
+ }
+
+ public Expression getSequence() {
+ return sequence;
+ }
+
+
+ public void setRangeVariable(LocalVariableBinding binding) {
+ this.rangeVariable = binding;
+ }
+
+ public LocalVariableBinding getRangeVariable() {
+ return rangeVariable;
+ }
+
+ /**
+ * Get the number of variables bound by this clause
+ *
+ * @return the number of variable bindings
+ */
+ @Override
+ public LocalVariableBinding[] getRangeVariables() {
+ return new LocalVariableBinding[]{rangeVariable};
+ }
+
+ /**
+ * Get a tuple stream that implements the functionality of this clause, taking its
+ * input from another tuple stream which this clause modifies
+ *
+ * @param base the input tuple stream
+ * @param context
+ * @return the output tuple stream
+ */
+
+ @Override
+ public TuplePull getPullStream(TuplePull base, XPathContext context) {
+ return new LetClausePull(base, this);
+ }
+
+ /**
+ * Get a push-mode tuple stream that implements the functionality of this clause, supplying its
+ * output to another tuple stream
+ *
+ * @param destination the output tuple stream
+ * @param context
+ * @return the push tuple stream that implements the functionality of this clause of the FLWOR
+ * expression
+ */
+ @Override
+ public TuplePush getPushStream(TuplePush destination, XPathContext context) {
+ return new LetClausePush(destination, this);
+ }
+
+ /**
+ * Process the subexpressions of this clause
+ *
+ * @param processor the expression processor used to process the subexpressions
+ */
+ @Override
+ public void processSubExpressions(ExpressionProcessor processor) throws XPathException {
+ sequence = processor.processExpression(sequence);
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ public void typeCheck(ExpressionVisitor visitor) throws XPathException {
+ RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, rangeVariable.getVariableQName(), 0);
+ sequence = TypeChecker.strictTypeCheck(
+ sequence, rangeVariable.getRequiredType(), role, visitor.getStaticContext());
+ }
+
+ @Override
+ public void gatherVariableReferences(final ExpressionVisitor visitor, Binding binding, List<VariableReference> references){
+ ExpressionTool.gatherVariableReferences(sequence, binding, references);
+ }
+
+ @Override
+ public void refineVariableType(ExpressionVisitor visitor, List<VariableReference> references, Expression returnExpr) {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ final ItemType actualItemType = sequence.getItemType(th);
+ for (VariableReference ref : references) {
+ ref.refineVariableType(actualItemType, sequence.getCardinality(),
+ (sequence instanceof Literal ? ((Literal) sequence).getValue() : null),
+ sequence.getSpecialProperties(), visitor);
+ visitor.resetStaticProperties();
+ }
+ }
+
+ /**
+ * Provide additional information about the type of the variable, typically derived by analyzing
+ * the initializer of the variable binding
+ * @param type the item type of the variable
+ * @param cardinality the cardinality of the variable
+ * @param constantValue the actual value of the variable, if this is known statically, otherwise null
+ * @param properties additional static properties of the variable's initializer
+ * @param visitor an ExpressionVisitor
+ */
+
+// public void refineVariableType(
+// ItemType type, int cardinality, Value constantValue, int properties, ExpressionVisitor visitor) {
+// Executable exec = visitor.getExecutable();
+// if (exec == null) {
+// // happens during use-when evaluation
+// return;
+// }
+// TypeHierarchy th = exec.getConfiguration().getTypeHierarchy();
+// ItemType oldItemType = rangeVariable.getRequiredType().getPrimaryType();
+// ItemType newItemType = oldItemType;
+// if (th.isSubType(type, oldItemType)) {
+// newItemType = type;
+// }
+// if (oldItemType instanceof NodeTest && type instanceof AtomicType) {
+// // happens when all references are flattened
+// newItemType = type;
+// }
+// int newcard = cardinality & rangeVariable.getRequiredType().getCardinality();
+// if (newcard==0) {
+// // this will probably lead to a type error later
+// newcard = rangeVariable.getRequiredType().getCardinality();
+// }
+// SequenceType seqType = SequenceType.makeSequenceType(newItemType, newcard);
+// //setStaticType(seqType, constantValue, properties);
+// }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+ @Override
+ public void explain(ExpressionPresenter out) {
+ out.startElement("let");
+ out.emitAttribute("var", getRangeVariable().getVariableQName().getDisplayName());
+ out.emitAttribute("slot", getRangeVariable().getLocalSlotNumber()+"");
+ sequence.explain(out);
+ out.endElement();
+ }
+
+ public String toString() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ fsb.append("let $");
+ fsb.append(rangeVariable.getVariableQName().getDisplayName());
+ fsb.append(" := ");
+ fsb.append(sequence.toString());
+ return fsb.toString();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/LetClausePull.java b/sf/saxon/expr/flwor/LetClausePull.java
new file mode 100644
index 0000000..5be7fa5
--- /dev/null
+++ b/sf/saxon/expr/flwor/LetClausePull.java
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Implements the changes to a tuple stream effected by the Let clause in a FLWOR expression
+ */
+public class LetClausePull extends TuplePull {
+
+ TuplePull base;
+ LetClause letClause;
+
+ public LetClausePull(TuplePull base, LetClause letClause) {
+ this.base = base;
+ this.letClause = letClause;
+ }
+
+ /**
+ * Move on to the next tuple. Before returning, this method must set all the variables corresponding
+ * to the "returned" tuple in the local stack frame associated with the context object
+ *
+ * @param context the dynamic evaluation context
+ * @return true if another tuple has been generated; false if the tuple stream is exhausted. If the
+ * method returns false, the values of the local variables corresponding to this tuple stream
+ * are undefined.
+ */
+ @Override
+ public boolean nextTuple(XPathContext context) throws XPathException {
+ if (!base.nextTuple(context)) {
+ return false;
+ }
+ Sequence val = ExpressionTool.lazyEvaluate(letClause.getSequence(), context, 100);
+ // TODO: be smarter, see LetExpression.eval()
+ context.setLocalVariable(letClause.getRangeVariable().getLocalSlotNumber(), val);
+ return true;
+ }
+
+ /**
+ * Close the tuple stream, indicating that although not all tuples have been read,
+ * no further tuples are required and resources can be released
+ */
+ @Override
+ public void close() {
+ base.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/LetClausePush.java b/sf/saxon/expr/flwor/LetClausePush.java
new file mode 100644
index 0000000..f846587
--- /dev/null
+++ b/sf/saxon/expr/flwor/LetClausePush.java
@@ -0,0 +1,47 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Implements the changes to a tuple stream effected by the Let clause in a FLWOR expression
+ */
+public class LetClausePush extends TuplePush {
+
+ TuplePush destination;
+ LetClause letClause;
+
+ public LetClausePush(TuplePush destination, LetClause letClause) {
+ this.destination = destination;
+ this.letClause = letClause;
+ }
+
+ /*
+ * Notify the next tuple.
+ */
+ @Override
+ public void processTuple(XPathContext context) throws XPathException {
+ Sequence val = ExpressionTool.lazyEvaluate(letClause.getSequence(), context, 100);
+ // TODO: be smarter, see LetExpression.eval()
+ context.setLocalVariable(letClause.getRangeVariable().getLocalSlotNumber(), val);
+ destination.processTuple(context);
+ }
+
+ /*
+ * Close the tuple stream
+ */
+ @Override
+ public void close() throws XPathException {
+ destination.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/LocalVariableBinding.java b/sf/saxon/expr/flwor/LocalVariableBinding.java
new file mode 100644
index 0000000..aba734f
--- /dev/null
+++ b/sf/saxon/expr/flwor/LocalVariableBinding.java
@@ -0,0 +1,180 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Binding;
+import net.sf.saxon.expr.FilterExpression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+/**
+ * Represents the defining occurrence of a variable declared within a FLWOR expression,
+ * for example the $p in "for $x at $p in ...". Also used for the variables bound by the
+ * bind-group and bind-grouping-key attributes in xsl:for-each-group
+ */
+
+public class LocalVariableBinding implements Binding {
+
+ private StructuredQName variableName;
+ private SequenceType requiredType;
+ private int slotNumber = -999;
+ private int refCount = 0;
+
+ /**
+ * Create a LocalVariableBinding
+ * @param name the name of the variable
+ * @param type the static type of the variable
+ */
+
+ public LocalVariableBinding(StructuredQName name, SequenceType type){
+ variableName = name;
+ requiredType = type;
+ }
+
+ /**
+ * Make a copy of this LocalVariableBinding (except for the slot number)
+ * @return a copy of the binding
+ */
+
+ public LocalVariableBinding copy() {
+ return new LocalVariableBinding(variableName, requiredType);
+ }
+
+ /**
+ * Get the name of the variable
+ * @return the name of the variable
+ */
+
+ public StructuredQName getVariableQName() {
+ return variableName;
+ }
+
+ /**
+ * Set the required or inferred type of the variable
+ * @param type the required or inferred type
+ */
+
+ public void setRequiredType(SequenceType type) {
+ requiredType = type;
+ }
+
+ /**
+ * Get the required type (declared type) of the variable
+ * @return the required type
+ */
+
+ public SequenceType getRequiredType() {
+ return requiredType;
+ }
+
+ /**
+ * If the variable is bound to an integer, get the minimum and maximum possible values.
+ * Return null if unknown or not applicable
+ */
+ public IntegerValue[] getIntegerBoundsForVariable() {
+ return null;
+ }
+
+ /**
+ * Get the (nominal) count of the number of references to this variable
+ * @return zero if there are no references, one if there is a single reference that is not in
+ * a loop, some higher number if there are multiple references (or a single reference in a loop),
+ * or the special value @link RangeVariable#FILTERED} if there are any references
+ * in filter expressions that require searching.
+ */
+
+ public int getNominalReferenceCount() {
+ return refCount;
+ }
+
+ /**
+ * Register a variable reference that refers to the variable bound in this expression
+ * @param isLoopingReference - true if the reference occurs within a loop, such as the predicate
+ * of a filter expression
+ */
+
+ public void addReference(boolean isLoopingReference) {
+ if (refCount != FilterExpression.FILTERED) {
+ refCount += (isLoopingReference ? 10 : 1);
+ }
+ }
+
+ /**
+ * Indicate that the variable bound by this let expression should be indexable
+ * (because it is used in an appropriate filter expression)
+ */
+
+ public void setIndexedVariable() {
+ refCount = FilterExpression.FILTERED;
+ }
+
+ /**
+ * Set the name of the variable
+ * @param variableName the name of the variable
+ */
+
+ public void setVariableQName(StructuredQName variableName) {
+ this.variableName = variableName;
+ }
+
+ /**
+ * Set the slot number for the range variable
+ * @param nr the slot number to be used
+ */
+
+ public void setSlotNumber(int nr) {
+ slotNumber = nr;
+ }
+
+ /**
+ * If this is a local variable held on the local stack frame, return the corresponding slot number.
+ * In other cases, return -1.
+ */
+
+ public int getLocalSlotNumber() {
+ return slotNumber;
+ }
+
+ /**
+ * Get the value of the range variable
+ */
+
+ public Sequence evaluateVariable(XPathContext context) throws XPathException {
+ return context.evaluateLocalVariable(slotNumber);
+ }
+
+ /**
+ * Test whether it is permitted to assign to the variable using the saxon:assign
+ * extension element. This will only be for an XSLT global variable where the extra
+ * attribute saxon:assignable="yes" is present.
+ *
+ * @return true if the binding is assignable
+ */
+
+ public boolean isAssignable() {
+ return false;
+ }
+
+ /**
+ * Indicate whether the binding is local or global. A global binding is one that has a fixed
+ * value for the life of a query or transformation; any other binding is local.
+ *
+ * @return true if the binding is global
+ */
+
+ public boolean isGlobal() {
+ return false;
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/flwor/OrderByClause.java b/sf/saxon/expr/flwor/OrderByClause.java
new file mode 100644
index 0000000..1d5b3ff
--- /dev/null
+++ b/sf/saxon/expr/flwor/OrderByClause.java
@@ -0,0 +1,184 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Binding;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.LocalVariableReference;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.SortKeyDefinition;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.SequenceType;
+
+/**
+ * This class represents an "order by" clause in a FLWOR expression
+ */
+public class OrderByClause extends Clause {
+
+ SortKeyDefinition[] sortKeys;
+ AtomicComparer[] comparators;
+ TupleExpression tupleExpression;
+
+ public OrderByClause(SortKeyDefinition[] sortKeys, TupleExpression tupleExpression) {
+ this.sortKeys = sortKeys;
+ this.tupleExpression = tupleExpression;
+ }
+
+ @Override
+ public int getClauseKey() {
+ return ORDERBYCLAUSE;
+ }
+
+ @Override
+ public boolean containsNonInlineableVariableReference(Binding binding) {
+ for (LocalVariableReference ref : tupleExpression.getSlots()) {
+ if (ref.getBinding() == binding) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public OrderByClause copy() {
+ SortKeyDefinition[] sk2 = new SortKeyDefinition[sortKeys.length];
+ for (int i=0; i<sortKeys.length; i++) {
+ sk2[i] = sortKeys[i].copy();
+ }
+ OrderByClause obc = new OrderByClause(sk2, (TupleExpression)tupleExpression.copy());
+ obc.setLocationId(getLocationId());
+ obc.comparators = comparators;
+ return obc;
+ }
+
+ public SortKeyDefinition[] getSortKeyDefinitions() {
+ return sortKeys;
+ }
+
+ public AtomicComparer[] getAtomicComparers() {
+ return comparators;
+ }
+
+ /**
+ * Get a tuple stream that implements the functionality of this clause, taking its
+ * input from another tuple stream which this clause modifies
+ *
+ *
+ * @param base the input tuple stream
+ * @param context
+ * @return the output tuple stream
+ */
+ @Override
+ public TuplePull getPullStream(TuplePull base, XPathContext context) {
+ return new OrderByClausePull(base, tupleExpression, this, context);
+ }
+
+ /**
+ * Get a push-mode tuple stream that implements the functionality of this clause, supplying its
+ * output to another tuple stream
+ *
+ * @param destination the output tuple stream
+ * @param context
+ * @return the push tuple stream that implements the functionality of this clause of the FLWOR
+ * expression
+ */
+ @Override
+ public TuplePush getPushStream(TuplePush destination, XPathContext context) {
+ return new OrderByClausePush(destination, tupleExpression, this, context);
+ }
+
+ /**
+ * Process the subexpressions of this clause
+ *
+ * @param processor the expression processor used to process the subexpressions
+ *
+ */
+ @Override
+ public void processSubExpressions(ExpressionProcessor processor) throws XPathException {
+ tupleExpression = (TupleExpression)processor.processExpression(tupleExpression);
+ for (int i=0; i<sortKeys.length; i++) {
+ sortKeys[i].processSubExpressions(processor);
+ }
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ public void typeCheck(ExpressionVisitor visitor) throws XPathException {
+ boolean allKeysFixed = true;
+ for (int i = 0; i < sortKeys.length; i++) {
+ if (!(sortKeys[i].isFixed())) {
+ allKeysFixed = false;
+ break;
+ }
+ }
+
+ if (allKeysFixed) {
+ comparators = new AtomicComparer[sortKeys.length];
+ }
+
+ for (int i = 0; i < sortKeys.length; i++) {
+ Expression sortKey = sortKeys[i].getSortKey();
+ RoleLocator role = new RoleLocator(RoleLocator.ORDER_BY, "", i);
+ role.setErrorCode("XPTY0004");
+ sortKey = TypeChecker.staticTypeCheck(sortKey, SequenceType.OPTIONAL_ATOMIC, false, role, visitor);
+ sortKeys[i].setSortKey(sortKey, false);
+ //sortKeys[i].typeCheck(visitor, contextItemType);
+ if (sortKeys[i].isFixed()) {
+ AtomicComparer comp = sortKeys[i].makeComparator(
+ visitor.getStaticContext().makeEarlyEvaluationContext());
+ sortKeys[i].setFinalComparator(comp);
+ if (allKeysFixed) {
+ comparators[i] = comp;
+ }
+ }
+
+ }
+ }
+
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+ @Override
+ public void explain(ExpressionPresenter out) {
+ out.startElement("order-by");
+ for (SortKeyDefinition k : sortKeys) {
+ out.startSubsidiaryElement("key");
+ k.getSortKey().explain(out);
+ out.endSubsidiaryElement();
+ }
+ out.endElement();
+ }
+
+ public String toString() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ fsb.append("order by ... ");
+ return fsb.toString();
+ }
+
+ /**
+ * Callback for evaluating the sort keys
+ */
+
+ /*@Nullable*/ public AtomicValue evaluateSortKey(int n, XPathContext c) throws XPathException {
+ return (AtomicValue)sortKeys[n].getSortKey().evaluateItem(c);
+ }
+
+}
+
diff --git a/sf/saxon/expr/flwor/OrderByClausePull.java b/sf/saxon/expr/flwor/OrderByClausePull.java
new file mode 100644
index 0000000..588a463
--- /dev/null
+++ b/sf/saxon/expr/flwor/OrderByClausePull.java
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.*;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+
+import java.util.ArrayList;
+
+/**
+ * Represents the tuple stream delivered by an "order by" clause. This sorts the tuple stream supplied
+ * as its input, and outputs the same tuples but in sorted order.
+ */
+public class OrderByClausePull extends TuplePull implements Sortable {
+
+ private TuplePull base;
+ private OrderByClause orderByClause;
+ private TupleExpression tupleExpr;
+ private int currentPosition = -1;
+ protected AtomicComparer[] comparers;
+ private ArrayList<ItemToBeSorted> tupleArray = new ArrayList<ItemToBeSorted>(100);
+
+ public OrderByClausePull(TuplePull base, TupleExpression tupleExpr, OrderByClause orderBy, XPathContext context) {
+ this.base = base;
+ this.tupleExpr = tupleExpr;
+ this.orderByClause = orderBy;
+
+ AtomicComparer[] suppliedComparers = orderBy.getAtomicComparers();
+ comparers = new AtomicComparer[suppliedComparers.length];
+ for (int n=0; n< comparers.length; n++) {
+ this.comparers[n] = suppliedComparers[n].provideContext(context);
+ }
+ }
+
+ /**
+ * Move on to the next tuple. Before returning, this method must set all the variables corresponding
+ * to the "returned" tuple in the local stack frame associated with the context object
+ *
+ * @param context the dynamic evaluation context
+ * @return true if another tuple has been generated; false if the tuple stream is exhausted. If the
+ * method returns false, the values of the local variables corresponding to this tuple stream
+ * are undefined.
+ */
+ @Override
+ public boolean nextTuple(XPathContext context) throws XPathException {
+ if (currentPosition < 0) {
+ currentPosition = 0;
+ int position = 0;
+
+ while (base.nextTuple(context)) {
+ Tuple tuple = tupleExpr.evaluateItem(context);
+ SortKeyDefinition[] sortKeyDefinitions = orderByClause.getSortKeyDefinitions();
+ ItemToBeSorted itbs = new ItemToBeSorted(sortKeyDefinitions.length);
+ itbs.value = tuple;
+ for (int i=0; i<sortKeyDefinitions.length; i++) {
+ itbs.sortKeyValues[i] = orderByClause.evaluateSortKey(i, context);
+ }
+ itbs.originalPosition = ++position;
+ tupleArray.add(itbs);
+ }
+
+ try {
+ GenericSorter.quickSort(0, position, this);
+ } catch (ClassCastException e) {
+ XPathException err = new XPathException("Non-comparable types found while sorting: " + e.getMessage());
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+ }
+
+ if (currentPosition < tupleArray.size()) {
+ tupleExpr.setCurrentTuple(context, (Tuple)tupleArray.get(currentPosition++).value);
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ /**
+ * Compare two objects within this Sortable, identified by their position.
+ *
+ * @return <0 if obj[a]<obj[b], 0 if obj[a]=obj[b], >0 if obj[a]>obj[b]
+ */
+ public int compare(int a, int b) {
+ try {
+ for (int i=0; i< comparers.length; i++) {
+ int comp = comparers[i].compareAtomicValues(
+ (AtomicValue)tupleArray.get(a).sortKeyValues[i], (AtomicValue)tupleArray.get(b).sortKeyValues[i]);
+ if (comp != 0) {
+ // we have found a difference, so we can return
+ return comp;
+ }
+ }
+ } catch (NoDynamicContextException e) {
+ throw new AssertionError("Sorting without dynamic context: " + e.getMessage());
+ }
+
+ // all sort keys equal: return the items in their original order
+
+ return tupleArray.get(a).originalPosition - tupleArray.get(b).originalPosition;
+ }
+
+ /**
+ * Swap two objects within this Sortable, identified by their position.
+ */
+ public void swap(int a, int b) {
+ ItemToBeSorted temp = tupleArray.get(a);
+ tupleArray.set(a, tupleArray.get(b));
+ tupleArray.set(b, temp);
+ }
+
+ /**
+ * Close the tuple stream, indicating that although not all tuples have been read,
+ * no further tuples are required and resources can be released
+ */
+ @Override
+ public void close() {
+ base.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/OrderByClausePush.java b/sf/saxon/expr/flwor/OrderByClausePush.java
new file mode 100644
index 0000000..821eb90
--- /dev/null
+++ b/sf/saxon/expr/flwor/OrderByClausePush.java
@@ -0,0 +1,116 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.*;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+
+import java.util.ArrayList;
+
+/**
+ * Represents the tuple stream delivered by an "order by" clause. This sorts the tuple stream supplied
+ * as its input, and outputs the same tuples but in sorted order.
+ */
+public class OrderByClausePush extends TuplePush implements Sortable {
+
+ private TuplePush destination;
+ private OrderByClause orderByClause;
+ private TupleExpression tupleExpr;
+ protected AtomicComparer[] comparers;
+ XPathContext context;
+ int position = 0;
+ private ArrayList<ItemToBeSorted> tupleArray = new ArrayList<ItemToBeSorted>(100);
+
+ public OrderByClausePush(TuplePush destination, TupleExpression tupleExpr, OrderByClause orderBy, XPathContext context) {
+ this.destination = destination;
+ this.tupleExpr = tupleExpr;
+ this.orderByClause = orderBy;
+ this.context = context;
+
+ AtomicComparer[] suppliedComparers = orderBy.getAtomicComparers();
+ comparers = new AtomicComparer[suppliedComparers.length];
+ for (int n=0; n< comparers.length; n++) {
+ this.comparers[n] = suppliedComparers[n].provideContext(context);
+ }
+ }
+
+ /**
+ * Process the next tuple.
+ */
+ @Override
+ public void processTuple(XPathContext context) throws XPathException {
+
+ Tuple tuple = tupleExpr.evaluateItem(context);
+ SortKeyDefinition[] sortKeyDefinitions = orderByClause.getSortKeyDefinitions();
+ ItemToBeSorted itbs = new ItemToBeSorted(sortKeyDefinitions.length);
+ itbs.value = tuple;
+ for (int i=0; i<sortKeyDefinitions.length; i++) {
+ itbs.sortKeyValues[i] = orderByClause.evaluateSortKey(i, context);
+ }
+ itbs.originalPosition = ++position;
+ tupleArray.add(itbs);
+
+ }
+
+ /**
+ * Compare two objects within this Sortable, identified by their position.
+ *
+ * @return <0 if obj[a]<obj[b], 0 if obj[a]=obj[b], >0 if obj[a]>obj[b]
+ */
+ public int compare(int a, int b) {
+ try {
+ for (int i=0; i< comparers.length; i++) {
+ int comp = comparers[i].compareAtomicValues(
+ (AtomicValue)tupleArray.get(a).sortKeyValues[i], (AtomicValue)tupleArray.get(b).sortKeyValues[i]);
+ if (comp != 0) {
+ // we have found a difference, so we can return
+ return comp;
+ }
+ }
+ } catch (NoDynamicContextException e) {
+ throw new AssertionError("Sorting without dynamic context: " + e.getMessage());
+ }
+
+ // all sort keys equal: return the items in their original order
+
+ return tupleArray.get(a).originalPosition - tupleArray.get(b).originalPosition;
+ }
+
+ /**
+ * Swap two objects within this Sortable, identified by their position.
+ */
+ public void swap(int a, int b) {
+ ItemToBeSorted temp = tupleArray.get(a);
+ tupleArray.set(a, tupleArray.get(b));
+ tupleArray.set(b, temp);
+ }
+
+ /**
+ * Close the tuple stream, indicating that no more tuples will be delivered
+ */
+ @Override
+ public void close() throws XPathException {
+ try {
+ GenericSorter.quickSort(0, position, this);
+ } catch (ClassCastException e) {
+ XPathException err = new XPathException("Non-comparable types found while sorting: " + e.getMessage());
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+
+ for (ItemToBeSorted itbs : tupleArray) {
+ tupleExpr.setCurrentTuple(context, (Tuple)itbs.value);
+ destination.processTuple(context);
+ }
+ destination.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/ReturnClauseIterator.java b/sf/saxon/expr/flwor/ReturnClauseIterator.java
new file mode 100644
index 0000000..7ebd5c1
--- /dev/null
+++ b/sf/saxon/expr/flwor/ReturnClauseIterator.java
@@ -0,0 +1,114 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This iterator applies the return expression of a FLWOR expression to each
+ * of the tuples in a supplied tuple stream, returning the result as an iterator
+ */
+
+public class ReturnClauseIterator implements SequenceIterator<Item> {
+
+ private TuplePull base;
+ private FLWORExpression flwor;
+ /*@Nullable*/ private Expression action;
+ private XPathContext context;
+ private SequenceIterator results = null;
+ private Item current = null;
+ private int position = 0;
+
+ /**
+ * Construct an iterator over the results of the FLWOR expression.
+ * @param base the base iterator
+ * @param flwor the FLWOR expression
+ * @param context the XPath dynamic context
+ */
+
+ public ReturnClauseIterator(TuplePull base, FLWORExpression flwor, XPathContext context) {
+ this.base = base;
+ this.flwor = flwor;
+ this.action = flwor.getReturnClause();
+ this.context = context;
+ }
+
+ public Item next() throws XPathException {
+ Item nextItem;
+ while (true) {
+ if (results != null) {
+ nextItem = results.next();
+ if (nextItem != null) {
+ break;
+ } else {
+ results = null;
+ }
+ }
+ if (base.nextTuple(context)) {
+ // Call the supplied return expression
+ results = action.iterate(context);
+ nextItem = results.next();
+ if (nextItem == null) {
+ results = null;
+ } else {
+ break;
+ }
+ // now go round the loop to get the next item from the base sequence
+ } else {
+ results = null;
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+
+ current = nextItem;
+ position++;
+ return nextItem;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ if (results != null) {
+ results.close();
+ }
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return flwor.iterate(context);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED}, {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/expr/flwor/ReturnClausePush.java b/sf/saxon/expr/flwor/ReturnClausePush.java
new file mode 100644
index 0000000..2f4b2d8
--- /dev/null
+++ b/sf/saxon/expr/flwor/ReturnClausePush.java
@@ -0,0 +1,48 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * The class represents the final stage in a push-mode tuple pipeline. The previous stages
+ * have stored the values corresponding to the current tuple in local variables on the
+ * stack; all that remains is to evaluate the return expression (with reference to these
+ * variables) and send the results to the current receiver.
+ */
+public class ReturnClausePush extends TuplePush {
+
+ private Expression returnExpr;
+
+ public ReturnClausePush(Expression returnExpr) {
+ this.returnExpr = returnExpr;
+ }
+
+ /**
+ * Notify the availability of the next tuple. Before calling this method,
+ * the supplier of the tuples must set all the variables corresponding
+ * to the supplied tuple in the local stack frame associated with the context object
+ *
+ * @param context the dynamic evaluation context
+ */
+ @Override
+ public void processTuple(XPathContext context) throws XPathException {
+ returnExpr.process(context);
+ }
+
+ /**
+ * Close the tuple stream, indicating that no more tuples will be supplied
+ */
+ @Override
+ public void close() throws XPathException {
+ // no action
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/SingularityPull.java b/sf/saxon/expr/flwor/SingularityPull.java
new file mode 100644
index 0000000..c2257ad
--- /dev/null
+++ b/sf/saxon/expr/flwor/SingularityPull.java
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+
+/**
+ * The "singularity" tuple stream delivers a single empty tuple. It is the base tuple stream
+ * for the outermost for/let clause in a FLWOR expression
+ */
+public class SingularityPull extends TuplePull {
+
+ private boolean done = false;
+
+ /**
+ * Move on to the next tuple. Before returning, this method must set all the variables corresponding
+ * to the "returned" tuple in the local stack frame associated with the context object
+ *
+ * @param context the dynamic evaluation context
+ * @return true if another tuple has been generated; false if the tuple stream is exhausted. If the
+ * method returns false, the values of the local variables corresponding to this tuple stream
+ * are undefined.
+ */
+ @Override
+ public boolean nextTuple(XPathContext context) {
+ if (done) {
+ return false;
+ } else {
+ done = true;
+ return true;
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/TraceClause.java b/sf/saxon/expr/flwor/TraceClause.java
new file mode 100644
index 0000000..ac5913e
--- /dev/null
+++ b/sf/saxon/expr/flwor/TraceClause.java
@@ -0,0 +1,117 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A "trace" clause in a FLWOR expression, added by a TraceCodeInjector for diagnostic
+ * tracing, debugging, profiling or similar purposes.
+ */
+public class TraceClause extends Clause {
+
+ private Clause target;
+ private Container container;
+ private NamespaceResolver nsResolver;
+
+ /**
+ * Create a traceClause
+ * @param target the clause whose evaluation is being traced
+ * @param container the container of the containing FLWORExpression
+ */
+
+ public TraceClause(Clause target, NamespaceResolver nsResolver, Container container) {
+ this.target = target;
+ this.nsResolver = nsResolver;
+ this.container = container;
+ }
+
+ /**
+ * Get the namespace bindings from the static context of the clause
+ * @return a namespace resolver that reflects the in scope namespaces of the clause
+ */
+
+ public NamespaceResolver getNamespaceResolver() {
+ return nsResolver;
+ }
+
+ /**
+ * Set the namespace bindings from the static context of the clause
+ * @param nsResolver a namespace resolver that reflects the in scope namespaces of the clause
+ */
+
+ public void setNamespaceResolver(NamespaceResolver nsResolver) {
+ this.nsResolver = nsResolver;
+ }
+
+
+ @Override
+ public int getClauseKey() {
+ return TRACE;
+ }
+
+ public TraceClause copy() {
+ return new TraceClause(target, nsResolver, container);
+ }
+
+ /**
+ * Get a tuple stream that implements the functionality of this clause, taking its
+ * input from another tuple stream which this clause modifies
+ * @param base the input tuple stream
+ * @param context the dynamic evaluation context
+ * @return the output tuple stream
+ */
+ @Override
+ public TuplePull getPullStream(TuplePull base, XPathContext context) {
+ return new TraceClausePull(base, this, target, container);
+ }
+
+ /**
+ * Get a push-mode tuple stream that implements the functionality of this clause, supplying its
+ * output to another tuple stream
+ *
+ * @param destination the output tuple stream
+ * @param context the dynamic evaluation context
+ * @return the push tuple stream that implements the functionality of this clause of the FLWOR
+ * expression
+ */
+ @Override
+ public TuplePush getPushStream(TuplePush destination, XPathContext context) {
+ return new TraceClausePush(destination, this, target, container);
+ }
+
+ /**
+ * Process the subexpressions of this clause
+ * @param processor the expression processor used to process the subexpressions
+ *
+ */
+ @Override
+ public void processSubExpressions(ExpressionProcessor processor) throws XPathException {
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+ @Override
+ public void explain(ExpressionPresenter out) {
+ out.startElement("trace");
+ out.endElement();
+ }
+
+ public String toString() {
+ return "trace";
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/TraceClausePull.java b/sf/saxon/expr/flwor/TraceClausePull.java
new file mode 100644
index 0000000..62a7a43
--- /dev/null
+++ b/sf/saxon/expr/flwor/TraceClausePull.java
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class represents the tuple stream delivered as the output of a trace clause in a
+ * FLWOR expression. It does not change the values of any variables in the tuple stream,
+ * but merely informs the TraceListener whenever a new tuple is requested.
+ */
+public class TraceClausePull extends TuplePull {
+
+ private TuplePull base;
+ private Clause baseClause;
+ private TraceClause traceClause;
+ private Container container;
+
+ public TraceClausePull(TuplePull base, TraceClause traceClause, Clause baseClause, Container container) {
+ this.base = base;
+ this.traceClause = traceClause;
+ this.baseClause = baseClause;
+ this.container = container;
+ }
+
+ /**
+ * Move on to the next tuple. Before returning, this method must set all the variables corresponding
+ * to the "returned" tuple in the local stack frame associated with the context object
+ *
+ * @param context the dynamic evaluation context
+ * @return true if another tuple has been generated; false if the tuple stream is exhausted. If the
+ * method returns false, the values of the local variables corresponding to this tuple stream
+ * are undefined.
+ */
+ @Override
+ public boolean nextTuple(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ if (controller.isTracing()) {
+ ClauseInfo baseInfo = new ClauseInfo(baseClause, container);
+ baseInfo.setNamespaceResolver(traceClause.getNamespaceResolver());
+ controller.getTraceListener().enter(baseInfo, context);
+ boolean b = base.nextTuple(context);
+ controller.getTraceListener().leave(baseInfo);
+ return b;
+ } else {
+ return base.nextTuple(context);
+ }
+
+ }
+
+ /**
+ * Close the tuple stream, indicating that although not all tuples have been read,
+ * no further tuples are required and resources can be released
+ */
+ @Override
+ public void close() {
+ base.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/TraceClausePush.java b/sf/saxon/expr/flwor/TraceClausePush.java
new file mode 100644
index 0000000..1d36939
--- /dev/null
+++ b/sf/saxon/expr/flwor/TraceClausePush.java
@@ -0,0 +1,59 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class represents the tuple stream delivered as the output of a trace clause in a
+ * FLWOR expression. It does not change the values of any variables in the tuple stream,
+ * but merely notifies the TraceListener whenever a new tuple is available.
+ */
+public class TraceClausePush extends TuplePush {
+
+ private TuplePush destination;
+ TraceClause traceClause;
+ private Clause baseClause;
+ private Container container;
+
+ public TraceClausePush(TuplePush destination, TraceClause traceClause, Clause baseClause, Container container) {
+ this.destination = destination;
+ this.traceClause = traceClause;
+ this.baseClause = baseClause;
+ this.container = container;
+ }
+
+ /*
+ * Process the next tuple.
+ */
+ @Override
+ public void processTuple(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ if (controller.isTracing()) {
+ ClauseInfo baseInfo = new ClauseInfo(baseClause, container);
+ baseInfo.setNamespaceResolver(traceClause.getNamespaceResolver());
+ controller.getTraceListener().enter(baseInfo, context);
+ destination.processTuple(context);
+ controller.getTraceListener().leave(baseInfo);
+ } else {
+ destination.processTuple(context);
+ }
+ }
+
+ /*
+ * Notify the end of the tuple stream
+ */
+ @Override
+ public void close() throws XPathException {
+ destination.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/Tuple.java b/sf/saxon/expr/flwor/Tuple.java
new file mode 100644
index 0000000..a7a43e7
--- /dev/null
+++ b/sf/saxon/expr/flwor/Tuple.java
@@ -0,0 +1,26 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.value.ObjectValue;
+
+/**
+ * A tuple, as it appears in an XQuery tuple stream handled by extended FLWOR expressions.
+ */
+public class Tuple extends ObjectValue {
+
+ public Tuple(Sequence[] members) {
+ super(members);
+ }
+
+ public Sequence[] getMembers() {
+ return (Sequence[])getObject();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/TupleExpression.java b/sf/saxon/expr/flwor/TupleExpression.java
new file mode 100644
index 0000000..6fd6b1f
--- /dev/null
+++ b/sf/saxon/expr/flwor/TupleExpression.java
@@ -0,0 +1,177 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.TupleExpressionCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.LocalVariableReference;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ExternalObjectType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A tuple expression is an expression that returns a tuple. Specifically,
+ * it is a list of slot numbers of local variables; it returns a Tuple item
+ * containg the current value of these variables.
+ */
+public class TupleExpression extends Expression {
+
+ LocalVariableReference[] slots;
+
+ public TupleExpression() {
+ slots = new LocalVariableReference[0]; // temporarily
+ }
+
+ public void setVariables(List<LocalVariableReference> refs) {
+ slots = new LocalVariableReference[refs.size()];
+ slots = refs.toArray(slots);
+ }
+
+ public LocalVariableReference[] getSlots() {
+ return slots;
+ }
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return new ExternalObjectType(Object.class, th.getConfiguration());
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor,
+ /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType)
+ throws XPathException {
+ for (int i = 0; i < slots.length; i++) {
+ slots[i] = (LocalVariableReference) visitor.typeCheck(slots[i], contextItemType);
+ }
+ return this;
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ /*@NotNull*/
+ @Override
+ public Iterator<Expression> iterateSubExpressions() {
+ return Arrays.asList((Expression[]) slots).iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+ @Override
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ for (int i = 0; i < slots.length; i++) {
+ if (original == slots[i]) {
+ slots[i] = (LocalVariableReference) replacement;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ LocalVariableReference[] refs2 = new LocalVariableReference[slots.length];
+ for (int i = 0; i < slots.length; i++) {
+ refs2[i] = (LocalVariableReference) slots[i].copy();
+ }
+ TupleExpression t2 = new TupleExpression();
+ t2.slots = refs2;
+ return t2;
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("tuple");
+ for (int i = 0; i < slots.length; i++) {
+ slots[i].explain(out);
+ }
+ out.endElement();
+ }
+
+
+ public Tuple evaluateItem(XPathContext context) throws XPathException {
+ Sequence[] tuple = new Sequence[slots.length];
+ for (int i = 0; i < slots.length; i++) {
+ tuple[i] = slots[i].evaluateVariable(context);
+ }
+ return new Tuple(tuple);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Tuple expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new TupleExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * Set the local variables in the current stack frame to values corresponding to a supplied tuple
+ *
+ * @param context identifies the stack frame to be modified
+ * @param tuple the tuple containing the current values
+ */
+
+ public void setCurrentTuple(XPathContext context, Tuple tuple) {
+ Sequence[] members = tuple.getMembers();
+ for (int i = 0; i < slots.length; i++) {
+ context.setLocalVariable(slots[i].getBinding().getLocalSlotNumber(), members[i]);
+ }
+ }
+
+ /**
+ * Get the cardinality of the expression. This is exactly one, in the sense
+ * that evaluating the TupleExpression returns a single tuple.
+ *
+ * @return the static cardinality - EXACTLY_ONE
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ public int getIntrinsicDependencies() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/expr/flwor/TuplePull.java b/sf/saxon/expr/flwor/TuplePull.java
new file mode 100644
index 0000000..fdd2d66
--- /dev/null
+++ b/sf/saxon/expr/flwor/TuplePull.java
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Abtract class representing a tuple stream (used to evaluate a FLWOR expression) in pull mode
+ * (where the consumer of tuples activates the provider of those tuples)
+ */
+public abstract class TuplePull {
+
+ /**
+ * Move on to the next tuple. Before returning, this method must set all the variables corresponding
+ * to the "returned" tuple in the local stack frame associated with the context object
+ * @param context the dynamic evaluation context
+ * @return true if another tuple has been generated; false if the tuple stream is exhausted. If the
+ * method returns false, the values of the local variables corresponding to this tuple stream
+ * are undefined.
+ */
+
+ public abstract boolean nextTuple(XPathContext context) throws XPathException;
+
+ /**
+ * Close the tuple stream, indicating that although not all tuples have been read,
+ * no further tuples are required and resources can be released
+ */
+
+ public void close() {
+ // default implementation takes no action
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/TuplePush.java b/sf/saxon/expr/flwor/TuplePush.java
new file mode 100644
index 0000000..86bf8d7
--- /dev/null
+++ b/sf/saxon/expr/flwor/TuplePush.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Abtract class representing a tuple stream (used to evaluate a FLWOR expression) in push mode
+ * (where the provider of tuples activates the consumer of those tuples)
+ */
+public abstract class TuplePush {
+
+ /**
+ * Notify the availability of the next tuple. Before calling this method,
+ * the supplier of the tuples must set all the variables corresponding
+ * to the supplied tuple in the local stack frame associated with the context object
+ * @param context the dynamic evaluation context
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public abstract void processTuple(XPathContext context) throws XPathException;
+
+ /**
+ * Close the tuple stream, indicating that no more tuples will be supplied
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public void close() throws XPathException {
+ // default implementation takes no action
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/WhereClause.java b/sf/saxon/expr/flwor/WhereClause.java
new file mode 100644
index 0000000..626fcb3
--- /dev/null
+++ b/sf/saxon/expr/flwor/WhereClause.java
@@ -0,0 +1,137 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.List;
+
+/**
+ * A "where" clause in a FLWOR expression
+ */
+public class WhereClause extends Clause {
+
+ private Expression predicate;
+
+ public WhereClause(Expression predicate) {
+ this.predicate = predicate;
+ }
+
+ @Override
+ public int getClauseKey() {
+ return WHERE;
+ }
+
+ public Expression getPredicate(){
+ return predicate;
+ }
+
+ public void setPredicate(Expression predicate){
+ this.predicate = predicate;
+ }
+
+ public WhereClause copy() {
+ WhereClause w2 = new WhereClause(predicate.copy());
+ w2.setLocationId(getLocationId());
+ return w2;
+ }
+
+ /**
+ * Type-check the expression
+ */
+ @Override
+ public void typeCheck(ExpressionVisitor visitor) throws XPathException {
+ super.typeCheck(visitor);
+ }
+
+
+ /**
+ * Get a tuple stream that implements the functionality of this clause, taking its
+ * input from another tuple stream which this clause modifies
+ * @param base the input tuple stream
+ * @param context the dynamic evaluation context
+ * @return the output tuple stream
+ */
+ @Override
+ public TuplePull getPullStream(TuplePull base, XPathContext context) {
+ return new WhereClausePull(base, predicate);
+ }
+
+ @Override
+ public void gatherVariableReferences(final ExpressionVisitor visitor, Binding binding, List<VariableReference> references){
+ ExpressionTool.gatherVariableReferences(predicate, binding, references);
+ }
+
+ @Override
+ public void refineVariableType(ExpressionVisitor visitor, List<VariableReference> references, Expression returnExpr) {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ final ItemType actualItemType = predicate.getItemType(th);
+ for (VariableReference ref : references) {
+ ref.refineVariableType(actualItemType, predicate.getCardinality(),
+ (predicate instanceof Literal ? ((Literal) predicate).getValue() : null),
+ predicate.getSpecialProperties(), visitor);
+ visitor.resetStaticProperties();
+ }
+ }
+
+ /**
+ * Get a push-mode tuple stream that implements the functionality of this clause, supplying its
+ * output to another tuple stream
+ *
+ * @param destination the output tuple stream
+ * @param context the dynamic evaluation context
+ * @return the push tuple stream that implements the functionality of this clause of the FLWOR
+ * expression
+ */
+ @Override
+ public TuplePush getPushStream(TuplePush destination, XPathContext context) {
+ return new WhereClausePush(destination, predicate);
+ }
+
+ /**
+ * Process the subexpressions of this clause
+ * @param processor the expression processor used to process the subexpressions
+ *
+ */
+ @Override
+ public void processSubExpressions(ExpressionProcessor processor) throws XPathException {
+ predicate = processor.processExpression(predicate);
+ }
+
+ public Expression getBaseExpression(){
+ return predicate;
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+ @Override
+ public void explain(ExpressionPresenter out) {
+ out.startElement("where");
+ predicate.explain(out);
+ out.endElement();
+ }
+
+ public String toString() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ fsb.append("where ");
+ fsb.append(predicate.toString());
+ return fsb.toString();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/WhereClausePull.java b/sf/saxon/expr/flwor/WhereClausePull.java
new file mode 100644
index 0000000..3b94398
--- /dev/null
+++ b/sf/saxon/expr/flwor/WhereClausePull.java
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class represents the tuple stream delivered as the output of a where clause in a
+ * FLWOR expression: that is, it returns all the tuples in its input stream that satisfy
+ * a specified predicate. It does not change the values of any variables in the tuple stream.
+ */
+public class WhereClausePull extends TuplePull {
+
+ TuplePull base;
+ Expression predicate;
+
+ public WhereClausePull(TuplePull base, Expression predicate) {
+ this.base = base;
+ this.predicate = predicate;
+ }
+
+ /**
+ * Move on to the next tuple. Before returning, this method must set all the variables corresponding
+ * to the "returned" tuple in the local stack frame associated with the context object
+ *
+ * @param context the dynamic evaluation context
+ * @return true if another tuple has been generated; false if the tuple stream is exhausted. If the
+ * method returns false, the values of the local variables corresponding to this tuple stream
+ * are undefined.
+ */
+ @Override
+ public boolean nextTuple(XPathContext context) throws XPathException {
+ while (base.nextTuple(context)) {
+ if (predicate.effectiveBooleanValue(context)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Close the tuple stream, indicating that although not all tuples have been read,
+ * no further tuples are required and resources can be released
+ */
+ @Override
+ public void close() {
+ base.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/WhereClausePush.java b/sf/saxon/expr/flwor/WhereClausePush.java
new file mode 100644
index 0000000..bdab9b2
--- /dev/null
+++ b/sf/saxon/expr/flwor/WhereClausePush.java
@@ -0,0 +1,47 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.flwor;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class represents the tuple stream delivered as the output of a where clause in a
+ * FLWOR expression: that is, it supplies all the tuples in its input stream that satisfy
+ * a specified predicate. It does not change the values of any variables in the tuple stream.
+ */
+public class WhereClausePush extends TuplePush {
+
+ TuplePush destination;
+ Expression predicate;
+
+ public WhereClausePush(TuplePush destination, Expression predicate) {
+ this.destination = destination;
+ this.predicate = predicate;
+ }
+
+ /*
+ * Process the next tuple.
+ */
+ @Override
+ public void processTuple(XPathContext context) throws XPathException {
+ if (predicate.effectiveBooleanValue(context)) {
+ destination.processTuple(context);
+ }
+ }
+
+ /*
+ * Notify the end of the tuple stream
+ */
+ @Override
+ public void close() throws XPathException {
+ destination.close();
+ }
+}
+
diff --git a/sf/saxon/expr/flwor/package.html b/sf/saxon/expr/flwor/package.html
new file mode 100644
index 0000000..d0a930c
--- /dev/null
+++ b/sf/saxon/expr/flwor/package.html
@@ -0,0 +1,37 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.expr.flwor</title>
+
+</head>
+ <body>
+ <p>This package contains classes responsible for evaluation of FLWOR expressions, in particular,
+ those clauses of FLWOR expressions that are supported in Saxon-HE.</p>
+
+ <p>FLWOR expressions are implemented as a pipeline of clauses, much as described in the specification.
+ The pipeline can be evaluated in push or pull mode: in push mode, the supplier of tuples activates the consumer
+ of tuples when a tuple is ready to be processed, while in pull mode, the consumer of tuples calls the supplier
+ to request the next tuple. In both cases the "tuple" is not actually passed as an argument or result of this call,
+ but is represented by the state of local variables in the XPathContext stack on completion of the call. The only
+ time tuples are represented as real objects is when the processing is not pipelined, for example when tuples need
+ to be sorted or grouped.</p>
+
+ <p>Simple "for" and "let" expressions do not use this mechanism: instead, they are compiled to a
+ ForExpression or LetExpression.</p>
+
+
+
+ <p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+28 November 2011</i></p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/expr/instruct/AnalyzeString.java b/sf/saxon/expr/instruct/AnalyzeString.java
new file mode 100644
index 0000000..e4275e1
--- /dev/null
+++ b/sf/saxon/expr/instruct/AnalyzeString.java
@@ -0,0 +1,597 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.AnalyzeStringCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.regex.RegexIterator;
+import net.sf.saxon.regex.RegularExpression;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An xsl:analyze-string element in the stylesheet. New at XSLT 2.0
+ */
+
+public class AnalyzeString extends Instruction {
+
+ private Expression select;
+ private Expression regex;
+ private Expression flags;
+ private Expression matching;
+ private Expression nonMatching;
+ private RegularExpression pattern;
+
+ private boolean allow30features = false;
+ private boolean useXsltErrorCodes = true;
+
+ /**
+ * Construct an AnalyzeString instruction
+ *
+ * @param select the expression containing the input string
+ * @param regex the regular expression
+ * @param flags the flags parameter
+ * @param matching actions to be applied to a matching substring
+ * @param nonMatching actions to be applied to a non-matching substring
+ * @param pattern the compiled regular expression, if it was known statically
+ */
+ public AnalyzeString(Expression select,
+ Expression regex,
+ Expression flags,
+ Expression matching,
+ Expression nonMatching,
+ RegularExpression pattern) {
+ this.select = select;
+ this.regex = regex;
+ this.flags = flags;
+ this.matching = matching;
+ this.nonMatching = nonMatching;
+ this.pattern = pattern;
+
+ Iterator kids = iterateSubExpressions();
+ while (kids.hasNext()) {
+ Expression child = (Expression) kids.next();
+ adoptChildExpression(child);
+ }
+
+ }
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_ANALYZE_STRING;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is prefered.
+ */
+
+ public int getImplementationMethod() {
+ return Expression.PROCESS_METHOD | Expression.ITERATE_METHOD;
+ }
+
+ /**
+ * @return the expression containing the input string
+ */
+ public Expression getSelectExpression() {
+ return select;
+ }
+
+ /**
+ * @return the compiled regular expression, if it was known statically
+ */
+ public RegularExpression getPatternExpression() {
+ return pattern;
+ }
+
+ /**
+ * @return the flags parameter
+ */
+ public Expression getFlagsExpression() {
+ return flags;
+ }
+
+ /**
+ * @return if allow XSLT 3.0 features
+ */
+ public boolean isAllow30features() {
+ return allow30features;
+ }
+
+ /**
+ * Say whether the expression should return the error codes for the fn:analyze-string function
+ * or the xsl:analyze-string instruction
+ *
+ * @param xslt if true use the error codes for xsl:analyze-string, otherwise use the error codes for fn:analyze-string
+ */
+ public void setUseXsltErrorCodes(boolean xslt) {
+ useXsltErrorCodes = xslt;
+ }
+
+ /**
+ * Ask whether the expression should return the error codes for the fn:analyze-string function
+ * or the xsl:analyze-string instruction
+ *
+ * @return true if using the error codes for xsl:analyze-string, otherwise use the error codes for fn:analyze-string
+ */
+
+ public boolean isUseXsltErrorCodes() {
+ return useXsltErrorCodes;
+ }
+
+ /**
+ * @return the regular expression
+ */
+ public Expression getRegexExpression() {
+ return regex;
+ }
+
+ /**
+ * Get the expression used to process matching substrings
+ *
+ * @return the expression used to process matching substrings
+ */
+
+ public Expression getMatchingExpression() {
+ return matching;
+ }
+
+ /**
+ * Get the expression used to process non-matching substrings
+ *
+ * @return the expression used to process non-matching substrings
+ */
+
+ public Expression getNonMatchingExpression() {
+ return nonMatching;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression).
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ * @throws XPathException if an error is discovered during expression
+ * rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ select = visitor.simplify(select);
+ regex = visitor.simplify(regex);
+ flags = visitor.simplify(flags);
+ matching = visitor.simplify(matching);
+ nonMatching = visitor.simplify(nonMatching);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ allow30features = visitor.getStaticContext().getXPathLanguageLevel().equals(DecimalValue.THREE);
+ select = visitor.typeCheck(select, contextItemType);
+ adoptChildExpression(select);
+ regex = visitor.typeCheck(regex, contextItemType);
+ adoptChildExpression(regex);
+ flags = visitor.typeCheck(flags, contextItemType);
+ adoptChildExpression(flags);
+ if (matching != null) {
+ matching = visitor.typeCheck(matching, new ExpressionVisitor.ContextItemType(BuiltInAtomicType.STRING, false));
+ adoptChildExpression(matching);
+ }
+ if (nonMatching != null) {
+ nonMatching = visitor.typeCheck(nonMatching, new ExpressionVisitor.ContextItemType(BuiltInAtomicType.STRING, false));
+ adoptChildExpression(nonMatching);
+ }
+ RoleLocator role = new RoleLocator(RoleLocator.INSTRUCTION, "analyze-string/select", 0);
+ SequenceType required = (allow30features ? SequenceType.OPTIONAL_STRING : SequenceType.SINGLE_STRING);
+ // see bug 7976
+ select = TypeChecker.staticTypeCheck(select, required, false, role, visitor);
+
+ role = new RoleLocator(RoleLocator.INSTRUCTION, "analyze-string/regex", 0);
+ regex = TypeChecker.staticTypeCheck(regex, SequenceType.SINGLE_STRING, false, role, visitor);
+
+ role = new RoleLocator(RoleLocator.INSTRUCTION, "analyze-string/flags", 0);
+ flags = TypeChecker.staticTypeCheck(flags, SequenceType.SINGLE_STRING, false, role, visitor);
+
+ return this;
+ }
+
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ select = visitor.optimize(select, contextItemType);
+ adoptChildExpression(select);
+ regex = visitor.optimize(regex, contextItemType);
+ adoptChildExpression(regex);
+ flags = visitor.optimize(flags, contextItemType);
+ adoptChildExpression(flags);
+ if (matching != null) {
+ matching = matching.optimize(visitor, new ExpressionVisitor.ContextItemType(BuiltInAtomicType.STRING, false));
+ adoptChildExpression(matching);
+ }
+ if (nonMatching != null) {
+ nonMatching = nonMatching.optimize(visitor, new ExpressionVisitor.ContextItemType(BuiltInAtomicType.STRING, false));
+ adoptChildExpression(nonMatching);
+ }
+ if (pattern == null && regex instanceof StringLiteral && flags instanceof StringLiteral) {
+ try {
+ final CharSequence regex = ((StringLiteral) this.regex).getStringValue();
+ final CharSequence flagstr = ((StringLiteral) flags).getStringValue();
+
+ String hostLang = (DecimalValue.THREE.equals(visitor.getStaticContext().getXPathLanguageLevel()) ? "XP30" : "XP20");
+ List<String> warnings = new ArrayList<String>();
+ pattern = Configuration.getPlatform().compileRegularExpression(regex, flagstr.toString(), hostLang, warnings);
+ for (String w : warnings) {
+ visitor.getStaticContext().issueWarning(w, this);
+ }
+
+ if (pattern.matches("")) {
+ // prevent it being reported more than once
+ pattern = Configuration.getPlatform().compileRegularExpression("x", "", "XP20", warnings);
+ invalidRegex("The regular expression must not be one that matches a zero-length string",
+ (useXsltErrorCodes ? "XTDE1150" : "FORX0003"));
+ }
+ } catch (XPathException err) {
+ if ("XTDE1150".equals(err.getErrorCodeLocalPart())) {
+ throw err;
+ }
+ if ("FORX0001".equals(err.getErrorCodeLocalPart())) {
+ invalidRegex("Error in regular expression flags: " + err, (useXsltErrorCodes ? "XTDE1145" : "FORX0001"));
+ } else {
+ invalidRegex("Error in regular expression: " + err, (useXsltErrorCodes ? "XTDE1140" : err.getErrorCodeLocalPart()));
+ }
+ }
+ }
+ return this;
+ }
+
+ private void invalidRegex(String message, String errorCode) throws XPathException {
+ pattern = null;
+ XPathException err = new XPathException(message, errorCode);
+ err.setLocator(this);
+ throw err;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new AnalyzeString(copy(select), copy(regex), copy(flags), copy(matching), copy(nonMatching), pattern);
+ }
+
+ private Expression copy(Expression exp) {
+ return (exp == null ? null : exp.copy());
+ }
+
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ if (matching != null) {
+ matching.checkPermittedContents(parentType, env, false);
+ }
+ if (nonMatching != null) {
+ nonMatching.checkPermittedContents(parentType, env, false);
+ }
+ }
+
+ /**
+ * Get the item type of the items returned by evaluating this instruction
+ *
+ * @param th the type hierarchy cache
+ * @return the static item type of the instruction
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (matching != null) {
+ if (nonMatching != null) {
+ return Type.getCommonSuperType(matching.getItemType(th), nonMatching.getItemType(th), th);
+ } else {
+ return matching.getItemType(th);
+ }
+ } else {
+ if (nonMatching != null) {
+ return nonMatching.getItemType(th);
+ } else {
+ return ErrorType.getInstance();
+ }
+ }
+ }
+
+ /**
+ * Compute the dependencies of an expression, as the union of the
+ * dependencies of its subexpressions. (This is overridden for path expressions
+ * and filter expressions, where the dependencies of a subexpression are not all
+ * propogated). This method should be called only once, to compute the dependencies;
+ * after that, getDependencies should be used.
+ *
+ * @return the depencies, as a bit-mask
+ */
+
+ public int computeDependencies() {
+ // some of the dependencies in the "action" part and in the grouping and sort keys aren't relevant,
+ // because they don't depend on values set outside the for-each-group expression
+ int dependencies = 0;
+ dependencies |= select.getDependencies();
+ dependencies |= regex.getDependencies();
+ dependencies |= flags.getDependencies();
+ if (matching != null) {
+ dependencies |= (matching.getDependencies() & ~
+ (StaticProperty.DEPENDS_ON_FOCUS | StaticProperty.DEPENDS_ON_REGEX_GROUP));
+ }
+ if (nonMatching != null) {
+ dependencies |= (nonMatching.getDependencies() & ~
+ (StaticProperty.DEPENDS_ON_FOCUS | StaticProperty.DEPENDS_ON_REGEX_GROUP));
+ }
+ return dependencies;
+ }
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ *
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ select = doPromotion(select, offer);
+ regex = doPromotion(regex, offer);
+ flags = doPromotion(flags, offer);
+ if (matching != null) {
+ matching = doPromotion(matching, offer);
+ }
+ if (nonMatching != null) {
+ nonMatching = doPromotion(nonMatching, offer);
+ }
+ }
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ ArrayList<Expression> list = new ArrayList<Expression>(5);
+ list.add(select);
+ list.add(regex);
+ list.add(flags);
+ if (matching != null) {
+ list.add(matching);
+ }
+ if (nonMatching != null) {
+ list.add(nonMatching);
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ ArrayList<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>(5);
+ list.add(new SubExpressionInfo(select, true, false, NODE_VALUE_CONTEXT));
+ list.add(new SubExpressionInfo(regex, true, false, NODE_VALUE_CONTEXT));
+ list.add(new SubExpressionInfo(flags, true, false, NODE_VALUE_CONTEXT));
+ if (matching != null) {
+ list.add(new SubExpressionInfo(matching, false, true, NAVIGATION_CONTEXT));
+ }
+ if (nonMatching != null) {
+ list.add(new SubExpressionInfo(nonMatching, false, true, NAVIGATION_CONTEXT));
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (regex == original) {
+ regex = replacement;
+ found = true;
+ }
+ if (flags == original) {
+ flags = replacement;
+ found = true;
+ }
+ if (matching == original) {
+ matching = replacement;
+ found = true;
+ }
+ if (nonMatching == original) {
+ nonMatching = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * ProcessLeavingTail: called to do the real work of this instruction. This method
+ * must be implemented in each subclass. The results of the instruction are written
+ * to the current Receiver, which can be obtained via the Controller.
+ *
+ * @param context The dynamic context of the transformation, giving access to the current node,
+ * the current variables, etc.
+ * @return null if the instruction has completed execution; or a TailCall indicating
+ * a function call or template call that is delegated to the caller, to be made after the stack has
+ * been unwound so as to save stack space.
+ */
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ RegexIterator iter = getRegexIterator(context);
+ XPathContextMajor c2 = context.newContext();
+ c2.setOrigin(this);
+ c2.setCurrentIterator(iter);
+ c2.setCurrentRegexIterator(iter);
+
+ while (true) {
+ Item it = iter.next();
+ if (it == null) {
+ break;
+ }
+ if (iter.isMatching()) {
+ if (matching != null) {
+ //if (processingAction == null) {
+ matching.process(c2);
+ //} else {
+ // iter.processMatchingSubstring(context, processingAction);
+ //}
+ }
+ } else {
+ if (nonMatching != null) {
+ nonMatching.process(c2);
+ }
+ }
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Get an iterator over the substrings defined by the regular expression
+ *
+ * @param context the evaluation context
+ * @return an iterator that returns matching and nonmatching substrings
+ * @throws XPathException if evaluation fails with a dynamic error
+ */
+
+ private RegexIterator getRegexIterator(XPathContext context) throws XPathException {
+ CharSequence input = select.evaluateAsString(context);
+
+ RegularExpression re = pattern;
+ if (re == null) {
+ CharSequence flagstr = flags.evaluateAsString(context);
+ re = Configuration.getPlatform().compileRegularExpression(
+ regex.evaluateAsString(context), flagstr.toString(), (allow30features ? "XP30" : "XP20"), null);
+
+ if (re.matches("")) {
+ dynamicError("The regular expression must not be one that matches a zero-length string",
+ "XTDE1150", context);
+ }
+ }
+
+ return re.analyze(input);
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<StringValue> iterate(XPathContext context) throws XPathException {
+ RegexIterator iter = getRegexIterator(context);
+ XPathContextMajor c2 = context.newContext();
+ c2.setOrigin(this);
+ c2.setCurrentIterator(iter);
+ c2.setCurrentRegexIterator(iter);
+
+ AnalyzeMappingFunction fn = new AnalyzeMappingFunction(iter, c2, nonMatching, matching);
+ return new ContextMappingIterator(fn, c2);
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the AnalyzeString expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new AnalyzeStringCompiler();
+ }
+ //#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("analyzeString");
+ out.startSubsidiaryElement("select");
+ select.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("regex");
+ regex.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("flags");
+ flags.explain(out);
+ out.endSubsidiaryElement();
+ if (matching != null) {
+ out.startSubsidiaryElement("matching");
+ matching.explain(out);
+ out.endSubsidiaryElement();
+ }
+ if (nonMatching != null) {
+ out.startSubsidiaryElement("nonMatching");
+ nonMatching.explain(out);
+ out.endSubsidiaryElement();
+ }
+ out.endElement();
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/instruct/ApplyImports.java b/sf/saxon/expr/instruct/ApplyImports.java
new file mode 100644
index 0000000..9aeedd9
--- /dev/null
+++ b/sf/saxon/expr/instruct/ApplyImports.java
@@ -0,0 +1,356 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ApplyImportsCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.ApplyImportsAdjunct;
+import com.saxonica.stream.adjunct.StreamingAdjunct;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.Mode;
+import net.sf.saxon.trans.Rule;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.DecimalValue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+
+
+/**
+* An xsl:apply-imports element in the stylesheet. NOTE: NextMatch is a subclass
+*/
+
+public class ApplyImports extends Instruction implements ITemplateCall {
+
+ // TODO: make ApplyImports and NextMatch subclasses of an abstract superclass
+
+ /*@NotNull*/ WithParam[] actualParams = WithParam.EMPTY_ARRAY;
+ /*@NotNull*/ WithParam[] tunnelParams = WithParam.EMPTY_ARRAY;
+ boolean allowAnyItem = false;
+
+ public ApplyImports() {
+ }
+
+ /**
+ * Set the actual parameters on the call, including tunnel parameters
+ * @param actualParams the non-tunnel parameters
+ * @param tunnelParams the tunnel parameters
+ */
+
+ public void setActualParameters(/*@NotNull*/ WithParam[] actualParams, /*@NotNull*/ WithParam[] tunnelParams ) {
+ this.actualParams = actualParams;
+ this.tunnelParams = tunnelParams;
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_APPLY_IMPORTS;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is prefered. For instructions this is the process() method.
+ */
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | Expression.WATCH_METHOD;
+ }
+
+
+ /**
+ * Get the actual (non-tunnel) parameters passed to the called template
+ * @return the non-tunnel parameters
+ */
+
+ /*@NotNull*/
+ public WithParam[] getActualParams() {
+ return actualParams;
+ }
+
+ /**
+ * Get the tunnel parameters passed to the called template
+ * @return the tunnel parameters
+ */
+
+ /*@NotNull*/
+ public WithParam[] getTunnelParams() {
+ return tunnelParams;
+ }
+
+ /**
+ * Ask whether the instruction can process any item (XSLT 3.0), or only nodes (XSLT 1.0/2.0)
+ * @return true if any item can be processed
+ */
+
+ public boolean isAllowAnyItem() {
+ return allowAnyItem;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression).
+ *
+ * @exception net.sf.saxon.trans.XPathException if an error is discovered during expression
+ * rewriting
+ * @return the simplified expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ WithParam.simplify(actualParams, visitor);
+ WithParam.simplify(tunnelParams, visitor);
+ allowAnyItem = visitor.getStaticContext().getXPathLanguageLevel().equals(DecimalValue.THREE);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ WithParam.typeCheck(actualParams, visitor, contextItemType);
+ WithParam.typeCheck(tunnelParams, visitor, contextItemType);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ WithParam.optimize(visitor, actualParams, contextItemType);
+ WithParam.optimize(visitor, tunnelParams, contextItemType);
+ return this;
+ }
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ApplyImports ai2 = new ApplyImports();
+ ai2.setActualParameters(WithParam.copy(actualParams), WithParam.copy(tunnelParams));
+ ai2.allowAnyItem = allowAnyItem;
+ return ai2;
+ }
+
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true (which is almost invariably the case, so it's not worth
+ * doing any further analysis to find out more precisely).
+ */
+
+ public final boolean createsNewNodes() {
+ return true;
+ }
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ WithParam.promoteParams(this, actualParams, offer);
+ WithParam.promoteParams(this, tunnelParams, offer);
+ }
+
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ for (WithParam w : actualParams) {
+ if (w.getSelectExpression().getStreamability(NAVIGATION_CONTEXT, allowExtensions, null) != W3C_MOTIONLESS) {
+ return W3C_FREE_RANGING;
+ }
+ }
+ return W3C_CONSUMING;
+ }
+
+ @Override
+ public StreamingAdjunct getStreamingAdjunct() {
+ return new ApplyImportsAdjunct();
+ }
+//#endif
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ ArrayList<Expression> list = new ArrayList<Expression>(10);
+ WithParam.gatherXPathExpressions(actualParams, list);
+ WithParam.gatherXPathExpressions(tunnelParams, list);
+ return list.iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (WithParam.replaceXPathExpression(actualParams, original, replacement)) {
+ found = true;
+ }
+ if (WithParam.replaceXPathExpression(tunnelParams, original, replacement)) {
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ // This logic is assuming the mode is streamable (so called templates can't return streamed nodes)
+ //PathMap.PathMapNodeSet result = super.addToPathMap(pathMap, pathMapNodeSet);
+ //result.setReturnable(false);
+ if (pathMapNodeSet == null) {
+ ContextItemExpression cie = new ContextItemExpression();
+ cie.setContainer(getContainer());
+ pathMapNodeSet = new PathMap.PathMapNodeSet(pathMap.makeNewRoot(cie));
+ }
+ pathMapNodeSet.addDescendants();
+ return new PathMap.PathMapNodeSet(pathMap.makeNewRoot(this));
+ }
+
+
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+
+ Controller controller = context.getController();
+ assert controller != null;
+ // handle parameters if any
+
+ ParameterSet params = assembleParams(context, actualParams);
+ ParameterSet tunnels = assembleTunnelParams(context, tunnelParams);
+
+ Rule currentTemplateRule = context.getCurrentTemplateRule();
+ if (currentTemplateRule==null) {
+ XPathException e = new XPathException("There is no current template rule");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0560");
+ e.setLocator(this);
+ throw e;
+ }
+
+ int min = currentTemplateRule.getMinImportPrecedence();
+ int max = currentTemplateRule.getPrecedence()-1;
+ Mode mode = context.getCurrentMode();
+ if (mode == null) {
+ mode = controller.getRuleManager().getUnnamedMode();
+ }
+ if (context.getCurrentIterator()==null) {
+ XPathException e = new XPathException("Cannot call xsl:apply-imports when there is no context item");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0565");
+ e.setLocator(this);
+ throw e;
+ }
+ Item currentItem = context.getCurrentIterator().current();
+ if (!allowAnyItem && !(currentItem instanceof NodeInfo)) {
+ XPathException e = new XPathException("Cannot call xsl:apply-imports when context item is not a node");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0565");
+ e.setLocator(this);
+ throw e;
+ }
+ Rule rule = controller.getRuleManager().getTemplateRule(currentItem, mode, min, max, context);
+
+ if (rule==null) { // use the default action for the node
+ mode.getBuiltInRuleSet().process(currentItem, params, tunnels, context, getLocationId());
+ } else {
+ XPathContextMajor c2 = context.newContext();
+ Template nh = (Template)rule.getAction();
+ c2.setOrigin(this);
+ //c2.setOriginatingConstructType(Location.TEMPLATE);
+ c2.setLocalParameters(params);
+ c2.setTunnelParameters(tunnels);
+ c2.openStackFrame(nh.getStackFrameMap());
+ c2.setCurrentTemplateRule(rule);
+ nh.apply(c2);
+ }
+ return null;
+ // We never treat apply-imports as a tail call, though we could
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ApplyImport expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ApplyImportsCompiler();
+ }
+ //#endif
+
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("applyImports");
+ if (actualParams.length > 0) {
+ out.startSubsidiaryElement("withParams");
+ WithParam.explainParameters(actualParams, out);
+ out.endSubsidiaryElement();
+ }
+ if (tunnelParams.length > 0) {
+ out.startSubsidiaryElement("tunnelParams");
+ WithParam.explainParameters(tunnelParams, out);
+ out.endSubsidiaryElement();
+ }
+ out.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/ApplyTemplates.java b/sf/saxon/expr/instruct/ApplyTemplates.java
new file mode 100644
index 0000000..460b124
--- /dev/null
+++ b/sf/saxon/expr/instruct/ApplyTemplates.java
@@ -0,0 +1,622 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ApplyTemplatesCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.Streamability;
+import com.saxonica.stream.StreamingPatternMaker;
+import com.saxonica.stream.adjunct.ApplyTemplatesAdjunct;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.Mode;
+import net.sf.saxon.trans.RuleManager;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+
+import java.util.*;
+
+
+/**
+ * An instruction representing an xsl:apply-templates element in the stylesheet
+ */
+
+public class ApplyTemplates extends Instruction implements ITemplateCall {
+
+ /*@NotNull*/ protected Expression select;
+ /*@NotNull*/ protected WithParam[] actualParams = WithParam.EMPTY_ARRAY;
+ /*@NotNull*/ protected WithParam[] tunnelParams = WithParam.EMPTY_ARRAY;
+ protected boolean useCurrentMode = false;
+ protected boolean useTailRecursion = false;
+ protected Mode mode;
+ protected boolean implicitSelect;
+ protected Expression threads = null;
+
+ protected ApplyTemplates() {
+ }
+
+ /**
+ * Construct an apply-templates instructino
+ *
+ * @param select the select expression
+ * @param useCurrentMode true if mode="#current" was specified
+ * @param useTailRecursion true if this instruction is the last in its template
+ * @param implicitSelect true if the select expression is implicit, that is, if there was no explicit
+ * select expression in the call. This information is used only to make error messages more meaningful.
+ * @param mode the mode specified on apply-templates
+ * @param threads expression whose value indicates how many threads to use when multithreading
+ */
+
+ public ApplyTemplates( /*@NotNull*/ Expression select,
+ boolean useCurrentMode,
+ boolean useTailRecursion,
+ boolean implicitSelect,
+ Mode mode,
+ Expression threads) {
+ init(select, useCurrentMode, useTailRecursion, mode);
+ this.implicitSelect = implicitSelect;
+ this.threads = threads;
+ }
+
+ protected void init(Expression select,
+ boolean useCurrentMode,
+ boolean useTailRecursion,
+ Mode mode) {
+ this.select = select;
+ this.useCurrentMode = useCurrentMode;
+ this.useTailRecursion = useTailRecursion;
+ this.mode = mode;
+ adoptChildExpression(select);
+ }
+
+ /**
+ * Set the actual parameters on the call
+ *
+ * @param actualParams represents the contained xsl:with-param elements having tunnel="no" (the default)
+ * @param tunnelParams represents the contained xsl:with-param elements having tunnel="yes"
+ */
+
+ public void setActualParameters(
+ WithParam[] actualParams,
+ WithParam[] tunnelParams) {
+ this.actualParams = actualParams;
+ this.tunnelParams = tunnelParams;
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_APPLY_TEMPLATES;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is prefered. For instructions this is the process() method.
+ */
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | Expression.WATCH_METHOD;
+ }
+
+ /**
+ * Get the number of threads requested
+ *
+ * @return the value of the saxon:threads attribute
+ */
+
+ public Expression getNumberOfThreadsExpression() {
+ return threads;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression).
+ *
+ * @param visitor the expression visitor
+ * @return the simplified expression
+ * @throws XPathException if an error is discovered during expression
+ * rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ WithParam.simplify(actualParams, visitor);
+ WithParam.simplify(tunnelParams, visitor);
+ select = visitor.simplify(select);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ WithParam.typeCheck(actualParams, visitor, contextItemType);
+ WithParam.typeCheck(tunnelParams, visitor, contextItemType);
+ try {
+ select = visitor.typeCheck(select, contextItemType);
+ } catch (XPathException e) {
+ if (implicitSelect) {
+ String code = e.getErrorCodeLocalPart();
+ if ("XPTY0020".equals(code) || "XPTY0019".equals(code)) {
+ XPathException err = new XPathException("Cannot apply-templates to child nodes when the context item is an atomic value");
+ err.setErrorCode("XTTE0510");
+ err.setIsTypeError(true);
+ throw err;
+ } else if ("XPDY0002".equals(code)) {
+ XPathException err = new XPathException("Cannot apply-templates to child nodes when the context item is absent");
+ err.setErrorCode("XTTE0510");
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+ throw e;
+ }
+ adoptChildExpression(select);
+ if (Literal.isEmptySequence(select)) {
+ return select;
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ WithParam.optimize(visitor, actualParams, contextItemType);
+ WithParam.optimize(visitor, tunnelParams, contextItemType);
+ select = visitor.typeCheck(select, contextItemType); // More info available second time around
+ select = visitor.optimize(select, contextItemType);
+ adoptChildExpression(select);
+ if (Literal.isEmptySequence(select)) {
+ return select;
+ }
+ if (mode != null && mode.isStreamable()) {
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ List<String> reasonsForFailure = new ArrayList<String>(2);
+ Expression e2 = opt.makeStreamingApplyTemplates(this, reasonsForFailure);
+ if (e2 != null) {
+ return e2;
+ } else {
+// String msg = "xsl:apply-templates instruction is not streamable";
+// for (String reason : reasonsForFailure) {
+// msg += "\n * " + reason;
+// }
+// throw new XPathException(msg);
+ }
+ }
+ if (threads != null) {
+ return visitor.getConfiguration().obtainOptimizer().generateMultithreadedInstruction(this);
+ }
+ return this;
+ }
+
+ public int getIntrinsicDependencies() {
+ // If the instruction uses mode="#current", this represents a dependency on the context
+ // which means the instruction cannot be loop-lifted or moved to a global variable.
+ // We overload the dependency DEPENDS_ON_CURRENT_ITEM to achieve this effect.
+ return super.getIntrinsicDependencies() | (useCurrentMode ? StaticProperty.DEPENDS_ON_CURRENT_ITEM : 0);
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ApplyTemplates a2 = new ApplyTemplates(select.copy(), useCurrentMode, useTailRecursion, implicitSelect, mode, threads);
+ a2.actualParams = WithParam.copy(actualParams);
+ a2.tunnelParams = WithParam.copy(tunnelParams);
+ a2.threads = threads;
+ return a2;
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true (which is almost invariably the case, so it's not worth
+ * doing any further analysis to find out more precisely).
+ */
+
+ public final boolean createsNewNodes() {
+ return true;
+ }
+
+ public void process(XPathContext context) throws XPathException {
+ apply(context, false);
+ }
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ return apply(context, useTailRecursion);
+ }
+
+ protected TailCall apply(XPathContext context, boolean returnTailCall) throws XPathException {
+ Mode thisMode = mode;
+ if (useCurrentMode) {
+ thisMode = context.getCurrentMode();
+ }
+
+ // handle parameters if any
+
+ ParameterSet params = assembleParams(context, actualParams);
+ ParameterSet tunnels = assembleTunnelParams(context, tunnelParams);
+
+ if (returnTailCall) {
+ XPathContextMajor c2 = context.newContext();
+ c2.setOrigin(this);
+ return new ApplyTemplatesPackage(
+ ExpressionTool.lazyEvaluate(select, context, 1),
+ thisMode, params, tunnels, c2, getLocationId());
+ }
+
+ // Get an iterator to iterate through the selected nodes in original order
+
+ SequenceIterator iter = select.iterate(context);
+
+ // Quick exit if the iterator is empty
+
+ if (iter instanceof EmptyIterator) {
+ return null;
+ }
+
+ // process the selected nodes now
+
+ XPathContextMajor c2 = context.newContext();
+ c2.setCurrentIterator(iter);
+ c2.setCurrentMode(thisMode);
+ c2.setOrigin(this);
+
+ try {
+ TailCall tc = thisMode.applyTemplates(params, tunnels, c2, getLocationId());
+ while (tc != null) {
+ tc = tc.processLeavingTail();
+ }
+ } catch (StackOverflowError e) {
+ XPathException err = new XPathException("Too many nested apply-templates calls. The stylesheet may be looping.");
+ err.setErrorCode(SaxonErrorCode.SXLM0001);
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+
+ return null;
+
+ }
+
+ //#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ if (reasons == null) {
+ reasons = new ArrayList<String>();
+ }
+ Mode mode = getMode();
+ for (WithParam wp : actualParams) {
+ if (wp.getSelectExpression().getStreamability(NAVIGATION_CONTEXT, allowExtensions, reasons) != W3C_MOTIONLESS) {
+ reasons.add("Calls xsl:apply-templates with a non-motionless parameter value");
+ return W3C_FREE_RANGING;
+ }
+ }
+ for (WithParam wp : tunnelParams) {
+ if (wp.getSelectExpression().getStreamability(NAVIGATION_CONTEXT, allowExtensions, reasons) != W3C_MOTIONLESS) {
+ reasons.add("Calls xsl:apply-templates with a non-motionless parameter value");
+ return W3C_FREE_RANGING;
+ }
+ }
+ if (select.getStreamability(syntacticContext, allowExtensions, reasons) == W3C_MOTIONLESS) {
+ return W3C_MOTIONLESS;
+ }
+
+ boolean modeOK = false;
+ if (usesCurrentMode()) {
+ PreparedStylesheet pss = (PreparedStylesheet) getExecutable();
+ RuleManager rm = pss.getRuleManager();
+ Set<Mode> possibleModes = new HashSet<Mode>();
+ Container container = getContainer();
+ if (container instanceof Template) {
+ for (StructuredQName name : ((Template) container).getModeNames()) {
+ if (name.equals(Mode.ALL_MODES)) {
+ possibleModes.addAll(rm.getAllNamedModes());
+ possibleModes.add(rm.getUnnamedMode());
+ } else {
+ Mode m = rm.getMode(name, false);
+ possibleModes.add(m);
+ }
+ }
+ } else {
+ possibleModes.add(pss.getRuleManager().getUnnamedMode());
+ }
+ for (Mode m : possibleModes) {
+ if (!m.isStreamable()) {
+ String offender = (m.getModeName().equals(Mode.UNNAMED_MODE_NAME) ? "the unnamed mode" : m.getModeName().getDisplayName());
+ reasons.add("There is an xsl:apply-templates instruction using mode=\"#current\", and at least " +
+ "one of the possible modes (specifically, " + offender + ") is not streamable");
+ return W3C_FREE_RANGING;
+ }
+ }
+ modeOK = true;
+ } else if (mode != null && mode.isStreamable()) {
+ modeOK = true;
+ }
+
+ if (modeOK) {
+ if (select instanceof GroupVariableReference) {
+ return W3C_GROUP_CONSUMING;
+ }
+
+ if (Streamability.isIncrementallyConsuming(select)) {
+ return W3C_CONSUMING;
+ }
+ if (allowExtensions) {
+ Pattern selection = StreamingPatternMaker.makeStreamingPattern(select, getExecutable().getConfiguration(), reasons);
+ if (selection != null) {
+ return W3C_CONSUMING;
+ }
+ }
+ reasons.add("There is an xsl:apply-templates instruction with a select expression {" + select.toString() +
+ "} that does not satisfy the streamability restrictions");
+ return W3C_FREE_RANGING;
+
+ } else {
+ reasons.add("There is an xsl:apply-templates instruction using a non-streamable mode");
+ return W3C_FREE_RANGING;
+ }
+
+ }
+
+ @Override
+ public ApplyTemplatesAdjunct getStreamingAdjunct() {
+ return new ApplyTemplatesAdjunct();
+ }
+//#endif
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ ArrayList<Expression> list = new ArrayList<Expression>(10);
+ list.add(select);
+ WithParam.gatherXPathExpressions(actualParams, list);
+ WithParam.gatherXPathExpressions(tunnelParams, list);
+ return list.iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (WithParam.replaceXPathExpression(actualParams, original, replacement)) {
+ found = true;
+ }
+ if (WithParam.replaceXPathExpression(tunnelParams, original, replacement)) {
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Get the select expression
+ *
+ * @return the select expression
+ */
+
+ public Expression getSelectExpression() {
+ return select;
+ }
+
+ /**
+ * Ask if the select expression was implicit
+ *
+ * @return true if no select attribute was explicitly specified
+ */
+
+ public boolean isImplicitSelect() {
+ return implicitSelect;
+ }
+
+ /**
+ * Ask if tail recursion is to be used
+ *
+ * @return true if tail recursion is used
+ */
+
+ public boolean useTailRecursion() {
+ return useTailRecursion;
+ }
+
+ /**
+ * Ask if mode="#current" was specified
+ *
+ * @return true if mode="#current" was specified
+ */
+
+ public boolean usesCurrentMode() {
+ return useCurrentMode;
+ }
+
+ /**
+ * Get the Mode
+ *
+ * @return the mode, or null if mode="#current" was specified
+ */
+
+ public Mode getMode() {
+ return mode;
+ }
+
+ /**
+ * Get the actual parameters passed to the called template
+ *
+ * @return the non-tunnel parameters
+ */
+
+ /*@NotNull*/
+ public WithParam[] getActualParams() {
+ return actualParams;
+ }
+
+ /**
+ * Get the tunnel parameters passed to the called template
+ *
+ * @return the tunnel parameters
+ */
+
+ /*@NotNull*/
+ public WithParam[] getTunnelParams() {
+ return tunnelParams;
+ }
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ *
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ select = doPromotion(select, offer);
+ WithParam.promoteParams(this, actualParams, offer);
+ WithParam.promoteParams(this, tunnelParams, offer);
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ // This logic is assuming the mode is streamable (so that called templates can't return streamed nodes)
+ PathMap.PathMapNodeSet result = super.addToPathMap(pathMap, pathMapNodeSet);
+ result.setReturnable(false);
+ return new PathMap.PathMapNodeSet(pathMap.makeNewRoot(this));
+ }
+
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out output destination
+ */
+
+ public void explain(ExpressionPresenter out) {
+
+ out.startElement("applyTemplates");
+ if (mode != null && !mode.isDefaultMode()) {
+ out.emitAttribute("mode", mode.getModeName().getDisplayName());
+ }
+ explainStreaming(out);
+ out.startSubsidiaryElement("select");
+ select.explain(out);
+ out.endSubsidiaryElement();
+ if (actualParams != null && actualParams.length > 0) {
+ out.startSubsidiaryElement("withParams");
+ WithParam.explainParameters(actualParams, out);
+ out.endSubsidiaryElement();
+ }
+ if (tunnelParams != null && tunnelParams.length > 0) {
+ out.startSubsidiaryElement("tunnelParams");
+ WithParam.explainParameters(tunnelParams, out);
+ out.endSubsidiaryElement();
+ }
+ out.endElement();
+ }
+
+ protected void explainStreaming(ExpressionPresenter out) {
+ // do nothing (implemented in subclass)
+ }
+
+ /**
+ * An ApplyTemplatesPackage is an object that encapsulates the sequence of nodes to be processed,
+ * the mode, the parameters to be supplied, and the execution context. This object can be returned as a tail
+ * call, so that the actual call is made from a lower point on the stack, allowing a tail-recursive
+ * template to execute in a finite stack size
+ */
+
+ protected static class ApplyTemplatesPackage implements TailCall {
+
+ private Sequence selectedItems;
+ private Mode mode;
+ private ParameterSet params;
+ private ParameterSet tunnelParams;
+ private XPathContextMajor evaluationContext;
+ private int locationId;
+
+ ApplyTemplatesPackage(Sequence selectedItems,
+ Mode mode,
+ ParameterSet params,
+ ParameterSet tunnelParams,
+ XPathContextMajor context,
+ int locationId
+ ) {
+ this.selectedItems = selectedItems;
+ this.mode = mode;
+ this.params = params;
+ this.tunnelParams = tunnelParams;
+ evaluationContext = context;
+ this.locationId = locationId;
+ }
+
+ public TailCall processLeavingTail() throws XPathException {
+ evaluationContext.setCurrentIterator(selectedItems.iterate());
+ evaluationContext.setCurrentMode(mode);
+ return mode.applyTemplates(params, tunnelParams, evaluationContext, locationId);
+ }
+ }
+
+ //#ifdefined BYTECODE
+
+ /**
+ * Return the compiler of the ApplyTemplates expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ApplyTemplatesCompiler();
+ }
+ //#endif
+
+}
+
diff --git a/sf/saxon/expr/instruct/AttributeCreator.java b/sf/saxon/expr/instruct/AttributeCreator.java
new file mode 100644
index 0000000..5debc78
--- /dev/null
+++ b/sf/saxon/expr/instruct/AttributeCreator.java
@@ -0,0 +1,260 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * Abstract class for fixed and computed attribute constructor expressions
+ */
+
+public abstract class AttributeCreator extends SimpleNodeConstructor implements ValidatingInstruction {
+
+ //Null implies untyped (no validation required)
+ /*@Nullable*/ SimpleType schemaType = null;
+ private int validationAction;
+ private int options;
+
+ /**
+ * Set the required schema type of the attribute
+ * @param type the required schema type, if validation against a specific type is required,
+ * or null if no validation is required
+ */
+
+ public void setSchemaType(/*@Nullable*/ SimpleType type) {
+ schemaType = type;
+ }
+
+ /**
+ * Return the required schema type of the attribute
+ * @return if validation against a schema type was requested, return the schema type (always a simple type).
+ * Otherwise, if validation against a specific type was not requested, return null
+ */
+
+ /*@Nullable*/
+ public SimpleType getSchemaType() {
+ return schemaType;
+ }
+
+ /**
+ * Set the validation action required
+ * @param action the validation action required, for example strict or lax
+ */
+
+ public void setValidationAction(int action) {
+ validationAction = action;
+ }
+
+ /**
+ * Get the validation action requested
+ * @return the validation action, for example strict or lax
+ */
+
+ public int getValidationAction() {
+ return validationAction;
+ }
+
+ /**
+ * Set the options to be used on the attribute event
+ * @param options Options to be used. The only option currently defined is
+ * {@link ReceiverOptions#REJECT_DUPLICATES}, which controls whether or not it is an error
+ * to create two attributes with the same name for the same element. (This is an error in XQuery
+ * but not in XSLT).
+ */
+
+ public void setOptions(int options) {
+ this.options = options;
+ }
+
+ /**
+ * Indicate that two attributes with the same name are not acceptable.
+ * (This option is set in XQuery, but not in XSLT)
+ */
+
+ public void setRejectDuplicates() {
+ options |= ReceiverOptions.REJECT_DUPLICATES;
+ }
+
+ /**
+ * Indicate that the attribute value contains no special characters that
+ * might need escaping
+ */
+
+ public void setNoSpecialChars() {
+ options |= ReceiverOptions.NO_SPECIAL_CHARS;
+ }
+
+ /**
+ * Get the options to be used on the attribute event
+ * @return the option flags to be used
+ */
+
+ public int getOptions() {
+ return options;
+ }
+
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+ @Override
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ if (getValidationAction() == Validation.SKIP) {
+ p |= StaticProperty.ALL_NODES_UNTYPED;
+ }
+ return p;
+ }
+
+ /**
+ * Get the static type of this expression
+ * @param th the type hierarchy cache
+ * @return the static type of the item returned by this expression
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return NodeKindTest.ATTRIBUTE;
+ }
+
+ /**
+ * Process the value of the node, to create the new node.
+ * @param value the string value of the new node
+ * @param context the dynamic evaluation context
+ * @throws XPathException
+ */
+
+ public void processValue(CharSequence value, XPathContext context) throws XPathException {
+ NodeName attName = evaluateNodeName(context);
+// if (nameCode == -1) {
+// return null;
+// }
+ SequenceReceiver out = context.getReceiver();
+ int opt = getOptions();
+ SimpleType ann;
+
+ // we may need to change the namespace prefix if the one we chose is
+ // already in use with a different namespace URI: this is done behind the scenes
+ // by the ComplexContentOutputter
+
+ //CharSequence value = expandChildren(context).toString();
+ SimpleType schemaType = getSchemaType();
+ int validationAction = getValidationAction();
+ if (schemaType != null) {
+ ann = schemaType;
+ // test whether the value actually conforms to the given type
+ try {
+ ValidationFailure err = schemaType.validateContent(
+ value, DummyNamespaceResolver.getInstance(), context.getConfiguration().getConversionRules());
+ if (err != null) {
+ ValidationException ve = new ValidationException(
+ "Attribute value " + Err.wrap(value, Err.VALUE) +
+ " does not match the required type " +
+ schemaType.getDescription() + ". " +
+ err.getMessage());
+ ve.setErrorCode("XTTE1540");
+ throw ve;
+ }
+ } catch (UnresolvedReferenceException ure) {
+ throw new ValidationException(ure);
+ }
+ } else if (validationAction== Validation.STRICT ||
+ validationAction==Validation.LAX) {
+ try {
+ Configuration config = context.getConfiguration();
+ ann = config.validateAttribute(attName.allocateNameCode(config.getNamePool()), value, validationAction);
+ } catch (ValidationException e) {
+ XPathException err = XPathException.makeXPathException(e);
+ err.maybeSetErrorCode((validationAction== Validation.STRICT ? "XTTE1510" : "XTTE1515"));
+ err.setXPathContext(context);
+ err.maybeSetLocation(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ } else {
+ ann = BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ if (attName.equals(StandardNames.XML_ID_NAME)) {
+ value = Whitespace.collapseWhitespace(value);
+ }
+
+ try {
+ out.attribute(attName, ann, value, locationId, opt);
+ } catch (XPathException err) {
+ throw dynamicError(this, err, context);
+ }
+
+ }
+
+ /**
+ * Validate a newly-constructed parentless attribute using the type and validation attributes
+ * @param orphan the new attribute node
+ * @param context the dynamic evaluation context
+ * @throws XPathException if validation fails
+ */
+
+ protected void validateOrphanAttribute(Orphan orphan, XPathContext context) throws XPathException {
+ ConversionRules rules = context.getConfiguration().getConversionRules();
+ SimpleType schemaType = getSchemaType();
+ int validationAction = getValidationAction();
+ if (schemaType != null) {
+ ValidationFailure err = schemaType.validateContent(
+ orphan.getStringValueCS(), DummyNamespaceResolver.getInstance(), rules);
+ if (err != null) {
+ ValidationException ve = new ValidationException("Attribute value " + Err.wrap(orphan.getStringValueCS(), Err.VALUE) +
+ " does not the match the required type " +
+ schemaType.getDescription() + ". " +
+ err.getMessage());
+ ve.setErrorCode("XTTE1555"); // TODO: different for XQuery
+ ve.setLocator((SourceLocator)this);
+ throw ve;
+ }
+ orphan.setTypeAnnotation(schemaType.getFingerprint());
+ if (schemaType.isNamespaceSensitive()) {
+ throw new XPathException("Cannot validate a parentless attribute whose content is namespace-sensitive", "XTTE1545");
+ }
+ } else if (validationAction== Validation.STRICT || validationAction==Validation.LAX) {
+ try {
+ final Controller controller = context.getController();
+ assert controller != null;
+ SimpleType ann = controller.getConfiguration().validateAttribute(
+ orphan.getNameCode(), orphan.getStringValueCS(), validationAction);
+ orphan.setTypeAnnotation(ann);
+ } catch (ValidationException e) {
+ XPathException err = XPathException.makeXPathException(e);
+ err.setErrorCodeQName(e.getErrorCodeQName());
+ err.setXPathContext(context);
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/AttributeSet.java b/sf/saxon/expr/instruct/AttributeSet.java
new file mode 100644
index 0000000..57166b3
--- /dev/null
+++ b/sf/saxon/expr/instruct/AttributeSet.java
@@ -0,0 +1,155 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* The compiled form of an xsl:attribute-set element in the stylesheet.
+*/
+
+// Note, there is no run-time check for circularity. This is checked at compile time.
+
+public class AttributeSet extends Procedure {
+
+ StructuredQName attributeSetName;
+
+ private AttributeSet[] useAttributeSets;
+
+ /**
+ * Create an empty attribute set
+ */
+
+ public AttributeSet() {
+ setHostLanguage(Configuration.XSLT);
+ }
+
+ /**
+ * Set the name of the attribute-set
+ * @param attributeSetName the name of the attribute-set
+ */
+
+ public void setName(StructuredQName attributeSetName) {
+ this.attributeSetName = attributeSetName;
+ }
+
+ /**
+ * Set the attribute sets used by this attribute set
+ * @param useAttributeSets the set of attribute sets used by this attribute set
+ */
+
+ public void setUseAttributeSets(AttributeSet[] useAttributeSets) {
+ this.useAttributeSets = useAttributeSets;
+ }
+
+ /**
+ * Set the stack frame map which allocates slots to variables declared in this attribute set
+ * @param stackFrameMap the stack frame map
+ */
+
+ public void setStackFrameMap(/*@Nullable*/ SlotManager stackFrameMap) {
+ if (stackFrameMap != null && stackFrameMap.getNumberOfVariables() > 0) {
+ super.setStackFrameMap(stackFrameMap);
+ }
+ }
+
+ /**
+ * Determine whether the attribute set has any dependencies on the focus
+ * @return the dependencies
+ */
+
+ public int getFocusDependencies() {
+ int d = 0;
+ if (body != null) {
+ d |= body.getDependencies() & StaticProperty.DEPENDS_ON_FOCUS;
+ }
+ if (useAttributeSets != null) {
+ for (AttributeSet useAttributeSet : useAttributeSets) {
+ d |= useAttributeSet.getFocusDependencies();
+ }
+ }
+ return d;
+ }
+
+//#ifdefined STREAM
+ public int getW3CStreamability() {
+ int max = body.getStreamability(Expression.NAVIGATION_CONTEXT, false, null);
+ for (AttributeSet aset : useAttributeSets) {
+ int s = aset.getW3CStreamability();
+ if (s > max) {
+ max = s;
+ }
+ }
+ return max;
+ }
+//#endif
+
+ /**
+ * Evaluate an attribute set
+ * @param context the dynamic context
+ * @throws XPathException if any failure occurs
+ */
+
+ public void expand(XPathContext context) throws XPathException {
+ // apply the content of any attribute sets mentioned in use-attribute-sets
+
+ if (useAttributeSets != null) {
+ AttributeSet.expand(useAttributeSets, context);
+ }
+
+ if (getStackFrameMap() != null) {
+ XPathContextMajor c2 = context.newContext();
+ c2.setOrigin(this);
+ c2.openStackFrame(getStackFrameMap());
+ getBody().process(c2);
+ } else {
+ getBody().process(context);
+ }
+ }
+
+ /**
+ * Get the type of construct. This will either be the fingerprint of a standard XSLT instruction name
+ * (values in {@link net.sf.saxon.om.StandardNames}: all less than 1024)
+ * or it will be a constant in class {@link net.sf.saxon.trace.Location}.
+ */
+
+ public int getConstructType() {
+ return StandardNames.XSL_ATTRIBUTE_SET;
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ *
+ */
+
+ public StructuredQName getObjectName() {
+ return attributeSetName;
+ }
+
+ /**
+ * Expand (evaluate) an array of attribute sets
+ * @param asets the attribute sets to be evaluated
+ * @param context the run-time context to use
+ * @throws XPathException if evaluation of any attribute-set fails with a dynamic error
+ */
+
+ public static void expand(AttributeSet[] asets, XPathContext context) throws XPathException {
+ for (AttributeSet aset : asets) {
+ aset.expand(context);
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/Bindery.java b/sf/saxon/expr/instruct/Bindery.java
new file mode 100644
index 0000000..5dde943
--- /dev/null
+++ b/sf/saxon/expr/instruct/Bindery.java
@@ -0,0 +1,320 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceExtent;
+import net.sf.saxon.value.SequenceType;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+
+/**
+* The Bindery class holds information about variables and their values. From Saxon 8.1, it is
+* used only for global variables: local variables are now held in the XPathContext object.
+*
+* Variables are identified by a Binding object. Values will always be of class Value.
+*/
+
+public final class Bindery {
+
+ private Sequence[] globals; // values of global variables and parameters
+ private long[] busy; // set to current thread id while variable is being evaluated
+ private GlobalParameterSet globalParameters; // supplied global parameters
+ private SlotManager globalVariableMap; // contains the mapping of variable names to slot numbers
+ private Map<GlobalVariable, Set<GlobalVariable>> dependencies =
+ new HashMap<GlobalVariable, Set<GlobalVariable>>();
+ private boolean applyConversionRules = true;
+
+ /**
+ * Define how many slots are needed for global variables
+ * @param map the SlotManager that keeps track of slot allocation for global variables.
+ */
+
+ public void allocateGlobals(SlotManager map) {
+ globalVariableMap = map;
+ int n = map.getNumberOfVariables()+1;
+ globals = new Sequence[n];
+ busy = new long[n];
+ for (int i=0; i<n; i++) {
+ globals[i] = null;
+ busy[i] = -1L;
+ }
+ }
+
+ /**
+ * Say whether the function conversion rules should be applied to supplied
+ * parameter values. For example, this allows an integer to be supplied as the value
+ * for a parameter where the expected type is xs:double. The default is true.
+ * @param convert true if function conversion rules are to be applied to supplied
+ * values; if false, the supplied value must match the required type exactly.
+ * @since 9.3
+ */
+
+ public void setApplyFunctionConversionRulesToExternalVariables(boolean convert) {
+ applyConversionRules = convert;
+ }
+
+ /**
+ * Ask whether the function conversion rules should be applied to supplied
+ * parameter values. For example, this allows an integer to be supplied as the value
+ * for a parameter where the expected type is xs:double. The default is true.
+ * @return true if function conversion rules are to be applied to supplied
+ * values; if false, the supplied value must match the required type exactly.
+ * @since 9.3
+ */
+
+ public boolean isApplyFunctionConversionRulesToExternalVariables() {
+ return applyConversionRules;
+ }
+
+
+ /**
+ * Define global parameters
+ * @param params The ParameterSet passed in by the user, eg. from the command line
+ */
+
+ public void defineGlobalParameters(GlobalParameterSet params) {
+ globalParameters = params;
+ // Reset any existing global variables - allows the controller and bindery to be serially reused with new parameter values
+ Arrays.fill(globals, null);
+ }
+
+ /**
+ * Use global parameter. This is called when a global xsl:param element is processed.
+ * If a parameter of the relevant name was supplied, it is bound to the xsl:param element.
+ * Otherwise the method returns false, so the xsl:param default will be evaluated.
+ * @param qName The name of the parameter
+ * @param slot The slot number allocated to the parameter
+ * @param requiredType The declared type of the parameter
+ * @param context the XPath dynamic evaluation context
+ * @return true if a parameter of this name was supplied, false if not
+ * @throws XPathException if a dynamic error occurs, for example if the supplied parameter value
+ * does not match the required type
+ */
+
+ public boolean useGlobalParameter(StructuredQName qName, int slot, SequenceType requiredType, XPathContext context)
+ throws XPathException {
+ if (globals != null && globals[slot] != null) {
+ return true;
+ }
+
+ if (globalParameters==null) {
+ return false;
+ }
+
+ Sequence val = globalParameters.convertParameterValue(qName, requiredType, applyConversionRules, context);
+ if (val==null) {
+ return false;
+ }
+
+ // If the supplied value is a document node, and the document node has a systemID that is an absolute
+ // URI, and the absolute URI does not already exist in the document pool, then register it in the document
+ // pool, so that the document-uri() function will find it there, and so that a call on doc() will not
+ // reload it.
+
+ if (val instanceof DocumentInfo) {
+ String systemId = ((DocumentInfo)val).getSystemId();
+ try {
+ if (systemId != null && new URI(systemId).isAbsolute()) {
+ Controller controller = context.getController();
+ assert controller != null;
+ DocumentPool pool = controller.getDocumentPool();
+ if (pool.find(systemId) == null) {
+ pool.add(((DocumentInfo)val), systemId);
+ }
+ }
+ } catch (URISyntaxException err) {
+ // ignore it
+ }
+ }
+
+ if (!(val instanceof GroundedValue)) {
+ val = new SequenceExtent(val.iterate());
+ }
+ globals[slot] = val;
+ return true;
+ }
+
+ /**
+ * Provide a value for a global variable
+ * @param binding identifies the variable
+ * @param value the value of the variable
+ */
+
+ public void setGlobalVariable(GlobalVariable binding, Sequence value) {
+ globals[binding.getSlotNumber()] = value;
+ }
+
+ /**
+ * Set/Unset a flag to indicate that a particular global variable is currently being
+ * evaluated. Note that this code is not synchronized, so there is no absolute guarantee that
+ * two threads will not both evaluate the same global variable; however, apart from wasted time,
+ * it is harmless if they do.
+ * @param binding the global variable in question
+ * @return true if evaluation of the variable should proceed; false if it is found that the variable has now been
+ * evaluated in another thread.
+ * @throws net.sf.saxon.trans.XPathException If an attempt is made to set the flag when it is already set, this means
+ * the definition of the variable is circular.
+ */
+
+ public boolean setExecuting(GlobalVariable binding)
+ throws XPathException {
+ long thisThread = Thread.currentThread().getId();
+ int slot = binding.getSlotNumber();
+
+ long busyThread = busy[slot];
+ if (busyThread != -1L) {
+ if (busyThread == thisThread) {
+ // The global variable is being evaluated in this thread. This shouldn't happen, because
+ // we have already tested for circularities. If it does happen, however, we fail cleanly.
+ throw new XPathException.Circularity("Circular definition of variable "
+ + binding.getVariableQName().getDisplayName());
+ } else {
+ // The global variable is being evaluated in another thread. Give it a chance to finish.
+ // It could be a circularity, or just an accident of timing. Note that in the latter case,
+ // we will actually re-evaluate the variable; this normally does no harm, though there is a small
+ // risk it could lead to problems with the identity of a node changing.
+ for (int i=0; i<10; i++) {
+ try {
+ Thread.sleep(20*i);
+ } catch (InterruptedException e) {
+ // no action
+ }
+ if (busy[slot] == -1L) {
+ // evaluation has finished in another thread
+ return false;
+ }
+ }
+ // We've waited long enough; there could be a deadlock if we wait any longer.
+ // Continue with the evaluation; whichever thread completes the evaluation first will
+ // save the value.
+ return true;
+ }
+ }
+ busy[slot] = thisThread;
+ return true;
+ }
+
+ /**
+ * Indicate that a global variable is not currently being evaluated
+ * @param binding the global variable
+ */
+
+ public void setNotExecuting(GlobalVariable binding) {
+ int slot = binding.getSlotNumber();
+ busy[slot] = -1L;
+ }
+
+
+ /**
+ * Save the value of a global variable, and mark evaluation as complete.
+ * @param binding the global variable in question
+ * @param value the value that this thread has obtained by evaluating the variable
+ * @return the value actually given to the variable. Exceptionally this will be different from the supplied
+ * value if another thread has evaluated the same global variable concurrently. The returned value should be
+ * used in preference, to ensure that all threads agree on the value. They could be different if for example
+ * the variable is initialized using the collection() function.
+ */
+
+ public synchronized Sequence saveGlobalVariableValue(GlobalVariable binding, Sequence value) {
+ int slot = binding.getSlotNumber();
+ if (globals[slot] != null) {
+ // another thread has already evaluated the value
+ return globals[slot];
+ } else {
+ busy[slot] = -1L;
+ globals[slot] = value;
+ return value;
+ }
+ }
+
+
+ /**
+ * Get the value of a global variable
+ * @param binding the Binding that establishes the unique instance of the variable
+ * @return the Value of the variable if defined, null otherwise.
+ */
+
+ public Sequence getGlobalVariableValue(GlobalVariable binding) {
+ return globals[binding.getSlotNumber()];
+ }
+
+ /**
+ * Get the value of a global variable whose slot number is known
+ * @param slot the slot number of the required variable
+ * @return the Value of the variable if defined, null otherwise.
+ */
+
+ public Sequence getGlobalVariable(int slot) {
+ return globals[slot];
+ }
+
+ /**
+ * Get the Global Variable Map, containing the mapping of variable names (fingerprints)
+ * to slot numbers. This is provided for use by debuggers.
+ * @return the SlotManager containing information about the assignment of slot numbers
+ * to global variables and parameters
+ */
+
+ public SlotManager getGlobalVariableMap() {
+ return globalVariableMap;
+ }
+
+ /**
+ * Get all the global variables, as an array. This is provided for use by debuggers
+ * that know the layout of the global variables within the array.
+ * @return the array of global varaibles.
+ */
+
+ public Sequence[] getGlobalVariables() {
+ return globals;
+ }
+
+ /**
+ * Register the dependency of one variable ("one") upon another ("two"), throwing an exception if this would establish
+ * a cycle of dependencies.
+ * @param one the first (dependent) variable
+ * @param two the second (dependee) variable
+ * @throws XPathException if adding this dependency creates a cycle of dependencies among global variables.
+ */
+
+ public synchronized void registerDependency(GlobalVariable one, GlobalVariable two) throws XPathException {
+ if (one == two) {
+ throw new XPathException.Circularity("Circular dependency among global variables: "
+ + one.getVariableQName().getDisplayName() + " depends on its own value");
+ }
+ Set<GlobalVariable> transitiveDependencies = dependencies.get(two);
+ if (transitiveDependencies != null) {
+ if (transitiveDependencies.contains(one)) {
+ throw new XPathException.Circularity("Circular dependency among variables: "
+ + one.getVariableQName().getDisplayName() + " depends on the value of "
+ + two.getVariableQName().getDisplayName() + ", which depends directly or indirectly on the value of "
+ + one.getVariableQName().getDisplayName());
+ }
+ for (GlobalVariable var : transitiveDependencies) {
+ // register the transitive dependencies
+ registerDependency(one, var);
+ }
+ }
+ Set<GlobalVariable> existingDependencies = dependencies.get(one);
+ if (existingDependencies == null) {
+ existingDependencies = new HashSet<GlobalVariable>();
+ dependencies.put(one, existingDependencies);
+ }
+ existingDependencies.add(two);
+
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/Block.java b/sf/saxon/expr/instruct/Block.java
new file mode 100644
index 0000000..fba37aa
--- /dev/null
+++ b/sf/saxon/expr/instruct/Block.java
@@ -0,0 +1,754 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.BlockCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.BlockAdjunct;
+import net.sf.saxon.evpull.BlockEventIterator;
+import net.sf.saxon.evpull.EmptyEventIterator;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.IntegerRange;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+
+
+/**
+* An expression that delivers the concatenation of the results of its subexpressions. This may
+ * represent an XSLT sequence constructor, or an XPath/XQuery expression of the form (a,b,c,d).
+*/
+
+public class Block extends Instruction {
+
+ // TODO: allow the last expression in a Block to be a tail-call of a function, at least in push mode
+
+ private Expression[] children;
+ private boolean allNodesUntyped;
+
+ /**
+ * Create an empty block
+ */
+
+ public Block() {
+ }
+
+ /**
+ * Static factory method to create a block. If one of the arguments is already a block,
+ * the contents will be merged into a new composite block
+ * @param e1 the first subexpression (child) of the block
+ * @param e2 the second subexpression (child) of the block
+ * @return a Block containing the two subexpressions, and if either of them is a block, it will
+ * have been collapsed to create a flattened sequence
+ */
+
+ public static Expression makeBlock(/*@Nullable*/ Expression e1, Expression e2) {
+ if (e1==null || Literal.isEmptySequence(e1)) {
+ return e2;
+ }
+ if (e2==null || Literal.isEmptySequence(e2)) {
+ return e1;
+ }
+ if (e1 instanceof Block || e2 instanceof Block) {
+ Iterator<Expression> it1 = (e1 instanceof Block ? e1.iterateSubExpressions() : new MonoIterator<Expression>(e1));
+ Iterator<Expression> it2 = (e2 instanceof Block ? e2.iterateSubExpressions() : new MonoIterator<Expression>(e2));
+ List<Expression> list = new ArrayList<Expression>(10);
+ while (it1.hasNext()) {
+ list.add(it1.next());
+ }
+ while (it2.hasNext()) {
+ list.add(it2.next());
+ }
+ Expression[] exps = new Expression[list.size()];
+ exps = list.toArray(exps);
+ Block b = new Block();
+ b.setChildren(exps);
+ return b;
+ } else {
+ Expression[] exps = {e1, e2};
+ Block b = new Block();
+ b.setChildren(exps);
+ return b;
+ }
+ }
+
+ /**
+ * Static factory method to create a block from a list of expressions
+ * @param list the list of expressions making up this block. The members of the List must
+ * be instances of Expression
+ * @return a Block containing the two subexpressions, and if either of them is a block, it will
+ * have been collapsed to create a flattened sequence
+ */
+
+ public static Expression makeBlock(List<Expression> list) {
+ if (list.size() == 0) {
+ return Literal.makeEmptySequence();
+ } else if (list.size() == 1) {
+ return list.get(0);
+ } else {
+ Expression[] exps = new Expression[list.size()];
+ exps = list.toArray(exps);
+ Block b = new Block();
+ b.setChildren(exps);
+ return b;
+ }
+ }
+
+
+ /**
+ * Set the children of this instruction
+ * @param children The instructions that are children of this instruction
+ */
+
+ public void setChildren(Expression[] children) {
+ this.children = children;
+ for (Expression aChildren : children) {
+ adoptChildExpression(aChildren);
+ }
+ }
+
+ public String getExpressionName() {
+ return "sequence";
+ }
+
+ /**
+ * Get the children of this instruction
+ * @return the children of this instruction, as an array of Instruction objects. May return
+ * a zero-length array if there are no children
+ */
+
+ public Expression[] getChildren() {
+ return children;
+ }
+
+
+ public int computeSpecialProperties() {
+ if (children.length == 0) {
+ // An empty sequence has all special properties except "has side effects".
+ return StaticProperty.SPECIAL_PROPERTY_MASK &~ StaticProperty.HAS_SIDE_EFFECTS;
+ }
+ int p = super.computeSpecialProperties();
+ if (allNodesUntyped) {
+ p |= StaticProperty.ALL_NODES_UNTYPED;
+ }
+ // if all the expressions are axis expressions, we have a same-document node-set
+ boolean allAxisExpressions = true;
+ boolean allChildAxis = true;
+ boolean allSubtreeAxis = true;
+ for (Expression child : children) {
+ if (!(child instanceof AxisExpression)) {
+ allAxisExpressions = false;
+ allChildAxis = false;
+ allSubtreeAxis = false;
+ break;
+ }
+ byte axis = ((AxisExpression) child).getAxis();
+ if (axis != AxisInfo.CHILD) {
+ allChildAxis = false;
+ }
+ if (!AxisInfo.isSubtreeAxis[axis]) {
+ allSubtreeAxis = false;
+ }
+ }
+ if (allAxisExpressions) {
+ p |= StaticProperty.CONTEXT_DOCUMENT_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET |
+ StaticProperty.NON_CREATIVE;
+ // if they all use the child axis, then we have a peer node-set
+ if (allChildAxis) {
+ p |= StaticProperty.PEER_NODESET;
+ }
+ if (allSubtreeAxis) {
+ p |= StaticProperty.SUBTREE_NODESET;
+ }
+ // special case: selecting attributes then children, node-set is sorted
+ if (children.length == 2 &&
+ ((AxisExpression)children[0]).getAxis() == AxisInfo.ATTRIBUTE &&
+ ((AxisExpression)children[1]).getAxis() == AxisInfo.CHILD) {
+ p |= StaticProperty.ORDERED_NODESET;
+ }
+ }
+ return p;
+ }
+
+ /**
+ * Determine whether the block includes any instructions that might return nodes with a type annotation
+ * @param th the type hierarchy cache
+ * @return true if any expression in the block can return type-annotated nodes
+ */
+
+ private boolean mayReturnTypedNodes(TypeHierarchy th) {
+ for (Expression exp : children) {
+ if ((exp.getSpecialProperties() & StaticProperty.ALL_NODES_UNTYPED) == 0) {
+ ItemType it = exp.getItemType(th);
+ if (th.relationship(it, NodeKindTest.ELEMENT) != TypeHierarchy.DISJOINT ||
+ th.relationship(it, NodeKindTest.ATTRIBUTE) != TypeHierarchy.DISJOINT ||
+ th.relationship(it, NodeKindTest.ATTRIBUTE) != TypeHierarchy.DISJOINT) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Merge any adjacent instructions that create literal text nodes
+ * @return the expression after merging literal text instructions
+ */
+
+ public Expression mergeAdjacentTextInstructions() {
+ boolean[] isLiteralText = new boolean[children.length];
+ boolean hasAdjacentTextNodes = false;
+ for (int i=0; i<children.length; i++) {
+ isLiteralText[i] = children[i] instanceof ValueOf &&
+ ((ValueOf)children[i]).getContentExpression() instanceof StringLiteral &&
+ !((ValueOf)children[i]).isDisableOutputEscaping();
+
+ if (i > 0 && isLiteralText[i] && isLiteralText[i-1]) {
+ hasAdjacentTextNodes = true;
+ }
+ }
+ if (hasAdjacentTextNodes) {
+ List<Expression> content = new ArrayList<Expression>(children.length);
+ String pendingText = null;
+ for (int i=0; i<children.length; i++) {
+ if (isLiteralText[i]) {
+ pendingText = (pendingText == null ? "" : pendingText) +
+ ((StringLiteral)((ValueOf)children[i]).getContentExpression()).getStringValue();
+ } else {
+ if (pendingText != null) {
+ ValueOf inst = new ValueOf(new StringLiteral(pendingText), false, false);
+ content.add(inst);
+ pendingText = null;
+ }
+ content.add(children[i]);
+ }
+ }
+ if (pendingText != null) {
+ ValueOf inst = new ValueOf(new StringLiteral(pendingText), false, false);
+ content.add(inst);
+ }
+ return makeBlock(content);
+ } else {
+ return this;
+ }
+ }
+
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return Arrays.asList(children).iterator();
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ List<SubExpressionInfo> info = new ArrayList<SubExpressionInfo>(children.length);
+ for (Expression child : children) {
+ info.add(new SubExpressionInfo(child, true, false, INHERITED_CONTEXT));
+ }
+ return info.iterator();
+ }
+
+ /**
+ * Test whether the Block includes a LocalParam instruction (which will be true only if it is the
+ * body of an XSLT template)
+ * @return true if the Block contains a LocalParam instruction
+ */
+
+// public boolean containsLocalParam() {
+// return children.length > 0 && children[0] instanceof LocalParamSetter;
+// }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ for (int c=0; c<children.length; c++) {
+ if (children[c] == original) {
+ children[c] = replacement;
+ found = true;
+ }
+ }
+ return found;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Block b2 = new Block();
+ Expression[] c2 = new Expression[children.length];
+ b2.children = c2;
+ for (int c=0; c<children.length; c++) {
+ c2[c] = children[c].copy();
+ }
+ b2.children = c2;
+ for (int c=0; c<children.length; c++) {
+ b2.adoptChildExpression(c2[c]);
+ }
+ b2.allNodesUntyped = allNodesUntyped;
+ ExpressionTool.copyLocationInfo(this, b2);
+ return b2;
+ }
+
+ /**
+ * Determine the data type of the items returned by this expression
+ * @return the data type
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public final ItemType getItemType(TypeHierarchy th) {
+ if (children.length==0) {
+ return ErrorType.getInstance();
+ }
+ ItemType t1 = children[0].getItemType(th);
+ for (int i=1; i<children.length; i++) {
+ t1 = Type.getCommonSuperType(t1, children[i].getItemType(th), th);
+ if (t1 instanceof AnyItemType) {
+ return t1; // no point going any further
+ }
+ }
+ return t1;
+ }
+
+ /**
+ * Determine the cardinality of the expression
+ */
+
+ public final int getCardinality() {
+ if (children.length==0) {
+ return StaticProperty.EMPTY;
+ }
+ int c1 = children[0].getCardinality();
+ for (int i=1; i<children.length; i++) {
+ c1 = Cardinality.sum(c1, children[i].getCardinality());
+ if (c1 == StaticProperty.ALLOWS_ZERO_OR_MORE) {
+ break;
+ }
+ }
+ return c1;
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true if any child instruction
+ * returns true.
+ */
+
+ public final boolean createsNewNodes() {
+ for (Expression aChildren : children) {
+ int props = aChildren.getSpecialProperties();
+ if ((props & StaticProperty.NON_CREATIVE) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Check to ensure that this expression does not contain any updating subexpressions.
+ * This check is overridden for those expressions that permit updating subexpressions.
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression has a non-permitted updateing subexpression
+ */
+
+ public void checkForUpdatingSubexpressions() throws XPathException {
+ if (children.length < 2) {
+ return;
+ }
+ boolean updating = false;
+ boolean nonUpdating = false;
+ for (Expression child : children) {
+ if (!ExpressionTool.isAllowedInUpdatingContext(child)) {
+ if (updating) {
+ XPathException err = new XPathException(
+ "If any subexpression is updating, then all must be updating", "XUST0001");
+ err.setLocator(child);
+ throw err;
+ }
+ nonUpdating = true;
+ }
+ if (child.isUpdatingExpression()) {
+ if (nonUpdating) {
+ XPathException err = new XPathException(
+ "If any subexpression is updating, then all must be updating", "XUST0001");
+ err.setLocator(child);
+ throw err;
+ }
+ updating = true;
+ }
+ }
+ }
+
+ /**
+ * Determine whether this is a vacuous expression as defined in the XQuery update specification
+ * @return true if this expression is vacuous
+ */
+
+ public boolean isVacuousExpression() {
+ // true if all subexpressions are vacuous
+ for (Expression child : children) {
+ if (!child.isVacuousExpression()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @exception XPathException if an error is discovered during expression
+ * rewriting
+ * @return the simplified expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ boolean allAtomic = true;
+ boolean nested = false;
+
+ for (int c=0; c<children.length; c++) {
+ children[c] = visitor.simplify(children[c]);
+ if (!Literal.isAtomic(children[c])) {
+ allAtomic = false;
+ }
+ if (children[c] instanceof Block) {
+ nested = true;
+ } else if (Literal.isEmptySequence(children[c])) {
+ nested = true;
+ }
+ }
+ if (children.length == 1) {
+ return getChildren()[0];
+ }
+ if (children.length == 0) {
+ Expression result = Literal.makeEmptySequence();
+ ExpressionTool.copyLocationInfo(this, result);
+ return result;
+ }
+ if (nested) {
+ List<Expression> list = new ArrayList<Expression>(children.length*2);
+ flatten(list);
+ children = new Expression[list.size()];
+ for (int i=0; i<children.length; i++) {
+ children[i] = list.get(i);
+ adoptChildExpression(children[i]);
+ }
+ }
+ if (allAtomic) {
+ AtomicValue[] values = new AtomicValue[children.length];
+ for (int c=0; c<children.length; c++) {
+ values[c] = (AtomicValue)((Literal)children[c]).getValue();
+ }
+ Expression result = Literal.makeLiteral(new SequenceExtent<AtomicValue>(values));
+ ExpressionTool.copyLocationInfo(this, result);
+ return result;
+ }
+
+ return this;
+ }
+
+ /**
+ * Simplify the contents of a Block by merging any nested blocks, merging adjacent
+ * literals, and eliminating any empty sequences.
+ * @param targetList the new list of expressions comprising the contents of the block
+ * after simplification
+ * @throws XPathException should not happen
+ */
+
+ private void flatten(List<Expression> targetList) throws XPathException {
+ List<Item> currentLiteralList = null;
+ for (Expression child : children) {
+ if (Literal.isEmptySequence(child)) {
+ // do nothing, omit it from the output
+ } else if (child instanceof Block) {
+ flushCurrentLiteralList(currentLiteralList, targetList);
+ currentLiteralList = null;
+ ((Block) child).flatten(targetList);
+ } else if (child instanceof Literal && !(((Literal) child).getValue() instanceof IntegerRange)) {
+ SequenceIterator iterator = ((Literal) child).getValue().iterate();
+ if (currentLiteralList == null) {
+ currentLiteralList = new ArrayList<Item>(10);
+ }
+ while (true) {
+ Item item = iterator.next();
+ if (item == null) {
+ break;
+ }
+ currentLiteralList.add(item);
+ }
+ // no-op
+ } else {
+ flushCurrentLiteralList(currentLiteralList, targetList);
+ currentLiteralList = null;
+ targetList.add(child);
+ }
+ }
+ flushCurrentLiteralList(currentLiteralList, targetList);
+ }
+
+ /**
+ * Determine whether the block is a candidate for evaluation using a "shared append expression"
+ * where the result of the evaluation is a sequence implemented as a list of subsequences
+ */
+
+ public boolean isCandidateForSharedAppend() {
+ for (Expression exp : children) {
+ if (exp instanceof VariableReference || exp instanceof Literal) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void flushCurrentLiteralList(List<Item> currentLiteralList, List<Expression> list) throws XPathException {
+ if (currentLiteralList != null) {
+ SequenceIterator<Item> iter = new net.sf.saxon.tree.iter.ListIterator<Item>(currentLiteralList);
+ list.add(Literal.makeLiteral(SequenceExtent.makeSequenceExtent(iter)));
+ }
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ for (int c=0; c<children.length; c++) {
+ children[c] = visitor.typeCheck(children[c], contextItemType);
+ adoptChildExpression(children[c]);
+ }
+ if (!mayReturnTypedNodes(visitor.getConfiguration().getTypeHierarchy())) {
+ resetLocalStaticProperties();
+ allNodesUntyped = true;
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ for (int c=0; c<children.length; c++) {
+ children[c] = visitor.optimize(children[c], contextItemType);
+ adoptChildExpression(children[c]);
+ }
+ boolean canSimplify = false;
+ boolean prevLiteral = false;
+ // Simplify the expression by collapsing nested blocks and merging adjacent literals
+ for (Expression child : children) {
+ if (child instanceof Block) {
+ canSimplify = true;
+ break;
+ }
+ if (child instanceof Literal) {
+ if (prevLiteral || Literal.isEmptySequence(child)) {
+ canSimplify = true;
+ break;
+ }
+ prevLiteral = true;
+ } else {
+ prevLiteral = false;
+ }
+ }
+ if (canSimplify) {
+ List<Expression> list = new ArrayList<Expression>(children.length*2);
+ flatten(list);
+ children = new Expression[list.size()];
+ for (int i=0; i<children.length; i++) {
+ children[i] = list.get(i);
+ adoptChildExpression(children[i]);
+ }
+ }
+ if (children.length == 0) {
+ return Literal.makeEmptySequence();
+ } else if (children.length == 1) {
+ return children[0];
+ } else {
+ return this;
+ }
+ }
+
+
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ for (int c=0; c<children.length; c++) {
+ children[c] = doPromotion(children[c], offer);
+ }
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ for (Expression child : children) {
+ child.checkPermittedContents(parentType, env, false);
+ }
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("sequence");
+ for (Expression child : children) {
+ child.explain(out);
+ }
+ out.endElement();
+ }
+
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ TailCall tc = null;
+ for (Expression child : children) {
+ try {
+ if (child instanceof TailCallReturner) {
+ tc = ((TailCallReturner) child).processLeavingTail(context);
+ } else {
+ child.process(context);
+ tc = null;
+ }
+ } catch (XPathException e) {
+ e.maybeSetLocation(child);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+ return tc;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided. This implementation provides both iterate() and
+ * process() methods natively.
+ */
+
+ public int getImplementationMethod() {
+ return ITERATE_METHOD | PROCESS_METHOD;
+ }
+
+ /**
+ * Iterate over the results of all the child expressions
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ if (children.length == 0) {
+ return EmptyIterator.emptyIterator();
+ } else if (children.length == 1) {
+ return children[0].iterate(context);
+ } else {
+ return new BlockIterator(children, context);
+ }
+ }
+
+ /**
+ * Get an EventIterator over the results of all the child expressions
+ * @param context the XPath dynamic context
+ * @return an EventIterator
+ */
+
+ public EventIterator iterateEvents(XPathContext context) throws XPathException {
+ if (children.length == 0) {
+ return EmptyEventIterator.getInstance();
+ } else if (children.length == 1) {
+ return children[0].iterateEvents(context);
+ } else {
+ return new BlockEventIterator(children, context);
+ }
+ }
+
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ for (Expression child : children) {
+ child.evaluatePendingUpdates(context, pul);
+ }
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Block expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new BlockCompiler();
+ }
+ //#endif
+//#ifdefined STREAM
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public BlockAdjunct getStreamingAdjunct() {
+ return new BlockAdjunct();
+ }
+
+ //#endif
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/instruct/BlockIterator.java b/sf/saxon/expr/instruct/BlockIterator.java
new file mode 100644
index 0000000..77c64a1
--- /dev/null
+++ b/sf/saxon/expr/instruct/BlockIterator.java
@@ -0,0 +1,127 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Iterate over the instructions in the Block, concatenating the result of each instruction
+ * into a single combined sequence.
+ */
+
+public class BlockIterator implements SequenceIterator<Item> {
+
+ private Expression[] children;
+ private int i = 0;
+ /*@Nullable*/ private SequenceIterator child;
+ private XPathContext context;
+ private Item current;
+ private int position = 0;
+
+ public BlockIterator(Expression[] children, XPathContext context) {
+ this.children = children;
+ this.context = context;
+ }
+
+ /**
+ * Get the next item in the sequence. <BR>
+ *
+ * @return the next item, or null if there are no more items.
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error occurs retrieving the next item
+ */
+
+ public Item next() throws XPathException {
+ if (position < 0) {
+ return null;
+ }
+ while (true) {
+ if (child == null) {
+ child = children[i++].iterate(context);
+ }
+ current = child.next();
+ if (current != null) {
+ position++;
+ return current;
+ }
+ child = null;
+ if (i >= children.length) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Get the current value in the sequence (the one returned by the
+ * most recent call on next()). This will be null before the first
+ * call of next().
+ *
+ * @return the current item, the one most recently returned by a call on
+ * next(); or null, if next() has not been called, or if the end
+ * of the sequence has been reached.
+ */
+
+ public Item current() {
+ return current;
+ }
+
+ /**
+ * Get the current position. This will be zero before the first call
+ * on next(), otherwise it will be the number of times that next() has
+ * been called.
+ *
+ * @return the current position, the position of the item returned by the
+ * most recent call of next()
+ */
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ if (child != null) {
+ child.close();
+ }
+ }
+
+ /**
+ * Get another SequenceIterator that iterates over the same items as the original,
+ * but which is repositioned at the start of the sequence.
+ *
+ * @return a SequenceIterator that iterates over the same items,
+ * positioned before the first item
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error occurs
+ */
+
+ /*@NotNull*/
+ public BlockIterator getAnother() throws XPathException {
+ return new BlockIterator(children, context);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link SequenceIterator#GROUNDED}, {@link SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/CallTemplate.java b/sf/saxon/expr/instruct/CallTemplate.java
new file mode 100644
index 0000000..a872e21
--- /dev/null
+++ b/sf/saxon/expr/instruct/CallTemplate.java
@@ -0,0 +1,552 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.CallTemplateCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.Controller;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.QNameException;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * Instruction representing an xsl:call-template element in the stylesheet.
+ */
+
+public class CallTemplate extends Instruction implements ITemplateCall {
+
+ /*@Nullable*/ private Template template = null; // Null only for saxon:call-template
+ /*@NotNull*/ private WithParam[] actualParams = WithParam.EMPTY_ARRAY;
+ /*@NotNull*/ private WithParam[] tunnelParams = WithParam.EMPTY_ARRAY;
+ private boolean useTailRecursion = false;
+ private Expression calledTemplateExpression; // allows name to be an AVT
+ private NamespaceResolver nsContext; // needed only for a dynamic call
+
+ /**
+ * Construct a CallTemplate instruction.
+ *
+ * @param template the Template object identifying the template to be called, in the normal
+ * case where this is known statically
+ * @param useTailRecursion true if the call is potentially tail recursive
+ * @param calledTemplateExpression expression to calculate the name of the template to be called
+ * at run-time, this supports the saxon:allow-avt option
+ * @param nsContext the static namespace context of the instruction, needed only in the case
+ * where the name of the called template is to be calculated dynamically
+ */
+
+ public CallTemplate(Template template,
+ boolean useTailRecursion,
+ Expression calledTemplateExpression,
+ NamespaceResolver nsContext) {
+
+ this.template = template;
+ this.useTailRecursion = useTailRecursion;
+ this.calledTemplateExpression = calledTemplateExpression;
+ this.nsContext = nsContext;
+ adoptChildExpression(calledTemplateExpression);
+ }
+
+ public Expression getCalledTemplateExpression(){
+ return calledTemplateExpression;
+ }
+
+ public NamespaceResolver getNsContext(){
+ return nsContext;
+ }
+
+ /**
+ * Set the actual parameters on the call
+ *
+ * @param actualParams the parameters that are not tunnel parameters
+ * @param tunnelParams the tunnel parameters
+ */
+
+ public void setActualParameters(
+ /*@NotNull*/ WithParam[] actualParams,
+ /*@NotNull*/ WithParam[] tunnelParams) {
+ this.actualParams = actualParams;
+ this.tunnelParams = tunnelParams;
+ for (WithParam actualParam : actualParams) {
+ adoptChildExpression(actualParam.getSelectExpression());
+ }
+ for (WithParam tunnelParam : tunnelParams) {
+ adoptChildExpression(tunnelParam.getSelectExpression());
+ }
+ }
+
+ /**
+ * Get the actual parameters passed to the called template
+ *
+ * @return the non-tunnel parameters
+ */
+
+ /*@NotNull*/
+ public WithParam[] getActualParams() {
+ return actualParams;
+ }
+
+ /**
+ * Get the tunnel parameters passed to the called template
+ *
+ * @return the tunnel parameters
+ */
+
+ /*@NotNull*/
+ public WithParam[] getTunnelParams() {
+ return tunnelParams;
+ }
+
+ /**
+ * Get the target template, if known statically
+ *
+ * @return the target template
+ */
+
+ public Template getTargetTemplate() {
+ return template;
+ }
+
+ /**
+ * Ask whether this is a tail call
+ * @return true if this is a tail call
+ */
+
+ public boolean usesTailRecursion() {
+ return useTailRecursion;
+ }
+
+ /**
+ * Return the name of this instruction.
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_CALL_TEMPLATE;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression).
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ * @throws XPathException if an error is discovered during expression
+ * rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ WithParam.simplify(actualParams, visitor);
+ WithParam.simplify(tunnelParams, visitor);
+ if (calledTemplateExpression != null) {
+ calledTemplateExpression = visitor.simplify(calledTemplateExpression);
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ WithParam.typeCheck(actualParams, visitor, contextItemType);
+ WithParam.typeCheck(tunnelParams, visitor, contextItemType);
+ if (calledTemplateExpression != null) {
+ calledTemplateExpression = visitor.typeCheck(calledTemplateExpression, contextItemType);
+ adoptChildExpression(calledTemplateExpression);
+ } else if (template.getBody() != null) {
+ // For non-tunnel parameters, see if the supplied value is type-safe against the declared
+ // type of the value, and if so, avoid the dynamic type check
+ // Can't do this check unless the target template has been compiled.
+ boolean backwards = visitor.getStaticContext().isInBackwardsCompatibleMode();
+ for (int p = 0; p < actualParams.length; p++) {
+ WithParam wp = actualParams[p];
+ int id = wp.getParameterId();
+ LocalParam lp = template.getLocalParam(id);
+ if (lp != null) {
+ SequenceType req = lp.getRequiredType();
+ RoleLocator role = new RoleLocator(RoleLocator.PARAM, wp.getVariableQName().getDisplayName(), p);
+ Expression select = TypeChecker.staticTypeCheck(
+ wp.getSelectExpression(), req, backwards, role, visitor);
+ wp.setSelectExpression(select);
+ wp.setTypeChecked(true);
+ }
+ }
+
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ WithParam.optimize(visitor, actualParams, contextItemType);
+ WithParam.optimize(visitor, tunnelParams, contextItemType);
+ if (calledTemplateExpression != null) {
+ calledTemplateExpression = visitor.optimize(calledTemplateExpression, contextItemType);
+ adoptChildExpression(calledTemplateExpression);
+ }
+ return this;
+ }
+
+
+ /**
+ * Get the cardinality of the sequence returned by evaluating this instruction
+ *
+ * @return the static cardinality
+ */
+
+ public int computeCardinality() {
+ if (template == null) {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ } else {
+ return template.getRequiredType().getCardinality();
+ }
+ }
+
+ /**
+ * Get the item type of the items returned by evaluating this instruction
+ *
+ * @param th the type hierarchy cache
+ * @return the static item type of the instruction
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (template == null) {
+ return AnyItemType.getInstance();
+ } else {
+ return template.getRequiredType().getPrimaryType();
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ CallTemplate ct = new CallTemplate(template, useTailRecursion,
+ (calledTemplateExpression==null ? null : calledTemplateExpression.copy()), nsContext);
+ ct.actualParams = WithParam.copy(actualParams);
+ ct.tunnelParams = WithParam.copy(tunnelParams);
+ return ct;
+ }
+
+ public int getIntrinsicDependencies() {
+ // we could go to the called template and find which parts of the context it depends on, but this
+ // would create the risk of infinite recursion. So we just assume that the dependencies exist
+ return StaticProperty.DEPENDS_ON_XSLT_CONTEXT |
+ StaticProperty.DEPENDS_ON_FOCUS;
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation currently returns true unconditionally.
+ */
+
+ public final boolean createsNewNodes() {
+ return true;
+ }
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ ArrayList<Expression> list = new ArrayList<Expression>(10);
+ if (calledTemplateExpression != null) {
+ list.add(calledTemplateExpression);
+ }
+ WithParam.gatherXPathExpressions(actualParams, list);
+ WithParam.gatherXPathExpressions(tunnelParams, list);
+ return list.iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (WithParam.replaceXPathExpression(actualParams, original, replacement)) {
+ found = true;
+ }
+ if (WithParam.replaceXPathExpression(tunnelParams, original, replacement)) {
+ found = true;
+ }
+ if (calledTemplateExpression == original) {
+ calledTemplateExpression = replacement;
+ }
+ return found;
+ }
+
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ *
+ * @param offer The type of rewrite being offered
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ if (calledTemplateExpression != null) {
+ calledTemplateExpression = doPromotion(calledTemplateExpression, offer);
+ }
+ WithParam.promoteParams(this, actualParams, offer);
+ WithParam.promoteParams(this, tunnelParams, offer);
+ }
+
+ /**
+ * Process this instruction, without leaving any tail calls.
+ *
+ * @param context the dynamic context for this transformation
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public void process(XPathContext context) throws XPathException {
+
+ Template t = getTargetTemplate(context);
+ XPathContextMajor c2 = context.newContext();
+ c2.setOrigin(this);
+ c2.openStackFrame(t.getStackFrameMap());
+ c2.setLocalParameters(assembleParams(context, actualParams));
+ c2.setTunnelParameters(assembleTunnelParams(context, tunnelParams));
+
+ try {
+ TailCall tc = t.expand(c2);
+ while (tc != null) {
+ tc = tc.processLeavingTail();
+ }
+ } catch (StackOverflowError e) {
+ XPathException err = new XPathException("Too many nested template or function calls. The stylesheet may be looping.");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ }
+
+ /**
+ * Process this instruction. If the called template contains a tail call (which may be
+ * an xsl:call-template of xsl:apply-templates instruction) then the tail call will not
+ * actually be evaluated, but will be returned in a TailCall object for the caller to execute.
+ *
+ * @param context the dynamic context for this transformation
+ * @return an object containing information about the tail call to be executed by the
+ * caller. Returns null if there is no tail call.
+ */
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ if (useTailRecursion) {
+
+ // if name is determined dynamically, determine it now
+
+ Template target = getTargetTemplate(context);
+
+ // handle parameters if any
+
+ ParameterSet params = assembleParams(context, actualParams);
+ ParameterSet tunnels = assembleTunnelParams(context, tunnelParams);
+
+ // Call the named template. Actually, don't call it; rather construct a call package
+ // and return it to the caller, who will then process this package.
+
+ //System.err.println("Call template using tail recursion");
+ if (params == null) { // bug 490967
+ params = ParameterSet.EMPTY_PARAMETER_SET;
+ }
+
+ // clear all the local variables: they are no longer needed
+ Arrays.fill(context.getStackFrame().getStackFrameValues(), null);
+
+ return new CallTemplatePackage(target, params, tunnels, this, context);
+
+ } else {
+ process(context);
+ return null;
+ }
+ }
+
+ /**
+ * Get the template, in the case where it is specified dynamically.
+ *
+ * @param context The dynamic context of the transformation
+ * @return The template to be called
+ * @throws XPathException if a dynamic error occurs: specifically, if the
+ * template name is computed at run-time (Saxon extension) and the name is invalid
+ * or does not reference a known template
+ */
+
+ public Template getTargetTemplate(XPathContext context) throws XPathException {
+ if (calledTemplateExpression != null) {
+ Controller controller = context.getController();
+ assert controller != null;
+ CharSequence qname = calledTemplateExpression.evaluateAsString(context);
+
+ String prefix;
+ String localName;
+ try {
+ String[] parts = controller.getConfiguration().getNameChecker().getQNameParts(qname);
+ prefix = parts[0];
+ localName = parts[1];
+ } catch (QNameException err) {
+ dynamicError("Invalid template name. " + err.getMessage(), "XTSE0650", context);
+ return null;
+ }
+ String uri = nsContext.getURIForPrefix(prefix, false);
+ if (uri == null) {
+ dynamicError("Namespace prefix " + prefix + " has not been declared", "XTSE0650", context);
+ }
+ StructuredQName qName = new StructuredQName("", uri, localName);
+ Template target = ((PreparedStylesheet) controller.getExecutable()).getNamedTemplate(qName);
+ if (target == null) {
+ dynamicError("Template " + qname + " has not been defined", "XTSE0650", context);
+ }
+ return target;
+ } else {
+ return template;
+ }
+ }
+
+
+ public StructuredQName getObjectName() {
+ return (template == null ? null : template.getTemplateName());
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CallTemplate expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CallTemplateCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("callTemplate");
+ if (template != null && template.getTemplateName() != null) {
+ out.emitAttribute("name", template.getTemplateName().getDisplayName());
+ } else {
+ out.startSubsidiaryElement("name");
+ calledTemplateExpression.explain(out);
+ out.endSubsidiaryElement();
+ }
+ if (actualParams.length > 0) {
+ out.startSubsidiaryElement("withParams");
+ WithParam.explainParameters(actualParams, out);
+ out.endSubsidiaryElement();
+ }
+ if (tunnelParams.length > 0) {
+ out.startSubsidiaryElement("tunnelParams");
+ WithParam.explainParameters(tunnelParams, out);
+ out.endSubsidiaryElement();
+ }
+ out.endElement();
+ }
+
+
+ /**
+ * A CallTemplatePackage is an object that encapsulates the name of a template to be called,
+ * the parameters to be supplied, and the execution context. This object can be returned as a tail
+ * call, so that the actual call is made from a lower point on the stack, allowing a tail-recursive
+ * template to execute in a finite stack size
+ */
+
+ public static class CallTemplatePackage implements TailCall {
+
+ private Template target;
+ private ParameterSet params;
+ private ParameterSet tunnelParams;
+ private Instruction instruction;
+ private XPathContext evaluationContext;
+
+ /**
+ * Construct a CallTemplatePackage that contains information about a call.
+ *
+ * @param template the Template to be called
+ * @param params the parameters to be supplied to the called template
+ * @param tunnelParams the tunnel parameter supplied to the called template
+ * @param evaluationContext saved context information from the Controller (current mode, etc)
+ * which must be reset to ensure that the template is called with all the context information
+ * intact
+ */
+
+ public CallTemplatePackage(Template template,
+ ParameterSet params,
+ ParameterSet tunnelParams,
+ Instruction instruction,
+ XPathContext evaluationContext) {
+ target = template;
+ this.params = params;
+ this.tunnelParams = tunnelParams;
+ this.instruction = instruction;
+ this.evaluationContext = evaluationContext;
+ }
+
+ /**
+ * Process the template call encapsulated by this package.
+ *
+ * @return another TailCall. This will never be the original call, but it may be the next
+ * recursive call. For example, if A calls B which calls C which calls D, then B may return
+ * a TailCall to A representing the call from B to C; when this is processed, the result may be
+ * a TailCall representing the call from C to D.
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public TailCall processLeavingTail() throws XPathException {
+ // TODO: the idea of tail call optimization is to reuse the caller's stack frame rather than
+ // creating a new one. We're doing this for the Java stack, but not for the context stack where
+ // local variables are held. It should be possible to avoid creating a new context, and instead
+ // to update the existing one in situ.
+ XPathContextMajor c2 = evaluationContext.newContext();
+ c2.setOrigin(instruction);
+ c2.setLocalParameters(params);
+ c2.setTunnelParameters(tunnelParams);
+ c2.openStackFrame(target.getStackFrameMap());
+
+ // System.err.println("Tail call on template");
+
+ return target.expand(c2);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/Choose.java b/sf/saxon/expr/instruct/Choose.java
new file mode 100644
index 0000000..c1f2621
--- /dev/null
+++ b/sf/saxon/expr/instruct/Choose.java
@@ -0,0 +1,1092 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ChooseCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.ExpressionInverter;
+import com.saxonica.stream.adjunct.ChooseAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.TypeCheckerEnvironment;
+import net.sf.saxon.evpull.EmptyEventIterator;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.functions.BooleanFn;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.pattern.ConditionalPattern;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Compiled representation of an xsl:choose or xsl:if element in the stylesheet.
+ * Also used for typeswitch in XQuery.
+*/
+
+public class Choose extends Instruction {
+
+ // The class implements both xsl:choose and xsl:if. There is a list of boolean
+ // expressions (conditions) and a list of corresponding actions: the conditions
+ // are evaluated in turn, and when one is found that is true, the corresponding
+ // action is evaluated. For xsl:if, there is always one condition and one action.
+ // An xsl:otherwise is compiled as if it were xsl:when test="true()". If no
+ // condition is satisfied, the instruction returns an empty sequence.
+
+ private Expression[] conditions;
+ private Expression[] actions;
+
+
+ /**
+ * Construct an xsl:choose instruction
+ * @param conditions the conditions to be tested, in order
+ * @param actions the actions to be taken when the corresponding condition is true
+ */
+
+ public Choose(Expression[] conditions, Expression[] actions) {
+ this.conditions = conditions;
+ this.actions = actions;
+ if (conditions.length != actions.length) {
+ throw new IllegalArgumentException("Choose: unequal length arguments");
+ }
+ for (int i=0; i<conditions.length; i++) {
+ adoptChildExpression(conditions[i]);
+ adoptChildExpression(actions[i]);
+ }
+ }
+
+ /**
+ * Make a simple conditional expression (if (condition) then (thenExp) else (elseExp)
+ * @param condition the condition to be tested
+ * @param thenExp the expression to be evaluated if the condition is true
+ * @param elseExp the expression to be evaluated if the condition is false
+ * @return the expression
+ */
+
+ public static Expression makeConditional(Expression condition, Expression thenExp, Expression elseExp) {
+ if (Literal.isEmptySequence(elseExp)) {
+ Expression[] conditions = new Expression[] {condition};
+ Expression[] actions = new Expression[] {thenExp};
+ return new Choose(conditions, actions);
+ } else {
+ Expression[] conditions = new Expression[] {condition, Literal.makeLiteral(BooleanValue.TRUE)};
+ Expression[] actions = new Expression[] {thenExp, elseExp};
+ return new Choose(conditions, actions);
+ }
+ }
+
+ /**
+ * Make a simple conditional expression (if (condition) then (thenExp) else ()
+ * @param condition the condition to be tested
+ * @param thenExp the expression to be evaluated if the condition is true
+ * @return the expression
+ */
+
+ public static Expression makeConditional(Expression condition, Expression thenExp) {
+ Expression[] conditions = new Expression[] {condition};
+ Expression[] actions = new Expression[] {thenExp};
+ return new Choose(conditions, actions);
+ }
+
+ /**
+ * Test whether an expression is a single-branch choose, that is, an expression of the form
+ * if (condition) then exp else ()
+ * @param exp the expression to be tested
+ * @return true if the expression is a choose expression and there is only one condition,
+ * so that the expression returns () if this condition is false
+ */
+
+ public static boolean isSingleBranchChoice(Expression exp) {
+ return (exp instanceof Choose && ((Choose)exp).conditions.length == 1);
+ }
+
+ /**
+ * Get the array of conditions to be tested
+ * @return the array of condition expressions
+ */
+
+ public Expression[] getConditions() {
+ return conditions;
+ }
+
+ /**
+ * Get the array of actions to be performed
+ * @return the array of expressions to be evaluated when the corresponding condition is true
+ */
+
+ public Expression[] getActions() {
+ return actions;
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ * We assume that if there was
+ * only one condition then it was an xsl:if; this is not necessarily so, but
+ * it's adequate for tracing purposes.
+ */
+
+
+ public int getInstructionNameCode() {
+ return (conditions.length==1 ? StandardNames.XSL_IF : StandardNames.XSL_CHOOSE);
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression).
+ *
+ * @exception XPathException if an error is discovered during expression
+ * rewriting
+ * @return the simplified expression
+ * @param visitor expression visitor object
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i] = visitor.simplify(conditions[i]);
+ try {
+ actions[i] = visitor.simplify(actions[i]);
+ } catch (XPathException err) {
+ // mustn't throw the error unless the branch is actually selected, unless its a type error
+ if (err.isTypeError()) {
+ throw err;
+ } else {
+ actions[i] = new ErrorExpression(err);
+ }
+ }
+ }
+ return this;
+ }
+
+ private Expression removeRedundantBranches(ExpressionVisitor visitor) {
+ // Eliminate a redundant if (false)
+
+ for (int i=0; i<conditions.length; i++) {
+ if (Literal.isConstantBoolean(conditions[i], false)) {
+ if (conditions.length == 1) {
+ Literal lit = Literal.makeEmptySequence();
+ ExpressionTool.copyLocationInfo(this, lit);
+ return lit;
+ }
+ Expression[] c = new Expression[conditions.length-1];
+ Expression[] a = new Expression[conditions.length-1];
+ if (i != 0) {
+ System.arraycopy(conditions, 0, c, 0, i);
+ System.arraycopy(actions, 0, a, 0, i);
+ }
+ if (i != conditions.length) {
+ System.arraycopy(conditions, i+1, c, i, conditions.length-i-1);
+ System.arraycopy(actions, i+1, a, i, actions.length-i-1);
+ }
+ conditions = c;
+ actions = a;
+ i--;
+ }
+ }
+
+ // Eliminate everything that follows if (true)
+
+ for (int i=0; i<conditions.length-1; i++) {
+ if (Literal.isConstantBoolean(conditions[i], true)) {
+ if (i == 0) {
+ return actions[0];
+ }
+ Expression[] c = new Expression[i+1];
+ Expression[] a = new Expression[i+1];
+ System.arraycopy(conditions, 0, c, 0, i+1);
+ System.arraycopy(actions, 0, a, 0, i+1);
+ conditions = c;
+ actions = a;
+ break;
+ }
+ }
+
+ // See if only condition left is if (true) then x else ()
+
+ if (conditions.length == 1 && Literal.isConstantBoolean(conditions[0], true)) {
+ return actions[0];
+ }
+
+ // Eliminate a redundant <xsl:otherwise/> or "when (test) then ()"
+
+ if (/*Literal.isConstantBoolean(conditions[conditions.length-1], true) && */
+ Literal.isEmptySequence(actions[actions.length-1])) {
+ if (conditions.length == 1) {
+ return Literal.makeEmptySequence();
+ } else {
+ Expression[] c = new Expression[conditions.length-1];
+ System.arraycopy(conditions, 0, c, 0, conditions.length-1);
+ Expression[] a = new Expression[actions.length-1];
+ System.arraycopy(actions, 0, a, 0, actions.length-1);
+ conditions = c;
+ actions = a;
+ }
+ }
+
+ // Flatten an "else if"
+
+ if (Literal.isConstantBoolean(conditions[conditions.length-1], true) &&
+ actions[actions.length-1] instanceof Choose) {
+ Choose choose2 = (Choose)actions[actions.length-1];
+ int newLen = conditions.length + choose2.conditions.length - 1;
+ Expression[] c2 = new Expression[newLen];
+ Expression[] a2 = new Expression[newLen];
+ System.arraycopy(conditions, 0, c2, 0, conditions.length - 1);
+ System.arraycopy(actions, 0, a2, 0, actions.length - 1);
+ System.arraycopy(choose2.conditions, 0, c2, conditions.length - 1, choose2.conditions.length);
+ System.arraycopy(choose2.actions, 0, a2, actions.length - 1, choose2.actions.length);
+ conditions = c2;
+ actions = a2;
+ }
+
+ // Rewrite "if (EXP) then true() else false()" as boolean(EXP)
+
+ if (conditions.length == 2 &&
+ Literal.isConstantBoolean(actions[0], true) &&
+ Literal.isConstantBoolean(actions[1], false) &&
+ Literal.isConstantBoolean(conditions[1], true)) {
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (th.isSubType(conditions[0].getItemType(th), BuiltInAtomicType.BOOLEAN) &&
+ conditions[0].getCardinality() == StaticProperty.EXACTLY_ONE) {
+ return conditions[0];
+ } else {
+ return SystemFunctionCall.makeSystemFunction("boolean", new Expression[]{conditions[0]});
+ }
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i] = visitor.typeCheck(conditions[i], contextItemType);
+ XPathException err = TypeChecker.ebvError(conditions[i], visitor.getConfiguration().getTypeHierarchy());
+ if (err != null) {
+ err.setLocator(conditions[i]);
+ throw err;
+ }
+ }
+ // Check that each of the action branches satisfies the expected type. This is a stronger check than checking the
+ // type of the top-level expression. It's important with tail recursion not to wrap a tail call in a type checking
+ // expression just because a dynamic type check is needed on a different branch of the choice.
+ for (int i=0; i<actions.length; i++) {
+ try {
+ actions[i] = visitor.typeCheck(actions[i], contextItemType);
+ } catch (XPathException err) {
+ // mustn't throw the error unless the branch is actually selected, unless its a static or type error
+ if (err.isStaticError()) {
+ throw err;
+ } else if (err.isTypeError()) {
+ // if this is an "empty" else branch, don't be draconian about the error handling. It might be
+ // the user knows the otherwise branch isn't needed because one of the when branches will always
+ // be satisfied.
+ // Also, don't throw a type error if the branch will never be executed; this can happen with
+ // a typeswitch where the purpose of the condition is to test the type.
+ if (Literal.isEmptySequence(actions[i]) || Literal.isConstantBoolean(conditions[i], false)) {
+ actions[i] = new ErrorExpression(err);
+ } else {
+ throw err;
+ }
+ } else {
+ actions[i] = new ErrorExpression(err);
+ }
+ }
+ }
+ return removeRedundantBranches(visitor);
+ }
+
+ /**
+ * Determine whether this expression implements its own method for static type checking
+ *
+ * @return true - this expression has a non-trivial implementation of the staticTypeCheck()
+ * method
+ */
+
+ public boolean implementsStaticTypeCheck() {
+ return true;
+ }
+
+ /**
+ * Static type checking for conditional expressions is delegated to the expression itself,
+ * and is performed separately on each branch of the conditional, so that dynamic checks are
+ * added only on those branches where the check is actually required. This also results in a static
+ * type error if any branch is incapable of delivering a value of the required type. One reason
+ * for this approach is to avoid doing dynamic type checking on a recursive function call as this
+ * prevents tail-call optimization being used.
+ * @param req the required type
+ * @param backwardsCompatible true if backwards compatibility mode applies
+ * @param role the role of the expression in relation to the required type
+ * @param visitor an expression visitor
+ * @return the expression after type checking (perhaps augmented with dynamic type checking code)
+ * @throws XPathException if failures occur, for example if the static type of one branch of the conditional
+ * is incompatible with the required type
+ */
+
+ public Expression staticTypeCheck(SequenceType req,
+ boolean backwardsCompatible,
+ RoleLocator role, TypeCheckerEnvironment visitor)
+ throws XPathException {
+ for (int i=0; i<actions.length; i++) {
+ try {
+ actions[i] = TypeChecker.staticTypeCheck(actions[i], req, backwardsCompatible, role, visitor);
+ } catch (XPathException err) {
+ if (err.isStaticError()) {
+ throw err;
+ } else if (err.isTypeError()) {
+ visitor.issueWarning("Branch " + (i+1) + " of conditional will fail with a type error if executed. " + err.getMessage(), this);
+ }
+ actions[i] = new ErrorExpression(err);
+ }
+ }
+ // If the last condition isn't true(), then we need to consider the fall-through case, which returns
+ // an empty sequence
+ if (!Literal.isConstantBoolean(conditions[conditions.length-1], true) &&
+ !Cardinality.allowsZero(req.getCardinality())) {
+ Expression[] c = new Expression[conditions.length + 1];
+ Expression[] a = new Expression[conditions.length + 1];
+ System.arraycopy(conditions, 0, c, 0, conditions.length);
+ System.arraycopy(actions, 0, a, 0, conditions.length);
+ c[conditions.length] = Literal.makeLiteral(BooleanValue.TRUE);
+ String cond = (conditions.length == 1 ? "The condition is not" : "None of the conditions is");
+ XPathException err = new XPathException(
+ "Conditional expression: " + cond + " satisfied, so an empty sequence is returned, " +
+ "but this is not allowed as the " + role.getMessage());
+ err.setErrorCode(role.getErrorCode());
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ ErrorExpression errExp = new ErrorExpression(err);
+ ExpressionTool.copyLocationInfo(this, errExp);
+ a[conditions.length] = errExp;
+ conditions = c;
+ actions = a;
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i] = visitor.optimize(conditions[i], contextItemType);
+ Expression ebv = BooleanFn.rewriteEffectiveBooleanValue(conditions[i], visitor, contextItemType);
+ if (ebv != null && ebv != conditions[i]) {
+ conditions[i] = ebv;
+ adoptChildExpression(ebv);
+ }
+ if (conditions[i] instanceof Literal &&
+ !(((Literal)conditions[i]).getValue() instanceof BooleanValue)) {
+ final boolean b;
+ try {
+ b = ((Literal)conditions[i]).getValue().effectiveBooleanValue();
+ } catch (XPathException err) {
+ err.setLocator(this);
+ throw err;
+ }
+ conditions[i] = Literal.makeLiteral(BooleanValue.get(b));
+ }
+ }
+ for (int i=0; i<actions.length; i++) {
+ try {
+ actions[i] = visitor.optimize(actions[i], contextItemType);
+ } catch (XPathException err) {
+ // mustn't throw the error unless the branch is actually selected, unless its a type error
+ if (err.isTypeError()) {
+ throw err;
+ } else {
+ actions[i] = new ErrorExpression(err);
+ }
+ }
+ }
+ if (actions.length == 0) {
+ return Literal.makeEmptySequence();
+ }
+ Expression e = removeRedundantBranches(visitor);
+ if (e instanceof Choose) {
+ return visitor.getConfiguration().obtainOptimizer().trySwitch((Choose)e, visitor.getStaticContext());
+ } else {
+ return e;
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Expression[] c2 = new Expression[conditions.length];
+ Expression[] a2 = new Expression[conditions.length];
+ for (int c=0; c<conditions.length; c++) {
+ c2[c] = conditions[c].copy();
+ a2[c] = actions[c].copy();
+ }
+ return new Choose(c2, a2);
+ }
+
+
+ /**
+ * Check to ensure that this expression does not contain any updating subexpressions.
+ * This check is overridden for those expressions that permit updating subexpressions.
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression has a non-permitted updateing subexpression
+ */
+
+ public void checkForUpdatingSubexpressions() throws XPathException {
+ for (Expression condition : conditions) {
+ condition.checkForUpdatingSubexpressions();
+ if (condition.isUpdatingExpression()) {
+ XPathException err = new XPathException(
+ "Updating expression appears in a context where it is not permitted", "XUST0001");
+ err.setLocator(condition);
+ throw err;
+ }
+ }
+ boolean updating = false;
+ boolean nonUpdating = false;
+ for (Expression act : actions) {
+ act.checkForUpdatingSubexpressions();
+ if (!ExpressionTool.isAllowedInUpdatingContext(act)) {
+ if (updating) {
+ XPathException err = new XPathException(
+ "If any branch of a conditional is an updating expression, then all must be updating expressions (or vacuous)",
+ "XUST0001");
+ err.setLocator(act);
+ throw err;
+ }
+ nonUpdating = true;
+ }
+ if (act.isUpdatingExpression()) {
+ if (nonUpdating) {
+ XPathException err = new XPathException(
+ "If any branch of a conditional is an updating expression, then all must be updating expressions (or vacuous)",
+ "XUST0001");
+ err.setLocator(act);
+ throw err;
+ }
+ updating = true;
+ }
+ }
+ }
+
+ /**
+ * Determine whether this is an updating expression as defined in the XQuery update specification
+ *
+ * @return true if this is an updating expression
+ */
+
+ public boolean isUpdatingExpression() {
+ for (Expression action : actions) {
+ if (action.isUpdatingExpression()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether this is a vacuous expression as defined in the XQuery update specification
+ * @return true if this expression is vacuous
+ */
+
+ public boolean isVacuousExpression() {
+ // The Choose is vacuous if all branches are vacuous
+ for (Expression action : actions) {
+ if (!action.isVacuousExpression()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is prefered. For instructions this is the process() method.
+ */
+
+ public int getImplementationMethod() {
+ int m = Expression.PROCESS_METHOD | Expression.ITERATE_METHOD | Expression.WATCH_METHOD;
+ if (!Cardinality.allowsMany(getCardinality())) {
+ m |= Expression.EVALUATE_METHOD;
+ }
+ return m;
+ }
+
+ /**
+ * Mark tail-recursive calls on functions. For most expressions, this does nothing.
+ *
+ * @return 0 if no tail call was found; 1 if a tail call on a different function was found;
+ * 2 if a tail recursive call was found and if this call accounts for the whole of the value.
+ */
+
+ public int markTailFunctionCalls(StructuredQName qName, int arity) {
+ int result = UserFunctionCall.NOT_TAIL_CALL;
+ for (Expression action : actions) {
+ result = Math.max(result, action.markTailFunctionCalls(qName, arity));
+ }
+ return result;
+ }
+
+ /**
+ * Get the item type of the items returned by evaluating this instruction
+ * @return the static item type of the instruction
+ * @param th Type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ ItemType type = actions[0].getItemType(th);
+ for (int i=1; i<actions.length; i++) {
+ type = Type.getCommonSuperType(type, actions[i].getItemType(th), th);
+ }
+ return type;
+ }
+
+ /**
+ * Compute the cardinality of the sequence returned by evaluating this instruction
+ * @return the static cardinality
+ */
+
+ public int computeCardinality() {
+ int card = 0;
+ boolean includesTrue = false;
+ for (int i=0; i<actions.length; i++) {
+ card = Cardinality.union(card, actions[i].getCardinality());
+ if (Literal.isConstantBoolean(conditions[i], true)) {
+ includesTrue = true;
+ }
+ }
+ if (!includesTrue) {
+ // we may drop off the end and return an empty sequence (typical for xsl:if)
+ card = Cardinality.union(card, StaticProperty.ALLOWS_ZERO);
+ }
+ return card;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+
+ public int computeSpecialProperties() {
+ // The special properties of a conditional are those which are common to every branch of the conditional
+ int props = actions[0].getSpecialProperties();
+ for (int i=1; i<actions.length; i++) {
+ props &= actions[i].getSpecialProperties();
+ }
+ return props;
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true if any of the "actions" creates new nodes.
+ * (Nodes created by the conditions can't contribute to the result).
+ */
+
+ public final boolean createsNewNodes() {
+ for (Expression action : actions) {
+ int props = action.getSpecialProperties();
+ if ((props & StaticProperty.NON_CREATIVE) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+
+ int max = W3C_MOTIONLESS;
+ for (Expression a : actions) {
+ int s = a.getStreamability(syntacticContext, allowExtensions, reasons);
+ if (s > max) {
+ max = s;
+ }
+ }
+
+ if (max == W3C_MOTIONLESS) {
+ for (Expression a : conditions) {
+ int s = a.getStreamability(INSPECTION_CONTEXT, allowExtensions, reasons);
+ if (s != W3C_MOTIONLESS) {
+ if (max != W3C_MOTIONLESS) {
+ reasons.add("More than one test condition is not motionless");
+ } else {
+ max = s;
+ }
+ }
+ }
+ } else {
+ for (Expression a : conditions) {
+ int s = a.getStreamability(INSPECTION_CONTEXT, allowExtensions, reasons);
+ if (s != W3C_MOTIONLESS) {
+ reasons.add("Both a test condition and a branch of the conditional are non-motionless");
+ return W3C_FREE_RANGING;
+ }
+ }
+ }
+
+ if (max == W3C_GROUP_CONSUMING) {
+ reasons.add("Saxon cannot handle streaming of a conditional expression in the body of xsl:for-each-group");
+ return W3C_FREE_RANGING;
+ }
+
+ return max;
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public ChooseAdjunct getStreamingAdjunct() {
+ return new ChooseAdjunct();
+ }
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ if (!ExpressionInverter.consumesStream(getConditions()[0])) {
+ Expression[] actions = getActions();
+ Pattern[] patterns = new Pattern[actions.length];
+ for (int i=0; i<actions.length; i++) {
+ patterns[i] = actions[i].toStreamingPattern(config, reasonForFailure);
+ }
+ if (!reasonForFailure.isEmpty()) {
+ return null;
+ }
+ return new ConditionalPattern(getConditions(), patterns);
+ } else {
+ return super.toStreamingPattern(config, reasonForFailure);
+ }
+ }
+
+//#endif
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new Iterator<Expression>() {
+ boolean doingConditions = true;
+ int index = 0;
+ public boolean hasNext() {
+ return doingConditions || index<actions.length;
+ }
+
+ public Expression next() {
+ if (doingConditions) {
+ if (index < conditions.length) {
+ return conditions[index++];
+ } else {
+ doingConditions = false;
+ index = 0;
+ return actions[index++];
+ }
+ } else {
+ if (index < actions.length) {
+ return actions[index++];
+ } else {
+ return null;
+ }
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new Iterator<SubExpressionInfo>() {
+ boolean doingConditions = true;
+ int index = 0;
+ public boolean hasNext() {
+ return doingConditions || index<actions.length;
+ }
+
+ public SubExpressionInfo next() {
+ if (doingConditions) {
+ if (index < conditions.length) {
+ return new SubExpressionInfo(conditions[index++], true, false, Expression.INSPECTION_CONTEXT);
+ } else {
+ doingConditions = false;
+ index = 0;
+ return new SubExpressionInfo(actions[index++], true, false, Expression.INHERITED_CONTEXT);
+ }
+ } else {
+ if (index < actions.length) {
+ return new SubExpressionInfo(actions[index++], true, false, Expression.INHERITED_CONTEXT);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ for (int i=0; i<conditions.length; i++) {
+ if (conditions[i] == original) {
+ conditions[i] = replacement;
+ found = true;
+ }
+ }
+ for (int i=0; i<actions.length; i++) {
+ if (actions[i] == original) {
+ actions[i] = replacement;
+ found = true;
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ // xsl:when acts as a guard: expressions inside the when mustn't be evaluated if the when is false,
+ // and conditions after the first mustn't be evaluated if a previous condition is true. So we
+ // don't pass all promotion offers on
+ if (offer.action == PromotionOffer.UNORDERED ||
+ offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES ||
+ offer.action == PromotionOffer.REPLACE_CURRENT ||
+ offer.action == PromotionOffer.EXTRACT_GLOBAL_VARIABLES) {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i] = doPromotion(conditions[i], offer);
+ }
+ for (int i=0; i<actions.length; i++) {
+ actions[i] = doPromotion(actions[i], offer);
+ }
+ } else {
+ // in other cases, only the first xsl:when condition is promoted
+ conditions[0] = doPromotion(conditions[0], offer);
+ }
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ for (Expression action : actions) {
+ action.checkPermittedContents(parentType, env, whole);
+ }
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the set of PathMap nodes to which the paths from this expression should be appended
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ // expressions used in a condition contribute paths, but these do not contribute to the result
+ for (Expression condition : conditions) {
+ condition.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ PathMap.PathMapNodeSet result = new PathMap.PathMapNodeSet();
+ for (Expression action : actions) {
+ PathMap.PathMapNodeSet temp = action.addToPathMap(pathMap, pathMapNodeSet);
+ result.addNodeSet(temp);
+ }
+ return result;
+ }
+
+ /**
+ * The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form, but there is no guarantee that the syntax will actually be true XPath.
+ * In the case of XSLT instructions, the toString() method gives an abstracted view of the syntax
+ * @return a representation of the expression as a string
+ */
+
+ public String toString() {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ sb.append("if (");
+ for (int i=0; i<conditions.length; i++) {
+ sb.append(conditions[i].toString());
+ sb.append(") then (");
+ sb.append(actions[i].toString());
+ if (i == conditions.length - 1) {
+ sb.append(")");
+ } else {
+ sb.append(") else if (");
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("choose");
+ for (int i=0; i<conditions.length; i++) {
+ out.startSubsidiaryElement("when");
+ conditions[i].explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("then");
+ actions[i].explain(out);
+ out.endSubsidiaryElement();
+ }
+ out.endElement();
+ }
+
+ /**
+ * Process this instruction, that is, choose an xsl:when or xsl:otherwise child
+ * and process it.
+ * @param context the dynamic context of this transformation
+ * @throws XPathException if any non-recoverable dynamic error occurs
+ * @return a TailCall, if the chosen branch ends with a call of call-template or
+ * apply-templates. It is the caller's responsibility to execute such a TailCall.
+ * If there is no TailCall, returns null.
+ */
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ final boolean b;
+ try {
+ b = conditions[i].effectiveBooleanValue(context);
+ } catch (XPathException e) {
+ e.maybeSetLocation(conditions[i]);
+ throw e;
+ }
+ if (b) {
+ if (actions[i] instanceof TailCallReturner) {
+ return ((TailCallReturner)actions[i]).processLeavingTail(context);
+ } else {
+ actions[i].process(context);
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Evaluate an expression as a single item. This always returns either a single Item or
+ * null (denoting the empty sequence). No conversion is done. This method should not be
+ * used unless the static type of the expression is a subtype of "item" or "item?": that is,
+ * it should not be called if the expression may return a sequence. There is no guarantee that
+ * this condition will be detected.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @exception XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @return the node or atomic value that results from evaluating the
+ * expression; or null to indicate that the result is an empty
+ * sequence
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ final boolean b;
+ try {
+ b = conditions[i].effectiveBooleanValue(context);
+ } catch (XPathException e) {
+ e.maybeSetLocation(conditions[i]);
+ throw e;
+ }
+ if (b) {
+ return actions[i].evaluateItem(context);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation relies on the process() method: it
+ * "pushes" the results of the instruction to a sequence in memory, and then
+ * iterates over this in-memory sequence.
+ *
+ * In principle instructions should implement a pipelined iterate() method that
+ * avoids the overhead of intermediate storage.
+ *
+ * @exception XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ final boolean b;
+ try {
+ b = conditions[i].effectiveBooleanValue(context);
+ } catch (XPathException e) {
+ e.maybeSetLocation(conditions[i]);
+ throw e;
+ }
+ if (b) {
+ return (actions[i]).iterate(context);
+ }
+ }
+ return EmptyIterator.emptyIterator();
+ }
+
+
+ /**
+ * Deliver the result of the expression as a sequence of events.
+ * <p/>
+ * <p>The events (of class {@link net.sf.saxon.evpull.PullEvent}) are either complete
+ * items, or one of startElement, endElement, startDocument, or endDocument, known
+ * as semi-nodes. The stream of events may also include a nested EventIterator.
+ * If a start-end pair exists in the sequence, then the events between
+ * this pair represent the content of the document or element. The content sequence will
+ * have been processed to the extent that any attribute and namespace nodes in the
+ * content sequence will have been merged into the startElement event. Namespace fixup
+ * will have been performed: that is, unique prefixes will have been allocated to element
+ * and attribute nodes, and all namespaces will be declared by means of a namespace node
+ * in the startElement event or in an outer startElement forming part of the sequence.
+ * However, duplicate namespaces may appear in the sequence.</p>
+ * <p>The content of an element or document may include adjacent or zero-length text nodes,
+ * atomic values, and nodes represented as nodes rather than broken down into events.</p>
+ *
+ * @param context The dynamic evaluation context
+ * @return the result of the expression as an iterator over a sequence of PullEvent objects
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during expression evaluation
+ */
+
+ public EventIterator iterateEvents(XPathContext context) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ final boolean b;
+ try {
+ b = conditions[i].effectiveBooleanValue(context);
+ } catch (XPathException e) {
+ e.maybeSetLocation(conditions[i]);
+ throw e;
+ }
+ if (b) {
+ return (actions[i]).iterateEvents(context);
+ }
+ }
+ return EmptyEventIterator.getInstance();
+ }
+
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ final boolean b;
+ try {
+ b = conditions[i].effectiveBooleanValue(context);
+ } catch (XPathException e) {
+ e.maybeSetLocation(conditions[i]);
+ throw e;
+ }
+ if (b) {
+ actions[i].evaluatePendingUpdates(context, pul);
+ return;
+ }
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Choose expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ChooseCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/instruct/Comment.java b/sf/saxon/expr/instruct/Comment.java
new file mode 100644
index 0000000..0f22720
--- /dev/null
+++ b/sf/saxon/expr/instruct/Comment.java
@@ -0,0 +1,178 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.CommentCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+
+/**
+ * An instruction representing an xsl:comment element in the stylesheet.
+ */
+
+public final class Comment extends SimpleNodeConstructor {
+
+ /**
+ * Construct the instruction
+ */
+
+ public Comment() {
+ }
+
+ /**
+ * Get the instruction name, for diagnostics and tracing
+ * return the string "xsl:comment"
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_COMMENT;
+ }
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return NodeKindTest.COMMENT;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Comment exp = new Comment();
+ exp.setSelect(select.copy(), getExecutable().getConfiguration());
+ return exp;
+ }
+
+ public void localTypeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ // Do early checking of content if known statically
+
+ if (select instanceof Literal) {
+ String s = ((Literal) select).getValue().getStringValue();
+ String s2 = checkContent(s, visitor.getStaticContext().makeEarlyEvaluationContext());
+ if (!s2.equals(s)) {
+ setSelect(new StringLiteral(s2), visitor.getConfiguration());
+ }
+ }
+ }
+
+
+ /**
+ * Process the value of the node, to create the new node.
+ *
+ * @param value the string value of the new node
+ * @param context the dynamic evaluation context
+ * @throws XPathException
+ */
+
+ public void processValue(CharSequence value, XPathContext context) throws XPathException {
+ //String comment = expandChildren(context).toString();
+ String comment = checkContent(value.toString(), context);
+ SequenceReceiver out = context.getReceiver();
+ out.comment(comment, locationId, 0);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Comment expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CommentCompiler();
+ }
+//#endif
+
+ /**
+ * Check the content of the node, and adjust it if necessary
+ *
+ * @param comment the supplied content
+ * @param context the dynamic context
+ * @return the original content, unless adjustments are needed
+ * @throws XPathException if the content is invalid
+ */
+
+ @Override
+ protected String checkContent(String comment, XPathContext context) throws XPathException {
+ if (isXSLT()) {
+ return checkContentXSLT(comment);
+ } else {
+ try {
+ return checkContentXQuery(comment);
+ } catch (XPathException err) {
+ err.setXPathContext(context);
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ }
+
+ /**
+ * Check the content of the comment according to the XSLT rules (which fix it if it is wrong)
+ * @param comment the proposed text of the comment
+ * @return the adjusted text of the comment
+ */
+
+ public static String checkContentXSLT(String comment) {
+ int hh;
+ while ((hh = comment.indexOf("--")) >= 0) {
+ comment = comment.substring(0, hh + 1) + ' ' + comment.substring(hh + 1);
+ }
+ if (comment.length() > 0 && comment.charAt(comment.length() - 1) == '-') {
+ comment = comment + ' ';
+ }
+ return comment;
+ }
+
+ /**
+ * Check the content of the comment according to the XQuery rules (which throw an error if it is wrong)
+ * @param comment the proposed text of the comment
+ * @return the adjusted text of the comment (always the same as the original if there is no error)
+ * @throws net.sf.saxon.trans.XPathException if the content is invalid
+ */
+
+ public static String checkContentXQuery(String comment) throws XPathException {
+ if (comment.contains("--")) {
+ throw new XPathException("Invalid characters (--) in comment", "XQDY0072");
+ }
+ if (comment.length() > 0 && comment.charAt(comment.length() - 1) == '-') {
+ throw new XPathException("Comment cannot end in '-'", "XQDY0072");
+ }
+ return comment;
+ }
+
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("comment");
+ getContentExpression().explain(out);
+ out.endElement();
+ }
+
+}
diff --git a/sf/saxon/expr/instruct/ComputedAttribute.java b/sf/saxon/expr/instruct/ComputedAttribute.java
new file mode 100644
index 0000000..627b784
--- /dev/null
+++ b/sf/saxon/expr/instruct/ComputedAttribute.java
@@ -0,0 +1,666 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ComputedAttributeCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StandardURIChecker;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * An instruction derived from an xsl:attribute element in stylesheet, or from
+ * an attribute constructor in XQuery, in cases where the attribute name is not known
+ * statically
+ */
+
+public final class ComputedAttribute extends AttributeCreator {
+
+ private Expression attributeName;
+ private Expression namespace = null;
+ private Expression onEmpty = null;
+ private NamespaceResolver nsContext;
+ private boolean allowNameAsQName;
+
+ /**
+ * Construct an Attribute instruction
+ *
+ * @param attributeName An expression to calculate the attribute name
+ * @param namespace An expression to calculate the attribute namespace
+ * @param nsContext a NamespaceContext object containing the static namespace context of the
+ * stylesheet instruction
+ * @param validationAction e.g. validation=strict, lax, strip, preserve
+ * @param schemaType Type against which the attribute must be validated. This must not be a namespace-sensitive
+ * type; it is the caller's responsibility to check this.
+ * @param allowNameAsQName true if the attributeName expression is allowed to evaluate to a value
+ * of type xs:QName (true in XQuery, false in XSLT)
+ */
+
+ public ComputedAttribute(Expression attributeName,
+ Expression namespace,
+ NamespaceResolver nsContext,
+ int validationAction,
+ SimpleType schemaType,
+ boolean allowNameAsQName) {
+ this.attributeName = attributeName;
+ this.namespace = namespace;
+ this.nsContext = nsContext;
+ setSchemaType(schemaType);
+ setValidationAction(validationAction);
+ setOptions(0);
+ this.allowNameAsQName = allowNameAsQName;
+ adoptChildExpression(attributeName);
+ adoptChildExpression(namespace);
+ }
+
+ /**
+ * Set the on-empty expression, which defines the value to be returned if the attribute would otherwise
+ * be empty
+ *
+ * @param onEmpty the expression to be evaluated if the attribute would otherwise be empty
+ */
+
+ public void setOnEmpty(Expression onEmpty) {
+ this.onEmpty = onEmpty;
+ }
+
+ /**
+ * Get the on-empty expression, which defines the value to be returned if the attribute would otherwise
+ * be empty
+ * @return the on-empty expression if there is one, or null otherwise
+ */
+
+ public Expression getOnEmpty() {
+ return onEmpty;
+ }
+
+ /**
+ * Indicate that two attributes with the same name are not acceptable.
+ * (This option is set in XQuery, but not in XSLT)
+ */
+
+ public void setRejectDuplicates() {
+ setOptions(getOptions() | ReceiverOptions.REJECT_DUPLICATES);
+ }
+
+ /**
+ * Get the name of this instruction
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_ATTRIBUTE;
+ }
+
+ /**
+ * Get the expression used to compute the name of the attribute
+ *
+ * @return the expression used to compute the name of the attribute
+ */
+
+ public Expression getNameExpression() {
+ return attributeName;
+ }
+
+ /**
+ * Get the expression used to compute the namespace part of the name of the attribute
+ *
+ * @return the expression used to compute the namespace part of the name of the attribute
+ */
+
+ public Expression getNamespaceExpression() {
+ return namespace;
+ }
+
+ /**
+ * Get the namespace resolver used to resolve any prefix in the name of the attribute
+ *
+ * @return the namespace resolver if one has been saved; or null otherwise
+ */
+
+ public NamespaceResolver getNamespaceResolver() {
+ return nsContext;
+ }
+
+ /**
+ * Get the static type of this expression
+ *
+ * @param th the type hierarchy cache
+ * @return the static type of the item returned by this expression
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (onEmpty == null || Literal.isEmptySequence(onEmpty)) {
+ return NodeKindTest.ATTRIBUTE;
+ } else {
+ return Type.getCommonSuperType(NodeKindTest.ATTRIBUTE, onEmpty.getItemType(th), th);
+ }
+ }
+
+ /**
+ * Get the static cardinality of this expression
+ *
+ * @return the static cardinality (exactly one)
+ */
+
+ public int getCardinality() {
+ if (onEmpty == null) {
+ return StaticProperty.EXACTLY_ONE;
+ } else if (Literal.isEmptySequence(onEmpty)) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ } else {
+ return Cardinality.union(StaticProperty.EXACTLY_ONE, onEmpty.getCardinality());
+ }
+ }
+
+ /**
+ * Ask whether it is allowed for the name to be evaluted as an xs:QName instance (true in XQuery)
+ *
+ * @return the boolean if name is allowed as a QName
+ */
+ public boolean isAllowNameAsQName() {
+ return allowNameAsQName;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+
+ public int computeSpecialProperties() {
+ return super.computeSpecialProperties() |
+ StaticProperty.SINGLE_DOCUMENT_NODESET;
+ }
+
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ attributeName = visitor.simplify(attributeName);
+ namespace = visitor.simplify(namespace);
+ onEmpty = visitor.simplify(onEmpty);
+ return super.simplify(visitor);
+ }
+
+ public void localTypeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ StaticContext env = visitor.getStaticContext();
+ attributeName = visitor.typeCheck(attributeName, contextItemType);
+ onEmpty = visitor.typeCheck(onEmpty, contextItemType);
+ adoptChildExpression(attributeName);
+
+ RoleLocator role = new RoleLocator(RoleLocator.INSTRUCTION, "attribute/name", 0);
+ //role.setSourceLocator(this);
+
+ if (allowNameAsQName) {
+ // Can only happen in XQuery
+ attributeName = TypeChecker.staticTypeCheck(attributeName,
+ SequenceType.SINGLE_ATOMIC, false, role, visitor);
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType nameItemType = attributeName.getItemType(th);
+ boolean maybeString = th.relationship(nameItemType, BuiltInAtomicType.STRING) != TypeHierarchy.DISJOINT ||
+ th.relationship(nameItemType, BuiltInAtomicType.UNTYPED_ATOMIC) != TypeHierarchy.DISJOINT;
+ boolean maybeQName = th.relationship(nameItemType, BuiltInAtomicType.QNAME) != TypeHierarchy.DISJOINT;
+ if (!(maybeString || maybeQName)) {
+ XPathException err = new XPathException(
+ "The attribute name must be either an xs:string, an xs:QName, or untyped atomic");
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ } else {
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (!th.isSubType(attributeName.getItemType(th), BuiltInAtomicType.STRING)) {
+ attributeName = SystemFunctionCall.makeSystemFunction("string", new Expression[]{attributeName});
+ }
+ }
+
+ if (namespace != null) {
+ visitor.typeCheck(namespace, contextItemType);
+ adoptChildExpression(namespace);
+
+ role = new RoleLocator(RoleLocator.INSTRUCTION, "attribute/namespace", 0);
+ //role.setSourceLocator(this);
+ namespace = TypeChecker.staticTypeCheck(
+ namespace, SequenceType.SINGLE_STRING, false, role, visitor);
+ }
+
+ if (Literal.isAtomic(attributeName)) {
+ // Check we have a valid lexical QName, whose prefix is in scope where necessary
+ try {
+ AtomicValue val = (AtomicValue) ((Literal) attributeName).getValue();
+ if (val instanceof StringValue) {
+ String[] parts = env.getConfiguration().getNameChecker().checkQNameParts(val.getStringValueCS());
+ if (namespace == null) {
+ String uri = getNamespaceResolver().getURIForPrefix(parts[0], true);
+ if (uri == null) {
+ XPathException se = new XPathException("Prefix " + parts[0] + " has not been declared");
+ if (isXSLT()) {
+ se.setErrorCode("XTDE0860");
+ se.setIsStaticError(true);
+ throw se;
+ } else {
+ se.setErrorCode("XQDY0074");
+ se.setIsStaticError(false);
+ throw se;
+ }
+ }
+ namespace = new StringLiteral(uri);
+ }
+ }
+ } catch (XPathException e) {
+ if (e.getErrorCodeQName() == null || e.getErrorCodeLocalPart().equals("FORG0001")) {
+ e.setErrorCode(isXSLT() ? "XTDE0850" : "XQDY0074");
+ }
+ e.maybeSetLocation(this);
+ e.setIsStaticError(true);
+ throw e;
+ }
+ }
+ }
+
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ attributeName = visitor.optimize(attributeName, contextItemType);
+ namespace = visitor.optimize(namespace, contextItemType);
+ onEmpty = visitor.optimize(onEmpty, contextItemType);
+ Expression exp = super.optimize(visitor, contextItemType);
+ if (exp != this) {
+ return exp;
+ }
+ // If the name is known statically, use a FixedAttribute instead
+ if (attributeName instanceof Literal && (namespace == null || namespace instanceof Literal) && onEmpty == null) {
+ XPathContext context = visitor.getStaticContext().makeEarlyEvaluationContext();
+ NodeName nc = evaluateNodeName(context);
+ FixedAttribute fa = new FixedAttribute(nc, getValidationAction(), getSchemaType());
+ fa.setSelect(getContentExpression(), visitor.getConfiguration());
+ return fa;
+ }
+ return this;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ComputedAttribute exp = new ComputedAttribute(
+ attributeName == null ? null : attributeName.copy(),
+ namespace == null ? null : namespace.copy(),
+ nsContext, getValidationAction(), getSchemaType(), allowNameAsQName);
+ exp.setOnEmpty(onEmpty);
+ exp.setSelect(select.copy(), getExecutable().getConfiguration());
+ return exp;
+ }
+
+ /**
+ * Get the subexpressions of this expression
+ *
+ * @return an iterator over the subexpressions
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ ArrayList<Expression> list = new ArrayList<Expression>(4);
+ list.add(select);
+ list.add(attributeName);
+ if (namespace != null) {
+ list.add(namespace);
+ }
+ if (onEmpty != null) {
+ list.add(onEmpty);
+ }
+ return list.iterator();
+ }
+
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ ArrayList<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>(4);
+ list.add(new SubExpressionInfo(select, true, false, NODE_VALUE_CONTEXT));
+ list.add(new SubExpressionInfo(attributeName, true, false, NODE_VALUE_CONTEXT));
+ if (namespace != null) {
+ list.add(new SubExpressionInfo(namespace, true, false, NODE_VALUE_CONTEXT));
+ }
+ if (onEmpty != null) {
+ list.add(new SubExpressionInfo(onEmpty, true, false, NODE_VALUE_CONTEXT));
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (attributeName == original) {
+ attributeName = replacement;
+ found = true;
+ }
+ if (namespace == original) {
+ namespace = replacement;
+ found = true;
+ }
+ if (onEmpty == original) {
+ onEmpty = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Offer promotion for subexpressions. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @throws XPathException if any error is detected
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ attributeName = doPromotion(attributeName, offer);
+ if (namespace != null) {
+ namespace = doPromotion(namespace, offer);
+ }
+ if (onEmpty != null) {
+ onEmpty = doPromotion(onEmpty, offer);
+ }
+ super.promoteInst(offer);
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ if (parentType instanceof SimpleType) {
+ String msg = "Attributes are not permitted here: ";
+ if (parentType.isAnonymousType()) {
+ msg += "the containing element is defined to have a simple type";
+ } else {
+ msg += "the containing element is of simple type " + parentType.getDescription();
+ }
+ XPathException err = new XPathException(msg);
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ }
+
+
+ /**
+ * Determine the name to be used for the attribute, as an integer name code
+ *
+ * @param context Dynamic evaluation context
+ * @return the integer name code for the attribute name
+ * @throws XPathException
+ */
+
+ public NodeName evaluateNodeName(XPathContext context) throws XPathException {
+ NamePool pool = context.getNamePool();
+
+ Item nameValue = attributeName.evaluateItem(context);
+
+ String prefix;
+ String localName;
+ String uri = null;
+
+ if (nameValue instanceof StringValue) {
+ // this will always be the case in XSLT
+ CharSequence rawName = nameValue.getStringValueCS();
+ rawName = Whitespace.trimWhitespace(rawName); // required in XSLT; possibly wrong in XQuery
+ try {
+ String[] parts = context.getConfiguration().getNameChecker().getQNameParts(rawName);
+ prefix = parts[0];
+ localName = parts[1];
+ } catch (QNameException err) {
+ XPathException err1 = new XPathException("Invalid attribute name: " + rawName, this);
+ err1.setErrorCode((isXSLT() ? "XTDE0850" : "XQDY0074"));
+ err1.setXPathContext(context);
+ throw dynamicError(this, err1, context);
+ }
+ if (rawName.toString().equals("xmlns")) {
+ if (namespace == null) {
+ XPathException err = new XPathException("Invalid attribute name: " + rawName, this);
+ err.setErrorCode((isXSLT() ? "XTDE0855" : "XQDY0044"));
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+ }
+ if (prefix.equals("xmlns")) {
+ if (namespace == null) {
+ XPathException err = new XPathException("Invalid attribute name: " + rawName, this);
+ err.setErrorCode((isXSLT() ? "XTDE0860" : "XQDY0044"));
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ } else {
+ // ignore the prefix "xmlns"
+ prefix = "";
+ }
+ }
+
+ } else if (nameValue instanceof QNameValue && allowNameAsQName) {
+ // this is allowed in XQuery
+ localName = ((QNameValue) nameValue).getLocalName();
+ uri = ((QNameValue) nameValue).getNamespaceURI();
+ if (localName.equals("xmlns") && uri.length() == 0) {
+ XPathException err = new XPathException("Invalid attribute name: xmlns", this);
+ err.setErrorCode("XQDY0044");
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ if (uri.length() == 0) {
+ prefix = "";
+ } else {
+ prefix = ((QNameValue) nameValue).getPrefix();
+ if (prefix.length() == 0) {
+ prefix = pool.suggestPrefixForURI(uri);
+ if (prefix == null) {
+ prefix = "ns0";
+ // If the prefix is a duplicate, a different one will be substituted
+ }
+ }
+ if (uri.equals(NamespaceConstant.XML) != "xml".equals(prefix)) {
+ String message;
+ if ("xml".equals(prefix)) {
+ message = "When the prefix is 'xml', the namespace URI must be " + NamespaceConstant.XML;
+ } else {
+ message = "When the namespace URI is " + NamespaceConstant.XML + ", the prefix must be 'xml'";
+ }
+ XPathException err = new XPathException(message, this);
+ err.setErrorCode((isXSLT() ? "XTDE0835" : "XQDY0044"));
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+ }
+
+ if ("xmlns".equals(prefix)) {
+ XPathException err = new XPathException("Invalid attribute namespace: http://www.w3.org/2000/xmlns/", this);
+ err.setErrorCode("XQDY0044");
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ } else {
+ XPathException err = new XPathException("Attribute name must be either a string or a QName", this);
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ if (namespace == null && uri == null) {
+ if (prefix.length() == 0) {
+ uri = "";
+ } else {
+ uri = nsContext.getURIForPrefix(prefix, false);
+ if (uri == null) {
+ XPathException err = new XPathException("Undeclared prefix in attribute name: " + prefix, this);
+ err.setErrorCode((isXSLT() ? "XTDE0860" : "XQDY0074"));
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+ }
+
+ } else {
+ if (uri == null) {
+ // generate a name using the supplied namespace URI
+ if (namespace instanceof StringLiteral) {
+ uri = ((StringLiteral) namespace).getStringValue();
+ } else {
+ uri = namespace.evaluateAsString(context).toString();
+ if (!StandardURIChecker.getInstance().isValidURI(uri)) {
+ XPathException de = new XPathException("The value of the namespace attribute must be a valid URI");
+ de.setErrorCode("XTDE0865");
+ de.setXPathContext(context);
+ de.setLocator(this);
+ throw de;
+ }
+ }
+ }
+ if (uri.length() == 0) {
+ // there is a special rule for this case in the XSLT specification;
+ // we force the attribute to go in the null namespace
+ prefix = "";
+
+ } else {
+ // if a suggested prefix is given, use it; otherwise try to find a prefix
+ // associated with this URI; if all else fails, invent one.
+ if (prefix.length() == 0) {
+ prefix = pool.suggestPrefixForURI(uri);
+ if (prefix == null) {
+ prefix = "ns0";
+ // this will be replaced later if it is already in use
+ }
+ }
+ }
+ }
+
+ if (uri.equals(NamespaceConstant.XMLNS)) {
+ XPathException err = new XPathException("Cannot create attribute in namespace " + uri, this);
+ err.setErrorCode((isXSLT() ? "XTDE0835" : "XQDY0044"));
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ return new FingerprintedQName(prefix, uri, localName);
+ }
+
+ /**
+ * Process the value of the node, to create the new node.
+ *
+ * @param value the string value of the new node
+ * @param context the dynamic evaluation context
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+ @Override
+ public void processValue(CharSequence value, XPathContext context) throws XPathException {
+ if (value.length() == 0 && onEmpty != null) {
+ onEmpty.process(context);
+ } else {
+ super.processValue(value, context);
+ }
+ }
+
+ @Override
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ Item node = super.evaluateItem(context);
+ if (onEmpty != null && node.getStringValue().length() == 0) {
+ return onEmpty.evaluateItem(context);
+ } else {
+ validateOrphanAttribute((Orphan)node, context);
+ return node;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ComputedAttribute expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ComputedAttributeCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("computedAttribute");
+ out.emitAttribute("validation", Validation.toString(getValidationAction()));
+ SimpleType type = getSchemaType();
+ if (type != null) {
+ out.emitAttribute("type", type.getDescription());
+ }
+ out.startSubsidiaryElement("name");
+ attributeName.explain(out);
+ out.endSubsidiaryElement();
+ if (namespace != null) {
+ out.startSubsidiaryElement("namespace");
+ namespace.explain(out);
+ out.endSubsidiaryElement();
+ }
+ if (onEmpty != null) {
+ out.startSubsidiaryElement("on-empty");
+ onEmpty.explain(out);
+ out.endSubsidiaryElement();
+ }
+ out.startSubsidiaryElement("select");
+ getContentExpression().explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/ComputedElement.java b/sf/saxon/expr/instruct/ComputedElement.java
new file mode 100644
index 0000000..7b8fa03
--- /dev/null
+++ b/sf/saxon/expr/instruct/ComputedElement.java
@@ -0,0 +1,558 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ComputedElementCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StandardURIChecker;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.ContentTypeTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * An instruction representing an xsl:element element in an XSLT stylesheet,
+ * or a computed element constructor in XQuery. (In both cases, if the element name
+ * is expressed as a compile-time expression, then a FixedElement instruction
+ * is used instead.)
+ * @see FixedElement
+ */
+
+public class ComputedElement extends ElementCreator {
+
+ private Expression elementName;
+ private Expression namespace = null;
+ private NamespaceResolver nsContext;
+ private boolean allowNameAsQName;
+ private ItemType itemType;
+
+ /**
+ * Create an instruction that creates a new element node
+ *
+ * @param elementName Expression that evaluates to produce the name of the
+ * element node as a lexical QName
+ * @param namespace Expression that evaluates to produce the namespace URI of
+ * the element node. Set to null if the namespace is to be deduced from the prefix
+ * of the elementName.
+ * @param nsContext Saved copy of the static namespace context for the instruction.
+ * Can be set to null if namespace is supplied. This namespace context
+ * must resolve the null prefix correctly, based on the different rules for
+ * XSLT and XQuery.
+ //* @param defaultNamespace Default namespace to be used if no namespace is supplied and the
+ //* computed element is a string with no prefix.
+ * @param validation Required validation mode (e.g. STRICT, LAX, SKIP)
+ * @param inheritNamespaces true if child elements automatically inherit the namespaces of their parent
+ * @param schemaType The required schema type for the content
+ * @param allowQName True if the elementName expression is allowed to return a QNameValue; false if
+ * it must return a string, and is cast to a string if necessary
+ * (that is, true in XQuery, false in XSLT).
+ */
+ public ComputedElement(Expression elementName,
+ Expression namespace,
+ NamespaceResolver nsContext,
+ //String defaultNamespace,
+ SchemaType schemaType,
+ int validation,
+ boolean inheritNamespaces,
+ boolean allowQName) {
+ this.elementName = elementName;
+ this.namespace = namespace;
+ this.nsContext = nsContext;
+ setValidationAction(validation, schemaType);
+ preservingTypes = schemaType == null && validation == Validation.PRESERVE;
+ this.inheritNamespaces = inheritNamespaces;
+ allowNameAsQName = allowQName;
+ adoptChildExpression(elementName);
+ adoptChildExpression(namespace);
+ }
+
+ /**
+ * Get the expression used to compute the element name
+ * @return the expression used to compute the element name
+ */
+
+ public Expression getNameExpression() {
+ return elementName;
+ }
+
+ /**
+ * Get the expression used to compute the namespace URI
+ * @return the expression used to compute the namespace URI
+ */
+
+ public Expression getNamespaceExpression() {
+ return namespace;
+ }
+
+ /**
+ * Get the namespace resolver that provides the namespace bindings defined in the static context
+ * @return the namespace resolver
+ */
+
+ public NamespaceResolver getNamespaceResolver() {
+ return nsContext;
+ }
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ elementName = visitor.simplify(elementName);
+ namespace = visitor.simplify(namespace);
+ Configuration config = visitor.getConfiguration();
+ setLazyConstruction(config.getBooleanProperty(FeatureKeys.LAZY_CONSTRUCTION_MODE));
+ boolean schemaAware = visitor.getExecutable().isSchemaAware();
+ preservingTypes |= !schemaAware;
+
+ final SchemaType schemaType = getSchemaType();
+ if (schemaType != null) {
+ itemType = new ContentTypeTest(Type.ELEMENT, schemaType, config, false);
+ schemaType.analyzeContentExpression(content, Type.ELEMENT, visitor.getStaticContext());
+ } else if (getValidationAction() == Validation.STRIP || !schemaAware) {
+ itemType = new ContentTypeTest(Type.ELEMENT, Untyped.getInstance(), config, false);
+ } else {
+ // paradoxically, we know less about the type if validation="strict" is specified!
+ // We know that it won't be untyped, but we have no way of representing that.
+ itemType = NodeKindTest.ELEMENT;
+ }
+ return super.simplify(visitor);
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ elementName = visitor.typeCheck(elementName, contextItemType);
+ //adoptChildExpression(elementName);
+ RoleLocator role = new RoleLocator(RoleLocator.INSTRUCTION, "element/name", 0);
+ //role.setSourceLocator(this);
+ if (allowNameAsQName) {
+ // Can only happen in XQuery
+ elementName = TypeChecker.staticTypeCheck(elementName,
+ SequenceType.SINGLE_ATOMIC, false, role, visitor);
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType supplied = elementName.getItemType(th);
+ if (th.relationship(supplied, BuiltInAtomicType.STRING) == TypeHierarchy.DISJOINT &&
+ th.relationship(supplied, BuiltInAtomicType.UNTYPED_ATOMIC) == TypeHierarchy.DISJOINT &&
+ th.relationship(supplied, BuiltInAtomicType.QNAME) == TypeHierarchy.DISJOINT) {
+ XPathException de = new XPathException("The name of a constructed element must be a string, QName, or untypedAtomic");
+ de.setErrorCode("XPTY0004");
+ de.setIsTypeError(true);
+ de.setLocator(this);
+ throw de;
+ }
+ } else {
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (!th.isSubType(elementName.getItemType(th), BuiltInAtomicType.STRING)) {
+ elementName = SystemFunctionCall.makeSystemFunction("string", new Expression[]{elementName});
+ }
+ }
+ if (namespace != null) {
+ namespace = visitor.typeCheck(namespace, contextItemType);
+ // TODO: not sure there are any circumstances in which this type check can fail
+ role = new RoleLocator(RoleLocator.INSTRUCTION, "element/namespace", 0);
+ namespace = TypeChecker.staticTypeCheck(
+ namespace, SequenceType.SINGLE_STRING, false, role, visitor);
+ }
+ if (Literal.isAtomic(elementName)) {
+ // Check we have a valid lexical QName, whose prefix is in scope where necessary
+ try {
+ AtomicValue val = (AtomicValue)((Literal)elementName).getValue();
+ if (val instanceof StringValue) {
+ String[] parts = visitor.getConfiguration().getNameChecker().checkQNameParts(val.getStringValueCS());
+ if (namespace == null) {
+ String prefix = parts[0];
+ String uri = getNamespaceResolver().getURIForPrefix(prefix, true);
+ if (uri == null) {
+ XPathException se = new XPathException("Prefix " + prefix + " has not been declared");
+ se.setErrorCode("XPST0081");
+ se.setIsStaticError(true);
+ throw se;
+ }
+ namespace = new StringLiteral(uri);
+ }
+ }
+ } catch (XPathException e) {
+ String code = e.getErrorCodeLocalPart();
+ if (code == null || code.equals("FORG0001")) {
+ e.setErrorCode(isXSLT() ? "XTDE0820" : "XQDY0074");
+ } else if (code.equals("XPST0081")) {
+ e.setErrorCode(isXSLT() ? "XTDE0830" : "XQDY0074");
+ }
+ e.maybeSetLocation(this);
+ e.setIsStaticError(true);
+ throw e;
+ }
+ }
+ return super.typeCheck(visitor, contextItemType);
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ elementName = visitor.optimize(elementName, contextItemType);
+ return super.optimize(visitor, contextItemType);
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ComputedElement ce = new ComputedElement(
+ elementName.copy(), (namespace==null ? null : namespace.copy()),
+ getNamespaceResolver(), /*defaultNamespace,*/ getSchemaType(),
+ getValidationAction(), inheritNamespaces, allowNameAsQName);
+ ce.setContentExpression(content.copy());
+ return ce;
+ }
+
+ /**
+ * Get the item type of the value returned by this instruction
+ *
+ * @return the item type
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (itemType == null) {
+ return super.getItemType(th);
+ }
+ return itemType;
+ }
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ List<Expression> list = new ArrayList<Expression>(3);
+ list.add(content);
+ list.add(elementName);
+ if (namespace != null) {
+ list.add(namespace);
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ List<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>(3);
+ list.add(new SubExpressionInfo(content, true, false, NODE_VALUE_CONTEXT));
+ list.add(new SubExpressionInfo(elementName, true, false, NODE_VALUE_CONTEXT));
+ if (namespace != null) {
+ list.add(new SubExpressionInfo(namespace, true, false, NODE_VALUE_CONTEXT));
+ }
+ if (getOnEmpty() != null) {
+ list.add(new SubExpressionInfo(onEmpty, true, false, NAVIGATION_CONTEXT));
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (content == original) {
+ content = replacement;
+ found = true;
+ }
+ if (elementName == original) {
+ elementName = replacement;
+ found = true;
+ }
+ if (namespace == original) {
+ namespace = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+
+ /**
+ * Offer promotion for subexpressions. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @throws net.sf.saxon.trans.XPathException if any error is detected
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ elementName = doPromotion(elementName, offer);
+ if (namespace != null) {
+ namespace = doPromotion(namespace, offer);
+ }
+ super.promoteInst(offer);
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ if (parentType instanceof SimpleType || ((ComplexType)parentType).isSimpleContent()) {
+ String msg = "Elements are not permitted here: the containing element ";
+ if (parentType instanceof SimpleType) {
+ if (parentType.isAnonymousType()) {
+ msg += "is defined to have a simple type";
+ } else {
+ msg += "is of simple type " + parentType.getDescription();
+ }
+ } else {
+ msg += "has a complex type with simple content";
+ }
+ XPathException err = new XPathException(msg);
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ // NOTE: we could in principle check that if all the elements permitted in the content of the parentType
+ // themselves have a simple type (not uncommon, perhaps) then this element must not have element content.
+ }
+
+
+ /**
+ * Callback from the superclass ElementCreator to get the nameCode
+ * for the element name
+ *
+ *
+ *
+ * @param context The evaluation context (not used)
+ * @param copiedNode Not applicable to this overload
+ * @return the name code for the element name
+ */
+
+ public NodeName getElementName(XPathContext context, NodeInfo copiedNode)
+ throws XPathException {
+
+ Controller controller = context.getController();
+ assert controller != null;
+
+ String prefix;
+ String localName;
+ String uri = null;
+
+ // name needs to be evaluated at run-time
+ AtomicValue nameValue = (AtomicValue)elementName.evaluateItem(context);
+ if (nameValue == null) {
+ XPathException err1 = new XPathException("Invalid element name (empty sequence)", this);
+ err1.setErrorCode((isXSLT() ? "XTDE0820" : "XPTY0004"));
+ err1.setXPathContext(context);
+ throw dynamicError(this, err1, context);
+ }
+ //nameValue = nameValue.getPrimitiveValue();
+ if (nameValue instanceof StringValue) { // which includes UntypedAtomic
+ // this will always be the case in XSLT
+ CharSequence rawName = nameValue.getStringValueCS();
+ rawName = Whitespace.trimWhitespace(rawName);
+ try {
+ String[] parts = controller.getConfiguration().getNameChecker().getQNameParts(rawName);
+ prefix = parts[0];
+ localName = parts[1];
+ } catch (QNameException err) {
+ String message = "Invalid element name. " + err.getMessage();
+ if (rawName.length() == 0) {
+ message = "Supplied element name is a zero-length string";
+ }
+ XPathException err1 = new XPathException(message, this);
+ err1.setErrorCode((isXSLT() ? "XTDE0820" : "XQDY0074"));
+ err1.setXPathContext(context);
+ throw dynamicError(this, err1, context);
+ }
+ } else if (nameValue instanceof QNameValue && allowNameAsQName) {
+ // this is allowed in XQuery
+ localName = ((QNameValue)nameValue).getLocalName();
+ uri = ((QNameValue)nameValue).getNamespaceURI();
+ prefix = ((QNameValue)nameValue).getPrefix();
+ if (prefix.equals("xmlns")) {
+ XPathException err = new XPathException("Computed element name has prefix xmlns");
+ err.setErrorCode("XQDY0096");
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+ } else {
+ XPathException err = new XPathException("Computed element name has incorrect type");
+ err.setErrorCode((isXSLT() ? "XTDE0820" : "XPTY0004"));
+ err.setIsTypeError(true);
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ if (namespace == null && uri == null) {
+// if (prefix.length() == 0) {
+// uri = defaultNamespace;
+// } else {
+ uri = nsContext.getURIForPrefix(prefix, true);
+ if (uri == null) {
+ XPathException err = new XPathException("Undeclared prefix in element name: " + prefix, this);
+ err.setErrorCode((isXSLT() ? "XTDE0830" : (prefix.equals("xmlns") ? "XQDY0096" : "XQDY0074")));
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+// }
+ } else {
+ if (uri == null) {
+ if (namespace instanceof StringLiteral) {
+ uri = ((StringLiteral)namespace).getStringValue();
+ } else {
+ uri = namespace.evaluateAsString(context).toString();
+ if (!StandardURIChecker.getInstance().isValidURI(uri)) {
+ XPathException de = new XPathException("The value of the namespace attribute must be a valid URI");
+ de.setErrorCode("XTDE0835");
+ de.setXPathContext(context);
+ de.setLocator(this);
+ throw de;
+ }
+ }
+ }
+ if (uri.length() == 0) {
+ // there is a special rule for this case in the specification;
+ // we force the element to go in the null namespace
+ prefix = "";
+ }
+ if (prefix.equals("xmlns")) {
+ // this isn't a legal prefix so we mustn't use it
+ prefix = "x-xmlns";
+ }
+ }
+ if (uri.equals(NamespaceConstant.XMLNS)) {
+ XPathException err = new XPathException("Cannot create element in namespace " + uri, this);
+ err.setErrorCode((isXSLT() ? "XTDE0835" : "XQDY0096"));
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+ if (uri.equals(NamespaceConstant.XML) != prefix.equals("xml")) {
+ String message;
+ if (prefix.equals("xml")) {
+ message = "When the prefix is 'xml', the namespace URI must be " + NamespaceConstant.XML;
+ } else {
+ message = "When the namespace URI is " + NamespaceConstant.XML + ", the prefix must be 'xml'";
+ }
+ XPathException err = new XPathException(message, this);
+ err.setErrorCode((isXSLT() ? "XTDE0835" : "XQDY0096"));
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ return new FingerprintedQName(prefix, uri, localName);
+ }
+
+
+ /**
+ * Ask whether the name can be supplied as a QName. In practice this is true for XQuery, false for XSLT
+ * @return true if the name can be supplied as a QName
+ */
+
+ public boolean isAllowNameAsQName(){
+ return allowNameAsQName;
+ }
+
+ public String getNewBaseURI(XPathContext context, NodeInfo copiedNode) {
+ return getBaseURI();
+ }
+
+ /**
+ * Callback to output namespace nodes for the new element.
+ * @param context The execution context
+ * @param out the Receiver where the namespace nodes are to be written
+ * @param nameCode The name of the element node being output
+ * @param copiedNode Where this is a copied node, the node being copied
+ * @throws XPathException
+ */
+ public void outputNamespaceNodes(XPathContext context, Receiver out, NodeName nameCode, NodeInfo copiedNode)
+ throws XPathException {
+ // no action
+ }
+
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_ELEMENT;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ComputedElement expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ComputedElementCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("computedElement");
+ out.emitAttribute("validation", Validation.toString(getValidationAction()));
+ final SchemaType schemaType = getSchemaType();
+ if (schemaType != null) {
+ out.emitAttribute("type", schemaType.getDescription());
+ }
+ out.startSubsidiaryElement("name");
+ elementName.explain(out);
+ out.endSubsidiaryElement();
+ if (namespace != null) {
+ out.startSubsidiaryElement("namespace");
+ namespace.explain(out);
+ out.endSubsidiaryElement();
+ }
+ out.startSubsidiaryElement("content");
+ content.explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/Copy.java b/sf/saxon/expr/instruct/Copy.java
new file mode 100644
index 0000000..6efd662
--- /dev/null
+++ b/sf/saxon/expr/instruct/Copy.java
@@ -0,0 +1,647 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.CopyCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.CopyAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.*;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.ContentTypeTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.util.NamespaceIterator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+
+
+/**
+* Handler for xsl:copy elements in stylesheet.
+*/
+
+public class Copy extends ElementCreator {
+
+ private boolean copyNamespaces;
+ private boolean selectSpecified;
+ private ItemType resultItemType;
+ /*@Nullable*/ private Expression select;
+
+ /**
+ * Create a shallow copy instruction
+ * @param select selects the node (or other item) to be copied. Never null.
+ * @param selectSpecified true if the select attribute of xsl:copy was specified explicitly (in which
+ * case the context for evaluating the body will change)
+ * @param copyNamespaces true if namespace nodes are to be copied when copying an element
+ * @param inheritNamespaces true if child elements are to inherit the namespace nodes of their parent
+ * @param schemaType the Schema type against which the content is to be validated
+ * @param validation the schema validation mode
+ */
+
+ public Copy(Expression select,
+ boolean selectSpecified,
+ boolean copyNamespaces,
+ boolean inheritNamespaces,
+ SchemaType schemaType,
+ int validation) {
+ this.copyNamespaces = copyNamespaces;
+ this.inheritNamespaces = inheritNamespaces;
+ this.selectSpecified = selectSpecified;
+ this.select = select;
+ setValidationAction(validation, schemaType);
+ preservingTypes = schemaType == null && validation == Validation.PRESERVE;
+ if (copyNamespaces) {
+ setLazyConstruction(false);
+ // can't do lazy construction at present in cases where namespaces need to be copied from the
+ // source document.
+ }
+ }
+
+ /**
+ * Get the expression that selects the node or other item to be copied
+ * @return the select expression. This will be a context item expression if no select attribute was supplied.
+ */
+
+ public Expression getSelectExpression() {
+ return select;
+ }
+
+ /**
+ * Ask whether the select attribute was explicitly specified
+ * @return true if it was
+ */
+
+ public boolean isSelectSpecified() {
+ return selectSpecified;
+ }
+
+ /**
+ * Ask whether namespace nodes are to be copied (in the case of an element)
+ * @return true if all in-scope namespaces are to be copied
+ */
+
+ public boolean isCopyNamespaces() {
+ return copyNamespaces;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression rewriting
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ select = visitor.simplify(select);
+ preservingTypes |= !visitor.getExecutable().isSchemaAware();
+ return super.simplify(visitor);
+ }
+
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ try {
+ select = visitor.typeCheck(select, contextItemType);
+ adoptChildExpression(select);
+ } catch (XPathException err) {
+ if (err.getErrorCodeLocalPart().equals("XPDY0002")) {
+ // See spec bug 7624, test case copy903err
+ err.setErrorCode("XTTE0945");
+ err.maybeSetLocation(this);
+ }
+ select = Literal.makeEmptySequence(); // to prevent duplicate error reporting
+ throw err;
+ }
+
+ ItemType selectItemType = select.getItemType(visitor.getConfiguration().getTypeHierarchy());
+ content = visitor.typeCheck(content, getInnerContextItemType(contextItemType, selectItemType));
+
+ if (selectItemType instanceof NodeTest) {
+ switch (selectItemType.getPrimitiveType()) {
+ // For elements and attributes, assume the type annotation will change
+ case Type.ELEMENT:
+ this.resultItemType = NodeKindTest.ELEMENT;
+ break;
+ case Type.ATTRIBUTE:
+ this.resultItemType = NodeKindTest.ATTRIBUTE;
+ break;
+ case Type.DOCUMENT:
+ this.resultItemType = NodeKindTest.DOCUMENT;
+ break;
+ default:
+ this.resultItemType = selectItemType;
+ }
+ } else {
+ this.resultItemType = selectItemType;
+ }
+
+ adoptChildExpression(content);
+ verifyLazyConstruction();
+ checkContentSequence(visitor.getStaticContext());
+ return this;
+ }
+
+ /**
+ * Get the context information for evaluating the content of the xsl;copy instruction, which
+ * varies depending on whether a select attribute was specified or not.
+ * @param contextItemType
+ * @param selectItemType
+ * @return
+ */
+
+ private ExpressionVisitor.ContextItemType getInnerContextItemType(ExpressionVisitor.ContextItemType contextItemType, ItemType selectItemType) {
+ ExpressionVisitor.ContextItemType innerContext = contextItemType;
+ if (selectSpecified) {
+ innerContext = new ExpressionVisitor.ContextItemType(selectItemType, false);
+ }
+ return innerContext;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Copy copy = new Copy(select, selectSpecified,
+ copyNamespaces, inheritNamespaces, getSchemaType(), getValidationAction());
+ copy.setContentExpression(content.copy());
+ copy.resultItemType = resultItemType;
+ return copy;
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ } else if (content == original) {
+ content = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as XPathContext.VARIABLES and
+ * XPathContext.CURRENT_NODE. The default implementation combines the intrinsic
+ * dependencies of this expression with the dependencies of the subexpressions,
+ * computed recursively. This is overridden for expressions such as FilterExpression
+ * where a subexpression's dependencies are not necessarily inherited by the parent
+ * expression.
+ * @return a set of bit-significant flags identifying the dependencies of
+ * the expression
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_COPY;
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression.
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(select, content);
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new PairIterator<SubExpressionInfo>(
+ new SubExpressionInfo(select, true, false, INSPECTION_CONTEXT),
+ new SubExpressionInfo(content, true, false, NODE_VALUE_CONTEXT));
+ }
+
+
+ /**
+ * Get the item type of the result of this instruction.
+ * @return The context item type.
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (resultItemType != null) {
+ return resultItemType;
+ } else {
+ resultItemType = computeItemType(th);
+ return resultItemType;
+ }
+ }
+
+ private ItemType computeItemType(TypeHierarchy th) {
+ ItemType selectItemType = select.getItemType(th);
+ Executable exec = getExecutable();
+ if (!exec.isSchemaAware()) {
+ return selectItemType;
+ }
+ // The rest of the code handles the complications of schema-awareness
+ Configuration config = exec.getConfiguration();
+ if (getSchemaType() != null) {
+ int e = th.relationship(selectItemType, NodeKindTest.ELEMENT);
+ if (e == TypeHierarchy.SAME_TYPE || e == TypeHierarchy.SUBSUMED_BY) {
+ return new ContentTypeTest(Type.ELEMENT, getSchemaType(), config, false);
+ }
+ int a = th.relationship(selectItemType, NodeKindTest.ATTRIBUTE);
+ if (a == TypeHierarchy.SAME_TYPE || a == TypeHierarchy.SUBSUMED_BY) {
+ return new ContentTypeTest(Type.ATTRIBUTE, getSchemaType(), config, false);
+ }
+ return AnyNodeTest.getInstance();
+ } else switch (getValidationAction()) {
+ case Validation.PRESERVE:
+ return selectItemType;
+ case Validation.STRIP: {
+ int e = th.relationship(selectItemType, NodeKindTest.ELEMENT);
+ if (e == TypeHierarchy.SAME_TYPE || e == TypeHierarchy.SUBSUMED_BY) {
+ return new ContentTypeTest(Type.ELEMENT, Untyped.getInstance(), config, false);
+ }
+ int a = th.relationship(selectItemType, NodeKindTest.ATTRIBUTE);
+ if (a == TypeHierarchy.SAME_TYPE || a == TypeHierarchy.SUBSUMED_BY) {
+ return new ContentTypeTest(Type.ATTRIBUTE, BuiltInAtomicType.UNTYPED_ATOMIC, config, false);
+ }
+ if (e != TypeHierarchy.DISJOINT || a != TypeHierarchy.DISJOINT) {
+ // it might be an element or attribute
+ return AnyNodeTest.getInstance();
+ } else {
+ // it can't be an element or attribute, so stripping type annotations can't affect it
+ return selectItemType;
+ }
+ }
+ case Validation.STRICT:
+ case Validation.LAX:
+ if (selectItemType instanceof NodeTest) {
+ int fp = ((NodeTest)selectItemType).getFingerprint();
+ if (fp != -1) {
+ int e = th.relationship(selectItemType, NodeKindTest.ELEMENT);
+ if (e == TypeHierarchy.SAME_TYPE || e == TypeHierarchy.SUBSUMED_BY) {
+ SchemaDeclaration elem = config.getElementDeclaration(fp);
+ if (elem != null) {
+ return new ContentTypeTest(Type.ELEMENT, elem.getType(), config, false);
+ } else {
+ // No element declaration now, but there might be one at run-time
+ return new ContentTypeTest(Type.ELEMENT, AnyType.getInstance(), config, false);
+ }
+ }
+ int a = th.relationship(selectItemType, NodeKindTest.ATTRIBUTE);
+ if (a == TypeHierarchy.SAME_TYPE || a == TypeHierarchy.SUBSUMED_BY) {
+ SchemaDeclaration attr = config.getElementDeclaration(fp);
+ if (attr != null) {
+ return new ContentTypeTest(Type.ATTRIBUTE, attr.getType(), config, false);
+ } else {
+ // No attribute declaration now, but there might be one at run-time
+ return new ContentTypeTest(Type.ATTRIBUTE, AnySimpleType.getInstance(), config, false);
+ }
+ }
+ } else {
+ int e = th.relationship(selectItemType, NodeKindTest.ELEMENT);
+ if (e == TypeHierarchy.SAME_TYPE || e == TypeHierarchy.SUBSUMED_BY) {
+ return NodeKindTest.ELEMENT;
+ }
+ int a = th.relationship(selectItemType, NodeKindTest.ATTRIBUTE);
+ if (a == TypeHierarchy.SAME_TYPE || a == TypeHierarchy.SUBSUMED_BY) {
+ return NodeKindTest.ATTRIBUTE;
+ }
+ }
+ return AnyNodeTest.getInstance();
+ } else if (selectItemType instanceof AtomicType) {
+ return selectItemType;
+ } else {
+ return AnyItemType.getInstance();
+ }
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ select = visitor.optimize(select, contextItemType);
+ Expression exp = super.optimize(visitor,
+ getInnerContextItemType(contextItemType, computeItemType(visitor.getConfiguration().getTypeHierarchy())));
+ if (exp == this) {
+ if (resultItemType == null) {
+ resultItemType = computeItemType(visitor.getConfiguration().getTypeHierarchy());
+ }
+ if (resultItemType.isPlainType() && select != null) {
+ return select;
+ }
+ }
+ return exp;
+ }
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ select = doPromotion(select, offer);
+ content = doPromotion(content, offer);
+ }
+
+ /**
+ * Callback from ElementCreator when constructing an element
+ *
+ *
+ * @param context XPath dynamic evaluation context
+ * @param copiedNode the node being copied
+ * @return the namecode of the element to be constructed
+ * @throws XPathException
+ */
+
+ public NodeName getElementName(XPathContext context, NodeInfo copiedNode) throws XPathException {
+ return new NameOfNode(copiedNode);
+ }
+
+ /**
+ * Get the base URI of a copied element node (the base URI is retained in the new copy)
+ * @param context XPath dynamic evaluation context
+ * @param copiedNode
+ * @return the base URI
+ */
+
+ public String getNewBaseURI(XPathContext context, NodeInfo copiedNode) {
+ return copiedNode.getBaseURI();
+ }
+
+ /**
+ * Callback to output namespace nodes for the new element.
+ *
+ *
+ * @param context The execution context
+ * @param receiver the Receiver where the namespace nodes are to be written
+ * @param nameCode
+ * @param copiedNode
+ * @throws XPathException
+ */
+
+ public void outputNamespaceNodes(XPathContext context, Receiver receiver, NodeName nameCode, NodeInfo copiedNode)
+ throws XPathException {
+ if (copyNamespaces) {
+ NamespaceIterator.sendNamespaces(copiedNode, receiver);
+ } else {
+ // Always output the namespace of the element name itself
+ receiver.namespace(nameCode.getNamespaceBinding(), 0);
+ }
+ }
+
+ /**
+ * Callback to get a list of the intrinsic namespaces that need to be generated for the element.
+ * The result is an array of namespace codes, the codes either occupy the whole array or are
+ * terminated by a -1 entry. A result of null is equivalent to a zero-length array.
+ */
+
+ public NamespaceBinding[] getActiveNamespaces() throws XPathException {
+ if (copyNamespaces) {
+ // we should have disabled lazy construction, so this shouldn't be called.
+ throw new UnsupportedOperationException();
+ } else {
+ return null;
+ }
+ }
+
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ SequenceReceiver out = context.getReceiver();
+ XPathContext c2 = context;
+ Item item;
+
+
+ item = select.evaluateItem(context);
+ if (item == null) {
+ // See spec bug 7624, test case copy904err
+ return null;
+ }
+
+ if (!(item instanceof NodeInfo)) {
+ out.append(item, locationId, NodeInfo.ALL_NAMESPACES);
+ return null;
+ }
+ NodeInfo source = (NodeInfo)item;
+ //out.getPipelineConfiguration().setBaseURI(source.getBaseURI());
+
+ // Processing depends on the node kind.
+
+ switch(source.getNodeKind()) {
+
+ case Type.ELEMENT:
+ // use the generic code for creating new elements
+ if (selectSpecified) {
+ c2 = context.newMinorContext();
+ SequenceIterator si = SingletonIterator.makeIterator(item);
+ si.next();
+ c2.setCurrentIterator(si);
+ }
+ return super.processLeavingTail(c2, (NodeInfo)item);
+
+ case Type.ATTRIBUTE:
+ if (getSchemaType() instanceof ComplexType) {
+ dynamicError("Cannot copy an attribute when the type requested for validation is a complex type", "XTTE1535", context);
+ }
+ try {
+ CopyOf.copyAttribute(source, (SimpleType)getSchemaType(), getValidationAction(), this, context, false);
+ } catch (NoOpenStartTagException err) {
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+ break;
+
+ case Type.TEXT:
+ out.characters(source.getStringValueCS(), locationId, 0);
+ break;
+
+ case Type.PROCESSING_INSTRUCTION:
+ out.processingInstruction(source.getDisplayName(), source.getStringValueCS(), locationId, 0);
+ break;
+
+ case Type.COMMENT:
+ out.comment(source.getStringValueCS(), locationId, 0);
+ break;
+
+ case Type.NAMESPACE:
+ try {
+ source.copy(out, 0, locationId);
+ } catch (NoOpenStartTagException err) {
+ XPathException e = new XPathException(err.getMessage());
+ e.setXPathContext(context);
+ e.setErrorCodeQName(err.getErrorCodeQName());
+ throw dynamicError(this, e, context);
+ }
+ break;
+
+ case Type.DOCUMENT:
+ if (selectSpecified) {
+ c2 = context.newMinorContext();
+ SequenceIterator si = SingletonIterator.makeIterator(item);
+ si.next();
+ c2.setCurrentIterator(si);
+ }
+ if (preservingTypes) {
+ out.startDocument(0);
+ content.process(c2);
+ out.endDocument();
+ } else {
+ boolean pop = false;
+ ParseOptions options = new ParseOptions(getValidationOptions());
+ options.setStripSpace(Whitespace.NONE);
+ Receiver val = controller.getConfiguration().
+ getDocumentValidator(out, source.getBaseURI(), options);
+ if (val != out) {
+ SequenceReceiver sr = new TreeReceiver(val);
+ sr.setPipelineConfiguration(out.getPipelineConfiguration());
+ c2.setReceiver(sr);
+ pop = true;
+ out = sr;
+ }
+ out.startDocument(0);
+ content.process(c2);
+ out.endDocument();
+ if (pop) {
+ context.setReceiver(out);
+ }
+ }
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown node kind " + source.getNodeKind());
+
+ }
+ return null;
+ }
+
+ /**
+ * Evaluate as an expression. We rely on the fact that when these instructions
+ * are generated by XQuery, there will always be a valueExpression to evaluate
+ * the content
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ SequenceReceiver saved = context.getReceiver();
+ SequenceOutputter seq = controller.allocateSequenceOutputter(1);
+ seq.getPipelineConfiguration().setHostLanguage(getHostLanguage());
+ context.setReceiver(seq);
+ process(context);
+ seq.close();
+ context.setReceiver(saved);
+ Item item = seq.getFirstItem();
+ seq.reset();
+ return item;
+ }
+
+//#ifdefined BYTECODE
+
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+
+ if (select instanceof ContextItemExpression) {
+ return content.getStreamability(NODE_VALUE_CONTEXT, allowExtensions, reasons);
+ } else {
+ return super.getStreamability(syntacticContext, allowExtensions, reasons);
+ }
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public CopyAdjunct getStreamingAdjunct() {
+ return new CopyAdjunct();
+ }
+
+ /**
+ * Process the first half of the instruction in streaming mode
+ */
+
+ public void processLeft(Stack<XPathContext> contextStack, Stack<Object> state) throws XPathException {
+ CopyAdjunct.processLeft(this, contextStack, state);
+ }
+
+ /**
+ * Process the second half of the instruction in streaming mode
+ */
+
+ public void processRight(Stack<XPathContext> contextStack, Stack<Object> state) throws XPathException {
+ CopyAdjunct.processRight(this, contextStack, state);
+ }
+
+ /**
+ * Return the compiler of the Copy expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CopyCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("copy");
+ out.startSubsidiaryElement("select");
+ select.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("action");
+ content.explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/instruct/CopyOf.java b/sf/saxon/expr/instruct/CopyOf.java
new file mode 100644
index 0000000..f73f303
--- /dev/null
+++ b/sf/saxon/expr/instruct/CopyOf.java
@@ -0,0 +1,844 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.CopyOfCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.Streamability;
+import com.saxonica.stream.adjunct.CopyOfAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.*;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.ContentTypeTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.wrapper.VirtualCopy;
+import net.sf.saxon.tree.wrapper.VirtualUntypedCopy;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import java.util.List;
+
+
+
+/**
+ * An xsl:copy-of element in the stylesheet.
+ */
+
+public class CopyOf extends Instruction implements ValidatingInstruction {
+
+ private Expression select;
+ private boolean copyNamespaces;
+ private int validation;
+ private SchemaType schemaType;
+ private boolean requireDocumentOrElement = false;
+ private boolean rejectDuplicateAttributes;
+ private boolean readOnce = false;
+ private boolean validating;
+ private boolean copyLineNumbers = true;
+ private boolean copyForUpdate = false;
+ private String staticBaseUri;
+
+ /**
+ * Create an xsl:copy-of instruction (also used in XQuery for implicit copying)
+ * @param select expression that selects the nodes to be copied
+ * @param copyNamespaces true if namespaces are to be copied
+ * @param validation validation mode for the result tree
+ * @param schemaType schema type for validating the result tree
+ * @param rejectDuplicateAttributes true if duplicate attributes are to be rejected (XQuery). False
+ * if duplicates are handled by discarding all but the first (XSLT).
+ */
+
+ public CopyOf(Expression select,
+ boolean copyNamespaces,
+ int validation,
+ SchemaType schemaType,
+ boolean rejectDuplicateAttributes) {
+ this.select = select;
+ this.copyNamespaces = copyNamespaces;
+ this.validation = validation;
+ this.schemaType = schemaType;
+ validating = schemaType != null || validation != Validation.PRESERVE;
+ this.rejectDuplicateAttributes = rejectDuplicateAttributes;
+ adoptChildExpression(select);
+ }
+
+ /**
+ * Set the select expression
+ * @param select the new select expression
+ */
+
+ public void setSelectExpression(Expression select) {
+ this.select = select;
+ }
+
+ /**
+ * Get the expression that selects the nodes to be copied
+ * @return the select expression
+ */
+
+ public Expression getSelectExpression() {
+ return select;
+ }
+
+ /**
+ * Get the validation mode
+ * @return the validation mode
+ */
+
+ public int getValidationAction() {
+ return validation;
+ }
+
+ /**
+ * Test if the instruction is doing validation
+ * @return true if it is
+ */
+
+ public boolean isValidating() {
+ return validating;
+ }
+
+ /**
+ * Get the schema type to be used for validation
+ * @return the schema type, or null if not validating against a type
+ */
+
+ public SchemaType getSchemaType() {
+ return schemaType;
+ }
+
+ /**
+ * Set the static base URI of the xsl:copy-of instruction
+ * @param base the static base URI
+ */
+
+ public void setStaticBaseUri(String base) {
+ staticBaseUri = base;
+ }
+
+ /**
+ * Set the "saxon:read-once" optimization mode
+ * @param b true to enable the optimization
+ */
+
+ public void setReadOnce(boolean b) {
+ readOnce = b;
+ }
+
+ /**
+ * Set whether line numbers are to be copied from the source to the result.
+ * Default is false.
+ * @param copy true if line numbers are to be copied
+ */
+
+ public void setCopyLineNumbers(boolean copy) {
+ copyLineNumbers = copy;
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * The result depends on the type of the select expression.
+ */
+
+ public final boolean createsNewNodes() {
+ Executable exec = getExecutable();
+ if (exec == null) {
+ return true; // This shouldn't happen, but we err on the safe side
+ }
+ final TypeHierarchy th = exec.getConfiguration().getTypeHierarchy();
+ return !select.getItemType(th).isPlainType();
+ }
+
+ /**
+ * Get the name of this instruction, for diagnostics and tracing
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_COPY_OF;
+ }
+
+ /**
+ * For XQuery, the operand (select) must be a single element or document node.
+ * @param requireDocumentOrElement true if the argument must be a single element or document node
+ */
+ public void setRequireDocumentOrElement(boolean requireDocumentOrElement) {
+ this.requireDocumentOrElement = requireDocumentOrElement;
+ }
+
+ /**
+ * Test whether this expression requires a document or element node
+ * @return true if this expression requires the value of the argument to be a document or element node,
+ * false if there is no such requirement
+ */
+
+ public boolean isDocumentOrElementRequired() {
+ return requireDocumentOrElement;
+ }
+
+ /**
+ * Set whether this instruction is creating a copy for the purpose of updating (XQuery transform expression)
+ * @param forUpdate true if this copy is being created to support an update
+ */
+
+ public void setCopyForUpdate(boolean forUpdate) {
+ copyForUpdate = forUpdate;
+ }
+
+ /**
+ * Ask whether this instruction is creating a copy for the purpose of updating (XQuery transform expression)
+ * @return true if this copy is being created to support an update
+ */
+
+ public boolean isCopyForUpdate() {
+ return copyForUpdate;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided. This implementation provides both iterate() and
+ * process() methods natively.
+ */
+
+ public int getImplementationMethod() {
+ return ITERATE_METHOD | PROCESS_METHOD | WATCH_METHOD;
+ }
+
+ /**
+ * Determine whether namespaces are to be copied or not
+ * @return true if namespaces are to be copied (the default)
+ */
+
+ public boolean isCopyNamespaces() {
+ return copyNamespaces;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ CopyOf c = new CopyOf(select.copy(), copyNamespaces, validation, schemaType, rejectDuplicateAttributes);
+ c.setContainer(getContainer());
+ // we don't normally setContainer() in the copy() method, but it's needed here because of the
+ // call on getContainer() in computeSpecialProperties()
+ c.setCopyForUpdate(copyForUpdate);
+ c.setCopyLineNumbers(copyLineNumbers);
+ c.setReadOnce(readOnce);
+ c.setStaticBaseUri(staticBaseUri);
+ return c;
+ }
+
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ select = visitor.simplify(select);
+ return this;
+ }
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ Executable exec = getExecutable();
+ assert exec != null;
+ ItemType in = select.getItemType(th);
+ if (!exec.isSchemaAware()) {
+ return in;
+ }
+ Configuration config = exec.getConfiguration();
+ if (schemaType != null) {
+ int e = th.relationship(in, NodeKindTest.ELEMENT);
+ if (e == TypeHierarchy.SAME_TYPE || e == TypeHierarchy.SUBSUMED_BY) {
+ return new ContentTypeTest(Type.ELEMENT, schemaType, config, false);
+ }
+ int a = th.relationship(in, NodeKindTest.ATTRIBUTE);
+ if (a == TypeHierarchy.SAME_TYPE || a == TypeHierarchy.SUBSUMED_BY) {
+ return new ContentTypeTest(Type.ATTRIBUTE, schemaType, config, false);
+ }
+ } else switch (validation) {
+ case Validation.PRESERVE:
+ return in;
+ case Validation.STRIP: {
+ int e = th.relationship(in, NodeKindTest.ELEMENT);
+ if (e == TypeHierarchy.SAME_TYPE || e == TypeHierarchy.SUBSUMED_BY) {
+ return new ContentTypeTest(Type.ELEMENT, Untyped.getInstance(), config, false);
+ }
+ int a = th.relationship(in, NodeKindTest.ATTRIBUTE);
+ if (a == TypeHierarchy.SAME_TYPE || a == TypeHierarchy.SUBSUMED_BY) {
+ return new ContentTypeTest(Type.ATTRIBUTE, BuiltInAtomicType.UNTYPED_ATOMIC, config, false);
+ }
+ if (e != TypeHierarchy.DISJOINT || a != TypeHierarchy.DISJOINT) {
+ // it might be an element or attribute
+ return AnyNodeTest.getInstance();
+ } else {
+ // it can't be an element or attribute, so stripping type annotations can't affect it
+ return in;
+ }
+ }
+ case Validation.STRICT:
+ case Validation.LAX:
+ if (in instanceof NodeTest) {
+ int fp = ((NodeTest)in).getFingerprint();
+ if (fp != -1) {
+ int e = th.relationship(in, NodeKindTest.ELEMENT);
+ if (e == TypeHierarchy.SAME_TYPE || e == TypeHierarchy.SUBSUMED_BY) {
+ SchemaDeclaration elem = config.getElementDeclaration(fp);
+ if (elem != null) {
+ return new ContentTypeTest(Type.ELEMENT, elem.getType(), config, false);
+ } else {
+ // Although there is no element declaration now, there might be one at run-time
+ return new ContentTypeTest(Type.ELEMENT, AnyType.getInstance(), config, false);
+ }
+ }
+ int a = th.relationship(in, NodeKindTest.ATTRIBUTE);
+ if (a == TypeHierarchy.SAME_TYPE || a == TypeHierarchy.SUBSUMED_BY) {
+ SchemaDeclaration attr = config.getElementDeclaration(fp);
+ if (attr != null) {
+ return new ContentTypeTest(Type.ATTRIBUTE, attr.getType(), config, false);
+ } else {
+ // Although there is no attribute declaration now, there might be one at run-time
+ return new ContentTypeTest(Type.ATTRIBUTE, AnySimpleType.getInstance(), config, false);
+ }
+ }
+ } else {
+ int e = th.relationship(in, NodeKindTest.ELEMENT);
+ if (e == TypeHierarchy.SAME_TYPE || e == TypeHierarchy.SUBSUMED_BY) {
+ return NodeKindTest.ELEMENT;
+ }
+ int a = th.relationship(in, NodeKindTest.ATTRIBUTE);
+ if (a == TypeHierarchy.SAME_TYPE || a == TypeHierarchy.SUBSUMED_BY) {
+ return NodeKindTest.ATTRIBUTE;
+ }
+ }
+ return AnyNodeTest.getInstance();
+ } else if (in instanceof AtomicType) {
+ return in;
+ } else {
+ return AnyItemType.getInstance();
+ }
+ }
+ return select.getItemType(th);
+ }
+
+ public int getCardinality() {
+ return select.getCardinality();
+ }
+
+ public int getDependencies() {
+ return select.getDependencies();
+ }
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ select = doPromotion(select, offer);
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ select = visitor.typeCheck(select, contextItemType);
+ if (isDocumentOrElementRequired()) {
+ // this implies the expression is actually an XQuery validate{} expression, hence the error messages
+ RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP, "validate", 0);
+ role.setErrorCode("XQTY0030");
+ select = TypeChecker.staticTypeCheck(select, SequenceType.SINGLE_NODE, false, role, visitor);
+
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType t = select.getItemType(th);
+ if (th.isSubType(t, NodeKindTest.ATTRIBUTE)) {
+ throw new XPathException("validate{} expression cannot be applied to an attribute", "XQTY0030");
+ }
+ if (th.isSubType(t, NodeKindTest.TEXT)) {
+ throw new XPathException("validate{} expression cannot be applied to a text node", "XQTY0030");
+ }
+ if (th.isSubType(t, NodeKindTest.COMMENT)) {
+ throw new XPathException("validate{} expression cannot be applied to a comment node", "XQTY0030");
+ }
+ if (th.isSubType(t, NodeKindTest.PROCESSING_INSTRUCTION)) {
+ throw new XPathException("validate{} expression cannot be applied to a processing instruction node", "XQTY0030");
+ }
+ if (th.isSubType(t, NodeKindTest.NAMESPACE)) {
+ throw new XPathException("validate{} expression cannot be applied to a namespace node", "XQTY0030");
+ }
+ }
+ adoptChildExpression(select);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (readOnce) {
+ Expression optcopy = visitor.getConfiguration().obtainOptimizer().optimizeCopy(select);
+ if (optcopy != null) {
+ return optcopy;
+ }
+ }
+ select = visitor.optimize(select, contextItemType);
+ if (Literal.isEmptySequence(select)) {
+ return select;
+ }
+ adoptChildExpression(select);
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (select.getItemType(th).isPlainType()) {
+ return select;
+ }
+ return this;
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("copyOf");
+ out.emitAttribute("validation", Validation.toString(validation));
+ select.explain(out);
+ out.endElement();
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ if (Streamability.isIncrementallyConsuming(select)) {
+ return W3C_CONSUMING;
+ }
+ int s = select.getStreamability(NODE_VALUE_CONTEXT, allowExtensions, reasons);
+ if (s == W3C_MOTIONLESS) {
+ return W3C_MOTIONLESS;
+ }
+ if (s == W3C_GROUP_CONSUMING) {
+ return W3C_GROUP_CONSUMING;
+ }
+ if (s == W3C_CONSUMING) {
+ return W3C_CONSUMING;
+ }
+ if (select instanceof ContextItemExpression ||
+ (select instanceof AxisExpression && ((AxisExpression)select).getAxis() == AxisInfo.CHILD)) {
+ return W3C_CONSUMING;
+ }
+ return W3C_FREE_RANGING;
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public CopyOfAdjunct getStreamingAdjunct() {
+ return new CopyOfAdjunct();
+ }
+
+ //#endif
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(select);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet result = super.addToPathMap(pathMap, pathMapNodeSet);
+ result.setReturnable(false);
+ TypeHierarchy th = getExecutable().getConfiguration().getTypeHierarchy();
+ ItemType type = getItemType(th);
+ if (th.relationship(type, NodeKindTest.ELEMENT) != TypeHierarchy.DISJOINT ||
+ th.relationship(type, NodeKindTest.DOCUMENT) != TypeHierarchy.DISJOINT) {
+ result.addDescendants();
+ }
+ return new PathMap.PathMapNodeSet(pathMap.makeNewRoot(this));
+ }
+
+ /**
+ * Process this xsl:copy-of instruction
+ *
+ * @param context the dynamic context for the transformation
+ * @return null - this implementation of the method never returns a TailCall
+ */
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+
+ Controller controller = context.getController();
+ SequenceReceiver out = context.getReceiver();
+ boolean copyBaseURI = (out.getSystemId() == null);
+ // if the copy is being attached to an existing parent, it inherits the base URI of the parent
+
+ int copyOptions = CopyOptions.TYPE_ANNOTATIONS;
+ if (copyNamespaces) {
+ copyOptions |= CopyOptions.ALL_NAMESPACES;
+ }
+ if (copyForUpdate) {
+ copyOptions |= CopyOptions.FOR_UPDATE;
+ }
+ //int whichNamespaces = (copyNamespaces ? NodeInfo.ALL_NAMESPACES : NodeInfo.NO_NAMESPACES);
+
+ SequenceIterator iter = select.iterate(context);
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (item instanceof NodeInfo) {
+ NodeInfo source = (NodeInfo) item;
+ int kind = source.getNodeKind();
+ if (requireDocumentOrElement &&
+ !(kind == Type.ELEMENT || kind == Type.DOCUMENT)) {
+ XPathException e = new XPathException("Operand of validate expression must be a document or element node");
+ e.setXPathContext(context);
+ e.setErrorCode("XQTY0030");
+ throw e;
+ }
+ final Configuration config = controller.getConfiguration();
+ switch (kind) {
+
+ case Type.ELEMENT: {
+ Receiver eval = out;
+ if (validating) {
+ ParseOptions options = new ParseOptions();
+ options.setSchemaValidationMode(validation);
+ options.setTopLevelType(schemaType);
+ options.setTopLevelElement(new NameOfNode(source));
+ eval = config.getElementValidator(out, options, locationId);
+ }
+ if (copyBaseURI) {
+ eval.setSystemId(computeNewBaseUri(source));
+ }
+
+ Receiver savedReceiver = null;
+ PipelineConfiguration savedPipe = null;
+ if (copyLineNumbers) {
+ savedReceiver = eval;
+ PipelineConfiguration pipe = eval.getPipelineConfiguration();
+ savedPipe = new PipelineConfiguration(pipe);
+ LocationCopier copier = new LocationCopier(false);
+ pipe.setLocationProvider(copier);
+ pipe.setComponent(CopyInformee.class.getName(), copier);
+ }
+ source.copy(eval, copyOptions, locationId);
+ if (copyLineNumbers) {
+ eval = savedReceiver;
+ eval.setPipelineConfiguration(savedPipe);
+ }
+ break;
+ }
+ case Type.ATTRIBUTE:
+ if (schemaType != null && schemaType.isComplexType()) {
+ XPathException e = new XPathException("When copying an attribute with schema validation, the requested type must not be a complex type");
+ e.setLocator(this);
+ e.setXPathContext(context);
+ e.setErrorCode("XTTE1535");
+ throw dynamicError(this, e, context);
+ }
+ try {
+ copyAttribute(source, (SimpleType)schemaType, validation, this, context, rejectDuplicateAttributes);
+ } catch (NoOpenStartTagException err) {
+ XPathException e = new XPathException(err.getMessage());
+ e.setLocator(this);
+ e.setXPathContext(context);
+ e.setErrorCodeQName(err.getErrorCodeQName());
+ throw dynamicError(this, e, context);
+ }
+ break;
+ case Type.TEXT:
+ out.characters(source.getStringValueCS(), locationId, 0);
+ break;
+
+ case Type.PROCESSING_INSTRUCTION:
+ if (copyBaseURI) {
+ out.setSystemId(source.getBaseURI());
+ }
+ out.processingInstruction(source.getDisplayName(), source.getStringValueCS(), locationId, 0);
+ break;
+
+ case Type.COMMENT:
+ out.comment(source.getStringValueCS(), locationId, 0);
+ break;
+
+ case Type.NAMESPACE:
+ try {
+ source.copy(out, 0, locationId);
+ } catch (NoOpenStartTagException err) {
+ XPathException e = new XPathException(err.getMessage());
+ e.setXPathContext(context);
+ e.setErrorCodeQName(err.getErrorCodeQName());
+ throw dynamicError(this, e, context);
+ }
+ break;
+
+ case Type.DOCUMENT: {
+ ParseOptions options = new ParseOptions();
+ options.setSchemaValidationMode(validation);
+ options.setStripSpace(Whitespace.NONE);
+ options.setTopLevelType(schemaType);
+ Receiver val = config.getDocumentValidator(out, source.getBaseURI(), options);
+ //val.setPipelineConfiguration(out.getPipelineConfiguration());
+ if (copyBaseURI) {
+ val.setSystemId(source.getBaseURI());
+ }
+ Receiver savedReceiver = null;
+ PipelineConfiguration savedPipe = null;
+ if (copyLineNumbers) {
+ savedReceiver = val;
+ savedPipe = new PipelineConfiguration(val.getPipelineConfiguration());
+ LocationCopier copier = new LocationCopier(true);
+ val.getPipelineConfiguration().setLocationProvider(copier);
+ val.getPipelineConfiguration().setComponent(CopyInformee.class.getName(), copier);
+
+ }
+ source.copy(val, copyOptions, locationId);
+ if (copyLineNumbers) {
+ val = savedReceiver;
+ val.setPipelineConfiguration(savedPipe);
+ }
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown node kind " + source.getNodeKind());
+ }
+
+ } else {
+ out.append(item, locationId, NodeInfo.ALL_NAMESPACES);
+ }
+ }
+ return null;
+ }
+
+ private String computeNewBaseUri(NodeInfo source) {
+ // These rules are the rules for xsl:copy-of instruction in XSLT. The same code is used to support the
+ // validate{} expression in XQuery. XQuery says nothing about the base URI of a node that results
+ // from a validate{} expression, so until it does, we might as well use the same logic.
+ String newBaseUri;
+ String xmlBase = source.getAttributeValue(NamespaceConstant.XML, "base");
+ if (xmlBase != null) {
+ try {
+ URI xmlBaseUri = new URI(xmlBase);
+ if (xmlBaseUri.isAbsolute()) {
+ newBaseUri = xmlBase;
+ } else if (staticBaseUri != null) {
+ URI sbu = new URI(staticBaseUri);
+ URI abs = sbu.resolve(xmlBaseUri);
+ newBaseUri = abs.toString();
+ } else {
+ newBaseUri = source.getBaseURI();
+ }
+ } catch (URISyntaxException err) {
+ newBaseUri = source.getBaseURI();
+ }
+ } else {
+ newBaseUri = source.getBaseURI();
+ }
+ return newBaseUri;
+ }
+
+ /**
+ * Method shared by xsl:copy and xsl:copy-of to copy an attribute node
+ * @param source the node to be copied
+ * @param schemaType the simple type against which the value is to be validated, if any
+ * @param validation one of preserve, strip, strict, lax
+ * @param instruction the calling instruction, used for diagnostics
+ * @param context the dynamic context
+ * @param rejectDuplicates true if duplicate attributes with the same name are disallowed (XQuery)
+ * @throws XPathException if a failure occurs
+ */
+
+ static void copyAttribute(NodeInfo source,
+ SimpleType schemaType,
+ int validation,
+ Instruction instruction,
+ XPathContext context,
+ boolean rejectDuplicates)
+ throws XPathException {
+ SimpleType annotation = BuiltInAtomicType.UNTYPED_ATOMIC;
+ int opt = 0;
+ if (rejectDuplicates) {
+ opt |= ReceiverOptions.REJECT_DUPLICATES;
+ }
+ CharSequence value = source.getStringValueCS();
+ if (schemaType != null) {
+ if (schemaType.isSimpleType()) {
+ if (schemaType.isNamespaceSensitive()) {
+ XPathException err = new XPathException("Cannot create a parentless attribute whose " +
+ "type is namespace-sensitive (such as xs:QName)");
+ err.setErrorCode("XTTE1545");
+ err.setXPathContext(context);
+ err.setLocator(instruction);
+ throw err;
+ }
+ try {
+ ValidationFailure err = schemaType.validateContent(
+ value, DummyNamespaceResolver.getInstance(), context.getConfiguration().getConversionRules());
+ if (err != null) {
+ throw new ValidationException("Attribute being copied does not match the required type. " +
+ err.getMessage());
+ }
+ annotation = schemaType;
+ } catch (UnresolvedReferenceException ure) {
+ throw new ValidationException(ure);
+ }
+ } else {
+ XPathException e = new XPathException("Cannot validate an attribute against a complex type");
+ e.setXPathContext(context);
+ e.setErrorCode("XTTE1535"); // See spec bug 13001
+ e.setIsTypeError(true);
+ throw e;
+ }
+ } else if (validation == Validation.STRICT || validation == Validation.LAX) {
+ try {
+ annotation = context.getConfiguration().validateAttribute(source.getNameCode(), value, validation);
+ } catch (ValidationException e) {
+ XPathException err = XPathException.makeXPathException(e);
+ err.setErrorCodeQName(e.getErrorCodeQName());
+ err.setXPathContext(context);
+ err.setLocator(instruction);
+ err.setIsTypeError(true);
+ throw err;
+ }
+
+ } else if (validation == Validation.PRESERVE) {
+ annotation = (SimpleType)source.getSchemaType();
+ if (!annotation.equals(BuiltInAtomicType.UNTYPED_ATOMIC) && annotation.isNamespaceSensitive()) {
+ XPathException err = new XPathException("Cannot preserve type annotation when copying an attribute with namespace-sensitive content");
+ err.setErrorCode((instruction.getHostLanguage() == Configuration.XSLT ? "XTTE0950" : "XQTY0086"));
+ err.setIsTypeError(true);
+ err.setXPathContext(context);
+ err.setLocator(instruction);
+ throw err;
+ }
+ }
+
+ context.getReceiver().attribute(new NameOfNode(source), annotation, value, instruction.getLocationId(), opt);
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ final Controller controller = context.getController();
+ assert controller != null;
+ if (schemaType == null && copyNamespaces && !copyForUpdate) {
+ if (validation == Validation.PRESERVE) {
+ // create a virtual copy of the underlying nodes
+ ItemMappingFunction<Item, Item> copier = new ItemMappingFunction<Item, Item>() {
+ public Item mapItem(Item item) {
+ if (item instanceof NodeInfo) {
+ VirtualCopy vc = VirtualCopy.makeVirtualCopy((NodeInfo)item, (NodeInfo) item);
+
+ long documentNumber =
+ controller.getConfiguration().getDocumentNumberAllocator().allocateDocumentNumber();
+ vc.setDocumentNumber(documentNumber);
+ if (((NodeInfo)item).getNodeKind() == Type.ELEMENT) {
+ vc.setSystemId(computeNewBaseUri((NodeInfo)item));
+ }
+ return vc;
+ } else {
+ return item;
+ }
+ }
+ };
+ return new ItemMappingIterator<Item, Item>((SequenceIterator<Item>)select.iterate(context), copier, true);
+ } else if (validation == Validation.STRIP) {
+ // create a virtual copy of the underlying nodes
+ ItemMappingFunction<Item, Item> copier = new ItemMappingFunction<Item, Item>() {
+ public Item mapItem(Item item) {
+ if (!(item instanceof NodeInfo)) {
+ return item;
+ }
+ VirtualCopy vc = VirtualUntypedCopy.makeVirtualUntypedCopy((NodeInfo) item, (NodeInfo) item);
+ long documentNumber =
+ controller.getConfiguration().getDocumentNumberAllocator().allocateDocumentNumber();
+ vc.setDocumentNumber(documentNumber);
+ if (((NodeInfo)item).getNodeKind() == Type.ELEMENT) {
+ vc.setSystemId(computeNewBaseUri((NodeInfo)item));
+ }
+ return vc;
+ }
+ };
+ return new ItemMappingIterator<Item, Item>((SequenceIterator<Item>)select.iterate(context), copier, true);
+ }
+ }
+ SequenceReceiver saved = context.getReceiver();
+ PipelineConfiguration pipe = controller.makePipelineConfiguration();
+ SequenceOutputter out = new SequenceOutputter(pipe);
+ pipe.setHostLanguage(getHostLanguage());
+ context.setReceiver(out);
+ try {
+ process(context);
+ } catch (XPathException err) {
+ if (err instanceof ValidationException) {
+ ((ValidationException) err).setSourceLocator(this);
+ ((ValidationException) err).setSystemId(getSystemId());
+ }
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ context.setReceiver(saved);
+ return out.getSequence().iterate();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CopyOf expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CopyOfCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/instruct/Debugger.java b/sf/saxon/expr/instruct/Debugger.java
new file mode 100644
index 0000000..fea461b
--- /dev/null
+++ b/sf/saxon/expr/instruct/Debugger.java
@@ -0,0 +1,24 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+/**
+ * This interface may be implemented by an external debugging tool
+ */
+
+public interface Debugger {
+
+ /**
+ * Create a SlotManager
+ * @return the SlotManager
+ */
+
+ public SlotManager makeSlotManager();
+
+}
+
diff --git a/sf/saxon/expr/instruct/Doctype.java b/sf/saxon/expr/instruct/Doctype.java
new file mode 100644
index 0000000..3482926
--- /dev/null
+++ b/sf/saxon/expr/instruct/Doctype.java
@@ -0,0 +1,369 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.InterpretedExpressionCompiler;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.MonoIterator;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.TinyBuilder;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.Type;
+
+import java.util.Iterator;
+
+/**
+ * A saxon:doctype element in the stylesheet.
+ */
+
+public class Doctype extends Instruction {
+
+ private Expression content;
+
+ public Doctype(Expression content) {
+ this.content = content;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ content = visitor.simplify(content);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ content = visitor.typeCheck(content, contextItemType);
+ adoptChildExpression(content);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ content = visitor.optimize(content, contextItemType);
+ adoptChildExpression(content);
+ return this;
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(content);
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ throw new UnsupportedOperationException("Doctype.copy()");
+ }
+
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (content == original) {
+ content = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ *
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ content = doPromotion(content, offer);
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true.
+ */
+
+ public final boolean createsNewNodes() {
+ return true;
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.SAXON_DOCTYPE;
+ }
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+
+ SequenceReceiver out = context.getReceiver();
+ PipelineConfiguration pipe = controller.makePipelineConfiguration();
+ pipe.setHostLanguage(getContainer().getHostLanguage());
+ TinyBuilder builder = new TinyBuilder(pipe);
+ builder.open();
+ builder.startDocument(0);
+ context.changeOutputDestination(builder, null);
+ content.process(context);
+ builder.endDocument();
+ builder.close();
+ context.setReceiver(out);
+ DocumentInfo dtdRoot = (DocumentInfo)builder.getCurrentRoot();
+
+ SequenceIterator children = dtdRoot.iterateAxis(AxisInfo.CHILD);
+ NodeInfo docType = (NodeInfo)children.next();
+ if (docType == null || !("doctype".equals(docType.getLocalPart()))) {
+ XPathException e = new XPathException("saxon:doctype instruction must contain dtd:doctype");
+ e.setXPathContext(context);
+ throw e;
+ }
+ String name = Navigator.getAttributeValue(docType, "", "name");
+ String system = Navigator.getAttributeValue(docType, "", "system");
+ String publicid = Navigator.getAttributeValue(docType, "", "public");
+
+ if (name == null) {
+ XPathException e = new XPathException("dtd:doctype must have a name attribute");
+ e.setXPathContext(context);
+ throw e;
+ }
+
+ write(out, "<!DOCTYPE " + name + ' ');
+ if (system != null) {
+ if (publicid != null) {
+ write(out, "PUBLIC \"" + publicid + "\" \"" + system + '\"');
+ } else {
+ write(out, "SYSTEM \"" + system + '\"');
+ }
+ }
+
+ boolean openSquare = false;
+ children = docType.iterateAxis(AxisInfo.CHILD);
+
+ NodeInfo child = (NodeInfo)children.next();
+ if (child != null) {
+ write(out, " [");
+ openSquare = true;
+ }
+
+ while (child != null) {
+ String localname = child.getLocalPart();
+
+ if ("element".equals(localname)) {
+ String elname = Navigator.getAttributeValue(child, "", "name");
+ String content = Navigator.getAttributeValue(child, "", "content");
+ if (elname == null) {
+ XPathException e = new XPathException("dtd:element must have a name attribute");
+ e.setXPathContext(context);
+ throw e;
+ }
+ if (content == null) {
+ XPathException e = new XPathException("dtd:element must have a content attribute");
+ e.setXPathContext(context);
+ throw e;
+ }
+ write(out, "\n <!ELEMENT " + elname + ' ' + content + '>');
+
+ } else if (localname.equals("attlist")) {
+ String elname = Navigator.getAttributeValue(child, "", "element");
+ if (elname == null) {
+ XPathException e = new XPathException("dtd:attlist must have an attribute named 'element'");
+ e.setXPathContext(context);
+ throw e;
+ }
+ write(out, "\n <!ATTLIST " + elname + ' ');
+
+ SequenceIterator attributes = child.iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo attDef = (NodeInfo)attributes.next();
+ if (attDef == null) {
+ break;
+ }
+
+ if ("attribute".equals(attDef.getLocalPart())) {
+
+ String atname = Navigator.getAttributeValue(attDef, "", "name");
+ String type = Navigator.getAttributeValue(attDef, "", "type");
+ String value = Navigator.getAttributeValue(attDef, "", "value");
+ if (atname == null) {
+ XPathException e = new XPathException("dtd:attribute must have a name attribute");
+ e.setXPathContext(context);
+ throw e;
+ }
+ if (type == null) {
+ XPathException e = new XPathException("dtd:attribute must have a type attribute");
+ e.setXPathContext(context);
+ throw e;
+ }
+ if (value == null) {
+ XPathException e = new XPathException("dtd:attribute must have a value attribute");
+ e.setXPathContext(context);
+ throw e;
+ }
+ write(out, "\n " + atname + ' ' + type + ' ' + value);
+ } else {
+ XPathException e = new XPathException("Unrecognized element within dtd:attlist");
+ e.setXPathContext(context);
+ throw e;
+ }
+ }
+ write(out, ">");
+
+ } else if (localname.equals("entity")) {
+
+ String entname = Navigator.getAttributeValue(child, "", "name");
+ String parameter = Navigator.getAttributeValue(child, "", "parameter");
+ String esystem = Navigator.getAttributeValue(child, "", "system");
+ String epublicid = Navigator.getAttributeValue(child, "", "public");
+ String notation = Navigator.getAttributeValue(child, "", "notation");
+
+ if (entname == null) {
+ XPathException e = new XPathException("dtd:entity must have a name attribute");
+ e.setXPathContext(context);
+ throw e;
+ }
+
+ // we could do a lot more checking now...
+
+ write(out, "\n <!ENTITY ");
+ if ("yes".equals(parameter)) {
+ write(out, "% ");
+ }
+ write(out, entname + ' ');
+ if (esystem != null) {
+ if (epublicid != null) {
+ write(out, "PUBLIC \"" + epublicid + "\" \"" + esystem + "\" ");
+ } else {
+ write(out, "SYSTEM \"" + esystem + "\" ");
+ }
+ }
+ if (notation != null) {
+ write(out, "NDATA " + notation + ' ');
+ }
+
+ SequenceIterator contents = child.iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo content = (NodeInfo)contents.next();
+ if (content == null) {
+ break;
+ }
+ content.copy(out, 0, locationId);
+ }
+ write(out, ">");
+
+ } else if (localname.equals("notation")) {
+ String notname = Navigator.getAttributeValue(child, "", "name");
+ String nsystem = Navigator.getAttributeValue(child, "", "system");
+ String npublicid = Navigator.getAttributeValue(child, "", "public");
+ if (notname == null) {
+ XPathException e = new XPathException("dtd:notation must have a name attribute");
+ e.setXPathContext(context);
+ throw e;
+ }
+ if ((nsystem == null) && (npublicid == null)) {
+ XPathException e = new XPathException("dtd:notation must have a system attribute or a public attribute");
+ e.setXPathContext(context);
+ throw e;
+ }
+ write(out, "\n <!NOTATION " + notname);
+ if (npublicid != null) {
+ write(out, " PUBLIC \"" + npublicid + "\" ");
+ if (nsystem != null) {
+ write(out, '\"' + nsystem + "\" ");
+ }
+ } else {
+ write(out, " SYSTEM \"" + nsystem + "\" ");
+ }
+ write(out, ">");
+ } else if (child.getNodeKind() == Type.TEXT) {
+ write(out, child.getStringValue());
+ } else {
+ XPathException e = new XPathException("Unrecognized element " + localname + " in DTD output");
+ e.setXPathContext(context);
+ throw e;
+ }
+ child = (NodeInfo)children.next();
+ }
+
+ if (openSquare) {
+ write(out, "\n]");
+ }
+ write(out, ">\n");
+
+ return null;
+
+ }
+
+ private void write(Receiver out, String s) throws XPathException {
+ out.characters(s, locationId, ReceiverOptions.DISABLE_ESCAPING);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Doctype expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new InterpretedExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("saxonDoctype");
+ out.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/DocumentInstr.java b/sf/saxon/expr/instruct/DocumentInstr.java
new file mode 100644
index 0000000..43802d4
--- /dev/null
+++ b/sf/saxon/expr/instruct/DocumentInstr.java
@@ -0,0 +1,417 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.DocumentInstrCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.DocumentInstrAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.evpull.BracketedDocumentIterator;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.evpull.SingletonEventIterator;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.functions.StringJoin;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.TextFragmentValue;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+import java.util.Stack;
+
+
+
+/**
+ * An instruction to create a document node. This corresponds to the xsl:document-node
+ * instruction in XSLT. It is also used to support the document node constructor
+ * expression in XQuery, and is generated implicitly within an xsl:variable
+ * that constructs a temporary tree.
+ *
+ * <p>Conceptually it represents an XSLT instruction xsl:document-node,
+ * with no attributes, whose content is a complex content constructor for the
+ * children of the document node.</p>
+ */
+
+public class DocumentInstr extends ParentNodeConstructor {
+
+ private boolean textOnly;
+ /*@Nullable*/ private String constantText;
+
+ /**
+ * Create a document constructor instruction
+ * @param textOnly true if the content contains text nodes only
+ * @param constantText if the content contains text nodes only and the text is known at compile time,
+ * supplies the textual content
+ * @param baseURI the base URI of the instruction
+ */
+
+ public DocumentInstr(boolean textOnly,
+ String constantText,
+ String baseURI) {
+ this.textOnly = textOnly;
+ this.constantText = constantText;
+ setBaseURI(baseURI);
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is prefered. For instructions this is the process() method.
+ */
+
+ public int getImplementationMethod() {
+ return Expression.EVALUATE_METHOD;
+ }
+
+ /**
+ * Determine whether this is a "text only" document: essentially, an XSLT xsl:variable that contains
+ * a single text node or xsl:value-of instruction.
+ * @return true if this is a text-only document
+ */
+
+ public boolean isTextOnly() {
+ return textOnly;
+ }
+
+ /**
+ * For a text-only instruction, determine if the text value is fixed and if so return it;
+ * otherwise return null
+ * @return the fixed text value if appropriate; otherwise null
+ */
+
+ public /*@Nullable*/ CharSequence getConstantText() {
+ return constantText;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression rewriting
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ setLazyConstruction(visitor.getConfiguration().getBooleanProperty(FeatureKeys.LAZY_CONSTRUCTION_MODE));
+ return super.simplify(visitor);
+ }
+
+
+
+ /**
+ * Check statically that the sequence of child instructions doesn't violate any obvious constraints
+ * on the content of the node
+ * @param env the static context
+ * @throws XPathException
+ */
+
+ protected void checkContentSequence(StaticContext env) throws XPathException {
+ checkContentSequence(env, content, getValidationOptions());
+ }
+
+ protected static void checkContentSequence(StaticContext env, Expression content, ParseOptions validationOptions)
+ throws XPathException {
+ Expression[] components;
+ if (content instanceof Block) {
+ components = ((Block)content).getChildren();
+ } else {
+ components = new Expression[] {content};
+ }
+ int validation = (validationOptions == null ? Validation.PRESERVE : validationOptions.getSchemaValidationMode());
+ SchemaType type = (validationOptions == null ? null : validationOptions.getTopLevelType());
+ int elementCount = 0;
+ boolean isXSLT = content.getHostLanguage() == Configuration.XSLT;
+ TypeHierarchy th = env.getConfiguration().getTypeHierarchy();
+ for (Expression component : components) {
+ ItemType it = component.getItemType(th);
+ if (it instanceof NodeTest) {
+ int possibleNodeKinds = ((NodeTest) it).getNodeKindMask();
+ if (possibleNodeKinds == 1 << Type.ATTRIBUTE) {
+ XPathException de = new XPathException("Cannot create an attribute node whose parent is a document node");
+ de.setErrorCode(isXSLT ? "XTDE0420" : "XPTY0004");
+ de.setLocator(component);
+ throw de;
+ } else if (possibleNodeKinds == 1 << Type.NAMESPACE) {
+ XPathException de = new XPathException("Cannot create a namespace node whose parent is a document node");
+ de.setErrorCode(isXSLT ? "XTDE0420" : "XQTY0024");
+ de.setLocator(component);
+ throw de;
+ }
+ if (possibleNodeKinds == 1 << Type.ELEMENT) {
+ elementCount++;
+ if (elementCount > 1 &&
+ (validation == Validation.STRICT || validation == Validation.LAX || type != null)) {
+ XPathException de = new XPathException("A valid document must have only one child element");
+ if (isXSLT) {
+ de.setErrorCode("XTTE1550");
+ } else {
+ de.setErrorCode("XQDY0061");
+ }
+ de.setLocator(component);
+ throw de;
+ }
+ if (validation == Validation.STRICT && component instanceof FixedElement) {
+ SchemaDeclaration decl = env.getConfiguration().getElementDeclaration(
+ ((FixedElement) component).getElementName().getFingerprint());
+ if (decl != null) {
+ ((FixedElement) component).getContentExpression().
+ checkPermittedContents(decl.getType(), env, true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+ @Override
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ p |= StaticProperty.SINGLE_DOCUMENT_NODESET;
+ if (getValidationAction() == Validation.SKIP) {
+ p |= StaticProperty.ALL_NODES_UNTYPED;
+ }
+ return p;
+ }
+
+ /**
+ * In the case of a text-only instruction (xsl:variable containing a text node or one or more xsl:value-of
+ * instructions), return an expression that evaluates to the textual content as an instance of xs:untypedAtomic
+ * @return an expression that evaluates to the textual content
+ */
+
+ public Expression getStringValueExpression() {
+ if (textOnly) {
+ if (constantText != null) {
+ return new StringLiteral(new UntypedAtomicValue(constantText));
+ } else if (content instanceof ValueOf) {
+ return ((ValueOf)content).convertToCastAsString();
+ } else {
+ StringJoin fn = (StringJoin) SystemFunctionCall.makeSystemFunction(
+ "string-join", new Expression[]{content, new StringLiteral(StringValue.EMPTY_STRING)});
+ CastExpression cast = new CastExpression(fn, BuiltInAtomicType.UNTYPED_ATOMIC, false);
+ ExpressionTool.copyLocationInfo(this, cast);
+ return cast;
+ }
+ } else {
+ throw new AssertionError("getStringValueExpression() called on non-text-only document instruction");
+ }
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ DocumentInstr doc = new DocumentInstr(textOnly, constantText, getBaseURI());
+ doc.setContentExpression(content.copy());
+ doc.setValidationAction(getValidationAction(), getSchemaType());
+ doc.setLazyConstruction(isLazyConstruction());
+ return doc;
+ }
+
+ /**
+ * Get the item type
+ * @param th The TypeHierarchy
+ * @return the in
+ */
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return NodeKindTest.DOCUMENT;
+ }
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ if (preservingTypes && !textOnly) {
+ SequenceReceiver out = context.getReceiver();
+ out.startDocument(0);
+ content.process(context);
+ out.endDocument();
+ return null;
+ } else {
+ Item item = evaluateItem(context);
+ if (item != null) {
+ SequenceReceiver out = context.getReceiver();
+ out.append(item, locationId, NodeInfo.ALL_NAMESPACES);
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Evaluate as an item.
+ */
+
+ public DocumentInfo evaluateItem(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ if (isLazyConstruction() && (
+ !controller.getExecutable().isSchemaAware() ||
+ (getValidationAction() == Validation.PRESERVE && getSchemaType() == null))) {
+ return context.getConfiguration().makeUnconstructedDocument(this, context);
+ //return new UnconstructedDocument(this, context);
+ } else {
+
+ DocumentInfo root;
+ if (textOnly) {
+ CharSequence textValue;
+ if (constantText != null) {
+ textValue = constantText;
+ } else {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ SequenceIterator iter = content.iterate(context);
+ while (true) {
+ Item item = iter.next();
+ if (item==null) break;
+ sb.append(item.getStringValueCS());
+ }
+ textValue = sb.condense();
+ }
+ root = new TextFragmentValue(textValue, getBaseURI());
+ ((TextFragmentValue)root).setConfiguration(controller.getConfiguration());
+ } else {
+ try {
+ SequenceReceiver saved = context.getReceiver();
+
+ Builder builder = controller.makeBuilder();
+
+ builder.setBaseURI(getBaseURI());
+ builder.setTiming(false);
+
+ PipelineConfiguration pipe = controller.makePipelineConfiguration();
+ pipe.setHostLanguage(getHostLanguage());
+ //pipe.setBaseURI(baseURI);
+ builder.setPipelineConfiguration(pipe);
+
+ context.changeOutputDestination(builder, getValidationOptions());
+ Receiver out = context.getReceiver();
+ out.open();
+ out.startDocument(0);
+
+ content.process(context);
+
+ out.endDocument();
+ out.close();
+ context.setReceiver(saved);
+ root = (DocumentInfo)builder.getCurrentRoot();
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+ return root;
+ }
+ }
+
+ public EventIterator iterateEvents(XPathContext context) throws XPathException {
+ if (getValidationAction() != Validation.PRESERVE) {
+ // Schema validation can't be done in pull mode
+ return new SingletonEventIterator(evaluateItem(context));
+ }
+ return new BracketedDocumentIterator(content.iterateEvents(context));
+
+ }
+
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ * (the string "document-constructor")
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_DOCUMENT;
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("documentNode");
+ out.emitAttribute("validation", Validation.toString(getValidationAction()));
+ final SchemaType schemaType = getSchemaType();
+ if (schemaType != null) {
+ out.emitAttribute("type", schemaType.getDescription());
+ }
+ content.explain(out);
+ out.endElement();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the DocumentInstr expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new DocumentInstrCompiler();
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public DocumentInstrAdjunct getStreamingAdjunct() {
+ return new DocumentInstrAdjunct();
+ }
+
+ /**
+ * In streaming mode, process the first half of the instruction (to start a new document or element)
+ * @param contextStack the dynamic evaluation context
+ * @param state a stack on which the instruction can save state information for use during the corresponding
+ */
+
+ public void processLeft(Stack<XPathContext> contextStack, Stack<Object> state) throws XPathException {
+ DocumentInstrAdjunct.processLeft(contextStack, state);
+ }
+
+ /**
+ * In streaming mode, proecss the right half of the instruction (to end a new document or element)
+ * @param contextStack the dynamic evaluation context
+ * @param state a stack on which the instruction can save state information for use during the corresponding
+ */
+
+ public void processRight(Stack<XPathContext> contextStack, Stack<Object> state) throws XPathException {
+ DocumentInstrAdjunct.processRight(contextStack, state);
+ }
+
+ //#endif
+}
+
diff --git a/sf/saxon/expr/instruct/DummyNamespaceResolver.java b/sf/saxon/expr/instruct/DummyNamespaceResolver.java
new file mode 100644
index 0000000..e8a305e
--- /dev/null
+++ b/sf/saxon/expr/instruct/DummyNamespaceResolver.java
@@ -0,0 +1,67 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+import net.sf.saxon.expr.PairIterator;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NamespaceResolver;
+
+import java.io.Serializable;
+import java.util.Iterator;
+
+/**
+ * A dummy namespace resolver used when validating QName-valued attributes written to
+ * the result tree. The namespace node might be created after the initial validation
+ * of the attribute, so in the first round of validation we only check the lexical form
+ * of the value, and we defer prefix checks until later.
+ */
+
+public final class DummyNamespaceResolver implements Serializable, NamespaceResolver {
+
+ private final static DummyNamespaceResolver THE_INSTANCE = new DummyNamespaceResolver();
+
+ /**
+ * Return the singular instance of this class
+ * @return the singular instance
+ */
+
+ public static DummyNamespaceResolver getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private DummyNamespaceResolver() {}
+
+
+ /**
+ * Get the namespace URI corresponding to a given prefix.
+ * @param prefix the namespace prefix
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is ""
+ * @return the uri for the namespace, or null if the prefix is not in scope
+ */
+
+ public String getURIForPrefix(String prefix, boolean useDefault) {
+ if (prefix.length()==0) {
+ return NamespaceConstant.NULL;
+ } else if ("xml".equals(prefix)) {
+ return NamespaceConstant.XML;
+ } else {
+ // this is a dummy namespace resolver, we don't actually know the URI
+ return NamespaceConstant.NULL;
+ }
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ return new PairIterator<String>("", "xml");
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/ElementCreator.java b/sf/saxon/expr/instruct/ElementCreator.java
new file mode 100644
index 0000000..cac708c
--- /dev/null
+++ b/sf/saxon/expr/instruct/ElementCreator.java
@@ -0,0 +1,569 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.stream.adjunct.ElementInstrAdjunct;
+import com.saxonica.stream.adjunct.StreamingAdjunct;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.*;
+import net.sf.saxon.evpull.*;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Cardinality;
+
+import java.util.Iterator;
+import java.util.Stack;
+
+
+
+
+
+/**
+ * An instruction that creates an element node. There are two subtypes, FixedElement
+ * for use where the name is known statically, and Element where it is computed
+ * dynamically. To allow use in both XSLT and XQuery, the class acts both as an
+ * Instruction and as an Expression.
+ */
+
+public abstract class ElementCreator extends ParentNodeConstructor {
+
+ /**
+ * The inheritNamespaces flag indicates that the namespace nodes on the element created by this instruction
+ * are to be inherited (copied) on the children of this element. That is, if this flag is false, the child
+ * elements must carry a namespace undeclaration for all the namespaces on the parent, unless they are
+ * redeclared in some way.
+ */
+
+ protected boolean inheritNamespaces = true;
+
+ /**
+ * The onEmpty expression is used in XSLT 3.0 to supply an alternative result to be returned
+ * when the content of the element (including attributes) is empty. The most common case is
+ * to return an empty sequence instead of constructing the element.
+ */
+
+ protected Expression onEmpty = null;
+
+ /**
+ * Construct an ElementCreator. Exists for the benefit of subclasses.
+ */
+
+ public ElementCreator() {
+ }
+
+ @Override
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ if (onEmpty != null) {
+ onEmpty = visitor.simplify(onEmpty);
+ }
+ return super.simplify(visitor);
+ }
+
+ @Override
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (onEmpty != null) {
+ onEmpty = visitor.typeCheck(onEmpty, contextItemType);
+ }
+ return super.typeCheck(visitor, contextItemType);
+ }
+
+ @Override
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (onEmpty != null) {
+ onEmpty = visitor.optimize(onEmpty, contextItemType);
+ }
+ return super.optimize(visitor, contextItemType);
+ }
+
+ /**
+ * Get the item type of the value returned by this instruction
+ *
+ * @param th the type hierarchy cache
+ * @return the item type
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (onEmpty == null) {
+ return NodeKindTest.ELEMENT;
+ } else {
+ return Type.getCommonSuperType(NodeKindTest.ELEMENT, onEmpty.getItemType(th), th);
+ }
+ }
+
+ @Override
+ public int getCardinality() {
+ if (onEmpty == null) {
+ return StaticProperty.EXACTLY_ONE;
+ } else {
+ return Cardinality.union(StaticProperty.EXACTLY_ONE, onEmpty.getCardinality());
+ }
+ }
+
+ @Override
+ public Iterator<Expression> iterateSubExpressions() {
+ if (onEmpty == null) {
+ return super.iterateSubExpressions();
+ } else {
+ return new PairIterator<Expression>(content, onEmpty);
+ }
+ }
+
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ if (onEmpty == null) {
+ return super.iterateSubExpressionInfo();
+ } else {
+ return new PairIterator<SubExpressionInfo>(
+ new SubExpressionInfo(content, true, false, NODE_VALUE_CONTEXT),
+ new SubExpressionInfo(onEmpty, true, false, NAVIGATION_CONTEXT));
+ }
+ }
+
+ /**
+ * Determine whether the inherit namespaces flag is set
+ *
+ * @return true if namespaces constructed on a parent element are to be inherited by its children
+ */
+
+ public boolean isInheritNamespaces() {
+ return inheritNamespaces;
+ }
+
+ /**
+ * Set the on-empty expression, which defines the value to be returned if the element would otherwise
+ * be empty
+ *
+ * @param onEmpty the expression to be evaluated if the element would otherwise be empty
+ */
+
+ public void setOnEmpty(Expression onEmpty) {
+ this.onEmpty = onEmpty;
+ }
+
+ /**
+ * Get the on-empty expression, which defines the value to be returned if the element would otherwise
+ * be empty
+ *
+ * @return the on-empty expression if there is one, or null otherwise
+ */
+
+ public Expression getOnEmpty() {
+ return onEmpty;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties() |
+ StaticProperty.SINGLE_DOCUMENT_NODESET;
+ if (getValidationAction() == Validation.STRIP) {
+ p |= StaticProperty.ALL_NODES_UNTYPED;
+ }
+ if (onEmpty != null) {
+ return p & onEmpty.getSpecialProperties();
+ } else {
+ return p;
+ }
+ }
+
+ /**
+ * Suppress validation on contained element constructors, on the grounds that the parent element
+ * is already performing validation. The default implementation does nothing.
+ */
+
+ public void suppressValidation(int parentValidationMode) {
+ if (getValidationAction() == parentValidationMode && getSchemaType() == null) {
+ // TODO: is this safe? e.g. if the child has validation=strict but matches a skip wildcard in the parent
+ setValidationAction(Validation.PRESERVE, null);
+ }
+ }
+
+ /**
+ * Check statically whether the content of the element creates attributes or namespaces
+ * after creating any child nodes
+ *
+ * @param env the static context
+ * @throws XPathException
+ */
+
+ protected void checkContentSequence(StaticContext env) throws XPathException {
+ if (content instanceof Block) {
+ TypeHierarchy th = env.getConfiguration().getTypeHierarchy();
+ Expression[] components = ((Block) content).getChildren();
+ boolean foundChild = false;
+ boolean foundPossibleChild = false;
+ int childNodeKinds = (1 << Type.TEXT | 1 << Type.ELEMENT | 1 << Type.COMMENT | 1 << Type.PROCESSING_INSTRUCTION);
+ for (Expression component : components) {
+
+ ItemType it = component.getItemType(th);
+ if (it instanceof NodeTest) {
+ boolean maybeEmpty = Cardinality.allowsZero(component.getCardinality());
+ int possibleNodeKinds = ((NodeTest) it).getNodeKindMask();
+ if ((possibleNodeKinds & 1 << Type.TEXT) != 0) {
+ // the text node might turn out to be zero-length. If that's a possibility,
+ // then we only issue a warning. Also, we need to completely ignore a known
+ // zero-length text node, which is included to prevent space-separation
+ // in an XQuery construct like <a>{@x}{@y}</b>
+ if (component instanceof ValueOf &&
+ ((ValueOf) component).select instanceof StringLiteral) {
+ String value = (((StringLiteral) ((ValueOf) component).select).getStringValue());
+ if (value.length() == 0) {
+ // continue; // not an error
+ } else {
+ foundChild = true;
+ }
+ } else {
+ foundPossibleChild = true;
+ }
+ } else if ((possibleNodeKinds & ~childNodeKinds) == 0) {
+ if (maybeEmpty) {
+ foundPossibleChild = true;
+ } else {
+ foundChild = true;
+ }
+ } else if (foundChild && possibleNodeKinds == 1 << Type.ATTRIBUTE && !maybeEmpty) {
+ XPathException de = new XPathException(
+ "Cannot create an attribute node after creating a child of the containing element");
+ de.setErrorCode(isXSLT() ? "XTDE0410" : "XQTY0024");
+ de.setLocator(component);
+ throw de;
+ } else if (foundChild && possibleNodeKinds == 1 << Type.NAMESPACE && !maybeEmpty) {
+ XPathException de = new XPathException(
+ "Cannot create a namespace node after creating a child of the containing element");
+ de.setErrorCode(isXSLT() ? "XTDE0410" : "XQTY0024");
+ de.setLocator(component);
+ throw de;
+ } else if ((foundChild || foundPossibleChild) && possibleNodeKinds == 1 << Type.ATTRIBUTE) {
+ env.issueWarning(
+ "Creating an attribute here will fail if previous instructions create any children",
+ component);
+ } else if ((foundChild || foundPossibleChild) && possibleNodeKinds == 1 << Type.NAMESPACE) {
+ env.issueWarning(
+ "Creating a namespace node here will fail if previous instructions create any children",
+ component);
+ }
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Determine (at run-time) the name code of the element being constructed
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param copiedNode for the benefit of xsl:copy, the node being copied; otherwise null
+ * @return the integer name code representing the element name
+ * @throws XPathException if a failure occurs
+ */
+
+ public abstract NodeName getElementName(XPathContext context, /*@Nullable*/ NodeInfo copiedNode) throws XPathException;
+
+ /**
+ * Get the base URI for the element being constructed
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param copiedNode the node being copied (for xsl:copy), otherwise null
+ * @return the base URI of the constructed element
+ */
+
+ public abstract String getNewBaseURI(XPathContext context, NodeInfo copiedNode);
+
+ /**
+ * Callback to output namespace nodes for the new element. This method is responsible
+ * for ensuring that a namespace node is always generated for the namespace of the element
+ * name itself.
+ *
+ * @param context The execution context
+ * @param receiver the Receiver where the namespace nodes are to be written
+ * @param nameCode the name code of the element being created
+ * @param copiedNode the node being copied (for xsl:copy) or null otherwise
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public abstract void outputNamespaceNodes(
+ XPathContext context, Receiver receiver, NodeName nameCode, /*@Nullable*/ NodeInfo copiedNode)
+ throws XPathException;
+
+ /**
+ * Callback to get a list of the intrinsic namespaces that need to be generated for the element.
+ *
+ * @return the set of namespace bindings.
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error occurs
+ */
+
+ public NamespaceBinding[] getActiveNamespaces() throws XPathException {
+ return null;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is prefered. For instructions this is the process() method.
+ */
+
+ public int getImplementationMethod() {
+ return Expression.PROCESS_METHOD | Expression.EVALUATE_METHOD;
+ }
+
+ public EventIterator iterateEvents(XPathContext context) throws XPathException {
+ return iterateEvents(context, null);
+ }
+
+ protected EventIterator iterateEvents(XPathContext context, /*@Nullable*/ NodeInfo copiedNode) throws XPathException {
+ if (!preservingTypes && getValidationAction() != Validation.STRIP) {
+ // Schema validation can't be done in pull mode
+ return new SingletonEventIterator(evaluateItem(context));
+ }
+ if (onEmpty != null) {
+ // The on-empty attribute can't be handled in pull mode
+ return new SingletonEventIterator(evaluateItem(context));
+ }
+ final Controller controller = context.getController();
+ assert controller != null;
+ StartElementEvent start = new StartElementEvent(controller.makePipelineConfiguration());
+ start.setElementName(getElementName(context, copiedNode));
+ start.setTypeCode(getValidationAction() == Validation.PRESERVE ? AnyType.getInstance() : Untyped.getInstance());
+ start.setLocalNamespaces(getActiveNamespaces());
+ start.setLocationId(locationId);
+ EventIterator result = new BracketedElementIterator(
+ start, content.iterateEvents(context), EndElementEvent.getInstance());
+ if (getValidationAction() == Validation.STRIP && controller.getExecutable().isSchemaAware()) {
+ return new EventAnnotationStripper(result);
+ } else {
+ return result;
+ }
+ }
+
+ /**
+ * Evaluate the instruction to produce a new element node. This method is typically used when there is
+ * a parent element or document in a result tree, to which the new element is added.
+ *
+ * @param context XPath dynamic evaluation context
+ * @return null (this instruction never returns a tail call)
+ * @throws XPathException
+ */
+ public TailCall processLeavingTail(XPathContext context)
+ throws XPathException {
+ return processLeavingTail(context, null);
+ }
+
+ /**
+ * Evaluate the instruction to produce a new element node. This method is typically used when there is
+ * a parent element or document in a result tree, to which the new element is added.
+ *
+ * @param context XPath dynamic evaluation context
+ * @param copiedNode null except in the case of xsl:copy, when it is the node being copied; otherwise null
+ * @return null (this instruction never returns a tail call)
+ * @throws XPathException if a dynamic error occurs
+ */
+ public final TailCall processLeavingTail(XPathContext context, /*@Nullable*/ NodeInfo copiedNode)
+ throws XPathException {
+
+ try {
+
+ NodeName elemName = getElementName(context, copiedNode);
+ SchemaType typeCode = (getValidationAction() == Validation.PRESERVE ? AnyType.getInstance() : Untyped.getInstance());
+
+ SequenceReceiver out = context.getReceiver();
+ SequenceReceiver saved = out;
+ boolean pop = false;
+ Receiver elemOut = out;
+ if (!preservingTypes) {
+ ParseOptions options = new ParseOptions(getValidationOptions());
+ options.setTopLevelElement(elemName);
+ Receiver validator = context.getConfiguration().getElementValidator(out, options, locationId);
+
+ if (validator != out) {
+ out = new TreeReceiver(validator);
+ context.setReceiver(out);
+ pop = true;
+ }
+ elemOut = out;
+ }
+
+ if (onEmpty != null) {
+ OnEmptyHandler monitor = new OnEmptyHandler(out, onEmpty, context);
+ context.setReceiver(monitor);
+ pop = true;
+ elemOut = monitor;
+ }
+
+ if (elemOut.getSystemId() == null) {
+ elemOut.setSystemId(getNewBaseURI(context, copiedNode));
+ }
+ int properties = (inheritNamespaces ? 0 : ReceiverOptions.DISINHERIT_NAMESPACES);
+ elemOut.startElement(elemName, typeCode, locationId, properties);
+
+ // output the required namespace nodes via a callback
+
+ outputNamespaceNodes(context, elemOut, elemName, copiedNode);
+
+ // process subordinate instructions to generate attributes and content
+ content.process(context);
+
+ // output the element end tag (which will fail if validation fails)
+ elemOut.endElement();
+
+ if (pop) {
+ context.setReceiver(saved);
+ }
+ return null;
+
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+ /**
+ * Evaluate the constructor, returning the constructed element node. If lazy construction
+ * mode is in effect, then an UnconstructedParent object is returned instead.
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ if (isLazyConstruction() && preservingTypes && onEmpty == null) {
+ return context.getConfiguration().makeUnconstructedElement(this, context);
+ } else {
+ NodeInfo node = constructElement(context, null);
+ // TODO: recover from validation errors that might have occurred
+ if (onEmpty != null && !node.hasChildNodes() && node.iterateAxis(AxisInfo.ATTRIBUTE).next() == null) {
+ return onEmpty.evaluateItem(context);
+ } else {
+ return node;
+ }
+ }
+ }
+
+ /**
+ * Construct the element node as a free-standing (parentless) node in a tiny tree
+ *
+ * @param context XPath dynamic evaluation context
+ * @param copiedNode for the benefit of xsl:copy, the node being copied
+ * @return the constructed element node
+ * @throws XPathException if a dynamic error occurs
+ */
+ private NodeInfo constructElement(XPathContext context, /*@Nullable*/ NodeInfo copiedNode) throws XPathException {
+ try {
+ Controller controller = context.getController();
+ assert controller != null;
+ SequenceReceiver saved = context.getReceiver();
+ SequenceOutputter seq = controller.allocateSequenceOutputter(1);
+ seq.getPipelineConfiguration().setHostLanguage(getHostLanguage());
+
+ NodeName elemName = getElementName(context, copiedNode);
+ SchemaType typeCode = (getValidationAction() == Validation.PRESERVE ? AnyType.getInstance() : Untyped.getInstance());
+
+ SequenceReceiver ini = seq;
+ if (!preservingTypes) {
+ ParseOptions options = new ParseOptions(getValidationOptions());
+ options.setTopLevelElement(elemName);
+ Receiver validator = controller.getConfiguration().getElementValidator(ini, options, locationId);
+
+ if (ini.getSystemId() == null) {
+ ini.setSystemId(getNewBaseURI(context, copiedNode));
+ }
+ if (validator == ini) {
+ context.setReceiver(ini);
+ } else {
+ TreeReceiver tr = new TreeReceiver(validator);
+ tr.setPipelineConfiguration(seq.getPipelineConfiguration());
+ context.setReceiver(tr);
+ ini = tr;
+ }
+ } else {
+ context.setReceiver(ini);
+ if (ini.getSystemId() == null) {
+ ini.setSystemId(getNewBaseURI(context, copiedNode));
+ }
+ }
+
+ ini.open();
+ int properties = (inheritNamespaces ? 0 : ReceiverOptions.DISINHERIT_NAMESPACES);
+ ini.startElement(elemName, typeCode, locationId, properties);
+
+ // output the namespace nodes for the new element
+ outputNamespaceNodes(context, ini, elemName, null);
+
+ content.process(context);
+
+ ini.endElement();
+ ini.close();
+ context.setReceiver(saved);
+
+ // the constructed element is the first and only item in the sequence
+ NodeInfo result = (NodeInfo) seq.popLastItem();
+ seq.reset();
+ return result;
+
+ } catch (XPathException err) {
+ if (err instanceof ValidationException) {
+ ((ValidationException) err).setSourceLocator(this);
+ ((ValidationException) err).setSystemId(getSystemId());
+ }
+ err.maybeSetLocation(this);
+ err.maybeSetContext(context);
+ throw err;
+ }
+ }
+
+//#ifdefined BYTECODE
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public StreamingAdjunct getStreamingAdjunct() {
+ return new ElementInstrAdjunct();
+ }
+
+ /**
+ * In streaming mode, process the first half of the instruction (to start a new document or element)
+ *
+ * @param contextStack the dynamic evaluation context
+ * @param state a stack on which the instruction can save state information for use during the corresponding
+ */
+
+ public void processLeft(Stack<XPathContext> contextStack, Stack<Object> state) throws XPathException {
+ ElementInstrAdjunct.processLeft(this, contextStack, state, null);
+ }
+
+
+ /**
+ * In streaming mode, process the right half of the instruction (to end a new document or element)
+ *
+ * @param contextStack the stack of XPath context objects for the current execution state
+ * @param state a stack on which the instruction can save state information for use during the corresponding
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public void processRight(Stack<XPathContext> contextStack, Stack<Object> state) throws XPathException {
+ ElementInstrAdjunct.processRight(this, contextStack, state);
+ }
+//#endif
+
+
+}
+
diff --git a/sf/saxon/expr/instruct/Executable.java b/sf/saxon/expr/instruct/Executable.java
new file mode 100644
index 0000000..966442e
--- /dev/null
+++ b/sf/saxon/expr/instruct/Executable.java
@@ -0,0 +1,807 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.CollationMap;
+import net.sf.saxon.expr.ErrorExpression;
+import net.sf.saxon.functions.FunctionLibraryList;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.NoElementsSpaceStrippingRule;
+import net.sf.saxon.om.SpaceStrippingRule;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.query.QueryModule;
+import net.sf.saxon.serialize.CharacterMapIndex;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.KeyManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceType;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * A compiled stylesheet or a query in executable form.
+ * Note that the original stylesheet tree is not retained.
+ */
+
+public class Executable implements Serializable {
+
+ // the Configuration options
+ /*@NotNull*/
+ private Configuration config;
+
+ // definitions of strip/preserve space action
+ private SpaceStrippingRule stripperRules;
+
+ // boolean indicating whether any whitespace is stripped
+ private boolean stripsWhitespace;
+
+ // definitions of keys, including keys created by the optimizer
+ private KeyManager keyManager;
+
+ // the map of slots used for global variables and params
+ private SlotManager globalVariableMap;
+
+ // Index of global variables and parameters, by name
+ // The key is the StructuredQName representing the variable name
+ // The value is the compiled GlobalVariable object.
+ private HashMap<StructuredQName, GlobalVariable> compiledGlobalVariables;
+
+ // default output properties (for the unnamed output format)
+ private Properties defaultOutputProperties;
+
+
+ // count of the maximum number of local variables in the match pattern of any template rule
+ private int largestPatternStackFrame = 0;
+
+ // table of named collations defined in the stylesheet/query
+ private CollationMap collationTable;
+
+ // table of character maps indexed by StructuredQName
+ private CharacterMapIndex characterMapIndex;
+
+ // location map for expressions in this executable
+ private LocationMap locationMap = new LocationMap();
+
+ // hash table of query library modules
+ private HashMap<String, List<QueryModule>> queryLibraryModules;
+
+ // hash set of query module location hints that have been processed
+ private HashSet<String> queryLocationHintsProcessed;
+
+ // flag to indicate that source documents are to have their type annotations stripped
+ private boolean stripsInputTypeAnnotations;
+
+ // list of functions available in the static context
+ private FunctionLibraryList functionLibrary;
+
+ // flag to indicate whether the principal language is for example XSLT or XQuery
+ private int hostLanguage = Configuration.XSLT;
+
+ // flag to indicate whether XPath 3.0 functionality is enabled
+ private boolean allowXPath30 = false;
+
+ // a list of required parameters, identified by the structured QName of their names
+ /*@Nullable*/ private HashSet<StructuredQName> requiredParams = null;
+
+ // Hash table of named (and unnamed) output declarations. This is assembled only
+ // if there is a need for it: that is, if there is a call on xsl:result-document
+ // with a format attribute computed at run-time. The key is a StructuredQName object,
+ // the value is a Properties object
+ /*@Nullable*/ private HashMap<StructuredQName, Properties> outputDeclarations = null;
+
+ // a boolean, true if the executable represents a stylesheet that uses xsl:result-document
+ private boolean createsSecondaryResult = false;
+
+ // a boolean, indicates that the executable is schema-aware. This will true by default only
+ // if it statically imports a schema. If the executable is not schema-aware, then
+ // all input documents must be untyped.
+ private boolean schemaAware = false;
+
+ // The name of a global variable that is coupled to the initial context item. If not null,
+ // the initial context item will be assigned to this variable. Properties of the variable
+ // such as its required type, its default value, and whether it is required or not, thus
+ // apply implicitly to the initial context item.
+ /*@Nullable*/ private StructuredQName initialContextItemVariableName = null;
+
+ /**
+ * Create a new Executable (a collection of stylesheet modules and/or query modules)
+ * @param config the Saxon Configuration
+ */
+
+ public Executable(Configuration config) {
+ setConfiguration(config);
+ }
+
+ /**
+ * Set the configuration
+ * @param config the Configuration
+ */
+
+ public void setConfiguration(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Get the configuration
+ * @return the Configuration
+ */
+
+ /*@NotNull*/
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+
+
+ /**
+ * Set the host language
+ * @param language the host language, as a constant such as {@link net.sf.saxon.Configuration#XSLT} or
+ * {@link net.sf.saxon.Configuration#XQUERY}
+ * @param allowXPath30 true if functionality defined in XPath 3.0 (for example, new casting options) is enabled
+ */
+
+ public void setHostLanguage(int language, boolean allowXPath30) {
+ hostLanguage = language;
+ this.allowXPath30 = allowXPath30;
+ }
+
+ /**
+ * Get the host language
+ *
+ * @return a value identifying the host language: {@link Configuration#XQUERY} or {@link Configuration#XSLT}
+ * or {@link Configuration#JAVA_APPLICATION}
+ */
+
+ public int getHostLanguage() {
+ return hostLanguage;
+ }
+
+ /**
+ * Ask whether XPath 3.0 functionality is enabled
+ * @return true of XPath 3.0 (and/or XSLT 3.0, XQuery 3.0) functionality can be used
+ */
+
+ public boolean isAllowXPath30() {
+ return allowXPath30;
+ }
+
+ /**
+ * Get the library containing all the in-scope functions in the static context
+ *
+ * @return the function libary
+ */
+
+ public FunctionLibraryList getFunctionLibrary() {
+ return functionLibrary;
+ }
+
+ /**
+ * Set the library containing all the in-scope functions in the static context
+ *
+ * @param functionLibrary the function libary
+ */
+
+ public void setFunctionLibrary(FunctionLibraryList functionLibrary) {
+ //System.err.println("***" + this + " setFunctionLib to " + functionLibrary);
+ this.functionLibrary = functionLibrary;
+ }
+
+ /**
+ * Set the index of named character maps
+ *
+ * @param cmi a hash table that maps the names of character maps
+ * to the HashMap objects representing the character maps
+ */
+
+ public void setCharacterMapIndex(CharacterMapIndex cmi) {
+ characterMapIndex = cmi;
+ }
+
+ /**
+ * Get the index of named character maps
+ *
+ * @return the hash table that maps the names of character maps
+ * to the IntHashMap objects representing the character maps
+ */
+
+ public CharacterMapIndex getCharacterMapIndex() {
+ if (characterMapIndex == null) {
+ characterMapIndex = new CharacterMapIndex();
+ }
+ return characterMapIndex;
+ }
+
+ /**
+ * Set the rules determining which nodes are to be stripped from the tree
+ *
+ * @param rules a Mode object containing the whitespace stripping rules. A Mode
+ * is generally a collection of template rules, but it is reused here to represent
+ * a collection of stripping rules.
+ */
+
+ public void setStripperRules(SpaceStrippingRule rules) {
+ stripperRules = rules;
+ }
+
+ /**
+ * Get the rules determining which nodes are to be stripped from the tree
+ *
+ * @return a SpaceStrippingRule object containing the whitespace stripping rules.
+ */
+
+ /*@NotNull*/
+ public SpaceStrippingRule getStripperRules() {
+ return (stripperRules == null ? NoElementsSpaceStrippingRule.getInstance() : stripperRules);
+ }
+
+ /**
+ * Indicate that the stylesheet does some whitespace stripping
+ *
+ * @param strips true if the stylesheet performs whitespace stripping
+ * of one or more elements.
+ */
+
+ public void setStripsWhitespace(boolean strips) {
+ stripsWhitespace = strips;
+ }
+
+ /**
+ * Determine whether this stylesheet does any whitespace stripping
+ *
+ * @return true if the stylesheet performs whitespace stripping
+ * of one or more elements.
+ */
+
+ public boolean stripsWhitespace() {
+ return stripsWhitespace;
+ }
+
+ /**
+ * Set whether source documents are to have their type annotations stripped
+ * @param strips true if type annotations are to be stripped
+ */
+
+ public void setStripsInputTypeAnnotations(boolean strips) {
+ stripsInputTypeAnnotations = strips;
+ }
+
+ /**
+ * Ask whether source documents are to have their type annotations stripped
+ * @return true if type annotations are stripped from source documents
+ */
+
+ public boolean stripsInputTypeAnnotations() {
+ return stripsInputTypeAnnotations;
+ }
+
+ /**
+ * Set the KeyManager which handles key definitions
+ * @param km the KeyManager containing the xsl:key definitions
+ */
+
+ public void setKeyManager(KeyManager km) {
+ keyManager = km;
+ }
+
+ /**
+ * Get the KeyManager which handles key definitions
+ *
+ * @return the KeyManager containing the xsl:key definitions
+ */
+
+ public KeyManager getKeyManager() {
+ if (keyManager == null) {
+ keyManager = new KeyManager(getConfiguration());
+ }
+ return keyManager;
+ }
+
+ /**
+ * Set the default output properties (the properties for the unnamed output format)
+ *
+ * @param properties the output properties to be used when the unnamed output format
+ * is selected
+ */
+
+ public void setDefaultOutputProperties(Properties properties) {
+ defaultOutputProperties = properties;
+ }
+
+ /**
+ * Get the default output properties
+ *
+ * @return the properties for the unnamed output format
+ */
+
+ public Properties getDefaultOutputProperties() {
+ if (defaultOutputProperties == null) {
+ defaultOutputProperties = new Properties();
+ }
+ return defaultOutputProperties;
+ }
+
+ /**
+ * Add a named output format
+ *
+ * @param qName the structured QName of the output format
+ * @param properties the properties of the output format
+ */
+
+ public void setOutputProperties(StructuredQName qName, Properties properties) {
+ if (outputDeclarations == null) {
+ outputDeclarations = new HashMap<StructuredQName, Properties>(5);
+ }
+ outputDeclarations.put(qName, properties);
+ }
+
+ /**
+ * Get a named output format
+ *
+ * @param qName the name of the output format
+ * @return properties the properties of the output format. Return null if there are
+ * no output properties with the given name
+ */
+
+ /*@Nullable*/ public Properties getOutputProperties(StructuredQName qName) {
+ if (outputDeclarations == null) {
+ return null;
+ } else {
+ return outputDeclarations.get(qName);
+ }
+ }
+
+ /**
+ * Set the table of collations
+ *
+ * @param table a hash table that maps collation names (URIs) to objects representing the
+ * collation information
+ */
+
+ public void setCollationMap(CollationMap table) {
+ collationTable = table;
+ }
+
+ /**
+ * Get the table of collations
+ *
+ * @return a hash table that maps collation names (URIs) to objects representing the
+ * collation information
+ */
+
+ public CollationMap getCollationTable() {
+ if (collationTable == null) {
+ collationTable = new CollationMap(config);
+ }
+ return collationTable;
+ }
+
+ /**
+ * Find a named collation.
+ *
+ * @param name identifies the name of the collation required; null indicates that the default
+ * collation is required
+ * @return the requested collation, or null if the collation is not found
+ */
+
+ public StringCollator getNamedCollation(String name) {
+ if (collationTable == null) {
+ collationTable = new CollationMap(config);
+ }
+ return collationTable.getNamedCollation(name);
+ }
+
+ /**
+ * Add an XQuery library module to the configuration. The Executable maintains a table indicating
+ * for each module namespace, the set of modules that have been loaded from that namespace. If a
+ * module import is encountered that specifies no location hint, all the known modules for that
+ * namespace are imported.
+ * @param module the library module to be added to this executable
+ */
+
+ public void addQueryLibraryModule(QueryModule module) {
+ if (queryLibraryModules == null) {
+ queryLibraryModules = new HashMap<String, List<QueryModule>>(5);
+ }
+ String uri = module.getModuleNamespace();
+ List<QueryModule> existing = queryLibraryModules.get(uri);
+ if (existing == null) {
+ existing = new ArrayList<QueryModule>(5);
+ existing.add(module);
+ queryLibraryModules.put(uri, existing);
+ } else {
+ existing.add(module);
+ }
+ }
+
+ /**
+ * Locate the known XQuery library modules for a given module namespace.
+ *
+ * @param namespace the module namespace URI
+ * @return a list of items each of which is the StaticQueryContext representing a module, or
+ * null if the module namespace is unknown
+ */
+
+ /*@Nullable*/ public List<QueryModule> getQueryLibraryModules(String namespace) {
+ if (queryLibraryModules == null) {
+ return null;
+ }
+ return queryLibraryModules.get(namespace);
+ }
+
+ /**
+ * Get the query library module with a given systemID
+ * @param systemId the SystemId of the required module
+ * @param topModule the top-level query module (usually a main module, except when
+ * importing library modules into XSLT)
+ * @return the module with that system id if found, otherwise null
+ */
+
+ /*@Nullable*/ public QueryModule getQueryModuleWithSystemId(/*@NotNull*/ String systemId, /*@NotNull*/ QueryModule topModule) {
+ if (systemId.equals(topModule.getSystemId())) {
+ return topModule;
+ }
+ Iterator miter = getQueryLibraryModules();
+ while (miter.hasNext()) {
+ QueryModule sqc = (QueryModule)miter.next();
+ String uri = sqc.getSystemId();
+ if (uri != null && uri.equals(systemId)) {
+ return sqc;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get an iterator over all the query library modules (does not include the main module)
+ * @return an iterator whose returned items are instances of {@link QueryModule}
+ */
+
+ public Iterator getQueryLibraryModules() {
+ if (queryLibraryModules == null) {
+ return Collections.EMPTY_LIST.iterator();
+ } else {
+ List<QueryModule> modules = new ArrayList<QueryModule>();
+ for (List<QueryModule> queryModules : queryLibraryModules.values()) {
+ modules.addAll(queryModules);
+ }
+ return modules.iterator();
+ }
+ }
+
+ /**
+ * Add a name to the list of query module location hints that have been
+ * processed during the construction of this executable
+ * @param uri the name to be added (the location URI as it appears in the
+ * "import module" declaration, expanded to an absolute URI by resolving against
+ * the base URI, but before passing to the Module Resolver)
+ */
+
+ public void addQueryLocationHintProcessed(String uri) {
+ if (queryLocationHintsProcessed == null) {
+ queryLocationHintsProcessed = new HashSet<String>();
+ }
+ queryLocationHintsProcessed.add(uri);
+ }
+
+ /**
+ * Ask whether a query module location hint has already been processed
+ * @param uri the query location hint (the location URI as it appears in the
+ * "import module" declaration, expanded to an absolute URI by resolving against
+ * the base URI, but before passing to the Module Resolver)
+ * @return true if the location hint has already been processed
+ */
+
+ public boolean isQueryLocationHintProcessed(String uri) {
+ return queryLocationHintsProcessed != null && queryLocationHintsProcessed.contains(uri);
+ }
+
+
+ /**
+ * Fix up global variables and functions in all query modules. This is done right at the end, because
+ * recursive imports are permitted
+ * @param main the main query module
+ * @param checkForCycles if a check for cyclicity among modules is to be performed. This is a check for
+ * cycles at the level of a module (error XQST0093)
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void fixupQueryModules(QueryModule main, boolean checkForCycles) throws XPathException {
+
+ // If there is no pseudo-variable representing the context item, then create one now
+ StructuredQName contextItemVarName = getInitialContextItemVariableName();
+ GlobalVariable contextItemVar;
+ if (contextItemVarName == null) {
+ GlobalParam var = new GlobalParam();
+ var.setExecutable(this);
+ var.setRequiredParam(false);
+ ErrorExpression ee = new ErrorExpression(new XPathException("Context item is absent", "XPDY0002"));
+ ee.setContainer(var);
+ var.setSelectExpression(ee);
+ StructuredQName varQName = new StructuredQName("saxon", NamespaceConstant.SAXON, "context-item");
+ var.setVariableQName(varQName);
+ var.setRequiredType(SequenceType.SINGLE_ITEM);
+ setInitialContextItemVariableName(varQName);
+ registerGlobalVariable(var);
+ getGlobalVariableMap().allocateSlotNumber(varQName);
+ contextItemVar = var;
+ } else {
+ contextItemVar = getGlobalVariable(contextItemVarName);
+ }
+
+ // Bind any previously unbound variables (forwards references)
+
+ main.bindUnboundVariables();
+
+ if (queryLibraryModules != null) {
+ for (List<QueryModule> queryModules : queryLibraryModules.values()) {
+ for (QueryModule env : queryModules) {
+ env.bindUnboundVariables();
+ }
+ }
+ }
+
+ List<GlobalVariable> varDefinitions = main.fixupGlobalVariables(main.getGlobalStackFrameMap(), contextItemVar);
+
+ main.bindUnboundFunctionCalls();
+
+ if (queryLibraryModules != null) {
+ for (List<QueryModule> queryModules : queryLibraryModules.values()) {
+ for (QueryModule env : queryModules) {
+ env.bindUnboundFunctionCalls();
+ }
+ }
+ }
+
+ // Note: the checks for circularities between variables and functions have to happen
+ // before functions are compiled and optimized, as the optimization can involve function
+ // inlining which eliminates the circularities (tests K-InternalVariablesWith-17, errata8-002)
+
+ main.checkForCircularities(varDefinitions, main.getGlobalFunctionLibrary());
+ main.fixupGlobalFunctions();
+
+ if (checkForCycles) {
+ Iterator miter = getQueryLibraryModules();
+ while (miter.hasNext()) {
+ QueryModule module = (QueryModule)miter.next();
+ module.lookForModuleCycles(new Stack<QueryModule>(), 1);
+ }
+ }
+
+ main.typeCheckGlobalVariables(varDefinitions);
+ main.optimizeGlobalFunctions();
+ }
+
+ /**
+ * Set the space requirements for variables used in template match patterns
+ *
+ * @param patternLocals The largest number of local variables used in the match pattern of any template rule
+ */
+
+ public void setPatternSlotSpace(int patternLocals) {
+ largestPatternStackFrame = patternLocals;
+ }
+
+ /**
+ * Get the global variable with a given name
+ * @param name the name of the required variable
+ * @return the GlobalVariable with this name, or null if not found
+ */
+
+ /*@Nullable*/ public GlobalVariable getGlobalVariable(StructuredQName name) {
+ if (compiledGlobalVariables != null) {
+ return compiledGlobalVariables.get(name);
+ }
+ return null;
+ }
+
+ /**
+ * Get the global variable map
+ *
+ * @return the SlotManager defining the allocation of slots to global variables
+ */
+
+ public SlotManager getGlobalVariableMap() {
+ if (globalVariableMap == null) {
+ globalVariableMap = config.makeSlotManager();
+ }
+ return globalVariableMap;
+ }
+
+ /**
+ * Get the index of global variables
+ *
+ * @return the index of global variables. This is a HashMap in which the key is the
+ * {@link net.sf.saxon.om.StructuredQName}
+ * of the variable name, and the value is the GlobalVariable object representing the compiled
+ * global variable. If there are no global variables, the method may return null.
+ */
+
+ public HashMap<StructuredQName, GlobalVariable> getCompiledGlobalVariables() {
+ return compiledGlobalVariables;
+ }
+
+ /**
+ * Explain (that is, output an expression tree) the global variables
+ * @param presenter the destination for the explanation of the global variables
+ */
+
+ public void explainGlobalVariables(ExpressionPresenter presenter) {
+ if (compiledGlobalVariables != null) {
+ presenter.startElement("globalVariables");
+ for (GlobalVariable var : compiledGlobalVariables.values()) {
+ presenter.startElement("declareVariable");
+ presenter.emitAttribute("name", var.getVariableQName().getDisplayName());
+ if (var.isAssignable()) {
+ presenter.emitAttribute("assignable", "true");
+ }
+ if (var.getSelectExpression() != null) {
+ var.getSelectExpression().explain(presenter);
+ }
+ presenter.endElement();
+ }
+ presenter.endElement();
+ }
+ }
+
+ /**
+ * Register a global variable
+ * @param variable the global variable to be registered
+ */
+
+ public void registerGlobalVariable(GlobalVariable variable) {
+ if (compiledGlobalVariables == null) {
+ compiledGlobalVariables = new HashMap<StructuredQName, GlobalVariable>(32);
+ }
+ compiledGlobalVariables.put(variable.getVariableQName(), variable);
+ }
+
+ /**
+ * Allocate space in bindery for all the variables needed
+ *
+ * @param bindery The bindery to be initialized
+ */
+
+ public void initializeBindery(Bindery bindery) {
+ bindery.allocateGlobals(getGlobalVariableMap());
+ }
+
+ /**
+ * Determine the size of the stack frame needed for evaluating match patterns
+ * @return the size of the largest stack frame needed for evaluating the match patterns
+ * that appear in XSLT template rules
+ */
+
+ public int getLargestPatternStackFrame() {
+ return largestPatternStackFrame;
+ }
+
+ /**
+ * Set the location map
+ * @param map the location map, which is used to identify the module URI and line number of locations of errors
+ */
+
+ public void setLocationMap(LocationMap map) {
+ locationMap = map;
+ }
+
+ /**
+ * Get the location map
+ * @return the location map, which is used to identify the locations of errors
+ */
+
+ public LocationMap getLocationMap() {
+ return locationMap;
+ }
+
+ /**
+ * Add a required parameter. Used in XSLT only.
+ * @param qName the name of the required parameter
+ */
+
+ public void addRequiredParam(StructuredQName qName) {
+ if (requiredParams == null) {
+ requiredParams = new HashSet<StructuredQName>(5);
+ }
+ requiredParams.add(qName);
+ }
+
+ /**
+ * Check that all required parameters have been supplied. Used in XSLT only.
+ * @param params the set of parameters that have been supplied
+ * @throws XPathException if there is a required parameter for which no value has been supplied
+ */
+
+ public void checkAllRequiredParamsArePresent(/*@Nullable*/ GlobalParameterSet params) throws XPathException {
+ if (requiredParams == null) {
+ return;
+ }
+ for (StructuredQName req : requiredParams) {
+ if (params == null || params.get(req) == null) {
+ XPathException err = new XPathException("No value supplied for required parameter " +
+ req.getDisplayName());
+ err.setErrorCode("XTDE0050");
+ throw err;
+ }
+ }
+ }
+
+
+ /**
+ * Set whether this executable represents a stylesheet that uses xsl:result-document
+ * to create secondary output documents
+ * @param flag true if the executable uses xsl:result-document
+ */
+
+ public void setCreatesSecondaryResult(boolean flag) {
+ createsSecondaryResult = flag;
+ }
+
+ /**
+ * Ask whether this executable represents a stylesheet that uses xsl:result-document
+ * to create secondary output documents
+ * @return true if the executable uses xsl:result-document
+ */
+
+ public boolean createsSecondaryResult() {
+ return createsSecondaryResult;
+ }
+
+ /**
+ * Set the name of the variable that will implicitly contain the value of the
+ * initial context item. The properties of this variable, such as its required
+ * type and initial value, automatically apply to the initial context item
+ * @param name the name of the global variable that mirrors the initial context item;
+ * or null if there is no such variable
+ */
+
+ public void setInitialContextItemVariableName(/*@Nullable*/ StructuredQName name) {
+ initialContextItemVariableName = name;
+ }
+
+ /**
+ * Get the the name of the variable that will implicitly contain the value of the
+ * initial context item. The properties of this variable, such as its required
+ * type and initial value, automatically apply to the initial context item
+ * @return the name of the global variable that mirrors the initial context item
+ */
+
+ /*@Nullable*/ public StructuredQName getInitialContextItemVariableName() {
+ return initialContextItemVariableName;
+ }
+
+ /**
+ * Set whether this executable is schema-aware. The initial value is false; it is set to true
+ * at compile time if the query or transformation imports a schema. If the value is false, then
+ * all documents used at run-time must be untyped
+ * @param aware true if the executable is schema-aware
+ * @throws IllegalArgumentException if schema-aware processing is requested in a Configuration
+ * that is not schema-aware
+ */
+
+ public void setSchemaAware(boolean aware) {
+ if (aware) {
+ config.checkLicensedFeature(Configuration.LicenseFeature.SCHEMA_VALIDATION, "schema-aware processing");
+ }
+ schemaAware = aware;
+ }
+
+ /**
+ * Ask whether this executable is schema-aware, that is, whether the query or transformation
+ * imports a schema.
+ * @return true if the executable is schema-aware, false if not.
+ */
+
+ public boolean isSchemaAware() {
+ return schemaAware;
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/FixedAttribute.java b/sf/saxon/expr/instruct/FixedAttribute.java
new file mode 100644
index 0000000..1d0c8c3
--- /dev/null
+++ b/sf/saxon/expr/instruct/FixedAttribute.java
@@ -0,0 +1,273 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.FixedAttributeCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.*;
+
+/**
+ * An instruction derived from an xsl:attribute element in stylesheet, or from
+ * an attribute constructor in XQuery. This version deals only with attributes
+ * whose name is known at compile time. It is also used for attributes of
+ * literal result elements. The value of the attribute is in general computed
+ * at run-time.
+*/
+
+public final class FixedAttribute extends AttributeCreator {
+
+ private NodeName nodeName;
+
+ /**
+ * Construct an Attribute instruction
+ * @param nodeName Represents the attribute name
+ * @param validationAction the validation required, for example strict or lax
+ * @param schemaType the schema type against which validation is required, null if not applicable
+ * of the instruction - zero if the attribute was not present
+ */
+
+ public FixedAttribute ( NodeName nodeName,
+ int validationAction,
+ SimpleType schemaType) {
+ this.nodeName = nodeName;
+ setSchemaType(schemaType);
+ setValidationAction(validationAction);
+ setOptions(0);
+ }
+
+ /**
+ * Get the name of this instruction (return 'xsl:attribute')
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_ATTRIBUTE;
+ }
+
+ public NodeName getAttributeName() {
+ return nodeName;
+ }
+
+ /**
+ * Set the expression defining the value of the attribute. If this is a constant, and if
+ * validation against a schema type was requested, the validation is done immediately.
+ * @param select The expression defining the content of the attribute
+ * @param config The Saxon configuration
+ */
+ public void setSelect(Expression select, Configuration config) {
+ super.setSelect(select, config);
+ // If attribute name is xml:id, add whitespace normalization
+ if (nodeName.equals(StandardNames.XML_ID_NAME)) {
+ select = SystemFunctionCall.makeSystemFunction("normalize-space", new Expression[]{select});
+ super.setSelect(select, config);
+ }
+ }
+
+ public void localTypeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Configuration config = visitor.getConfiguration();
+ final ConversionRules rules = config.getConversionRules();
+ SimpleType schemaType = getSchemaType();
+ if (schemaType == null) {
+ int validation = getValidationAction();
+ if (validation == Validation.STRICT) {
+ SchemaDeclaration decl = config.getAttributeDeclaration(nodeName.getFingerprint());
+ if (decl == null) {
+ XPathException se = new XPathException(
+ "Strict validation fails: there is no global attribute declaration for " +
+ nodeName.getDisplayName());
+ se.setErrorCode("XTTE1510");
+ se.setLocator(this);
+ throw se;
+ }
+ schemaType = (SimpleType)decl.getType();
+ } else if (validation == Validation.LAX) {
+ SchemaDeclaration decl = config.getAttributeDeclaration(nodeName.getFingerprint());
+ if (decl != null) {
+ schemaType = (SimpleType)decl.getType();
+ } else {
+ visitor.getStaticContext().issueWarning(
+ "Lax validation has no effect: there is no global attribute declaration for " +
+ nodeName.getDisplayName(), this);
+ }
+ }
+ }
+
+ // Attempt early validation if possible
+ if (Literal.isAtomic(select) && schemaType != null && !schemaType.isNamespaceSensitive()) {
+ CharSequence value = ((Literal)select).getValue().getStringValueCS();
+ ValidationFailure err = schemaType.validateContent(
+ value, DummyNamespaceResolver.getInstance(), rules);
+ if (err != null) {
+ XPathException se = new XPathException("Attribute value " + Err.wrap(value, Err.VALUE) +
+ " does not the match the required type " +
+ schemaType.getDescription() + ". " +
+ err.getMessage());
+ se.setErrorCode("XTTE1540");
+ throw se;
+ }
+ }
+
+ // If value is fixed, test whether there are any special characters that might need to be
+ // escaped when the time comes for serialization
+ if (select instanceof StringLiteral) {
+ boolean special = false;
+ CharSequence val = ((StringLiteral)select).getStringValue();
+ for (int k=0; k<val.length(); k++) {
+ char c = val.charAt(k);
+ if ((int)c<33 || (int)c>126 ||
+ c=='<' || c=='>' || c=='&' || c=='\"') {
+ special = true;
+ break;
+ }
+ }
+ if (!special) {
+ setNoSpecialChars();
+ }
+ }
+ }
+
+ /**
+ * Get the name pool name code of the attribute to be constructed
+ * @return the attribute's name code
+ */
+
+ public int getAttributeNameCode() {
+ return nodeName.getNameCode();
+ }
+
+
+
+ public int getCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ FixedAttribute exp = new FixedAttribute(nodeName, getValidationAction(), getSchemaType());
+ exp.setSelect(select.copy(), getExecutable().getConfiguration());
+ return exp;
+ }
+
+ public NodeName evaluateNodeName(XPathContext context) {
+ return nodeName;
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ int fp = nodeName.getFingerprint();
+ if (fp == StandardNames.XSI_TYPE ||
+ fp == StandardNames.XSI_SCHEMA_LOCATION ||
+ fp == StandardNames.XSI_NIL ||
+ fp == StandardNames.XSI_NO_NAMESPACE_SCHEMA_LOCATION) {
+ return;
+ }
+ if (parentType instanceof SimpleType) {
+ XPathException err = new XPathException("Attribute " + nodeName.getDisplayName() +
+ " is not permitted in the content model of the simple type " + parentType.getDescription());
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ if (getHostLanguage() == Configuration.XSLT) {
+ err.setErrorCode("XTTE1510");
+ } else {
+ err.setErrorCode("XQDY0027");
+ }
+ throw err;
+ }
+ SchemaType type;
+ try {
+ type = ((ComplexType)parentType).getAttributeUseType(fp);
+ } catch (SchemaException e) {
+ throw new XPathException(e);
+ }
+ if (type == null) {
+ XPathException err = new XPathException("Attribute " + nodeName.getDisplayName() +
+ " is not permitted in the content model of the complex type " + parentType.getDescription());
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ if (getHostLanguage() == Configuration.XSLT) {
+ err.setErrorCode("XTTE1510");
+ } else {
+ err.setErrorCode("XQDY0027");
+ }
+ throw err;
+ }
+
+ try {
+ // When select is a SimpleContentConstructor, this does nothing
+ select.checkPermittedContents(type, env, true);
+ } catch (XPathException e) {
+ if (e.getLocator() == null || e.getLocator() == e) {
+ e.setLocator(this);
+ }
+ throw e;
+ }
+ }
+
+
+ public NodeInfo evaluateItem(XPathContext context) throws XPathException {
+ Orphan o = (Orphan)super.evaluateItem(context);
+ assert o != null;
+ validateOrphanAttribute(o, context);
+ return o;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the FixedAttribute expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new FixedAttributeCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("directAttribute");
+ out.emitAttribute("name", nodeName.getDisplayName());
+ out.emitAttribute("validation", Validation.toString(getValidationAction()));
+ if (getSchemaType() != null) {
+ out.emitAttribute("type", getSchemaType().getDescription());
+ }
+ getContentExpression().explain(out);
+ out.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/FixedElement.java b/sf/saxon/expr/instruct/FixedElement.java
new file mode 100644
index 0000000..b9eedc5
--- /dev/null
+++ b/sf/saxon/expr/instruct/FixedElement.java
@@ -0,0 +1,567 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.FixedElementCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.CombinedNodeTest;
+import net.sf.saxon.pattern.ContentTypeTest;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import org.xml.sax.Locator;
+
+import java.util.Iterator;
+
+
+/**
+* An instruction that creates an element node whose name is known statically.
+ * Used for literal results elements in XSLT, for direct element constructors
+ * in XQuery, and for xsl:element in cases where the name and namespace are
+ * known statically.
+*/
+
+public class FixedElement extends ElementCreator {
+
+ private NodeName elementName;
+ /*@NotNull*/ protected NamespaceBinding[] namespaceBindings;
+ private ItemType itemType;
+
+ /**
+ * Create an instruction that creates a new element node
+ * @param elementName Represents the name of the element node
+ * @param namespaceBindings List of namespaces to be added to the element node.
+ * Supply an empty array if none are required.
+ * @param inheritNamespaces true if the children of this element are to inherit its namespaces
+ * @param schemaType Type annotation for the new element node
+ * @param validation Validation mode to be applied, for example STRICT, LAX, SKIP
+ */
+ public FixedElement(NodeName elementName,
+ NamespaceBinding[] namespaceBindings,
+ boolean inheritNamespaces,
+ SchemaType schemaType,
+ int validation) {
+ this.elementName = elementName;
+ this.namespaceBindings = namespaceBindings;
+ this.inheritNamespaces = inheritNamespaces;
+ setValidationAction(validation, schemaType);
+ preservingTypes = schemaType == null && validation == Validation.PRESERVE;
+ }
+
+ /**
+ * Simplify an expression. This performs any context-independent rewriting
+ * @param visitor the expression visitor
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ final Configuration config = visitor.getConfiguration();
+ setLazyConstruction(config.getBooleanProperty(FeatureKeys.LAZY_CONSTRUCTION_MODE));
+ preservingTypes |= !visitor.getExecutable().isSchemaAware();
+ return super.simplify(visitor);
+ }
+
+ /**
+ * Check statically whether the content of the element creates attributes or namespaces
+ * after creating any child nodes
+ * @param env the static context
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ protected void checkContentSequence(StaticContext env) throws XPathException {
+ super.checkContentSequence(env);
+ itemType = computeFixedElementItemType(this, env,
+ getValidationAction(), getSchemaType(), elementName, content);
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+ // Remove any unnecessary creation of namespace nodes by child literal result elements.
+ // Specifically, if this instruction creates a namespace node, then a child literal result element
+ // doesn't need to create the same namespace if all the following conditions are true:
+ // (a) the child element is in the same namespace as its parent, and
+ // (b) this element doesn't specify xsl:inherit-namespaces="no"
+ // (c) the child element is incapable of creating attributes in a non-null namespace
+
+ if (!inheritNamespaces) {
+ return this;
+ }
+ if (namespaceBindings.length == 0) {
+ return this;
+ }
+ if (content instanceof FixedElement) {
+ FixedElement fixedContent = ((FixedElement)content);
+ if (elementName.isInSameNamespace(fixedContent.getElementName())) {
+ fixedContent.removeRedundantNamespaces(visitor, namespaceBindings);
+ }
+ return this;
+ }
+ if (content instanceof Block) {
+ Iterator iter = content.iterateSubExpressions();
+ while (iter.hasNext()) {
+ Expression exp = (Expression)iter.next();
+ if (exp instanceof FixedElement && elementName.isInSameNamespace(((FixedElement)exp).getElementName())) {
+ ((FixedElement)exp).removeRedundantNamespaces(visitor, namespaceBindings);
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Remove namespaces that are not required for this element because they are output on
+ * the parent element
+ * @param visitor the expression visitor
+ * @param parentNamespaces the namespaces that are output by the parent element
+ */
+
+ private void removeRedundantNamespaces(ExpressionVisitor visitor, NamespaceBinding[] parentNamespaces) {
+ // It's only safe to remove any namespaces if the element is incapable of creating any attribute nodes
+ // in a non-null namespace
+ // This is because namespaces created on this element take precedence over namespaces created by namespace
+ // fixup based on the prefix used in the attribute name (see atrs24)
+ if (namespaceBindings.length == 0) {
+ return;
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType contentType = content.getItemType(th);
+ boolean ok = th.relationship(contentType, NodeKindTest.ATTRIBUTE) == TypeHierarchy.DISJOINT;
+ if (!ok) {
+ // if the content might include attributes, discount any that are known to be in the null namespace
+ if (content instanceof Block) {
+ ok = true;
+ Iterator iter = content.iterateSubExpressions();
+ while (iter.hasNext()) {
+ Expression exp = (Expression)iter.next();
+ if (exp instanceof FixedAttribute) {
+ int attNameCode = ((FixedAttribute)exp).getAttributeNameCode();
+ if (NamePool.isPrefixed(attNameCode)) {
+ ok = false;
+ break;
+ }
+ } else {
+ ItemType childType = exp.getItemType(th);
+ if (th.relationship(childType, NodeKindTest.ATTRIBUTE) != TypeHierarchy.DISJOINT) {
+ ok = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (ok) {
+ int removed = 0;
+ for (int i=0; i< namespaceBindings.length; i++) {
+ for (NamespaceBinding parentNamespace : parentNamespaces) {
+ if (namespaceBindings[i] == parentNamespace) {
+ namespaceBindings[i] = null;
+ removed++;
+ break;
+ }
+ }
+ }
+ if (removed > 0) {
+ if (removed == namespaceBindings.length) {
+ namespaceBindings = NamespaceBinding.EMPTY_ARRAY;
+ } else {
+ NamespaceBinding[] ns2 = new NamespaceBinding[namespaceBindings.length - removed];
+ int j=0;
+ for (NamespaceBinding namespaceBinding : namespaceBindings) {
+ if (namespaceBinding != null) {
+ ns2[j++] = namespaceBinding;
+ }
+ }
+ namespaceBindings = ns2;
+ }
+ }
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ NamespaceBinding[] ns2 = namespaceBindings;
+ if (namespaceBindings.length != 0) {
+ ns2 = new NamespaceBinding[namespaceBindings.length];
+ System.arraycopy(namespaceBindings, 0, ns2, 0, ns2.length);
+ }
+ FixedElement fe = new FixedElement(elementName, ns2, inheritNamespaces, getSchemaType(), getValidationAction());
+ fe.setContentExpression(content.copy());
+ fe.setBaseURI(getBaseURI());
+ if (getOnEmpty() != null) {
+ fe.setOnEmpty(getOnEmpty().copy());
+ }
+ return fe;
+ }
+
+ /**
+ * Determine the item type of an element being constructed
+ * @param instr the FixedElement instruction
+ * @param env the static context
+ * @param validation the schema validation mode
+ * @param schemaType the schema type for validation
+ * @param elementName the name of the element
+ * @param content the expression that computes the content of the element
+ * @return the item type
+ * @throws XPathException if a static error is detected
+ */
+
+ private ItemType computeFixedElementItemType(FixedElement instr, StaticContext env,
+ int validation, SchemaType schemaType,
+ NodeName elementName, Expression content) throws XPathException {
+ final Configuration config = env.getConfiguration();
+ ItemType itemType;
+ int fp = elementName.getFingerprint();
+ if (schemaType == null) {
+ if (validation == Validation.STRICT) {
+ SchemaDeclaration decl = config.getElementDeclaration(fp);
+ if (decl == null) {
+ XPathException err = new XPathException("There is no global element declaration for " +
+ elementName.getStructuredQName().getClarkName() +
+ ", so strict validation will fail");
+ err.setErrorCode(instr.isXSLT() ? "XTTE1512" : "XQDY0027");
+ err.setIsTypeError(true);
+ err.setLocator(instr);
+ throw err;
+ }
+ if (decl.isAbstract()) {
+ XPathException err = new XPathException("The element declaration for " +
+ elementName.getStructuredQName().getClarkName() +
+ " is abstract, so strict validation will fail");
+ err.setErrorCode(instr.isXSLT() ? "XTTE1512" : "XQDY0027");
+ err.setIsTypeError(true);
+ err.setLocator(instr);
+ throw err;
+ }
+ schemaType = decl.getType();
+ instr.getValidationOptions().setTopLevelType(schemaType);
+ itemType = new CombinedNodeTest(
+ new NameTest(Type.ELEMENT, fp, env.getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, schemaType, config, false));
+ try {
+ schemaType.analyzeContentExpression(content, Type.ELEMENT, env);
+ } catch (XPathException e) {
+ e.setErrorCode(instr.isXSLT() ? "XTTE1510" : "XQDY0027");
+ e.setLocator(instr);
+ throw e;
+ }
+ SchemaType xsiType = instr.getXSIType(env);
+ if (xsiType != null) {
+ xsiType.analyzeContentExpression(content, Type.ELEMENT, env);
+ try {
+ config.checkTypeDerivationIsOK(xsiType, schemaType, 0);
+ } catch (SchemaException e) {
+ ValidationException ve = new ValidationException("The specified xsi:type " + xsiType.getDescription() +
+ " is not validly derived from the required type " + schemaType.getDescription());
+ ve.setConstraintReference(1, "cvc-elt", "4.3");
+ ve.setErrorCode(instr.isXSLT() ? "XTTE1515" : "XQDY0027");
+ ve.setLocator((Locator)instr);
+ throw ve;
+ }
+ }
+ } else if (validation == Validation.LAX) {
+ SchemaDeclaration decl = config.getElementDeclaration(fp);
+ if (decl == null) {
+ env.issueWarning("There is no global element declaration for " +
+ elementName.getDisplayName(), instr);
+ itemType = new NameTest(Type.ELEMENT, fp, env.getNamePool());
+ } else {
+ schemaType = decl.getType();
+ instr.getValidationOptions().setTopLevelType(schemaType);
+ itemType = new CombinedNodeTest(
+ new NameTest(Type.ELEMENT, fp, env.getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, instr.getSchemaType(), config, false));
+ try {
+ schemaType.analyzeContentExpression(content, Type.ELEMENT, env);
+ } catch (XPathException e) {
+ e.setErrorCode(instr.isXSLT() ? "XTTE1515" : "XQDY0027");
+ e.setLocator(instr);
+ throw e;
+ }
+ }
+ } else if (validation == Validation.PRESERVE) {
+ // we know the result will be an element of type xs:anyType
+ itemType = new CombinedNodeTest(
+ new NameTest(Type.ELEMENT, fp, env.getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, AnyType.getInstance(), config, false));
+ } else {
+ // we know the result will be an untyped element
+ itemType = new CombinedNodeTest(
+ new NameTest(Type.ELEMENT, fp, env.getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, Untyped.getInstance(), config, false));
+ }
+ } else {
+ itemType = new CombinedNodeTest(
+ new NameTest(Type.ELEMENT, fp, env.getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, schemaType, config, false)
+ );
+ try {
+ schemaType.analyzeContentExpression(content, Type.ELEMENT, env);
+ } catch (XPathException e) {
+ e.setErrorCode(instr.isXSLT() ? "XTTE1540" : "XQDY0027");
+ e.setLocator(instr);
+ throw e;
+ }
+ }
+ return itemType;
+ }
+
+ /**
+ * Get the type of the item returned by this instruction
+ * @return the item type
+ * @param th The type hierarchy cache
+ */
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (itemType == null) {
+ return super.getItemType(th);
+ }
+ return itemType;
+ }
+
+ /**
+ * Callback from the superclass ElementCreator to get the nameCode
+ * for the element name
+ *
+ *
+ * @param context The evaluation context (not used)
+ * @param copiedNode For the benefit of the xsl:copy instruction, the node to be copied
+ * @return the name code for the element name
+ */
+
+ public NodeName getElementName(XPathContext context, NodeInfo copiedNode) {
+ return elementName;
+ }
+
+ public NodeName getElementName() {
+ return elementName;
+ }
+
+ public String getNewBaseURI(XPathContext context, NodeInfo copiedNode) {
+ return getBaseURI();
+ }
+
+ /**
+ * Determine whether the element constructor creates a fixed xsi:type attribute, and if so, return the
+ * relevant type.
+ * @param env the static context
+ * @return the type denoted by the constructor's xsi:type attribute if there is one.
+ * Return null if there is no xsi:type attribute, or if the value of the xsi:type
+ * attribute is a type that is not statically known (this is allowed)
+ * @throws XPathException if there is an xsi:type attribute and its value is not a QName.
+ */
+
+ private SchemaType getXSIType(StaticContext env) throws XPathException {
+ if (content instanceof FixedAttribute) {
+ return testForXSIType((FixedAttribute)content, env);
+ } else if (content instanceof Block) {
+ Iterator iter = content.iterateSubExpressions();
+ while (iter.hasNext()) {
+ Expression exp = (Expression)iter.next();
+ if (exp instanceof FixedAttribute) {
+ SchemaType type = testForXSIType((FixedAttribute)exp, env);
+ if (type != null) {
+ return type;
+ }
+ }
+ }
+ return null;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Test whether a FixedAttribute child instruction of this FixedElement is creating an xsi:type
+ * attribute whose value is known statically; if this is the case, then return the schema type
+ * named in this attribute
+ * @param fat The FixedAttribute instruction
+ * @param env The XPath static context
+ * @return the schema type if this is an xsi:type attribute instruction whose value is known at compile time;
+ * otherwise null
+ * @throws XPathException if an error occurs
+ */
+
+ private SchemaType testForXSIType(FixedAttribute fat, StaticContext env) throws XPathException {
+ int att = fat.getAttributeNameCode() & NamePool.FP_MASK;
+ if (att == StandardNames.XSI_TYPE) {
+ Expression attValue = fat.getContentExpression();
+ if (attValue instanceof StringLiteral) {
+ try {
+ NamePool pool = env.getNamePool();
+ String[] parts = env.getConfiguration().getNameChecker().getQNameParts(
+ ((StringLiteral)attValue).getStringValue());
+ // The only namespace bindings we can trust are those declared on this element
+ // We could also trust those on enclosing LREs in the same function/template,
+ // but it's not a big win to go looking for them.
+ String uri = null;
+ for (NamespaceBinding namespaceBinding : namespaceBindings) {
+ String prefix = namespaceBinding.getPrefix();
+ if (prefix.equals(parts[0])) {
+ uri = namespaceBinding.getURI();
+ break;
+ }
+ }
+ if (uri == null) {
+ return null;
+ }
+ int typefp = pool.allocate(parts[0], uri, parts[1]) & NamePool.FP_MASK;
+ return env.getConfiguration().getSchemaType(typefp);
+ } catch (QNameException e) {
+ throw new XPathException(e.getMessage());
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ if (parentType instanceof SimpleType) {
+ XPathException err = new XPathException("Element " + elementName.getDisplayName() +
+ " is not permitted here: the containing element is of simple type " + parentType.getDescription());
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ } else if (((ComplexType)parentType).isSimpleContent()) {
+ XPathException err = new XPathException("Element " + elementName.getDisplayName() +
+ " is not permitted here: the containing element has a complex type with simple content");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+
+ // Check that a sequence consisting of this element alone is valid against the content model
+ if (whole) {
+ Block block = new Block();
+ block.setChildren(new Expression[]{this});
+ parentType.analyzeContentExpression(block, Type.ELEMENT, env);
+ }
+
+ SchemaType type;
+ try {
+ type = ((ComplexType)parentType).getElementParticleType(elementName.getFingerprint(), true);
+ } catch (SchemaException e) {
+ throw new XPathException(e);
+ }
+ if (type == null) {
+ XPathException err = new XPathException("Element " + elementName.getDisplayName() +
+ " is not permitted in the content model of the complex type " +
+ parentType.getDescription());
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ err.setErrorCode(isXSLT() ? "XTTE1510" : "XQDY0027");
+ throw err;
+ }
+ if (type instanceof AnyType) {
+ return;
+ }
+
+ try {
+ content.checkPermittedContents(type, env, true);
+ } catch (XPathException e) {
+ if (e.getLocator() == null || e.getLocator() == e) {
+ e.setLocator(this);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Callback from the superclass ElementCreator to output the namespace nodes
+ * @param context The evaluation context (not used)
+ * @param out The receiver to handle the output
+ * @param nameCode the name of this element
+ * @param copiedNode in the case of xsl:copy, the node being copied
+ */
+
+ public void outputNamespaceNodes(XPathContext context, Receiver out, NodeName nameCode, NodeInfo copiedNode)
+ throws XPathException {
+ for (NamespaceBinding namespaceBinding : namespaceBindings) {
+ out.namespace(namespaceBinding, 0);
+ }
+ }
+
+ /**
+ * Callback to get a list of the intrinsic namespaces that need to be generated for the element.
+ * The result is an array of namespace codes, the codes either occupy the whole array or are
+ * terminated by a -1 entry. A result of null is equivalent to a zero-length array.
+ */
+
+ public NamespaceBinding[] getActiveNamespaces() {
+ return namespaceBindings;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the FixedElement expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new FixedElementCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("directElement");
+ out.emitAttribute("name", elementName.getStructuredQName().getClarkName());
+ out.emitAttribute("validation", Validation.toString(getValidationAction()));
+ SchemaType type = getSchemaType();
+ if (type != null) {
+ out.emitAttribute("type", type.getDescription());
+ }
+ content.explain(out);
+ out.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/ForEach.java b/sf/saxon/expr/instruct/ForEach.java
new file mode 100644
index 0000000..bb0ff80
--- /dev/null
+++ b/sf/saxon/expr/instruct/ForEach.java
@@ -0,0 +1,565 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SlashExpressionCompiler;
+import com.saxonica.stream.adjunct.ForEachAdjunct;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+
+
+/**
+ * Handler for xsl:for-each elements in a stylesheet. The same class handles the "!" operator in XPath 3.0,
+ * which has identical semantics to xsl:for-each, and it is used to support the "/" operator in cases where it
+ * is known that the rhs delivers atomic values.
+*/
+
+public class ForEach extends Instruction implements ContextMappingFunction<Item>, ContextSwitchingExpression {
+
+ protected Expression select;
+ protected Expression action;
+ /*@Nullable*/ protected Expression threads;
+ protected boolean containsTailCall;
+
+ /**
+ * Create an xsl:for-each instruction
+ * @param select the select expression
+ * @param action the body of the xsl:for-each loop
+ */
+
+ public ForEach(Expression select, Expression action) {
+ this.select = select;
+ this.action = action;
+ this.containsTailCall = false;
+ this.threads = null;
+ adoptChildExpression(select);
+ adoptChildExpression(action);
+ }
+
+ /**
+ * Create an xsl:for-each instruction
+ * @param select the select expression
+ * @param action the body of the xsl:for-each loop
+ * @param containsTailCall true if the body of the loop contains a tail call on the containing function
+ * @param threads if >1 causes multithreaded execution (Saxon-EE only)
+ */
+
+ public ForEach(Expression select, Expression action, boolean containsTailCall, Expression threads) {
+ this.select = select;
+ this.action = action;
+ this.containsTailCall = containsTailCall && action instanceof TailCallReturner;
+ this.threads = threads;
+ adoptChildExpression(select);
+ adoptChildExpression(action);
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ * @return the code for name xsl:for-each
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_FOR_EACH;
+ }
+
+ /**
+ * Get the select expression
+ * @return the select expression. Note this will have been wrapped in a sort expression
+ * if sorting was requested.
+ */
+
+ public Expression getSelectExpression() {
+ return select;
+ }
+
+ /**
+ * Set the select expression
+ * @param select the select expression
+ */
+
+ public void setSelectExpression(Expression select) {
+ this.select = select;
+ }
+
+ /**
+ * Get the subexpression that sets the context item
+ *
+ * @return the subexpression that sets the context item, position, and size to each of its
+ * items in turn
+ */
+ public Expression getControllingExpression() {
+ return select;
+ }
+
+ /**
+ * Get the action expression (the content of the for-each)
+ * @return the body of the for-each loop
+ */
+
+ public Expression getActionExpression() {
+ return action;
+ }
+
+ /**
+ * Set the action expression
+ * @param action the select expression
+ */
+
+ public void setActionExpression(Expression action) {
+ this.action = action;
+ }
+
+ /**
+ * Get the subexpression that is evaluated in the new context
+ *
+ * @return the subexpression evaluated in the context set by the controlling expression
+ */
+ public Expression getControlledExpression() {
+ return action;
+ }
+
+ /**
+ * Get the number of threads requested
+ * @return the value of the saxon:threads attribute
+ */
+
+ public Expression getNumberOfThreadsExpression() {
+ return threads;
+ }
+
+ /**
+ * Determine the data type of the items returned by this expression
+ * @return the data type
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public final ItemType getItemType(TypeHierarchy th) {
+ return action.getItemType(th);
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true if the "action" creates new nodes.
+ * (Nodes created by the condition can't contribute to the result).
+ */
+
+ public final boolean createsNewNodes() {
+ int props = action.getSpecialProperties();
+ return ((props & StaticProperty.NON_CREATIVE) == 0);
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression).
+ *
+ * @exception XPathException if an error is discovered during expression
+ * rewriting
+ * @return the simplified expression
+ * @param visitor the expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ select = visitor.simplify(select);
+ action = visitor.simplify(action);
+ threads = visitor.simplify(threads);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ select = visitor.typeCheck(select, contextItemType);
+ adoptChildExpression(select);
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(select.getItemType(th), false);
+ action = visitor.typeCheck(action, cit);
+ adoptChildExpression(action);
+ if (Literal.isEmptySequence(select)) {
+ return select;
+ }
+ if (Literal.isEmptySequence(action)) {
+ return action;
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ select = visitor.optimize(select, contextItemType);
+ adoptChildExpression(select);
+ action = action.optimize(visitor, new ExpressionVisitor.ContextItemType(select.getItemType(th), false));
+ adoptChildExpression(action);
+ if (Literal.isEmptySequence(select)) {
+ return select;
+ }
+ if (Literal.isEmptySequence(action)) {
+ return action;
+ }
+
+ // If any subexpressions within the body of the for-each are not dependent on the focus,
+ // promote them: this causes them to be evaluated once, outside the for-each loop
+
+ PromotionOffer offer = new PromotionOffer(visitor.getConfiguration().obtainOptimizer());
+ offer.action = PromotionOffer.FOCUS_INDEPENDENT;
+ offer.promoteDocumentDependent = (select.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0;
+ offer.promoteXSLTFunctions = false;
+ offer.containingExpression = this;
+ offer.bindingList = new Binding[0];
+ action = doPromotion(action, offer);
+
+ if (offer.containingExpression instanceof LetExpression) {
+ offer.containingExpression =
+ visitor.optimize(offer.containingExpression, contextItemType);
+ }
+ Expression e2 = offer.containingExpression;
+ if (e2 != this) {
+ return e2;
+ }
+ if (threads != null) {
+ return visitor.getConfiguration().obtainOptimizer().generateMultithreadedInstruction(this);
+ }
+ return this;
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the set of nodes in the path map that are affected
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet target = select.addToPathMap(pathMap, pathMapNodeSet);
+ return action.addToPathMap(pathMap, target);
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new ForEach(select.copy(), action.copy(), containsTailCall, threads);
+ }
+
+ /**
+ * Compute the dependencies of an expression, as the union of the
+ * dependencies of its subexpressions. (This is overridden for path expressions
+ * and filter expressions, where the dependencies of a subexpression are not all
+ * propogated). This method should be called only once, to compute the dependencies;
+ * after that, getDependencies should be used.
+ *
+ * @return the depencies, as a bit-mask
+ */
+
+ public int computeDependencies() {
+ // Some of the dependencies aren't relevant. Note that the sort keys are absorbed into the select
+ // expression.
+ int dependencies = 0;
+ dependencies |= select.getDependencies();
+ dependencies |= (action.getDependencies() & ~StaticProperty.DEPENDS_ON_FOCUS);
+ return dependencies;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+ @Override
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ p |= (action.getSpecialProperties() & StaticProperty.ALL_NODES_UNTYPED);
+ return p;
+ }
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ select = doPromotion(select, offer);
+ if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES ||
+ offer.action == PromotionOffer.EXTRACT_GLOBAL_VARIABLES ||
+ offer.action == PromotionOffer.REPLACE_CURRENT) {
+ // Don't pass on other requests
+ action = doPromotion(action, offer);
+ }
+ }
+
+//#ifdefined STREAM
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ return ForEachAdjunct.getStreamability(this, syntacticContext, allowExtensions, reasons);
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public ForEachAdjunct getStreamingAdjunct() {
+ return new ForEachAdjunct();
+ }
+
+ //#endif
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ if (threads == null) {
+ return new PairIterator<Expression>(select, action);
+ } else {
+ return Arrays.asList(select, action, threads).iterator();
+ }
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ SubExpressionInfo selectInfo = new SubExpressionInfo(select, true, false, INSPECTION_CONTEXT);
+ SubExpressionInfo actionInfo = new SubExpressionInfo(action, false, true, INHERITED_CONTEXT);
+ if (threads == null) {
+ return new PairIterator<SubExpressionInfo>(selectInfo, actionInfo);
+ } else {
+ SubExpressionInfo threadsInfo = new SubExpressionInfo(threads, true, false, NODE_VALUE_CONTEXT);
+ return Arrays.asList(selectInfo, actionInfo, threadsInfo).iterator();
+ }
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (action == original) {
+ action = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided. This implementation provides both iterate() and
+ * process() methods natively.
+ */
+
+ public int getImplementationMethod() {
+ return ITERATE_METHOD | PROCESS_METHOD | Expression.WATCH_METHOD | Expression.ITEM_FEED_METHOD;
+ }
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ action.checkPermittedContents(parentType, env, false);
+ }
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ SequenceIterator iter = select.iterate(context);
+
+ XPathContextMajor c2 = context.newContext();
+ c2.setOrigin(this);
+ c2.setCurrentIterator(iter);
+ c2.setCurrentTemplateRule(null);
+
+ if (containsTailCall) {
+ if (controller.isTracing()) {
+ TraceListener listener = controller.getTraceListener();
+ assert listener != null;
+ Item item = iter.next();
+ if (item == null) {
+ return null;
+ }
+ listener.startCurrentItem(item);
+ TailCall tc = ((TailCallReturner)action).processLeavingTail(c2);
+ listener.endCurrentItem(item);
+ return tc;
+ } else {
+ Item item = iter.next();
+ if (item == null) {
+ return null;
+ }
+ return ((TailCallReturner)action).processLeavingTail(c2);
+ }
+ } else {
+ if (controller.isTracing()) {
+ TraceListener listener = controller.getTraceListener();
+ assert listener != null;
+ while(true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ listener.startCurrentItem(item);
+ action.process(c2);
+ listener.endCurrentItem(item);
+ }
+ } else {
+ while(true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ action.process(c2);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of the sequence.
+ *
+ * @exception XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ SequenceIterator<? extends Item> master = select.iterate(context);
+ XPathContextMinor c2 = context.newMinorContext();
+ c2.setCurrentIterator(master);
+ master = new ContextMappingIterator<Item>(this, c2);
+ return master;
+ }
+
+ /**
+ * Map one item to a sequence.
+ * @param context The processing context. The item to be mapped is the context item identified
+ * from this context: the values of position() and last() also relate to the set of items being mapped
+ * @return a SequenceIterator over the sequence of items that the supplied input
+ * item maps to
+ */
+
+ public SequenceIterator map(XPathContext context) throws XPathException {
+ return action.iterate(context);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ForEach expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SlashExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("forEach");
+ explainThreads(out);
+ select.explain(out);
+ out.startSubsidiaryElement("return");
+ action.explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+ protected void explainThreads(ExpressionPresenter out) {
+ // no action in this class: implemented in subclass
+ }
+
+ /**
+ * <p>The toString() method for an expression attempts to give a representation of the expression
+ * in an XPath-like form.</p>
+ * <p/>
+ * <p>For subclasses of Expression that represent XPath expressions, the result should always be a string that
+ * parses as an XPath 3.0 expression</p>
+ *
+ * @return a representation of the expression as a string
+ */
+ @Override
+ public String toString() {
+ return ExpressionTool.parenthesize(select) + " ! " + ExpressionTool.parenthesize(action);
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/ForEachGroup.java b/sf/saxon/expr/instruct/ForEachGroup.java
new file mode 100644
index 0000000..2d16487
--- /dev/null
+++ b/sf/saxon/expr/instruct/ForEachGroup.java
@@ -0,0 +1,1001 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ForEachGroupCompiler;
+import com.saxonica.stream.Streamability;
+import com.saxonica.stream.adjunct.ForEachGroupAdjunct;
+import com.saxonica.stream.adjunct.StreamingAdjunct;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.flwor.LocalVariableBinding;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.expr.sort.*;
+import net.sf.saxon.functions.CollatingFunction;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.PatternSponsor;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.StringValue;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * Handler for xsl:for-each-group elements in stylesheet. This is a new instruction
+ * defined in XSLT 2.0
+ */
+
+public class ForEachGroup extends Instruction
+ implements SortKeyEvaluator, ContextMappingFunction {
+
+ public static final int GROUP_BY = 0;
+ public static final int GROUP_ADJACENT = 1;
+ public static final int GROUP_STARTING = 2;
+ public static final int GROUP_ENDING = 3;
+
+ private Expression select;
+ private Expression action;
+ private byte algorithm;
+ private Expression key; // for group-starting and group-ending, this is a PatternSponsor
+ private Expression collationNameExpression;
+ private int keyItemType;
+ private URI baseURI;
+ private StringCollator collator = null; // collation used for the grouping comparisons
+ private SortKeyDefinition[] sortKeyDefinitions = null;
+ private transient AtomicComparer[] sortComparators = null; // comparators used for sorting the groups
+ private LocalVariableBinding groupBinding = null;
+ private LocalVariableBinding keyBinding = null;
+ private boolean composite = false;
+
+ /**
+ * Create a for-each-group instruction
+ * @param select the select expression (selects the population to be grouped)
+ * @param action the body of the for-each-group (applied to each group in turn)
+ * @param algorithm one of group-by, group-adjacent, group-starting-with, group-ending-with
+ * @param key expression to evaluate the grouping key
+ * @param collator user for comparing strings
+ * @param collationNameExpression expression that yields the name of the collation to be used
+ * @param baseURI static base URI of the expression
+ * @param sortKeys list of xsl:sort keys for sorting the groups
+ */
+
+ public ForEachGroup(Expression select,
+ Expression action,
+ byte algorithm,
+ Expression key,
+ StringCollator collator,
+ Expression collationNameExpression,
+ URI baseURI,
+ SortKeyDefinition[] sortKeys) {
+ this.select = select;
+ this.action = action;
+ this.algorithm = algorithm;
+ this.key = key;
+ this.collator = collator;
+ this.collationNameExpression = collationNameExpression;
+ this.baseURI = baseURI;
+ this.sortKeyDefinitions = sortKeys;
+ Iterator kids = iterateSubExpressions();
+ while (kids.hasNext()) {
+ Expression child = (Expression)kids.next();
+ adoptChildExpression(child);
+ }
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ * @return the name of the instruction
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_FOR_EACH_GROUP;
+ }
+
+ /**
+ * Get the select expression
+ * @return the select expression
+ */
+
+ public Expression getSelectExpression() {
+ return select;
+ }
+
+ /**
+ * Get the action expression (the content of the for-each-group)
+ * @return the body of the xsl:for-each-group instruction
+ */
+
+ public Expression getActionExpression() {
+ return action;
+ }
+
+ /**
+ * Get the grouping algorithm (one of group-by, group-adjacent, group-starting-with, group-ending-with)
+ * @return one of group-by, group-adjacent, group-starting-with, group-ending-with
+ */
+
+ public byte getAlgorithm() {
+ return algorithm;
+ }
+
+ /**
+ * Get the grouping key expression expression (the group-by or group-adjacent expression, or a
+ * PatternSponsor containing the group-starting-with or group-ending-with expression)
+ * @return the expression used to calculate grouping keys
+ */
+
+ public Expression getGroupingKey() {
+ return key;
+ }
+
+ /**
+ * Get the primitive item type of the key
+ * @return the primitive item type of the grouping key
+ */
+
+ public int getKeyItemType() {
+ return keyItemType;
+ }
+
+ /**
+ * Get the sort keys defined at the for-each-group level, that is, the keys for sorting the groups
+ * @return the definitions of the sort keys defined as children of the xsl:for-each-group element
+ */
+
+ public SortKeyDefinition[] getSortKeyDefinitions() {
+ return sortKeyDefinitions;
+ }
+
+ /**
+ * Get the statically-allocated sort key comparators for sorting at the group level, if known
+ * @return the comparators used for comparing sort key values, one entry in the array for each
+ * nested xsl:sort element
+ */
+
+ public AtomicComparer[] getSortKeyComparators() {
+ return sortComparators;
+ }
+
+ /**
+ * Get the statically-determined collator, or null if the collation was not determined statically
+ * @return the collation, if known statically, or null if not
+ */
+
+ /*@Nullable*/ public StringCollator getCollation() {
+ return collator;
+ }
+
+ /**
+ * Get the static base URI of the instruction
+ * @return the static base URI if known, or null otherwise
+ */
+
+ /*@Nullable*/ public URI getBaseURI() {
+ return baseURI;
+ }
+
+
+ public LocalVariableBinding getGroupBinding() {
+ return groupBinding;
+ }
+
+ public void setGroupBinding(LocalVariableBinding binding) {
+ groupBinding = binding;
+ }
+
+ public LocalVariableBinding getKeyBinding() {
+ return keyBinding;
+ }
+
+ public void setKeyBinding(LocalVariableBinding binding) {
+ keyBinding = binding;
+ }
+
+ public boolean isComposite() {
+ return composite;
+ }
+
+ public void setComposite(boolean composite) {
+ this.composite = composite;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression).
+ *
+ * @return the simplified expression
+ * @throws XPathException if an error is discovered during expression
+ * rewriting
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ select = visitor.simplify(select);
+ action = visitor.simplify(action);
+ key = visitor.simplify(key);
+ if (collationNameExpression != null) {
+ collationNameExpression = visitor.simplify(collationNameExpression);
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ select = visitor.typeCheck(select, contextItemType);
+ ItemType selectedItemType = select.getItemType(th);
+ if (groupBinding != null) {
+ groupBinding.setRequiredType(SequenceType.makeSequenceType(selectedItemType, StaticProperty.ALLOWS_ONE_OR_MORE));
+ }
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(selectedItemType, false);
+ action = visitor.typeCheck(action, cit);
+ key = visitor.typeCheck(key, cit);
+ if (collationNameExpression != null) {
+ collationNameExpression = visitor.typeCheck(collationNameExpression, contextItemType);
+ }
+ if (Literal.isEmptySequence(select)) {
+ return select;
+ }
+ if (Literal.isEmptySequence(action)) {
+ return action;
+ }
+ if (sortKeyDefinitions != null) {
+
+ boolean allFixed = true;
+ for (SortKeyDefinition sk : sortKeyDefinitions) {
+ Expression sortKey = sk.getSortKey();
+ sortKey = visitor.typeCheck(sortKey, cit);
+ if (visitor.getStaticContext().isInBackwardsCompatibleMode()) {
+ sortKey = FirstItemExpression.makeFirstItemExpression(sortKey);
+ } else {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:sort/select", 0);
+ role.setErrorCode("XTTE1020");
+ sortKey = CardinalityChecker.makeCardinalityChecker(sortKey, StaticProperty.ALLOWS_ZERO_OR_ONE, role);
+ }
+ sk.setSortKey(sortKey, true);
+
+ if (sk.isFixed()) {
+ AtomicComparer comp = sk.makeComparator(
+ visitor.getStaticContext().makeEarlyEvaluationContext());
+ sk.setFinalComparator(comp);
+ } else {
+ allFixed = false;
+ }
+ }
+ if (allFixed) {
+ sortComparators = new AtomicComparer[sortKeyDefinitions.length];
+ for (int i=0; i< sortKeyDefinitions.length; i++) {
+ sortComparators[i] = sortKeyDefinitions[i].getFinalComparator();
+ }
+ }
+ }
+ keyItemType = key.getItemType(th).getPrimitiveType();
+ if (groupBinding != null) {
+ fixupGroupReferences(this, this, groupBinding);
+ }
+ return this;
+ }
+
+ private static void fixupGroupReferences(Expression exp, ForEachGroup feg, LocalVariableBinding groupBinding) {
+ if (exp instanceof GroupVariableReference && ((GroupVariableReference)exp).getBinding() == groupBinding) {
+ ((GroupVariableReference)exp).setControllingExpression(feg);
+ } else {
+ for (Iterator<Expression> iter = exp.iterateSubExpressions(); iter.hasNext();) {
+ Expression child = iter.next();
+ fixupGroupReferences(child, feg, groupBinding);
+ }
+ }
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ select = visitor.optimize(select, contextItemType);
+ ItemType selectedItemType = select.getItemType(th);
+ ExpressionVisitor.ContextItemType sit = new ExpressionVisitor.ContextItemType(selectedItemType, false);
+ action = action.optimize(visitor, sit);
+ key = key.optimize(visitor, sit);
+ adoptChildExpression(select);
+ adoptChildExpression(action);
+ adoptChildExpression(key);
+ if (Literal.isEmptySequence(select)) {
+ return select;
+ }
+ if (Literal.isEmptySequence(action)) {
+ return action;
+ }
+ // Optimize the sort key definitions
+ if (sortKeyDefinitions != null) {
+ for (SortKeyDefinition skd : sortKeyDefinitions) {
+ Expression sortKey = skd.getSortKey();
+ sortKey = visitor.optimize(sortKey, sit);
+ skd.setSortKey(sortKey, true);
+ }
+ }
+ if (collationNameExpression != null) {
+ collationNameExpression = visitor.optimize(collationNameExpression, contextItemType);
+ }
+ if (collator == null && (collationNameExpression instanceof StringLiteral)) {
+ String collation = ((StringLiteral)collationNameExpression).getStringValue();
+ URI collationURI;
+ try {
+ collationURI = new URI(collation);
+ if (!collationURI.isAbsolute()) {
+ collationURI = baseURI.resolve(collationURI);
+ final String collationNameString = collationURI.toString();
+ collationNameExpression = new StringLiteral(collationNameString);
+ collator = visitor.getStaticContext().getCollation(collationNameString);
+ if (collator == null) {
+ XPathException err = new XPathException("Unknown collation " + Err.wrap(collationURI.toString(), Err.URI));
+ err.setErrorCode("XTDE1110");
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ } catch (URISyntaxException err) {
+ XPathException e = new XPathException("Collation name '" + collationNameExpression + "' is not a valid URI");
+ e.setErrorCode("XTDE1110");
+ e.setLocator(this);
+ throw e;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ SortKeyDefinition[] newKeyDef = null;
+ if (sortKeyDefinitions != null) {
+ newKeyDef = new SortKeyDefinition[sortKeyDefinitions.length];
+ for (int i = 0; i < sortKeyDefinitions.length; i++) {
+ newKeyDef[i] = sortKeyDefinitions[i].copy();
+ }
+ }
+ ForEachGroup feg = new ForEachGroup(
+ select.copy(),
+ action.copy(),
+ algorithm,
+ key.copy(),
+ collator,
+ collationNameExpression.copy(),
+ baseURI,
+ newKeyDef);
+ if (groupBinding != null) {
+ LocalVariableBinding lvb = groupBinding.copy();
+ feg.setGroupBinding(lvb);
+ ExpressionTool.rebindVariableReferences(feg, groupBinding, lvb);
+ }
+ if (keyBinding != null) {
+ LocalVariableBinding lvb = keyBinding.copy();
+ feg.setGroupBinding(lvb);
+ ExpressionTool.rebindVariableReferences(feg, keyBinding, lvb);
+ }
+ feg.setComposite(isComposite());
+ return feg;
+ }
+
+
+ /**
+ * Get the item type of the items returned by evaluating this instruction
+ *
+ * @return the static item type of the instruction
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return action.getItemType(th);
+ }
+
+ /**
+ * Compute the dependencies of an expression, as the union of the
+ * dependencies of its subexpressions. (This is overridden for path expressions
+ * and filter expressions, where the dependencies of a subexpression are not all
+ * propogated). This method should be called only once, to compute the dependencies;
+ * after that, getDependencies should be used.
+ *
+ * @return the depencies, as a bit-mask
+ */
+
+ public int computeDependencies() {
+ // some of the dependencies in the "action" part and in the grouping and sort keys aren't relevant,
+ // because they don't depend on values set outside the for-each-group expression
+ int dependencies = 0;
+ dependencies |= select.getDependencies();
+ dependencies |= key.getDependencies() & ~StaticProperty.DEPENDS_ON_FOCUS;
+ dependencies |= (action.getDependencies()
+ &~ (StaticProperty.DEPENDS_ON_FOCUS | StaticProperty.DEPENDS_ON_CURRENT_GROUP));
+ if (sortKeyDefinitions != null) {
+ for (SortKeyDefinition skd : sortKeyDefinitions) {
+ dependencies |= (skd.getSortKey().getDependencies() & ~StaticProperty.DEPENDS_ON_FOCUS);
+ Expression e = skd.getCaseOrder();
+ if (e != null && !(e instanceof Literal)) {
+ dependencies |= (e.getDependencies());
+ }
+ e = skd.getDataTypeExpression();
+ if (e != null && !(e instanceof Literal)) {
+ dependencies |= (e.getDependencies());
+ }
+ e = skd.getLanguage();
+ if (e != null && !(e instanceof Literal)) {
+ dependencies |= (e.getDependencies());
+ }
+ }
+ }
+ if (collationNameExpression != null) {
+ dependencies |= collationNameExpression.getDependencies();
+ }
+ return dependencies;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+ @Override
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ p |= (action.getSpecialProperties() & StaticProperty.ALL_NODES_UNTYPED);
+ return p;
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true if the "action" creates new nodes.
+ * (Nodes created by the condition can't contribute to the result).
+ */
+
+ public final boolean createsNewNodes() {
+ int props = action.getSpecialProperties();
+ return ((props & StaticProperty.NON_CREATIVE) == 0);
+ }
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ *
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ select = doPromotion(select, offer);
+ if (offer.action == PromotionOffer.INLINE_VARIABLE_REFERENCES ||
+ offer.action == PromotionOffer.EXTRACT_GLOBAL_VARIABLES) {
+ // Don't pass on other requests
+ action = doPromotion(action, offer);
+ key = doPromotion(key, offer);
+ }
+ // TODO: promote expressions in the sort key definitions
+ }
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ ArrayList<Expression> list = new ArrayList<Expression>(8);
+ list.add(select);
+ list.add(action);
+ list.add(key);
+ if (collationNameExpression != null) {
+ list.add(collationNameExpression);
+ }
+ if (sortKeyDefinitions != null) {
+ for (SortKeyDefinition skd : sortKeyDefinitions) {
+ list.add(skd.getSortKey());
+ Expression e = skd.getOrder();
+ if (e != null) {
+ list.add(e);
+ }
+ e = skd.getCaseOrder();
+ if (e != null) {
+ list.add(e);
+ }
+ e = skd.getDataTypeExpression();
+ if (e != null) {
+ list.add(e);
+ }
+ e = skd.getLanguage();
+ if (e != null) {
+ list.add(e);
+ }
+ e = skd.getCollationNameExpression();
+ if (e != null) {
+ list.add(e);
+ }
+ }
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ ArrayList<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>(8);
+ list.add(new SubExpressionInfo(select, true, false, INSPECTION_CONTEXT));
+ list.add(new SubExpressionInfo(action, false, true, INHERITED_CONTEXT));
+ list.add(new SubExpressionInfo(key, false, true, NODE_VALUE_CONTEXT));
+ if (collationNameExpression != null) {
+ list.add(new SubExpressionInfo(collationNameExpression, true, false, NODE_VALUE_CONTEXT));
+ }
+ if (sortKeyDefinitions != null) {
+ for (SortKeyDefinition skd : sortKeyDefinitions) {
+ list.add(new SubExpressionInfo(skd.getSortKey(), false, true, NODE_VALUE_CONTEXT));
+ Expression e = skd.getOrder();
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ e = skd.getCaseOrder();
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ e = skd.getDataTypeExpression();
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ e = skd.getLanguage();
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ e = skd.getCollationNameExpression();
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ }
+ }
+ return list.iterator();
+
+ }
+
+//#ifdefined STREAM
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions true if Saxon streaming extensions are allowed
+ * @param reasons return parameter to hold reasons for non-streamability
+ */
+
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ int s = select.getStreamability(INSPECTION_CONTEXT, allowExtensions, reasons);
+ if (s == W3C_GROUP_CONSUMING) {
+ reasons.add("Saxon cannot handle nested grouping in streaming mode");
+ return W3C_FREE_RANGING;
+ }
+ int a = action.getStreamability(syntacticContext, allowExtensions, reasons);
+ if (s == W3C_MOTIONLESS && a == W3C_MOTIONLESS) {
+ return W3C_MOTIONLESS;
+ }
+ if (a == W3C_MOTIONLESS || a == W3C_GROUP_CONSUMING) {
+ if (select instanceof GroupVariableReference) {
+ return W3C_GROUP_CONSUMING;
+ } else if (Streamability.isIncrementallyConsuming(select)) {
+ return W3C_CONSUMING;
+ }
+ }
+ return W3C_FREE_RANGING;
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public StreamingAdjunct getStreamingAdjunct() {
+ return new ForEachGroupAdjunct();
+ }
+
+ //#endif
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the set of nodes within the path map
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet target = select.addToPathMap(pathMap, pathMapNodeSet);
+ if (groupBinding != null) {
+ pathMap.registerPathForVariable(groupBinding, target);
+ }
+ if (collationNameExpression != null) {
+ collationNameExpression.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ if (sortKeyDefinitions != null) {
+ for (SortKeyDefinition skd : sortKeyDefinitions) {
+ skd.getSortKey().addToPathMap(pathMap, target);
+ Expression e = skd.getOrder();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ e = skd.getCaseOrder();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ e = skd.getDataTypeExpression();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ e = skd.getLanguage();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ e = skd.getCollationNameExpression();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ }
+ }
+ return action.addToPathMap(pathMap, target);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (action == original) {
+ action = replacement;
+ found = true;
+ }
+ if (collationNameExpression == original) {
+ collationNameExpression = replacement;
+ found = true;
+ }
+ if (key == original) {
+ key = replacement;
+ found = true;
+ }
+ if (sortKeyDefinitions != null) {
+ for (SortKeyDefinition skd : sortKeyDefinitions) {
+ if (skd.getSortKey() == original) {
+ skd.setSortKey(replacement, true);
+ found = true;
+ }
+ if (skd.getOrder() == original) {
+ skd.setOrder(replacement);
+ found = true;
+ }
+ if (skd.getCaseOrder() == original) {
+ skd.setCaseOrder(replacement);
+ found = true;
+ }
+ if (skd.getDataTypeExpression() == original) {
+ skd.setDataTypeExpression(replacement);
+ found = true;
+ }
+ if (skd.getLanguage() == original) {
+ skd.setLanguage(replacement);
+ found = true;
+ }
+ }
+ }
+ return found;
+ }
+
+
+
+ /**
+ * Check that any elements and attributes constructed or returned by this expression are acceptable
+ * in the content model of a given complex type. It's always OK to say yes, since the check will be
+ * repeated at run-time. The process of checking element and attribute constructors against the content
+ * model of a complex type also registers the type of content expected of those constructors, so the
+ * static validation can continue recursively.
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ action.checkPermittedContents(parentType, env, false);
+ }
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+
+ GroupIterator groupIterator = getGroupIterator(context);
+
+ XPathContextMajor c2 = context.newContext();
+ c2.setOrigin(this);
+ c2.setCurrentIterator(groupIterator);
+ c2.setCurrentGroupIterator(groupIterator);
+ c2.setCurrentTemplateRule(null);
+
+ if (controller.isTracing()) {
+ TraceListener listener = controller.getTraceListener();
+ assert listener != null;
+ while (true) {
+ Item item = groupIterator.next();
+ if (item == null) {
+ break;
+ }
+ listener.startCurrentItem(item);
+ action.process(c2);
+ listener.endCurrentItem(item);
+ }
+ } else {
+ while (true) {
+ Item item = groupIterator.next();
+ if (item == null) {
+ break;
+ }
+ action.process(c2);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the expression which, on evaluation, yields the name of the collation to be used
+ * @return the expression that returns the collation name
+ */
+
+ public Expression getCollationNameExpression() {
+ return collationNameExpression;
+ }
+
+ /**
+ * Get (and if necessary, create) the comparator used for comparing grouping key values
+ * @param context XPath dynamic context
+ * @return a StringCollator suitable for comparing the values of grouping keys
+ * @throws XPathException if a failure occurs evaluating the expression that determines the collation name
+ */
+
+ private StringCollator getCollator(XPathContext context) throws XPathException {
+ if (collationNameExpression != null) {
+ StringValue collationValue = (StringValue)collationNameExpression.evaluateItem(context);
+ assert collationValue != null;
+ String cname = collationValue.getStringValue();
+ cname = CollatingFunction.expandCollationURI(cname, baseURI);
+ return context.getCollation(cname);
+ } else {
+ // Fallback - this shouldn't happen
+ return CodepointCollator.getInstance();
+ }
+ }
+
+ public AtomicComparer getAtomicComparer(XPathContext context) throws XPathException {
+ StringCollator coll = collator;
+ if (coll==null) {
+ // The collation is determined at run-time
+ coll = getCollator(context);
+ }
+ return AtomicSortComparer.makeSortComparer(coll, keyItemType, context);
+ }
+
+ private GroupIterator getGroupIterator(XPathContext context) throws XPathException {
+ SequenceIterator population = select.iterate(context);
+
+ // get an iterator over the groups in "order of first appearance"
+
+ GroupIterator groupIterator;
+ switch (algorithm) {
+ case GROUP_BY: {
+ AtomicComparer comparer = getAtomicComparer(context);
+ XPathContext c2 = context.newMinorContext();
+ c2.setCurrentIterator(population);
+ // TODO: how come this needs a new context and group-adjacent doesn't?
+ groupIterator = new GroupByIterator(population, key, c2, comparer, composite);
+ break;
+ }
+ case GROUP_ADJACENT: {
+ AtomicComparer comparer = getAtomicComparer(context);
+ groupIterator = new GroupAdjacentIterator(population, key, context, comparer, composite);
+ break;
+ }
+ case GROUP_STARTING:
+ groupIterator = new GroupStartingIterator(population,
+ ((PatternSponsor)key).getPattern(),
+ context);
+ break;
+ case GROUP_ENDING:
+ groupIterator = new GroupEndingIterator(population,
+ ((PatternSponsor)key).getPattern(),
+ context);
+ break;
+ default:
+ throw new AssertionError("Unknown grouping algorithm");
+ }
+
+ if (groupBinding != null) {
+ groupIterator.setGroupSlot(groupBinding.getLocalSlotNumber());
+ }
+ if (keyBinding != null) {
+ groupIterator.setKeySlot(keyBinding.getLocalSlotNumber());
+ }
+
+
+ // now iterate over the leading nodes of the groups
+
+ if (sortKeyDefinitions != null) {
+ AtomicComparer[] comps = sortComparators;
+ XPathContext xpc = context.newMinorContext();
+ if (comps == null) {
+ comps = new AtomicComparer[sortKeyDefinitions.length];
+ for (int s = 0; s < sortKeyDefinitions.length; s++) {
+ comps[s] = sortKeyDefinitions[s].makeComparator(xpc);
+ }
+ }
+ groupIterator = new SortedGroupIterator(xpc, groupIterator, this, comps);
+
+ if (groupBinding != null) {
+ groupIterator.setGroupSlot(groupBinding.getLocalSlotNumber());
+ }
+ if (keyBinding != null) {
+ groupIterator.setKeySlot(keyBinding.getLocalSlotNumber());
+ }
+ }
+
+ return groupIterator;
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation relies on the process() method: it
+ * "pushes" the results of the instruction to a sequence in memory, and then
+ * iterates over this in-memory sequence.
+ * <p/>
+ * In principle instructions should implement a pipelined iterate() method that
+ * avoids the overhead of intermediate storage.
+ *
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws XPathException if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ GroupIterator master = getGroupIterator(context);
+ XPathContextMajor c2 = context.newContext();
+ c2.setOrigin(this);
+ c2.setCurrentIterator(master);
+ c2.setCurrentGroupIterator(master);
+ c2.setCurrentTemplateRule(null);
+ return new ContextMappingIterator(this, c2);
+ }
+
+ /**
+ * Map one item to a sequence.
+ *
+ * @param context The processing context. This is supplied only for mapping constructs that
+ * set the context node, position, and size. Otherwise it is null.
+ * @return either (a) a SequenceIterator over the sequence of items that the supplied input
+ * item maps to, or (b) an Item if it maps to a single item, or (c) null if it maps to an empty
+ * sequence.
+ */
+
+ public SequenceIterator map(XPathContext context) throws XPathException {
+ return action.iterate(context);
+ }
+
+ /**
+ * Callback for evaluating the sort keys
+ */
+
+ public AtomicValue evaluateSortKey(int n, XPathContext c) throws XPathException {
+ return (AtomicValue) sortKeyDefinitions[n].getSortKey().evaluateItem(c);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ForEachGroup expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ForEachGroupCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("forEachGroup");
+ out.emitAttribute("algorithm", getAlgorithmName(algorithm));
+ out.startSubsidiaryElement("select");
+ select.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("key");
+ key.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("return");
+ action.explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+ private static String getAlgorithmName(byte algorithm) {
+ switch (algorithm) {
+ case GROUP_BY:
+ return "group-by";
+ case GROUP_ADJACENT:
+ return "group-adjacent";
+ case GROUP_STARTING:
+ return "group-starting-with";
+ case GROUP_ENDING:
+ return "group-ending-with";
+ default:
+ return "** unknown algorithm **";
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/GeneralVariable.java b/sf/saxon/expr/instruct/GeneralVariable.java
new file mode 100644
index 0000000..dd29f50
--- /dev/null
+++ b/sf/saxon/expr/instruct/GeneralVariable.java
@@ -0,0 +1,563 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+import javax.xml.transform.SourceLocator;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+* This class defines common behaviour across xsl:variable, xsl:param, and xsl:with-param;
+* also saxon:assign
+*/
+
+public abstract class GeneralVariable implements Binding, SourceLocator {
+
+ private static final int REQUIRED = 4;
+ private static final int TUNNEL = 8;
+ private static final int IMPLICITLY_REQUIRED = 16; // a parameter that is required because the fallback
+ // value is not a valid instance of the type.
+
+ private byte properties = 0;
+ Expression select = null;
+ protected StructuredQName variableQName;
+ SequenceType requiredType;
+ protected int slotNumber;
+ protected int referenceCount = 10;
+ protected int evaluationMode = ExpressionTool.UNDECIDED;
+ private Container container;
+ private int locationId = -1;
+
+ /**
+ * Create a general variable
+ */
+
+ public GeneralVariable() {}
+
+ /**
+ * Initialize the properties of the variable
+ * @param select the expression to which the variable is bound
+ * @param qName the name of the variable
+ */
+
+ public void init(Expression select, StructuredQName qName) {
+ this.select = select;
+ variableQName = qName;
+ //adoptChildExpression(select);
+ }
+
+ /**
+ * Mark a variable as being in a given Container. This link is used primarily for diagnostics:
+ * the container links to the location map held in the executable.
+ *
+ * <p>This affects the expression and all its subexpressions. Any subexpressions that are not in the
+ * same container are marked with the new container, and this proceeds recursively. However, any
+ * subexpression that is already in the correct container is not modified.</p>
+ *
+ * @param container The container of this expression.
+ */
+
+ public void setContainer(Container container) {
+ this.container = container;
+ if (container != null) {
+ Iterator children = iterateSubExpressions();
+ while (children.hasNext()) {
+ Expression child = (Expression)children.next();
+ // child can be null while expressions are under construction
+ Container childContainer;
+ if (child != null &&
+ (childContainer = child.getContainer()) != container &&
+ (childContainer == null || childContainer.getContainerGranularity() < container.getContainerGranularity())) {
+ child.setContainer(container);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the container in which this expression is located. This will usually be a top-level construct
+ * such as a function or global variable, and XSLT template, or an XQueryExpression. In the case of
+ * free-standing XPath expressions it will be the StaticContext object
+ * @return the expression's container
+ */
+
+ public Container getContainer() {
+ return container;
+ }
+
+ /**
+ * Set the location ID on an expression.
+ * @param id the location id
+ */
+
+ public void setLocationId(int id) {
+ locationId = id;
+ }
+
+ /**
+ * Get the location ID of the expression
+ * @return a location identifier, which can be turned into real
+ * location information by reference to a location provider
+ */
+
+ public final int getLocationId() {
+ return locationId;
+ }
+
+ /**
+ * Get the line number of the expression
+ */
+
+ public int getLineNumber() {
+ if (locationId == -1) {
+ return -1;
+ }
+ return locationId & 0xfffff;
+ }
+
+ /**
+ * Get the column number of the expression
+ */
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Get the systemId of the module containing the expression
+ */
+
+ public String getSystemId() {
+ if (locationId == -1) {
+ return null;
+ }
+ Executable exec = getExecutable();
+ if (exec == null) {
+ return null;
+ }
+ LocationMap map = exec.getLocationMap();
+ if (map == null) {
+ return null;
+ }
+ return map.getSystemId(locationId);
+ }
+
+ /**
+ * Get the publicId of the module containing the expression (to satisfy the SourceLocator interface)
+ */
+
+ public final String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Get the executable containing this expression
+ * @return the containing Executable
+ */
+
+ public Executable getExecutable() {
+ Container container = getContainer();
+ return container == null ? null : container.getExecutable();
+ }
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ * @return the LocationProvider used to turn the location id into real location information
+ */
+
+ public LocationProvider getLocationProvider() {
+ Container container = getContainer();
+ return container == null ? null : container.getLocationProvider();
+ }
+
+ public String getSystemId(long locationId) {
+ return getLocationProvider().getSystemId(locationId);
+ }
+
+// public int getLineNumber(long locationId) {
+// return getLineNumber();
+// }
+//
+// public int getColumnNumber(long locationId) {
+// return getColumnNumber();
+// }
+
+ /**
+ * Set the expression to which this variable is bound
+ * @param select the initializing expression
+ */
+
+ public void setSelectExpression(Expression select) {
+ this.select = select;
+ evaluationMode = ExpressionTool.UNDECIDED;
+ }
+
+ /**
+ * Get the expression to which this variable is bound
+ * @return the initializing expression
+ */
+
+ public Expression getSelectExpression() {
+ return select;
+ }
+
+ /**
+ * Set the required type of this variable
+ * @param required the required type
+ */
+
+ public void setRequiredType(SequenceType required) {
+ requiredType = required;
+ }
+
+ /**
+ * Get the required type of this variable
+ * @return the required type
+ */
+
+ public SequenceType getRequiredType() {
+ return requiredType;
+ }
+
+ /**
+ * If the variable is bound to an integer, get the minimum and maximum possible values.
+ * Return null if unknown or not applicable
+ */
+ public IntegerValue[] getIntegerBoundsForVariable() {
+ if (select != null) {
+ return select.getIntegerBounds();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Indicate that this variable represents a required parameter
+ * @param requiredParam true if this is a required parameter
+ */
+
+ public void setRequiredParam(boolean requiredParam) {
+ if (requiredParam) {
+ properties |= REQUIRED;
+ } else {
+ properties &= ~REQUIRED;
+ }
+ }
+
+ /**
+ * Indicate that this variable represents a parameter that is implicitly required (because there is no
+ * usable default value)
+ * @param requiredParam true if this is an implicitly required parameter
+ */
+
+ public void setImplicitlyRequiredParam(boolean requiredParam) {
+ if (requiredParam) {
+ properties |= IMPLICITLY_REQUIRED;
+ } else {
+ properties &= ~IMPLICITLY_REQUIRED;
+ }
+ }
+
+ /**
+ * Indicate whether this variable represents a tunnel parameter
+ * @param tunnel true if this is a tunnel parameter
+ */
+
+ public void setTunnel(boolean tunnel) {
+ if (tunnel) {
+ properties |= TUNNEL;
+ } else {
+ properties &= ~TUNNEL;
+ }
+ }
+
+ /**
+ * Set the nominal number of references to this variable
+ * @param refCount the nominal number of references
+ */
+
+ public void setReferenceCount(int refCount) {
+ referenceCount = refCount;
+ }
+
+ /**
+ * Get the evaluation mode of the variable
+ * @return the evaluation mode (a constant in {@link ExpressionTool}
+ */
+
+ public int getEvaluationMode() {
+ if (evaluationMode == ExpressionTool.UNDECIDED) {
+ if (referenceCount == FilterExpression.FILTERED) {
+ evaluationMode = ExpressionTool.MAKE_INDEXED_VARIABLE;
+ } else {
+ evaluationMode = ExpressionTool.lazyEvaluationMode(select);
+ }
+ }
+ return evaluationMode;
+ }
+
+ /**
+ * Get the cardinality of the result of this instruction. An xsl:variable instruction returns nothing, so the
+ * type is empty.
+ * @return the empty cardinality.
+ */
+
+ public int getCardinality() {
+ return StaticProperty.EMPTY;
+ }
+
+ public boolean isAssignable() {
+ return false;
+ }
+
+ public boolean isGlobal() {
+ return false;
+ }
+
+ /**
+ * If this is a local variable held on the local stack frame, return the corresponding slot number.
+ * In other cases, return -1.
+ */
+
+ public int getLocalSlotNumber() {
+ return slotNumber;
+ }
+
+ /**
+ * Ask whether this variable represents a required parameter
+ * @return true if this is a required parameter
+ */
+
+ public final boolean isRequiredParam() {
+ return (properties & REQUIRED) != 0;
+ }
+
+ /**
+ * Ask whether this variable represents a parameter that is implicitly required, because there is no usable
+ * default value
+ * @return true if this variable is an implicitly required parameter
+ */
+
+ public final boolean isImplicitlyRequiredParam() {
+ return (properties & IMPLICITLY_REQUIRED) != 0;
+ }
+
+ /**
+ * Ask whether this variable represents a tunnel parameter
+ * @return true if this is a tunnel parameter
+ */
+
+ public final boolean isTunnelParam() {
+ return (properties & TUNNEL) != 0;
+ }
+
+ /**
+ * Get the name of this instruction (that is xsl:variable, xsl:param etc) for diagnostics
+ * @return the name of this instruction, as a name pool name code
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_VARIABLE;
+ }
+
+ /**
+ * Simplify this variable
+ * @param visitor an expression
+ * @throws XPathException if a failure occurs
+ */
+
+ public void simplify(ExpressionVisitor visitor) throws XPathException {
+ if (select != null) {
+ select = visitor.simplify(select);
+ }
+ }
+
+ public void typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (select != null) {
+ select = visitor.typeCheck(select, contextItemType);
+ //adoptChildExpression(select);
+ }
+ checkAgainstRequiredType(visitor);
+ }
+
+ public void optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (select != null) {
+ select = visitor.optimize(select, contextItemType);
+ //adoptChildExpression(select);
+ computeEvaluationMode();
+ }
+ }
+
+ public void computeEvaluationMode() {
+ if (isAssignable()) {
+ evaluationMode = ExpressionTool.eagerEvaluationMode(select);
+ } else if (referenceCount == FilterExpression.FILTERED) {
+ evaluationMode = ExpressionTool.MAKE_INDEXED_VARIABLE;
+ } else {
+ evaluationMode = ExpressionTool.lazyEvaluationMode(select);
+ }
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ public Expression copy() {
+ throw new UnsupportedOperationException("GeneralVariable.copy()");
+ }
+
+ public void addReference(boolean isLoopingReference) {
+
+ }
+
+ /**
+ * Check the select expression against the required type.
+ * @param visitor an expression visitor
+ * @throws XPathException if the check fails
+ */
+
+ public void checkAgainstRequiredType(ExpressionVisitor visitor)
+ throws XPathException {
+ // Note, in some cases we are doing this twice.
+ RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, variableQName, 0);
+ //role.setSourceLocator(this);
+ SequenceType r = requiredType;
+ if (r != null && select != null) {
+ // check that the expression is consistent with the required type
+ select = TypeChecker.staticTypeCheck(select, requiredType, false, role, visitor);
+ }
+ }
+
+ /**
+ * Evaluate the variable. That is,
+ * get the value of the select expression if present or the content
+ * of the element otherwise, either as a tree or as a sequence
+ * @param context the XPath dynamic context
+ * @return the result of evaluating the variable
+ * @throws net.sf.saxon.trans.XPathException if evaluation of the select expression fails
+ * with a dynamic error
+ */
+
+ public Sequence getSelectValue(XPathContext context) throws XPathException {
+ if (select==null) {
+ throw new AssertionError("*** No select expression!!");
+ // The value of the variable is a sequence of nodes and/or atomic values
+
+ } else {
+ // There is a select attribute: do a lazy evaluation of the expression,
+ // which will already contain any code to force conversion to the required type.
+
+ return ExpressionTool.evaluate(select, evaluationMode, context, referenceCount);
+
+ }
+ }
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ * @return an iterator over all the contained expressions (in practice,
+ * the select expression)
+ */
+
+
+ public Iterator<Expression> iterateSubExpressions() {
+ if (select != null) {
+ return new MonoIterator<Expression>(select);
+ } else {
+ List<Expression> list = Collections.emptyList();
+ return list.iterator();
+ }
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ * @param out the object used to present the output
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("variable");
+ out.emitAttribute("name", variableQName.getDisplayName());
+ if (select != null) {
+ select.explain(out);
+ }
+ out.endElement();
+ }
+
+ /**
+ * Get the slot number allocated to this variable
+ * @return the slot number, that is the position allocated to the variable on its stack frame
+ */
+
+ public int getSlotNumber() {
+ return slotNumber;
+ }
+
+ /**
+ * Set the slot number of this variable
+ * @param s the slot number, that is, the position allocated to this variable on its stack frame
+ */
+
+ public void setSlotNumber(int s) {
+ slotNumber = s;
+ }
+
+ /**
+ * Set the name of the variable
+ * @param s the name of the variable (a QName)
+ */
+
+ public void setVariableQName(StructuredQName s) {
+ variableQName = s;
+ }
+
+ /**
+ * Get the name of this variable
+ * @return the name of this variable (a QName)
+ */
+
+ public StructuredQName getVariableQName() {
+ return variableQName;
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/GlobalParam.java b/sf/saxon/expr/instruct/GlobalParam.java
new file mode 100644
index 0000000..1a5d93f
--- /dev/null
+++ b/sf/saxon/expr/instruct/GlobalParam.java
@@ -0,0 +1,82 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* The compiled form of a global xsl:param element in an XSLT stylesheet or an
+* external variable declared in the prolog of a Query. <br>
+* The xsl:param element in XSLT has mandatory attribute name and optional attribute select. It can also
+* be specified as required="yes" or required="no". In standard XQuery 1.0 external variables are always required,
+* and no default value can be specified; but Saxon provides an extension pragma that allows a query
+* to specify a default. XQuery 1.1 adds standard syntax for defining a default value.
+*/
+
+public final class GlobalParam extends GlobalVariable {
+
+ public GlobalParam() {}
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_PARAM;
+ }
+
+ /**
+ * Evaluate the variable
+ */
+
+ @Override
+ public Sequence evaluateVariable(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ Bindery b = controller.getBindery();
+ boolean wasSupplied;
+ try {
+ wasSupplied = b.useGlobalParameter(
+ getVariableQName(), getSlotNumber(), getRequiredType(), context);
+ } catch (XPathException e) {
+ e.setLocator(this);
+ throw e;
+ }
+
+ Sequence val = b.getGlobalVariableValue(this);
+ if (wasSupplied || val!=null) {
+ return val;
+ } else {
+ if (isRequiredParam()) {
+ XPathException e = new XPathException("No value supplied for required parameter $" +
+ getVariableQName().getDisplayName());
+ e.setXPathContext(context);
+ e.setLocator(this);
+ e.setErrorCode(getHostLanguage() == Configuration.XSLT ? "XTDE0050" : "XPDY0002");
+ throw e;
+ } else if (isImplicitlyRequiredParam()) {
+ XPathException e = new XPathException("A value must be supplied for parameter $" +
+ getVariableQName().getDisplayName() +
+ " because there is no default value for the required type");
+ e.setXPathContext(context);
+ e.setLocator(this);
+ e.setErrorCode("XTDE0610");
+ throw e;
+ }
+ // evaluate and save the default value
+ return actuallyEvaluate(context);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/GlobalParameterSet.java b/sf/saxon/expr/instruct/GlobalParameterSet.java
new file mode 100644
index 0000000..4663283
--- /dev/null
+++ b/sf/saxon/expr/instruct/GlobalParameterSet.java
@@ -0,0 +1,150 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.JPConverter;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ExternalObjectType;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.ObjectValue;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+
+/**
+ * A GlobalParameterSet is a set of parameters supplied when invoking a stylesheet or query.
+ * It is a collection of name-value pairs, the names being represented by StructuredQName objects.
+ * The values are objects, as supplied by the caller: conversion of the object
+ * to a required type takes place when the parameter is actually used.
+ */
+
+public class GlobalParameterSet
+{
+ private HashMap<StructuredQName, Object> params = new HashMap<StructuredQName, Object>(10);
+
+ /**
+ * Create an empty parameter set
+ */
+
+ public GlobalParameterSet() {}
+
+ /**
+ * Add a parameter to the ParameterSet
+ *
+ * @param qName The fingerprint of the parameter name.
+ * @param value The value of the parameter, or null if the parameter is to be removed
+ */
+
+ public void put (StructuredQName qName, /*@Nullable*/ Object value) {
+ if (value == null) {
+ params.remove(qName);
+ } else {
+ params.put(qName, value);
+ }
+ }
+
+ /**
+ * Get a parameter
+ *
+ * @param qName The parameter name.
+ * @return The value of the parameter, or null if not defined
+ */
+
+ public Object get (StructuredQName qName) {
+ return params.get(qName);
+ }
+
+ public boolean containsKey (StructuredQName qName) {
+ return params.containsKey(qName);
+ }
+
+ /**
+ * Get the value of a parameter, converted to a Sequence object
+ * @param qName the name of the parameter
+ * @param requiredType the required type of the parameter
+ * @param convert set to true if function conversion rules are to be applied, or to false if the value
+ * is simply to be checked against the required type
+ * @param context dynamic evaluation context
+ * @return the value after conversion and type checking; or null if there is no value for this parameter
+ * @throws XPathException if the value is of the wrong type
+ */
+
+ public Sequence convertParameterValue(
+ StructuredQName qName, SequenceType requiredType, boolean convert, XPathContext context)
+ throws XPathException {
+ Sequence val;
+ Object obj = get(qName);
+ if (obj == null) {
+ return null;
+ }
+ if (obj instanceof Sequence) {
+ val = (Sequence)obj;
+ } else if (obj instanceof String && convert) {
+ val = new UntypedAtomicValue((String)obj);
+ } else if (requiredType.getPrimaryType() instanceof ExternalObjectType) {
+ val = new ObjectValue(obj);
+ } else {
+ JPConverter converter = JPConverter.allocate(obj.getClass(), context.getConfiguration());
+ val = converter.convert(obj, context);
+ }
+ if (val==null) {
+ val = EmptySequence.getInstance();
+ }
+
+ if (requiredType != null) {
+ if (convert) {
+ RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, qName.getDisplayName(), -1);
+ Configuration config = context.getConfiguration();
+ val = config.getTypeHierarchy().applyFunctionConversionRules(val, requiredType, role, null);
+ } else {
+ XPathException err = TypeChecker.testConformance(val, requiredType, context);
+ if (err != null) {
+ throw err;
+ }
+ }
+ }
+ return val;
+ }
+
+
+ /**
+ * Clear all values
+ */
+
+ public void clear() {
+ params.clear();
+ }
+
+ /**
+ * Get all the keys that have been allocated
+ * @return the names of the parameter keys (QNames)
+ */
+
+ public Collection<StructuredQName> getKeys() {
+ return params.keySet();
+ }
+
+ /**
+ * Get the number of entries in the result of getKeys() that are significant
+ * @return the number of entries
+ */
+
+ public int getNumberOfKeys() {
+ return params.size();
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/instruct/GlobalVariable.java b/sf/saxon/expr/instruct/GlobalVariable.java
new file mode 100644
index 0000000..b23f564
--- /dev/null
+++ b/sf/saxon/expr/instruct/GlobalVariable.java
@@ -0,0 +1,682 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.query.XQueryFunction;
+import net.sf.saxon.query.XQueryFunctionLibrary;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.SingletonClosure;
+
+import java.util.*;
+
+/**
+* A compiled global variable in a stylesheet or query. <br>
+*/
+
+public class GlobalVariable extends GeneralVariable implements Container, net.sf.saxon.query.Declaration, InstructionInfo {
+
+ protected List<BindingReference> references = new ArrayList<BindingReference>(10);
+ // Note that variableReferences on this list might be dormant;
+ // that is, they might be disconnected from the live expression tree.
+
+ private Executable executable;
+ /*@Nullable*/ private SlotManager stackFrameMap = null;
+ private boolean indexed;
+ private int lineNumber;
+ private String systemId;
+ private boolean isPrivate = false;
+ private boolean isAssignable = false;
+ private GlobalVariable originalVariable;
+
+ /**
+ * Create a global variable
+ */
+
+ public GlobalVariable(){}
+
+ /**
+ * Get the executable containing this global variable
+ * @return the containing executable
+ */
+
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Set the containing executable
+ * @param executable the executable that contains this global variable
+ */
+
+ public void setExecutable(Executable executable) {
+ this.executable = executable;
+ }
+
+ /**
+ * Say that this (XQuery) variable is a copy of some originally declared variable. It's a
+ * separate variable when imported into another module, but it retains the link
+ * to the original.
+ * @param var the variable in the imported module from which this variable is derived
+ */
+
+ public void setOriginalVariable(GlobalVariable var) {
+ originalVariable = var;
+ }
+
+ /**
+ * Get the original declaration of this variable
+ * @return the variable in the imported module from which this variable is derived
+ */
+
+ public GlobalVariable getOriginalVariable() {
+ return originalVariable;
+ }
+
+ /**
+ * Get the original declaration of this variable, or its original declaration, transitively
+ * @return the real variable declaration in some transitively imported module from which this variable is
+ * ultimately derived
+ */
+
+ public GlobalVariable getUltimateOriginalVariable() {
+ if (originalVariable == null) {
+ return this;
+ } else {
+ return originalVariable.getUltimateOriginalVariable();
+ }
+ }
+
+ /**
+ * Set the line number where the variable declaration appears in the source
+ * @param lineNumber the line number
+ */
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Get the line number where the declaration appears
+ */
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public int getLineNumber(long locationId) {
+ return lineNumber;
+ }
+
+ /**
+ * Set the system ID of the module where the variable declaration appears
+ * @param systemId the System ID (base URI)
+ */
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the system ID of the module containing the variable declaration
+ * @return the System ID (base URI)
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Get the column number within the document, entity, or module containing a particular location
+ *
+ * @param locationId identifier of the location in question (as passed down the Receiver pipeline)
+ * @return the column number within the document, entity, or module, or -1 if this is not available
+ */
+ public int getColumnNumber(long locationId) {
+ return executable.getLocationMap().getLineNumber(locationId);
+ }
+
+ /**
+ * Ask whether this global variable is private
+ * @return true if this variable is private
+ */
+
+ public boolean isPrivate() {
+ return isPrivate;
+ }
+
+ /**
+ * Say whether this global variable is a "parameter" (an external variable, in XQuery terminology)
+ * @param b true if this variable is external
+ */
+ public void setPrivate(boolean b) {
+ isPrivate = b;
+ }
+
+ /**
+ * Indicate whether this variable is assignable using saxon:assign
+ * @param assignable true if this variable is assignable
+ */
+
+ public void setAssignable(boolean assignable) {
+ isAssignable = assignable;
+ }
+
+ /**
+ * Test whether it is permitted to assign to the variable using the saxon:assign
+ * extension element. This will only be true if the extra attribute saxon:assignable="yes"
+ * is present.
+ */
+
+ public final boolean isAssignable() {
+ return isAssignable;
+ }
+
+
+
+ /**
+ * Get the type of construct. This will either be the fingerprint of a standard XSLT instruction name
+ * (values in {@link net.sf.saxon.om.StandardNames}: all less than 1024)
+ * or it will be a constant in class {@link net.sf.saxon.trace.Location}.
+ *
+ * @return an integer identifying the kind of construct
+ */
+ public int getConstructType() {
+ return StandardNames.XSL_VARIABLE;
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ *
+ * @return the QName of the object declared or manipulated by this instruction or expression
+ */
+ public StructuredQName getObjectName() {
+ return getVariableQName();
+ }
+
+ /**
+ * Get the value of a particular property of the instruction. Properties
+ * of XSLT instructions are generally known by the name of the stylesheet attribute
+ * that defines them.
+ *
+ * @param name The name of the required property
+ * @return The value of the requested property, or null if the property is not available
+ */
+ public Object getProperty(String name) {
+ return null;
+ }
+
+ /**
+ * Get an iterator over all the properties available. The values returned by the iterator
+ * will be of type String, and each string can be supplied as input to the getProperty()
+ * method to retrieve the value of the property. The iterator may return properties whose
+ * value is null.
+ *
+ * @return an iterator over the properties.
+ */
+ public Iterator<String> getProperties() {
+ List<String> list = Collections.emptyList();
+ return list.iterator();
+ }
+
+ /**
+ * Get the container in which this expression is located. This will usually be a top-level construct
+ * such as a function or global variable, and XSLT template, or an XQueryExpression. In the case of
+ * free-standing XPath expressions it will be the StaticContext object
+ *
+ * @return the expression's container
+ */
+ @Override
+ public Container getContainer() {
+ return this;
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+
+ public int getHostLanguage() {
+ return executable.getHostLanguage();
+ }
+
+ /**
+ * Mark this as an indexed variable, to allow fast searching
+ */
+
+ public void setIndexedVariable() {
+ indexed = true;
+ }
+
+ /**
+ * Ask whether this is an indexed variable
+ * @return true if this variable is indexed
+ */
+
+ public boolean isIndexedVariable() {
+ return indexed;
+ }
+
+ /**
+ * Get the granularity of the container.
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 2;
+ }
+
+ /**
+ * The expression that initializes a global variable may itself use local variables.
+ * In this case a stack frame needs to be allocated while evaluating the global variable
+ * @param map The stack frame map for local variables used while evaluating this global
+ * variable.
+ */
+
+ public void setContainsLocals(SlotManager map) {
+ this.stackFrameMap = map;
+ }
+
+ /**
+ * Is this a global variable?
+ * @return true (yes, it is a global variable)
+ */
+
+ public boolean isGlobal() {
+ return true;
+ }
+
+ /**
+ * Register a variable reference that refers to this global variable
+ * @param ref the variable reference
+ */
+ public void registerReference(BindingReference ref) {
+ references.add(ref);
+ }
+
+ /**
+ * Iterate over the references to this variable
+ * @return an iterator over the references: returns objects of class {@link VariableReference}
+ */
+
+ public Iterator iterateReferences() {
+ return references.iterator();
+ }
+
+ /**
+ * Create a compiled representation of this global variable
+ * @param exec the executable
+ * @param slot the slot number allocated to this variable
+ * @throws XPathException if compile-time errors are found.
+ */
+
+ public void compile(/*@NotNull*/ Executable exec, int slot) throws XPathException {
+
+ if (references.isEmpty()) {
+ return;
+ }
+ TypeHierarchy th = exec.getConfiguration().getTypeHierarchy();
+
+ setSlotNumber(slot);
+ if (this instanceof GlobalParam) {
+ setRequiredParam(select == null);
+ }
+ final SequenceType type = getRequiredType();
+ for (BindingReference ref : references) {
+ ref.fixup(this);
+ GroundedValue constantValue = null;
+ int properties = 0;
+ Expression select = getSelectExpression();
+ if (select instanceof Literal && !(this instanceof GlobalParam)) {
+ // we can't rely on the constant value because it hasn't yet been type-checked,
+ // which could change it (eg by numeric promotion). Rather than attempt all the type-checking
+ // now, we do a quick check. See test bug64
+ int relation = th.relationship(select.getItemType(th), type.getPrimaryType());
+ if (relation == TypeHierarchy.SAME_TYPE || relation == TypeHierarchy.SUBSUMED_BY) {
+ constantValue = ((Literal) select).getValue();
+ }
+ }
+ if (select != null) {
+ properties = select.getSpecialProperties();
+ }
+ properties |= StaticProperty.NON_CREATIVE;
+ // a variable reference is non-creative even if its initializer is creative
+ ref.setStaticType(type, constantValue, properties);
+ }
+ exec.registerGlobalVariable(this);
+ setReferenceCount(10); // TODO: temporary!
+ }
+
+
+
+ /**
+ * Type check the compiled representation of this global variable
+ * @param visitor an expression visitor
+ * @throws XPathException if compile-time errors are found.
+ */
+
+ public void typeCheck(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ Expression value = getSelectExpression();
+ if (value != null) {
+ value.checkForUpdatingSubexpressions();
+ if (value.isUpdatingExpression()) {
+ throw new XPathException(
+ "Initializing expression for global variable must not be an updating expression", "XUST0001");
+ }
+ value.setContainer(this);
+ RoleLocator role = new RoleLocator(
+ RoleLocator.VARIABLE, getVariableQName(), 0);
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(AnyItemType.getInstance(), true);
+ Expression value2 = TypeChecker.strictTypeCheck(
+ visitor.typeCheck(visitor.simplify(value), cit),
+ getRequiredType(), role, visitor.getStaticContext());
+ value2 = value2.optimize(visitor, cit);
+ setSelectExpression(value2);
+ value2.setContainer(this);
+ // the value expression may declare local variables
+ SlotManager map = visitor.getConfiguration().makeSlotManager();
+ int slots = ExpressionTool.allocateSlots(value2, 0, map);
+ if (slots > 0) {
+ setContainsLocals(map);
+ }
+
+ if (getRequiredType() == SequenceType.ANY_SEQUENCE && !(this instanceof GlobalParam)) {
+ // no type was declared; try to deduce a type from the value
+ try {
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ final ItemType itemType = value.getItemType(th);
+ final int cardinality = value.getCardinality();
+ setRequiredType(SequenceType.makeSequenceType(itemType, cardinality));
+ GroundedValue constantValue = null;
+ if (value2 instanceof Literal) {
+ constantValue = ((Literal)value2).getValue();
+ }
+ for (BindingReference reference : references) {
+ BindingReference ref = (reference);
+ if (ref instanceof VariableReference) {
+ ((VariableReference) ref).refineVariableType(
+ itemType, cardinality, constantValue, value.getSpecialProperties(), visitor);
+ }
+ }
+ } catch (Exception err) {
+ // exceptions can happen because references to variables and functions are still unbound
+ }
+ }
+
+
+ }
+ }
+
+
+ /**
+ * Check for cycles in this variable definition
+ * @param referees the calls leading up to this one; it's an error if this variable is on the
+ * stack, because that means it calls itself directly or indirectly. The stack may contain
+ * variable definitions (GlobalVariable objects) and user-defined functions (UserFunction objects).
+ * It will never contain the same object more than once.
+ * @param globalFunctionLibrary the library containing all global functions
+ * @throws net.sf.saxon.trans.XPathException if cycles are found
+ */
+
+ public void lookForCycles(Stack<Container> referees, XQueryFunctionLibrary globalFunctionLibrary) throws XPathException {
+ if (referees.contains(this)) {
+ int s = referees.indexOf(this);
+ referees.push(this);
+ String message = "Circular definition of global variable: ";
+ if (getVariableQName().getLocalPart().equals("context-item")) {
+ message += "Context item";
+ } else {
+ message += "$" + getVariableQName().getDisplayName();
+ }
+
+ for (int i = s; i < referees.size() - 1; i++) {
+ if (i != s) {
+ message += ", which";
+ }
+ if (referees.get(i + 1) instanceof GlobalVariable) {
+ GlobalVariable next = (GlobalVariable)referees.get(i + 1);
+ if (next.getVariableQName().getLocalPart().equals("context-item")) {
+ message += " uses context item";
+ } else {
+ message += " uses $" + next.getVariableQName().getDisplayName();
+ }
+ } else if (referees.get(i + 1) instanceof XQueryFunction) {
+ XQueryFunction next = (XQueryFunction)referees.get(i + 1);
+ message += " calls " + next.getFunctionName().getDisplayName() +
+ "#" + next.getNumberOfArguments() + "()";
+ }
+ }
+ message += '.';
+ XPathException err = new XPathException(message);
+ String errorCode;
+ if (getHostLanguage() == Configuration.XSLT) {
+ errorCode = "XTDE0640";
+ } else if (executable.isAllowXPath30()) {
+ errorCode = "XQDY0054";
+ } else {
+ errorCode = "XQST0054";
+ }
+ err.setErrorCode(errorCode);
+ err.setIsStaticError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ if (select != null) {
+ referees.push(this);
+ List<Binding> list = new ArrayList<Binding>(10);
+ ExpressionTool.gatherReferencedVariables(select, list);
+ for (Binding b : list) {
+ if (b instanceof GlobalVariable) {
+ ((GlobalVariable) b).lookForCycles(referees, globalFunctionLibrary);
+ }
+ }
+ List<String> flist = new ArrayList<String>();
+ ExpressionTool.gatherCalledFunctionNames(select, flist);
+ for (String s : flist) {
+ XQueryFunction f = globalFunctionLibrary.getDeclarationByKey(s);
+ if (!referees.contains(f)) {
+ // recursive function calls are allowed
+ lookForFunctionCycles(f, referees, globalFunctionLibrary);
+ }
+ }
+ referees.pop();
+ }
+ }
+
+ /**
+ * Look for cyclic variable references that go via one or more function calls
+ * @param f a used-defined function
+ * @param referees a list of variables and functions that refer directly or indirectly to this variable
+ * @param globalFunctionLibrary the library containing all global functions
+ */
+
+ private static void lookForFunctionCycles(
+ XQueryFunction f, Stack<Container> referees, XQueryFunctionLibrary globalFunctionLibrary) throws XPathException {
+ Expression body = f.getBody();
+ referees.push(f);
+ List<Binding> list = new ArrayList<Binding>(10);
+ ExpressionTool.gatherReferencedVariables(body, list);
+ for (Binding b : list) {
+ if (b instanceof GlobalVariable) {
+ ((GlobalVariable) b).lookForCycles(referees, globalFunctionLibrary);
+ }
+ }
+ List<String> flist = new ArrayList<String>();
+ ExpressionTool.gatherCalledFunctionNames(body, flist);
+ for (String s : flist) {
+ XQueryFunction qf = globalFunctionLibrary.getDeclarationByKey(s);
+ if (!referees.contains(qf)) {
+ // recursive function calls are allowed
+ lookForFunctionCycles(qf, referees, globalFunctionLibrary);
+ }
+ }
+ referees.pop();
+ }
+
+ /**
+ * Evaluate the variable. That is,
+ * get the value of the select expression if present or the content
+ * of the element otherwise, either as a tree or as a sequence
+ */
+
+ public Sequence getSelectValue(XPathContext context) throws XPathException {
+ if (select==null) {
+ throw new AssertionError("*** No select expression for global variable $" +
+ getVariableQName().getDisplayName() + "!!");
+ } else {
+ try {
+ XPathContextMajor c2 = context.newCleanContext();
+ c2.setOrigin(this);
+ final Controller controller = c2.getController();
+ assert controller != null;
+ UnfailingIterator initialNode =
+ SingletonIterator.makeIterator(controller.getContextForGlobalVariables());
+ initialNode.next();
+ c2.setCurrentIterator(initialNode);
+ if (stackFrameMap != null) {
+ c2.openStackFrame(stackFrameMap);
+ }
+ return ExpressionTool.evaluate(select, evaluationMode, c2, referenceCount);
+ } catch (XPathException e) {
+ if (!getVariableQName().getURI().equals(NamespaceConstant.SAXON_GENERATED_GLOBAL)) {
+ e.setIsGlobalError(true);
+ }
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * Evaluate the variable
+ */
+
+ public Sequence evaluateVariable(XPathContext context) throws XPathException {
+ final Controller controller = context.getController();
+ assert controller != null;
+ final Bindery b = controller.getBindery();
+
+ final Sequence v = b.getGlobalVariable(getSlotNumber());
+
+ if (v != null) {
+ return v;
+ } else {
+ return actuallyEvaluate(context);
+ }
+ }
+
+ /**
+ * Evaluate the global variable, and save its value for use in subsequent references.
+ * @param context the XPath dynamic context
+ * @return the value of the variable
+ * @throws XPathException if evaluation fails
+ */
+
+ protected Sequence actuallyEvaluate(XPathContext context) throws XPathException {
+ final Controller controller = context.getController();
+ assert controller != null;
+ final Bindery b = controller.getBindery();
+ try {
+ // This is the first reference to a global variable; try to evaluate it now.
+ // But first check for circular dependencies.
+ setDependencies(b, this, context);
+
+ // Set a flag to indicate that the variable is being evaluated. This is designed to prevent
+ // (where possible) the same global variable being evaluated several times in different threads
+ boolean go = b.setExecuting(this);
+ if (!go) {
+ // some other thread has evaluated the variable while we were waiting
+ return b.getGlobalVariable(getSlotNumber());
+ }
+
+ Sequence value = getSelectValue(context);
+ if (indexed) {
+ value = controller.getConfiguration().obtainOptimizer().makeIndexedValue(value.iterate());
+ }
+ return b.saveGlobalVariableValue(this, value);
+
+ } catch (XPathException err) {
+ b.setNotExecuting(this);
+ if (err instanceof XPathException.Circularity) {
+ String errorCode;
+ if (getHostLanguage() == Configuration.XSLT) {
+ errorCode = "XTDE0640";
+ } else if (executable.isAllowXPath30()) {
+ errorCode = "XQDY0054";
+ } else {
+ errorCode = "XQST0054";
+ }
+ err.setErrorCode(errorCode);
+ err.setXPathContext(context);
+ // Detect it more quickly the next time (in a pattern, the error is recoverable)
+ SingletonClosure closure = new SingletonClosure(new ErrorExpression(err), context);
+ b.setGlobalVariable(this, closure);
+ err.setLocator(this);
+ throw err;
+ } else {
+ throw err;
+ }
+ }
+ }
+
+ /**
+ * Get the variable that is immediately dependent on this one, and register the dependency, so
+ * that circularities can be detected across threads. This relies on the fact that when the initialiser
+ * for variable X contains a reference to variable Y, then when Y is evaluated, a stack frame will be found
+ * on the context stack representing the evaluation of X. We don't set a dependency from X to Y if the value
+ * of Y was already available in the Bindery; it's not needed, because in this case we know that evaluation
+ * of Y is unproblematic, and can't lead to any circularities.
+ * @param bindery the Bindery
+ * @param var the global variable or parameter being evaluated
+ * @param context the dynamic evaluation context
+ */
+
+ protected static void setDependencies(Bindery bindery, GlobalVariable var, XPathContext context) throws XPathException {
+ if (!(context instanceof XPathContextMajor)) {
+ context = getMajorCaller(context);
+ }
+ while (context != null) {
+ do {
+ InstructionInfo instructionInfo = ((XPathContextMajor) context).getOrigin();
+ if (instructionInfo instanceof GlobalVariable) {
+ bindery.registerDependency((GlobalVariable)instructionInfo, var);
+ return;
+ }
+ context = getMajorCaller(context);
+ } while (context != null);
+ }
+ }
+
+ private static XPathContextMajor getMajorCaller(XPathContext context) {
+ XPathContext caller = context.getCaller();
+ while (!(caller == null || caller instanceof XPathContextMajor)) {
+ caller = caller.getCaller();
+ }
+ return (XPathContextMajor)caller;
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/ITemplateCall.java b/sf/saxon/expr/instruct/ITemplateCall.java
new file mode 100644
index 0000000..e387e98
--- /dev/null
+++ b/sf/saxon/expr/instruct/ITemplateCall.java
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+/**
+ * An interface satisfied by all instructions that invoke templates: apply-templates,
+ * call-template. apply-imports, next-match
+ */
+
+public interface ITemplateCall {
+
+ /**
+ * Get the actual parameters passed to the called template
+ * @return the non-tunnel parameters
+ */
+
+ /*@NotNull*/
+ WithParam[] getActualParams();
+
+ /**
+ * Get the tunnel parameters passed to the called template
+ * @return the tunnel parameters
+ */
+
+ /*@NotNull*/
+ WithParam[] getTunnelParams();
+}
+
diff --git a/sf/saxon/expr/instruct/Instruction.java b/sf/saxon/expr/instruct/Instruction.java
new file mode 100644
index 0000000..dc4ced3
--- /dev/null
+++ b/sf/saxon/expr/instruct/Instruction.java
@@ -0,0 +1,443 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.SequenceOutputter;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+* Abstract superclass for all instructions in the compiled stylesheet.
+* This represents a compiled instruction, and as such, the minimum information is
+* retained from the original stylesheet. <br>
+* Note: this class implements SourceLocator: that is, it can identify where in the stylesheet
+* the source instruction was located.
+*/
+
+public abstract class Instruction extends Expression implements SourceLocator, TailCallReturner {
+
+ /**
+ * Constructor
+ */
+
+ public Instruction() {}
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is prefered. For instructions this is the process() method.
+ */
+
+ public int getImplementationMethod() {
+ return Expression.PROCESS_METHOD;
+ }
+
+ /**
+ * Get the namecode of the instruction for use in diagnostics
+ * @return a code identifying the instruction: typically but not always
+ * the fingerprint of a name in the XSLT namespace
+ */
+
+ public int getInstructionNameCode() {
+ return -1;
+ }
+
+ /**
+ * Get a name identifying the kind of instruction, in terms meaningful to a user. This method
+ * is not used in the case where the instruction name code is a standard name (<1024).
+ * @return a name identifying the kind of instruction, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the instruction.
+ */
+
+ public String getInstructionName() {
+ return getClass().getName();
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ int code = getInstructionNameCode();
+ if (code < 0) {
+ return getClass().getName();
+ }
+ if (code < 1024) {
+ return StandardNames.getDisplayName(code);
+ } else {
+ return getInstructionName();
+ }
+ }
+
+ /**
+ * Get the item type of the items returned by evaluating this instruction
+ * @return the static item type of the instruction
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return Type.ITEM_TYPE;
+ }
+
+ /**
+ * Get the cardinality of the sequence returned by evaluating this instruction
+ * @return the static cardinality
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * ProcessLeavingTail: called to do the real work of this instruction. This method
+ * must be implemented in each subclass. The results of the instruction are written
+ * to the current Receiver, which can be obtained via the Controller.
+ * @param context The dynamic context of the transformation, giving access to the current node,
+ * the current variables, etc.
+ * @return null if the instruction has completed execution; or a TailCall indicating
+ * a function call or template call that is delegated to the caller, to be made after the stack has
+ * been unwound so as to save stack space.
+ */
+
+ public abstract TailCall processLeavingTail(XPathContext context) throws XPathException;
+
+ /**
+ * Process the instruction, without returning any tail calls
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ try {
+ TailCall tc = processLeavingTail(context);
+ while (tc != null) {
+ tc = tc.processLeavingTail();
+ }
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+ /**
+ * Get a SourceLocator identifying the location of this instruction
+ * @return the location of this instruction in the source stylesheet or query
+ */
+
+ public SourceLocator getSourceLocator() {
+ return this;
+ }
+
+ /**
+ * Construct an exception with diagnostic information. Note that this method
+ * returns the exception, it does not throw it: that is up to the caller.
+ * @param loc the location of the error
+ * @param error The exception containing information about the error
+ * @param context The controller of the transformation
+ * @return an exception based on the supplied exception, but with location information
+ * added relating to this instruction
+ */
+
+ protected static XPathException dynamicError(SourceLocator loc, XPathException error, XPathContext context) {
+ if (error instanceof TerminationException) {
+ return error;
+ }
+ error.maybeSetLocation(loc);
+ error.maybeSetContext(context);
+ return error;
+ }
+
+ /**
+ * Assemble a ParameterSet. Method used by instructions that have xsl:with-param
+ * children. This method is used for the non-tunnel parameters.
+ * @param context the XPath dynamic context
+ * @param actualParams the set of with-param parameters that specify tunnel="no"
+ * @return a ParameterSet
+ * @throws net.sf.saxon.trans.XPathException if an error occurs evaluating one of the
+ * parameters
+ */
+
+ public static ParameterSet assembleParams(XPathContext context,
+ WithParam[] actualParams)
+ throws XPathException {
+ if (actualParams == null || actualParams.length == 0) {
+ return null;
+ }
+ ParameterSet params = new ParameterSet(actualParams.length);
+ for (WithParam actualParam : actualParams) {
+ params.put(actualParam.getParameterId(),
+ actualParam.getSelectValue(context),
+ actualParam.isTypeChecked());
+ }
+ return params;
+ }
+
+ /**
+ * Assemble a ParameterSet. Method used by instructions that have xsl:with-param
+ * children. This method is used for the tunnel parameters.
+ * @param context the XPath dynamic context
+ * @param actualParams the set of with-param parameters that specify tunnel="yes"
+ * @return a ParameterSet
+ * @throws net.sf.saxon.trans.XPathException if an error occurs evaluating one of the
+ * tunnel parameters
+ */
+
+ public static ParameterSet assembleTunnelParams(XPathContext context,
+ WithParam[] actualParams)
+ throws XPathException {
+ ParameterSet existingParams = context.getTunnelParameters();
+ if (existingParams == null) {
+ return assembleParams(context, actualParams);
+ }
+ ParameterSet newParams = new ParameterSet(existingParams, (actualParams==null ? 0 : actualParams.length));
+ if (actualParams == null || actualParams.length == 0) {
+ return newParams;
+ }
+ for (WithParam actualParam : actualParams) {
+ newParams.put(actualParam.getParameterId(),
+ actualParam.getSelectValue(context),
+ false);
+ }
+ return newParams;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @exception net.sf.saxon.trans.XPathException if an error is discovered during expression
+ * rewriting
+ * @return the simplified expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public abstract Expression simplify(ExpressionVisitor visitor) throws XPathException;
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ if (createsNewNodes()) {
+ return p;
+ } else {
+ return p | StaticProperty.NON_CREATIVE;
+ }
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns a default value of false
+ * @return true if the instruction creates new nodes (or if it can't be proved that it doesn't)
+ */
+
+ public boolean createsNewNodes() {
+ return false;
+ }
+
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ * @param offer The type of rewrite being offered
+ * @throws XPathException if an error occurs
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ }
+
+ /**
+ * Offer promotion for this subexpression. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent the parent of the subexpression
+ * @exception net.sf.saxon.trans.XPathException if any error is detected
+ * @return if the offer is not accepted, return this expression unchanged.
+ * Otherwise return the result of rewriting the expression to promote
+ * this subexpression
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp!=null) {
+ return exp;
+ } else {
+ promoteInst(offer);
+ return this;
+ }
+ }
+
+ /**
+ * Evaluate an expression as a single item. This always returns either a single Item or
+ * null (denoting the empty sequence). No conversion is done. This method should not be
+ * used unless the static type of the expression is a subtype of "item" or "item?": that is,
+ * it should not be called if the expression may return a sequence. There is no guarantee that
+ * this condition will be detected.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @exception XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @return the node or atomic value that results from evaluating the
+ * expression; or null to indicate that the result is an empty
+ * sequence
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ int m = getImplementationMethod();
+ if ((m & EVALUATE_METHOD) != 0) {
+ throw new AssertionError(
+ "evaluateItem() is not implemented in the subclass " + getClass());
+ } else if ((m & ITERATE_METHOD) != 0) {
+ return iterate(context).next();
+ } else {
+ Controller controller = context.getController();
+ assert controller != null;
+ SequenceReceiver saved = context.getReceiver();
+ SequenceOutputter seq = controller.allocateSequenceOutputter(1);
+ seq.getPipelineConfiguration().setHostLanguage(getHostLanguage());
+ context.setReceiver(seq);
+ process(context);
+ context.setReceiver(saved);
+ seq.close();
+ Item result = seq.getFirstItem();
+ seq.reset();
+ return result;
+ }
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @exception XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ int m = getImplementationMethod();
+ if ((m & EVALUATE_METHOD) != 0) {
+ Item item = evaluateItem(context);
+ if (item==null) {
+ return EmptyIterator.emptyIterator();
+ } else {
+ return SingletonIterator.makeIterator(item);
+ }
+ } else if ((m & ITERATE_METHOD) != 0) {
+ throw new AssertionError("iterate() is not implemented in the subclass " + getClass());
+ } else {
+ return getIteratorFromProcessMethod(context);
+ }
+ }
+
+ /**
+ * Helper method to construct an iterator over the results of the expression when all that
+ * the expression itself offers is a process() method. This builds the entire results of the
+ * expression as a sequence in memory and then iterates over it.
+ * @param context the dynamic evaluation context
+ * @return an iterator over the results of the expression
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ final protected SequenceIterator<? extends Item> getIteratorFromProcessMethod(XPathContext context) throws XPathException {
+ // TODO: this fragment of code appears in many places - reuse it!
+ Controller controller = context.getController();
+ assert controller != null;
+ SequenceReceiver saved = context.getReceiver();
+ SequenceOutputter seq = controller.allocateSequenceOutputter(20);
+ seq.getPipelineConfiguration().setHostLanguage(getHostLanguage());
+ context.setReceiver(seq);
+ process(context);
+ context.setReceiver(saved);
+ seq.close();
+ return seq.iterate();
+ }
+
+ /**
+ * Evaluate an expression as a String. This function must only be called in contexts
+ * where it is known that the expression will return a single string (or where an empty sequence
+ * is to be treated as a zero-length string). Implementations should not attempt to convert
+ * the result to a string, other than converting () to "". This method is used mainly to
+ * evaluate expressions produced by compiling an attribute value template.
+ *
+ * @exception net.sf.saxon.trans.XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @exception java.lang.ClassCastException if the result type of the
+ * expression is not xs:string?
+ * @param context The context in which the expression is to be evaluated
+ * @return the value of the expression, evaluated in the current context.
+ * The expression must return a string or (); if the value of the
+ * expression is (), this method returns "".
+ */
+
+ public final CharSequence evaluateAsString(XPathContext context) throws XPathException {
+ Item item = evaluateItem(context);
+ if (item==null) {
+ return "";
+ } else {
+ return item.getStringValue();
+ }
+ }
+
+ /**
+ * Get the type of this expression for use in tracing and diagnostics
+ *
+ * @return the type of expression, as enumerated in class {@link net.sf.saxon.trace.Location}
+ */
+
+ public int getConstructType() {
+ return getInstructionNameCode();
+ }
+
+ /**
+ * Establish whether this is an XSLT instruction or an XQuery instruction
+ * (used to produce appropriate diagnostics)
+ * @return true for XSLT, false for XQuery
+ */
+ public boolean isXSLT() {
+ return getHostLanguage() == Configuration.XSLT;
+ }
+}
+
+
diff --git a/sf/saxon/expr/instruct/InstructionDetails.java b/sf/saxon/expr/instruct/InstructionDetails.java
new file mode 100644
index 0000000..19c10da
--- /dev/null
+++ b/sf/saxon/expr/instruct/InstructionDetails.java
@@ -0,0 +1,184 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trace.Location;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+* Details about an instruction, used when reporting errors and when tracing
+*/
+
+public final class InstructionDetails implements InstructionInfo, Serializable {
+
+ private int constructType = Location.UNCLASSIFIED;
+ /*@Nullable*/ private String systemId = null;
+ private int lineNumber = -1;
+ private int columnNumber = -1;
+ /*@Nullable*/ private StructuredQName objectName;
+ private HashMap<String, Object> properties = new HashMap<String, Object>(5);
+
+ public InstructionDetails() {}
+
+ /**
+ * Set the type of construct
+ * @param type the type of contruct
+ */
+
+ public void setConstructType(int type) {
+ constructType = type;
+ }
+
+ /**
+ * Get the construct type
+ */
+ public int getConstructType() {
+ return constructType;
+ }
+
+ /**
+ * Set the URI of the module containing the instruction
+ * @param systemId the module's URI, or null indicating unknown
+ */
+
+ public void setSystemId(/*@Nullable*/ String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the URI of the module containing the instruction
+ * @return the module's URI, or null indicating unknown
+ */
+
+ /*@Nullable*/
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Set the line number of the instruction within the module
+ * @param lineNumber the line number
+ */
+
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Get the line number of the instruction within its module
+ * @return the line number
+ */
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Set a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ * @param qName the name of the object, for example a function or variable name, or null to indicate
+ * that it has no name
+ */
+
+ public void setObjectName(/*@Nullable*/ StructuredQName qName) {
+ objectName = qName;
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ * @return the name of the object, or null to indicate that it has no name
+ */
+
+ /*@Nullable*/
+ public StructuredQName getObjectName() {
+ if (objectName != null) {
+ return objectName;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set a named property of the instruction
+ * @param name the name of the property
+ * @param value the value of the property
+ */
+
+ public void setProperty(String name, Object value) {
+ properties.put(name, value);
+ }
+
+ /**
+ * Get a named property of the instruction
+ * @param name name of the property
+ * @return the value of the named property
+ */
+
+ public Object getProperty(String name) {
+ return properties.get(name);
+ }
+
+ /**
+ * Get an iterator over all the properties available. The values returned by the iterator
+ * will be of type String, and each string can be supplied as input to the getProperty()
+ * method to retrieve the value of the property.
+ * @return an iterator over the names of the properties
+ */
+
+ public Iterator<String> getProperties() {
+ return properties.keySet().iterator();
+ }
+
+ /**
+ * Get the public ID of the module containing the instruction. This method
+ * is provided to satisfy the SourceLocator interface. However, the public ID is
+ * not maintained by Saxon, and the method always returns null
+ * @return null
+ */
+
+ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Set the column number
+ * @param column the column number of the instruction in the source module
+ */
+
+ public void setColumnNumber(int column) {
+ columnNumber = column;
+ }
+
+ /**
+ * Get the column number identifying the position of the instruction.
+ * @return -1 if column number is not known
+ */
+
+ public int getColumnNumber() {
+ return columnNumber;
+ }
+
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/LocalParam.java b/sf/saxon/expr/instruct/LocalParam.java
new file mode 100644
index 0000000..88e3e7f
--- /dev/null
+++ b/sf/saxon/expr/instruct/LocalParam.java
@@ -0,0 +1,230 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.MonoIterator;
+import net.sf.saxon.expr.PairIterator;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.IntegerValue;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * The compiled form of an xsl:param element within a template in an XSLT stylesheet.
+ *
+ * <p>The xsl:param element in XSLT has mandatory attribute name and optional attribute select. It can also
+ * be specified as required="yes" or required="no".</p>
+ *
+ * <p>This is used only for parameters to XSLT templates. For function calls, the caller of the function
+ * places supplied arguments onto the callee's stackframe and the callee does not need to do anything.
+ * Global parameters (XQuery external variables) are handled using {@link GlobalParam}.</p>
+ *
+ * <p>The LocalParam class is also used to represent parameters with the saxon:iterate instruction</p>
+ */
+
+public final class LocalParam extends GeneralVariable {
+
+ private int parameterId;
+ /*@Nullable*/ private Expression conversion = null;
+ private int conversionEvaluationMode = ExpressionTool.UNDECIDED;
+
+ /**
+ * Allocate a number which is essentially an alias for the parameter name,
+ * unique within a stylesheet
+ * @param id the parameter id
+ */
+
+ public void setParameterId(int id) {
+ parameterId = id;
+ }
+
+ /**
+ * Get the parameter id, which is essentially an alias for the parameter name,
+ * unique within a stylesheet
+ * @return the parameter id
+ */
+
+ public int getParameterId() {
+ return parameterId;
+ }
+
+ /**
+ * Define a conversion that is to be applied to the supplied parameter value.
+ * @param convertor The expression to be applied. This performs type checking,
+ * and the basic conversions implied by function calling rules, for example
+ * numeric promotion, atomization, and conversion of untyped atomic values to
+ * a required type. The conversion uses the actual parameter value as input,
+ * referencing it using a VariableReference. The argument can be null to indicate
+ * that no conversion is required.
+ */
+ public void setConversion(/*@Nullable*/ Expression convertor) {
+ conversion = convertor;
+ if (convertor != null) {
+ conversionEvaluationMode = ExpressionTool.eagerEvaluationMode(conversion);
+ }
+ }
+
+ /**
+ * Get the conversion expression
+ * @return the expression used to convert the value to the required type,
+ * or null if there is none
+ */
+
+ /*@Nullable*/ public Expression getConversion() {
+ return conversion;
+ }
+
+ public int getConversionEvaluationMode() {
+ return conversionEvaluationMode;
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_PARAM;
+ }
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ public Iterator<Expression> iterateSubExpressions() {
+ if (select != null && conversion != null) {
+ return new PairIterator<Expression>(select, conversion);
+ } else if (select != null) {
+ return new MonoIterator<Expression>(select);
+ } else if (conversion != null) {
+ return new MonoIterator<Expression>(conversion);
+ } else {
+ final List<Expression> list = Collections.emptyList();
+ return list.iterator();
+ }
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (conversion == original) {
+ conversion = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Process the local parameter declaration
+ * @param context the dynamic context
+ * @return either null if processing is complete, or a tailcall if one is left outstanding
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs in the evaluation
+ */
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ int wasSupplied = context.useLocalParameter(parameterId, slotNumber, isTunnelParam());
+ switch (wasSupplied) {
+ case ParameterSet.SUPPLIED_AND_CHECKED:
+ // No action needed
+ break;
+
+ case ParameterSet.SUPPLIED:
+ // if a parameter was supplied by the caller, with no type-checking by the caller,
+ // then we may need to convert it to the type required
+ if (conversion != null) {
+ context.setLocalVariable(slotNumber,
+ ExpressionTool.evaluate(conversion, conversionEvaluationMode, context, 10));
+ // We do an eager evaluation here for safety, because the result of the
+ // type conversion overwrites the slot where the actual supplied parameter
+ // is contained.
+ }
+ break;
+
+ // don't evaluate the default if a value has been supplied or if it has already been
+ // evaluated by virtue of a forwards reference
+
+ case ParameterSet.NOT_SUPPLIED:
+ if (isImplicitlyRequiredParam()) {
+ String name = "$" + getVariableQName().getDisplayName();
+ XPathException e = new XPathException("A value must be supplied for parameter "
+ + name + " because " +
+ "the default value is not a valid instance of the required type");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0610");
+ throw e;
+ } else if (isRequiredParam()) {
+ String name = "$" + getVariableQName().getDisplayName();
+ XPathException e = new XPathException("No value supplied for required parameter " + name);
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0700");
+ throw e;
+ }
+ context.setLocalVariable(slotNumber, getSelectValue(context));
+ }
+ return null;
+ }
+
+ /**
+ * If the variable is bound to an integer, get the minimum and maximum possible values.
+ * Return null if unknown or not applicable
+ */
+ @Override
+ public IntegerValue[] getIntegerBoundsForVariable() {
+ return null;
+ }
+
+ /**
+ * Evaluate the variable
+ */
+
+ public Sequence evaluateVariable(XPathContext c) {
+ return c.evaluateLocalVariable(slotNumber);
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("param");
+ out.emitAttribute("name", variableQName.getDisplayName());
+ if (select != null) {
+ select.explain(out);
+ }
+ if (conversion != null) {
+ Expression exp = conversion;
+ out.startElement("conversion");
+ exp.explain(out);
+ out.endElement();
+ }
+ out.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/LocalParamBlock.java b/sf/saxon/expr/instruct/LocalParamBlock.java
new file mode 100644
index 0000000..c80d8a7
--- /dev/null
+++ b/sf/saxon/expr/instruct/LocalParamBlock.java
@@ -0,0 +1,227 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.LocalParamBlockCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ErrorType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+
+/**
+* Represents the set of xsl:param elements at the start of an xsl:iterate instruction
+*/
+
+public class LocalParamBlock extends Instruction {
+
+ private LocalParamSetter[] children;
+
+ /**
+ * Create the block of parameters
+ * @param params the parameters
+ */
+
+ public LocalParamBlock(LocalParamSetter[] params) {
+ this.children = params;
+ for (LocalParamSetter child : children) {
+ adoptChildExpression(child);
+ }
+ }
+
+ public String getExpressionName() {
+ return "block";
+ }
+
+ /**
+ * Get the children of this instruction
+ * @return the children of this instruction, as an array of Instruction objects. May return
+ * a zero-length array if there are no children
+ */
+
+ public LocalParamSetter[] getChildren() {
+ return children;
+ }
+
+
+ public int computeSpecialProperties() {
+ return 0;
+ }
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return Arrays.asList((Expression[])children).iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (replacement instanceof LocalParamSetter) {
+ for (int c=0; c<children.length; c++) {
+ if (children[c] == original) {
+ children[c] = (LocalParamSetter)replacement;
+ found = true;
+ }
+ }
+ }
+ return found;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ LocalParamSetter[] c2 = new LocalParamSetter[children.length];
+ for (int c=0; c<children.length; c++) {
+ c2[c] = (LocalParamSetter)children[c].copy();
+ }
+ return new LocalParamBlock(c2);
+ }
+
+ /**
+ * Determine the data type of the items returned by this expression
+ * @return the data type
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public final ItemType getItemType(TypeHierarchy th) {
+ return ErrorType.getInstance();
+ }
+
+ /**
+ * Determine the cardinality of the expression
+ */
+
+ public final int getCardinality() {
+ return StaticProperty.EMPTY;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @exception net.sf.saxon.trans.XPathException if an error is discovered during expression
+ * rewriting
+ * @return the simplified expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ for (int c=0; c<children.length; c++) {
+ children[c] = (LocalParamSetter)visitor.simplify(children[c]);
+ adoptChildExpression(children[c]);
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ for (int c=0; c<children.length; c++) {
+ children[c] = (LocalParamSetter)visitor.typeCheck(children[c], contextItemType);
+ adoptChildExpression(children[c]);
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ for (int c=0; c<children.length; c++) {
+ children[c] = (LocalParamSetter)visitor.optimize(children[c], contextItemType);
+ adoptChildExpression(children[c]);
+ }
+ return this;
+ }
+
+
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ * @param offer The type of rewrite being offered
+ * @throws net.sf.saxon.trans.XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ for (LocalParamSetter p : children) {
+ p.getBinding().setSelectExpression(doPromotion(p.getBinding().getSelectExpression(), offer));
+ }
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("params");
+ for (LocalParamSetter child : children) {
+ child.explain(out);
+ }
+ out.endElement();
+ }
+
+
+ /*@Nullable*/ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ for (LocalParamSetter param : children) {
+ try {
+ context.setLocalVariable(param.getBinding().getSlotNumber(), param.getBinding().getSelectValue(context));
+ } catch (XPathException e) {
+ e.maybeSetLocation(param);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided. This implementation provides both iterate() and
+ * process() methods natively.
+ */
+
+ public int getImplementationMethod() {
+ return PROCESS_METHOD;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the LocalParamBlock expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new LocalParamBlockCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/instruct/LocalParamSetter.java b/sf/saxon/expr/instruct/LocalParamSetter.java
new file mode 100644
index 0000000..073738d
--- /dev/null
+++ b/sf/saxon/expr/instruct/LocalParamSetter.java
@@ -0,0 +1,321 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.LocalParamCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ErrorType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.Iterator;
+
+/**
+ * The compiled form of an xsl:param element within a template in an XSLT stylesheet. An xsl:param
+ * element used as a template parameter is compiled into two objects: a LocalParam which represents the
+ * parameter definition and the Binding for the value, and a LocalParamSetter which is an Instruction
+ * to set a default value for the parameter if none has been supplied.
+ *
+ * <p>The xsl:param element in XSLT has mandatory attribute name and optional attribute select. It can also
+ * be specified as required="yes" or required="no".</p>
+ *
+ * <p>This is used only for parameters to XSLT templates. For function calls, the caller of the function
+ * places supplied arguments onto the callee's stackframe and the callee does not need to do anything.
+ * Global parameters (XQuery external variables) are handled using {@link net.sf.saxon.expr.instruct.GlobalParam}.</p>
+ *
+ * <p>The LocalParam class is also used to represent parameters with the saxon:iterate instruction</p>
+ */
+
+public final class LocalParamSetter extends Instruction {
+
+ /*@NotNull*/
+ private LocalParam binding;
+
+ public LocalParamSetter(/*@NotNull*/ LocalParam binding) {
+ this.binding = binding;
+ }
+
+ /**
+ * Get the LocalParam element representing the binding for this parameter
+ * @return the binding element
+ */
+
+ /*@NotNull*/
+ public LocalParam getBinding() {
+ return binding;
+ }
+
+ /**
+ * Get the item type of the items returned by evaluating this instruction
+ *
+ * @param th the type hierarchy cache
+ * @return the static item type of the instruction
+ */
+ /*@NotNull*/
+ @Override
+ public ItemType getItemType(TypeHierarchy th) {
+ return ErrorType.getInstance();
+ }
+
+ /**
+ * Get the cardinality of the sequence returned by evaluating this instruction
+ *
+ * @return the static cardinality
+ */
+ @Override
+ public int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+ @Override
+ public int computeSpecialProperties() {
+ return 0;
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns a default value of false
+ *
+ * @return true if the instruction creates new nodes (or if it can't be proved that it doesn't)
+ */
+ @Override
+ public boolean createsNewNodes() {
+ return false;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression
+ * rewriting
+ */
+ /*@NotNull*/
+ @Override
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ Expression select = binding.getSelectExpression();
+ if (select != null) {
+ binding.setSelectExpression(visitor.simplify(select));
+ adoptChildExpression(select);
+ }
+ return this;
+ }
+
+ /**
+ * Perform type checking of an expression and its subexpressions. This is the second phase of
+ * static optimization.
+ * <p/>
+ * <p>This checks statically that the operands of the expression have
+ * the correct type; if necessary it generates code to do run-time type checking or type
+ * conversion. A static type error is reported only if execution cannot possibly succeed, that
+ * is, if a run-time type error is inevitable. The call may return a modified form of the expression.</p>
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable. However, the types of such functions and
+ * variables may not be accurately known if they have not been explicitly declared.</p>
+ * <p/>
+ * <p>If the implementation returns a value other than "this", then it is required to ensure that
+ * the location information in the returned expression have been set up correctly.
+ * It should not rely on the caller to do this, although for historical reasons many callers do so.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten to perform necessary run-time type checks,
+ * and to perform other type-related optimizations
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+ /*@NotNull*/
+ @Override
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression select = binding.getSelectExpression();
+ if (select != null) {
+ binding.setSelectExpression(visitor.typeCheck(select, contextItemType));
+ adoptChildExpression(select);
+ }
+ binding.checkAgainstRequiredType(visitor);
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions. This is the third and final
+ * phase of static optimization.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+ /*@NotNull*/
+ @Override
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression select = binding.getSelectExpression();
+ if (select != null) {
+ binding.setSelectExpression(visitor.optimize(select, contextItemType));
+ adoptChildExpression(select);
+ binding.computeEvaluationMode();
+ }
+ return this;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+ /*@NotNull*/
+ @Override
+ public Expression copy() {
+ return new LocalParamSetter(binding);
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_PARAM;
+ }
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return binding.iterateSubExpressions();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ return binding.replaceSubExpression(original, replacement);
+ }
+
+
+ /**
+ * Process the local parameter declaration
+ */
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ int slotNumber = binding.getSlotNumber();
+ int wasSupplied = context.useLocalParameter(binding.getParameterId(), slotNumber, binding.isTunnelParam());
+ switch (wasSupplied) {
+ case ParameterSet.SUPPLIED_AND_CHECKED:
+ // No action needed
+ break;
+
+ case ParameterSet.SUPPLIED:
+ // if a parameter was supplied by the caller, with no type-checking by the caller,
+ // then we may need to convert it to the type required
+ Expression conversion = binding.getConversion();
+ int conversionEvaluationMode = binding.getConversionEvaluationMode();
+ if (conversion != null) {
+ context.setLocalVariable(slotNumber,
+ ExpressionTool.evaluate(conversion, conversionEvaluationMode, context, 10));
+ // We do an eager evaluation here for safety, because the result of the
+ // type conversion overwrites the slot where the actual supplied parameter
+ // is contained.
+ }
+ break;
+
+ // don't evaluate the default if a value has been supplied or if it has already been
+ // evaluated by virtue of a forwards reference
+
+ case ParameterSet.NOT_SUPPLIED:
+ if (binding.isImplicitlyRequiredParam()) {
+ String name = "$" + binding.getVariableQName().getDisplayName();
+ XPathException e = new XPathException("A value must be supplied for parameter "
+ + name + " because " +
+ "the default value is not a valid instance of the required type");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0610");
+ throw e;
+ } else if (binding.isRequiredParam()) {
+ String name = "$" + binding.getVariableQName().getDisplayName();
+ XPathException e = new XPathException("No value supplied for required parameter " + name);
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0700");
+ throw e;
+ }
+ context.setLocalVariable(slotNumber, binding.getSelectValue(context));
+ }
+ return null;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the LocalParamSetter expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new LocalParamCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("param");
+ out.emitAttribute("name", binding.getVariableQName().getDisplayName());
+ if (binding.getSelectExpression() != null) {
+ binding.getSelectExpression().explain(out);
+ }
+ Expression conversion = binding.getConversion();
+ if (conversion != null) {
+ out.startElement("conversion");
+ conversion.explain(out);
+ out.endElement();
+ }
+ out.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/LocationMap.java b/sf/saxon/expr/instruct/LocationMap.java
new file mode 100644
index 0000000..159dae7
--- /dev/null
+++ b/sf/saxon/expr/instruct/LocationMap.java
@@ -0,0 +1,98 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.event.LocationProvider;
+
+import java.io.Serializable;
+
+/**
+ * A LocationMap allocates integer codes to (systemId, lineNumber) pairs. The integer
+ * codes are held inside an Expression object to track the location of the expression
+ * in the source code
+ */
+
+public class LocationMap implements LocationProvider, Serializable {
+
+ private String[] modules = new String[10];
+ private int numberOfModules = 0;
+
+ /**
+ * Create a location map
+ */
+
+ public LocationMap() {}
+
+ /**
+ * Allocate a location identifier to an expression
+ * @param module the URI (system identifier) of the module
+ * @param lineNumber the line number of the expression within the module
+ * @return the corresponding location identifier
+ */
+
+ public int allocateLocationId(/*@Nullable*/ String module, int lineNumber) {
+ if (module == null) {
+ // the module has no base URI
+ module = "*module with no systemId*";
+ }
+ int mod = -1;
+ for (int m=numberOfModules-1; m>=0; m--) {
+ if (modules[m].equals(module)) {
+ mod = m;
+ break;
+ }
+ }
+ if (mod == -1) {
+ if (numberOfModules >= modules.length) {
+ String[] m2 = new String[numberOfModules*2];
+ System.arraycopy(modules, 0, m2, 0, numberOfModules);
+ modules = m2;
+ }
+ mod = numberOfModules;
+ modules[numberOfModules++] = module;
+ }
+ if (mod >= 1024) {
+ modules[mod] = "*unknown module*";
+ mod = 1023;
+ }
+ if (lineNumber > 999999) {
+ lineNumber = 999999;
+ }
+ return (mod<<20) + lineNumber;
+ }
+
+ /**
+ * Get the system identifier corresponding to a locationId
+ * @param locationId the location identifier
+ * @return the corresponding system identifier
+ */
+
+ public String getSystemId(long locationId) {
+ int m = ((int)locationId)>>20;
+ if (m < 0 || m >= numberOfModules) {
+ return null;
+ }
+ return modules[m];
+ }
+
+ /**
+ * Get the line number corresponding to a locationId
+ * @param locationId the location identifier
+ * @return the corresponding line number
+ */
+
+ public int getLineNumber(long locationId) {
+ return ((int)locationId) & 0xfffff;
+ }
+
+ public int getColumnNumber(long locationId) {
+ return -1;
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/Message.java b/sf/saxon/expr/instruct/Message.java
new file mode 100644
index 0000000..d736720
--- /dev/null
+++ b/sf/saxon/expr/instruct/Message.java
@@ -0,0 +1,386 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.InterpretedExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.*;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.lib.SerializerFactory;
+import net.sf.saxon.lib.StandardErrorListener;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+
+import javax.xml.transform.OutputKeys;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * An xsl:message element in the stylesheet.
+ */
+
+public class Message extends Instruction {
+
+ /*@NotNull*/ private Expression terminate;
+ /*@NotNull*/ private Expression select;
+ private Expression errorCode;
+ private NamespaceResolver nsResolver;
+ private boolean isAssert;
+
+ /**
+ * Create an xsl:message instruction
+ *
+ * @param select the expression that constructs the message (composite of the select attribute
+ * and the contained sequence constructor)
+ * @param terminate expression that calculates terminate = yes or no.
+ */
+
+ public Message(/*@NotNull*/ Expression select, /*@NotNull*/ Expression terminate, Expression errorCode) {
+ this.terminate = terminate;
+ this.select = select;
+ this.errorCode = errorCode;
+ adoptChildExpression(terminate);
+ adoptChildExpression(select);
+ }
+
+ /**
+ * Say whether this instruction is implementing xsl:message or xsl:assert
+ * @param isAssert true if this is xsl:assert; false if it is xsl:message
+ */
+
+ public void setIsAssert(boolean isAssert) {
+ this.isAssert = isAssert;
+ }
+
+ /**
+ * Set a namespace resolver for resolving any namespace prefix appearing in the value of the error-code
+ * attribute
+ * @param resolver
+ */
+
+ public void setNamespaceResolver(NamespaceResolver resolver) {
+ this.nsResolver = resolver;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ select = visitor.simplify(select);
+ terminate = visitor.simplify(terminate);
+ errorCode = visitor.simplify(errorCode);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ select = visitor.typeCheck(select, contextItemType);
+ adoptChildExpression(select);
+ terminate = visitor.typeCheck(terminate, contextItemType);
+ adoptChildExpression(terminate);
+ errorCode = visitor.typeCheck(errorCode, contextItemType);
+ adoptChildExpression(errorCode);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ select = visitor.optimize(select, contextItemType);
+ adoptChildExpression(select);
+ terminate = visitor.optimize(terminate, contextItemType);
+ adoptChildExpression(terminate);
+ errorCode = visitor.optimize(errorCode, contextItemType);
+ adoptChildExpression(errorCode);
+ return this;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new Message(select.copy(), terminate.copy(), errorCode.copy());
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return (isAssert ? StandardNames.XSL_ASSERT : StandardNames.XSL_MESSAGE);
+ }
+
+ /**
+ * Get the item type. To avoid spurious compile-time type errors, we falsely declare that the
+ * instruction can return anything
+ *
+ * @param th the type hierarchy cache
+ * @return AnyItemType
+ */
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Get the static cardinality. To avoid spurious compile-time type errors, we falsely declare that the
+ * instruction returns zero or one items - this is always acceptable
+ *
+ * @return zero or one
+ */
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true.
+ */
+
+ public final boolean createsNewNodes() {
+ return true;
+ }
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ *
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ select = doPromotion(select, offer);
+ terminate = doPromotion(terminate, offer);
+ errorCode = doPromotion(errorCode, offer);
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ Iterator<Expression> sub = iterateSubExpressions();
+ int contentStreamability = W3C_FREE_RANGING;
+ while (sub.hasNext()) {
+ Expression child = sub.next();
+ int s = child.getStreamability(NAVIGATION_CONTEXT, allowExtensions, null);
+ if (child == select) {
+ contentStreamability = s;
+ } else {
+ if (s != W3C_MOTIONLESS) {
+ return W3C_FREE_RANGING;
+ }
+ }
+ }
+ return contentStreamability;
+ }
+//#endif
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ Expression[] children = new Expression[]{select, terminate, errorCode};
+ return Arrays.asList(children).iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (terminate == original) {
+ terminate = replacement;
+ found = true;
+ }
+ if (errorCode == original) {
+ errorCode = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ Receiver emitter = controller.getMessageEmitter();
+ if (emitter != null) {
+ //noinspection SynchronizationOnLocalVariableOrMethodParameter
+ synchronized (emitter) {
+ // In Saxon-EE, multithreading can cause different messages to be entangled unless we synchronize.
+
+ SequenceReceiver rec = new TreeReceiver(emitter);
+ rec = new AttributeMasker(rec);
+
+ SequenceReceiver saved = context.getReceiver();
+
+ Properties props = new Properties();
+ props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ SerializerFactory sf = context.getConfiguration().getSerializerFactory();
+ PipelineConfiguration pipe = controller.makePipelineConfiguration();
+ pipe.setHostLanguage(Configuration.XSLT);
+ Receiver receiver = sf.getReceiver(rec, pipe, props);
+ context.changeOutputDestination(receiver, null);
+
+ boolean abort = false;
+ String term = terminate.evaluateAsString(context).toString();
+ if (term.equals("no")) {
+ // no action
+ } else if (term.equals("yes")) {
+ abort = true;
+ } else {
+ XPathException e = new XPathException("The terminate attribute of xsl:message must be 'yes' or 'no'");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0030");
+ throw e;
+ }
+
+
+ rec.startDocument(abort ? ReceiverOptions.TERMINATE : 0);
+
+ SequenceIterator iter = select.iterate(context);
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ rec.append(item, locationId, NodeInfo.ALL_NAMESPACES);
+ }
+
+ rec.endDocument();
+ context.setReceiver(saved);
+ if (abort) {
+ TerminationException te = new TerminationException(
+ "Processing terminated by " + StandardErrorListener.getInstructionName(this) +
+ " at line " + getLineNumber() +
+ " in " + StandardErrorListener.abbreviatePath(getSystemId()));
+ try {
+ String code = errorCode.evaluateAsString(context).toString();
+ StructuredQName errorCode = StructuredQName.fromLexicalQName(
+ code, false, true, context.getConfiguration().getNameChecker(), nsResolver);
+ te.setErrorCodeQName(errorCode);
+ te.setLocator(this);
+ } catch (XPathException err) {
+ // no action, ignore the error
+ }
+ throw te;
+ }
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("xslMessage");
+ out.endElement();
+ }
+
+ private static class AttributeMasker extends ProxyReceiver {
+ private boolean contentStarted = true;
+
+ public AttributeMasker(SequenceReceiver next) {
+ super(next);
+ }
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ contentStarted = false;
+ super.startElement(nameCode, typeCode, locationId, properties);
+ }
+
+ public void startContent() throws XPathException {
+ contentStarted = true;
+ super.startContent();
+ }
+
+
+ public void attribute(NodeName attributeName, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ if (contentStarted) {
+ String attName = attributeName.getDisplayName();
+ processingInstruction("attribute", "name=\"" + attName + "\" value=\"" + value + "\"", 0, 0);
+ } else {
+ super.attribute(attributeName, typeCode, value, locationId, properties);
+ }
+ }
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ if (contentStarted) {
+ String prefix = namespaceBinding.getPrefix();
+ String uri = namespaceBinding.getURI();
+ processingInstruction("namespace", "prefix=\"" + prefix + "\" uri=\"" + uri + "\"", 0, 0);
+ } else {
+ super.namespace(namespaceBinding, properties);
+ }
+ }
+
+ public void append(Item item, int locationId, int copyNamespaces) throws XPathException {
+ if (item instanceof NodeInfo) {
+ int kind = ((NodeInfo) item).getNodeKind();
+ if (kind == Type.ATTRIBUTE || kind == Type.NAMESPACE) {
+ ((NodeInfo) item).copy(this, 0, 0);
+ return;
+ }
+ }
+ ((SequenceReceiver) nextReceiver).append(item, locationId, copyNamespaces);
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Message expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new InterpretedExpressionCompiler();
+ }
+//#endif
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/instruct/NamespaceConstructor.java b/sf/saxon/expr/instruct/NamespaceConstructor.java
new file mode 100644
index 0000000..4d176a3
--- /dev/null
+++ b/sf/saxon/expr/instruct/NamespaceConstructor.java
@@ -0,0 +1,259 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+//#ifdefined BYTECODE
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NamespaceConstructorCompiler;
+//#endif
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StandardURIChecker;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.Iterator;
+
+/**
+* A namespace constructor instruction. (xsl:namespace in XSLT 2.0, or namespace{}{} in XQuery 1.1)
+*/
+
+public class NamespaceConstructor extends SimpleNodeConstructor {
+
+ private Expression name;
+
+ /**
+ * Create an xsl:namespace instruction for dynamic construction of namespace nodes
+ * @param name the expression to evaluate the name of the node (that is, the prefix)
+ */
+
+ public NamespaceConstructor(Expression name) {
+ this.name = name;
+ adoptChildExpression(name);
+ }
+
+ /**
+ * Get the expression that defines the namespace node's name
+ * @return the expression that defines the namespace node's name (that is, the namespace prefix)
+ */
+
+ public Expression getNameExpression() {
+ return name;
+ }
+
+ /**
+ * Set the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_NAMESPACE;
+ }
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ name = visitor.simplify(name);
+ return super.simplify(visitor);
+ }
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return NodeKindTest.NAMESPACE;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ select = doPromotion(select, offer);
+ name = doPromotion(name, offer);
+ super.promoteInst(offer);
+ }
+
+ public void localTypeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ name = visitor.typeCheck(name, contextItemType);
+ adoptChildExpression(name);
+
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (!th.isSubType(name.getItemType(th), BuiltInAtomicType.STRING)) {
+ name = SystemFunctionCall.makeSystemFunction("string", new Expression[]{name});
+ }
+ }
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(select, name);
+ }
+
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new PairIterator<SubExpressionInfo>(
+ new SubExpressionInfo(name, true, false, NODE_VALUE_CONTEXT),
+ new SubExpressionInfo(select, true, false, NODE_VALUE_CONTEXT)
+ );
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ NamespaceConstructor exp = new NamespaceConstructor(name.copy());
+ exp.setSelect(select.copy(), getExecutable().getConfiguration());
+ ExpressionTool.copyLocationInfo(this, exp);
+ return exp;
+ }
+
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (name == original) {
+ name = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ private String evaluatePrefix(XPathContext context) throws XPathException {
+ String prefix = Whitespace.trim(name.evaluateAsString(context));
+ assert prefix != null;
+ if (!(prefix.length() == 0 || context.getConfiguration().getNameChecker().isValidNCName(prefix))) {
+ XPathException err = new XPathException("Namespace prefix is invalid: " + prefix, this);
+ err.setErrorCode(isXSLT() ? "XTDE0920" : "FORG0001");
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ if (prefix.equals("xmlns")) {
+ XPathException err = new XPathException("Namespace prefix 'xmlns' is not allowed", this);
+ err.setErrorCode(isXSLT() ? "XTDE0920" : "XQDY0101");
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+ return prefix;
+ }
+
+ public NodeName evaluateNodeName(XPathContext context) throws XPathException {
+ String prefix = evaluatePrefix(context);
+ return new NoNamespaceName(prefix);
+ }
+
+ public void processValue(CharSequence value, XPathContext context) throws XPathException {
+ String prefix = evaluatePrefix(context);
+ String uri = value.toString();
+ checkPrefixAndUri(prefix, uri, context);
+
+ NamespaceBinding nscode = new NamespaceBinding(prefix, uri);
+ SequenceReceiver out = context.getReceiver();
+ out.namespace(nscode, ReceiverOptions.REJECT_DUPLICATES);
+ }
+
+
+ /**
+ * Evaluate as an expression. We rely on the fact that when these instructions
+ * are generated by XQuery, there will always be a valueExpression to evaluate
+ * the content
+ */
+
+ public NodeInfo evaluateItem(XPathContext context) throws XPathException {
+ NodeInfo node = (NodeInfo)super.evaluateItem(context);
+ assert node != null;
+ String prefix = node.getLocalPart();
+ String uri = node.getStringValue();
+ checkPrefixAndUri(prefix, uri, context);
+ return node;
+ }
+
+ private void checkPrefixAndUri(String prefix, String uri, XPathContext context) throws XPathException {
+ if (prefix.equals("xml") != uri.equals(NamespaceConstant.XML)) {
+ XPathException err = new XPathException("Namespace prefix 'xml' and namespace uri " + NamespaceConstant.XML +
+ " must only be used together", this);
+ err.setErrorCode(isXSLT() ? "XTDE0925" : "XQDY0101");
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ if (uri.length() == 0) {
+ XPathException err = new XPathException("Namespace URI is an empty string", this);
+ err.setErrorCode(isXSLT() ? "XTDE0930" : "XQDY0101");
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ if (uri.equals(NamespaceConstant.XMLNS)) {
+ XPathException err = new XPathException("A namespace node cannot have the reserved namespace " +
+ NamespaceConstant.XMLNS, this);
+ err.setErrorCode("XTDE0935");
+ err.setXPathContext(context);
+ throw dynamicError(this, err, context);
+ }
+
+ if (!StandardURIChecker.getInstance().isValidURI(uri)) {
+ XPathException de = new XPathException("The string value of the constructed namespace node must be a valid URI");
+ de.setErrorCode("XTDE0905");
+ de.setXPathContext(context);
+ de.setLocator(this);
+ throw de;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NamespaceConstructor expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NamespaceConstructorCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("namespace");
+ out.startSubsidiaryElement("name");
+ name.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("select");
+ getContentExpression().explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/instruct/NextMatch.java b/sf/saxon/expr/instruct/NextMatch.java
new file mode 100644
index 0000000..1cd2d53
--- /dev/null
+++ b/sf/saxon/expr/instruct/NextMatch.java
@@ -0,0 +1,219 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NextMatchCompiler;
+import com.saxonica.stream.adjunct.NextMatchAdjunct;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.Mode;
+import net.sf.saxon.trans.Rule;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.Arrays;
+
+
+
+/**
+* An xsl:next-match element in the stylesheet
+*/
+
+public class NextMatch extends ApplyImports {
+
+ boolean useTailRecursion;
+
+ public NextMatch(boolean useTailRecursion) {
+ this.useTailRecursion = useTailRecursion;
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_NEXT_MATCH;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ NextMatch nm2 = new NextMatch(useTailRecursion);
+ nm2.setActualParameters(WithParam.copy(actualParams), WithParam.copy(tunnelParams));
+ nm2.allowAnyItem = allowAnyItem;
+ return nm2;
+ }
+
+
+ /*@Nullable*/ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+
+ Controller controller = context.getController();
+ assert controller != null;
+
+ // handle parameters if any
+
+ ParameterSet params = assembleParams(context, actualParams);
+ ParameterSet tunnels = assembleTunnelParams(context, tunnelParams);
+
+ Rule currentRule = context.getCurrentTemplateRule();
+ if (currentRule==null) {
+ XPathException e = new XPathException("There is no current template rule");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0560");
+ throw e;
+ }
+ Mode mode = context.getCurrentMode();
+ if (mode == null) {
+ mode = controller.getRuleManager().getUnnamedMode();
+ }
+ if (context.getCurrentIterator()==null) {
+ XPathException e = new XPathException("Cannot call xsl:next-match when there is no context item");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0565");
+ throw e;
+ }
+ Item currentItem = context.getCurrentIterator().current();
+ if (!allowAnyItem && !(currentItem instanceof NodeInfo)) {
+ XPathException e = new XPathException("Cannot call xsl:next-match when context item is not a node");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0565");
+ throw e;
+ }
+ Rule rule = controller.getRuleManager().getNextMatchHandler(currentItem, mode, currentRule, context);
+
+ if (rule==null) { // use the default action for the node
+ mode.getBuiltInRuleSet().process(currentItem, params, tunnels, context, getLocationId());
+ } else if (useTailRecursion) {
+ //Template nh = (Template)rule.getAction();
+ // clear all the local variables: they are no longer needed
+ Arrays.fill(context.getStackFrame().getStackFrameValues(), null);
+ return new NextMatchPackage(rule, params, tunnels, context);
+ } else {
+ Template nh = (Template)rule.getAction();
+ XPathContextMajor c2 = context.newContext();
+ c2.setOrigin(this);
+ c2.setOriginatingConstructType(Location.TEMPLATE);
+ c2.openStackFrame(nh.getStackFrameMap());
+ c2.setLocalParameters(params);
+ c2.setTunnelParameters(tunnels);
+ c2.setCurrentTemplateRule(rule);
+ nh.apply(c2);
+ }
+ return null;
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("nextMatch");
+ if (actualParams.length > 0) {
+ out.startSubsidiaryElement("withParams");
+ WithParam.explainParameters(actualParams, out);
+ out.endSubsidiaryElement();
+ }
+ if (tunnelParams.length > 0) {
+ out.startSubsidiaryElement("tunnelParams");
+ WithParam.explainParameters(tunnelParams, out);
+ out.endSubsidiaryElement();
+ }
+ out.endElement();
+ }
+
+ /**
+ * A NextMatchPackage is an object that encapsulates the name of a template to be called,
+ * the parameters to be supplied, and the execution context. This object can be returned as a tail
+ * call, so that the actual call is made from a lower point on the stack, allowing a tail-recursive
+ * template to execute in a finite stack size
+ */
+
+ private class NextMatchPackage implements TailCall {
+
+ private Rule rule;
+ private ParameterSet params;
+ private ParameterSet tunnelParams;
+ private XPathContext evaluationContext;
+
+ /**
+ * Construct a NextMatchPackage that contains information about a call.
+ * @param rule the rule identifying the Template to be called
+ * @param params the parameters to be supplied to the called template
+ * @param tunnelParams the tunnel parameter supplied to the called template
+ * @param evaluationContext saved context information from the Controller (current mode, etc)
+ * which must be reset to ensure that the template is called with all the context information
+ * intact
+ */
+
+ public NextMatchPackage(Rule rule,
+ ParameterSet params,
+ ParameterSet tunnelParams,
+ XPathContext evaluationContext) {
+ this.rule = rule;
+ this.params = params;
+ this.tunnelParams = tunnelParams;
+ this.evaluationContext = evaluationContext;
+ }
+
+ /**
+ * Process the template call encapsulated by this package.
+ * @return another TailCall. This will never be the original call, but it may be the next
+ * recursive call. For example, if A calls B which calls C which calls D, then B may return
+ * a TailCall to A representing the call from B to C; when this is processed, the result may be
+ * a TailCall representing the call from C to D.
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public TailCall processLeavingTail() throws XPathException {
+ Template nh = (Template)rule.getAction();
+ XPathContextMajor c2 = evaluationContext.newContext();
+ c2.setOrigin(NextMatch.this);
+ c2.setOriginatingConstructType(Location.TEMPLATE);
+ c2.setLocalParameters(params);
+ c2.setTunnelParameters(tunnelParams);
+ c2.openStackFrame(nh.getStackFrameMap());
+ c2.setCurrentTemplateRule(rule);
+
+ // System.err.println("Tail call on template");
+
+ return nh.applyLeavingTail(c2);
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NextMatch expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NextMatchCompiler();
+ }
+
+ @Override
+ public NextMatchAdjunct getStreamingAdjunct() {
+ return new NextMatchAdjunct();
+ }
+
+ //#endif
+
+}
+
diff --git a/sf/saxon/expr/instruct/NumberInstruction.java b/sf/saxon/expr/instruct/NumberInstruction.java
new file mode 100644
index 0000000..4f1f637
--- /dev/null
+++ b/sf/saxon/expr/instruct/NumberInstruction.java
@@ -0,0 +1,714 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.InterpretedExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Atomizer;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.number.NumberFormatter;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.functions.NumberFn;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.Numberer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.pattern.PatternSponsor;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An xsl:number element in the stylesheet. Although this is an XSLT instruction, it is compiled
+ * into an expression, evaluated using xsl:value-of to create the resulting text node.<br>
+ */
+
+public class NumberInstruction extends Expression {
+
+ private static final int SINGLE = 0;
+ private static final int MULTI = 1;
+ private static final int ANY = 2;
+ private static final int SIMPLE = 3;
+
+ private int level;
+ /*@Nullable*/ private Pattern count = null;
+ private Pattern from = null;
+ private Expression select = null;
+ private Expression value = null;
+ private Expression format = null;
+ private Expression groupSize = null;
+ private Expression groupSeparator = null;
+ private Expression letterValue = null;
+ private Expression ordinal = null;
+ private Expression startAt = null;
+ private Expression lang = null;
+ private NumberFormatter formatter = null;
+ private Numberer numberer = null;
+ private boolean hasVariablesInPatterns;
+ private boolean backwardsCompatible;
+
+ /**
+ * Construct a NumberInstruction
+ * @param config the Saxon configuration
+ * @param select the expression supplied in the select attribute
+ * @param level one of "single", "level", "multi"
+ * @param count the pattern supplied in the count attribute
+ * @param from the pattern supplied in the from attribute
+ * @param value the expression supplied in the value attribute
+ * @param format the expression supplied in the format attribute
+ * @param groupSize the expression supplied in the group-size attribute
+ * @param groupSeparator the expression supplied in the grouping-separator attribute
+ * @param letterValue the expression supplied in the letter-value attribute
+ * @param ordinal the expression supplied in the ordinal attribute
+ * @param lang the expression supplied in the lang attribute
+ * @param formatter A NumberFormatter to be used
+ * @param numberer A Numberer to be used for localization
+ * @param hasVariablesInPatterns true if one or more of the patterns contains variable references
+ * @param backwardsCompatible true if running in 1.0 compatibility mode
+ */
+
+ public NumberInstruction(Configuration config,
+ Expression select,
+ int level,
+ Pattern count,
+ Pattern from,
+ Expression value,
+ Expression format,
+ Expression groupSize,
+ Expression groupSeparator,
+ Expression letterValue,
+ Expression ordinal,
+ Expression startAt,
+ Expression lang,
+ NumberFormatter formatter,
+ Numberer numberer,
+ boolean hasVariablesInPatterns,
+ boolean backwardsCompatible) {
+ this.select = select;
+ this.level = level;
+ this.count = count;
+ this.from = from;
+ this.value = value;
+ this.format = format;
+ this.groupSize = groupSize;
+ this.groupSeparator = groupSeparator;
+ this.letterValue = letterValue;
+ this.ordinal = ordinal;
+ this.startAt = startAt;
+ this.lang = lang;
+ this.formatter = formatter;
+ this.numberer = numberer;
+ this.hasVariablesInPatterns = hasVariablesInPatterns;
+ this.backwardsCompatible = backwardsCompatible;
+
+ final TypeHierarchy th = config.getTypeHierarchy();
+ if (this.value != null && !this.value.getItemType(th).isPlainType()) {
+ this.value = Atomizer.makeAtomizer(this.value);
+ }
+
+ Iterator<Expression> kids = iterateSubExpressions();
+ while (kids.hasNext()) {
+ Expression child = kids.next();
+ adoptChildExpression(child);
+ }
+ }
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ select = visitor.simplify(select);
+ value = visitor.simplify(value);
+ format = visitor.simplify(format);
+ groupSize = visitor.simplify(groupSize);
+ groupSeparator = visitor.simplify(groupSeparator);
+ letterValue = visitor.simplify(letterValue);
+ ordinal = visitor.simplify(ordinal);
+ startAt = visitor.simplify(startAt);
+ lang = visitor.simplify(lang);
+ if (count != null) {
+ count = count.simplify(visitor);
+ }
+ if (from != null) {
+ from = from.simplify(visitor);
+ }
+ return this;
+ }
+
+ /**
+ * Perform static analysis of an expression and its subexpressions.
+ *
+ * <p>This checks statically that the operands of the expression have
+ * the correct type; if necessary it generates code to do run-time type checking or type
+ * conversion. A static type error is reported only if execution cannot possibly succeed, that
+ * is, if a run-time type error is inevitable. The call may return a modified form of the expression.</p>
+ *
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable. However, the types of such functions and
+ * variables will only be accurately known if they have been explicitly declared.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ * @return the original expression, rewritten to perform necessary
+ * run-time type checks, and to perform other type-related
+ * optimizations
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (select != null) {
+ select = visitor.typeCheck(select, contextItemType);
+ } else {
+ if (value==null) {
+ // we are numbering the context node
+ XPathException err = null;
+ if (contextItemType == null || contextItemType.itemType == null) {
+ err = new XPathException(
+ "xsl:number requires a select attribute, a value attribute, or a context item");
+ } else if (contextItemType.itemType.isPlainType()) {
+ err = new XPathException(
+ "xsl:number requires the context item to be a node, but it is an atomic value");
+
+ }
+ if (err != null) {
+ err.setIsTypeError(true);
+ err.setErrorCode("XTTE0990");
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ }
+ if (value != null) {
+ value = visitor.typeCheck(value, contextItemType);
+ }
+ if (format != null) {
+ format = visitor.typeCheck(format, contextItemType);
+ }
+ if (groupSize != null) {
+ groupSize = visitor.typeCheck(groupSize, contextItemType);
+ }
+ if (groupSeparator != null) {
+ groupSeparator = visitor.typeCheck(groupSeparator, contextItemType);
+ }
+ if (letterValue != null) {
+ letterValue = visitor.typeCheck(letterValue, contextItemType);
+ }
+ if (ordinal != null) {
+ ordinal = visitor.typeCheck(ordinal, contextItemType);
+ }
+ if (startAt != null) {
+ startAt = visitor.typeCheck(startAt, contextItemType);
+ }
+ if (lang != null) {
+ lang = visitor.typeCheck(lang, contextItemType);
+ }
+ if (count != null) {
+ visitor.typeCheck(new PatternSponsor(count), contextItemType);
+ }
+ if (from != null) {
+ visitor.typeCheck(new PatternSponsor(from), contextItemType);
+ }
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (select != null) {
+ select = visitor.optimize(select, contextItemType);
+ }
+ if (value != null) {
+ value = visitor.optimize(value, contextItemType);
+ }
+ if (format != null) {
+ format = visitor.optimize(format, contextItemType);
+ }
+ if (groupSize != null) {
+ groupSize = visitor.optimize(groupSize, contextItemType);
+ }
+ if (groupSeparator != null) {
+ groupSeparator = visitor.optimize(groupSeparator, contextItemType);
+ }
+ if (letterValue != null) {
+ letterValue = visitor.optimize(letterValue, contextItemType);
+ }
+ if (ordinal != null) {
+ ordinal = visitor.optimize(ordinal, contextItemType);
+ }
+ if (startAt != null) {
+ startAt = visitor.optimize(startAt, contextItemType);
+ }
+ if (lang != null) {
+ lang = visitor.optimize(lang, contextItemType);
+ }
+ return this;
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ List<Expression> sub = new ArrayList<Expression>(9);
+ if (select != null) {
+ sub.add(select);
+ }
+ if (value != null) {
+ sub.add(value);
+ }
+ if (format != null) {
+ sub.add(format);
+ }
+ if (groupSize != null) {
+ sub.add(groupSize);
+ }
+ if (groupSeparator != null) {
+ sub.add(groupSeparator);
+ }
+ if (letterValue != null) {
+ sub.add(letterValue);
+ }
+ if (ordinal != null) {
+ sub.add(ordinal);
+ }
+ if (startAt != null) {
+ sub.add(startAt);
+ }
+ if (lang != null) {
+ sub.add(lang);
+ }
+ if (count != null) {
+ sub.add(new PatternSponsor(count));
+ }
+ if (from != null) {
+ sub.add(new PatternSponsor(from));
+ }
+ return sub.iterator();
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new NumberInstruction(
+ getExecutable().getConfiguration(),
+ copy(select), level, count, from, copy(value), copy(format),
+ copy(groupSize), copy(groupSeparator), copy(letterValue), copy(ordinal), copy(startAt),
+ copy(lang), formatter, numberer, hasVariablesInPatterns, backwardsCompatible);
+ // TODO: copy the patterns (level and count)
+ }
+
+ private Expression copy(Expression exp) {
+ return (exp == null ? null : exp.copy());
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (value == original) {
+ value = replacement;
+ found = true;
+ }
+ if (format == original) {
+ format = replacement;
+ found = true;
+ }
+ if (groupSize == original) {
+ groupSize = replacement;
+ found = true;
+ }
+ if (groupSeparator == original) {
+ groupSeparator = replacement;
+ found = true;
+ }
+ if (letterValue == original) {
+ letterValue = replacement;
+ found = true;
+ }
+ if (ordinal == original) {
+ ordinal = replacement;
+ found = true;
+ }
+ if (startAt == original) {
+ startAt = replacement;
+ found = true;
+ }
+ if (lang == original) {
+ lang = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Determine the intrinsic dependencies of an expression, that is, those which are not derived
+ * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
+ * on the context position, while (position()+1) does not. The default implementation
+ * of the method returns 0, indicating "no dependencies".
+ *
+ * @return a set of bit-significant flags identifying the "intrinsic"
+ * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
+ */
+
+ public int getIntrinsicDependencies() {
+ return (select == null ? StaticProperty.DEPENDS_ON_CONTEXT_ITEM : 0);
+ }
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.STRING;
+ }
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Offer promotion for this subexpression. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent the containing expression in the expression tree
+ * @return if the offer is not accepted, return this expression unchanged.
+ * Otherwise return the result of rewriting the expression to promote
+ * this subexpression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp!=null) {
+ return exp;
+ } else {
+ if (select != null) {
+ select = doPromotion(select, offer);
+ }
+ if (value != null) {
+ value = doPromotion(value, offer);
+ }
+ if (format != null) {
+ format = doPromotion(format, offer);
+ }
+ if (groupSize != null) {
+ groupSize = doPromotion(groupSize, offer);
+ }
+ if (groupSeparator != null) {
+ groupSeparator = doPromotion(groupSeparator, offer);
+ }
+ if (letterValue != null) {
+ letterValue = doPromotion(letterValue, offer);
+ }
+ if (ordinal != null) {
+ ordinal = doPromotion(ordinal, offer);
+ }
+ if (startAt != null) {
+ startAt = doPromotion(startAt, offer);
+ }
+ if (lang != null) {
+ lang = doPromotion(lang, offer);
+ }
+ if (count != null) {
+ count.promote(offer, this);
+ }
+ if (from != null) {
+ from.promote(offer, this);
+ }
+ return this;
+ }
+ }
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ long value = -1;
+ List<Object> vec = null; // a list whose items may be of type either Long or
+ // BigInteger or the string to be output (e.g. "NaN")
+ final ConversionRules rules = context.getConfiguration().getConversionRules();
+ long startValue;
+ String startAv = startAt.evaluateAsString(context).toString();
+ try {
+ startValue = Integer.parseInt(startAv);
+ } catch (NumberFormatException e) {
+ XPathException err = new XPathException("Value of start-at attribute must be an integer", "XTDE0030");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ startValue--;
+ if (this.value != null) {
+
+ SequenceIterator iter = this.value.iterate(context);
+ vec = new ArrayList<Object>(4);
+ while (true) {
+ AtomicValue val = (AtomicValue) iter.next();
+ if (val == null) {
+ break;
+ }
+ if (backwardsCompatible && !vec.isEmpty()) {
+ break;
+ }
+ try {
+ NumericValue num;
+ if (val instanceof NumericValue) {
+ num = (NumericValue) val;
+ } else {
+ num = NumberFn.convert(val, context.getConfiguration());
+ }
+ if (num.isNaN()) {
+ throw new XPathException("NaN"); // thrown to be caught
+ }
+ num = num.round(0);
+ if (num.compareTo(Int64Value.MAX_LONG) > 0) {
+ BigInteger bi = ((BigIntegerValue) Converter.convert(num, BuiltInAtomicType.INTEGER, rules).asAtomic()).asBigInteger();
+ if (startValue != 0) {
+ bi = bi.add(BigInteger.valueOf(startValue));
+ }
+ vec.add(bi);
+ } else {
+ if (num.compareTo(Int64Value.ZERO) < 0) {
+ throw new XPathException("The numbers to be formatted must not be negative");
+ // thrown to be caught
+ }
+ long i = ((NumericValue) Converter.convert(num, BuiltInAtomicType.INTEGER, rules).asAtomic()).longValue();
+ i += startValue;
+ vec.add(i);
+ }
+ } catch (XPathException err) {
+ if (backwardsCompatible) {
+ vec.add("NaN");
+ } else {
+ vec.add(val.getStringValue());
+ XPathException e = new XPathException("Cannot convert supplied value to an integer. " + err.getMessage());
+ e.setErrorCode("XTDE0980");
+ e.setLocator(this);
+ e.setXPathContext(context);
+ throw e;
+ }
+ }
+ }
+ if (backwardsCompatible && vec.isEmpty()) {
+ vec.add("NaN");
+ }
+ } else {
+ NodeInfo source;
+ if (select != null) {
+ source = (NodeInfo) select.evaluateItem(context);
+ } else {
+ Item item = context.getContextItem();
+ if (!(item instanceof NodeInfo)) {
+ XPathException err = new XPathException("context item for xsl:number must be a node");
+ err.setErrorCode("XTTE0990");
+ err.setIsTypeError(true);
+ err.setXPathContext(context);
+ err.setLocator(this);
+ throw err;
+ }
+ source = (NodeInfo) item;
+ }
+
+ if (level == SIMPLE) {
+ value = Navigator.getNumberSimple(source, context);
+ value += startValue;
+ } else if (level == SINGLE) {
+ value = Navigator.getNumberSingle(source, count, from, context);
+ if (value == 0) {
+ vec = Collections.emptyList(); // an empty list
+ } else {
+ value += startValue;
+ }
+ } else if (level == ANY) {
+ value = Navigator.getNumberAny(this, source, count, from, context, hasVariablesInPatterns);
+ if (value == 0) {
+ vec = Collections.emptyList(); // an empty list
+ } else {
+ value += startValue;
+ }
+ } else if (level == MULTI) {
+ vec = new ArrayList<Object>();
+ for (long n : Navigator.getNumberMulti(source, count, from, context)) {
+ vec.add(n + startValue);
+ }
+ }
+ }
+
+ int gpsize = 0;
+ String gpseparator = "";
+ String letterVal;
+ String ordinalVal = null;
+
+ if (groupSize != null) {
+ String g = groupSize.evaluateAsString(context).toString();
+ try {
+ gpsize = Integer.parseInt(g);
+ } catch (NumberFormatException err) {
+ XPathException e = new XPathException("grouping-size must be numeric");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0030");
+ e.setLocator(this);
+ throw e;
+ }
+ }
+
+ if (groupSeparator != null) {
+ gpseparator = groupSeparator.evaluateAsString(context).toString();
+ }
+
+ if (ordinal != null) {
+ ordinalVal = ordinal.evaluateAsString(context).toString();
+ }
+
+ // fast path for the simple case
+
+ if (vec == null && format == null && gpsize == 0 && lang == null) {
+ return new StringValue("" + value);
+ }
+
+ // Use the numberer decided at compile time if possible; otherwise try to get it from
+ // a table of numberers indexed by language; if not there, load the relevant class and
+ // add it to the table.
+ Numberer numb = numberer;
+ if (numb == null) {
+ String language = lang.evaluateAsString(context).toString();
+ ValidationFailure vf = StringConverter.STRING_TO_LANGUAGE.validate(language);
+ if (vf != null) {
+ throw new XPathException("The lang attribute of xsl:number must be a valid language code", "XTDE0030");
+ }
+ numb = context.getConfiguration().makeNumberer(language, null);
+ }
+
+ if (letterValue == null) {
+ letterVal = "";
+ } else {
+ letterVal = letterValue.evaluateAsString(context).toString();
+ if (!("alphabetic".equals(letterVal) || "traditional".equals(letterVal))) {
+ XPathException e = new XPathException("letter-value must be \"traditional\" or \"alphabetic\"");
+ e.setXPathContext(context);
+ e.setErrorCode("XTDE0030");
+ e.setLocator(this);
+ throw e;
+ }
+ }
+
+ if (vec == null) {
+ vec = new ArrayList<Object>(1);
+ vec.add(value);
+ }
+
+ NumberFormatter nf;
+ if (formatter == null) { // format not known until run-time
+ nf = new NumberFormatter();
+ nf.prepare(format.evaluateAsString(context).toString());
+ } else {
+ nf = formatter;
+ }
+
+ CharSequence s = nf.format(vec, gpsize, gpseparator, letterVal, ordinalVal, numb);
+ return new StringValue(s);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NumberInstruction expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new InterpretedExpressionCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("xslNumber");
+ out.emitAttribute("level", (level==ANY ? "any" : level==SINGLE ? "single" : "multi"));
+ if (count != null) {
+ out.emitAttribute("count", count.toString());
+ }
+ if (from != null) {
+ out.emitAttribute("from", from.toString());
+ }
+ if (select != null) {
+ out.startSubsidiaryElement("select");
+ select.explain(out);
+ out.endSubsidiaryElement();
+ }
+ if (value != null) {
+ out.startSubsidiaryElement("value");
+ value.explain(out);
+ out.endSubsidiaryElement();
+ }
+ if (format != null) {
+ out.startSubsidiaryElement("format");
+ format.explain(out);
+ out.endSubsidiaryElement();
+ }
+ out.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/ParameterSet.java b/sf/saxon/expr/instruct/ParameterSet.java
new file mode 100644
index 0000000..a53d199
--- /dev/null
+++ b/sf/saxon/expr/instruct/ParameterSet.java
@@ -0,0 +1,153 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Closure;
+
+/**
+ * A ParameterSet is a set of parameters supplied when calling a template.
+ * It is a collection of id-value pairs, the ids being numeric aliases for the parameter name,
+ * unique within a stylesheet
+ */
+
+public class ParameterSet {
+ private int[] keys;
+ private Sequence[] values;
+ private boolean[] typeChecked;
+ private int used = 0;
+
+ public static ParameterSet EMPTY_PARAMETER_SET = new ParameterSet(0);
+
+ /**
+ * Create an empty parameter set
+ */
+
+ public ParameterSet() {
+ this(10);
+ }
+
+ /**
+ * Create a parameter set specifying the initial capacity
+ * @param capacity the nominal number of entries in the parameter set
+ */
+
+ public ParameterSet(int capacity) {
+ keys = new int[capacity];
+ values = new Sequence[capacity];
+ typeChecked = new boolean[capacity];
+ }
+
+ /**
+ * Create a parameter set as a copy of an existing parameter set
+ * @param existing the parameter set to be copied
+ * @param extra the space to be allocated for additional entries
+ */
+
+ public ParameterSet(ParameterSet existing, int extra) {
+ this(existing.used + extra);
+ for (int i=0; i<existing.used; i++) {
+ put(existing.keys[i], existing.values[i], existing.typeChecked[i]);
+ }
+ }
+
+ /**
+ * Add a parameter to the ParameterSet
+ *
+ * @param id The parameter id, representing its name.
+ * @param value The value of the parameter
+ * @param checked True if the caller has done static type checking against the required type
+ */
+
+ public void put (int id, Sequence value, boolean checked) {
+ for (int i=0; i<used; i++) {
+ if (keys[i] == id) {
+ values[i] = value;
+ typeChecked[i] = checked;
+ return;
+ }
+ }
+ if (used+1 > keys.length) {
+ int newlength = (used<=5 ? 10 : used*2);
+ int[] newkeys = new int[newlength];
+ boolean[] newChecked = new boolean[newlength];
+ Sequence[] newvalues = new Sequence[newlength];
+ System.arraycopy(values, 0, newvalues, 0, used);
+ System.arraycopy(keys, 0, newkeys, 0, used);
+ System.arraycopy(typeChecked, 0, newChecked, 0, used);
+ values = newvalues;
+ keys = newkeys;
+ typeChecked = newChecked;
+ }
+ keys[used] = id;
+ typeChecked[used] = checked;
+ values[used++] = value;
+ }
+
+ /**
+ * Get the index position of a parameter
+ *
+ * @param id The numeric parameter id, representing its name.
+ * @return The index position of the parameter, or -1 if not defined
+ */
+
+ public int getIndex (int id) {
+ for (int i=0; i<used; i++) {
+ if (keys[i] == id) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Get the value of the parameter at a given index
+ * @param index the position of the entry required
+ * @return the value of the parameter at that position
+ */
+
+ public Sequence getValue(int index) {
+ return values[index];
+ }
+
+ /**
+ * Determine whether the parameter at a given index has been type-checked
+ * @param index the position of the entry required
+ * @return true if the parameter at that position has been type-checked
+ */
+
+ public boolean isTypeChecked(int index) {
+ return typeChecked[index];
+ }
+
+ /**
+ * Clear all values
+ */
+
+ public void clear() {
+ used = 0;
+ }
+
+ /**
+ * If any values are non-memo closures, expand them
+ * @throws net.sf.saxon.trans.XPathException if an error occurs evaluating any closures
+ */
+
+ public void materializeValues() throws XPathException {
+ for (int i=0; i<used; i++) {
+ if (values[i] instanceof Closure) {
+ values[i] = ((Closure)values[i]).reduce();
+ }
+ }
+ }
+
+ public static final int NOT_SUPPLIED = 0;
+ public static final int SUPPLIED = 1;
+ public static final int SUPPLIED_AND_CHECKED = 2;
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/instruct/ParentNodeConstructor.java b/sf/saxon/expr/instruct/ParentNodeConstructor.java
new file mode 100644
index 0000000..140f392
--- /dev/null
+++ b/sf/saxon/expr/instruct/ParentNodeConstructor.java
@@ -0,0 +1,389 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.stream.adjunct.DivisibleInstruction;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.type.Untyped;
+
+import java.util.Iterator;
+
+/**
+ * An abstract class to act as a common parent for instructions that create element nodes
+ * and document nodes.
+ */
+
+public abstract class ParentNodeConstructor extends Instruction implements ValidatingInstruction
+//#ifdefined BYTECODE
+ , DivisibleInstruction
+//#endif
+{
+
+ /*@NotNull*/ protected Expression content;
+ private boolean lazyConstruction = false;
+ private ParseOptions validationOptions = null;
+ private String baseURI;
+
+ /**
+ * Flag set to true if validation=preserve and no schema type supplied for validation; also true
+ * when validation="strip" if there is no need to physically strip type annotations
+ */
+
+ protected boolean preservingTypes = true;
+
+ /**
+ * Create a document or element node constructor instruction
+ */
+
+ public ParentNodeConstructor() {
+ }
+
+ /**
+ * Set the static base URI of the instruction
+ *
+ * @param uri the static base URI
+ */
+
+ public void setBaseURI(String uri) {
+ baseURI = uri;
+ }
+
+ /**
+ * Get the static base URI of the instruction
+ *
+ * @return the static base URI
+ */
+
+ public String getBaseURI() {
+ return baseURI;
+ }
+
+ /**
+ * Indicate that lazy construction should (or should not) be used. Note that
+ * this request will be ignored if validation is required
+ *
+ * @param lazy set to true if lazy construction should be used
+ */
+
+ public void setLazyConstruction(boolean lazy) {
+ lazyConstruction = lazy;
+ }
+
+ /**
+ * Establish whether lazy construction is to be used
+ *
+ * @return true if lazy construction is to be used
+ */
+
+ public final boolean isLazyConstruction() {
+ return lazyConstruction;
+ }
+
+ /**
+ * Get the schema type chosen for validation; null if not defined
+ *
+ * @return the type to be used for validation. (For a document constructor, this is the required
+ * type of the document element)
+ */
+
+ public SchemaType getSchemaType() {
+ return validationOptions==null ? null : validationOptions.getTopLevelType();
+ }
+
+ /**
+ * Get the validation options
+ * @return the validation options for the content of the constructed node. May be null if no
+ * validation was requested.
+ */
+
+ public ParseOptions getValidationOptions() {
+ return validationOptions;
+ }
+
+ /**
+ * Set the validation mode for the new document or element node
+ *
+ * @param mode the validation mode, for example {@link Validation#STRICT}
+ * @param schemaType the required type (for validation by type). Null if not
+ * validating by type
+ */
+
+
+ public void setValidationAction(int mode, /*@Nullable*/ SchemaType schemaType) {
+ preservingTypes = (mode == Validation.PRESERVE && schemaType == null);
+ if (!preservingTypes) {
+ if (validationOptions == null) {
+ validationOptions = new ParseOptions();
+ }
+ if (schemaType == Untyped.getInstance()) {
+ validationOptions.setSchemaValidationMode(Validation.SKIP);
+ } else {
+ validationOptions.setSchemaValidationMode(mode);
+ validationOptions.setTopLevelType(schemaType);
+ }
+ }
+ }
+
+
+ /**
+ * Get the validation mode for this instruction
+ *
+ * @return the validation mode, for example {@link Validation#STRICT} or {@link Validation#PRESERVE}
+ */
+ public int getValidationAction() {
+ return validationOptions == null ? Validation.PRESERVE : validationOptions.getSchemaValidationMode();
+ }
+
+ /**
+ * Set that the newly constructed node and everything underneath it will automatically be untyped,
+ * without any need to physically remove type annotations, even though validation=STRIP is set.
+ */
+
+ public void setNoNeedToStrip() {
+ preservingTypes = true;
+ }
+
+ /**
+ * Set the expression that constructs the content of the element
+ *
+ * @param content the content expression
+ */
+
+ public void setContentExpression(Expression content) {
+ this.content = content;
+ adoptChildExpression(content);
+ }
+
+ /**
+ * Get the expression that constructs the content of the element
+ *
+ * @return the content expression
+ */
+
+ public Expression getContentExpression() {
+ return content;
+ }
+
+ /**
+ * Get the cardinality of the sequence returned by evaluating this instruction
+ *
+ * @return the static cardinality
+ */
+
+ public int computeCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ content = visitor.simplify(content);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ content = visitor.typeCheck(content, contextItemType);
+ adoptChildExpression(content);
+ verifyLazyConstruction();
+ checkContentSequence(visitor.getStaticContext());
+ return this;
+ }
+
+ /**
+ * Check that the child instructions don't violate any obvious constraints for this kind of node
+ *
+ * @param env the static context
+ * @throws XPathException if the check fails
+ */
+
+ protected abstract void checkContentSequence(StaticContext env) throws XPathException;
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ content = visitor.optimize(content, contextItemType);
+ if (content instanceof Block) {
+ content = ((Block) content).mergeAdjacentTextInstructions();
+ }
+ adoptChildExpression(content);
+ if (visitor.isOptimizeForStreaming()) {
+ visitor.getConfiguration().obtainOptimizer().makeCopyOperationsExplicit(this, content);
+ }
+ if (getExecutable().isSchemaAware()) {
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (getValidationAction() == Validation.STRIP) {
+ if ((content.getSpecialProperties() & StaticProperty.ALL_NODES_UNTYPED) != 0 ||
+ (th.relationship(content.getItemType(th), NodeKindTest.ELEMENT) == TypeHierarchy.DISJOINT &&
+ th.relationship(content.getItemType(th), NodeKindTest.ATTRIBUTE) == TypeHierarchy.DISJOINT)) {
+ // No need to strip type annotations if there are none needing to be stripped
+ setNoNeedToStrip();
+ }
+ }
+ } else {
+ setNoNeedToStrip();
+ }
+ return this;
+ }
+
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ *
+ * @param offer The type of rewrite being offered
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ if (offer.action != PromotionOffer.UNORDERED) {
+ content = doPromotion(content, offer);
+ }
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(content);
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new MonoIterator<SubExpressionInfo>(
+ new SubExpressionInfo(content, true, false, NODE_VALUE_CONTEXT));
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (content == original) {
+ content = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true.
+ */
+
+ public final boolean createsNewNodes() {
+ return true;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Check that lazy construction is possible for this element
+ */
+
+ void verifyLazyConstruction() {
+ if (!isLazyConstruction()) {
+ return;
+ }
+ // Lazy construction is not possible if the expression depends on the values of position() or last(),
+ // as we can't save these.
+ if ((getDependencies() & (StaticProperty.DEPENDS_ON_POSITION | StaticProperty.DEPENDS_ON_LAST)) != 0) {
+ setLazyConstruction(false);
+ }
+ // Lazy construction is not possible if validation is required
+ if (getValidationAction() == Validation.STRICT || getValidationAction() == Validation.LAX
+ || getSchemaType() != null) {
+ setLazyConstruction(false);
+ }
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet result = super.addToPathMap(pathMap, pathMapNodeSet);
+ result.setReturnable(false);
+ TypeHierarchy th = getExecutable().getConfiguration().getTypeHierarchy();
+ ItemType type = getItemType(th);
+ if (th.relationship(type, NodeKindTest.ELEMENT) != TypeHierarchy.DISJOINT ||
+ th.relationship(type, NodeKindTest.DOCUMENT) != TypeHierarchy.DISJOINT) {
+ result.addDescendants();
+ }
+ return new PathMap.PathMapNodeSet(pathMap.makeNewRoot(this));
+ }
+
+ /**
+ * Determine whether this elementCreator performs validation or strips type annotations
+ *
+ * @return false if the instruction performs validation of the constructed output or if it strips
+ * type annotations, otherwise true
+ */
+
+ public boolean isPreservingTypes() {
+ return preservingTypes;
+ }
+}
+
+
diff --git a/sf/saxon/expr/instruct/Procedure.java b/sf/saxon/expr/instruct/Procedure.java
new file mode 100644
index 0000000..739635f
--- /dev/null
+++ b/sf/saxon/expr/instruct/Procedure.java
@@ -0,0 +1,148 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.trace.InstructionInfo;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This object represents the compiled form of a user-written function, template, attribute-set, etc
+ * (the source can be either an XSLT stylesheet function or an XQuery function).
+ *
+ * <p>It is assumed that type-checking, of both the arguments and the results,
+ * has been handled at compile time. That is, the expression supplied as the body
+ * of the function must be wrapped in code to check or convert the result to the
+ * required type, and calls on the function must be wrapped at compile time to check or
+ * convert the supplied arguments.
+ */
+
+public abstract class Procedure implements Serializable, Container, InstructionInfo, LocationProvider {
+
+ protected Expression body;
+ private Executable executable;
+ private String systemId;
+ private int lineNumber;
+ private SlotManager stackFrameMap;
+ private int hostLanguage;
+
+ public Procedure() {}
+
+ /**
+ * Get the granularity of the container.
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 2;
+ }
+
+ public void setBody(Expression body) {
+ this.body = body;
+ body.setContainer(this);
+ }
+
+ public void setHostLanguage(int language) {
+ hostLanguage = language;
+ }
+
+ public int getHostLanguage() {
+ return hostLanguage;
+ }
+
+ public final Expression getBody() {
+ return body;
+ }
+
+ public void setStackFrameMap(SlotManager map) {
+ stackFrameMap = map;
+ }
+
+ public SlotManager getStackFrameMap() {
+ return stackFrameMap;
+ }
+
+ public final Executable getExecutable() {
+ return executable;
+ }
+
+ public void setExecutable(Executable executable) {
+ this.executable = executable;
+ }
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ */
+
+ public LocationProvider getLocationProvider() {
+ return this;
+ }
+
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+ public String getSystemId(long locationId) {
+ return systemId;
+ }
+
+ public int getLineNumber(long locationId) {
+ return lineNumber;
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+ public Object getProperty(String name) {
+ return null;
+ }
+
+
+ /**
+ * Get an iterator over all the properties available. The values returned by the iterator
+ * will be of type String, and each string can be supplied as input to the getProperty()
+ * method to retrieve the value of the property. The iterator may return properties whose
+ * value is null.
+ */
+
+ public Iterator<String> getProperties() {
+ final List<String> list = Collections.emptyList();
+ return list.iterator();
+ }
+}
+
+
diff --git a/sf/saxon/expr/instruct/ProcessRegexMatchInstruction.java b/sf/saxon/expr/instruct/ProcessRegexMatchInstruction.java
new file mode 100644
index 0000000..c7dd67f
--- /dev/null
+++ b/sf/saxon/expr/instruct/ProcessRegexMatchInstruction.java
@@ -0,0 +1,93 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ProcessRegexMatchInstructionCompiler;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.regex.RegexIterator;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.Untyped;
+
+/**
+ * An internal instruction used by the fn:analyze-string function to process a matching
+ * substring. The instruction generates the content of the fn:match output element, by
+ * generating startElement and endElement events at the start and end of a group.
+ */
+public class ProcessRegexMatchInstruction extends Instruction {
+
+ NodeName groupNameCode = new FingerprintedQName("fn", NamespaceConstant.FN, "group");
+ NodeName nrNameCode = new NoNamespaceName("nr");
+
+ public ProcessRegexMatchInstruction(NamePool namePool) {
+ groupNameCode.allocateNameCode(namePool);
+ nrNameCode.allocateNameCode(namePool);
+ }
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_FOCUS;
+ }
+
+ /*@Nullable*/ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ SequenceIterator iter = context.getCurrentIterator();
+ if (!(iter instanceof RegexIterator)) {
+ throw new IllegalStateException("Current iterator should be a RegexIterator");
+ }
+ ((RegexIterator)iter).processMatchingSubstring(context, new RegexIterator.OnGroup() {
+
+ public void onGroupStart(XPathContext c, int groupNumber) throws XPathException {
+ Receiver out = c.getReceiver();
+ out.startElement(groupNameCode, Untyped.getInstance(), 0, 0);
+ out.attribute(nrNameCode, BuiltInAtomicType.UNTYPED_ATOMIC, ""+groupNumber, 0, 0);
+ }
+
+ public void onGroupEnd(XPathContext c, int groupNumber) throws XPathException{
+ Receiver out = c.getReceiver();
+ out.endElement();
+ }
+ });
+ return null;
+ }
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression copy() {
+ // Safe to return "this", because the only fields are constant for a Configuration
+ return this;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ProcessRegexMatchInstruction expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ProcessRegexMatchInstructionCompiler();
+ }
+//#endif
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("processRegexMatchingSubstring");
+ out.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/ProcessingInstruction.java b/sf/saxon/expr/instruct/ProcessingInstruction.java
new file mode 100644
index 0000000..4796e44
--- /dev/null
+++ b/sf/saxon/expr/instruct/ProcessingInstruction.java
@@ -0,0 +1,321 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ProcessingInstructionCompiler;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.NoNamespaceName;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.Iterator;
+
+
+/**
+ * An xsl:processing-instruction element in the stylesheet, or a processing-instruction
+ * constructor in a query
+ */
+
+public class ProcessingInstruction extends SimpleNodeConstructor {
+
+ /*@NotNull*/ private Expression name;
+
+ /**
+ * Create an xsl:processing-instruction instruction
+ * @param name the expression used to compute the name of the generated
+ * processing-instruction
+ */
+
+ public ProcessingInstruction(/*@NotNull*/ Expression name) {
+ this.name = name;
+ adoptChildExpression(name);
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ * @return the string "xsl:processing-instruction"
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_PROCESSING_INSTRUCTION;
+ }
+
+ /**
+ * Get the expression that defines the processing instruction name
+ * @return the expression that defines the processing instruction name
+ */
+
+ /*@NotNull*/
+ public Expression getNameExpression() {
+ return name;
+ }
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return NodeKindTest.PROCESSING_INSTRUCTION;
+ }
+
+ public int getCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ name = visitor.simplify(name);
+ return super.simplify(visitor);
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ProcessingInstruction exp = new ProcessingInstruction(name.copy());
+ exp.setSelect(select.copy(), getExecutable().getConfiguration());
+ return exp;
+ }
+
+ public void localTypeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ StaticContext env = visitor.getStaticContext();
+ name = visitor.typeCheck(name, contextItemType);
+ adoptChildExpression(name);
+
+ RoleLocator role = new RoleLocator(RoleLocator.INSTRUCTION, "processing-instruction/name", 0);
+ //role.setSourceLocator(this);
+ name = TypeChecker.staticTypeCheck(name, SequenceType.SINGLE_STRING, false, role, visitor);
+ adoptChildExpression(name);
+
+ // Do early checking of name if known statically
+
+ if (name instanceof Literal) {
+ String s = ((Literal)name).getValue().getStringValue();
+ checkName(Whitespace.trim(s), env.makeEarlyEvaluationContext());
+ }
+
+ // Do early checking of content if known statically
+
+ if (select instanceof Literal) {
+ String s = ((Literal)select).getValue().getStringValue();
+ String s2 = checkContent(s, env.makeEarlyEvaluationContext());
+ if (!s2.equals(s)) {
+ setSelect(new StringLiteral(s2), env.getConfiguration());
+ }
+ }
+ }
+
+ public int getDependencies() {
+ return name.getDependencies() | super.getDependencies();
+ }
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(name, select);
+ }
+
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new PairIterator<SubExpressionInfo>(
+ new SubExpressionInfo(name, true, false, NODE_VALUE_CONTEXT),
+ new SubExpressionInfo(select, true, false, NODE_VALUE_CONTEXT)
+ );
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ if (name == original) {
+ name = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+
+ /**
+ * Offer promotion for subexpressions. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @exception XPathException if any error is detected
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ name = doPromotion(name, offer);
+ super.promoteInst(offer);
+ }
+
+
+ /**
+ * Process the value of the node, to create the new node.
+ * @param value the string value of the new node
+ * @param context the dynamic evaluation context
+ * @throws XPathException
+ */
+
+ public void processValue(CharSequence value, XPathContext context) throws XPathException {
+ String expandedName = evaluateName(context);
+ if (expandedName != null) {
+ String data = checkContent(value.toString(), context);
+ SequenceReceiver out = context.getReceiver();
+ out.processingInstruction(expandedName, data, locationId, 0);
+ }
+ }
+
+ /**
+ * Check the content of the node, and adjust it if necessary
+ *
+ * @param data the supplied content
+ * @return the original content, unless adjustments are needed
+ * @throws XPathException if the content is invalid
+ */
+
+ protected String checkContent(String data, XPathContext context) throws XPathException {
+ if (isXSLT()) {
+ return checkContentXSLT(data);
+ } else {
+ try {
+ return checkContentXQuery(data);
+ } catch (XPathException err) {
+ err.setXPathContext(context);
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ }
+
+ /**
+ * Check the content of the node, and adjust it if necessary, using the XSLT rules
+ *
+ * @param data the supplied content
+ * @return the original content, unless adjustments are needed
+ */
+
+ public static String checkContentXSLT(String data) {
+ int hh;
+ while ((hh = data.indexOf("?>")) >= 0) {
+ data = data.substring(0, hh + 1) + ' ' + data.substring(hh + 1);
+ }
+ return Whitespace.removeLeadingWhitespace(data).toString();
+ }
+
+ /**
+ * Check the content of the node, and adjust it if necessary, using the XQuery rules
+ *
+ * @param data the supplied content
+ * @return the original content, unless adjustments are needed
+ * @throws XPathException if the content is invalid
+ */
+
+ public static String checkContentXQuery(String data) throws XPathException {
+ if (data.contains("?>")) {
+ throw new XPathException("Invalid characters (?>) in processing instruction", "XQDY0026");
+ }
+ return Whitespace.removeLeadingWhitespace(data).toString();
+ }
+
+ public NodeName evaluateNodeName(XPathContext context) throws XPathException {
+ String expandedName = evaluateName(context);
+ return new NoNamespaceName(expandedName);
+ }
+
+ /**
+ * Evaluate the name of the processing instruction.
+ * @param context the dynamic evaluation context
+ * @return the name of the processing instruction (an NCName), or null, incicating an invalid name
+ * @throws XPathException if evaluation fails, or if the recoverable error is treated as fatal
+ */
+ private String evaluateName(XPathContext context) throws XPathException {
+ String expandedName;
+ try {
+ expandedName = Whitespace.trim(name.evaluateAsString(context));
+ } catch (ClassCastException err) {
+ XPathException e = new XPathException("Processing instruction name is not a string");
+ e.setXPathContext(context);
+ e.setErrorCode("XQDY0041");
+ throw dynamicError(this, e, context);
+ }
+ checkName(expandedName, context);
+ return expandedName;
+ }
+
+ private void checkName(String expandedName, XPathContext context) throws XPathException {
+ if (!(context.getConfiguration().getNameChecker().isValidNCName(expandedName))) {
+ XPathException e = new XPathException("Processing instruction name " + Err.wrap(expandedName) + " is not a valid NCName");
+ e.setXPathContext(context);
+ e.setErrorCode((isXSLT() ? "XTDE0890" : "XQDY0041"));
+ throw dynamicError(this, e, context);
+ }
+ if (expandedName.equalsIgnoreCase("xml")) {
+ XPathException e = new XPathException("Processing instructions cannot be named 'xml' in any combination of upper/lower case");
+ e.setXPathContext(context);
+ e.setErrorCode((isXSLT() ? "XTDE0890" : "XQDY0064"));
+ throw dynamicError(this, e, context);
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ProcessingInstruction expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ProcessingInstructionCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("processingInstruction");
+ out.startSubsidiaryElement("name");
+ name.explain(out);
+ out.endSubsidiaryElement();
+ out.startSubsidiaryElement("select");
+ getContentExpression().explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/ResultDocument.java b/sf/saxon/expr/instruct/ResultDocument.java
new file mode 100644
index 0000000..13ada76
--- /dev/null
+++ b/sf/saxon/expr/instruct/ResultDocument.java
@@ -0,0 +1,1029 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ResultDocumentCompiler;
+import com.saxonica.stream.adjunct.DivisibleInstruction;
+import com.saxonica.stream.adjunct.ResultDocumentAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.functions.EscapeURI;
+import net.sf.saxon.lib.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Whitespace;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntIterator;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamResult;
+import java.net.URI;
+import java.util.*;
+
+
+
+/**
+ * The compiled form of an xsl:result-document element in the stylesheet.
+ * <p/>
+ * The xsl:result-document element takes an attribute href="filename". The filename will
+ * often contain parameters, e.g. {position()} to ensure that a different file is produced
+ * for each element instance.
+ * <p/>
+ * There is a further attribute "format" which determines the format of the
+ * output file, it identifies the name of an xsl:output element containing the output
+ * format details. In addition, individual serialization properties may be specified as attributes.
+ * These are attribute value templates, so they may need to be computed at run-time.
+ */
+
+public class ResultDocument extends Instruction implements ValidatingInstruction
+//#ifdefined STREAM
+ , DivisibleInstruction
+//#endif
+{
+
+ /*@Nullable*/ protected Expression href;
+ protected Expression formatExpression; // null if format was known at compile time
+ protected Expression content;
+ private boolean async = false;
+ protected Properties globalProperties;
+ protected Properties localProperties;
+ protected String baseURI; // needed only for saxon:next-in-chain, or with fn:put()
+ protected ParseOptions validationOptions;
+ protected IntHashMap<Expression> serializationAttributes;
+ protected NamespaceResolver nsResolver;
+ protected Expression dynamicOutputElement; // used in saxon:result-document() extension function
+ protected boolean resolveAgainstStaticBase = false; // used with fn:put()
+
+
+ /**
+ * Create a result-document instruction
+ * @param globalProperties properties defined on static xsl:output
+ * @param localProperties non-AVT properties defined on result-document element
+ * @param href href attribute of instruction
+ * @param formatExpression format attribute of instruction
+ * @param baseURI base URI of the instruction
+ * @param validationAction for example {@link net.sf.saxon.lib.Validation#STRICT}
+ * @param schemaType schema type against which output is to be validated
+ * @param serializationAttributes computed local properties
+ * @param nsResolver namespace resolver
+ */
+
+ public ResultDocument(Properties globalProperties, // properties defined on static xsl:output
+ Properties localProperties, // non-AVT properties defined on result-document element
+ Expression href,
+ Expression formatExpression, // AVT defining the output format
+ String baseURI,
+ int validationAction,
+ SchemaType schemaType,
+ IntHashMap<Expression> serializationAttributes, // computed local properties only
+ NamespaceResolver nsResolver) {
+ this.globalProperties = globalProperties;
+ this.localProperties = localProperties;
+ this.href = href;
+ this.formatExpression = formatExpression;
+ this.baseURI = baseURI;
+ setValidationAction(validationAction, schemaType);
+ this.serializationAttributes = serializationAttributes;
+ this.nsResolver = nsResolver;
+ adoptChildExpression(href);
+ for (Iterator it = serializationAttributes.valueIterator(); it.hasNext();) {
+ adoptChildExpression((Expression)it.next());
+ }
+ }
+
+ /**
+ * Set the expression that constructs the content
+ * @param content the expression defining the content of the result document
+ */
+
+ public void setContentExpression(Expression content) {
+ this.content = content;
+ adoptChildExpression(content);
+ }
+
+ /**
+ * Get the expression that constructs the content
+ * @return the content expression
+ */
+
+ public Expression getContentExpression() {
+ return content;
+ }
+
+ /**
+ * Set the schema type to be used for validation
+ *
+ * @param type the type to be used for validation. (For a document constructor, this is the required
+ * type of the document element)
+ */
+
+ public void setSchemaType(SchemaType type) {
+ if (validationOptions == null) {
+ validationOptions = new ParseOptions();
+ }
+ validationOptions.setSchemaValidationMode(Validation.BY_TYPE);
+ validationOptions.setTopLevelType(type);
+ }
+
+ /**
+ * Get the schema type chosen for validation; null if not defined
+ *
+ * @return the type to be used for validation. (For a document constructor, this is the required
+ * type of the document element)
+ */
+
+ public SchemaType getSchemaType() {
+ return validationOptions==null ? null : validationOptions.getTopLevelType();
+ }
+
+ /**
+ * Get the expression that computes the href attribute
+ * @return the href expression, or null if there is no href attribute
+ */
+
+ public Expression getHrefExpression() {
+ return href;
+ }
+
+ /**
+ * Get the static base URI of the expression
+ * @return the static base URI
+ */
+
+ public String getStaticBaseURI() {
+ return baseURI;
+ }
+
+ public boolean isResolveAgainstStaticBase() {
+ return resolveAgainstStaticBase;
+ }
+
+ /**
+ * Get the validation options
+ * @return the validation options for the content of the constructed node. May be null if no
+ * validation was requested.
+ */
+
+ public ParseOptions getValidationOptions() {
+ return validationOptions;
+ }
+
+ /**
+ * Set the validation mode for the new document
+ *
+ * @param mode the validation mode, for example {@link Validation#STRICT}
+ * @param schemaType the required type (for validation by type). Null if not
+ * validating by type
+ */
+
+
+ public void setValidationAction(int mode, /*@Nullable*/ SchemaType schemaType) {
+ boolean preservingTypes = (mode == Validation.PRESERVE && schemaType == null);
+ if (!preservingTypes) {
+ if (validationOptions == null) {
+ validationOptions = new ParseOptions();
+ validationOptions.setSchemaValidationMode(mode);
+ validationOptions.setTopLevelType(schemaType);
+ }
+ }
+ }
+
+
+ /**
+ * Get the validation mode for this instruction
+ *
+ * @return the validation mode, for example {@link Validation#STRICT} or {@link Validation#PRESERVE}
+ */
+ public int getValidationAction() {
+ return validationOptions == null ? Validation.PRESERVE : validationOptions.getSchemaValidationMode();
+ }
+
+
+ public Expression getFormatExpression() {
+ return formatExpression;
+ }
+
+ /**
+ * Set an expression that evaluates to a run-time xsl:output element, used in the saxon:result-document()
+ * extension function designed for use in XQuery
+ * @param exp the expression whose result should be an xsl:output element
+ */
+
+ public void setDynamicOutputElement(Expression exp) {
+ dynamicOutputElement = exp;
+ }
+
+ /**
+ * Set whether the the instruction should resolve the href relative URI against the static
+ * base URI (rather than the dynamic base output URI)
+ * @param staticBase set to true by fn:put(), to resolve against the static base URI of the query.
+ * Default is false, which causes resolution against the base output URI obtained dynamically
+ * from the Controller
+ */
+
+ public void setUseStaticBaseUri(boolean staticBase) {
+ resolveAgainstStaticBase = staticBase;
+ }
+
+
+
+ public void setAsynchronous(boolean async) {
+ this.async = async;
+ }
+
+ /**
+ * Ask if the instruction is to be asynchronous
+ * @return true unless saxon:asynchronous="no" was specified (regardless of other options that might
+ * suppress asychronous operation)
+ */
+
+ public boolean isAsynchronous() {
+ return async;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ * @param visitor an expression visitor
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression rewriting
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ content = visitor.simplify(content);
+ href = visitor.simplify(href);
+ for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) {
+ int key = it.next();
+ Expression value = serializationAttributes.get(key);
+ if (!(value instanceof Literal)) {
+ value = visitor.simplify(value);
+ serializationAttributes.put(key, value);
+ }
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ content = visitor.typeCheck(content, contextItemType);
+ adoptChildExpression(content);
+ if (href != null) {
+ href = visitor.typeCheck(href, contextItemType);
+ adoptChildExpression(href);
+ }
+ if (formatExpression != null) {
+ formatExpression = visitor.typeCheck(formatExpression, contextItemType);
+ adoptChildExpression(formatExpression);
+ }
+ for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) {
+ int key = it.next();
+ Expression value = serializationAttributes.get(key);
+ if (!(value instanceof Literal)) {
+ value = visitor.typeCheck(value, contextItemType);
+ adoptChildExpression(value);
+ serializationAttributes.put(key, value);
+ }
+ }
+ try {
+ DocumentInstr.checkContentSequence(visitor.getStaticContext(), content, validationOptions);
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ content = visitor.optimize(content, contextItemType);
+ adoptChildExpression(content);
+ if (href != null) {
+ href = visitor.optimize(href, contextItemType);
+ adoptChildExpression(href);
+ }
+ if (formatExpression != null) {
+ formatExpression = visitor.optimize(formatExpression, contextItemType);
+ adoptChildExpression(formatExpression);
+ // TODO: if the formatExpression is now a constant, could get the output properties now
+ }
+ for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) {
+ int key = it.next();
+ Expression value = serializationAttributes.get(key);
+ if (!(value instanceof Literal)) {
+ value = visitor.optimize(value, contextItemType);
+ adoptChildExpression(value);
+ serializationAttributes.put(key, value);
+ }
+ }
+ return this;
+ }
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.HAS_SIDE_EFFECTS;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ResultDocument r = new ResultDocument(
+ globalProperties,
+ localProperties,
+ (href == null ? null : href.copy()),
+ (formatExpression == null ? null : formatExpression.copy()),
+ baseURI,
+ getValidationAction(),
+ getSchemaType(),
+ serializationAttributes,
+ nsResolver);
+ r.content = content.copy();
+ r.dynamicOutputElement = (dynamicOutputElement == null ? null : dynamicOutputElement.copy());
+ r.resolveAgainstStaticBase = resolveAgainstStaticBase;
+ r.async = async;
+ return r;
+ }
+
+ /**
+ * Handle promotion offers, that is, non-local tree rewrites.
+ * @param offer The type of rewrite being offered
+ * @throws XPathException
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ content = doPromotion(content, offer);
+ if (href != null) {
+ href = doPromotion(href, offer);
+ }
+ for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) {
+ int key = it.next();
+ Expression value = serializationAttributes.get(key);
+ if (!(value instanceof Literal)) {
+ value = doPromotion(value, offer);
+ serializationAttributes.put(key, value);
+ }
+ }
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ * (the string "xsl:result-document")
+ */
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_RESULT_DOCUMENT;
+ }
+
+ /**
+ * Get the item type of the items returned by evaluating this instruction
+ * @param th the type hierarchy cache
+ * @return the static item type of the instruction. This is empty: the result-document instruction
+ * returns nothing.
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return ErrorType.getInstance();
+ }
+
+ /**
+ * Get all the XPath expressions associated with this instruction
+ * (in XSLT terms, the expression present on attributes of the instruction,
+ * as distinct from the child instructions in a sequence construction)
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ ArrayList<Expression> list = new ArrayList<Expression>(6);
+ list.add(content);
+ if (href != null) {
+ list.add(href);
+ }
+ if (formatExpression != null) {
+ list.add(formatExpression);
+ }
+ for (Iterator<Expression> it = serializationAttributes.valueIterator(); it.hasNext();) {
+ list.add(it.next());
+ }
+ if (dynamicOutputElement != null) {
+ list.add(dynamicOutputElement);
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (content == original) {
+ content = replacement;
+ found = true;
+ }
+ if (href == original) {
+ href = replacement;
+ found = true;
+ }
+ for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) {
+ int k = it.next();
+ if (serializationAttributes.get(k) == original) {
+ serializationAttributes.put(k, replacement);
+ found = true;
+ }
+ }
+ if (dynamicOutputElement == original) {
+ dynamicOutputElement = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ process(content, context);
+ return null;
+ }
+
+ public void process(Expression content, XPathContext context) throws XPathException {
+ context.getConfiguration().processResultDocument(this, content, context);
+ }
+
+ /**
+ * Evaluation method designed for calling from compiled bytecode.
+ * @param content The content expression. When called from bytecode, this will be the compiled version
+ * of the interpreted content expression
+ * @param context dynamic evaluation context
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public void processInstruction(Expression content, XPathContext context) throws XPathException {
+ final Controller controller = context.getController();
+ assert controller != null;
+ SequenceReceiver saved = context.getReceiver();
+ if (context.isTemporaryOutputState()) {
+ XPathException err = new XPathException("Cannot execute xsl:result-document while writing a temporary tree");
+ err.setErrorCode("XTDE1480");
+ err.setLocator(this);
+ throw err;
+ }
+
+ Result result;
+ OutputURIResolver resolver = (href == null ? null : controller.getOutputURIResolver().newInstance());
+
+ try {
+ result = getResult(href, baseURI, context, resolver, resolveAgainstStaticBase);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ throw e;
+ }
+ SerializerFactory sf = context.getConfiguration().getSerializerFactory();
+
+ Properties computedLocalProps = gatherOutputProperties(context);
+ String nextInChain = computedLocalProps.getProperty(SaxonOutputKeys.NEXT_IN_CHAIN);
+ if (nextInChain != null && nextInChain.length() > 0) {
+ try {
+ result = sf.prepareNextStylesheet(controller, nextInChain, baseURI, result);
+ } catch (TransformerException e) {
+ throw XPathException.makeXPathException(e);
+ }
+ }
+
+ // TODO: cache the serializer and reuse it if the serialization properties are fixed at
+ // compile time (that is, if serializationAttributes.isEmpty). Need to save the serializer
+ // in a form where the final output destination can be changed.
+
+ PipelineConfiguration pipe = controller.makePipelineConfiguration();
+ pipe.setHostLanguage(Configuration.XSLT);
+ LocationProvider provider = pipe.getLocationProvider();
+ Receiver receiver = sf.getReceiver(result, pipe, computedLocalProps);
+ context.changeOutputDestination(receiver, validationOptions);
+ // changeOutputDestination changes the location provider in a way we don't want (affects validation errors)
+ pipe.setLocationProvider(provider);
+ SequenceReceiver out = context.getReceiver();
+
+ out.open();
+ try {
+ out.startDocument(0);
+ content.process(context);
+ out.endDocument();
+ } catch (XPathException err) {
+ err.setXPathContext(context);
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ out.close();
+ context.setReceiver(saved);
+ if (resolver != null && result != controller.getPrincipalResult()) {
+ try {
+ //System.err.println("Trying to close " + result);
+ resolver.close(result);
+ } catch (TransformerException e) {
+ throw XPathException.makeXPathException(e);
+ }
+ }
+ }
+
+ public static Result getResult(Expression href, String baseURI,
+ XPathContext context, OutputURIResolver resolver,
+ boolean resolveAgainstStaticBase) throws XPathException {
+ String resultURI;
+ Result result;
+ Controller controller = context.getController();
+ if (href == null) {
+ result = controller.getPrincipalResult();
+ resultURI = controller.getBaseOutputURI();
+ if (resultURI == null) {
+ resultURI = Controller.ANONYMOUS_PRINCIPAL_OUTPUT_URI;
+ }
+ } else {
+ try {
+ String base;
+ if (resolveAgainstStaticBase) {
+ base = baseURI;
+ } else {
+ base = controller.getCookedBaseOutputURI();
+ }
+
+ String hrefValue = EscapeURI.iriToUri(href.evaluateAsString(context)).toString();
+ if (hrefValue.equals("")) {
+ result = controller.getPrincipalResult();
+ resultURI = controller.getBaseOutputURI();
+ if (resultURI == null) {
+ resultURI = Controller.ANONYMOUS_PRINCIPAL_OUTPUT_URI;
+ }
+ } else {
+ try {
+ result = (resolver==null ? null : resolver.resolve(hrefValue, base));
+ //System.err.println("Resolver returned " + result);
+ } catch (TransformerException err) {
+ throw XPathException.makeXPathException(err);
+ } catch (Exception err) {
+ err.printStackTrace();
+ throw new XPathException("Exception thrown by OutputURIResolver", err);
+ }
+ if (result == null) {
+ resolver = StandardOutputResolver.getInstance();
+ result = resolver.resolve(hrefValue, base);
+ }
+ resultURI = result.getSystemId();
+ if (resultURI == null) {
+ try {
+ resultURI = new URI(base).resolve(hrefValue).toString();
+ result.setSystemId(resultURI);
+ } catch (Exception err) {
+ // no action
+ }
+ }
+ }
+ } catch (TransformerException e) {
+ throw XPathException.makeXPathException(e);
+ }
+ }
+ checkAcceptableUri(context, resultURI);
+ traceDestination(context, result);
+ return result;
+ }
+
+ public static void traceDestination(XPathContext context, Result result) {
+ Configuration config = context.getConfiguration();
+ boolean timing = config.isTiming();
+ if (timing) {
+ String dest = result.getSystemId();
+ if (dest == null) {
+ if (result instanceof StreamResult) {
+ dest = "anonymous output stream";
+ } else if (result instanceof SAXResult) {
+ dest = "SAX2 ContentHandler";
+ } else if (result instanceof DOMResult) {
+ dest = "DOM tree";
+ } else {
+ dest = result.getClass().getName();
+ }
+ }
+ config.getStandardErrorOutput().println("Writing to " + dest);
+ }
+ }
+
+ public static void checkAcceptableUri(XPathContext context, String uri) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ //String uri = result.getSystemId();
+ if (uri != null) {
+ if (controller.getDocumentPool().find(uri) != null) {
+ XPathException err = new XPathException("Cannot write to a URI that has already been read: " +
+ (uri.equals(Controller.ANONYMOUS_PRINCIPAL_OUTPUT_URI) ? "(implicit output URI)" : uri));
+ err.setXPathContext(context);
+ err.setErrorCode("XTRE1500");
+ throw err;
+ }
+
+ DocumentURI documentKey = new DocumentURI(uri);
+ synchronized (controller.getDocumentPool()) {
+ if (!controller.checkUniqueOutputDestination(documentKey)) {
+ XPathException err = new XPathException("Cannot write more than one result document to the same URI: " +
+ (uri.equals(Controller.ANONYMOUS_PRINCIPAL_OUTPUT_URI) ? "(implicit output URI)" : uri));
+ err.setXPathContext(context);
+ err.setErrorCode("XTDE1490");
+ throw err;
+ } else {
+ controller.addUnavailableOutputDestination(documentKey);
+ }
+ }
+ }
+ controller.setThereHasBeenAnExplicitResultDocument();
+ }
+
+ /**
+ * Create a properties object that combines the serialization properties specified
+ * on the xsl:result-document itself with those specified in the referenced xsl:output declaration
+ * @param context The XPath evaluation context
+ * @return the assembled properties
+ * @throws XPathException if invalid properties are found
+ */
+
+ public Properties gatherOutputProperties(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ Configuration config = context.getConfiguration();
+ NamePool namePool = config.getNamePool();
+ Properties computedGlobalProps = globalProperties;
+
+ if (formatExpression != null) {
+ // format was an AVT and now needs to be computed
+ CharSequence format = formatExpression.evaluateAsString(context);
+ String[] parts;
+ try {
+ parts = config.getNameChecker().getQNameParts(format);
+ } catch (QNameException e) {
+ XPathException err = new XPathException("The requested output format " + Err.wrap(format) + " is not a valid QName");
+ err.setErrorCode("XTDE1460");
+ err.setXPathContext(context);
+ throw err;
+ }
+ String uri = nsResolver.getURIForPrefix(parts[0], false);
+ if (uri == null) {
+ XPathException err = new XPathException("The namespace prefix in the format name " + format + " is undeclared");
+ err.setErrorCode("XTDE1460");
+ err.setXPathContext(context);
+ throw err;
+ }
+ StructuredQName qName = new StructuredQName(parts[0], uri, parts[1]);
+ computedGlobalProps = getExecutable().getOutputProperties(qName);
+ if (computedGlobalProps == null) {
+ XPathException err = new XPathException("There is no xsl:output format named " + format);
+ err.setErrorCode("XTDE1460");
+ err.setXPathContext(context);
+ throw err;
+ }
+
+ }
+
+ // Now combine the properties specified on xsl:result-document with those specified on xsl:output
+
+ Properties computedLocalProps = new Properties(computedGlobalProps);
+
+ // First handle the properties with fixed values on xsl:result-document
+
+ for (Object keyo : localProperties.keySet()) {
+ String key = (String)keyo;
+ String[] parts = NamePool.parseClarkName(key);
+ try {
+ setSerializationProperty(computedLocalProps, parts[0], parts[1],
+ localProperties.getProperty(key), nsResolver, true, config);
+ } catch (XPathException e) {
+ e.setErrorCode("XTDE0030");
+ e.maybeSetLocation(this);
+ throw e;
+ }
+ }
+
+ // Now add the properties that were specified as AVTs
+
+ if (serializationAttributes.size() > 0) {
+ for (IntIterator it = serializationAttributes.keyIterator(); it.hasNext();) {
+ int key = it.next();
+ Expression exp = serializationAttributes.get(key);
+ String value = exp.evaluateAsString(context).toString();
+ String lname = namePool.getLocalName(key);
+ String uri = namePool.getURI(key);
+ try {
+ setSerializationProperty(computedLocalProps, uri, lname, value, nsResolver, false, config);
+ } catch (XPathException e) {
+ e.setErrorCode("XTDE0030");
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ if (NamespaceConstant.SAXON.equals(e.getErrorCodeNamespace()) &&
+ "SXWN".equals(e.getErrorCodeLocalPart().substring(0, 4))) {
+ try {
+ controller.getErrorListener().warning(e);
+ } catch (TransformerException e2) {
+ throw XPathException.makeXPathException(e2);
+ }
+ } else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ // Handle properties specified using a dynamic xsl:output element
+ // (Used when the instruction is generated from a saxon:result-document extension function call)
+
+ if (dynamicOutputElement != null) {
+ Item outputArg = dynamicOutputElement.evaluateItem(context);
+ if (!(outputArg instanceof NodeInfo &&
+ ((NodeInfo)outputArg).getNodeKind() == Type.ELEMENT &&
+ ((NodeInfo)outputArg).getFingerprint() == StandardNames.XSL_OUTPUT)) {
+ XPathException err = new XPathException(
+ "The third argument of saxon:result-document must be an <xsl:output> element");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ Properties dynamicProperties = new Properties();
+ processXslOutputElement((NodeInfo)outputArg, dynamicProperties, context);
+ for (Object o : dynamicProperties.keySet()) {
+ String key = (String) o;
+ StructuredQName name = StructuredQName.fromClarkName(key);
+ String value = dynamicProperties.getProperty(key);
+ try {
+ setSerializationProperty(
+ computedLocalProps, name.getURI(), name.getLocalPart(),
+ value, nsResolver, false, config);
+ } catch (XPathException e) {
+ e.setErrorCode("XTDE0030");
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+ }
+ return computedLocalProps;
+ }
+
+ /**
+ * Validate a serialization property and add its value to a Properties collection
+ * @param details the properties to be updated
+ * @param uri the uri of the property name
+ * @param lname the local part of the property name
+ * @param value the value of the serialization property. In the case of QName-valued values,
+ * this will use lexical QNames if prevalidated is false and a NamespaceResolver is supplied;
+ * otherwise they will use Clark-format names
+ * @param nsResolver resolver for lexical QNames; not needed if prevalidated, or if QNames are supplied in Clark
+ * format
+ * @param prevalidated true if values are already known to be valid and lexical QNames have been
+ * expanded into Clark notation
+ * @param config the Saxon configuration
+ * @throws XPathException if any serialization property has an invalid value
+ */
+
+ public static void setSerializationProperty(Properties details, String uri, String lname,
+ String value, /*@Nullable*/ NamespaceResolver nsResolver,
+ boolean prevalidated, Configuration config)
+ throws XPathException {
+
+ NameChecker checker = config.getNameChecker();
+ if (uri.length() == 0 || NamespaceConstant.SAXON.equals(uri)) {
+ if (lname.equals(StandardNames.METHOD)) {
+ if (value.equals("xml") || value.equals("html") ||
+ value.equals("text") || value.equals("xhtml") || prevalidated || value.startsWith("{")) {
+ details.setProperty(OutputKeys.METHOD, value);
+ } else {
+ String[] parts;
+ try {
+ parts = checker.getQNameParts(value);
+ String prefix = parts[0];
+ if (prefix.length() == 0) {
+ XPathException err = new XPathException("method must be xml, html, xhtml, or text, or a prefixed name");
+ err.setErrorCode("SEPM0016");
+ err.setIsStaticError(true);
+ throw err;
+ } else if (nsResolver != null) {
+ String muri = nsResolver.getURIForPrefix(prefix, false);
+ if (muri == null) {
+ XPathException err = new XPathException("Namespace prefix '" + prefix + "' has not been declared");
+ err.setErrorCode("SEPM0016");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ details.setProperty(OutputKeys.METHOD, '{' + muri + '}' + parts[1]);
+ } else {
+ details.setProperty(OutputKeys.METHOD, value);
+ }
+ } catch (QNameException e) {
+ XPathException err = new XPathException("Invalid method name. " + e.getMessage());
+ err.setErrorCode("SEPM0016");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ }
+ } else if (lname.equals(StandardNames.USE_CHARACTER_MAPS)) {
+ // The use-character-maps attribute is always turned into a Clark-format name at compile time
+ String existing = details.getProperty(SaxonOutputKeys.USE_CHARACTER_MAPS);
+ if (existing == null) {
+ existing = "";
+ }
+ details.setProperty(SaxonOutputKeys.USE_CHARACTER_MAPS, existing + value);
+ } else if (lname.equals("cdata-section-elements")) {
+ processListOfNodeNames(details, OutputKeys.CDATA_SECTION_ELEMENTS, value, nsResolver, true, prevalidated, checker);
+ } else if (lname.equals("suppress-indentation")) {
+ processListOfNodeNames(details, SaxonOutputKeys.SUPPRESS_INDENTATION, value, nsResolver, true, prevalidated, checker);
+ } else if (lname.equals("double-space")) {
+ processListOfNodeNames(details, SaxonOutputKeys.DOUBLE_SPACE, value, nsResolver, true, prevalidated, checker);
+ } else if (lname.equals("attribute-order")) {
+ processListOfNodeNames(details, SaxonOutputKeys.ATTRIBUTE_ORDER, value, nsResolver, false, prevalidated, checker);
+ } else if (lname.equals("next-in-chain")) {
+ XPathException e = new XPathException("saxon:next-in-chain property is available only on xsl:output");
+ e.setErrorCodeQName(
+ new StructuredQName("saxon", NamespaceConstant.SAXON, SaxonErrorCode.SXWN9004));
+ throw e;
+ } else {
+ // all other properties in the default or Saxon namespaces
+ if (lname.equals("output-version")) {
+ lname = "version";
+ }
+ String clarkName = lname;
+ if (uri.length() != 0) {
+ clarkName = '{' + uri + '}' + lname;
+ }
+ if (!prevalidated) {
+ try {
+ SaxonOutputKeys.checkOutputProperty(clarkName, value, config);
+ } catch (XPathException err) {
+ err.maybeSetErrorCode("SEPM0016");
+ throw err;
+ }
+ }
+ details.setProperty(clarkName, value);
+ }
+ } else {
+ // properties in user-defined namespaces
+ details.setProperty('{' + uri + '}' + lname, value);
+ }
+
+ }
+
+ private static void processListOfNodeNames(Properties details, String key, String value,
+ NamespaceResolver nsResolver, boolean useDefaultNS, boolean prevalidated,
+ NameChecker checker) throws XPathException {
+ String existing = details.getProperty(key);
+ if (existing == null) {
+ existing = "";
+ }
+ String s = SaxonOutputKeys.parseListOfNodeNames(value, nsResolver, useDefaultNS, prevalidated, checker, "SEPM0016");
+ details.setProperty(key, existing + s);
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("resultDocument");
+ if (href != null) {
+ out.startSubsidiaryElement("href");
+ href.explain(out);
+ out.endSubsidiaryElement();
+ }
+ out.startSubsidiaryElement("content");
+ content.explain(out);
+ out.endSubsidiaryElement();
+ out.endElement();
+ }
+
+ /**
+ * Construct a set of output properties from an xsl:output element supplied at run-time
+ * @param element an xsl:output element
+ * @param props Properties object to which will be added the values of those serialization properties
+ * that were specified
+ * @param c the XPath dynamic context
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ public static void processXslOutputElement(NodeInfo element, Properties props, XPathContext c) throws XPathException {
+ SequenceIterator iter = element.iterateAxis(AxisInfo.ATTRIBUTE);
+ NamespaceResolver resolver = new InscopeNamespaceResolver(element);
+ while (true) {
+ NodeInfo att = (NodeInfo)iter.next();
+ if (att == null) {
+ break;
+ }
+ String uri = att.getURI();
+ String local = att.getLocalPart();
+ String val = Whitespace.trim(att.getStringValueCS());
+ setSerializationProperty(props, uri, local, val, resolver, false, c.getConfiguration());
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the bytecode compiler for the ResultDocument expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ResultDocumentCompiler();
+ }
+
+//#endif
+
+//#ifdefined STREAM
+
+ /**
+ * Analyse the streamability of the instruction
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return the "sweep" of the instruction
+ */
+
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ Iterator<Expression> sub = iterateSubExpressions();
+ int contentStreamability = W3C_FREE_RANGING;
+ while (sub.hasNext()) {
+ Expression child = sub.next();
+ int s = child.getStreamability(NAVIGATION_CONTEXT, allowExtensions, null);
+ if (child == content) {
+ contentStreamability = s;
+ } else {
+ if (s != W3C_MOTIONLESS) {
+ return W3C_FREE_RANGING;
+ }
+ }
+ }
+ return contentStreamability;
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public ResultDocumentAdjunct getStreamingAdjunct() {
+ return new ResultDocumentAdjunct();
+ }
+
+ /**
+ * In streaming mode, process the first half of the instruction (for example, to start a new document or element)
+ * @param contextStack Stack of XPathContext objects. The instruction should use the one at the top of the stack.
+ * Some instructions (such as xsl:result-document) create a new context object and add it to the stack, removing it
+ * in the corresponding processRight() action.
+ * @param state a stack on which the instruction can save state information during the call on processLeft()
+ */
+
+ public void processLeft(Stack<XPathContext> contextStack, Stack<Object> state) throws XPathException {
+ ResultDocumentAdjunct.processLeft(this, contextStack, state);
+ }
+
+ /**
+ * In streaming mode, process the right half of the instruction (for example, to end a new document or element)
+ * @param contextStack the stack of dynamic context objects
+ * @param state a stack on which the instruction can save state information during the call on processLeft()
+ */
+
+ public void processRight(Stack<XPathContext> contextStack, Stack<Object> state) throws XPathException {
+ ResultDocumentAdjunct.processRight(this, contextStack, state);
+ }
+
+//#endif
+}
+
diff --git a/sf/saxon/expr/instruct/SavedNamespaceContext.java b/sf/saxon/expr/instruct/SavedNamespaceContext.java
new file mode 100644
index 0000000..508a902
--- /dev/null
+++ b/sf/saxon/expr/instruct/SavedNamespaceContext.java
@@ -0,0 +1,117 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NamespaceResolver;
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * An object representing a list of Namespaces. Used when the namespace
+ * controller in the stylesheet needs to be kept for use at run-time. The list of namespaces
+ * is maintained in the form of numeric prefix/uri codes, which are only meaningful
+ * in the context of a name pool
+ */
+
+public final class SavedNamespaceContext implements Serializable, NamespaceResolver {
+
+ // TODO: static context can't vary within an XPath expression. Therefore, save the
+ // NamespaceContext at the outermost expression level if any subexpression needs it.
+ // (Or put a namespace context expression on the expression tree, which has no effect
+ // at run-time, but is available to descendant expressions).
+
+ /*@NotNull*/ private Map<String,String> bindings = new HashMap<String, String>();
+
+ /**
+ * Create a NamespaceContext object
+ * @param nsBindings an array of namespace bindings. Each namespace code is an integer
+ * in which the first 16 bits represent the prefix (zero if it's the default namespace)
+ * and the next 16 bits represent the uri. These are codes held in the NamePool. The
+ * list will be searched from the "high" end.
+ */
+
+ public SavedNamespaceContext(/*@NotNull*/ Iterable<NamespaceBinding> nsBindings) {
+ this(nsBindings.iterator());
+ }
+
+ /**
+ * Create a NamespaceContext object
+ * @param nsBindings an array of namespace bindings. Each namespace code is an integer
+ * in which the first 16 bits represent the prefix (zero if it's the default namespace)
+ * and the next 16 bits represent the uri. These are codes held in the NamePool. The
+ * list will be searched from the "high" end.
+ */
+
+ public SavedNamespaceContext(/*@NotNull*/ Iterator<NamespaceBinding> nsBindings) {
+ while (nsBindings.hasNext()) {
+ NamespaceBinding next = nsBindings.next();
+ bindings.put(next.getPrefix(), next.getURI());
+ }
+ }
+
+ /**
+ * Create a SavedNamespaceContext that captures all the information in a given NamespaceResolver
+ * @param resolver the NamespaceResolver
+ */
+
+ public SavedNamespaceContext(/*@NotNull*/ NamespaceResolver resolver) {
+ Iterator iter = resolver.iteratePrefixes();
+ while (iter.hasNext()) {
+ String prefix = (String)iter.next();
+ String uri = resolver.getURIForPrefix(prefix, true);
+ bindings.put(prefix, uri);
+ }
+ }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ * @param prefix the namespace prefix
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is ""
+ * @return the uri for the namespace, or null if the prefix is not in scope
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(/*@NotNull*/ String prefix, boolean useDefault) {
+
+ if (prefix.length()==0 && !useDefault) {
+ return NamespaceConstant.NULL;
+ }
+
+ if (prefix.equals("xml")) {
+ return NamespaceConstant.XML;
+ }
+
+ String uri = bindings.get(prefix);
+ if (uri == null) {
+ return (prefix.length()==0 ? NamespaceConstant.NULL : null);
+ } else {
+ return uri;
+ }
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ List<String> prefixes = new ArrayList<String>(bindings.size() + 1);
+ for (String s : bindings.keySet()) {
+ prefixes.add(s);
+ }
+ prefixes.add("xml");
+ return prefixes.iterator();
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/instruct/SimpleNodeConstructor.java b/sf/saxon/expr/instruct/SimpleNodeConstructor.java
new file mode 100644
index 0000000..c722915
--- /dev/null
+++ b/sf/saxon/expr/instruct/SimpleNodeConstructor.java
@@ -0,0 +1,315 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.stream.adjunct.SimpleNodeConstructorAdjunct;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.functions.StringFn;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+
+import java.util.Iterator;
+
+/**
+ * Common superclass for XSLT instructions whose content template produces a text
+ * value: xsl:attribute, xsl:comment, xsl:processing-instruction, xsl:namespace,
+ * and xsl:text, and their XQuery equivalents
+ */
+
+public abstract class SimpleNodeConstructor extends Instruction {
+
+ // The select expression is adjusted to return xs:string?
+ // If the select expresion returns empty, then the node constructor returns empty.
+
+ /*@NotNull*/ protected Expression select;
+
+ /**
+ * Default constructor used by subclasses
+ */
+
+ public SimpleNodeConstructor() {
+ select = Literal.makeEmptySequence(); // because a non-null value is needed
+ }
+
+ /**
+ * Set the select expression: the value of this expression determines the string-value of the node
+ * @param select the expression that computes the string value of the node
+ * @param config the Saxon configuration (used for example to do early validation of the content
+ * of an attribute against the schema-defined type)
+ */
+
+ public void setSelect(Expression select, Configuration config) {
+ this.select = select;
+ adoptChildExpression(select);
+ }
+
+ /**
+ * Get the expression that determines the string value of the constructed node
+ * @return the select expression
+ */
+
+ public Expression getContentExpression() {
+ return select;
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ * This implementation returns true.
+ */
+
+ public final boolean createsNewNodes() {
+ return true;
+ }
+
+ /**
+ * Get the cardinality of the sequence returned by evaluating this instruction
+ * @return the static cardinality
+ */
+
+ public int computeCardinality() {
+ return select.getCardinality(); // may allow empty sequence
+ }
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ select = visitor.simplify(select);
+ return this;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+
+ public int computeSpecialProperties() {
+ return super.computeSpecialProperties() |
+ StaticProperty.SINGLE_DOCUMENT_NODESET;
+ }
+
+ /**
+ * Method to perform type-checking specific to the kind of instruction
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of the context item
+ * @throws XPathException if a type error is detected
+ */
+
+ public abstract void localTypeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException;
+
+ /**
+ * The typeCheck() method is called in XQuery, where node constructors
+ * are implemented as Expressions. In this case the required type for the
+ * select expression is a single string.
+ * @param visitor an expression visitor
+ * @return the rewritten expression
+ * @throws XPathException if any static errors are found in this expression
+ * or any of its children
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ localTypeCheck(visitor, contextItemType);
+
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ select = visitor.typeCheck(select, contextItemType);
+ if (select instanceof ValueOf) {
+ Expression valSelect = ((ValueOf)select).getContentExpression();
+ if (th.isSubType(valSelect.getItemType(th), BuiltInAtomicType.STRING) &&
+ !Cardinality.allowsMany(valSelect.getCardinality())) {
+ select = valSelect;
+ }
+ }
+
+ // Don't bother converting untypedAtomic to string
+ if (select instanceof StringFn) {
+ StringFn fn = (StringFn)select;
+ Expression arg = fn.getArguments()[0];
+ if (arg.getItemType(th) == BuiltInAtomicType.UNTYPED_ATOMIC && !Cardinality.allowsMany(arg.getCardinality())) {
+ select = arg;
+ }
+ } else if (select instanceof CastExpression && ((CastExpression)select).getTargetType() == BuiltInAtomicType.STRING) {
+ Expression arg = ((CastExpression)select).getBaseExpression();
+ if (arg.getItemType(th) == BuiltInAtomicType.UNTYPED_ATOMIC && !Cardinality.allowsMany(arg.getCardinality())) {
+ select = arg;
+ }
+ }
+ adoptChildExpression(select);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ select = visitor.optimize(select, contextItemType);
+ if (select instanceof StringFn) {
+ StringFn sf = (StringFn)select;
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (th.isSubType(sf.getArguments()[0].getItemType(th), BuiltInAtomicType.STRING) &&
+ !Cardinality.allowsMany(sf.getArguments()[0].getCardinality())) {
+ select = sf.getArguments()[0];
+ }
+ }
+ adoptChildExpression(select);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(select);
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ return new MonoIterator<SubExpressionInfo>(
+ new SubExpressionInfo(select, true, false, NODE_VALUE_CONTEXT));
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+//#ifdefined BYTECODE
+
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public SimpleNodeConstructorAdjunct getStreamingAdjunct() {
+ return new SimpleNodeConstructorAdjunct();
+ }
+//#endif
+
+ /**
+ * Process this instruction
+ * @param context the dynamic context of the transformation
+ * @return a TailCall to be executed by the caller, always null for this instruction
+ */
+
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ CharSequence value = select.evaluateAsString(context);
+ processValue(value, context);
+ return null;
+ }
+
+
+ /**
+ * Process the value of the node, to create the new node.
+ * @param value the string value of the new node
+ * @param context the dynamic evaluation context
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public abstract void processValue(CharSequence value, XPathContext context) throws XPathException;
+
+ /**
+ * Evaluate as an expression.
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ Item contentItem = select.evaluateItem(context);
+ String content;
+ if (contentItem == null) {
+ content = "";
+ } else {
+ content = contentItem.getStringValue();
+ content = checkContent(content, context);
+ }
+ final TypeHierarchy th = context.getConfiguration().getTypeHierarchy();
+ Orphan o = new Orphan(context.getConfiguration());
+ o.setNodeKind((short)getItemType(th).getPrimitiveType());
+ o.setStringValue(content);
+ o.setNodeName(evaluateNodeName(context));
+ return o;
+ }
+
+ /**
+ * Check the content of the node, and adjust it if necessary. The checks depend on the node kind.
+ * @param data the supplied content
+ * @param context the dynamic context
+ * @return the original content, unless adjustments are needed
+ * @throws XPathException if the content is invalid
+ */
+
+ protected String checkContent(String data, XPathContext context) throws XPathException {
+ return data;
+ }
+
+ /**
+ * Run-time method to compute the name of the node being constructed. This is overridden
+ * for nodes that have a name. The default implementation returns -1, which is suitable for
+ * unnamed nodes such as comments
+ *
+ * @param context the XPath dynamic evaluation context
+ * @return the name pool nameCode identifying the name of the constructed node
+ * @throws XPathException if any failure occurs
+ */
+
+ public NodeName evaluateNodeName(XPathContext context) throws XPathException {
+ return null;
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<Item> iterate(XPathContext context) throws XPathException {
+ return SingletonIterator.makeIterator(evaluateItem(context));
+ }
+
+ /**
+ * Offer promotion for subexpressions. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @exception XPathException if any error is detected
+ */
+
+ protected void promoteInst(PromotionOffer offer) throws XPathException {
+ select = doPromotion(select, offer);
+ super.promoteInst(offer);
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/instruct/SlotManager.java b/sf/saxon/expr/instruct/SlotManager.java
new file mode 100644
index 0000000..c93e3dc
--- /dev/null
+++ b/sf/saxon/expr/instruct/SlotManager.java
@@ -0,0 +1,99 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.om.StructuredQName;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A SlotManager supports functions, templates, etc: specifically, any executable code that
+ * requires a stack frame containing local variables. In XSLT a SlotManager underpins any
+ * top-level element that can contain local variable declarations,
+ * specifically, a top-level xsl:template, xsl:variable, xsl:param, or xsl:function element
+ * or an xsl:attribute-set element or xsl:key element. In XQuery it underpins functions and
+ * global variables. The purpose of the SlotManager is to allocate slot numbers to variables
+ * in the stack, and to record how many slots are needed. A Debugger may define a subclass
+ * with additional functionality.
+*/
+
+public class SlotManager implements Serializable {
+
+ /**
+ * An empty SlotManager
+ */
+
+ public static SlotManager EMPTY = new SlotManager(0);
+
+ private ArrayList<StructuredQName> variableMap = new ArrayList<StructuredQName>(10);
+ // values are StructuredQName objects representing the variable names
+ private int numberOfVariables = 0;
+
+ /**
+ * The constructor should not be called directly. A new SlotManager should be obtained using
+ * the factory method {@link net.sf.saxon.Configuration#makeSlotManager()}.
+ */
+
+ public SlotManager(){}
+
+ /**
+ * Create a SlotManager with a given number of slots
+ * @param n the number of slots
+ */
+
+ public SlotManager(int n) {
+ numberOfVariables = n;
+ variableMap = new ArrayList<StructuredQName>(n);
+ }
+
+ /**
+ * Get number of variables (size of stack frame)
+ * @return the number of slots for variables
+ */
+
+ public int getNumberOfVariables() {
+ return numberOfVariables;
+ }
+
+ /**
+ * Set the number of variables
+ * @param numberOfVariables the space to be allocated for variables
+ */
+
+ public void setNumberOfVariables(int numberOfVariables) {
+ this.numberOfVariables = numberOfVariables;
+ variableMap.trimToSize();
+ }
+
+ /**
+ * Allocate a slot number for a variable
+ * @param qName the name of the variable
+ * @return the allocated slot number (the next one available)
+ */
+
+ public int allocateSlotNumber(StructuredQName qName) {
+ variableMap.add(qName);
+ return numberOfVariables++;
+ }
+
+ /**
+ * Get the variable map (simply a list of variable names as structured QNames). Note that it
+ * is possible for several variables to have the same name.
+ * <p><b>Changed in Saxon 9.0 to return a list of StructuredQName values rather than integers</b></p>
+ * @return the list of variable names for this stack frame
+ */
+
+ /*@NotNull*/ public List<StructuredQName> getVariableMap() {
+ return variableMap;
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/TailCall.java b/sf/saxon/expr/instruct/TailCall.java
new file mode 100644
index 0000000..a5c6bff
--- /dev/null
+++ b/sf/saxon/expr/instruct/TailCall.java
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+import net.sf.saxon.trans.XPathException;
+
+
+/**
+* Interface representing a Tail Call. This is a package of information passed back by a called
+* instruction to its caller, representing a call (and its arguments) that needs to be made
+* by the caller. This saves stack space by unwinding the stack before making the call.
+*/
+
+public interface TailCall {
+
+ /**
+ * Process this TailCall (that is, executed the template call that is packaged in this
+ * object). This may return a further TailCall, which should also be processed: this
+ * is the mechanism by which a nested set of recursive calls is converted into an iteration.
+ * @return a further TailCall, if the recursion continues, or null, indicating that the
+ * recursion has terminated.
+ * @throws net.sf.saxon.trans.XPathException if any error occurs processing the tail call
+ */
+
+ public TailCall processLeavingTail() throws XPathException;
+
+}
+
diff --git a/sf/saxon/expr/instruct/TailCallReturner.java b/sf/saxon/expr/instruct/TailCallReturner.java
new file mode 100644
index 0000000..0ac527d
--- /dev/null
+++ b/sf/saxon/expr/instruct/TailCallReturner.java
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This interface represents an expression that is capable of being processed leaving tail calls for the
+ * calling instruction to deal with.
+ */
+
+public interface TailCallReturner {
+
+ /**
+ * ProcessLeavingTail: called to do the real work of this instruction. This method
+ * must be implemented in each subclass. The results of the instruction are written
+ * to the current Receiver, which can be obtained via the Controller.
+ * @param context The dynamic context of the transformation, giving access to the current node,
+ * the current variables, etc.
+ * @return null if the instruction has completed execution; or a TailCall indicating
+ * a function call or template call that is delegated to the caller, to be made after the stack has
+ * been unwound so as to save stack space.
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs during the evaluation
+ * of the instruction
+ */
+
+ /*@Nullable*/ public TailCall processLeavingTail(XPathContext context) throws XPathException;
+}
+
diff --git a/sf/saxon/expr/instruct/Template.java b/sf/saxon/expr/instruct/Template.java
new file mode 100644
index 0000000..1b70742
--- /dev/null
+++ b/sf/saxon/expr/instruct/Template.java
@@ -0,0 +1,316 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.RuleTarget;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+* The runtime object corresponding to an xsl:template element in the stylesheet.
+ *
+ * Note that the Template object no longer has precedence information associated with it; this is now
+ * only in the Rule object that references this Template. This allows two rules to share the same template,
+ * with different precedences. This occurs when a stylesheet module is imported more than once, from different
+ * places, with different import precedences.
+*/
+
+public class Template extends Procedure implements RuleTarget {
+
+ // TODO: change the calling mechanism for named templates to use positional parameters
+ // in the same way as functions. For templates that have both a match and a name attribute,
+ // create a match template as a wrapper around the named template, resulting in separate
+ // NamedTemplate and MatchTemplate classes. For named templates, perhaps compile into function
+ // calls directly, the only difference being that context is retained.
+
+ // The body of the template is represented by an expression,
+ // which is responsible for any type checking that's needed.
+
+ private Pattern matchPattern;
+ private StructuredQName templateName;
+ private boolean hasRequiredParams;
+ private boolean bodyIsTailCallReturner;
+ private SequenceType requiredType;
+ private boolean declaredStreamable;
+ private StructuredQName[] modeNames;
+
+ /**
+ * Create a template
+ */
+
+ public Template () {
+ setHostLanguage(Configuration.XSLT);
+ }
+
+ /**
+ * Initialize the template
+ * @param templateName the name of the template (if any)
+ * performed by apply-imports
+ */
+
+ public void setTemplateName(StructuredQName templateName) {
+ this.templateName = templateName;
+ }
+
+ /**
+ * Set the match pattern used with this template
+ * @param pattern the match pattern (may be null for a named template)
+ */
+
+ public void setMatchPattern(Pattern pattern) {
+ matchPattern = pattern;
+ }
+
+ /**
+ * Get the match pattern used with this template
+ * @return the match pattern, or null if this is a named template with no match pattern
+ */
+
+ public Pattern getMatchPattern() {
+ return matchPattern;
+ }
+
+ /**
+ * Set the expression that forms the body of the template
+ * @param body the body of the template
+ */
+
+ public void setBody(Expression body) {
+ super.setBody(body);
+ bodyIsTailCallReturner = (body instanceof TailCallReturner);
+ }
+
+ /**
+ * Get the name of the template (if it is named)
+ * @return the template name, or null if unnamed
+ */
+
+ public StructuredQName getTemplateName() {
+ return templateName;
+ }
+
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ *
+ */
+
+ public StructuredQName getObjectName() {
+ return templateName;
+ }
+
+ /**
+ * Set whether this template has one or more required parameters
+ * @param has true if the template has at least one required parameter
+ */
+
+ public void setHasRequiredParams(boolean has) {
+ hasRequiredParams = has;
+ }
+
+ /**
+ * Ask whether this template has one or more required parameters
+ * @return true if this template has at least one required parameter
+ */
+
+ public boolean hasRequiredParams() {
+ return hasRequiredParams;
+ }
+
+ /**
+ * Set the required type to be returned by this template
+ * @param type the required type as defined in the "as" attribute on the xsl:template element
+ */
+
+ public void setRequiredType(SequenceType type) {
+ requiredType = type;
+ }
+
+ /**
+ * Get the required type to be returned by this template
+ * @return the required type as defined in the "as" attribute on the xsl:template element
+ */
+
+ public SequenceType getRequiredType() {
+ if (requiredType == null) {
+ return SequenceType.ANY_SEQUENCE;
+ } else {
+ return requiredType;
+ }
+ }
+
+ /**
+ * Set the names of the modes associated with this template rule
+ * @param modeNames the names of the modes for this template rule
+ */
+
+ public void setModeNames(StructuredQName[] modeNames) {
+ this.modeNames = modeNames;
+ }
+
+ /**
+ * Get the names of the modes associated with this template rule
+ * @return the names of the modes for this template rule
+ */
+
+ public StructuredQName[] getModeNames() {
+ return modeNames;
+ }
+
+ /**
+ * Say whether or not this template is declared as streamable
+ * @param streamable true if the template belongs to a streamable mode
+ */
+
+ public void setDeclaredStreamable(boolean streamable) {
+ this.declaredStreamable = streamable;
+ }
+
+ /**
+ * Ask whether or not this template is declared as streamable
+ * @return true if the template belongs to a streamable mode
+ */
+
+ public boolean isDeclaredStreamable() {
+ return declaredStreamable;
+ }
+
+ /**
+ * Get the local parameter with a given parameter id
+ * @param id the parameter id
+ * @return the local parameter with this id if found, otherwise null
+ */
+
+ /*@Nullable*/ public LocalParam getLocalParam(int id) {
+ Iterator<Expression> iter = body.iterateSubExpressions();
+ while (iter.hasNext()) {
+ Expression child = iter.next();
+ if (child instanceof LocalParamSetter && ((LocalParamSetter)child).getBinding().getParameterId() == id) {
+ return ((LocalParamSetter)child).getBinding();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Process the template, without returning any tail calls. This path is used by
+ * xsl:apply-imports and xsl:next-match
+ * @param context The dynamic context, giving access to the current node,
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs while evaluating
+ * the template
+ */
+
+ public void apply(XPathContextMajor context) throws XPathException {
+ TailCall tc = applyLeavingTail(context);
+ while (tc != null) {
+ tc = tc.processLeavingTail();
+ }
+ }
+
+ /**
+ * Process this template, with the possibility of returning a tail call package if the template
+ * contains any tail calls that are to be performed by the caller.
+ * @param context the XPath dynamic context
+ * @return null if the template exited normally; but if it was a tail call, details of the call
+ * that hasn't been made yet and needs to be made by the caller
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs while evaluating
+ * the template
+ */
+
+ public TailCall applyLeavingTail(XPathContextMajor context) throws XPathException {
+ if (bodyIsTailCallReturner) {
+ return ((TailCallReturner)body).processLeavingTail(context);
+ } else {
+ body.process(context);
+ return null;
+ }
+ }
+
+ /**
+ * Expand the template. Called when the template is invoked using xsl:call-template.
+ * Invoking a template by this method does not change the current template.
+ * @param context the XPath dynamic context
+ * @return null if the template exited normally; but if it was a tail call, details of the call
+ * that hasn't been made yet and needs to be made by the caller
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs while evaluating
+ * the template
+ */
+
+ public TailCall expand(XPathContext context) throws XPathException {
+ if (bodyIsTailCallReturner) {
+ return ((TailCallReturner)body).processLeavingTail(context);
+ } else if (body != null) {
+ body.process(context);
+ }
+ return null;
+ }
+
+
+ /**
+ * Get the type of construct. This will either be the fingerprint of a standard XSLT instruction name
+ * (values in {@link net.sf.saxon.om.StandardNames}: all less than 1024)
+ * or it will be a constant in class {@link net.sf.saxon.trace.Location}.
+ */
+
+ public int getConstructType() {
+ return Location.TEMPLATE;
+ }
+
+ /**
+ * Output diagnostic explanation to an ExpressionPresenter
+ */
+
+ public void explain(ExpressionPresenter presenter) {
+ presenter.emitAttribute("line", getLineNumber()+"");
+ presenter.emitAttribute("module", getSystemId());
+ if (isDeclaredStreamable()) {
+ presenter.emitAttribute("streamable", "true");
+ }
+ if (getBody() != null) {
+ getBody().explain(presenter);
+ }
+ }
+
+//#ifdefined STREAM
+ /**
+ * Ask whether this template satisfies the rules for a guaranteed streamable template
+ *
+ * @param allowExtensions if true, the method tests for streamability against the Saxon
+ * rules rather than the W3C rules
+ * @param reasons the caller may supply a list which on return will contain explanations
+ * for why the template is not streamable, suitable for inclusion in error messages
+ * @return true if the template is within the (Saxon or W3C) definition of guaranteed streamable
+ */
+
+ public boolean isActuallyStreamable(boolean allowExtensions, List<String> reasons) {
+ // TODO: check the pattern is motionless
+ if (body == null) {
+ if (reasons != null) {
+ reasons.add("The template has no body");
+ }
+ return false;
+ }
+ int s = body.getStreamability(Expression.NAVIGATION_CONTEXT, allowExtensions, reasons);
+ return s == Expression.W3C_CONSUMING || s == Expression.W3C_MOTIONLESS;
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/instruct/TerminationException.java b/sf/saxon/expr/instruct/TerminationException.java
new file mode 100644
index 0000000..87f2be0
--- /dev/null
+++ b/sf/saxon/expr/instruct/TerminationException.java
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* An exception thrown by xsl:message terminate="yes".
+*/
+
+public class TerminationException extends XPathException {
+
+ /**
+ * Construct a TerminationException
+ * @param message the text of the message to be output
+ */
+
+ public TerminationException(String message) {
+ super(message);
+ setErrorCode("XTMM9000");
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/instruct/TraceExpression.java b/sf/saxon/expr/instruct/TraceExpression.java
new file mode 100644
index 0000000..b3635e3
--- /dev/null
+++ b/sf/saxon/expr/instruct/TraceExpression.java
@@ -0,0 +1,509 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.TraceExpressionCompiler;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A wrapper expression used to trace expressions in XPath and XQuery.
+ */
+
+public class TraceExpression extends Instruction implements InstructionInfo {
+
+ private StructuredQName objectName;
+ private int constructType;
+ /*@Nullable*/ private NamespaceResolver namespaceResolver = null;
+ private HashMap<String, Object> properties = new HashMap<String, Object>(10);
+ Expression child; // the instruction or other expression to be traced
+
+ /**
+ * Create a trace expression that traces execution of a given child expression
+ * @param child the expression to be traced. This will be available to the TraceListener
+ * as the value of the "expression" property of the InstructionInfo.
+ */
+ public TraceExpression(Expression child) {
+ this.child = child;
+ adoptChildExpression(child);
+ setProperty("expression", child);
+ }
+
+ /**
+ * Set the type of construct. This will generally be a constant
+ * in class {@link net.sf.saxon.trace.Location}
+ * @param type an integer code for the type of construct being traced
+ */
+
+ public void setConstructType(int type) {
+ constructType = type;
+ }
+
+ /**
+ * Get the construct type. This will generally be a constant
+ * in class {@link net.sf.saxon.trace.Location}
+ */
+ public int getConstructType() {
+ return constructType;
+ }
+
+ /**
+ * Set the namespace context for the instruction being traced. This is needed if the
+ * tracelistener wants to evaluate XPath expressions in the context of the current instruction
+ * @param resolver The namespace resolver, or null if none is needed
+ */
+
+ public void setNamespaceResolver(/*@Nullable*/ NamespaceResolver resolver) {
+ namespaceResolver = resolver;
+ }
+
+ /**
+ * Get the namespace resolver to supply the namespace context of the instruction
+ * that is being traced
+ * @return the namespace resolver, or null if none is in use
+ */
+
+ /*@Nullable*/
+ public NamespaceResolver getNamespaceResolver() {
+ return namespaceResolver;
+ }
+
+ /**
+ * Set a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ * @param qName the name of the object, or null if not applicable
+ */
+
+ public void setObjectName(StructuredQName qName) {
+ objectName = qName;
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ * @return the name of the object, or null if not applicable
+ */
+
+ public StructuredQName getObjectName() {
+ return objectName;
+ }
+
+ /**
+ * Set a named property of the instruction/expression
+ * @param name the name of the property
+ * @param value the value of the property
+ */
+
+ public void setProperty(String name, Object value) {
+ properties.put(name, value);
+ }
+
+ /**
+ * Get a named property of the instruction/expression
+ * @param name the name of the property
+ * @return the value of the property
+ */
+
+ public Object getProperty(String name) {
+ return properties.get(name);
+ }
+
+ /**
+ * Get an iterator over all the properties available. The values returned by the iterator
+ * will be of type String, and each string can be supplied as input to the getProperty()
+ * method to retrieve the value of the property.
+ */
+
+ public Iterator<String> getProperties() {
+ return properties.keySet().iterator();
+ }
+
+
+ /**
+ * Get the InstructionInfo details about the construct. This is to satisfy the InstructionInfoProvider
+ * interface.
+ * @return the instruction details
+ */
+
+ public InstructionInfo getInstructionInfo() {
+ return this;
+ }
+
+ /**
+ * Get the system identifier (that is the base URI) of the static context of the expression being
+ * traced. This returns the same result as getSystemId(), it is provided to satisfy the
+ * {@link net.sf.saxon.event.LocationProvider} interface.
+ * @param locationId not used
+ * @return the URI of the module containing the expression
+ */
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+ /**
+ * Get the line number of the expression being
+ * traced. This returns the same result as getLineNumber(), it is provided to satisfy the
+ * {@link net.sf.saxon.event.LocationProvider} interface.
+ * @param locationId not used
+ * @return the line number of the expression within its module
+ */
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+ /*@NotNull*/
+ public Expression copy() {
+ TraceExpression t = new TraceExpression(child.copy());
+ t.objectName = objectName;
+ t.namespaceResolver = namespaceResolver;
+ t.constructType = constructType;
+ return t;
+ }
+
+ /**
+ * Determine whether this is an updating expression as defined in the XQuery update specification
+ * @return true if this is an updating expression
+ */
+
+ @Override
+ public boolean isUpdatingExpression() {
+ return child.isUpdatingExpression();
+ }
+
+ /**
+ * Determine whether this is a vacuous expression as defined in the XQuery update specification
+ *
+ * @return true if this expression is vacuous
+ */
+
+ @Override
+ public boolean isVacuousExpression() {
+ return child.isVacuousExpression();
+ }
+
+ /**
+ * Check to ensure that this expression does not contain any inappropriate updating subexpressions.
+ * This check is overridden for those expressions that permit updating subexpressions.
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression has a non-permitted updating subexpression
+ */
+
+ @Override
+ public void checkForUpdatingSubexpressions() throws XPathException {
+ child.checkForUpdatingSubexpressions();
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @exception net.sf.saxon.trans.XPathException if an error is discovered during expression
+ * rewriting
+ * @return the simplified expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ child = visitor.simplify(child);
+ if (child instanceof TraceExpression) {
+ return child;
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ child = visitor.typeCheck(child, contextItemType);
+ adoptChildExpression(child);
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ child = visitor.optimize(child, contextItemType);
+ adoptChildExpression(child);
+ return this;
+ }
+
+ public int getImplementationMethod() {
+ return child.getImplementationMethod();
+ }
+
+ /**
+ * Offer promotion for this subexpression. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent the parent of the subexpression
+ * @return if the offer is not accepted, return this expression unchanged.
+ * Otherwise return the result of rewriting the expression to promote
+ * this subexpression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ // Many rewrites are not attempted if tracing is activated. But those that are, for example
+ // rewriting of calls to current(), must be carried out.
+ Expression newChild = child.promote(offer, parent);
+ if (newChild != child) {
+ child = newChild;
+ adoptChildExpression(child);
+ return this;
+ }
+ return this;
+ }
+
+ /**
+ * Execute this instruction, with the possibility of returning tail calls if there are any.
+ * This outputs the trace information via the registered TraceListener,
+ * and invokes the instruction being traced.
+ * @param context the dynamic execution context
+ * @return either null, or a tail call that the caller must invoke on return
+ * @throws net.sf.saxon.trans.XPathException
+ */
+ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ TraceListener listener = controller.getTraceListener();
+ if (controller.isTracing()) {
+ assert listener != null;
+ listener.enter(getInstructionInfo(), context);
+ }
+ // Don't attempt tail call optimization when tracing, the results are too confusing
+ child.process(context);
+ if (controller.isTracing()) {
+ assert listener != null;
+ listener.leave(getInstructionInfo());
+ }
+ return null;
+ }
+
+ /**
+ * Get the item type of the items returned by evaluating this instruction
+ * @return the static item type of the instruction
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return child.getItemType(th);
+ }
+
+ /**
+ * Determine the static cardinality of the expression. This establishes how many items
+ * there will be in the result of the expression, at compile time (i.e., without
+ * actually evaluating the result.
+ *
+ * @return one of the values Cardinality.ONE_OR_MORE,
+ * Cardinality.ZERO_OR_MORE, Cardinality.EXACTLY_ONE,
+ * Cardinality.ZERO_OR_ONE, Cardinality.EMPTY. This default
+ * implementation returns ZERO_OR_MORE (which effectively gives no
+ * information).
+ */
+
+ public int getCardinality() {
+ return child.getCardinality();
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as {@link net.sf.saxon.expr.StaticProperty#DEPENDS_ON_CONTEXT_ITEM} and
+ * {@link net.sf.saxon.expr.StaticProperty#DEPENDS_ON_CURRENT_ITEM}. The default implementation combines the intrinsic
+ * dependencies of this expression with the dependencies of the subexpressions,
+ * computed recursively. This is overridden for expressions such as FilterExpression
+ * where a subexpression's dependencies are not necessarily inherited by the parent
+ * expression.
+ *
+ * @return a set of bit-significant flags identifying the dependencies of
+ * the expression
+ */
+
+ public int getDependencies() {
+ return child.getDependencies();
+ }
+
+ /**
+ * Determine whether this instruction creates new nodes.
+ *
+ *
+ */
+
+ public final boolean createsNewNodes() {
+ return (child.getSpecialProperties() & StaticProperty.NON_CREATIVE) == 0;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ *
+ * @return a set of flags indicating static properties of this expression
+ */
+
+ public int computeDependencies() {
+ return child.computeDependencies();
+ }
+
+ /**
+ * Evaluate an expression as a single item. This always returns either a single Item or
+ * null (denoting the empty sequence). No conversion is done. This method should not be
+ * used unless the static type of the expression is a subtype of "item" or "item?": that is,
+ * it should not be called if the expression may return a sequence. There is no guarantee that
+ * this condition will be detected.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @exception net.sf.saxon.trans.XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @return the node or atomic value that results from evaluating the
+ * expression; or null to indicate that the result is an empty
+ * sequence
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ if (controller.isTracing()) {
+ controller.getTraceListener().enter(getInstructionInfo(), context);
+ }
+ Item result = child.evaluateItem(context);
+ if (controller.isTracing()) {
+ controller.getTraceListener().leave(getInstructionInfo());
+ }
+ return result;
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @exception net.sf.saxon.trans.XPathException if any dynamic error occurs evaluating the
+ * expression
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<Item> iterate(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ if (controller.isTracing()) {
+ controller.getTraceListener().enter(getInstructionInfo(), context);
+ }
+ SequenceIterator result = child.iterate(context);
+ if (controller.isTracing()) {
+ controller.getTraceListener().leave(getInstructionInfo());
+ }
+ return result;
+ }
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(child);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (child == original) {
+ child = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ public Expression getChildExpression() {
+ return child;
+ }
+
+ public int getInstructionNameCode() {
+ if (child instanceof Instruction) {
+ return ((Instruction)child).getInstructionNameCode();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ child.explain(out);
+ }
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ Controller controller = context.getController();
+ assert controller != null;
+ if (controller.isTracing()) {
+ controller.getTraceListener().enter(getInstructionInfo(), context);
+ }
+ child.evaluatePendingUpdates(context, pul);
+ if (controller.isTracing()) {
+ controller.getTraceListener().leave(getInstructionInfo());
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Trace expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new TraceExpressionCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/instruct/UseAttributeSets.java b/sf/saxon/expr/instruct/UseAttributeSets.java
new file mode 100644
index 0000000..e651807
--- /dev/null
+++ b/sf/saxon/expr/instruct/UseAttributeSets.java
@@ -0,0 +1,250 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.UseAttributeSetsCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.List;
+
+/**
+ * This instruction corresponds to a use-attribute-sets attribute on a literal result element, xsl:element,
+ * or xsl:copy.
+ */
+public class UseAttributeSets extends Instruction {
+
+ private AttributeSet[] attributeSets;
+
+ /**
+ * Create a use-attribute-sets expression
+ * @param sets the set of attribute sets to be expanded
+ */
+
+ public UseAttributeSets(AttributeSet[] sets) {
+ attributeSets = sets;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @return the simplified expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during expression
+ * rewriting
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ throw new UnsupportedOperationException("UseAttributeSets.copy()");
+ }
+
+ /**
+ * Perform type checking of an expression and its subexpressions.
+ * <p/>
+ * <p>This checks statically that the operands of the expression have
+ * the correct type; if necessary it generates code to do run-time type checking or type
+ * conversion. A static type error is reported only if execution cannot possibly succeed, that
+ * is, if a run-time type error is inevitable. The call may return a modified form of the expression.</p>
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable. However, the types of such functions and
+ * variables may not be accurately known if they have not been explicitly declared.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten to perform necessary
+ * run-time type checks, and to perform other type-related
+ * optimizations
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Get the item type of the items returned by evaluating this instruction
+ *
+ * @return the static item type of the instruction
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return NodeKindTest.ATTRIBUTE;
+ }
+
+
+ /**
+ * Determine the intrinsic dependencies of an expression, that is, those which are not derived
+ * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
+ * on the context position, while (position()+1) does not. The default implementation
+ * of the method returns 0, indicating "no dependencies".
+ *
+ * @return a set of bit-significant flags identifying the "intrinsic"
+ * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
+ */
+
+ public int getIntrinsicDependencies() {
+ int d = 0;
+ for (AttributeSet as : attributeSets) {
+ d |= as.getFocusDependencies();
+ }
+ return d;
+ }
+
+ /**
+ * Get the target attribute sets of this instruction. Called from generated bytecode.
+ * @return the target attribute sets, as an array
+ */
+
+ public AttributeSet[] getTargetAttributeSets() {
+ return attributeSets;
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ int max = W3C_MOTIONLESS;
+ for (AttributeSet aset : attributeSets) {
+ int s = aset.getW3CStreamability();
+ if (s > max) {
+ max = s;
+ }
+ }
+ return max;
+ }
+//#endif
+
+ /**
+ * ProcessLeavingTail: called to do the real work of this instruction. This method
+ * must be implemented in each subclass. The results of the instruction are written
+ * to the current Receiver, which can be obtained via the Controller.
+ *
+ * @param context The dynamic context of the transformation, giving access to the current node,
+ * the current variables, etc.
+ * @return null if the instruction has completed execution; or a TailCall indicating
+ * a function call or template call that is delegated to the caller, to be made after the stack has
+ * been unwound so as to save stack space.
+ */
+
+ /*@Nullable*/ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ AttributeSet.expand(attributeSets, context);
+ return null;
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("useAttributeSets");
+ for (AttributeSet as : attributeSets) {
+ out.startElement("useAttributeSet");
+ out.emitAttribute("name", as.getObjectName().getDisplayName());
+ out.endElement();
+ }
+ out.endElement();
+ }
+
+ /**
+ * Test whether this UseAttributeSets expression is equal to another
+ * @param obj the other expression
+ */
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof UseAttributeSets)) {
+ return false;
+ }
+ if (attributeSets.length != ((UseAttributeSets)obj).attributeSets.length) {
+ return false;
+ }
+ for (int i=0; i<attributeSets.length; i++) {
+ AttributeSet as0 = attributeSets[i];
+ AttributeSet as1 = ((UseAttributeSets)obj).attributeSets[i];
+ if (!as0.getObjectName().equals(as1.getObjectName())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Compute a hashcode
+ */
+
+ public int hashCode() {
+ int h = 0x86423719;
+ for (AttributeSet attributeSet : attributeSets) {
+ h ^= attributeSet.getObjectName().hashCode();
+ }
+ return h;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the UseAttributeSets expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new UseAttributeSetsCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/expr/instruct/UserFunction.java b/sf/saxon/expr/instruct/UserFunction.java
new file mode 100644
index 0000000..37dd5ec
--- /dev/null
+++ b/sf/saxon/expr/instruct/UserFunction.java
@@ -0,0 +1,481 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.functions.hof.SpecificFunctionType;
+import net.sf.saxon.Controller;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.query.Annotation;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.FunctionItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This object represents the compiled form of a user-written function
+ * (the source can be either an XSLT stylesheet function or an XQuery function).
+ * <p/>
+ * <p>It is assumed that type-checking, of both the arguments and the results,
+ * has been handled at compile time. That is, the expression supplied as the body
+ * of the function must be wrapped in code to check or convert the result to the
+ * required type, and calls on the function must be wrapped at compile time to check or
+ * convert the supplied arguments.
+ */
+
+public class UserFunction extends Procedure {
+
+ private StructuredQName functionName;
+ private boolean tailCalls = false;
+ // indicates that the function contains tail calls, not necessarily recursive ones.
+ private boolean tailRecursive = false;
+ // indicates that the function contains tail calls on itself
+ private UserFunctionParameter[] parameterDefinitions;
+ private SequenceType resultType;
+ protected int evaluationMode = ExpressionTool.UNDECIDED;
+ private boolean isUpdating = false;
+ private int inlineable = -1; // 0:no 1:yes -1:don't know
+ private Map<StructuredQName, Annotation> annotationMap;
+
+ /**
+ * Create a user-defined function (the body must be added later)
+ */
+
+ public UserFunction() {
+ }
+
+ /**
+ * Set the function name
+ *
+ * @param name the function name
+ */
+
+ public void setFunctionName(StructuredQName name) {
+ functionName = name;
+ }
+
+ /**
+ * Get the function name
+ *
+ * @return the function name, as a StructuredQName
+ */
+
+ public StructuredQName getFunctionName() {
+ return functionName;
+ }
+
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ */
+
+ public StructuredQName getObjectName() {
+ return functionName;
+ }
+
+//#ifdefined HOF
+ /**
+ * Get the type of the function
+ * @return the function type
+ */
+
+ public FunctionItemType getFunctionType() {
+ SequenceType[] argTypes = new SequenceType[parameterDefinitions.length];
+ for (int i=0; i<parameterDefinitions.length; i++) {
+ UserFunctionParameter ufp = parameterDefinitions[i];
+ argTypes[i] = ufp.getRequiredType();
+ }
+ return new SpecificFunctionType(argTypes, resultType);
+ }
+//#endif
+
+ public Callable asCallable() {
+ return new Callable() {
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return UserFunction.this.call(context.newCleanContext(), arguments);
+ }
+ };
+ }
+
+ /**
+ * Supply a set of annotations
+ * @param map the new set of annotations, which will replace any previous annotations on the function
+ */
+
+ public void setAnnotationMap(Map<StructuredQName, Annotation> map) {
+ this.annotationMap = map;
+ }
+
+ /**
+ * Determine the preferred evaluation mode for this function
+ */
+
+ public void computeEvaluationMode() {
+ if (tailRecursive) {
+ // If this function contains tail calls, we evaluate it eagerly, because
+ // the caller needs to know whether a tail call was returned or not: if we
+ // return a Closure, the tail call escapes into the wild and can reappear anywhere...
+ evaluationMode = ExpressionTool.eagerEvaluationMode(getBody());
+ } else {
+ evaluationMode = ExpressionTool.lazyEvaluationMode(getBody());
+ }
+ }
+
+ /**
+ * Ask whether the function can be inlined
+ *
+ * @return true (yes), false (no), or null (don't know)
+ */
+
+ /*@Nullable*/
+ public Boolean isInlineable() {
+ if (inlineable < 0) {
+ return null;
+ } else {
+ return inlineable == 1;
+ }
+ }
+
+ /**
+ * Say whether this function can be inlined
+ *
+ * @param inlineable true or false
+ */
+
+ public void setInlineable(boolean inlineable) {
+ this.inlineable = (inlineable ? 1 : 0);
+ }
+
+ /**
+ * Set the definitions of the declared parameters for this function
+ *
+ * @param params an array of parameter definitions
+ */
+
+ public void setParameterDefinitions(UserFunctionParameter[] params) {
+ parameterDefinitions = params;
+ }
+
+ /**
+ * Get the definitions of the declared parameters for this function
+ *
+ * @return an array of parameter definitions
+ */
+
+ public UserFunctionParameter[] getParameterDefinitions() {
+ return parameterDefinitions;
+ }
+
+ /**
+ * Set the declared result type of the function
+ *
+ * @param resultType the declared return type
+ */
+
+ public void setResultType(SequenceType resultType) {
+ this.resultType = resultType;
+ }
+
+ /**
+ * Indicate whether the function contains a tail call
+ *
+ * @param tailCalls true if the function contains a tail call (on any function)
+ * @param recursiveTailCalls true if the function contains a tail call (on itself)
+ */
+
+ public void setTailRecursive(boolean tailCalls, boolean recursiveTailCalls) {
+ this.tailCalls = tailCalls;
+ tailRecursive = recursiveTailCalls;
+ }
+
+ /**
+ * Determine whether the function contains tail calls (on this or other functions)
+ *
+ * @return true if the function contains tail calls
+ */
+
+ public boolean containsTailCalls() {
+ return tailCalls;
+ }
+
+ /**
+ * Determine whether the function contains a tail call, calling itself
+ *
+ * @return true if the function contains a directly-recursive tail call
+ */
+
+ public boolean isTailRecursive() {
+ return tailRecursive;
+ }
+
+ /**
+ * Set whether this is an updating function (as defined in XQuery Update)
+ *
+ * @param isUpdating true if this is an updating function
+ */
+
+ public void setUpdating(boolean isUpdating) {
+ this.isUpdating = isUpdating;
+ }
+
+ /**
+ * Ask whether this is an updating function (as defined in XQuery Update)
+ *
+ * @return true if this is an updating function
+ */
+
+ public boolean isUpdating() {
+ return isUpdating;
+ }
+
+
+ /**
+ * Get the type of value returned by this function
+ *
+ * @param th the type hierarchy cache
+ * @return the declared result type, or the inferred result type
+ * if this is more precise
+ */
+
+ public SequenceType getResultType(TypeHierarchy th) {
+ if (resultType == SequenceType.ANY_SEQUENCE) {
+ // see if we can infer a more precise result type. We don't do this if the function contains
+ // calls on further functions, to prevent infinite regress.
+ if (!containsUserFunctionCalls(getBody())) {
+ resultType = SequenceType.makeSequenceType(
+ getBody().getItemType(th), getBody().getCardinality());
+ }
+ }
+ return resultType;
+ }
+
+ /**
+ * Get the declared result type
+ *
+ * @return the declared result type
+ */
+
+ public SequenceType getDeclaredResultType() {
+ return resultType;
+ }
+
+ /**
+ * Determine whether a given expression contains calls on user-defined functions
+ *
+ * @param exp the expression to be tested
+ * @return true if the expression contains calls to user functions.
+ */
+
+ private static boolean containsUserFunctionCalls(Expression exp) {
+ if (exp instanceof UserFunctionCall) {
+ return true;
+ }
+ Iterator i = exp.iterateSubExpressions();
+ while (i.hasNext()) {
+ Expression e = (Expression) i.next();
+ if (containsUserFunctionCalls(e)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the required types of an argument to this function
+ *
+ * @param n identifies the argument in question, starting at 0
+ * @return a SequenceType object, indicating the required type of the argument
+ */
+
+ public SequenceType getArgumentType(int n) {
+ return parameterDefinitions[n].getRequiredType();
+ }
+
+ /**
+ * Get the evaluation mode. The evaluation mode will be computed if this has not already been done
+ *
+ * @return the computed evaluation mode
+ */
+
+ public int getEvaluationMode() {
+ if (evaluationMode == ExpressionTool.UNDECIDED) {
+ computeEvaluationMode();
+ }
+ return evaluationMode;
+ }
+
+ /**
+ * Get the arity of this function
+ *
+ * @return the number of arguments
+ */
+
+ public int getNumberOfArguments() {
+ return parameterDefinitions.length;
+ }
+
+ /**
+ * Ask whether this function is a memo function
+ *
+ * @return false (overridden in a subclass)
+ */
+
+ public boolean isMemoFunction() {
+ return false;
+ }
+
+
+ /**
+ * Call this function to return a value.
+ *
+ *
+ * @param context This provides the run-time context for evaluating the function. It is the caller's
+ * responsibility to allocate a "clean" context for the function to use; the context that is provided
+ * will be overwritten by the function.
+ * @param actualArgs the arguments supplied to the function. These must have the correct
+ * types required by the function signature (it is the caller's responsibility to check this).
+ * It is acceptable to supply a {@link net.sf.saxon.value.Closure} to represent a value whose
+ * evaluation will be delayed until it is needed. The array must be the correct size to match
+ * the number of arguments: again, it is the caller's responsibility to check this.
+ * @return a Value representing the result of the function.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs while evaluating the function
+ */
+
+ public Sequence call(XPathContextMajor context, Sequence[] actualArgs)
+ throws XPathException {
+ if (evaluationMode == ExpressionTool.UNDECIDED) {
+ // should have been done at compile time
+ computeEvaluationMode();
+ }
+
+ // Otherwise evaluate the function
+
+ context.setStackFrame(getStackFrameMap(), actualArgs);
+ Sequence result;
+ try {
+ result = ExpressionTool.evaluate(getBody(), evaluationMode, context, 1);
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ throw err;
+ }
+
+ return result;
+ }
+
+ /**
+ * Call this function in "push" mode, writing the results to the current output destination.
+ *
+ * @param actualArgs the arguments supplied to the function. These must have the correct
+ * types required by the function signature (it is the caller's responsibility to check this).
+ * It is acceptable to supply a {@link net.sf.saxon.value.Closure} to represent a value whose
+ * evaluation will be delayed until it is needed. The array must be the correct size to match
+ * the number of arguments: again, it is the caller's responsibility to check this.
+ * @param context This provides the run-time context for evaluating the function. It is the caller's
+ * responsibility to allocate a "clean" context for the function to use; the context that is provided
+ * will be overwritten by the function.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs while evaluating the function
+ */
+
+ public void process(Sequence[] actualArgs, XPathContextMajor context)
+ throws XPathException {
+ context.setStackFrame(getStackFrameMap(), actualArgs);
+ getBody().process(context);
+ }
+
+ /**
+ * Call this function in "pull" mode, returning the results as a sequence of PullEvents.
+ *
+ * @param actualArgs the arguments supplied to the function. These must have the correct
+ * types required by the function signature (it is the caller's responsibility to check this).
+ * It is acceptable to supply a {@link net.sf.saxon.value.Closure} to represent a value whose
+ * evaluation will be delayed until it is needed. The array must be the correct size to match
+ * the number of arguments: again, it is the caller's responsibility to check this.
+ * @param context This provides the run-time context for evaluating the function. It is the caller's
+ * responsibility to allocate a "clean" context for the function to use; the context that is provided
+ * will be overwritten by the function.
+ * @return an iterator over the results of the function call
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs while evaluating the function
+ */
+
+ public EventIterator iterateEvents(Sequence[] actualArgs, XPathContextMajor context)
+ throws XPathException {
+ context.setStackFrame(getStackFrameMap(), actualArgs);
+ return getBody().iterateEvents(context);
+ }
+
+ /**
+ * Call this function. This method allows an XQuery function to be called directly from a Java
+ * application. It creates the environment needed to achieve this
+ *
+ * @param actualArgs the arguments supplied to the function. These must have the correct
+ * types required by the function signature (it is the caller's responsibility to check this).
+ * It is acceptable to supply a {@link net.sf.saxon.value.Closure} to represent a value whose
+ * evaluation will be delayed until it is needed. The array must be the correct size to match
+ * the number of arguments: again, it is the caller's responsibility to check this.
+ * @param controller This provides the run-time context for evaluating the function. A Controller
+ * may be obtained by calling {@link net.sf.saxon.query.XQueryExpression#newController}. This may
+ * be used for a series of calls on functions defined in the same module as the XQueryExpression.
+ * @return a Value representing the result of the function.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs while evaluating the function.
+ */
+
+ public Sequence call(Sequence[] actualArgs, Controller controller) throws XPathException {
+ return call(controller.newXPathContext(), actualArgs);
+ }
+
+ /**
+ * Call an updating function.
+ *
+ * @param actualArgs the arguments supplied to the function. These must have the correct
+ * types required by the function signature (it is the caller's responsibility to check this).
+ * It is acceptable to supply a {@link net.sf.saxon.value.Closure} to represent a value whose
+ * evaluation will be delayed until it is needed. The array must be the correct size to match
+ * the number of arguments: again, it is the caller's responsibility to check this.
+ * @param context the dynamic evaluation context
+ * @param pul the pending updates list, to which the function's update actions are to be added.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs while evaluating the function.
+ */
+
+ public void callUpdating(Sequence[] actualArgs, XPathContextMajor context, PendingUpdateList pul)
+ throws XPathException {
+ context.setStackFrame(getStackFrameMap(), actualArgs);
+ try {
+ getBody().evaluatePendingUpdates(context, pul);
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+
+ /**
+ * Get the type of construct. This will either be the fingerprint of a standard XSLT instruction name
+ * (values in {@link net.sf.saxon.om.StandardNames}: all less than 1024)
+ * or it will be a constant in class {@link net.sf.saxon.trace.Location}.
+ */
+
+ public int getConstructType() {
+ return Location.FUNCTION;
+ }
+
+}
+
+
diff --git a/sf/saxon/expr/instruct/UserFunctionParameter.java b/sf/saxon/expr/instruct/UserFunctionParameter.java
new file mode 100644
index 0000000..19463a0
--- /dev/null
+++ b/sf/saxon/expr/instruct/UserFunctionParameter.java
@@ -0,0 +1,179 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.expr.Binding;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+import java.io.Serializable;
+
+/**
+ * Run-time object representing a formal argument to a user-defined function
+ */
+public class UserFunctionParameter implements Binding, Serializable {
+
+ private SequenceType requiredType;
+ private StructuredQName variableQName;
+ private int slotNumber;
+ private int referenceCount = 999;
+ // The initial value is deliberately set to indicate "many" so that it will be assumed a parameter
+ // is referenced repeatedly until proved otherwise
+ private boolean isIndexed = false;
+
+ /**
+ * Create a UserFunctionParameter
+ */
+
+ public UserFunctionParameter(){}
+
+ /**
+ * Indicate whether the binding is local or global. A global binding is one that has a fixed
+ * value for the life of a query or transformation; any other binding is local.
+ * @return false (always)
+ */
+
+ public final boolean isGlobal() {
+ return false;
+ }
+
+ /**
+ * Test whether it is permitted to assign to the variable using the saxon:assign
+ * extension element. This will only be for an XSLT global variable where the extra
+ * attribute saxon:assignable="yes" is present.
+ * @return false (always)
+ */
+
+ public final boolean isAssignable() {
+ return false;
+ }
+
+ /**
+ * Set the slot number to be used by this parameter
+ * @param slot the slot number, that is, the position of the parameter value within the local stack frame
+ */
+
+ public void setSlotNumber(int slot) {
+ slotNumber = slot;
+ }
+
+ /**
+ * If this is a local variable held on the local stack frame, return the corresponding slot number.
+ * In other cases, return -1.
+ * @return the slot number, indicating the position of the parameter on the local stack frame
+ */
+
+ public int getLocalSlotNumber() {
+ return slotNumber;
+ }
+
+ /**
+ * Set the required type of this function parameter
+ * @param type the declared type of the parameter
+ */
+
+ public void setRequiredType(SequenceType type) {
+ requiredType = type;
+ }
+
+ /**
+ * Get the required type of this function parameter
+ * @return the declared type of the parameter
+ */
+
+ public SequenceType getRequiredType() {
+ return requiredType;
+ }
+
+ /**
+ * If the variable is bound to an integer, get the minimum and maximum possible values.
+ * Return null if unknown or not applicable
+ */
+ /*@Nullable*/ public IntegerValue[] getIntegerBoundsForVariable() {
+ return null;
+ }
+
+ /**
+ * Set the name of this parameter
+ * @param name the name of the parameter
+ */
+
+ public void setVariableQName(StructuredQName name) {
+ variableQName = name;
+ }
+
+ /**
+ * Get the name of this parameter
+ * @return the name of this parameter
+ */
+
+ public StructuredQName getVariableQName() {
+ return variableQName;
+ }
+
+ public void addReference(boolean isLoopingReference) {
+
+ }
+
+ /**
+ * Set the (nominal) number of references within the function body to this parameter, where a reference
+ * inside a loop is counted as multiple references
+ * @param count the nominal number of references
+ */
+
+ public void setReferenceCount(int count) {
+ referenceCount = count;
+ }
+
+ /**
+ * Get the (nominal) number of references within the function body to this parameter, where a reference
+ * inside a loop is counted as multiple references
+ * @return the nominal number of references
+ */
+
+ public int getReferenceCount() {
+ return referenceCount;
+ }
+
+ /**
+ * Indicate that this parameter requires (or does not require) support for indexing
+ * @param indexed true if support for indexing is required. This will be set if the parameter
+ * is used in a filter expression such as $param[@a = 17]
+ */
+
+ public void setIndexedVariable(boolean indexed) {
+ isIndexed = indexed;
+ }
+
+ /**
+ * Ask whether this parameter requires support for indexing
+ * @return true if support for indexing is required. This will be set if the parameter
+ * is used in a filter expression such as $param[@a = 17]
+ */
+
+ public boolean isIndexedVariable() {
+ return isIndexed;
+ }
+
+ /**
+ * Evaluate this function parameter
+ * @param context the XPath dynamic context
+ * @return the value of the parameter
+ * @throws XPathException if an error occurs
+ */
+
+ public Sequence evaluateVariable(XPathContext context) throws XPathException {
+ return context.evaluateLocalVariable(slotNumber);
+ }
+
+}
+
diff --git a/sf/saxon/expr/instruct/ValidatingInstruction.java b/sf/saxon/expr/instruct/ValidatingInstruction.java
new file mode 100644
index 0000000..fc8aca2
--- /dev/null
+++ b/sf/saxon/expr/instruct/ValidatingInstruction.java
@@ -0,0 +1,23 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.type.SchemaType;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * Interface implemented by instructions that have validation and type attributes
+ */
+public interface ValidatingInstruction extends SourceLocator {
+
+ /*@Nullable*/ SchemaType getSchemaType();
+
+ int getValidationAction();
+}
+
diff --git a/sf/saxon/expr/instruct/ValueOf.java b/sf/saxon/expr/instruct/ValueOf.java
new file mode 100644
index 0000000..c104508
--- /dev/null
+++ b/sf/saxon/expr/instruct/ValueOf.java
@@ -0,0 +1,327 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ValueOfCompiler;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.functions.StringFn;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* An xsl:value-of element in the stylesheet. <br>
+* The xsl:value-of element takes attributes:<ul>
+* <li>a mandatory attribute select="expression".
+* This must be a valid String expression</li>
+* <li>an optional disable-output-escaping attribute, value "yes" or "no"</li>
+* <li>an optional separator attribute. This is handled at compile-time: if the separator attribute
+* is present, the select expression passed in here will be a call to the string-join() function.</li>
+* </ul>
+*/
+
+public final class ValueOf extends SimpleNodeConstructor {
+
+ private int options;
+ private boolean isNumberingInstruction = false; // set to true if generated by xsl:number
+ private boolean noNodeIfEmpty;
+
+ /**
+ * Create a new ValueOf expression
+ * @param select the select expression
+ * @param disable true if disable-output-escaping is in force
+ * @param noNodeIfEmpty true if the instruction is to return () if the select expression is (),
+ * false if it is to return an empty text node
+ */
+
+ public ValueOf(Expression select, boolean disable, boolean noNodeIfEmpty) {
+ this.select = select;
+ options = (disable ? ReceiverOptions.DISABLE_ESCAPING : 0);
+ this.noNodeIfEmpty = noNodeIfEmpty;
+ adoptChildExpression(select);
+
+ // If value is fixed, test whether there are any special characters that might need to be
+ // escaped when the time comes for serialization
+ if (select instanceof StringLiteral) {
+ boolean special = false;
+ CharSequence val = ((StringLiteral)select).getStringValue();
+ for (int k=0; k<val.length(); k++) {
+ char c = val.charAt(k);
+ if ((int)c<33 || (int)c>126 ||
+ c=='<' || c=='>' || c=='&') {
+ special = true;
+ break;
+ }
+ }
+ if (!special) {
+ options |= ReceiverOptions.NO_SPECIAL_CHARS;
+ }
+ }
+ }
+
+ /**
+ * Indicate that this is really an xsl:nunber instruction
+ */
+
+ public void setIsNumberingInstruction() {
+ isNumberingInstruction = true;
+ }
+
+ /**
+ * Determine whether this is really an xsl:number instruction
+ * @return true if this derives from xsl:number
+ */
+
+ public boolean isNumberingInstruction() {
+ return isNumberingInstruction;
+ }
+
+ public boolean isNoNodeIfEmpty(){
+ return noNodeIfEmpty;
+ }
+
+ /**
+ * Get the name of this instruction for diagnostic and tracing purposes
+ * @return the namecode of the instruction name
+ */
+
+ public int getInstructionNameCode() {
+ if (isNumberingInstruction) {
+ return StandardNames.XSL_NUMBER;
+ } else if (select instanceof StringLiteral) {
+ return StandardNames.XSL_TEXT;
+ } else {
+ return StandardNames.XSL_VALUE_OF;
+ }
+ }
+
+ /**
+ * Test for any special options such as disable-output-escaping
+ * @return any special options
+ */
+
+ public int getOptions() {
+ return options;
+ }
+
+ /**
+ * Test whether disable-output-escaping was requested
+ * @return true if disable-output-escaping was requested
+ */
+
+ public boolean isDisableOutputEscaping() {
+ return (options & ReceiverOptions.DISABLE_ESCAPING) != 0;
+ }
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return NodeKindTest.TEXT;
+ }
+
+ public int computeCardinality() {
+ if (noNodeIfEmpty) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ } else {
+ return StaticProperty.EXACTLY_ONE;
+ }
+ }
+
+ public void localTypeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) {
+ //
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ValueOf exp = new ValueOf(select.copy(), (options&ReceiverOptions.DISABLE_ESCAPING) != 0 , noNodeIfEmpty);
+ if (isNumberingInstruction) {
+ exp.setIsNumberingInstruction();
+ }
+ return exp;
+ }
+
+ /**
+ * Check statically that the results of the expression are capable of constructing the content
+ * of a given schema type.
+ *
+ * @param parentType The schema type
+ * @param env the static context
+ * @param whole true if this expression is to account for the whole value of the type
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression doesn't match the required content type
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ // if the expression is a constant value, check that it is valid for the type
+ if (select instanceof Literal) {
+ GroundedValue selectValue = ((Literal)select).getValue();
+ SimpleType stype = null;
+ if (parentType instanceof SimpleType && whole) {
+ stype = (SimpleType)parentType;
+ } else if (parentType instanceof ComplexType && ((ComplexType)parentType).isSimpleContent()) {
+ stype = ((ComplexType)parentType).getSimpleContentType();
+ }
+ if (whole && stype != null && !stype.isNamespaceSensitive()) {
+ // Can't validate namespace-sensitive content statically
+ ValidationFailure err = stype.validateContent(
+ selectValue.getStringValue(), null, env.getConfiguration().getConversionRules());
+ if (err != null) {
+ err.setLocator(this);
+ err.setErrorCode(isXSLT() ? "XTTE1540" : "XQDY0027");
+ throw err.makeException();
+ }
+ return;
+ }
+ if (parentType instanceof ComplexType &&
+ !((ComplexType)parentType).isSimpleContent() &&
+ !((ComplexType)parentType).isMixedContent() &&
+ !Whitespace.isWhite(selectValue.getStringValue())) {
+ XPathException err = new XPathException("The containing element must be of type " + parentType.getDescription() +
+ ", which does not allow text content " +
+ Err.wrap(selectValue.getStringValue()));
+ err.setLocator(this);
+ err.setIsTypeError(true);
+ throw err;
+ }
+ } else {
+ // check that the type allows text nodes. If not, this is a warning condition, since the text
+ // node might turn out to be whitespace
+ if (parentType instanceof ComplexType &&
+ !((ComplexType)parentType).isSimpleContent() &&
+ !((ComplexType)parentType).isMixedContent()) {
+ env.issueWarning("The containing element must be of type " + parentType.getDescription() +
+ ", which does not allow text content other than whitespace", this);
+ }
+ }
+ }
+
+ /**
+ * Convert this value-of instruction to an expression that delivers the string-value of the resulting
+ * text node as an untyped atomic value.
+ * @return the converted expression
+ */
+
+ public Expression convertToCastAsString() {
+ if (noNodeIfEmpty || !Cardinality.allowsZero(select.getCardinality())) {
+ return new CastExpression(select, BuiltInAtomicType.UNTYPED_ATOMIC, true);
+ } else {
+ // must return zero-length string rather than () if empty
+ StringFn sf = (StringFn) SystemFunctionCall.makeSystemFunction("string", new Expression[]{select});
+ return new CastExpression(sf, BuiltInAtomicType.UNTYPED_ATOMIC, false);
+ }
+ }
+
+ /**
+ * Process this instruction
+ * @param context the dynamic context of the transformation
+ * @return a TailCall to be executed by the caller, always null for this instruction
+ */
+
+ /*@Nullable*/ public TailCall processLeavingTail(XPathContext context) throws XPathException {
+ if (noNodeIfEmpty) {
+ StringValue value = (StringValue)select.evaluateItem(context);
+ if (value != null) {
+ processValue(value.getStringValueCS(), context);
+ }
+ return null;
+ } else {
+ return super.processLeavingTail(context);
+ }
+ }
+
+ /**
+ * Process the value of the node, to create the new node.
+ * @param value the string value of the new node
+ * @param context the dynamic evaluation context
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ public void processValue(CharSequence value, XPathContext context) throws XPathException {
+ SequenceReceiver out = context.getReceiver();
+ out.characters(value, locationId, options);
+ }
+
+ /**
+ * Evaluate this expression, returning the resulting text node to the caller
+ * @param context the dynamic evaluation context
+ * @return the parentless text node that results from evaluating this instruction, or null to
+ * represent an empty sequence
+ * @throws XPathException
+ */
+
+ public NodeInfo evaluateItem(XPathContext context) throws XPathException {
+ try {
+ CharSequence val;
+ Item item = select.evaluateItem(context);
+ if (item == null) {
+ if (noNodeIfEmpty) {
+ return null;
+ } else {
+ val = "";
+ }
+ } else {
+ val = item.getStringValueCS();
+ }
+ Controller controller = context.getController();
+ assert controller != null;
+ Orphan o = new Orphan(controller.getConfiguration());
+ o.setNodeKind(Type.TEXT);
+ o.setStringValue(val);
+ return o;
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ValueOf expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ValueOfCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("valueOf");
+ getContentExpression().explain(out);
+ out.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/WithParam.java b/sf/saxon/expr/instruct/WithParam.java
new file mode 100644
index 0000000..a0245f2
--- /dev/null
+++ b/sf/saxon/expr/instruct/WithParam.java
@@ -0,0 +1,241 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.instruct;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An object derived from a xsl:with-param element in the stylesheet. <br>
+ */
+
+public class WithParam extends GeneralVariable {
+
+ public static WithParam[] EMPTY_ARRAY = new WithParam[0];
+
+ int parameterId;
+ boolean typeChecked = false;
+
+ public WithParam() {
+ }
+
+ /**
+ * Allocate a number which is essentially an alias for the parameter name,
+ * unique within a stylesheet
+ *
+ * @param id the parameter id
+ */
+
+ public void setParameterId(int id) {
+ parameterId = id;
+ }
+
+ /**
+ * Say whether this parameter will have been typechecked by the caller to ensure it satisfies
+ * the required type, in which case the callee need not do a dynamic type check
+ *
+ * @param checked true if the caller has done static type checking against the required type
+ */
+
+ public void setTypeChecked(boolean checked) {
+ typeChecked = checked;
+ }
+
+ /**
+ * Get the parameter id, which is essentially an alias for the parameter name,
+ * unique within a stylesheet
+ *
+ * @return the parameter id
+ */
+
+ public int getParameterId() {
+ return parameterId;
+ }
+
+
+ public int getInstructionNameCode() {
+ return StandardNames.XSL_WITH_PARAM;
+ }
+
+ /**
+ * Static method to simplify a set of with-param elements
+ * @param params the set of parameters to be simplified
+ * @param visitor the expression visitor
+ * @throws XPathException if a static error is found
+ */
+
+ public static void simplify(WithParam[] params, ExpressionVisitor visitor) throws XPathException {
+ if (params != null) {
+ for (WithParam param : params) {
+ Expression select = param.getSelectExpression();
+ if (select != null) {
+ param.setSelectExpression(visitor.simplify(select));
+ }
+ }
+ }
+ }
+
+ /**
+ * Static method to typecheck a set of with-param elements
+ * @param params the set of parameters to be checked
+ * @param visitor the expression visitor
+ * @param contextItemType static information about the context item type and existence
+ * @throws XPathException if a static error is found
+ */
+
+
+ public static void typeCheck(WithParam[] params, ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (params != null) {
+ for (WithParam param : params) {
+ Expression select = param.getSelectExpression();
+ if (select != null) {
+ param.setSelectExpression(visitor.typeCheck(select, contextItemType));
+ }
+ }
+ }
+ }
+
+ /**
+ * Static method to optimize a set of with-param elements
+ * @param params the set of parameters to be optimized
+ * @param visitor the expression visitor
+ * @param contextItemType static information about the context item type and existence
+ * @throws XPathException if a static error is found
+ */
+
+ public static void optimize(ExpressionVisitor visitor, WithParam[] params, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (params != null) {
+ for (WithParam param : params) {
+ param.optimize(visitor, contextItemType);
+ }
+ }
+ }
+
+ /**
+ * Static method to do expression promotion on a set of with-param elements
+ * @param parent the parent instruction (for example apply-templates or call-template)
+ * @param params the set of parameters to be be investigated for promotion
+ * @param offer the promotion offer to be passed to subexpressions
+ * @throws XPathException if a static error is found
+ */
+
+ public static void promoteParams(Expression parent, WithParam[] params, PromotionOffer offer) throws XPathException {
+ if (params != null) {
+ for (WithParam param : params) {
+ Expression select = param.getSelectExpression();
+ if (select != null) {
+ param.setSelectExpression(select.promote(offer, parent));
+ }
+ }
+ }
+ }
+
+ /**
+ * Static method to copy a set of parameters
+ *
+ * @param params the parameters to be copied
+ * @return the resulting copy
+ */
+
+ public static WithParam[] copy(WithParam[] params) {
+ if (params == null) {
+ return null;
+ }
+ WithParam[] result = new WithParam[params.length];
+ for (int i = 0; i < params.length; i++) {
+ result[i] = new WithParam();
+ result[i].parameterId = params[i].parameterId;
+ result[i].slotNumber = params[i].slotNumber;
+ result[i].typeChecked = params[i].typeChecked;
+ result[i].select = params[i].select.copy();
+ result[i].requiredType = params[i].requiredType;
+ result[i].variableQName = params[i].variableQName;
+ }
+ return result;
+ }
+
+ /**
+ * Static method to gather the XPath expressions used in an array of WithParam parameters (add them to the supplied list)
+ * @param params the set of with-param elements to be searched
+ * @param list the list to which the subexpressions will be added
+ */
+
+ public static void gatherXPathExpressions(WithParam[] params, List<Expression> list) {
+ if (params != null) {
+ for (WithParam param : params) {
+ for (Iterator<Expression> it = param.iterateSubExpressions(); it.hasNext(); ) {
+ list.add(it.next());
+ }
+ }
+ }
+ }
+
+ /**
+ * Static method to explain a set of parameters
+ * @param params the set of parameters to be explained
+ * @param out the destination for the explanation
+ */
+
+ public static void explainParameters(WithParam[] params, ExpressionPresenter out) {
+ if (params != null) {
+ for (WithParam param : params) {
+ out.startElement("withParam");
+ out.emitAttribute("name", param.getVariableQName().getDisplayName());
+ param.getSelectExpression().explain(out);
+ out.endElement();
+ }
+ }
+ }
+
+ /**
+ * Static method to replace a subexpression within any parameter within which it is found
+ * @param params the set of parameters to be examined
+ * @param original the subexpression to be replaced
+ * @param replacement the replacement expression
+ * @return true if a replacement was made
+ */
+
+ public static boolean replaceXPathExpression(WithParam[] params, Expression original, Expression replacement) {
+ boolean found = false;
+ if (params != null) {
+ for (WithParam param : params) {
+ boolean f = param.replaceSubExpression(original, replacement);
+ found |= f;
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Evaluate the variable (method exists only to satisfy the interface)
+ */
+
+ public Sequence evaluateVariable(XPathContext context) throws XPathException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Ask whether static type checking has been done
+ *
+ * @return true if the caller has done static type checking against the type required by the callee
+ */
+
+ public boolean isTypeChecked() {
+ return typeChecked;
+ }
+}
+
diff --git a/sf/saxon/expr/instruct/package.html b/sf/saxon/expr/instruct/package.html
new file mode 100644
index 0000000..66e2aa0
--- /dev/null
+++ b/sf/saxon/expr/instruct/package.html
@@ -0,0 +1,46 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview: net.sf.saxon.expr.instruct</title>
+
+</head>
+
+
+<body>
+
+<p>This package provides classes for the compiled representation of the various elements
+and other instructions found in an XSLT stylesheet. The same constructs are also used for
+evaluating similar constructs in XQuery, in particular, expressions that construct new nodes.</p>
+
+<p>Instances of these classes are constructed when the stylesheet or query is compiled. In the
+case of XSLT, the objects
+representing the compile-time stylesheet (in package net.sf.saxon.style) can then be
+discarded and garbage-collected.</p>
+
+<p>The most important class is <code>Instruction</code>, which represents an XSLT Instruction. In most cases
+these instructions have a one-to-one relationship with instructions in the original source XSLT
+stylesheet, and the names of the subclasses (for example ApplyImports, ApplyTemplates, Choose)
+reflect this.</p>
+
+<p>In XSLT 1.0, XSLT instructions and XPath expressions were quite distinct, and were evaluated in different
+ways: XSLT instructions in "push" mode (they were described as "writing to the result tree"), and XPath expressions
+in "pull" mode (reading from the source tree). This distinction is no longer present in the XSLT 2.0 processing
+model, and the boundary between the <code>Instruction</code> and <code>Expression</code> classes is therefore
+a rather fuzzy one. Both instructions and expressions can now be evaluated in either push or pull mode.
+Flow-of-control constructs such as conditional expressions and FOR expressions are evaluated in either mode
+depending on their parent expression.</p>
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+28 November 2011</i></p>
+</body>
+</html>
diff --git a/sf/saxon/expr/number/AbstractNumberer.java b/sf/saxon/expr/number/AbstractNumberer.java
new file mode 100644
index 0000000..166ec17
--- /dev/null
+++ b/sf/saxon/expr/number/AbstractNumberer.java
@@ -0,0 +1,723 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.number;
+
+import net.sf.saxon.lib.Numberer;
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.io.Serializable;
+
+/**
+ * Class AbstractNumberer is a base implementation of Numberer that provides language-independent
+ * default numbering
+ * This supports the xsl:number element.
+ * Methods and data are declared as protected, and static is avoided, to allow easy subclassing.
+ *
+ * @author Michael H. Kay
+ */
+
+public abstract class AbstractNumberer implements Numberer, Serializable {
+
+ private String country;
+
+ public static final int UPPER_CASE = 0;
+ public static final int LOWER_CASE = 1;
+ public static final int TITLE_CASE = 2;
+
+
+ /**
+ * Set the country used by this numberer (currently used only for names of timezones)
+ */
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+
+ /**
+ * Get the country used by this numberer.
+ *
+ * @return the country used by this numberer, or null if no country has been set
+ */
+
+ public String getCountry() {
+ return country;
+ }
+
+ /**
+ * Format a number into a string. This method is provided for backwards compatibility. It merely
+ * calls the other format method after constructing a RegularGroupFormatter. The method is final;
+ * localization subclasses should implement the method
+ * {@link net.sf.saxon.lib.Numberer#format(long, UnicodeString, NumericGroupFormatter, String, String)} rather than this method.
+ *
+ * @param number The number to be formatted
+ * @param picture The format token. This is a single component of the format attribute
+ * of xsl:number, e.g. "1", "01", "i", or "a"
+ * @param groupSize number of digits per group (0 implies no grouping)
+ * @param groupSeparator string to appear between groups of digits
+ * @param letterValue The letter-value specified to xsl:number: "alphabetic" or
+ * "traditional". Can also be an empty string or null.
+ * @param ordinal The value of the ordinal attribute specified to xsl:number
+ * The value "yes" indicates that ordinal numbers should be used; "" or null indicates
+ * that cardinal numbers
+ * @return the formatted number. Note that no errors are reported; if the request
+ * is invalid, the number is formatted as if the string() function were used.
+ */
+
+ public final String format(long number,
+ UnicodeString picture,
+ int groupSize,
+ String groupSeparator,
+ String letterValue,
+ String ordinal) {
+
+ return format(number, picture,
+ new RegularGroupFormatter(groupSize, groupSeparator, UnicodeString.EMPTY_STRING),
+ letterValue, ordinal);
+ }
+
+ /**
+ * Format a number into a string
+ *
+ *
+ * @param number The number to be formatted
+ * @param picture The format token. This is a single component of the format attribute
+ * of xsl:number, e.g. "1", "01", "i", or "a"
+ * @param numGroupFormatter object contains separators to appear between groups of digits
+ * @param letterValue The letter-value specified to xsl:number: "alphabetic" or
+ * "traditional". Can also be an empty string or null.
+ * @param ordinal The value of the ordinal attribute specified to xsl:number
+ * The value "yes" indicates that ordinal numbers should be used; "" or null indicates
+ * that cardinal numbers
+ * @return the formatted number. Note that no errors are reported; if the request
+ * is invalid, the number is formatted as if the string() function were used.
+ */
+ public String format(long number,
+ /*@Nullable*/ UnicodeString picture,
+ NumericGroupFormatter numGroupFormatter,
+ String letterValue,
+ String ordinal) {
+
+
+ if (number < 0) {
+ return "" + number;
+ }
+ if (picture == null || picture.length() == 0) {
+ return "" + number;
+ }
+
+ int pictureLength = picture.length();
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+ int formchar = picture.charAt(0);
+
+ switch (formchar) {
+
+ case '0':
+ case '1':
+ sb.append(toRadical(number, westernDigits, pictureLength, numGroupFormatter));
+ if (ordinal != null && ordinal.length() > 0) {
+ sb.append(ordinalSuffix(ordinal, number));
+ }
+ break;
+
+ case 'A':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, latinUpper);
+
+ case 'a':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, latinLower);
+
+ case 'w':
+ case 'W':
+ int wordCase;
+ if (picture.length() == 1) {
+ if (formchar == 'W') {
+ wordCase = UPPER_CASE;
+ } else /*if (formchar == 'w')*/ {
+ wordCase = LOWER_CASE;
+ }
+ } else {
+ // includes cases like "ww" or "Wz". The action here is conformant, but it's not clear what's best
+ wordCase = TITLE_CASE;
+ }
+ if (ordinal != null && ordinal.length() > 0) {
+ return toOrdinalWords(ordinal, number, wordCase);
+ } else {
+ return toWords(number, wordCase);
+ }
+
+ case 'i':
+ if (number == 0) {
+ return "0";
+ }
+ if (letterValue == null || letterValue.length() == 0 ||
+ letterValue.equals("traditional")) {
+ return toRoman(number);
+ } else {
+ alphaDefault(number, 'i', sb);
+ }
+ break;
+
+ case 'I':
+ if (number == 0) {
+ return "0";
+ }
+ if (letterValue == null || letterValue.length() == 0 ||
+ letterValue.equals("traditional")) {
+ return toRoman(number).toUpperCase();
+ } else {
+ alphaDefault(number, 'I', sb);
+ }
+ break;
+
+ case '\u2460':
+ // circled digits
+ if (number == 0 || number > 20) {
+ return "" + number;
+ }
+ return "" + (char) (0x2460 + number - 1);
+
+ case '\u2474':
+ // parenthesized digits
+ if (number == 0 || number > 20) {
+ return "" + number;
+ }
+ return "" + (char) (0x2474 + number - 1);
+
+ case '\u2488':
+ // digit full stop
+ if (number == 0 || number > 20) {
+ return "" + number;
+ }
+ return "" + (char) (0x2488 + number - 1);
+
+ case '\u0391':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, greekUpper);
+
+ case '\u03b1':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, greekLower);
+
+ case '\u0410':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, cyrillicUpper);
+
+ case '\u0430':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, cyrillicLower);
+
+ case '\u05d0':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, hebrew);
+
+ case '\u3042':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, hiraganaA);
+
+ case '\u30a2':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, katakanaA);
+
+ case '\u3044':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, hiraganaI);
+
+ case '\u30a4':
+ if (number == 0) {
+ return "0";
+ }
+ return toAlphaSequence(number, katakanaI);
+
+ case '\u4e00':
+ return toJapanese(number);
+
+ default:
+
+ int digitValue = Alphanumeric.getDigitValue(formchar);
+ if (digitValue >= 0) {
+
+ int zero = formchar - digitValue;
+ int[] digits = new int[10];
+ for (int z = 0; z <= 9; z++) {
+ digits[z] = zero + z;
+ }
+
+ return toRadical(number, digits, pictureLength, numGroupFormatter);
+
+ } else {
+ if (formchar < '\u1100' && Character.isLetter((char) formchar) && number > 0) {
+ alphaDefault(number, (char) formchar, sb);
+ } else {
+ // fallback to western numbering
+ sb.append(toRadical(number, westernDigits, pictureLength, numGroupFormatter));
+ if (ordinal != null && ordinal.length() > 0) {
+ sb.append(ordinalSuffix(ordinal, number));
+ }
+ //return toRadical(number, westernDigits, pictureLength, numGroupFormatter);
+ }
+ break;
+
+ }
+ }
+
+ return sb.toString();
+ }
+
+
+ /**
+ * Construct the ordinal suffix for a number, for example "st", "nd", "rd". The default
+ * (language-neutral) implementation returns a zero-length string
+ *
+ * @param ordinalParam the value of the ordinal attribute (used in non-English
+ * language implementations)
+ * @param number the number being formatted
+ * @return the ordinal suffix to be appended to the formatted number
+ */
+
+ protected String ordinalSuffix(String ordinalParam, long number) {
+ return "";
+ }
+
+ protected static final int[] westernDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+ protected static final String latinUpper =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ protected static final String latinLower =
+ "abcdefghijklmnopqrstuvwxyz";
+
+ protected static final String greekUpper =
+ "\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a" +
+ "\u039b\u039c\u039d\u039e\u039f\u03a0\u03a1\u03a2\u03a3\u03a4" +
+ "\u03a5\u03a6\u03a7\u03a8\u03a9";
+
+ protected static final String greekLower =
+ "\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba" +
+ "\u03bb\u03bc\u03bd\u03be\u03bf\u03c0\u03c1\u03c2\u03c3\u03c4" +
+ "\u03c5\u03c6\u03c7\u03c8\u03c9";
+
+ // Cyrillic information from Dmitry Kirsanov [dmitry at kirsanov.com]
+ // (based on his personal knowledge of Russian texts, not any authoritative source)
+
+ protected static final String cyrillicUpper =
+ "\u0410\u0411\u0412\u0413\u0414\u0415\u0416\u0417\u0418" +
+ "\u041a\u041b\u041c\u041d\u041e\u041f\u0420\u0421\u0421\u0423" +
+ "\u0424\u0425\u0426\u0427\u0428\u0429\u042b\u042d\u042e\u042f";
+
+ protected static final String cyrillicLower =
+ "\u0430\u0431\u0432\u0433\u0434\u0435\u0436\u0437\u0438" +
+ "\u043a\u043b\u043c\u043d\u043e\u043f\u0440\u0441\u0441\u0443" +
+ "\u0444\u0445\u0446\u0447\u0448\u0449\u044b\u044d\u044e\u044f";
+
+ protected static final String hebrew =
+ "\u05d0\u05d1\u05d2\u05d3\u05d4\u05d5\u05d6\u05d7\u05d8\u05d9\u05db\u05dc" +
+ "\u05de\u05e0\u05e1\u05e2\u05e4\u05e6\u05e7\u05e8\u05e9\u05ea";
+
+
+ // The following Japanese sequences were supplied by
+ // MURAKAMI Shinyu [murakami at nadita.com]
+
+ protected static final String hiraganaA =
+ "\u3042\u3044\u3046\u3048\u304a\u304b\u304d\u304f\u3051\u3053" +
+ "\u3055\u3057\u3059\u305b\u305d\u305f\u3061\u3064\u3066\u3068" +
+ "\u306a\u306b\u306c\u306d\u306e\u306f\u3072\u3075\u3078\u307b" +
+ "\u307e\u307f\u3080\u3081\u3082\u3084\u3086\u3088\u3089\u308a" +
+ "\u308b\u308c\u308d\u308f\u3092\u3093";
+
+ protected static final String katakanaA =
+
+ "\u30a2\u30a4\u30a6\u30a8\u30aa\u30ab\u30ad\u30af\u30b1\u30b3" +
+ "\u30b5\u30b7\u30b9\u30bb\u30bd\u30bf\u30c1\u30c4\u30c6\u30c8" +
+ "\u30ca\u30cb\u30cc\u30cd\u30ce\u30cf\u30d2\u30d5\u30d8\u30db" +
+ "\u30de\u30df\u30e0\u30e1\u30e2\u30e4\u30e6\u30e8\u30e9\u30ea" +
+ "\u30eb\u30ec\u30ed\u30ef\u30f2\u30f3";
+
+ protected static final String hiraganaI =
+
+ "\u3044\u308d\u306f\u306b\u307b\u3078\u3068\u3061\u308a\u306c" +
+ "\u308b\u3092\u308f\u304b\u3088\u305f\u308c\u305d\u3064\u306d" +
+ "\u306a\u3089\u3080\u3046\u3090\u306e\u304a\u304f\u3084\u307e" +
+ "\u3051\u3075\u3053\u3048\u3066\u3042\u3055\u304d\u3086\u3081" +
+ "\u307f\u3057\u3091\u3072\u3082\u305b\u3059";
+
+ protected static final String katakanaI =
+
+ "\u30a4\u30ed\u30cf\u30cb\u30db\u30d8\u30c8\u30c1\u30ea\u30cc" +
+ "\u30eb\u30f2\u30ef\u30ab\u30e8\u30bf\u30ec\u30bd\u30c4\u30cd" +
+ "\u30ca\u30e9\u30e0\u30a6\u30f0\u30ce\u30aa\u30af\u30e4\u30de" +
+ "\u30b1\u30d5\u30b3\u30a8\u30c6\u30a2\u30b5\u30ad\u30e6\u30e1" +
+ "\u30df\u30b7\u30f1\u30d2\u30e2\u30bb\u30b9";
+
+
+
+
+
+ /**
+ * Default processing with an alphabetic format token: use the contiguous
+ * range of Unicode letters starting with that token.
+ *
+ * @param number the number to be formatted
+ * @param formchar the format character, for example 'A' for the numbering sequence A,B,C
+ * @param sb buffer to hold the result of the formatting
+ */
+
+ protected void alphaDefault(long number, char formchar, FastStringBuffer sb) {
+ int min = (int) formchar;
+ int max = (int) formchar;
+ // use the contiguous range of letters starting with the specified one
+ while (Character.isLetterOrDigit((char) (max + 1))) {
+ max++;
+ }
+ sb.append(toAlpha(number, min, max));
+ }
+
+ /**
+ * Format the number as an alphabetic label using the alphabet consisting
+ * of consecutive Unicode characters from min to max
+ *
+ * @param number the number to be formatted
+ * @param min the start of the Unicode codepoint range
+ * @param max the end of the Unicode codepoint range
+ * @return the formatted number
+ */
+
+ protected String toAlpha(long number, int min, int max) {
+ if (number <= 0) {
+ return "" + number;
+ }
+ int range = max - min + 1;
+ char last = (char) (((number - 1) % range) + min);
+ if (number > range) {
+ return toAlpha((number - 1) / range, min, max) + last;
+ } else {
+ return "" + last;
+ }
+ }
+
+ /**
+ * Convert the number into an alphabetic label using a given alphabet.
+ * For example, if the alphabet is "xyz" the sequence is x, y, z, xx, xy, xz, ....
+ *
+ * @param number the number to be formatted
+ * @param alphabet a string containing the characters to be used, for example "abc...xyz"
+ * @return the formatted number
+ */
+
+ protected String toAlphaSequence(long number, String alphabet) {
+ if (number <= 0) {
+ return "" + number;
+ }
+ int range = alphabet.length();
+ char last = alphabet.charAt((int) ((number - 1) % range));
+ if (number > range) {
+ return toAlphaSequence((number - 1) / range, alphabet) + last;
+ } else {
+ return "" + last;
+ }
+ }
+
+ /**
+ * Convert the number into a decimal or other representation using the given set of
+ * digits.
+ * For example, if the digits are "01" the sequence is 1, 10, 11, 100, 101, 110, 111, ...
+ * More commonly, the digits will be "0123456789", giving the usual decimal numbering.
+ *
+ * @param number the number to be formatted
+ * @param digits the codepoints to be used for the digits
+ * @param pictureLength the length of the picture that is significant: for example "3" if the
+ * picture is "001"
+ * @param numGroupFormatter an object that encapsulates the rules for inserting grouping separators
+ * @return the formatted number
+ */
+
+ private String toRadical(long number, int[] digits, int pictureLength,
+ NumericGroupFormatter numGroupFormatter) {
+
+ FastStringBuffer temp = new FastStringBuffer(FastStringBuffer.TINY);
+ int base = digits.length;
+ FastStringBuffer s = new FastStringBuffer(FastStringBuffer.TINY);
+ long n = number;
+ int count = 0;
+ while (n > 0) {
+ int digit = digits[(int) (n % base)];
+ s.prependWideChar(digit);
+ count++;
+ n = n / base;
+ }
+
+ for (int i = 0; i < (pictureLength - count); i++) {
+ temp.appendWideChar(digits[0]);
+ }
+ temp.append(s);
+
+ if (numGroupFormatter == null) {
+ return temp.toString();
+ }
+
+ return numGroupFormatter.format(temp);
+ }
+
+ /**
+ * Generate a Roman numeral (in lower case)
+ *
+ * @param n the number to be formatted
+ * @return the Roman numeral representation of the number in lower case
+ */
+
+ public static String toRoman(long n) {
+ if (n <= 0 || n > 9999) {
+ return "" + n;
+ }
+ return romanThousands[(int) n / 1000] +
+ romanHundreds[((int) n / 100) % 10] +
+ romanTens[((int) n / 10) % 10] +
+ romanUnits[(int) n % 10];
+ }
+
+ // Roman numbers beyond 4000 use overlining and other conventions which we won't
+ // attempt to reproduce. We'll go high enough to handle present-day Gregorian years.
+
+ private static String[] romanThousands =
+ {"", "m", "mm", "mmm", "mmmm", "mmmmm", "mmmmmm", "mmmmmmm", "mmmmmmmm", "mmmmmmmmm"};
+ private static String[] romanHundreds =
+ {"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"};
+ private static String[] romanTens =
+ {"", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"};
+ private static String[] romanUnits =
+ {"", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"};
+
+
+ /**
+ * Format the number in Japanese.
+ * @param number the number to be formatted: formatted in Western decimal style unless in the range 1 to 9999
+ * @return the Japanese Kanji representation of the number if in the range 1-9999
+ */
+
+ public String toJapanese(long number) {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.TINY);
+ if (number == 0) {
+ fsb.appendWideChar(0x3007);
+ } else if (number <= 9999) {
+ toJapanese((int)number, fsb, false);
+ } else {
+ fsb.append("" + number);
+ }
+ return fsb.toString();
+ }
+
+ /**
+ * Format the number in Japanese.
+ * @param nr the number to be formatted: must be in the range 1 to 9999 (or 0 on a recursive call)
+ * @param fsb buffer to receive the formatted number as the Japanese Kanji representation of the number in lower case
+ * @param isInitial true except on a recursive call
+ */
+
+ private static void toJapanese(int nr, FastStringBuffer fsb, boolean isInitial) {
+ if (nr == 0) {
+ // no action (not used at top level)
+ } else if (nr <= 9) {
+ if (!(nr == 1 && isInitial)) {
+ fsb.appendWideChar(kanjiDigits[nr]);
+ }
+ } else if (nr == 10) {
+ fsb.appendWideChar(0x5341);
+ } else if (nr <= 99) {
+ toJapanese(nr / 10, fsb, true);
+ fsb.appendWideChar(0x5341);
+ toJapanese(nr % 10, fsb, false);
+ } else if (nr <= 999) {
+ toJapanese(nr / 100, fsb, true);
+ fsb.appendWideChar(0x767e);
+ toJapanese(nr % 100, fsb, false);
+ } else if (nr <= 9999) {
+ toJapanese(nr / 1000, fsb, true);
+ fsb.appendWideChar(0x5343);
+ toJapanese(nr % 1000, fsb, false);
+ }
+
+ }
+
+ private static final int[] kanjiDigits =
+ {0x3007, 0x4e00, 0x4e8c, 0x4e09, 0x56db, 0x4e94, 0x516d, 0x4e03, 0x516b, 0x4e5d};
+
+ /**
+ * Show the number as words in title case. (We choose title case because
+ * the result can then be converted algorithmically to lower case or upper case).
+ *
+ * @param number the number to be formatted
+ * @return the number formatted as English words
+ */
+
+ public abstract String toWords(long number);
+
+ /**
+ * Format a number as English words with specified case options
+ *
+ * @param number the number to be formatted
+ * @param wordCase the required case for example {@link #UPPER_CASE},
+ * {@link #LOWER_CASE}, {@link #TITLE_CASE}
+ * @return the formatted number
+ */
+
+ public String toWords(long number, int wordCase) {
+ String s;
+ if (number == 0) {
+ s = "Zero";
+ } else {
+ s = toWords(number);
+ }
+ if (wordCase == UPPER_CASE) {
+ return s.toUpperCase();
+ } else if (wordCase == LOWER_CASE) {
+ return s.toLowerCase();
+ } else {
+ return s;
+ }
+ }
+
+ /**
+ * Show an ordinal number as English words in a requested case (for example, Twentyfirst)
+ *
+ * @param ordinalParam the value of the "ordinal" attribute as supplied by the user
+ * @param number the number to be formatted
+ * @param wordCase the required case for example {@link #UPPER_CASE},
+ * {@link #LOWER_CASE}, {@link #TITLE_CASE}
+ * @return the formatted number
+ */
+
+ public abstract String toOrdinalWords(String ordinalParam, long number, int wordCase);
+
+ /**
+ * Get a month name or abbreviation
+ *
+ * @param month The month number (1=January, 12=December)
+ * @param minWidth The minimum number of characters
+ * @param maxWidth The maximum number of characters
+ */
+
+ public abstract String monthName(int month, int minWidth, int maxWidth);
+
+ /**
+ * Get a day name or abbreviation
+ *
+ * @param day The day of the week (1=Monday, 7=Sunday)
+ * @param minWidth The minimum number of characters
+ * @param maxWidth The maximum number of characters
+ */
+
+ public abstract String dayName(int day, int minWidth, int maxWidth);
+
+ /**
+ * Get an am/pm indicator. Default implementation works for English, on the basis that some
+ * other languages might like to copy this (most non-English countries don't actually use the
+ * 12-hour clock, so it's irrelevant).
+ *
+ * @param minutes the minutes within the day
+ * @param minWidth minimum width of output
+ * @param maxWidth maximum width of output
+ * @return the AM or PM indicator
+ */
+
+ public String halfDayName(int minutes, int minWidth, int maxWidth) {
+ String s;
+ if (minutes == 0 && maxWidth >= 8 && "gb".equals(country)) {
+ s = "Midnight";
+ } else if (minutes < 12 * 60) {
+ switch (maxWidth) {
+ case 1:
+ s = "A";
+ break;
+ case 2:
+ case 3:
+ s = "Am";
+ break;
+ default:
+ s = "A.M.";
+ }
+ } else if (minutes == 12 * 60 && maxWidth >= 8 && "gb".equals(country)) {
+ s = "Noon";
+ } else {
+ switch (maxWidth) {
+ case 1:
+ s = "P";
+ break;
+ case 2:
+ case 3:
+ s = "Pm";
+ break;
+ default:
+ s = "P.M.";
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Get an ordinal suffix for a particular component of a date/time.
+ *
+ * @param component the component specifier from a format-dateTime picture, for
+ * example "M" for the month or "D" for the day.
+ * @return a string that is acceptable in the ordinal attribute of xsl:number
+ * to achieve the required ordinal representation. For example, "-e" for the day component
+ * in German, to have the day represented as "dritte August".
+ */
+
+ public String getOrdinalSuffixForDateTime(String component) {
+ return "yes";
+ }
+
+ /**
+ * Get the name for an era (e.g. "BC" or "AD")
+ *
+ * @param year the proleptic gregorian year, using "0" for the year before 1AD
+ */
+
+ public String getEraName(int year) {
+ return (year > 0 ? "AD" : "BC");
+ }
+
+ /**
+ * Get the name of a calendar
+ *
+ * @param code The code representing the calendar as in the XSLT 2.0 spec, e.g. AD for the Gregorian calendar
+ */
+
+ public String getCalendarName(String code) {
+ if (code.equals("AD")) {
+ return "Gregorian";
+ } else {
+ return code;
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/number/Alphanumeric.java b/sf/saxon/expr/number/Alphanumeric.java
new file mode 100644
index 0000000..73727cb
--- /dev/null
+++ b/sf/saxon/expr/number/Alphanumeric.java
@@ -0,0 +1,227 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.number;
+
+import net.sf.saxon.z.IntRangeSet;
+
+/**
+ * This class contains static utility methods to test whether a character is alphanumeric, as defined
+ * by the rules of xsl:number: that is, whether it is in one of the Unicode categories
+ * Nd, Nl, No, Lu, Ll, Lt, Lm or Lo
+ */
+
+public class Alphanumeric {
+
+ private static int[] zeroDigits = {
+ 0x0030, 0x0660, 0x06f0, 0x0966, 0x09e6, 0x0a66, 0x0ae6, 0x0b66, 0x0be6, 0x0c66, 0x0ce6,
+ 0x0d66, 0x0e50, 0x0ed0, 0x0f20, 0x1040, 0x17e0, 0x1810, 0x1946, 0x19d0, 0xff10,
+ 0x104a0, 0x107ce, 0x107d8, 0x107e2, 0x107ec, 0x107f6 };
+
+ // These data sets were generated from the Unicode 4.0 database using a custom stylesheet.
+ // (copied below; source in MyJava/Unicode-db4/listAlphanumeric.xsl)
+ // Note that the characters in the CJK Extended Ideograph ranges A and B, 3400-4DB5 and
+ // 20000-2A6D6 as well as 4E00-9FBB and AC00-D7A3 are not listed individually in the database,
+ // and therefore need to be handled specially.
+
+ // TODO: regenerate these lists from a later version of the Unicode database.
+
+ private static int[] startPoints = new int[]{
+ 0x0030, 0x0041, 0x0061, 0x00AA, 0x00B2, 0x00B5, 0x00B9, 0x00BC, 0x00C0, 0x00D8, 0x00F8,
+ 0x0250, 0x02C6, 0x02E0, 0x02EE, 0x037A, 0x0386, 0x0388, 0x038C, 0x038E, 0x03A3, 0x03D0,
+ 0x03F7, 0x048A, 0x04D0, 0x0500, 0x0531, 0x0559, 0x0561, 0x05D0, 0x05F0, 0x0621, 0x0640,
+ 0x0660, 0x066E, 0x0671, 0x06D5, 0x06E5, 0x06EE, 0x06FF, 0x0710, 0x0712, 0x074D, 0x0780,
+ 0x07B1, 0x0904, 0x093D, 0x0950, 0x0958, 0x0966, 0x097D, 0x0985, 0x098F, 0x0993, 0x09AA,
+ 0x09B2, 0x09B6, 0x09BD, 0x09CE, 0x09DC, 0x09DF, 0x09E6, 0x09F4, 0x0A05, 0x0A0F, 0x0A13,
+ 0x0A2A, 0x0A32, 0x0A35, 0x0A38, 0x0A59, 0x0A5E, 0x0A66, 0x0A72, 0x0A85, 0x0A8F, 0x0A93,
+ 0x0AAA, 0x0AB2, 0x0AB5, 0x0ABD, 0x0AD0, 0x0AE0, 0x0AE6, 0x0B05, 0x0B0F, 0x0B13, 0x0B2A,
+ 0x0B32, 0x0B35, 0x0B3D, 0x0B5C, 0x0B5F, 0x0B66, 0x0B71, 0x0B83, 0x0B85, 0x0B8E, 0x0B92,
+ 0x0B99, 0x0B9C, 0x0B9E, 0x0BA3, 0x0BA8, 0x0BAE, 0x0BE6, 0x0C05, 0x0C0E, 0x0C12, 0x0C2A,
+ 0x0C35, 0x0C60, 0x0C66, 0x0C85, 0x0C8E, 0x0C92, 0x0CAA, 0x0CB5, 0x0CBD, 0x0CDE, 0x0CE0,
+ 0x0CE6, 0x0D05, 0x0D0E, 0x0D12, 0x0D2A, 0x0D60, 0x0D66, 0x0D85, 0x0D9A, 0x0DB3, 0x0DBD,
+ 0x0DC0, 0x0E01, 0x0E32, 0x0E40, 0x0E50, 0x0E81, 0x0E84, 0x0E87, 0x0E8A, 0x0E8D, 0x0E94,
+ 0x0E99, 0x0EA1, 0x0EA5, 0x0EA7, 0x0EAA, 0x0EAD, 0x0EB2, 0x0EBD, 0x0EC0, 0x0EC6, 0x0ED0,
+ 0x0EDC, 0x0F00, 0x0F20, 0x0F40, 0x0F49, 0x0F88, 0x1000, 0x1023, 0x1029, 0x1040, 0x1050,
+ 0x10A0, 0x10D0, 0x10FC, 0x1100, 0x115F, 0x11A8, 0x1200, 0x124A, 0x1250, 0x1258, 0x125A,
+ 0x1260, 0x128A, 0x1290, 0x12B2, 0x12B8, 0x12C0, 0x12C2, 0x12C8, 0x12D8, 0x1312, 0x1318,
+ 0x1369, 0x1380, 0x13A0, 0x1401, 0x166F, 0x1681, 0x16A0, 0x16EE, 0x1700, 0x170E, 0x1720,
+ 0x1740, 0x1760, 0x176E, 0x1780, 0x17D7, 0x17DC, 0x17E0, 0x17F0, 0x1810, 0x1820, 0x1880,
+ 0x1900, 0x1946, 0x1970, 0x1980, 0x19C1, 0x19D0, 0x1A00, 0x1D00, 0x1E00, 0x1EA0, 0x1F00,
+ 0x1F18, 0x1F20, 0x1F48, 0x1F50, 0x1F59, 0x1F5B, 0x1F5D, 0x1F5F, 0x1F80, 0x1FB6, 0x1FBE,
+ 0x1FC2, 0x1FC6, 0x1FD0, 0x1FD6, 0x1FE0, 0x1FF2, 0x1FF6, 0x2070, 0x2074, 0x207F, 0x2090,
+ 0x2102, 0x2107, 0x210A, 0x2115, 0x2119, 0x2124, 0x2126, 0x2128, 0x212A, 0x212F, 0x2133,
+ 0x213C, 0x2145, 0x2153, 0x2460, 0x24EA, 0x2776, 0x2C00, 0x2C30, 0x2C80, 0x2CFD, 0x2D00,
+ 0x2D30, 0x2D6F, 0x2D80, 0x2DA0, 0x2DA8, 0x2DB0, 0x2DB8, 0x2DC0, 0x2DC8, 0x2DD0, 0x2DD8,
+ 0x3005, 0x3021, 0x3031, 0x3038, 0x3041, 0x309D, 0x30A1, 0x30FC, 0x3105, 0x3131, 0x3192,
+ 0x31A0, 0x31F0, 0x3220, 0x3251, 0x3280, 0x32B1, 0x3400, 0x4E00, /*0x9FBB,*/ 0xA000,
+ 0xA800, 0xA803, 0xA807, 0xA80C, 0xAC00, /*0xD7A3,*/ 0xF900, 0xFA30, 0xFA70, 0xFB00, 0xFB13,
+ 0xFB1D, 0xFB1F, 0xFB2A, 0xFB38, 0xFB3E, 0xFB40, 0xFB43, 0xFB46, 0xFBD3, 0xFD50, 0xFD92,
+ 0xFDF0, 0xFE70, 0xFE76, 0xFF10, 0xFF21, 0xFF41, 0xFF66, 0xFFC2, 0xFFCA, 0xFFD2, 0xFFDA,
+ 0x10000, 0x1000D, 0x10028, 0x1003C, 0x1003F, 0x10050, 0x10080, 0x10107, 0x10140, 0x1018A,
+ 0x10300, 0x10320, 0x10330, 0x10380, 0x103A0, 0x103C8, 0x103D1, 0x10400, 0x104A0, 0x10800,
+ 0x10808, 0x1080A, 0x10837, 0x1083C, 0x1083F, 0x10A00, 0x10A10, 0x10A15, 0x10A19, 0x10A40,
+ 0x1D400, 0x1D456, 0x1D49E, 0x1D4A2, 0x1D4A5, 0x1D4A9, 0x1D4AE, 0x1D4BB, 0x1D4BD, 0x1D4C5,
+ 0x1D507, 0x1D50D, 0x1D516, 0x1D51E, 0x1D53B, 0x1D540, 0x1D546, 0x1D54A, 0x1D552, 0x1D6A8,
+ 0x1D6C2, 0x1D6DC, 0x1D6FC, 0x1D716, 0x1D736, 0x1D750, 0x1D770, 0x1D78A, 0x1D7AA, 0x1D7C4,
+ 0x1D7CE, 0x20000, 0x2F800 };
+
+ private static int[] endPoints = new int[]{
+ 0x0039, 0x005A, 0x007A, 0x00AA, 0x00B3, 0x00B5, 0x00BA, 0x00BE, 0x00D6, 0x00F6, 0x0241,
+ 0x02C1, 0x02D1, 0x02E4, 0x02EE, 0x037A, 0x0386, 0x038A, 0x038C, 0x03A1, 0x03CE, 0x03F5,
+ 0x0481, 0x04CE, 0x04F9, 0x050F, 0x0556, 0x0559, 0x0587, 0x05EA, 0x05F2, 0x063A, 0x064A,
+ 0x0669, 0x066F, 0x06D3, 0x06D5, 0x06E6, 0x06FC, 0x06FF, 0x0710, 0x072F, 0x076D, 0x07A5,
+ 0x07B1, 0x0939, 0x093D, 0x0950, 0x0961, 0x096F, 0x097D, 0x098C, 0x0990, 0x09A8, 0x09B0,
+ 0x09B2, 0x09B9, 0x09BD, 0x09CE, 0x09DD, 0x09E1, 0x09F1, 0x09F9, 0x0A0A, 0x0A10, 0x0A28,
+ 0x0A30, 0x0A33, 0x0A36, 0x0A39, 0x0A5C, 0x0A5E, 0x0A6F, 0x0A74, 0x0A8D, 0x0A91, 0x0AA8,
+ 0x0AB0, 0x0AB3, 0x0AB9, 0x0ABD, 0x0AD0, 0x0AE1, 0x0AEF, 0x0B0C, 0x0B10, 0x0B28, 0x0B30,
+ 0x0B33, 0x0B39, 0x0B3D, 0x0B5D, 0x0B61, 0x0B6F, 0x0B71, 0x0B83, 0x0B8A, 0x0B90, 0x0B95,
+ 0x0B9A, 0x0B9C, 0x0B9F, 0x0BA4, 0x0BAA, 0x0BB9, 0x0BF2, 0x0C0C, 0x0C10, 0x0C28, 0x0C33,
+ 0x0C39, 0x0C61, 0x0C6F, 0x0C8C, 0x0C90, 0x0CA8, 0x0CB3, 0x0CB9, 0x0CBD, 0x0CDE, 0x0CE1,
+ 0x0CEF, 0x0D0C, 0x0D10, 0x0D28, 0x0D39, 0x0D61, 0x0D6F, 0x0D96, 0x0DB1, 0x0DBB, 0x0DBD,
+ 0x0DC6, 0x0E30, 0x0E33, 0x0E46, 0x0E59, 0x0E82, 0x0E84, 0x0E88, 0x0E8A, 0x0E8D, 0x0E97,
+ 0x0E9F, 0x0EA3, 0x0EA5, 0x0EA7, 0x0EAB, 0x0EB0, 0x0EB3, 0x0EBD, 0x0EC4, 0x0EC6, 0x0ED9,
+ 0x0EDD, 0x0F00, 0x0F33, 0x0F47, 0x0F6A, 0x0F8B, 0x1021, 0x1027, 0x102A, 0x1049, 0x1055,
+ 0x10C5, 0x10FA, 0x10FC, 0x1159, 0x11A2, 0x11F9, 0x1248, 0x124D, 0x1256, 0x1258, 0x125D,
+ 0x1288, 0x128D, 0x12B0, 0x12B5, 0x12BE, 0x12C0, 0x12C5, 0x12D6, 0x1310, 0x1315, 0x135A,
+ 0x137C, 0x138F, 0x13F4, 0x166C, 0x1676, 0x169A, 0x16EA, 0x16F0, 0x170C, 0x1711, 0x1731,
+ 0x1751, 0x176C, 0x1770, 0x17B3, 0x17D7, 0x17DC, 0x17E9, 0x17F9, 0x1819, 0x1877, 0x18A8,
+ 0x191C, 0x196D, 0x1974, 0x19A9, 0x19C7, 0x19D9, 0x1A16, 0x1DBF, 0x1E9B, 0x1EF9, 0x1F15,
+ 0x1F1D, 0x1F45, 0x1F4D, 0x1F57, 0x1F59, 0x1F5B, 0x1F5D, 0x1F7D, 0x1FB4, 0x1FBC, 0x1FBE,
+ 0x1FC4, 0x1FCC, 0x1FD3, 0x1FDB, 0x1FEC, 0x1FF4, 0x1FFC, 0x2071, 0x2079, 0x2089, 0x2094,
+ 0x2102, 0x2107, 0x2113, 0x2115, 0x211D, 0x2124, 0x2126, 0x2128, 0x212D, 0x2131, 0x2139,
+ 0x213F, 0x2149, 0x2183, 0x249B, 0x24FF, 0x2793, 0x2C2E, 0x2C5E, 0x2CE4, 0x2CFD, 0x2D25,
+ 0x2D65, 0x2D6F, 0x2D96, 0x2DA6, 0x2DAE, 0x2DB6, 0x2DBE, 0x2DC6, 0x2DCE, 0x2DD6, 0x2DDE,
+ 0x3007, 0x3029, 0x3035, 0x303C, 0x3096, 0x309F, 0x30FA, 0x30FF, 0x312C, 0x318E, 0x3195,
+ 0x31B7, 0x31FF, 0x3229, 0x325F, 0x3289, 0x32BF, 0x4DB5, 0x9FBB, /*0x9FBB,*/ 0xA48C,
+ 0xA801, 0xA805, 0xA80A, 0xA822, /*0xAC00,*/ 0xD7A3, 0xFA2D, 0xFA6A, 0xFAD9, 0xFB06, 0xFB17,
+ 0xFB1D, 0xFB28, 0xFB36, 0xFB3C, 0xFB3E, 0xFB41, 0xFB44, 0xFBB1, 0xFD3D, 0xFD8F, 0xFDC7,
+ 0xFDFB, 0xFE74, 0xFEFC, 0xFF19, 0xFF3A, 0xFF5A, 0xFFBE, 0xFFC7, 0xFFCF, 0xFFD7, 0xFFDC,
+ 0x1000B, 0x10026, 0x1003A, 0x1003D, 0x1004D, 0x1005D, 0x100FA, 0x10133, 0x10178, 0x1018A,
+ 0x1031E, 0x10323, 0x1034A, 0x1039D, 0x103C3, 0x103CF, 0x103D5, 0x1049D, 0x104A9, 0x10805,
+ 0x10808, 0x10835, 0x10838, 0x1083C, 0x1083F, 0x10A00, 0x10A13, 0x10A17, 0x10A33, 0x10A47,
+ 0x1D454, 0x1D49C, 0x1D49F, 0x1D4A2, 0x1D4A6, 0x1D4AC, 0x1D4B9, 0x1D4BB, 0x1D4C3, 0x1D505,
+ 0x1D50A, 0x1D514, 0x1D51C, 0x1D539, 0x1D53E, 0x1D544, 0x1D546, 0x1D550, 0x1D6A5, 0x1D6C0,
+ 0x1D6DA, 0x1D6FA, 0x1D714, 0x1D734, 0x1D74E, 0x1D76E, 0x1D788, 0x1D7A8, 0x1D7C2, 0x1D7C9,
+ 0x1D7FF, 0x2A6D6, 0x2FA1D };
+
+ /*@NotNull*/ private static IntRangeSet alphanumerics = new IntRangeSet(startPoints, endPoints);
+
+ /**
+ * Determine whether a Unicode codepoint is alphanumeric, that is, whether it is in one of the
+ * categories Nd, Nl, No, Lu, Ll, Lt, Lm or Lo
+ * @param codepoint the codepoint to be tested
+ * @return true if the codepoint is in one of these categories
+ */
+
+ public static boolean isAlphanumeric(int codepoint) {
+ return alphanumerics.contains(codepoint);
+ }
+
+ /**
+ * Determine whether a character represents a decimal digit and if so, which digit.
+ * @param in the Unicode character being tested.
+ * @return -1 if it's not a decimal digit, otherwise the digit value.
+ */
+
+ public static int getDigitValue(int in) {
+ for (int z=0; z<zeroDigits.length; z++) {
+ if (in <= zeroDigits[z]+9) {
+ if (in >= zeroDigits[z]) {
+ return in - zeroDigits[z];
+ } else {
+ return -1;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Determine which digit family a decimal digit belongs to: that is, return the corresponding zero digit.
+ * @param in a Unicode character
+ * @return if the character is a digit, return the Unicode character that represents zero in the same digit
+ * family. Otherwise, return -1.
+ */
+
+ public static int getDigitFamily(int in){
+ for (int z=0; z<zeroDigits.length; z++) {
+ if (in <= zeroDigits[z]+9) {
+ if (in >= zeroDigits[z]) {
+ return zeroDigits[z];
+ } else {
+ return -1;
+ }
+ }
+ }
+ return -1;
+
+ }
+
+ private Alphanumeric(){}
+}
+
+// For completeness, here is the stylesheet used to generate these lists of ranges from UnicodeData.txt:
+
+//<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+// xmlns:xs="http://www.w3.org/2001/XMLSchema"
+// xmlns:f="http://saxonica.com/ns/unicode"
+// exclude-result-prefixes="xs f"
+//>
+//
+//<!-- Output a list of the start and end points of contiguous ranges of characters
+// classified as letters or digits.
+//
+// Note this doesn't handle the CJK Extended Ideograph ranges A and B, 3400-4DB5 and 20000-2A6D6,
+// which have to be edited in by hand. Also 4E00-9FBB and AC00-D7A3
+//-->
+//
+//<xsl:output method="text"/>
+//<xsl:variable name="data" select="doc('UnicodeData.xml')"/>
+//
+//<xsl:function name="f:isAlphaNum" as="xs:boolean">
+// <xsl:param name="char" as="element(Char)"/>
+// <xsl:sequence select="$char/Field3=('Nd', 'Nl', 'No', 'Lu', 'Ll', 'Lt', 'Lm', 'Lo')"/>
+//</xsl:function>
+//
+//<xsl:function name="f:hexToInt" as="xs:integer?">
+// <xsl:param name="hex" as="xs:string?"/>
+// <xsl:sequence select="if (empty($hex)) then () else Integer:parseInt($hex, 16)"
+// xmlns:Integer="java:java.lang.Integer"/>
+//</xsl:function>
+//
+//<xsl:param name="p"/>
+//<xsl:template name="test">
+// <xsl:value-of select="f:hexToInt($p)"/>
+//</xsl:template>
+//
+//<xsl:template name="main">
+//
+// <xsl:text>int[] startPoints = new int[]{</xsl:text>
+// <xsl:for-each-group select="$data/*/Char" group-adjacent="concat(f:isAlphaNum(.), f:hexToInt(code) - position())">
+// <xsl:if test="f:isAlphaNum(.)">
+// <xsl:text>0x</xsl:text>
+// <xsl:value-of select="current-group()[1]/code"/>
+// <xsl:text>, </xsl:text>
+// <xsl:if test="position() mod 10 = 0">
</xsl:if>
+// </xsl:if>
+// </xsl:for-each-group>
+// <xsl:text>};

</xsl:text>
+// <xsl:text>int[] endPoints = new int[]{</xsl:text>
+// <xsl:for-each-group select="$data/*/Char" group-adjacent="concat(f:isAlphaNum(.), f:hexToInt(code) - position())">
+// <xsl:if test="f:isAlphaNum(.)">
+// <xsl:text>0x</xsl:text>
+// <xsl:value-of select="current-group()[last()]/code"/>
+// <xsl:text>, </xsl:text>
+// <xsl:if test="position() mod 10 = 0">
</xsl:if>
+// </xsl:if>
+// </xsl:for-each-group>
+// <xsl:text>};

</xsl:text>
+//
+//</xsl:template>
+//
+//
+//</xsl:stylesheet>
\ No newline at end of file
diff --git a/sf/saxon/expr/number/IrregularGroupFormatter.java b/sf/saxon/expr/number/IrregularGroupFormatter.java
new file mode 100644
index 0000000..92107fe
--- /dev/null
+++ b/sf/saxon/expr/number/IrregularGroupFormatter.java
@@ -0,0 +1,73 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.number;
+
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.z.IntSet;
+
+import java.util.List;
+
+/**
+ * Handles grouping separators when formatting a number in cases where the grouping separators are
+ * not at regular intervals
+ */
+
+public class IrregularGroupFormatter extends NumericGroupFormatter {
+
+ /*@Nullable*/ private IntSet groupingPositions = null;
+ private List<Integer> separators = null;
+
+ /**
+ * Create a formatter for numbers where the grouping separators occur at irregular positions
+ * @param groupingPositions the positions where the separators are to be inserted
+ * @param sep array holding the separators to be inserted, as Unicode codepoints, in order starting
+ * with the right-most
+ * @param adjustedPicture
+ */
+
+ public IrregularGroupFormatter(IntSet groupingPositions, List<Integer> sep, UnicodeString adjustedPicture) {
+ this.groupingPositions = groupingPositions;
+ separators = sep;
+ this.adjustedPicture = adjustedPicture;
+ }
+
+ @Override
+ public String format(FastStringBuffer value) {
+ UnicodeString in = UnicodeString.makeUnicodeString(value);
+ int[] out = new int[in.length() + separators.size()];
+ int j = 0;
+ int k = out.length - 1;
+ for (int i=in.length()-1; i>=0; i--) {
+ out[k--] = in.charAt(i);
+ if (groupingPositions.contains(in.length()-i)) {
+ out[k--] = separators.get(j++);
+ }
+ }
+ return UnicodeString.makeUnicodeString(out).toString();
+ }
+
+ /**
+ * Get the grouping separator to be used. If more than one is used, return the last.
+ * If no grouping separators are used, return null
+ *
+ * @return the grouping separator
+ */
+ @Override
+ public String getSeparator() {
+ if (separators.size() == 0) {
+ return null;
+ } else {
+ int sep = separators.get(separators.size() - 1);
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.TINY);
+ fsb.appendWideChar(sep);
+ return fsb.toString();
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/number/NamedTimeZone.java b/sf/saxon/expr/number/NamedTimeZone.java
new file mode 100644
index 0000000..0e7bf8f
--- /dev/null
+++ b/sf/saxon/expr/number/NamedTimeZone.java
@@ -0,0 +1,1011 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.number;
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.DateTimeValue;
+
+import java.util.*;
+
+/**
+ * This class attempts to identify a timezone name, given the date (including the time zone offset)
+ * and the country. The process is heuristic: sometimes there is more than one timezone that matches
+ * this information, sometimes there is none, but the class simply does its best. This is in support
+ * of the XSLT format-date() function.
+ */
+public class NamedTimeZone {
+
+ static HashMap<String, List<String>> idForCountry = new HashMap<String, List<String>>(50);
+
+ /**
+ * Register a timezone in use in a particular country. Note that some countries use multiple
+ * time zones
+ * @param country the two-character code for the country
+ * @param zoneId the Olsen timezone name for the timezone
+ */
+
+ static void tz(String country, String zoneId) {
+ List<String> list = idForCountry.get(country);
+ if (list == null) {
+ list = new ArrayList<String>(4);
+ }
+ list.add(zoneId);
+ idForCountry.put(country, list);
+ }
+
+ static {
+
+ // The table starts with countries that use multiple timezones, then proceeds in alphabetical order
+
+ tz("us", "America/New_York");
+ tz("us", "America/Chicago");
+ tz("us", "America/Denver");
+ tz("us", "America/Los_Angeles");
+ tz("us", "America/Anchorage");
+ tz("us", "America/Halifax");
+
+ tz("ca", "Canada/Pacific");
+ tz("ca", "Canada/Mountain");
+ tz("ca", "Canada/Central");
+ tz("ca", "Canada/Eastern");
+ tz("ca", "Canada/Atlantic");
+
+ tz("au", "Australia/Sydney");
+ tz("au", "Australia/Darwin");
+ tz("au", "Australia/Perth");
+
+ tz("ru", "Europe/Moscow");
+ tz("ru", "Europe/Samara");
+ tz("ru", "Asia/Yekaterinburg");
+ tz("ru", "Asia/Novosibirsk");
+ tz("ru", "Asia/Krasnoyarsk");
+ tz("ru", "Asia/Irkutsk");
+ tz("ru", "Asia/Chita");
+ tz("ru", "Asia/Vladivostok");
+
+ tz("an", "Europe/Andorra");
+ tz("ae", "Asia/Abu_Dhabi");
+ tz("af", "Asia/Kabul");
+ tz("al", "Europe/Tirana");
+ tz("am", "Asia/Yerevan");
+ tz("ao", "Africa/Luanda");
+ tz("ar", "America/Buenos_Aires");
+ tz("as", "Pacific/Samoa");
+ tz("at", "Europe/Vienna");
+ tz("aw", "America/Aruba");
+ tz("az", "Asia/Baku");
+
+ tz("ba", "Europe/Sarajevo");
+ tz("bb", "America/Barbados");
+ tz("bd", "Asia/Dhaka");
+ tz("be", "Europe/Brussels");
+ tz("bf", "Africa/Ouagadougou");
+ tz("bg", "Europe/Sofia");
+ tz("bh", "Asia/Bahrain");
+ tz("bi", "Africa/Bujumbura");
+ tz("bm", "Atlantic/Bermuda");
+ tz("bn", "Asia/Brunei");
+ tz("bo", "America/La_Paz");
+ tz("br", "America/Sao_Paulo");
+ tz("bs", "America/Nassau");
+ tz("bw", "Gaborone");
+ tz("by", "Europe/Minsk");
+ tz("bz", "America/Belize");
+
+ tz("cd", "Africa/Kinshasa");
+ tz("ch", "Europe/Zurich");
+ tz("ci", "Africa/Abidjan");
+ tz("cl", "America/Santiago");
+ tz("cn", "Asia/Shanghai");
+ tz("co", "America/Bogota");
+ tz("cr", "America/Costa_Rica");
+ tz("cu", "America/Cuba");
+ tz("cv", "Atlantic/Cape_Verde");
+ tz("cy", "Asia/Nicosia");
+ tz("cz", "Europe/Prague");
+
+ tz("de", "Europe/Berlin");
+ tz("dj", "Africa/Djibouti");
+ tz("dk", "Europe/Copenhagen");
+ tz("do", "America/Santo_Domingo");
+ tz("dz", "Africa/Algiers");
+
+ tz("ec", "America/Quito");
+ tz("ee", "Europe/Tallinn");
+ tz("eg", "Africa/Cairo");
+ tz("er", "Africa/Asmara");
+ tz("es", "Europe/Madrid");
+
+ tz("fi", "Europe/Helsinki");
+ tz("fj", "Pacific/Fiji");
+ tz("fk", "America/Stanley");
+ tz("fr", "Europe/Paris");
+
+ tz("ga", "Africa/Libreville");
+ tz("gb", "Europe/London");
+ tz("gd", "America/Grenada");
+ tz("ge", "Asia/Tbilisi");
+ tz("gh", "Africa/Accra");
+ tz("gm", "Africa/Banjul");
+ tz("gn", "Africa/Conakry");
+ tz("gr", "Europe/Athens");
+ tz("gy", "America/Guyana");
+
+ tz("hk", "Asia/Hong_Kong");
+ tz("hn", "America/Tegucigalpa");
+ tz("hr", "Europe/Zagreb");
+ tz("ht", "America/Port-au-Prince");
+ tz("hu", "Europe/Budapest");
+
+ tz("id", "Asia/Jakarta");
+ tz("ie", "Europe/Dublin");
+ tz("il", "Asia/Tel_Aviv");
+ tz("in", "Asia/Calcutta");
+ tz("iq", "Asia/Baghdad");
+ tz("ir", "Asia/Tehran");
+ tz("is", "Atlantic/Reykjavik");
+ tz("it", "Europe/Rome");
+
+ tz("jm", "America/Jamaica");
+ tz("jo", "Asia/Amman");
+ tz("jp", "Asia/Tokyo");
+
+ tz("ke", "Africa/Nairobi");
+ tz("kg", "Asia/Bishkek");
+ tz("kh", "Asia/Phnom_Penh");
+ tz("kp", "Asia/Pyongyang");
+ tz("kr", "Asia/Seoul");
+ tz("kw", "Asia/Kuwait");
+
+ tz("lb", "Asia/Beirut");
+ tz("li", "Europe/Liechtenstein");
+ tz("lk", "Asia/Colombo");
+ tz("lr", "Africa/Monrovia");
+ tz("ls", "Africa/Maseru");
+ tz("lt", "Europe/Vilnius");
+ tz("lu", "Europe/Luxembourg");
+ tz("lv", "Europe/Riga");
+ tz("ly", "Africa/Tripoli");
+
+ tz("ma", "Africa/Rabat");
+ tz("mc", "Europe/Monaco");
+ tz("md", "Europe/Chisinau");
+ tz("mg", "Indian/Antananarivo");
+ tz("mk", "Europe/Skopje");
+ tz("ml", "Africa/Bamako");
+ tz("mm", "Asia/Rangoon");
+ tz("mn", "Asia/Ulaanbaatar");
+ tz("mo", "Asia/Macao");
+ tz("mq", "America/Martinique");
+ tz("mt", "Europe/Malta");
+ tz("mu", "Indian/Mauritius");
+ tz("mv", "Indian/Maldives");
+ tz("mw", "Africa/Lilongwe");
+ tz("mx", "America/Mexico_City");
+ tz("my", "Asia/Kuala_Lumpur");
+
+ tz("na", "Africa/Windhoek");
+ tz("ne", "Africa/Niamey");
+ tz("ng", "Africa/Lagos");
+ tz("ni", "America/Managua");
+ tz("nl", "Europe/Amsterdam");
+ tz("no", "Europe/Oslo");
+ tz("np", "Asia/Kathmandu");
+ tz("nz", "Pacific/Aukland");
+
+ tz("om", "Asia/Muscat");
+
+ tz("pa", "America/Panama");
+ tz("pe", "America/Lima");
+ tz("pg", "Pacific/Port_Moresby");
+ tz("ph", "Asia/Manila");
+ tz("pk", "Asia/Karachi");
+ tz("pl", "Europe/Warsaw");
+ tz("pr", "America/Puerto_Rico");
+ tz("pt", "Europe/Lisbon");
+ tz("py", "America/Asuncion");
+
+ tz("qa", "Asia/Qatar");
+
+ tz("ro", "Europe/Bucharest");
+ tz("rs", "Europe/Belgrade");
+
+ tz("rw", "Africa/Kigali");
+
+ tz("sa", "Asia/Riyadh");
+ tz("sd", "Africa/Khartoum");
+ tz("se", "Europe/Stockholm");
+ tz("sg", "Asia/Singapore");
+ tz("si", "Europe/Ljubljana");
+ tz("sk", "Europe/Bratislava");
+ tz("sl", "Africa/Freetown");
+ tz("so", "Africa/Mogadishu");
+ tz("sr", "America/Paramaribo");
+ tz("sv", "America/El_Salvador");
+ tz("sy", "Asia/Damascus");
+ tz("sz", "Africa/Mbabane");
+
+ tz("td", "Africa/Ndjamena");
+ tz("tg", "Africa/Lome");
+ tz("th", "Asia/Bangkok");
+ tz("tj", "Asia/Dushanbe");
+ tz("tm", "Asia/Ashgabat");
+ tz("tn", "Africa/Tunis");
+ tz("to", "Pacific/Tongatapu");
+ tz("tr", "Asia/Istanbul");
+ tz("tw", "Asia/Taipei");
+ tz("tz", "Africa/Dar_es_Salaam");
+
+ tz("ua", "Europe/Kiev");
+ tz("ug", "Africa/Kampala");
+ tz("uk", "Europe/London");
+ tz("uy", "America/Montevideo");
+ tz("uz", "Asia/Tashkent");
+
+ tz("ve", "America/Caracas");
+ tz("vn", "Asia/Hanoi");
+
+ tz("za", "Africa/Johannesburg");
+ tz("zm", "Africa/Lusaka");
+ tz("zw", "Africa/Harare");
+
+
+ }
+
+ /**
+ * Try to identify a timezone name corresponding to a given date (including time zone)
+ * and a given country. Note that this takes account of Java's calendar of daylight savings time
+ * changes in different countries. The returned value is the convenional short timezone name, for example
+ * PDT for Pacific Daylight Time
+ * @param date the dateTimeValue, including timezone
+ * @param country the two-letter ISO country code
+ * @return the short timezone name if a timezone with the given time displacement is in use in the country
+ * in question (on the appropriate date, if known). Otherwise, the formatted (numeric) timezone offset. If
+ * the dateTimeValue supplied has no timezone, return a zero-length string.
+ */
+
+ public static String getTimeZoneNameForDate(DateTimeValue date, /*@Nullable*/ String country) {
+ if (!date.hasTimezone()) {
+ return "";
+ }
+ if (country == null) {
+ return formatTimeZoneOffset(date);
+ }
+ List<String> possibleIds = idForCountry.get(country.toLowerCase());
+ String exampleId;
+ if (possibleIds == null) {
+ return formatTimeZoneOffset(date);
+ } else {
+ exampleId = possibleIds.get(0);
+ }
+ TimeZone exampleZone = TimeZone.getTimeZone(exampleId);
+ Date javaDate;
+ try {
+ javaDate = date.getCalendar().getTime();
+ } catch (IllegalArgumentException e) {
+ // this happens with timezones that are allowed in XPath but not in Java, especially on JDK 1.4
+ return formatTimeZoneOffset(date);
+ }
+ boolean inSummerTime = exampleZone.inDaylightTime(javaDate);
+ int tzMinutes = date.getTimezoneInMinutes();
+ for (int i=0; i<possibleIds.size(); i++) {
+ TimeZone possibleTimeZone = TimeZone.getTimeZone(possibleIds.get(i));
+ int offset = possibleTimeZone.getOffset(javaDate.getTime());
+ if (offset == tzMinutes*60000) {
+ return possibleTimeZone.getDisplayName(inSummerTime, TimeZone.SHORT);
+ }
+ }
+ return formatTimeZoneOffset(date);
+ }
+
+ /**
+ * Format a timezone in numeric form for example +03:00 (or Z for +00:00)
+ * @param timeValue the value whose timezone is to be formatted
+ * @return the formatted timezone
+ */
+
+ public static String formatTimeZoneOffset(DateTimeValue timeValue) {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+ DateTimeValue.appendTimezone(timeValue.getTimezoneInMinutes(), sb);
+ return sb.toString();
+ }
+
+ /**
+ * Try to identify a timezone name corresponding to a given date (including time zone)
+ * and a given country. Note that this takes account of Java's calendar of daylight savings time
+ * changes in different countries. The returned value is the Olsen time zone name, for example
+ * "Pacific/Los_Angeles", followed by an asterisk (*) if the time is in daylight savings time in that
+ * timezone.
+ * @param date the dateTimeValue, including timezone
+ * @param country the country, as a two-letter code
+ * @return the Olsen timezone name if a timezone with the given time displacement is in use in the country
+ * in question (on the appropriate date, if known). In this case an asterisk is appended to the result if the
+ * date/time is in daylight savings time. Otherwise, the formatted (numeric) timezone offset. If
+ * the dateTimeValue supplied has no timezone, return a zero-length string.
+ */
+
+ public static String getOlsenTimeZoneName(DateTimeValue date, String country) {
+ if (!date.hasTimezone()) {
+ return "";
+ }
+ List<String> possibleIds = idForCountry.get(country.toLowerCase());
+ String exampleId;
+ if (possibleIds == null) {
+ return formatTimeZoneOffset(date);
+ } else {
+ exampleId = possibleIds.get(0);
+ }
+ TimeZone exampleZone = TimeZone.getTimeZone(exampleId);
+ Date javaDate = date.getCalendar().getTime();
+ boolean inSummerTime = exampleZone.inDaylightTime(javaDate);
+ int tzMinutes = date.getTimezoneInMinutes();
+ for (int i=0; i<possibleIds.size(); i++) {
+ String olsen = possibleIds.get(i);
+ TimeZone possibleTimeZone = TimeZone.getTimeZone(olsen);
+ int offset = possibleTimeZone.getOffset(javaDate.getTime());
+ if (offset == tzMinutes*60000) {
+ return inSummerTime ? olsen + "*" : olsen;
+ }
+ }
+ return formatTimeZoneOffset(date);
+ }
+
+ /**
+ * Determine whether a given date/time is in summer time (daylight savings time)
+ * in a given region. This relies on the Java database of changes to daylight savings time.
+ * Since summer time changes are set by civil authorities the information is not necessarily
+ * reliable when applied to dates in the future.
+ * @param date the date/time in question
+ * @param region either the two-letter ISO country code, or an Olsen timezone name such as
+ * "America/New_York" or "Europe/Lisbon". If the country code denotes a country spanning several
+ * timezones, such as the US, then one of them is chosen arbitrarily.
+ * @return true if the date/time is known to be in summer time in the relevant country;
+ * false if it is known not to be in summer time; null if there is no timezone or if no
+ * information is available for the specified region.
+ */
+
+ public static Boolean inSummerTime(DateTimeValue date, String region) {
+ String olsenName;
+ if (region.length() == 2) {
+ List<String> possibleIds = idForCountry.get(region.toLowerCase());
+ if (possibleIds == null) {
+ return null;
+ } else {
+ olsenName = possibleIds.get(0);
+ }
+ } else {
+ olsenName = region;
+ }
+ TimeZone zone = TimeZone.getTimeZone(olsenName);
+ return Boolean.valueOf(zone.inDaylightTime(date.getCalendar().getTime()));
+ }
+
+ /**
+ * Get the civil time offset to be made to a given date/time in a given
+ * civil timezone. For example, if the timezone is America/New_York, the civil time
+ * offset will be -5 hours (= 5 x 3600000 ms) during the winter and -4 hours
+ * (=4 x 3600000 ms) during the summer
+ * @param date the date/time in question. If this has no timezone, it is assumed
+ * to be in GMT.
+ * @param olsenName the Olsen name of the timezone, for example Europe/Lisbon
+ * @return the civil time offset, in milliseconds, to be applied to the given
+ * date/time
+ */
+
+ public static int civilTimeOffset(DateTimeValue date, String olsenName) {
+ TimeZone zone = TimeZone.getTimeZone(olsenName);
+ return zone.getOffset(date.getCalendar().getTime().getTime());
+ }
+
+
+ /**
+ * Main method for testing
+ * @param args first argument: a dateTime value
+ * second argument: a country code
+ */
+
+// public static void main(String[] args) {
+// System.err.println(NamedTimeZone.getTimeZoneNameForDate((DateTimeValue)DateTimeValue.makeDateTimeValue(args[0]), args[1]));
+// }
+
+ /**
+ * Main method to generate the list of timezone names known to Java
+ * @param args not used
+ */
+
+// public static void main(String[] args) {
+// String[] ids = TimeZone.getAvailableIDs(/* -5*60*60*1000 */);
+// for (int i=0; i<ids.length; i++) {
+// System.err.println(ids[i] + " - " + TimeZone.getTimeZone(ids[i]).getDisplayName(true, TimeZone.SHORT) +
+// " - " + TimeZone.getTimeZone(ids[i]).getDisplayName(false, TimeZone.SHORT));
+// }
+// }
+}
+
+/*
+Etc/GMT+12 - GMT-12:00 - GMT-12:00
+Etc/GMT+11 - GMT-11:00 - GMT-11:00
+MIT - WST - WST
+Pacific/Apia - WST - WST
+Pacific/Midway - SST - SST
+Pacific/Niue - NUT - NUT
+Pacific/Pago_Pago - SST - SST
+Pacific/Samoa - SST - SST
+US/Samoa - SST - SST
+America/Adak - HADT - HAST
+America/Atka - HADT - HAST
+Etc/GMT+10 - GMT-10:00 - GMT-10:00
+HST - HST - HST
+Pacific/Fakaofo - TKT - TKT
+Pacific/Honolulu - HST - HST
+Pacific/Johnston - HST - HST
+Pacific/Rarotonga - CKT - CKT
+Pacific/Tahiti - TAHT - TAHT
+SystemV/HST10 - HST - HST
+US/Aleutian - HADT - HAST
+US/Hawaii - HST - HST
+Pacific/Marquesas - MART - MART
+AST - AKDT - AKST
+America/Anchorage - AKDT - AKST
+America/Juneau - AKDT - AKST
+America/Nome - AKDT - AKST
+America/Yakutat - AKDT - AKST
+Etc/GMT+9 - GMT-09:00 - GMT-09:00
+Pacific/Gambier - GAMT - GAMT
+SystemV/YST9 - AKST - AKST
+SystemV/YST9YDT - AKDT - AKST
+US/Alaska - AKDT - AKST
+America/Dawson - PDT - PST
+America/Ensenada - PDT - PST
+America/Los_Angeles - PDT - PST
+America/Tijuana - PDT - PST
+America/Vancouver - PDT - PST
+America/Whitehorse - PDT - PST
+Canada/Pacific - PDT - PST
+Canada/Yukon - PDT - PST
+Etc/GMT+8 - GMT-08:00 - GMT-08:00
+Mexico/BajaNorte - PDT - PST
+PST - PDT - PST
+PST8PDT - PDT - PST
+Pacific/Pitcairn - PST - PST
+SystemV/PST8 - PST - PST
+SystemV/PST8PDT - PDT - PST
+US/Pacific - PDT - PST
+US/Pacific-New - PDT - PST
+America/Boise - MDT - MST
+America/Cambridge_Bay - MDT - MST
+America/Chihuahua - MDT - MST
+America/Dawson_Creek - MST - MST
+America/Denver - MDT - MST
+America/Edmonton - MDT - MST
+America/Hermosillo - MST - MST
+America/Inuvik - MDT - MST
+America/Mazatlan - MDT - MST
+America/Phoenix - MST - MST
+America/Shiprock - MDT - MST
+America/Yellowknife - MDT - MST
+Canada/Mountain - MDT - MST
+Etc/GMT+7 - GMT-07:00 - GMT-07:00
+MST - MST - MST
+MST7MDT - MDT - MST
+Mexico/BajaSur - MDT - MST
+Navajo - MDT - MST
+PNT - MST - MST
+SystemV/MST7 - MST - MST
+SystemV/MST7MDT - MDT - MST
+US/Arizona - MST - MST
+US/Mountain - MDT - MST
+America/Belize - CST - CST
+America/Cancun - CDT - CST
+America/Chicago - CDT - CST
+America/Costa_Rica - CST - CST
+America/El_Salvador - CST - CST
+America/Guatemala - CST - CST
+America/Indiana/Knox - CDT - CST
+America/Indiana/Petersburg - CDT - CST
+America/Indiana/Vincennes - CDT - CST
+America/Knox_IN - EDT - EST
+America/Managua - CST - CST
+America/Menominee - CDT - CST
+America/Merida - CDT - CST
+America/Mexico_City - CDT - CST
+America/Monterrey - CDT - CST
+America/North_Dakota/Center - CDT - CST
+America/North_Dakota/New_Salem - CDT - CST
+America/Rainy_River - CDT - CST
+America/Rankin_Inlet - CDT - CST
+America/Regina - CST - CST
+America/Swift_Current - CST - CST
+America/Tegucigalpa - CST - CST
+America/Winnipeg - CDT - CST
+CST - CDT - CST
+CST6CDT - CDT - CST
+Canada/Central - CDT - CST
+Canada/East-Saskatchewan - CST - CST
+Canada/Saskatchewan - CST - CST
+Chile/EasterIsland - EASST - EAST
+Etc/GMT+6 - GMT-06:00 - GMT-06:00
+Mexico/General - CDT - CST
+Pacific/Easter - EASST - EAST
+Pacific/Galapagos - GALT - GALT
+SystemV/CST6 - CST - CST
+SystemV/CST6CDT - CDT - CST
+US/Central - CDT - CST
+US/Indiana-Starke - EDT - EST
+America/Bogota - COT - COT
+America/Cayman - EST - EST
+America/Coral_Harbour - EST - EST
+America/Detroit - EDT - EST
+America/Eirunepe - ACT - ACT
+America/Fort_Wayne - EDT - EST
+America/Grand_Turk - EDT - EST
+America/Guayaquil - ECT - ECT
+America/Havana - CDT - CST
+America/Indiana/Indianapolis - EDT - EST
+America/Indiana/Marengo - EDT - EST
+America/Indiana/Vevay - EDT - EST
+America/Indianapolis - EDT - EST
+America/Iqaluit - EDT - EST
+America/Jamaica - EST - EST
+America/Kentucky/Louisville - EDT - EST
+America/Kentucky/Monticello - EDT - EST
+America/Lima - PET - PET
+America/Louisville - EDT - EST
+America/Montreal - EDT - EST
+America/Nassau - EDT - EST
+America/New_York - EDT - EST
+America/Nipigon - EDT - EST
+America/Panama - EST - EST
+America/Pangnirtung - EDT - EST
+America/Port-au-Prince - EDT - EST
+America/Porto_Acre - ACT - ACT
+America/Rio_Branco - ACT - ACT
+America/Thunder_Bay - EDT - EST
+America/Toronto - EDT - EST
+Brazil/Acre - ACT - ACT
+Canada/Eastern - EDT - EST
+Cuba - CDT - CST
+EST - EST - EST
+EST5EDT - EDT - EST
+Etc/GMT+5 - GMT-05:00 - GMT-05:00
+IET - EDT - EST
+Jamaica - EST - EST
+SystemV/EST5 - EST - EST
+SystemV/EST5EDT - EDT - EST
+US/East-Indiana - EDT - EST
+US/Eastern - EDT - EST
+US/Michigan - EDT - EST
+America/Anguilla - AST - AST
+America/Antigua - AST - AST
+America/Aruba - AST - AST
+America/Asuncion - PYST - PYT
+America/Barbados - AST - AST
+America/Boa_Vista - AMT - AMT
+America/Campo_Grande - AMST - AMT
+America/Caracas - VET - VET
+America/Cuiaba - AMST - AMT
+America/Curacao - AST - AST
+America/Dominica - AST - AST
+America/Glace_Bay - ADT - AST
+America/Goose_Bay - ADT - AST
+America/Grenada - AST - AST
+America/Guadeloupe - AST - AST
+America/Guyana - GYT - GYT
+America/Halifax - ADT - AST
+America/La_Paz - BOT - BOT
+America/Manaus - AMT - AMT
+America/Martinique - AST - AST
+America/Moncton - ADT - AST
+America/Montserrat - AST - AST
+America/Port_of_Spain - AST - AST
+America/Porto_Velho - AMT - AMT
+America/Puerto_Rico - AST - AST
+America/Santiago - CLST - CLT
+America/Santo_Domingo - AST - AST
+America/St_Kitts - AST - AST
+America/St_Lucia - AST - AST
+America/St_Thomas - AST - AST
+America/St_Vincent - AST - AST
+America/Thule - ADT - AST
+America/Tortola - AST - AST
+America/Virgin - AST - AST
+Antarctica/Palmer - CLST - CLT
+Atlantic/Bermuda - ADT - AST
+Atlantic/Stanley - FKST - FKT
+Brazil/West - AMT - AMT
+Canada/Atlantic - ADT - AST
+Chile/Continental - CLST - CLT
+Etc/GMT+4 - GMT-04:00 - GMT-04:00
+PRT - AST - AST
+SystemV/AST4 - AST - AST
+SystemV/AST4ADT - ADT - AST
+America/St_Johns - NDT - NST
+CNT - NDT - NST
+Canada/Newfoundland - NDT - NST
+AGT - ART - ART
+America/Araguaina - BRT - BRT
+America/Argentina/Buenos_Aires - ART - ART
+America/Argentina/Catamarca - ART - ART
+America/Argentina/ComodRivadavia - ART - ART
+America/Argentina/Cordoba - ART - ART
+America/Argentina/Jujuy - ART - ART
+America/Argentina/La_Rioja - ART - ART
+America/Argentina/Mendoza - ART - ART
+America/Argentina/Rio_Gallegos - ART - ART
+America/Argentina/San_Juan - ART - ART
+America/Argentina/Tucuman - ART - ART
+America/Argentina/Ushuaia - ART - ART
+America/Bahia - BRT - BRT
+America/Belem - BRT - BRT
+America/Buenos_Aires - ART - ART
+America/Catamarca - ART - ART
+America/Cayenne - GFT - GFT
+America/Cordoba - ART - ART
+America/Fortaleza - BRT - BRT
+America/Godthab - WGST - WGT
+America/Jujuy - ART - ART
+America/Maceio - BRT - BRT
+America/Mendoza - ART - ART
+America/Miquelon - PMDT - PMST
+America/Montevideo - UYT - UYT
+America/Paramaribo - SRT - SRT
+America/Recife - BRT - BRT
+America/Rosario - ART - ART
+America/Sao_Paulo - BRST - BRT
+Antarctica/Rothera - ROTT - ROTT
+BET - BRST - BRT
+Brazil/East - BRST - BRT
+Etc/GMT+3 - GMT-03:00 - GMT-03:00
+America/Noronha - FNT - FNT
+Atlantic/South_Georgia - GST - GST
+Brazil/DeNoronha - FNT - FNT
+Etc/GMT+2 - GMT-02:00 - GMT-02:00
+America/Scoresbysund - EGST - EGT
+Atlantic/Azores - AZOST - AZOT
+Atlantic/Cape_Verde - CVT - CVT
+Etc/GMT+1 - GMT-01:00 - GMT-01:00
+Africa/Abidjan - GMT - GMT
+Africa/Accra - GMT - GMT
+Africa/Bamako - GMT - GMT
+Africa/Banjul - GMT - GMT
+Africa/Bissau - GMT - GMT
+Africa/Casablanca - WET - WET
+Africa/Conakry - GMT - GMT
+Africa/Dakar - GMT - GMT
+Africa/El_Aaiun - WET - WET
+Africa/Freetown - GMT - GMT
+Africa/Lome - GMT - GMT
+Africa/Monrovia - GMT - GMT
+Africa/Nouakchott - GMT - GMT
+Africa/Ouagadougou - GMT - GMT
+Africa/Sao_Tome - GMT - GMT
+Africa/Timbuktu - GMT - GMT
+America/Danmarkshavn - GMT - GMT
+Atlantic/Canary - WEST - WET
+Atlantic/Faeroe - WEST - WET
+Atlantic/Madeira - WEST - WET
+Atlantic/Reykjavik - GMT - GMT
+Atlantic/St_Helena - GMT - GMT
+Eire - IST - GMT
+Etc/GMT - GMT+00:00 - GMT+00:00
+Etc/GMT+0 - GMT+00:00 - GMT+00:00
+Etc/GMT-0 - GMT+00:00 - GMT+00:00
+Etc/GMT0 - GMT+00:00 - GMT+00:00
+Etc/Greenwich - GMT - GMT
+Etc/UCT - UTC - UTC
+Etc/UTC - UTC - UTC
+Etc/Universal - UTC - UTC
+Etc/Zulu - UTC - UTC
+Europe/Belfast - BST - GMT
+Europe/Dublin - IST - GMT
+Europe/Lisbon - WEST - WET
+Europe/London - BST - GMT
+GB - BST - GMT
+GB-Eire - BST - GMT
+GMT - GMT - GMT
+GMT0 - GMT+00:00 - GMT+00:00
+Greenwich - GMT - GMT
+Iceland - GMT - GMT
+Portugal - WEST - WET
+UCT - UTC - UTC
+UTC - UTC - UTC
+Universal - UTC - UTC
+WET - WEST - WET
+Zulu - UTC - UTC
+Africa/Algiers - CET - CET
+Africa/Bangui - WAT - WAT
+Africa/Brazzaville - WAT - WAT
+Africa/Ceuta - CEST - CET
+Africa/Douala - WAT - WAT
+Africa/Kinshasa - WAT - WAT
+Africa/Lagos - WAT - WAT
+Africa/Libreville - WAT - WAT
+Africa/Luanda - WAT - WAT
+Africa/Malabo - WAT - WAT
+Africa/Ndjamena - WAT - WAT
+Africa/Niamey - WAT - WAT
+Africa/Porto-Novo - WAT - WAT
+Africa/Tunis - CEST - CET
+Africa/Windhoek - WAST - WAT
+Arctic/Longyearbyen - CEST - CET
+Atlantic/Jan_Mayen - CEST - CET
+CET - CEST - CET
+ECT - CEST - CET
+Etc/GMT-1 - GMT+01:00 - GMT+01:00
+Europe/Amsterdam - CEST - CET
+Europe/Andorra - CEST - CET
+Europe/Belgrade - CEST - CET
+Europe/Berlin - CEST - CET
+Europe/Bratislava - CEST - CET
+Europe/Brussels - CEST - CET
+Europe/Budapest - CEST - CET
+Europe/Copenhagen - CEST - CET
+Europe/Gibraltar - CEST - CET
+Europe/Ljubljana - CEST - CET
+Europe/Luxembourg - CEST - CET
+Europe/Madrid - CEST - CET
+Europe/Malta - CEST - CET
+Europe/Monaco - CEST - CET
+Europe/Oslo - CEST - CET
+Europe/Paris - CEST - CET
+Europe/Prague - CEST - CET
+Europe/Rome - CEST - CET
+Europe/San_Marino - CEST - CET
+Europe/Sarajevo - CEST - CET
+Europe/Skopje - CEST - CET
+Europe/Stockholm - CEST - CET
+Europe/Tirane - CEST - CET
+Europe/Vaduz - CEST - CET
+Europe/Vatican - CEST - CET
+Europe/Vienna - CEST - CET
+Europe/Warsaw - CEST - CET
+Europe/Zagreb - CEST - CET
+Europe/Zurich - CEST - CET
+MET - MEST - MET
+Poland - CEST - CET
+ART - EEST - EET
+Africa/Blantyre - CAT - CAT
+Africa/Bujumbura - CAT - CAT
+Africa/Cairo - EEST - EET
+Africa/Gaborone - CAT - CAT
+Africa/Harare - CAT - CAT
+Africa/Johannesburg - SAST - SAST
+Africa/Kigali - CAT - CAT
+Africa/Lubumbashi - CAT - CAT
+Africa/Lusaka - CAT - CAT
+Africa/Maputo - CAT - CAT
+Africa/Maseru - SAST - SAST
+Africa/Mbabane - SAST - SAST
+Africa/Tripoli - EET - EET
+Asia/Amman - EEST - EET
+Asia/Beirut - EEST - EET
+Asia/Damascus - EEST - EET
+Asia/Gaza - EEST - EET
+Asia/Istanbul - EEST - EET
+Asia/Jerusalem - IDT - IST
+Asia/Nicosia - EEST - EET
+Asia/Tel_Aviv - IDT - IST
+CAT - CAT - CAT
+EET - EEST - EET
+Egypt - EEST - EET
+Etc/GMT-2 - GMT+02:00 - GMT+02:00
+Europe/Athens - EEST - EET
+Europe/Bucharest - EEST - EET
+Europe/Chisinau - EEST - EET
+Europe/Helsinki - EEST - EET
+Europe/Istanbul - EEST - EET
+Europe/Kaliningrad - EEST - EET
+Europe/Kiev - EEST - EET
+Europe/Mariehamn - EEST - EET
+Europe/Minsk - EEST - EET
+Europe/Nicosia - EEST - EET
+Europe/Riga - EEST - EET
+Europe/Simferopol - EEST - EET
+Europe/Sofia - EEST - EET
+Europe/Tallinn - EEST - EET
+Europe/Tiraspol - EEST - EET
+Europe/Uzhgorod - EEST - EET
+Europe/Vilnius - EEST - EET
+Europe/Zaporozhye - EEST - EET
+Israel - IDT - IST
+Libya - EET - EET
+Turkey - EEST - EET
+Africa/Addis_Ababa - EAT - EAT
+Africa/Asmera - EAT - EAT
+Africa/Dar_es_Salaam - EAT - EAT
+Africa/Djibouti - EAT - EAT
+Africa/Kampala - EAT - EAT
+Africa/Khartoum - EAT - EAT
+Africa/Mogadishu - EAT - EAT
+Africa/Nairobi - EAT - EAT
+Antarctica/Syowa - SYOT - SYOT
+Asia/Aden - AST - AST
+Asia/Baghdad - ADT - AST
+Asia/Bahrain - AST - AST
+Asia/Kuwait - AST - AST
+Asia/Qatar - AST - AST
+Asia/Riyadh - AST - AST
+EAT - EAT - EAT
+Etc/GMT-3 - GMT+03:00 - GMT+03:00
+Europe/Moscow - MSD - MSK
+Indian/Antananarivo - EAT - EAT
+Indian/Comoro - EAT - EAT
+Indian/Mayotte - EAT - EAT
+W-SU - MSD - MSK
+Asia/Riyadh87 - GMT+03:07 - GMT+03:07
+Asia/Riyadh88 - GMT+03:07 - GMT+03:07
+Asia/Riyadh89 - GMT+03:07 - GMT+03:07
+Mideast/Riyadh87 - GMT+03:07 - GMT+03:07
+Mideast/Riyadh88 - GMT+03:07 - GMT+03:07
+Mideast/Riyadh89 - GMT+03:07 - GMT+03:07
+Asia/Tehran - IRST - IRST
+Iran - IRST - IRST
+Asia/Baku - AZST - AZT
+Asia/Dubai - GST - GST
+Asia/Muscat - GST - GST
+Asia/Tbilisi - GET - GET
+Asia/Yerevan - AMST - AMT
+Etc/GMT-4 - GMT+04:00 - GMT+04:00
+Europe/Samara - SAMST - SAMT
+Indian/Mahe - SCT - SCT
+Indian/Mauritius - MUT - MUT
+Indian/Reunion - RET - RET
+NET - AMST - AMT
+Asia/Kabul - AFT - AFT
+Asia/Aqtau - AQTT - AQTT
+Asia/Aqtobe - AQTT - AQTT
+Asia/Ashgabat - TMT - TMT
+Asia/Ashkhabad - TMT - TMT
+Asia/Dushanbe - TJT - TJT
+Asia/Karachi - PKT - PKT
+Asia/Oral - ORAT - ORAT
+Asia/Samarkand - UZT - UZT
+Asia/Tashkent - UZT - UZT
+Asia/Yekaterinburg - YEKST - YEKT
+Etc/GMT-5 - GMT+05:00 - GMT+05:00
+Indian/Kerguelen - TFT - TFT
+Indian/Maldives - MVT - MVT
+PLT - PKT - PKT
+Asia/Calcutta - IST - IST
+Asia/Colombo - LKT - LKT
+IST - IST - IST
+Asia/Katmandu - NPT - NPT
+Antarctica/Mawson - MAWT - MAWT
+Antarctica/Vostok - VOST - VOST
+Asia/Almaty - ALMT - ALMT
+Asia/Bishkek - KGT - KGT
+Asia/Dacca - BDT - BDT
+Asia/Dhaka - BDT - BDT
+Asia/Novosibirsk - NOVST - NOVT
+Asia/Omsk - OMSST - OMST
+Asia/Qyzylorda - QYZT - QYZT
+Asia/Thimbu - BTT - BTT
+Asia/Thimphu - BTT - BTT
+BST - BDT - BDT
+Etc/GMT-6 - GMT+06:00 - GMT+06:00
+Indian/Chagos - IOT - IOT
+Asia/Rangoon - MMT - MMT
+Indian/Cocos - CCT - CCT
+Antarctica/Davis - DAVT - DAVT
+Asia/Bangkok - ICT - ICT
+Asia/Hovd - HOVST - HOVT
+Asia/Jakarta - WIT - WIT
+Asia/Krasnoyarsk - KRAST - KRAT
+Asia/Phnom_Penh - ICT - ICT
+Asia/Pontianak - WIT - WIT
+Asia/Saigon - ICT - ICT
+Asia/Vientiane - ICT - ICT
+Etc/GMT-7 - GMT+07:00 - GMT+07:00
+Indian/Christmas - CXT - CXT
+VST - ICT - ICT
+Antarctica/Casey - WST - WST
+Asia/Brunei - BNT - BNT
+Asia/Chongqing - CST - CST
+Asia/Chungking - CST - CST
+Asia/Harbin - CST - CST
+Asia/Hong_Kong - HKT - HKT
+Asia/Irkutsk - IRKST - IRKT
+Asia/Kashgar - CST - CST
+Asia/Kuala_Lumpur - MYT - MYT
+Asia/Kuching - MYT - MYT
+Asia/Macao - CST - CST
+Asia/Macau - CST - CST
+Asia/Makassar - CIT - CIT
+Asia/Manila - PHT - PHT
+Asia/Shanghai - CST - CST
+Asia/Singapore - SGT - SGT
+Asia/Taipei - CST - CST
+Asia/Ujung_Pandang - CIT - CIT
+Asia/Ulaanbaatar - ULAST - ULAT
+Asia/Ulan_Bator - ULAST - ULAT
+Asia/Urumqi - CST - CST
+Australia/Perth - WST - WST
+Australia/West - WST - WST
+CTT - CST - CST
+Etc/GMT-8 - GMT+08:00 - GMT+08:00
+Hongkong - HKT - HKT
+PRC - CST - CST
+Singapore - SGT - SGT
+Asia/Choibalsan - CHOST - CHOT
+Asia/Dili - TPT - TPT
+Asia/Jayapura - EIT - EIT
+Asia/Pyongyang - KST - KST
+Asia/Seoul - KST - KST
+Asia/Tokyo - JST - JST
+Asia/Yakutsk - YAKST - YAKT
+Etc/GMT-9 - GMT+09:00 - GMT+09:00
+JST - JST - JST
+Japan - JST - JST
+Pacific/Palau - PWT - PWT
+ROK - KST - KST
+ACT - CST - CST
+Australia/Adelaide - CST - CST
+Australia/Broken_Hill - CST - CST
+Australia/Darwin - CST - CST
+Australia/North - CST - CST
+Australia/South - CST - CST
+Australia/Yancowinna - CST - CST
+AET - EST - EST
+Antarctica/DumontDUrville - DDUT - DDUT
+Asia/Sakhalin - SAKST - SAKT
+Asia/Vladivostok - VLAST - VLAT
+Australia/ACT - EST - EST
+Australia/Brisbane - EST - EST
+Australia/Canberra - EST - EST
+Australia/Currie - EST - EST
+Australia/Hobart - EST - EST
+Australia/Lindeman - EST - EST
+Australia/Melbourne - EST - EST
+Australia/NSW - EST - EST
+Australia/Queensland - EST - EST
+Australia/Sydney - EST - EST
+Australia/Tasmania - EST - EST
+Australia/Victoria - EST - EST
+Etc/GMT-10 - GMT+10:00 - GMT+10:00
+Pacific/Guam - ChST - ChST
+Pacific/Port_Moresby - PGT - PGT
+Pacific/Saipan - ChST - ChST
+Pacific/Truk - TRUT - TRUT
+Pacific/Yap - YAPT - YAPT
+Australia/LHI - LHST - LHST
+Australia/Lord_Howe - LHST - LHST
+Asia/Magadan - MAGST - MAGT
+Etc/GMT-11 - GMT+11:00 - GMT+11:00
+Pacific/Efate - VUT - VUT
+Pacific/Guadalcanal - SBT - SBT
+Pacific/Kosrae - KOST - KOST
+Pacific/Noumea - NCT - NCT
+Pacific/Ponape - PONT - PONT
+SST - SBT - SBT
+Pacific/Norfolk - NFT - NFT
+Antarctica/McMurdo - NZDT - NZST
+Antarctica/South_Pole - NZDT - NZST
+Asia/Anadyr - ANAST - ANAT
+Asia/Kamchatka - PETST - PETT
+Etc/GMT-12 - GMT+12:00 - GMT+12:00
+Kwajalein - MHT - MHT
+NST - NZDT - NZST
+NZ - NZDT - NZST
+Pacific/Auckland - NZDT - NZST
+Pacific/Fiji - FJT - FJT
+Pacific/Funafuti - TVT - TVT
+Pacific/Kwajalein - MHT - MHT
+Pacific/Majuro - MHT - MHT
+Pacific/Nauru - NRT - NRT
+Pacific/Tarawa - GILT - GILT
+Pacific/Wake - WAKT - WAKT
+Pacific/Wallis - WFT - WFT
+NZ-CHAT - CHADT - CHAST
+Pacific/Chatham - CHADT - CHAST
+Etc/GMT-13 - GMT+13:00 - GMT+13:00
+Pacific/Enderbury - PHOT - PHOT
+Pacific/Tongatapu - TOT - TOT
+Etc/GMT-14 - GMT+14:00 - GMT+14:00
+Pacific/Kiritimati - LINT - LINT
+
+*/
\ No newline at end of file
diff --git a/sf/saxon/expr/number/NumberFormatter.java b/sf/saxon/expr/number/NumberFormatter.java
new file mode 100644
index 0000000..8940527
--- /dev/null
+++ b/sf/saxon/expr/number/NumberFormatter.java
@@ -0,0 +1,172 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.number;
+
+import net.sf.saxon.lib.Numberer;
+import net.sf.saxon.regex.Categories;
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.z.IntPredicate;
+import net.sf.saxon.z.IntUnionPredicate;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class NumberFormatter defines a method to format a ArrayList of integers as a character
+ * string according to a supplied format specification.
+ * @author Michael H. Kay
+ */
+
+public class NumberFormatter implements Serializable {
+
+ private ArrayList<UnicodeString> formatTokens;
+ private ArrayList<UnicodeString> punctuationTokens;
+ private boolean startsWithPunctuation;
+
+ /**
+ * Tokenize the format pattern.
+ * @param format the format specification. Contains one of the following values:<ul>
+ * <li>"1": conventional decimal numbering</li>
+ * <li>"a": sequence a, b, c, ... aa, ab, ac, ...</li>
+ * <li>"A": sequence A, B, C, ... AA, AB, AC, ...</li>
+ * <li>"i": sequence i, ii, iii, iv, v ...</li>
+ * <li>"I": sequence I, II, III, IV, V, ...</li>
+ * </ul>
+ * This symbol may be preceded and followed by punctuation (any other characters) which is
+ * copied to the output string.
+ */
+
+ public void prepare(String format) {
+
+ // Tokenize the format string into alternating alphanumeric and non-alphanumeric tokens
+
+ if (format.length()==0) {
+ format="1";
+ }
+
+ formatTokens = new ArrayList<UnicodeString>(10);
+ punctuationTokens = new ArrayList<UnicodeString>(10);
+
+ UnicodeString uFormat = UnicodeString.makeUnicodeString(format);
+ int len = uFormat.length();
+ int i=0;
+ int t;
+ boolean first = true;
+ startsWithPunctuation = true;
+
+ while (i<len) {
+ int c = uFormat.charAt(i);
+ t=i;
+ while (isLetterOrDigit(c)) {
+ i++;
+ if (i==len) break;
+ c = uFormat.charAt(i);
+ }
+ if (i>t) {
+ UnicodeString tok = uFormat.substring(t, i);
+ formatTokens.add(tok);
+ if (first) {
+ punctuationTokens.add(UnicodeString.makeUnicodeString("."));
+ startsWithPunctuation = false;
+ first = false;
+ }
+ }
+ if (i==len) break;
+ t=i;
+ c = uFormat.charAt(i);
+ while (!isLetterOrDigit(c)) {
+ first = false;
+ i++;
+ if (i==len) break;
+ c = uFormat.charAt(i);
+ }
+ if (i>t) {
+ UnicodeString sep = uFormat.substring(t, i);
+ punctuationTokens.add(sep);
+ }
+ }
+
+ if (formatTokens.isEmpty()) {
+ formatTokens.add(UnicodeString.makeUnicodeString("1"));
+ if (punctuationTokens.size() == 1) {
+ punctuationTokens.add(punctuationTokens.get(0));
+ }
+ }
+
+ }
+
+ /**
+ * Determine whether a (possibly non-BMP) character is a letter or digit.
+ * @param c the codepoint of the character to be tested
+ * @return true if this is a number or letter as defined in the XSLT rules for xsl:number pictures.
+ */
+
+ public static boolean isLetterOrDigit(int c) {
+ if (c <= 0x7F) {
+ // Fast path for ASCII characters
+ return (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A);
+ } else {
+ return alphanumeric.matches(c);
+ }
+ }
+
+ private static IntPredicate alphanumeric =
+ new IntUnionPredicate(Categories.getCategory("N"), Categories.getCategory("L"));
+
+ /**
+ * Format a list of numbers.
+ * @param numbers the numbers to be formatted (a sequence of integer values; it may also contain
+ * preformatted strings as part of the error recovery fallback)
+ * @return the formatted output string.
+ */
+
+ public CharSequence format(List numbers, int groupSize, String groupSeparator,
+ String letterValue, String ordinal, /*@NotNull*/ Numberer numberer) {
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+ int num = 0;
+ int tok = 0;
+ // output first punctuation token
+ if (startsWithPunctuation) {
+ sb.append(punctuationTokens.get(tok));
+ }
+ // output the list of numbers
+ while (num<numbers.size()) {
+ if (num>0) {
+ if (tok==0 && startsWithPunctuation) {
+ // The first punctuation token isn't a separator if it appears before the first
+ // formatting token. Such a punctuation token is used only once, at the start.
+ sb.append(".");
+ } else {
+ sb.append(punctuationTokens.get(tok));
+ }
+ }
+ Object o = numbers.get(num++);
+ String s;
+ if (o instanceof Long) {
+ long nr = (Long) o;
+ RegularGroupFormatter rgf = new RegularGroupFormatter(groupSize, groupSeparator, UnicodeString.EMPTY_STRING);
+ s = numberer.format(nr, formatTokens.get(tok), rgf, letterValue, ordinal);
+ } else {
+ s = o.toString();
+ }
+ sb.append(s);
+ tok++;
+ if (tok==formatTokens.size()) tok--;
+ }
+ // output the final punctuation token
+ if (punctuationTokens.size()>formatTokens.size()) {
+ sb.append(punctuationTokens.get(punctuationTokens.size()-1));
+ }
+ return sb.condense();
+ }
+
+}
+
diff --git a/sf/saxon/expr/number/Numberer_en.java b/sf/saxon/expr/number/Numberer_en.java
new file mode 100644
index 0000000..d3ed420
--- /dev/null
+++ b/sf/saxon/expr/number/Numberer_en.java
@@ -0,0 +1,246 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.number;
+
+/**
+ * Numberer class for the English language.
+ */
+
+public class Numberer_en extends AbstractNumberer {
+
+ // Separator between tens and units. Allows customisation: "twenty five", "twenty-five", "twentyfive",
+ // or "thirty second", "thirty-second", "thirtysecond".
+ private String tensUnitsSeparatorCardinal = " ";
+
+ // Separator between tens and units. Allows customisation: "thirty second", "thirty-second", "thirtysecond".
+ private String tensUnitsSeparatorOrdinal = "-";
+
+ /**
+ * Set the separator to be used between tens and units for cardinal numbers. This allows customization
+ * of the output, for example "thirty six", "thirty-six", or "thirtysix". Default is a single space.
+ * <p>Currently the only way of calling this is from a subclass, which can be nominated by setting
+ * a {@link net.sf.saxon.lib.LocalizerFactory} on the {@link net.sf.saxon.Configuration}</p>
+ *
+ * @param separator the separator to be used between tens and units when cardinal numbers are written
+ * as words.
+ */
+ public void setTensUnitsSeparatorCardinal(String separator) {
+ tensUnitsSeparatorCardinal = separator;
+ }
+
+ /**
+ * Set the separator to be used between tens and units for ordinal numbers. This allows customization
+ * of the output, for example "thirty sixth", "thirty-sixth", or "thirtysixth". Default is a hyphen.
+ * <p>Currently the only way of calling this is from a subclass, which can be nominated by setting
+ * a {@link net.sf.saxon.lib.LocalizerFactory} on the {@link net.sf.saxon.Configuration}</p>
+ * @param separator the separator to be used between tens and units when ordinal numbers are written
+ * as words.
+ */
+
+ public void setTensUnitsSeparatorOrdinal(String separator) {
+ tensUnitsSeparatorOrdinal = separator;
+ }
+
+ /**
+ * Construct the ordinal suffix for a number, for example "st", "nd", "rd"
+ * @param ordinalParam the value of the ordinal attribute (used in non-English
+ * language implementations)
+ * @param number the number being formatted
+ * @return the ordinal suffix to be appended to the formatted number
+ */
+
+ protected String ordinalSuffix(String ordinalParam, long number) {
+ int penult = ((int)(number % 100)) / 10;
+ int ult = (int)(number % 10);
+ if (penult==1) {
+ // e.g. 11th, 12th, 13th
+ return "th";
+ } else {
+ if (ult==1) {
+ return "st";
+ } else if (ult==2) {
+ return "nd";
+ } else if (ult==3) {
+ return "rd";
+ } else {
+ return "th";
+ }
+ }
+ }
+
+ /**
+ * Show the number as words in title case. (We choose title case because
+ * the result can then be converted algorithmically to lower case or upper case).
+ * @param number the number to be formatted
+ * @return the number formatted as English words
+ */
+
+ public String toWords(long number) {
+ if (number >= 1000000000) {
+ long rem = number % 1000000000;
+ return toWords(number / 1000000000) + " Billion" +
+ (rem==0 ? "" : (rem < 100 ? " and " : " ") + toWords(rem));
+ } else if (number >= 1000000) {
+ long rem = number % 1000000;
+ return toWords(number / 1000000) + " Million" +
+ (rem==0 ? "" : (rem < 100 ? " and " : " ") + toWords(rem));
+ } else if (number >= 1000) {
+ long rem = number % 1000;
+ return toWords(number / 1000) + " Thousand" +
+ (rem==0 ? "" : (rem < 100 ? " and " : " ") + toWords(rem));
+ } else if (number >= 100) {
+ long rem = number % 100;
+ return toWords(number / 100) + " Hundred" +
+ (rem==0 ? "" : " and " + toWords(rem));
+ } else {
+ if (number < 20) return englishUnits[(int)number];
+ int rem = (int)(number % 10);
+ return englishTens[(int)number / 10] +
+ (rem==0 ? "" : tensUnitsSeparatorCardinal + englishUnits[rem]);
+ }
+ }
+
+ /**
+ * Show an ordinal number as English words in a requested case (for example, Twentyfirst)
+ * @param ordinalParam the value of the "ordinal" attribute as supplied by the user
+ * @param number the number to be formatted
+ * @param wordCase the required case for example {@link #UPPER_CASE},
+ * {@link #LOWER_CASE}, {@link #TITLE_CASE}
+ * @return the formatted number
+ */
+
+ public String toOrdinalWords(String ordinalParam, long number, int wordCase) {
+ String s;
+ if (number >= 1000000000) {
+ long rem = number % 1000000000;
+ s = toWords(number / 1000000000) + " Billion" +
+ (rem==0 ? "th" : (rem < 100 ? " and " : " ") +
+ toOrdinalWords(ordinalParam, rem, wordCase));
+ } else if (number >= 1000000) {
+ long rem = number % 1000000;
+ s = toWords(number / 1000000) + " Million" +
+ (rem==0 ? "th" : (rem < 100 ? " and " : " ") +
+ toOrdinalWords(ordinalParam, rem, wordCase));
+ } else if (number >= 1000) {
+ long rem = number % 1000;
+ s = toWords(number / 1000) + " Thousand" +
+ (rem==0 ? "th" : (rem < 100 ? " and " : " ") +
+ toOrdinalWords(ordinalParam, rem, wordCase));
+ } else if (number >= 100) {
+ long rem = number % 100;
+ s = toWords(number / 100) + " Hundred" +
+ (rem==0 ? "th" : " and " +
+ toOrdinalWords(ordinalParam, rem, wordCase));
+ } else {
+ if (number < 20) {
+ s = englishOrdinalUnits[(int)number];
+ } else {
+ int rem = (int)(number % 10);
+ if (rem==0) {
+ s = englishOrdinalTens[(int)number / 10];
+ } else {
+ s = englishTens[(int)number / 10] + tensUnitsSeparatorOrdinal + englishOrdinalUnits[rem];
+ }
+ }
+ }
+ if (wordCase == UPPER_CASE) {
+ return s.toUpperCase();
+ } else if (wordCase == LOWER_CASE) {
+ return s.toLowerCase();
+ } else {
+ return s;
+ }
+ }
+
+ private static String[] englishUnits = {
+ "", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine",
+ "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen",
+ "Seventeen", "Eighteen", "Nineteen"};
+
+ private static String[] englishTens = {
+ "", "Ten", "Twenty", "Thirty", "Forty", "Fifty",
+ "Sixty", "Seventy", "Eighty", "Ninety"};
+
+ private static String[] englishOrdinalUnits = {
+ "", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth", "Ninth",
+ "Tenth", "Eleventh", "Twelfth", "Thirteenth", "Fourteenth", "Fifteenth", "Sixteenth",
+ "Seventeenth", "Eighteenth", "Nineteenth"};
+
+ private static String[] englishOrdinalTens = {
+ "", "Tenth", "Twentieth", "Thirtieth", "Fortieth", "Fiftieth",
+ "Sixtieth", "Seventieth", "Eightieth", "Ninetieth"};
+
+
+ /**
+ * Get a month name or abbreviation
+ * @param month The month number (1=January, 12=December)
+ * @param minWidth The minimum number of characters
+ * @param maxWidth The maximum number of characters
+ */
+
+ public String monthName(int month, int minWidth, int maxWidth) {
+ String name = englishMonths[month-1];
+ if (maxWidth < 3) {
+ maxWidth = 3;
+ }
+ if (name.length() > maxWidth) {
+ name = name.substring(0, maxWidth);
+ }
+ while (name.length() < minWidth) {
+ name = name + ' ';
+ }
+ return name;
+ }
+
+ private static String[] englishMonths = {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ };
+
+ /**
+ * Get a day name or abbreviation
+ * @param day The day of the week (1=Monday, 7=Sunday)
+ * @param minWidth The minimum number of characters
+ * @param maxWidth The maximum number of characters
+ */
+
+ public String dayName(int day, int minWidth, int maxWidth) {
+ String name = englishDays[day-1];
+ if (maxWidth < 2) {
+ maxWidth = 2;
+ }
+ if (name.length() > maxWidth) {
+ name = englishDayAbbreviations[day-1];
+ if (name.length() > maxWidth) {
+ name = name.substring(0, maxWidth);
+ }
+ }
+ while (name.length() < minWidth) {
+ name = name + ' ';
+ }
+ if (minWidth==1 && maxWidth==2) {
+ // special case
+ name = name.substring(0, minUniqueDayLength[day-1]);
+ }
+ return name;
+ }
+
+ private static String[] englishDays = {
+ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
+ };
+
+ private static String[] englishDayAbbreviations = {
+ "Mon", "Tues", "Weds", "Thurs", "Fri", "Sat", "Sun"
+ };
+
+ /*@NotNull*/ private static int[] minUniqueDayLength = {
+ 1, 2, 1, 2, 1, 2, 2
+ };
+
+
+}
diff --git a/sf/saxon/expr/number/NumericGroupFormatter.java b/sf/saxon/expr/number/NumericGroupFormatter.java
new file mode 100644
index 0000000..40a8ba5
--- /dev/null
+++ b/sf/saxon/expr/number/NumericGroupFormatter.java
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.number;
+
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.io.Serializable;
+
+/**
+ * A NumericGroupFormatter is responsible for insertion of grouping separators
+ * into a formatted number (for example, reformatting "1234" as "1,234").
+ */
+
+public abstract class NumericGroupFormatter implements Serializable {
+
+ protected UnicodeString adjustedPicture;
+
+ /**
+ * Get the adjusted (preprocessed) picture
+ * @return the adjusted picture
+ */
+
+ public UnicodeString getAdjustedPicture() {
+ return adjustedPicture;
+ }
+
+ /**
+ * Reformat a number to add grouping separators
+ * @param value a buffer holding the number to be reformatted
+ * @return the reformatted number
+ */
+
+ public abstract String format(FastStringBuffer value);
+
+ /**
+ * Get the grouping separator to be used, as a Unicode codepoint. If more than one is used, return the last.
+ * If no grouping separators are used, return null
+ * @return the grouping separator
+ */
+
+ /*@Nullable*/ public abstract String getSeparator();
+}
+
diff --git a/sf/saxon/expr/number/RegularGroupFormatter.java b/sf/saxon/expr/number/RegularGroupFormatter.java
new file mode 100644
index 0000000..0092e48
--- /dev/null
+++ b/sf/saxon/expr/number/RegularGroupFormatter.java
@@ -0,0 +1,67 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.number;
+
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+/**
+ * A RegularGroupFormatter is a NumericGroupFormatter that inserts a separator
+ * at constant intervals through a number: for example, a comma after every three
+ * digits counting from the right.
+ */
+
+public class RegularGroupFormatter extends NumericGroupFormatter {
+
+ private int groupSize;
+ private String groupSeparator;
+
+ /**
+ * Create a RegularGroupFormatter
+ * @param grpSize the grouping size. If zero, no grouping separators are inserted
+ * @param grpSep the grouping separator (normally but not necessarily a single character)
+ * @param adjustedPicture The picture, adjusted to conform to the rules of the xsl:number function,
+ * which means the picture supplied to format-integer minus any modifiers, and minus grouping separators
+ * and optional-digit signs
+ *
+ */
+
+ public RegularGroupFormatter(int grpSize, String grpSep, UnicodeString adjustedPicture){
+ groupSize = grpSize;
+ groupSeparator = grpSep;
+ this.adjustedPicture = adjustedPicture;
+ }
+
+ @Override
+ public String format(/*@NotNull*/ FastStringBuffer value) {
+ if (groupSize>0 && groupSeparator.length() > 0) {
+ UnicodeString valueEx = UnicodeString.makeUnicodeString(value);
+ FastStringBuffer temp = new FastStringBuffer(FastStringBuffer.TINY);
+ for (int i=valueEx.length()-1, j=0; i>=0; i--, j++) {
+ if (j!=0 && (j % groupSize) == 0) {
+ temp.prepend(groupSeparator);
+ }
+ temp.prependWideChar(valueEx.charAt(i));
+ }
+ return temp.toString();
+ } else {
+ return value.toString();
+ }
+ }
+
+ /**
+ * Get the grouping separator to be used
+ *
+ * @return the grouping separator
+ */
+ @Override
+ public String getSeparator() {
+ return groupSeparator;
+ }
+}
+
diff --git a/sf/saxon/expr/number/package.html b/sf/saxon/expr/number/package.html
new file mode 100644
index 0000000..0f7c8f8
--- /dev/null
+++ b/sf/saxon/expr/number/package.html
@@ -0,0 +1,51 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.number</title>
+</head>
+
+<body>
+
+<p>This package provides classes associated with numbering and the <code>xsl:number</code> element. </p>
+
+<p>It is possible to extend the range of numberings available by providing a Numberer
+for a specific language. This must be registered with the <code>Configuration</code>.
+In earlier releases, there was a fixed relationship between the language and the
+ implementing class (the Numberer was always named Numberer_xx where xx is the language code,
+corresponding to the value of the lang attribute in <code>xsl:number</code>). From Saxon 9.2,
+this relationship no longer exists.</p>
+
+<p>These classes also include code to support the localization of dates as defined
+in the XSLT <code>format-dateTime()</code> group of functions.</p>
+
+<p>The class <code>Numberer_en</code> provides the standard numbering options. As well as the
+format tokens defined in the XSLT 1.0 specification (for example, "1", "001", "a", "i") it supports
+other numbering options including:</p>
+
+<li>
+<ul>Greek upper and lower case letters</ul>
+<ul>Cyrillic upper and lower case letters</ul>
+<ul>Hebrew letters</ul>
+<ul>Japanese: Hiragana-A, Hiragana-B, Katakana-A, or Katakana-B letters, and Kanji digits</ul>
+<ul>English words: the format token "one" produces numbers such as "twenty five"</ul>
+</li>
+
+<p>Localizations for a number of European languages are provided in package <code>net.sf.saxon.option.local</code>.
+In Saxon-PE and Saxon-EE these are issued in binary form as part of the Saxon JAR. For Saxon-HE, they are
+issued only in source code form.</p>
+
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+12 June 2009</i></p>
+</body>
+</html>
diff --git a/sf/saxon/expr/package.html b/sf/saxon/expr/package.html
new file mode 100644
index 0000000..aa26bd2
--- /dev/null
+++ b/sf/saxon/expr/package.html
@@ -0,0 +1,67 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.expr</title>
+</head>
+
+<body>
+
+<p>This package provides classes associated with XPath expression handling. Generally, these
+classes are not intended to be used directly by user-written applications; the API for XPath
+evaluation is now provided by classes in the package <code>net.sf.saxon.xpath</code></p>
+
+<p>The principal classes are:</p>
+
+<p><b>Expression</b>:<br>
+This represents an XPath Expression. There is a static method Expression.make() which is
+used to construct an Expression from a String (it is a factory method rather than a
+ constructor, because it typically returns some subclass of Expression according
+ to the syntax supplied). Subclasses of Expression represent different kinds of expression
+ such as StringExpression and BooleanExpression. What they all have in common is an evaluate()
+ method, which evaluates the expression in a given context to yield a Value, and an iterate() method,
+ which treats the result of the expression as a sequence, and iterates over the items in the sequence.</p>
+
+<p><b>ExpressionParser</b>:<br>
+This class does the work of parsing both Expressions and Patterns. Applications should not call
+it directly. It uses the class Tokenizer for lexical analysis.</p>
+
+<p><b>StaticContext</b>:<br>
+This interface defines the information available at the time an expression is being parsed. This
+includes the names of variables, the bindings of namespace prefixes, the functions that are available,
+the names of collating sequences, and so on. When an XPath expression appears in a stylesheet, the
+class <code>net.sf.saxon.style.ExpressionContext</code> provides the StaticContext. For a free-standing XPath
+expression, the class <code>net.sf.saxon.xpath.StandaloneContext</code> is available, though a user-written
+class that implements <code>StaticContext</code> can also be used.</p>
+
+<p><b>XPathContext</b>:<br>
+This class defines the context information available at run-time, for example the context node, position,
+and size. (It actually does this by wrapping a SequenceIterator that represents the current sequence.)
+When expressions are used during an XSLT transformation, the XPathContext also provides access to the
+Controller, which contains all the context information about XSLT processing (for example the current template,
+the current group, and so on).</p>
+
+<p>Most of the classes in this package represent individual syntactic constructs found in XPath or XQuery expressions:
+for example <code>ValueComparison</code>, <code>UserFunctionCall</code>, and <code>RootExpression</code>. The objects
+that instantiate these classes form the nodes on the Abstract Syntax Tree constructed by the compiler and modified
+by the optimizer; at run-time their methods are used to drive a pipelined evaluation of the expression.</p>
+
+<p>The distinction between the compiled form of XSLT instructions and the compiled form of XPath expressions has
+become blurred. Generally, the compiled form of instructions is in the package <code>net.sf.saxon.instruct</code>
+(this includes expressions in XQuery that are equivalent to XSLT instructions, for example element constructors).
+However, some constructs such as conditional expressions now exist in both languages and may generate the same
+run-time constructs.</p>
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/expr/parser/CodeInjector.java b/sf/saxon/expr/parser/CodeInjector.java
new file mode 100644
index 0000000..7271b94
--- /dev/null
+++ b/sf/saxon/expr/parser/CodeInjector.java
@@ -0,0 +1,46 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.flwor.Clause;
+import net.sf.saxon.om.StructuredQName;
+
+/**
+ * A code injector can be used to add code to the expression tree (for example, diagnostic tracing code)
+ * during the process of parsing and tree construction
+ */
+public interface CodeInjector {
+
+ /**
+ * Wrap an expression in a diagnostic expression.
+ *
+ * @param exp the expression to be wrapped
+ * @param env the static context
+ * @param construct integer constant identifying the kind of construct
+ * @param qName the name of the construct (if applicable)
+ * @return a replacement for the original expression (or the original expression unchanged). Normally
+ * the new expression will collect or output some diagnostic information and then invoke the original
+ * expression. However this is not required; the new expression could be a complete replacement for the
+ * original.
+ */
+
+ public Expression inject(Expression exp, StaticContext env, int construct, StructuredQName qName);
+
+ /**
+ * Insert a tracing clause into the pipeline of clauses that evaluates a FLWOR expression
+ * @param target the clause whose execution is being traced
+ * @param env the static context of the containing FLWOR expression
+ * @param container the container of the FLWOR expression (providing location information)
+ */
+
+ public Clause injectClause(Clause target, StaticContext env, Container container);
+}
+
diff --git a/sf/saxon/expr/parser/ExpressionLocation.java b/sf/saxon/expr/parser/ExpressionLocation.java
new file mode 100644
index 0000000..4917773
--- /dev/null
+++ b/sf/saxon/expr/parser/ExpressionLocation.java
@@ -0,0 +1,193 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.event.SaxonLocator;
+import net.sf.saxon.expr.instruct.LocationMap;
+import org.xml.sax.Locator;
+
+import javax.xml.transform.SourceLocator;
+import java.io.Serializable;
+
+/**
+ * Class to hold details of the location of an expression, of an error in a source file, etc.
+ */
+
+public class ExpressionLocation implements SaxonLocator, Serializable {
+
+ private String systemId;
+ private int lineNumber;
+ private int columnNumber = -1;
+
+ /**
+ * Create an ExpressionLocation
+ */
+
+ public ExpressionLocation() {}
+
+ /**
+ * Create an ExpressionLocation, taking the data from a supplied JAXP SourceLocator
+ * @param loc the JAXP SourceLocator
+ */
+
+ public ExpressionLocation(SourceLocator loc) {
+ systemId = loc.getSystemId();
+ lineNumber = loc.getLineNumber();
+ columnNumber = loc.getColumnNumber();
+ }
+
+ /**
+ * Create an ExpressionLocation, taking the data from a supplied SAX Locator
+ * @param loc the SAX Locator
+ */
+
+ public static ExpressionLocation makeFromSax(Locator loc) {
+ return new ExpressionLocation(loc.getSystemId(), loc.getLineNumber(), loc.getColumnNumber());
+ }
+
+ /**
+ * Create an ExpressionLocation, taking the data from a supplied locationId along with a
+ * LocationProvider to interpret its meaning
+ * @param provider the LocationProvider
+ * @param locationId the locationId
+ */
+
+ public ExpressionLocation(LocationProvider provider, long locationId) {
+ systemId = provider.getSystemId(locationId);
+ lineNumber = provider.getLineNumber(locationId);
+ columnNumber = provider.getColumnNumber(locationId);
+ }
+
+ /**
+ * Create an ExpressionLocation corresponding to a given module, line number, and column number
+ * @param systemId the module URI
+ * @param lineNumber the line number
+ * @param columnNumber the column number
+ */
+
+ public ExpressionLocation(String systemId, int lineNumber, int columnNumber) {
+ this.systemId = systemId;
+ this.lineNumber = lineNumber;
+ this.columnNumber = columnNumber;
+ }
+
+ /**
+ * Get the system ID (the module URI)
+ * @return the system ID
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Get the Public ID
+ * @return always null in this implementation
+ */
+
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Get the line number
+ * @return the line number
+ */
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Get the column number
+ * @return the column number
+ */
+
+ public int getColumnNumber() {
+ return columnNumber;
+ }
+
+ /**
+ * Set the systemId (the module URI)
+ * @param systemId the systemId
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Set the line number
+ * @param lineNumber the line number within the module
+ */
+
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Set the column number
+ * @param columnNumber the column number
+ */
+
+ public void setColumnNumber(int columnNumber) {
+ this.columnNumber = columnNumber;
+ }
+
+ /**
+ * Get the system Id corresponding to a given location Id
+ * @param locationId the location Id
+ * @return the system Id
+ */
+
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ /**
+ * Get the line number corresponding to a given location Id
+ * @param locationId the location Id
+ * @return the line number
+ */
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+ /**
+ * Construct an object holding location information for a validation error message
+ * @param locationId The locationId as supplied with an event such as startElement or attribute
+ * @param locationProvider The object that understands how to interpret the locationId
+ * @return a SaxonLocator containing the location information
+ */
+ public static SaxonLocator getSourceLocator(long locationId, LocationProvider locationProvider) {
+ SaxonLocator locator;
+ if (locationProvider instanceof LocationMap && locationId != 0) {
+ // this is typically true when validating output documents
+ ExpressionLocation loc = new ExpressionLocation();
+ loc.setLineNumber(locationProvider.getLineNumber(locationId));
+ loc.setSystemId(locationProvider.getSystemId(locationId));
+ locator = loc;
+ } else if (locationProvider instanceof SaxonLocator) {
+ // this is typically true when validating input documents
+ locator = (SaxonLocator)locationProvider;
+ } else {
+ ExpressionLocation loc = new ExpressionLocation();
+ loc.setLineNumber(locationProvider.getLineNumber(locationId));
+ loc.setSystemId(locationProvider.getSystemId(locationId));
+ locator = loc;
+ }
+ return locator;
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/parser/ExpressionParser.java b/sf/saxon/expr/parser/ExpressionParser.java
new file mode 100644
index 0000000..394d6d5
--- /dev/null
+++ b/sf/saxon/expr/parser/ExpressionParser.java
@@ -0,0 +1,3176 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.expr.CastableToUnion;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.flwor.Clause;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.functions.*;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.query.Annotation;
+import net.sf.saxon.query.QueryModule;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.z.IntArraySet;
+import net.sf.saxon.z.IntSet;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Stack;
+
+import static net.sf.saxon.type.BuiltInAtomicType.*;
+
+/**
+ * Parser for XPath expressions and XSLT patterns.
+ * <p/>
+ * This code was originally inspired by James Clark's xt but has been totally rewritten (several times)
+ * <p/>
+ * The base class handles parsing of XPath 2.0 and XPath 3.0 syntax (switched by a languageVersion variable).
+ * Subclasses refine this to handle XQuery syntax (1.0 and 3.0) and XQuery Update syntax.
+ *
+ * @author Michael Kay
+ */
+
+
+public class ExpressionParser {
+
+ protected Tokenizer t;
+ protected StaticContext env;
+ protected Stack<Binding> rangeVariables = new Stack<Binding>();
+ // The stack holds a list of range variables that are in scope.
+ // Each entry on the stack is a Binding object containing details
+ // of the variable.
+
+ protected Container defaultContainer;
+ protected NameChecker nameChecker;
+
+ protected boolean allowXPath30Syntax = false;
+
+ protected boolean scanOnly = false;
+ // scanOnly is set to true while attributes in direct element constructors
+ // are being processed. We need to parse enclosed expressions in the attribute
+ // in order to find the end of the attribute value, but we don't yet know the
+ // full namespace context at this stage.
+
+ /*@Nullable*/
+ protected CodeInjector codeInjector = null;
+
+ protected int language = XPATH; // know which language we are parsing, for diagnostics
+ public static final int XPATH = 0;
+ public static final int XSLT_PATTERN = 1;
+ public static final int SEQUENCE_TYPE = 2;
+ public static final int XQUERY = 3;
+
+ protected DecimalValue languageVersion = DecimalValue.TWO;
+ protected int catchDepth = 0;
+
+ /**
+ * Create an expression parser
+ */
+
+ public ExpressionParser() {
+ }
+
+ /**
+ * Set a CodeInjector which can be used to modify or wrap expressions on the tree
+ * as the expression is parsed and the tree is constructed. This is typically used
+ * to add tracing code.
+ *
+ * @param injector the code injector to be used
+ */
+
+ public void setCodeInjector(/*@Nullable*/ CodeInjector injector) {
+ this.codeInjector = injector;
+ }
+
+ /**
+ * Set a CodeInjector which can be used to modify or wrap expressions on the tree
+ * as the expression is parsed and the tree is constructed. This is typically used
+ * to add tracing code.
+ *
+ * @return the code injector in use, if any; or null otherwise
+ */
+
+ /*@Nullable*/
+ public CodeInjector getCodeInjector() {
+ return codeInjector;
+ }
+
+ /**
+ * Get the tokenizer (the lexical analyzer)
+ *
+ * @return the tokenizer (the lexical analyzer)
+ */
+
+ public Tokenizer getTokenizer() {
+ return t;
+ }
+
+ /**
+ * Get the static context used by this expression parser
+ *
+ * @return the static context
+ */
+
+ public StaticContext getStaticContext() {
+ return env;
+ }
+
+ /**
+ * Set the default container for newly constructed expressions
+ *
+ * @param container the default container
+ */
+
+ public void setDefaultContainer(Container container) {
+ this.defaultContainer = container;
+ }
+
+ /**
+ * Get the default container for newly constructed expressions
+ *
+ * @return the default container
+ */
+
+ public Container getDefaultContainer() {
+ return defaultContainer;
+ }
+
+ /**
+ * Get the name checker used by this parser
+ *
+ * @return the name checker (specific to XML 1.0o
+ */
+
+ public NameChecker getNameChecker() {
+ return nameChecker;
+ }
+
+ /**
+ * Read the next token, catching any exception thrown by the tokenizer
+ *
+ * @throws XPathException if an invalid token is found
+ */
+
+ public void nextToken() throws XPathException {
+ try {
+ t.next();
+ if ((t.currentToken == Token.NAME || t.currentToken == Token.FUNCTION) &&
+ t.currentTokenValue.startsWith("{")) {
+ if (allowXPath30Syntax) {
+ t.currentTokenValue = normalizeEQName(t.currentTokenValue);
+ } else {
+ throw new XPathException(
+ "The expanded QName syntax \"uri\":local is not allowed in this version of XPath/XQuery");
+ }
+ }
+ } catch (XPathException err) {
+ grumble(err.getMessage());
+ }
+ }
+
+ /**
+ * Expect a given token; fail if the current token is different. Note that this method
+ * does not read any tokens.
+ *
+ * @param token the expected token
+ * @throws XPathException if the current token is not the expected
+ * token
+ */
+
+ public void expect(int token) throws XPathException {
+ if (t.currentToken != token) {
+ grumble("expected \"" + Token.tokens[token] +
+ "\", found " + currentTokenDisplay());
+ }
+ }
+
+ /**
+ * Report a syntax error (a static error with error code XPST0003)
+ *
+ * @param message the error message
+ * @throws XPathException always thrown: an exception containing the
+ * supplied message
+ */
+
+ public void grumble(String message) throws XPathException {
+ grumble(message, (language == XSLT_PATTERN ? "XTSE0340" : "XPST0003"));
+ }
+
+ /**
+ * Report a static error
+ *
+ * @param message the error message
+ * @param errorCode the error code
+ * @throws XPathException always thrown: an exception containing the
+ * supplied message
+ */
+
+ public void grumble(String message, String errorCode) throws XPathException {
+ grumble(message, new StructuredQName("", NamespaceConstant.ERR, errorCode), -1);
+ }
+
+ /**
+ * Report a static error, with location information
+ *
+ * @param message the error message
+ * @param errorCode the error code
+ * @param offset the coded location of the error, or -1 if the location of the current token should be used
+ * @throws XPathException always thrown: an exception containing the
+ * supplied message
+ */
+
+ public void grumble(String message, String errorCode, int offset) throws XPathException {
+ grumble(message, new StructuredQName("", NamespaceConstant.ERR, errorCode), offset);
+ }
+
+ /**
+ * Report a static error
+ *
+ * @param message the error message
+ * @param errorCode the error code
+ * @param offset the coded location of the error, or -1 if the location of the current token should be used
+ * @throws XPathException always thrown: an exception containing the
+ * supplied message
+ */
+
+ protected void grumble(String message, /*@Nullable*/ StructuredQName errorCode, int offset) throws XPathException {
+ if (errorCode == null) {
+ errorCode = new StructuredQName("err", NamespaceConstant.ERR, "XPST0003");
+ }
+ String s = t.recentText(-1);
+ int line;
+ int column;
+ if (offset == -1) {
+ line = t.getLineNumber();
+ column = t.getColumnNumber();
+ } else {
+ line = t.getLineNumber(offset);
+ column = t.getColumnNumber(offset);
+ }
+ String lineInfo = (line == 1 ? "" : ("on line " + line + ' '));
+ String columnInfo = "at char " + column + ' ';
+ String prefix = getLanguage() + " syntax error " + columnInfo + lineInfo +
+ (s.startsWith("...") ? "near" : "in") +
+ ' ' + Err.wrap(s) + ":\n ";
+ XPathException err = new XPathException(message);
+ err.setAdditionalLocationText(prefix);
+ err.setIsStaticError(true);
+ err.setErrorCodeQName(errorCode);
+ throw err;
+ }
+
+ /**
+ * Output a warning message
+ *
+ * @param message the text of the message
+ * @throws XPathException if the message cannot be output
+ */
+
+ protected void warning(/*@NotNull*/ String message) throws XPathException {
+ String s = t.recentText(-1);
+ int line = t.getLineNumber();
+ String lineInfo = (line == 1 ? "" : ("on line " + line + ' '));
+ String prefix = lineInfo +
+ (message.startsWith("...") ? "near" : "in") +
+ ' ' + Err.wrap(s) + ":\n ";
+ env.issueWarning(prefix + message, null);
+ }
+
+ /**
+ * Set the current language (XPath or XQuery, XSLT Pattern, or SequenceType)
+ *
+ * @param language one of the constants {@link #XPATH}, {@link #XQUERY}, {@link #XSLT_PATTERN}, {@link #SEQUENCE_TYPE}
+ * @param version The XPath or XQuery language version. For XQuery the value must be
+ * "1.0" or "3.0; for XPath it must be "2.0" or "3.0". Currently
+ * support for XQuery 3.0 and XPath 3.0 is incomplete: check the release notes.
+ */
+
+ public void setLanguage(int language, DecimalValue version) {
+ switch (language) {
+ case XPATH:
+ case XSLT_PATTERN:
+ case SEQUENCE_TYPE:
+ if (DecimalValue.TWO_POINT_ONE.equals(version)) {
+ // temporarily until things stabilise
+ version = DecimalValue.THREE;
+ }
+ if (!(DecimalValue.TWO.equals(version) || DecimalValue.THREE.equals(version))) {
+ throw new IllegalArgumentException("Unsupported language version " + version);
+ }
+ break;
+ case XQUERY:
+ if (DecimalValue.ONE_POINT_ONE.equals(version)) {
+ // temporarily until things stabilise
+ version = DecimalValue.THREE;
+ }
+ if (!(DecimalValue.ONE.equals(version) || DecimalValue.THREE.equals(version))) {
+ throw new IllegalArgumentException("Unsupported language version " + version);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown language " + language);
+ }
+ this.language = language;
+ this.languageVersion = version;
+ this.allowXPath30Syntax = DecimalValue.THREE.equals(languageVersion);
+ }
+
+ /**
+ * Get the current language (XPath or XQuery)
+ *
+ * @return a string representation of the language being parsed, for use in error messages
+ */
+
+ protected String getLanguage() {
+ switch (language) {
+ case XPATH:
+ return "XPath";
+ case XSLT_PATTERN:
+ return "XSLT Pattern";
+ case SEQUENCE_TYPE:
+ return "SequenceType";
+ case XQUERY:
+ return "XQuery";
+ default:
+ return "XPath";
+ }
+ }
+
+ /**
+ * Display the current token in an error message
+ *
+ * @return the display representation of the token
+ */
+ /*@NotNull*/
+ protected String currentTokenDisplay() {
+ if (t.currentToken == Token.NAME) {
+ return "name \"" + t.currentTokenValue + '\"';
+ } else if (t.currentToken == Token.UNKNOWN) {
+ return "(unknown token)";
+ } else {
+ return '\"' + Token.tokens[t.currentToken] + '\"';
+ }
+ }
+
+ /**
+ * Parse a string representing an expression. This will accept an XPath expression if called on an
+ * ExpressionParser, or an XQuery expression if called on a QueryParser.
+ *
+ * @param expression the expression expressed as a String
+ * @param start offset within the string where parsing is to start
+ * @param terminator character to treat as terminating the expression
+ * @param lineNumber location of the start of the expression, for diagnostics
+ * @param env the static context for the expression
+ * @return an Expression object representing the result of parsing
+ * @throws XPathException if the expression contains a syntax error
+ */
+
+ /*@NotNull*/
+ public Expression parse(String expression, int start, int terminator, int lineNumber, /*@NotNull*/ StaticContext env)
+ throws XPathException {
+ // System.err.println("Parse expression: " + expression);
+ this.env = env;
+
+ //defaultContainer = new TemporaryContainer(env.getLocationMap(), 1);
+ nameChecker = env.getConfiguration().getNameChecker();
+ t = new Tokenizer();
+ customizeTokenizer(t);
+ try {
+ t.tokenize(expression, start, -1, lineNumber);
+ } catch (XPathException err) {
+ grumble(err.getMessage());
+ }
+ Expression exp = parseExpression();
+ if (t.currentToken != terminator) {
+ if (t.currentToken == Token.EOF && terminator == Token.RCURLY) {
+ grumble("Missing curly brace after expression in attribute value template", "XTSE0350");
+ } else {
+ grumble("Unexpected token " + currentTokenDisplay() + " beyond end of expression");
+ }
+ }
+ return exp;
+ }
+
+ /**
+ * Callback to tailor the tokenizer
+ *
+ * @param t the Tokenizer to be customized
+ */
+
+ protected void customizeTokenizer(Tokenizer t) {
+ // do nothing
+ }
+
+
+ /**
+ * Parse a string representing a sequence type
+ *
+ * @param input the string, which should conform to the XPath SequenceType
+ * production
+ * @param env the static context
+ * @return a SequenceType object representing the type
+ * @throws XPathException if any error is encountered
+ */
+
+ public SequenceType parseSequenceType(String input, /*@NotNull*/ StaticContext env) throws XPathException {
+ this.env = env;
+ nameChecker = env.getConfiguration().getNameChecker();
+ language = SEQUENCE_TYPE;
+ t = new Tokenizer();
+ try {
+ t.tokenize(input, 0, -1, 1);
+ } catch (XPathException err) {
+ grumble(err.getMessage());
+ }
+ SequenceType req = parseSequenceType();
+ if (t.currentToken != Token.EOF) {
+ grumble("Unexpected token " + currentTokenDisplay() + " beyond end of SequenceType");
+ }
+ return req;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // EXPRESSIONS //
+ //////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Parse a top-level Expression:
+ * ExprSingle ( ',' ExprSingle )*
+ *
+ * @return the Expression object that results from parsing
+ * @throws XPathException if the expression contains a syntax error
+ */
+
+ /*@NotNull*/
+ public Expression parseExpression() throws XPathException {
+ Expression exp = parseExprSingle();
+ ArrayList<Expression> list = null;
+ while (t.currentToken == Token.COMMA) {
+ // An expression containing a comma often contains many, so we accumulate all the
+ // subexpressions into a list before creating the Block expression which reduces it to an array
+ if (list == null) {
+ list = new ArrayList<Expression>(10);
+ list.add(exp);
+ }
+ nextToken();
+ Expression next = parseExprSingle();
+ setLocation(next);
+ list.add(next);
+ }
+ if (list != null) {
+ exp = Block.makeBlock(list);
+ setLocation(exp);
+ }
+ return exp;
+ }
+
+ /**
+ * Parse an ExprSingle
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@NotNull*/
+ public Expression parseExprSingle() throws XPathException {
+ switch (t.currentToken) {
+ case Token.FOR:
+ case Token.LET:
+ case Token.FOR_SLIDING:
+ case Token.FOR_TUMBLING:
+ return parseFLWORExpression();
+ case Token.SOME:
+ case Token.EVERY:
+ return parseQuantifiedExpression();
+ case Token.IF:
+ return parseIfExpression();
+ case Token.SWITCH:
+ return parseSwitchExpression();
+ case Token.TYPESWITCH:
+ return parseTypeswitchExpression();
+ case Token.VALIDATE:
+ case Token.VALIDATE_STRICT:
+ case Token.VALIDATE_LAX:
+ case Token.VALIDATE_TYPE:
+ return parseValidateExpression();
+ case Token.PRAGMA:
+ return parseExtensionExpression(); // XQuery only
+ case Token.KEYWORD_CURLY:
+ if (t.currentTokenValue.equals("try")) {
+ return parseTryCatchExpression();
+ } else if (t.currentTokenValue.equals("map")) {
+ return parseMapExpression();
+ }
+ // else drop through
+
+ default:
+ return parseBinaryExpression(parseUnaryExpression(), 4);
+ }
+ }
+
+ /**
+ * Parse a binary expression, using operator precedence parsing. This is used
+ * to parse the part of the grammary consisting largely of binary operators
+ * distinguished by precedence: from "or expressions" down to "unary expressions".
+ * Algorithm for the mainstream binary operators is from Wikipedia article
+ * on precedence parsing; operator precedences are from the XQuery specification
+ * appendix B.
+ *
+ * @param lhs Left-hand side "basic expression"
+ * @param minPrecedence the minimum precedence of an operator that is to be treated as not terminating the
+ * current expression
+ * @return the parsed expression
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ public Expression parseBinaryExpression(Expression lhs, int minPrecedence) throws XPathException {
+ while (getCurrentOperatorPrecedence() >= minPrecedence) {
+ int operator = t.currentToken;
+ int prec = getCurrentOperatorPrecedence();
+ switch (operator) {
+ case Token.INSTANCE_OF:
+ case Token.TREAT_AS:
+ nextToken();
+ SequenceType seq = parseSequenceType();
+ lhs = makeSequenceTypeExpression(lhs, operator, seq);
+ setLocation(lhs);
+ if (getCurrentOperatorPrecedence() >= prec) {
+ grumble("Left operand of '" + Token.tokens[t.currentToken] + "' needs parentheses");
+ }
+ break;
+ case Token.CAST_AS:
+ case Token.CASTABLE_AS:
+ nextToken();
+ expect(Token.NAME);
+ SimpleType at = getSimpleType(t.currentTokenValue);
+ if (at == ANY_ATOMIC) {
+ grumble("No value is castable to xs:anyAtomicType", "XPST0080");
+ }
+ if (at == NOTATION) {
+ grumble("No value is castable to xs:NOTATION", "XPST0080");
+ }
+ nextToken();
+ boolean allowEmpty = (t.currentToken == Token.QMARK);
+ if (allowEmpty) {
+ nextToken();
+ }
+ lhs = makeSingleTypeExpression(lhs, operator, at, allowEmpty);
+ setLocation(lhs);
+ if (getCurrentOperatorPrecedence() >= prec) {
+ grumble("Left operand of '" + Token.tokens[t.currentToken] + "' needs parentheses");
+ }
+ break;
+ default:
+ nextToken();
+ Expression rhs = parseUnaryExpression();
+ while (getCurrentOperatorPrecedence() > prec) {
+ rhs = parseBinaryExpression(rhs, getCurrentOperatorPrecedence());
+ }
+ lhs = makeBinaryExpression(lhs, operator, rhs);
+ setLocation(lhs);
+ }
+ }
+ return lhs;
+ }
+
+ private int getCurrentOperatorPrecedence() {
+ switch (t.currentToken) {
+ case Token.OR:
+ return 4;
+ case Token.AND:
+ return 5;
+ case Token.FEQ:
+ case Token.FNE:
+ case Token.FLE:
+ case Token.FLT:
+ case Token.FGE:
+ case Token.FGT:
+ case Token.EQUALS:
+ case Token.NE:
+ case Token.LE:
+ case Token.LT:
+ case Token.GE:
+ case Token.GT:
+ case Token.IS:
+ case Token.PRECEDES:
+ case Token.FOLLOWS:
+ return 6;
+ case Token.CONCAT:
+ return 7;
+ case Token.TO:
+ return 8;
+ case Token.PLUS:
+ case Token.MINUS:
+ return 9;
+ case Token.MULT:
+ case Token.DIV:
+ case Token.IDIV:
+ case Token.MOD:
+ return 10;
+ case Token.UNION:
+ return 11;
+ case Token.INTERSECT:
+ case Token.EXCEPT:
+ return 12;
+ case Token.INSTANCE_OF:
+ return 13;
+ case Token.TREAT_AS:
+ return 14;
+ case Token.CASTABLE_AS:
+ return 15;
+ case Token.CAST_AS:
+ return 16;
+ default:
+ return -1;
+ }
+ }
+
+ /*@NotNull*/
+ private Expression makeBinaryExpression(Expression lhs, int operator, Expression rhs) throws XPathException {
+ switch (operator) {
+ case Token.OR:
+ return new OrExpression(lhs, rhs);
+ case Token.AND:
+ return new AndExpression(lhs, rhs);
+ case Token.FEQ:
+ case Token.FNE:
+ case Token.FLE:
+ case Token.FLT:
+ case Token.FGE:
+ case Token.FGT:
+ return new ValueComparison(lhs, operator, rhs);
+ case Token.EQUALS:
+ case Token.NE:
+ case Token.LE:
+ case Token.LT:
+ case Token.GE:
+ case Token.GT:
+ return new GeneralComparison(lhs, operator, rhs);
+ case Token.IS:
+ case Token.PRECEDES:
+ case Token.FOLLOWS:
+ return new IdentityComparison(lhs, operator, rhs);
+ case Token.TO:
+ return new RangeExpression(lhs, operator, rhs);
+ case Token.CONCAT:
+ if (!env.getXPathLanguageLevel().equals(DecimalValue.THREE)) {
+ grumble("Concatenation operator ('||') requires XPath 3.0 to be enabled");
+ }
+ Expression cc = SystemFunctionCall.makeSystemFunction("concat", new Expression[]{lhs, rhs});
+ assert cc != null;
+ return cc;
+ case Token.PLUS:
+ case Token.MINUS:
+ case Token.MULT:
+ case Token.DIV:
+ case Token.IDIV:
+ case Token.MOD:
+ return new ArithmeticExpression(lhs, operator, rhs);
+ case Token.UNION:
+ case Token.INTERSECT:
+ case Token.EXCEPT:
+ return new VennExpression(lhs, operator, rhs);
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private Expression makeSequenceTypeExpression(Expression lhs, int operator, /*@NotNull*/ SequenceType type) {
+ switch (operator) {
+ case Token.INSTANCE_OF:
+ return new InstanceOfExpression(lhs, type);
+ case Token.TREAT_AS:
+ return TreatExpression.make(lhs, type);
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ }
+
+ private Expression makeSingleTypeExpression(Expression lhs, int operator, /*@NotNull*/ SimpleType type, boolean allowEmpty)
+ throws XPathException {
+ if (type instanceof AtomicType) {
+ switch (operator) {
+ case Token.CASTABLE_AS:
+ CastableExpression castable = new CastableExpression(lhs, (AtomicType) type, allowEmpty);
+ if (lhs instanceof StringLiteral) {
+ castable.setOperandIsStringLiteral(true);
+ }
+ if (type.isNamespaceSensitive()) {
+ castable.setNamespaceResolver(new SavedNamespaceContext(env.getNamespaceResolver()));
+ }
+ return castable;
+
+ case Token.CAST_AS:
+ CastExpression cast = new CastExpression(lhs, (AtomicType) type, allowEmpty);
+ if (lhs instanceof StringLiteral) {
+ cast.setOperandIsStringLiteral(true);
+ }
+ if (type.isNamespaceSensitive()) {
+ cast.setNamespaceResolver(new SavedNamespaceContext(env.getNamespaceResolver()));
+ }
+ return cast;
+
+ default:
+ throw new IllegalArgumentException();
+ }
+ } else {
+ switch (operator) {
+ case Token.CASTABLE_AS:
+ if (type.isUnionType()) {
+ return new CastableToUnion(lhs, (UnionType)type, allowEmpty);
+ } else if (type.isListType()) {
+ return env.getConfiguration().obtainOptimizer().makeCastableToList(lhs, type, allowEmpty);
+ }
+ break;
+ case Token.CAST_AS:
+ if (type.isUnionType()) {
+ return new CastToUnion(lhs, (UnionType)type, allowEmpty);
+ } else if (type.isListType()) {
+ return new CastToList(lhs, (ListType)type, allowEmpty);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ if (type == AnySimpleType.getInstance()) {
+ throw new XPathException("Cannot cast to xs:anySimpleType", "XPST0051");
+ } else {
+ throw new XPathException("Cannot cast to " + type.getDescription(), "XPST0051");
+ }
+
+ }
+
+ }
+
+ /**
+ * Parse a Typeswitch Expression.
+ * This construct is XQuery-only, so the XPath version of this
+ * method throws an error unconditionally
+ *
+ * @return the expression that results from the parsing
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Expression parseTypeswitchExpression() throws XPathException {
+ grumble("typeswitch is not allowed in XPath");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Parse a Switch Expression.
+ * This construct is XQuery-1.1 only, so the XPath and XQuery 1.0 versions of this
+ * method throw an error unconditionally
+ *
+ * @return the expression that results from the parsing
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Expression parseSwitchExpression() throws XPathException {
+ try {
+ return parseFunctionCall();
+ } catch(XPathException ex){
+ grumble("switch expression requires XQuery 3.0");
+ return new ErrorExpression();
+ }
+ }
+
+ /**
+ * Parse a Validate Expression.
+ * This construct is XQuery-only, so the XPath version of this
+ * method throws an error unconditionally
+ *
+ * @return the parsed expression; except that this version of the method always
+ * throws an exception
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Expression parseValidateExpression() throws XPathException {
+ grumble("validate{} expressions are not allowed in XPath");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Parse an Extension Expression
+ * This construct is XQuery-only, so the XPath version of this
+ * method throws an error unconditionally
+ *
+ * @return the parsed expression; except that this version of the method
+ * always throws an exception
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Expression parseExtensionExpression() throws XPathException {
+ grumble("extension expressions (#...#) are not allowed in XPath");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Parse a Type Pattern
+ * This construct is used only in XSLT patterns, so the XPath version of this
+ * method throws an error unconditionally
+ *
+ * @return the parsed expression; except that this version of the method
+ * always throws an exception
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Expression parseTypePattern() throws XPathException {
+ grumble("Type patterns (~ItemType) are not allowed in XPath (only in XSLT 3.0 patterns)");
+ return new ErrorExpression();
+ }
+
+
+ /**
+ * Parse a try/catch Expression
+ * This construct is XQuery-3.0 only, so the XPath version of this
+ * method throws an error unconditionally
+ *
+ * @return the parsed expression; except that this version of the method
+ * always throws an exception
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Expression parseTryCatchExpression() throws XPathException {
+ grumble("try/catch expressions are not allowed in XPath");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Parse a FOR or LET expression:
+ * for $x in expr (',' $y in expr)* 'return' expr
+ * let $x := expr (', $y := expr)* 'return' expr
+ * This version of the method handles the subset of the FLWOR syntax allowed in XPath
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ protected Expression parseFLWORExpression() throws XPathException {
+ if (t.currentToken == Token.LET && !allowXPath30Syntax) {
+ grumble("'let' is not permitted in XPath 2.0");
+ }
+ if (t.currentToken == Token.FOR_SLIDING || t.currentToken == Token.FOR_TUMBLING) {
+ grumble("sliding/tumbling windows can only be used in XQuery");
+ }
+ int clauses = 0;
+ int offset;
+ int operator = t.currentToken;
+ Assignation first = null;
+ Assignation previous = null;
+ do {
+ offset = t.currentTokenStartOffset;
+ nextToken();
+ expect(Token.DOLLAR);
+ nextToken();
+ expect(Token.NAME);
+ String var = t.currentTokenValue;
+
+ // declare the range variable
+ Assignation v;
+ if (operator == Token.FOR) {
+ v = new ForExpression();
+ v.setRequiredType(SequenceType.SINGLE_ITEM);
+ } else /*if (operator == Token.LET)*/ {
+ v = new LetExpression();
+ v.setRequiredType(SequenceType.ANY_SEQUENCE);
+ }
+
+ clauses++;
+ setLocation(v, offset);
+ v.setVariableQName(makeStructuredQName(var, ""));
+ nextToken();
+
+ // process the "in" or ":=" clause
+ expect(operator == Token.LET ? Token.ASSIGN : Token.IN);
+ nextToken();
+ v.setSequence(parseExprSingle());
+ declareRangeVariable(v);
+ if (previous == null) {
+ first = v;
+ } else {
+ previous.setAction(v);
+ }
+ previous = v;
+
+ } while (t.currentToken == Token.COMMA);
+
+ // process the "return" expression (called the "action")
+ expect(Token.RETURN);
+ nextToken();
+ previous.setAction(parseExprSingle());
+
+ // undeclare all the range variables
+
+ for (int i = 0; i < clauses; i++) {
+ undeclareRangeVariable();
+ }
+ return makeTracer(offset, first, Location.FOR_EXPRESSION, first.getVariableQName());
+ }
+
+ /**
+ * Parse a quantified expression:
+ * (some|every) $x in expr 'satisfies' expr
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ private Expression parseQuantifiedExpression() throws XPathException {
+ int clauses = 0;
+ int operator = t.currentToken;
+ QuantifiedExpression first = null;
+ QuantifiedExpression previous = null;
+ int initialOffset = t.currentTokenStartOffset;
+ do {
+ int offset = t.currentTokenStartOffset;
+ nextToken();
+ expect(Token.DOLLAR);
+ nextToken();
+ expect(Token.NAME);
+ String var = t.currentTokenValue;
+ clauses++;
+
+ // declare the range variable
+ QuantifiedExpression v = new QuantifiedExpression();
+ v.setRequiredType(SequenceType.SINGLE_ITEM);
+ v.setOperator(operator);
+ setLocation(v, offset);
+
+ v.setVariableQName(makeStructuredQName(var, ""));
+ nextToken();
+
+ if (t.currentToken == Token.AS && language == XQUERY) {
+ // We use this path for quantified expressions in XQuery, which permit an "as" clause
+ nextToken();
+ SequenceType type = parseSequenceType();
+ if (type.getCardinality() != StaticProperty.EXACTLY_ONE) {
+ warning("Occurrence indicator on singleton range variable has no effect");
+ type = SequenceType.makeSequenceType(type.getPrimaryType(), StaticProperty.EXACTLY_ONE);
+ }
+ v.setRequiredType(type);
+ }
+
+ // process the "in" clause
+ expect(Token.IN);
+ nextToken();
+ v.setSequence(parseExprSingle());
+ declareRangeVariable(v);
+ if (previous != null) {
+ previous.setAction(v);
+ } else {
+ first = v;
+ }
+ previous = v;
+
+ } while (t.currentToken == Token.COMMA);
+
+ // process the "return/satisfies" expression (called the "action")
+ expect(Token.SATISFIES);
+ nextToken();
+ previous.setAction(parseExprSingle());
+
+
+ // undeclare all the range variables
+
+ for (int i = 0; i < clauses; i++) {
+ undeclareRangeVariable();
+ }
+ return makeTracer(initialOffset, first, Location.FOR_EXPRESSION, first.getVariableQName());
+
+ }
+
+
+ /**
+ * Parse an IF expression:
+ * if '(' expr ')' 'then' expr 'else' expr
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ private Expression parseIfExpression() throws XPathException {
+ // left paren already read
+ int ifoffset = t.currentTokenStartOffset;
+ nextToken();
+ Expression condition = parseExpression();
+ expect(Token.RPAR);
+ nextToken();
+ int thenoffset = t.currentTokenStartOffset;
+ expect(Token.THEN);
+ nextToken();
+ Expression thenExp = makeTracer(thenoffset, parseExprSingle(), Location.THEN_EXPRESSION, null);
+ int elseoffset = t.currentTokenStartOffset;
+ expect(Token.ELSE);
+ nextToken();
+ Expression elseExp = makeTracer(elseoffset, parseExprSingle(), Location.ELSE_EXPRESSION, null);
+ Expression ifExp = Choose.makeConditional(condition, thenExp, elseExp);
+ setLocation(ifExp, ifoffset);
+ return makeTracer(ifoffset, ifExp, Location.IF_EXPRESSION, null);
+ }
+
+ /**
+ * Analyze a token whose expected value is the name of an atomic type,
+ * or in XPath 3.0 a "plain" union type and return the object representing the atomic or union type.
+ *
+ * @param qname The lexical QName of the atomic type; alternatively, a Clark name
+ * @return The atomic type
+ * @throws XPathException if the QName is invalid or if no atomic type of that
+ * name exists as a built-in type or a type in an imported schema
+ */
+ /*@NotNull*/
+ private ItemType getPlainType(/*@NotNull*/ String qname) throws XPathException {
+ if (scanOnly) {
+ return STRING;
+ }
+ String uri;
+ String local;
+ if (qname.startsWith("{")) {
+ StructuredQName sq = StructuredQName.fromClarkName(qname);
+ uri = sq.getURI();
+ local = sq.getLocalPart();
+ } else {
+ try {
+ String[] parts = nameChecker.getQNameParts(qname);
+ if (parts[0].length() == 0) {
+ uri = env.getDefaultElementNamespace();
+ } else {
+ try {
+ uri = env.getURIForPrefix(parts[0]);
+ } catch (XPathException err) {
+ grumble(err.getMessage(), err.getErrorCodeQName(), -1);
+ uri = "";
+ }
+ }
+ local = parts[1];
+ } catch (QNameException err) {
+ grumble(err.getMessage());
+ return ANY_ATOMIC;
+ }
+ }
+
+ boolean builtInNamespace = uri.equals(NamespaceConstant.SCHEMA);
+
+ if (builtInNamespace) {
+ ItemType t = Type.getBuiltInItemType(uri, local);
+ if (t == null) {
+ grumble("Unknown atomic type " + qname, "XPST0051");
+ }
+ if (t instanceof BuiltInAtomicType) {
+ if (!env.isAllowedBuiltInType((BuiltInAtomicType) t)) {
+ grumble("The type " + qname + " is not recognized by a Basic XSLT Processor. ", "XPST0080");
+ }
+ return t;
+ } else if (t.isPlainType()) {
+ return t;
+ } else {
+ grumble("The type " + qname + " is not atomic", "XPST0051");
+ }
+ } else if (uri.equals(NamespaceConstant.JAVA_TYPE)) {
+ Class theClass;
+ try {
+ String className = local.replace('-', '$');
+ theClass = env.getConfiguration().getClass(className, false, null);
+ } catch (XPathException err) {
+ grumble("Unknown Java class " + local, "XPST0051");
+ return ExternalObjectType.EXTERNAL_OBJECT_TYPE;
+ }
+ return new ExternalObjectType(theClass, env.getConfiguration());
+ } else if (uri.equals(NamespaceConstant.DOT_NET_TYPE)) {
+ return (AtomicType) Configuration.getPlatform().getExternalObjectType(env.getConfiguration(), uri, local);
+ } else {
+
+ int fp = env.getNamePool().getFingerprint(uri, local);
+ if (fp == -1) {
+ grumble("Unknown type " + qname, "XPST0051");
+ }
+ SchemaType st = env.getConfiguration().getSchemaType(fp);
+ if (st == null) {
+ grumble("Unknown atomic type " + qname, "XPST0051");
+ } else if (st.isAtomicType()) {
+ if (!env.isImportedSchema(uri)) {
+ grumble("Atomic type " + qname + " exists, but its schema definition has not been imported", "XPST0051");
+ }
+ return (AtomicType) st;
+ } else if (st instanceof ItemType && ((ItemType) st).isPlainType() && DecimalValue.THREE.equals(env.getXPathLanguageLevel())) {
+ if (!env.isImportedSchema(uri)) {
+ grumble("Type " + qname + " exists, but its schema definition has not been imported", "XPST0051");
+ }
+ return (ItemType) st;
+ } else if (st.isComplexType()) {
+ grumble("Type (" + qname + ") is a complex type", "XPST0051");
+ return ANY_ATOMIC;
+ } else if (((SimpleType) st).isListType()) {
+ grumble("Type (" + qname + ") is a list type", "XPST0051");
+ return ANY_ATOMIC;
+ } else if (DecimalValue.THREE.equals(env.getXPathLanguageLevel())) {
+ grumble("Type (" + qname + ") is a union type that cannot be used as an item type", "XPST0051");
+ return ANY_ATOMIC;
+ } else {
+ grumble("The union type (" + qname + ") cannot be used as an item type unless XPath 3.0 is enabled", "XPST0051");
+ return ANY_ATOMIC;
+ }
+ }
+ grumble("Unknown atomic type " + qname, "XPST0051");
+ return ANY_ATOMIC;
+ }
+
+
+ /**
+ * Analyze a token whose expected value is the name of a simple type: any type name
+ * allowed as the operand of "cast" or "castable".
+ *
+ * @param qname The lexical QName of the atomic type; alternatively, a Clark name
+ * @return The atomic type
+ * @throws XPathException if the QName is invalid or if no atomic type of that
+ * name exists as a built-in type or a type in an imported schema
+ */
+ /*@NotNull*/
+ private SimpleType getSimpleType(/*@NotNull*/ String qname) throws XPathException {
+ if (scanOnly) {
+ return STRING;
+ }
+ String uri;
+ String local;
+ if (qname.startsWith("{")) {
+ StructuredQName sq = StructuredQName.fromClarkName(qname);
+ uri = sq.getURI();
+ local = sq.getLocalPart();
+ } else {
+ try {
+ String[] parts = nameChecker.getQNameParts(qname);
+ if (parts[0].length() == 0) {
+ uri = env.getDefaultElementNamespace();
+ } else {
+ try {
+ uri = env.getURIForPrefix(parts[0]);
+ } catch (XPathException err) {
+ grumble(err.getMessage(), err.getErrorCodeQName(), -1);
+ uri = "";
+ }
+ }
+ local = parts[1];
+ } catch (QNameException err) {
+ grumble(err.getMessage());
+ return ANY_ATOMIC;
+ }
+ }
+
+ boolean builtInNamespace = uri.equals(NamespaceConstant.SCHEMA);
+ if (builtInNamespace) {
+ SimpleType t = Type.getBuiltInSimpleType(uri, local);
+ if (t == null) {
+ grumble("Unknown simple type " + qname, "XPST0051");
+ }
+ if (t instanceof BuiltInAtomicType) {
+ if (!env.isAllowedBuiltInType((BuiltInAtomicType) t)) {
+ grumble("The type " + qname + " is not recognized by a Basic XSLT Processor. ", "XPST0080");
+ }
+ }
+ return t;
+ } else if (uri.equals(NamespaceConstant.DOT_NET_TYPE)) {
+ return (AtomicType) Configuration.getPlatform().getExternalObjectType(env.getConfiguration(), uri, local);
+
+ } else {
+
+ int fp = env.getNamePool().getFingerprint(uri, local);
+ if (fp == -1) {
+ grumble("Unknown type " + qname, "XPST0051");
+ }
+ SchemaType st = env.getConfiguration().getSchemaType(fp);
+ if (st == null) {
+ grumble("Unknown simple type " + qname, "XPST0051");
+ return ANY_ATOMIC;
+ }
+ if (DecimalValue.THREE.equals(env.getXPathLanguageLevel())) {
+ // XPath 3.0
+ if (!env.isImportedSchema(uri)) {
+ grumble("Simple type " + qname + " exists, but its target namespace has not been imported in the static context");
+ }
+ return (SimpleType) st;
+
+ } else {
+ // XPath 2.0
+ if (st.isAtomicType()) {
+ if (!env.isImportedSchema(uri)) {
+ grumble("Atomic type " + qname + " exists, but its target namespace has not been imported in the static context");
+ }
+ return (AtomicType) st;
+ } else if (st.isComplexType()) {
+ grumble("Cannot cast to a complex type (" + qname + ")", "XPST0051");
+ return ANY_ATOMIC;
+ } else if (((SimpleType) st).isListType()) {
+ grumble("Casting to a list type (" + qname + ") requires XPath 3.0", "XPST0051");
+ return ANY_ATOMIC;
+ } else {
+ grumble("casting to a union type (" + qname + ") requires XPath 3.0", "XPST0051");
+ return ANY_ATOMIC;
+ }
+ }
+ }
+ }
+
+ /**
+ * Parse the sequence type production.
+ * The QName must be the name of a built-in schema-defined data type.
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ public SequenceType parseSequenceType() throws XPathException {
+ boolean disallowIndicator = t.currentTokenValue.equals("empty-sequence");
+ ItemType primaryType = parseItemType();
+ if (disallowIndicator) {
+ // No occurrence indicator allowed
+ return SequenceType.makeSequenceType(primaryType, StaticProperty.EMPTY);
+ }
+ int occurrenceFlag;
+ switch (t.currentToken) {
+ case Token.STAR:
+ case Token.MULT:
+ // "*" will be tokenized different ways depending on what precedes it
+ occurrenceFlag = StaticProperty.ALLOWS_ZERO_OR_MORE;
+ // Make the tokenizer ignore the occurrence indicator when classifying the next token
+ t.currentToken = Token.RPAR;
+ nextToken();
+ break;
+ case Token.PLUS:
+ occurrenceFlag = StaticProperty.ALLOWS_ONE_OR_MORE;
+ // Make the tokenizer ignore the occurrence indicator when classifying the next token
+ t.currentToken = Token.RPAR;
+ nextToken();
+ break;
+ case Token.QMARK:
+ occurrenceFlag = StaticProperty.ALLOWS_ZERO_OR_ONE;
+ // Make the tokenizer ignore the occurrence indicator when classifying the next token
+ t.currentToken = Token.RPAR;
+ nextToken();
+ break;
+ default:
+ occurrenceFlag = StaticProperty.EXACTLY_ONE;
+ }
+ return SequenceType.makeSequenceType(primaryType, occurrenceFlag);
+ }
+
+ /**
+ * Parse an ItemType within a SequenceType
+ *
+ * @return the ItemType after parsing
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ public ItemType parseItemType() throws XPathException {
+ ItemType primaryType;
+ if (t.currentToken == Token.LPAR) {
+ primaryType = parseParenthesizedItemType();
+ //nextToken();
+ } else if (t.currentToken == Token.NAME) {
+ primaryType = getPlainType(t.currentTokenValue);
+ nextToken();
+ } else if (t.currentToken == Token.NODEKIND) {
+ if (t.currentTokenValue.equals("item")) {
+ nextToken();
+ expect(Token.RPAR);
+ nextToken();
+ primaryType = AnyItemType.getInstance();
+ } else if (t.currentTokenValue.equals("function")) {
+ primaryType = parseFunctionItemType();
+ } else if (t.currentTokenValue.equals("map")) {
+ primaryType = parseMapItemType();
+ } else if (t.currentTokenValue.equals("empty-sequence")) {
+ nextToken();
+ expect(Token.RPAR);
+ nextToken();
+ primaryType = ErrorType.getInstance();
+ } else {
+ primaryType = parseKindTest();
+ }
+ } else if (t.currentToken == Token.PERCENT) {
+ Map<StructuredQName, Annotation> annotationAssertions = parseAnnotations();
+ // TODO retain the annotation assertions
+ if (t.currentTokenValue.equals("function")) {
+ primaryType = parseFunctionItemType();
+ } else {
+ grumble("Expected 'function' to follow annotation assertions, found " + Token.tokens[t.currentToken]);
+ return null;
+ }
+ } else {
+ grumble("Expected type name in SequenceType, found " + Token.tokens[t.currentToken]);
+ return ANY_ATOMIC;
+ }
+ return primaryType;
+ }
+
+ /**
+ * Get the item type used for function items (XPath 3.0 higher order functions)
+ *
+ * @return the item type representing a function item
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error occurs (including the case
+ * where XPath 3.0 syntax is not enabled)
+ */
+
+ /*@NotNull*/
+ protected ItemType parseFunctionItemType() throws XPathException {
+ grumble("The item type function() is available only when XPath 3.0 is enabled");
+ return ANY_ATOMIC;
+ }
+
+ /**
+ * Get the item type used for map items (XPath 3.0)
+ *
+ * @return the item type of the map
+ * @throws XPathException if a parsing error occurs or if the map syntax
+ * is not available
+ */
+
+ /*@NotNull*/
+ protected ItemType parseMapItemType() throws XPathException {
+ grumble("The item type map() is available only when XPath 3.0 is enabled");
+ return ANY_ATOMIC;
+ }
+
+ /**
+ * Parse a parenthesized item type (XPath 3.0)
+ *
+ * @return the item type
+ * @throws XPathException if a syntax error is found
+ */
+
+ /*@NotNull*/
+ protected ItemType parseParenthesizedItemType() throws XPathException {
+ grumble("A parenthesized item type is allowed only when XPath 3.0 is enabled");
+ return ANY_ATOMIC;
+ }
+
+ /**
+ * Parse a UnaryExpr:<br>
+ * ('+'|'-')* ValueExpr
+ * parsed as ('+'|'-')? UnaryExpr
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@NotNull*/
+ private Expression parseUnaryExpression() throws XPathException {
+ Expression exp;
+ switch (t.currentToken) {
+ case Token.MINUS:
+ nextToken();
+ exp = new ArithmeticExpression(Literal.makeLiteral(Int64Value.ZERO),
+ Token.NEGATE,
+ parseUnaryExpression());
+ break;
+ case Token.PLUS:
+ nextToken();
+ // Unary plus: can't ignore it completely, it might be a type error, or it might
+ // force conversion to a number which would affect operations such as "=".
+ exp = new ArithmeticExpression(Literal.makeLiteral(Int64Value.ZERO),
+ Token.PLUS,
+ parseUnaryExpression());
+ break;
+ case Token.VALIDATE:
+ case Token.VALIDATE_STRICT:
+ case Token.VALIDATE_LAX:
+ case Token.VALIDATE_TYPE:
+ exp = parseValidateExpression();
+ break;
+ case Token.PRAGMA:
+ exp = parseExtensionExpression();
+ break;
+ case Token.TILDE:
+ exp = parseTypePattern();
+ break;
+ case Token.KEYWORD_CURLY:
+ if (t.currentTokenValue.equals("validate")) {
+ exp = parseValidateExpression();
+ break;
+ }
+ // else fall through
+ default:
+ exp = parseSimpleMappingExpression();
+ }
+ setLocation(exp);
+ return exp;
+ }
+
+ /**
+ * Test whether the current token is one that can start a RelativePathExpression
+ *
+ * @return the resulting subexpression
+ */
+
+ protected boolean atStartOfRelativePath() {
+ switch (t.currentToken) {
+ case Token.AXIS:
+ case Token.AT:
+ case Token.NAME:
+ case Token.PREFIX:
+ case Token.SUFFIX:
+ case Token.STAR:
+ case Token.NODEKIND:
+ case Token.DOT:
+ case Token.DOTDOT:
+ case Token.FUNCTION:
+ case Token.STRING_LITERAL:
+ case Token.NUMBER:
+ case Token.LPAR:
+ case Token.DOLLAR:
+ case Token.PRAGMA:
+ case Token.ELEMENT_QNAME:
+ case Token.ATTRIBUTE_QNAME:
+ case Token.PI_QNAME:
+ case Token.NAMESPACE_QNAME:
+ case Token.INLINE_FUNCTION_LITERAL:
+ return true;
+ case Token.KEYWORD_CURLY:
+ return (t.currentTokenValue.equals("ordered") || t.currentTokenValue.equals("unordered"));
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Test whether the current token is one that is disallowed after a "leading lone slash".
+ * These composite tokens have been parsed as operators, but are not allowed after "/" under the
+ * rules of erratum E24
+ *
+ * @return the resulting subexpression
+ */
+
+ protected boolean disallowedAtStartOfRelativePath() {
+ switch (t.currentToken) {
+ // Although these "double keyword" operators can readily be recognized as operators,
+ // they are not permitted after leading "/" under the rules of erratum XQ.E24
+ case Token.CAST_AS:
+ case Token.CASTABLE_AS:
+ case Token.INSTANCE_OF:
+ case Token.TREAT_AS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Parse a PathExpresssion. This includes "true" path expressions such as A/B/C, and also
+ * constructs that may start a path expression such as a variable reference $name or a
+ * parenthesed expression (A|B). Numeric and string literals also come under this heading.
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@NotNull*/
+ protected Expression parsePathExpression() throws XPathException {
+ switch (t.currentToken) {
+ case Token.SLASH:
+ nextToken();
+ final RootExpression start = new RootExpression();
+ setLocation(start);
+ if (disallowedAtStartOfRelativePath()) {
+ grumble("Operator '" + Token.tokens[t.currentToken] + "' is not allowed after '/'");
+ }
+ if (atStartOfRelativePath()) {
+ final Expression path = parseRemainingPath(start);
+ setLocation(path);
+ return path;
+ } else {
+ return start;
+ }
+
+ case Token.SLASH_SLASH:
+ nextToken();
+ final RootExpression start2 = new RootExpression();
+ setLocation(start2);
+ final AxisExpression axisExp = new AxisExpression(AxisInfo.DESCENDANT_OR_SELF, null);
+ setLocation(axisExp);
+ final Expression slashExp = ExpressionTool.makePathExpression(start2, axisExp, false);
+ setLocation(slashExp);
+ final Expression exp = parseRemainingPath(slashExp);
+ setLocation(exp);
+ return exp;
+
+ default:
+ if (t.currentToken == Token.NAME &&
+ (t.currentTokenValue.equals("true") || t.currentTokenValue.equals("false"))) {
+ warning("The expression is looking for a child element named '" + t.currentTokenValue +
+ "' - perhaps " + t.currentTokenValue + "() was intended? To avoid this warning, use child::" +
+ t.currentTokenValue + " or ./" + t.currentTokenValue);
+ }
+ return parseRelativePath();
+ }
+
+ }
+
+ /**
+ * Parse an XPath 3.0 simple mapping expression ("!" operator)
+ */
+
+ protected Expression parseSimpleMappingExpression() throws XPathException {
+ Expression exp = parsePathExpression();
+ while (t.currentToken == Token.BANG) {
+ if (!env.getXPathLanguageLevel().equals(DecimalValue.THREE)) {
+ grumble("XPath '!' operator requires XPath 3.0 to be enabled");
+ }
+ nextToken();
+ Expression next = parsePathExpression();
+ exp = new ForEach(exp, next);
+ }
+ return exp;
+ }
+
+
+ /**
+ * Parse a relative path (a sequence of steps). Called when the current token immediately
+ * follows a separator (/ or //), or an implicit separator (XYZ is equivalent to ./XYZ)
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@NotNull*/
+ protected Expression parseRelativePath() throws XPathException {
+ Expression exp = parseStepExpression(language == XSLT_PATTERN);
+ while (t.currentToken == Token.SLASH ||
+ t.currentToken == Token.SLASH_SLASH) {
+ int op = t.currentToken;
+ nextToken();
+ Expression next = parseStepExpression(false);
+ if (op == Token.SLASH) {
+ exp = ExpressionTool.makePathExpression(exp, next, true);
+ } else if (op == Token.SLASH_SLASH) {
+ // add implicit descendant-or-self::node() step
+ AxisExpression ae = new AxisExpression(AxisInfo.DESCENDANT_OR_SELF, null);
+ setLocation(ae);
+ Expression one = ExpressionTool.makePathExpression(exp, ae, false);
+ setLocation(one);
+ exp = ExpressionTool.makePathExpression(one, next, true);
+ }
+ setLocation(exp);
+ }
+ return exp;
+ }
+
+ /**
+ * Parse the remaining steps of an absolute path expression (one starting in "/" or "//"). Note that the
+ * token immediately after the "/" or "//" has already been read, and in the case of "/", it has been confirmed
+ * that we have a path expression starting with "/" rather than a standalone "/" expression.
+ *
+ * @param start the initial implicit expression: root() in the case of "/", root()/descendant-or-self::node in
+ * the case of "//"
+ * @return the completed path expression
+ * @throws XPathException if a static error is found
+ */
+ /*@NotNull*/
+ protected Expression parseRemainingPath(Expression start) throws XPathException {
+ Expression exp = start;
+ int op = Token.SLASH;
+ while (true) {
+ Expression next = parseStepExpression(false);
+ if (op == Token.SLASH) {
+ exp = ExpressionTool.makePathExpression(exp, next, true);
+ } else if (op == Token.SLASH_SLASH) {
+ // add implicit descendant-or-self::node() step
+ AxisExpression descOrSelf = new AxisExpression(AxisInfo.DESCENDANT_OR_SELF, null);
+ setLocation(descOrSelf);
+ Expression step = ExpressionTool.makePathExpression(descOrSelf, next, false);
+ setLocation(step);
+ exp = ExpressionTool.makePathExpression(exp, step, true);
+ } else /*if (op == Token.BANG)*/ {
+ if (!env.getXPathLanguageLevel().equals(DecimalValue.THREE)) {
+ grumble("The '!' operator requires XPath 3.0 to be enabled");
+ }
+ exp = new ForEach(exp, next);
+ }
+ setLocation(exp);
+ op = t.currentToken;
+ if (op != Token.SLASH && op != Token.SLASH_SLASH && op != Token.BANG) {
+ break;
+ }
+ nextToken();
+ }
+ return exp;
+ }
+
+
+ /**
+ * Parse a step (including an optional sequence of predicates)
+ *
+ * @param firstInPattern true only if we are parsing the first step in a
+ * RelativePathPattern in the XSLT Pattern syntax
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@NotNull*/
+ protected Expression parseStepExpression(boolean firstInPattern) throws XPathException {
+ Expression step = parseBasicStep(firstInPattern);
+
+ // When the filter is applied to an Axis step, the nodes are considered in
+ // axis order. In all other cases they are considered in document order
+ boolean reverse = (step instanceof AxisExpression) &&
+ !AxisInfo.isForwards[((AxisExpression) step).getAxis()];
+
+ while (true) {
+ if (t.currentToken == Token.LSQB) {
+ nextToken();
+ Expression predicate = parsePredicate();
+ expect(Token.RSQB);
+ nextToken();
+ step = new FilterExpression(step, predicate);
+ setLocation(step);
+ } else if (t.currentToken == Token.LPAR) {
+ // dynamic function call (XQuery 3.0/XPath 3.0 syntax)
+ step = parseDynamicFunctionCall(step);
+ setLocation(step);
+ } else {
+ break;
+ }
+ }
+ if (reverse) {
+ step = SystemFunctionCall.makeSystemFunction("reverse", new Expression[]{step});
+ assert step != null;
+ return step;
+ } else {
+ return step;
+ }
+ }
+
+ /**
+ * Parse the expression within a predicate. A separate method so it can be overridden
+ *
+ * @return the expression within the predicate
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Expression parsePredicate() throws XPathException {
+ return parseExpression();
+ }
+
+ protected Map<StructuredQName, Annotation> parseAnnotations() throws XPathException {
+ grumble("Function and variable annotations are allowed only in XQuery 3.0");
+ return null;
+ }
+
+ protected boolean isReservedInQuery(String uri) {
+ return NamespaceConstant.isReservedInQuery(uri);
+ }
+
+ /**
+ * Parse a basic step expression (without the predicates)
+ *
+ * @param firstInPattern true only if we are parsing the first step in a
+ * RelativePathPattern in the XSLT Pattern syntax
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@NotNull*/
+ protected Expression parseBasicStep(boolean firstInPattern) throws XPathException {
+ switch (t.currentToken) {
+ case Token.DOLLAR:
+ return parseVariableReference();
+
+ case Token.LPAR:
+ nextToken();
+ if (t.currentToken == Token.RPAR) {
+ nextToken();
+ return Literal.makeEmptySequence();
+ }
+ Expression seq = parseExpression();
+ expect(Token.RPAR);
+ nextToken();
+ return seq;
+
+ case Token.STRING_LITERAL:
+ return parseStringLiteral();
+
+ case Token.NUMBER:
+ return parseNumericLiteral();
+
+ case Token.FUNCTION:
+ return parseFunctionCall();
+
+ case Token.DOT:
+ nextToken();
+ Expression cie = new ContextItemExpression();
+ setLocation(cie);
+ return cie;
+
+ case Token.DOTDOT:
+ nextToken();
+ Expression pne = new ParentNodeExpression();
+ setLocation(pne);
+ return pne;
+
+ case Token.PERCENT:
+ Map<StructuredQName, Annotation> annotations = parseAnnotations();
+ if (!t.currentTokenValue.equals("function")) {
+ grumble("Expected 'function' to follow the annotation assertion");
+ }
+ if (annotations.containsKey(Annotation.PRIVATE) ||
+ annotations.containsKey(Annotation.PUBLIC)) {
+ grumble("Inline functions must not be annotated %private or %public", "XQST0125");
+ }
+ return parseInlineFunction(annotations);
+
+ case Token.NODEKIND:
+
+ if (t.currentTokenValue.equals("function")) {
+ if (languageVersion.equals(DecimalValue.THREE)) {
+ return parseInlineFunction(null);
+ } else {
+ return parseFunctionCall();
+ }
+ } else if (t.currentTokenValue.equals("namespace-node") || t.currentTokenValue.equals("switch")) {
+ if(!languageVersion.equals(DecimalValue.THREE)) {
+ return parseFunctionCall();
+ }
+ } else if(t.currentTokenValue.equals("map")) {
+ return parseFunctionCall();
+ }
+
+ // fall through!
+ case Token.NAME:
+ case Token.PREFIX:
+ case Token.SUFFIX:
+ case Token.STAR:
+ //case Token.NODEKIND:
+ byte defaultAxis = AxisInfo.CHILD;
+ if (t.currentToken == Token.NODEKIND &&
+ (t.currentTokenValue.equals("attribute") || t.currentTokenValue.equals("schema-attribute"))) {
+ defaultAxis = AxisInfo.ATTRIBUTE;
+ } else if (t.currentToken == Token.NODEKIND && t.currentTokenValue.equals("namespace-node")) {
+ defaultAxis = AxisInfo.NAMESPACE;
+ testPermittedAxis(AxisInfo.NAMESPACE);
+ } else if (firstInPattern && t.currentToken == Token.NODEKIND && t.currentTokenValue.equals("document-node")) {
+ defaultAxis = AxisInfo.SELF;
+ }
+ NodeTest test = parseNodeTest(Type.ELEMENT);
+ if (test instanceof AnyNodeTest) {
+ // handles patterns of the form match="node()"
+ test = (defaultAxis == AxisInfo.CHILD ? AnyChildNodeTest.getInstance() : NodeKindTest.ATTRIBUTE);
+ }
+ AxisExpression ae = new AxisExpression(defaultAxis, test);
+ setLocation(ae);
+ return ae;
+
+ case Token.AT:
+ nextToken();
+ switch (t.currentToken) {
+
+ case Token.NAME:
+ case Token.PREFIX:
+ case Token.SUFFIX:
+ case Token.STAR:
+ case Token.NODEKIND:
+ AxisExpression ae2 = new AxisExpression(AxisInfo.ATTRIBUTE, parseNodeTest(Type.ATTRIBUTE));
+ setLocation(ae2);
+ return ae2;
+
+ default:
+ grumble("@ must be followed by a NodeTest");
+ }
+ break;
+
+ case Token.AXIS:
+ byte axis;
+ try {
+ axis = AxisInfo.getAxisNumber(t.currentTokenValue);
+ } catch (XPathException err) {
+ grumble(err.getMessage());
+ axis = AxisInfo.CHILD; // error recovery
+ }
+ testPermittedAxis(axis);
+ short principalNodeType = AxisInfo.principalNodeType[axis];
+ nextToken();
+ switch (t.currentToken) {
+
+ case Token.NAME:
+ case Token.PREFIX:
+ case Token.SUFFIX:
+ case Token.STAR:
+ case Token.NODEKIND:
+ Expression ax = new AxisExpression(axis, parseNodeTest(principalNodeType));
+ setLocation(ax);
+ return ax;
+
+ default:
+ grumble("Unexpected token " + currentTokenDisplay() + " after axis name");
+ }
+ break;
+
+ case Token.KEYWORD_CURLY:
+ if (t.currentTokenValue.equals("map")) {
+ return parseMapExpression();
+ }
+ // else fall through
+ case Token.ELEMENT_QNAME:
+ case Token.ATTRIBUTE_QNAME:
+ case Token.NAMESPACE_QNAME:
+ case Token.PI_QNAME:
+ case Token.TAG:
+ return parseConstructor();
+
+ case Token.INLINE_FUNCTION_LITERAL:
+ return parseLiteralFunctionItem();
+
+ case Token.TILDE:
+ return parseTypePattern();
+
+
+ default:
+ grumble("Unexpected token " + currentTokenDisplay() + " in path expression");
+ //break;
+ }
+ return new ErrorExpression();
+ }
+
+ protected void testPermittedAxis(byte axis) throws XPathException {
+ // no action by default - all axes are permitted
+ }
+
+
+ protected Expression parseNumericLiteral() throws XPathException {
+ int offset = t.currentTokenStartOffset;
+ NumericValue number = NumericValue.parseNumber(t.currentTokenValue);
+ if (number.isNaN()) {
+ grumble("Invalid numeric literal " + Err.wrap(t.currentTokenValue, Err.VALUE));
+ }
+ nextToken();
+ Literal lit = Literal.makeLiteral(number);
+ setLocation(lit);
+ return makeTracer(offset, lit, 0, null);
+ }
+
+ protected Expression parseStringLiteral() throws XPathException {
+ int offset = t.currentTokenStartOffset;
+ Literal literal = makeStringLiteral(t.currentTokenValue);
+ nextToken();
+ return makeTracer(offset, literal, 0, null);
+ }
+
+ /*@NotNull*/
+ protected Expression parseVariableReference() throws XPathException {
+ nextToken();
+ expect(Token.NAME);
+ String var = t.currentTokenValue;
+ nextToken();
+
+ if (scanOnly) {
+ return new ContextItemExpression();
+ // don't do any semantic checks during a prescan
+ }
+
+ //int vtest = makeNameCode(var, false) & 0xfffff;
+ StructuredQName vtest = makeStructuredQName(var, "");
+
+ // See if it's a range variable or a variable in the context
+ Binding b = findRangeVariable(vtest);
+ Expression ref;
+ if (b != null) {
+ ref = new LocalVariableReference(b);
+ } else {
+ if (catchDepth > 0) {
+ for (StructuredQName errorVariable : StandardNames.errorVariables) {
+ if (errorVariable.getLocalPart().equals(vtest.getLocalPart())) {
+ IntegratedFunctionLibrary lib = env.getConfiguration().getVendorFunctionLibrary();
+ StructuredQName functionName =
+ new StructuredQName("saxon", NamespaceConstant.SAXON, "dynamic-error-info");
+ Expression[] args = new Expression[]{new StringLiteral(vtest.getLocalPart())};
+ return lib.bind(functionName, 1, args, env, null);
+ }
+ }
+ }
+ try {
+ ref = env.bindVariable(vtest);
+ } catch (XPathException err) {
+ Expression dummy = new ContextItemExpression();
+ setLocation(dummy);
+ err.maybeSetLocation(dummy);
+ throw err;
+ }
+ }
+ setLocation(ref);
+ return ref;
+ }
+
+ /**
+ * Method to make a string literal from a token identified as a string
+ * literal. This is trivial in XPath, but in XQuery the method is overridden
+ * to identify pseudo-XML character and entity references. Note that the job of handling
+ * doubled string delimiters is done by the tokenizer.
+ *
+ * @param currentTokenValue the token as read (excluding quotation marks)
+ * @return The string value of the string literal
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Literal makeStringLiteral(String currentTokenValue) throws XPathException {
+ StringLiteral literal = new StringLiteral(currentTokenValue);
+ setLocation(literal);
+ return literal;
+ }
+
+ /**
+ * Parse a node constructor. This is allowed only in XQuery, so the method throws
+ * an error for XPath.
+ *
+ * @return the expression that results from the parsing
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error occurs
+ */
+
+ /*@NotNull*/
+ protected Expression parseConstructor() throws XPathException {
+ grumble("Node constructor expressions are allowed only in XQuery, not in XPath");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Parse a dynamic function call
+ *
+ * @param functionItem the expression that determines the function to be called
+ * @return the expression that results from the parsing
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Expression parseDynamicFunctionCall(Expression functionItem) throws XPathException {
+ grumble("Unexpected '(' after primary expression. (Dynamic function calls require XPath 3.0)");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Parse a NodeTest.
+ * One of QName, prefix:*, *:suffix, *, text(), node(), comment(), or
+ * processing-instruction(literal?), or element(~,~), attribute(~,~), etc.
+ *
+ * @param nodeType the node type being sought if one is specified
+ * @return the resulting NodeTest object
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@NotNull*/
+ protected NodeTest parseNodeTest(short nodeType) throws XPathException {
+ int tok = t.currentToken;
+ String tokv = t.currentTokenValue;
+ switch (tok) {
+ case Token.NAME:
+ nextToken();
+ return makeNameTest(nodeType, tokv, nodeType == Type.ELEMENT);
+
+ case Token.PREFIX:
+ nextToken();
+ return makeNamespaceTest(nodeType, tokv);
+
+ case Token.SUFFIX:
+ nextToken();
+ tokv = t.currentTokenValue;
+ expect(Token.NAME);
+ nextToken();
+ return makeLocalNameTest(nodeType, tokv);
+
+ case Token.STAR:
+ nextToken();
+ return NodeKindTest.makeNodeKindTest(nodeType);
+
+ case Token.NODEKIND:
+ return parseKindTest();
+
+ default:
+ grumble("Unrecognized node test");
+ throw new XPathException(""); // unreachable instruction
+ }
+ }
+
+ /**
+ * Parse a KindTest
+ *
+ * @return the KindTest, expressed as a NodeTest object
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error is found
+ */
+
+ /*@NotNull*/
+ private NodeTest parseKindTest() throws XPathException {
+ String typeName = t.currentTokenValue;
+ boolean schemaDeclaration = (typeName.startsWith("schema-"));
+ int primaryType = getSystemType(typeName);
+ int nameCode = -1;
+ int contentType;
+ boolean empty = false;
+ nextToken();
+ if (t.currentToken == Token.RPAR) {
+ if (schemaDeclaration) {
+ grumble("schema-element() and schema-attribute() require a name to be supplied");
+ return null;
+ }
+ empty = true;
+ nextToken();
+ }
+ switch (primaryType) {
+ case Type.ITEM:
+ grumble("item() is not allowed in a path expression");
+ return null;
+ case Type.NODE:
+ if (empty) {
+ return AnyNodeTest.getInstance();
+ } else {
+ grumble("No arguments are allowed in node()");
+ return null;
+ }
+ case Type.TEXT:
+ if (empty) {
+ return NodeKindTest.TEXT;
+ } else {
+ grumble("No arguments are allowed in text()");
+ return null;
+ }
+ case Type.COMMENT:
+ if (empty) {
+ return NodeKindTest.COMMENT;
+ } else {
+ grumble("No arguments are allowed in comment()");
+ return null;
+ }
+ case Type.NAMESPACE:
+ if (empty) {
+ if (!isNamespaceTestAllowed()) {
+ grumble("namespace-node() test is not allowed in XPath 2.0/XQuery 1.0");
+ }
+ return NodeKindTest.NAMESPACE;
+ } else {
+ grumble("No arguments are allowed in namespace-node()");
+ return null;
+ }
+ case Type.DOCUMENT:
+ if (empty) {
+ return NodeKindTest.DOCUMENT;
+ } else {
+ int innerType;
+ try {
+ innerType = getSystemType(t.currentTokenValue);
+ } catch (XPathException err) {
+ innerType = Type.ITEM;
+ }
+ if (innerType != Type.ELEMENT) {
+ grumble("Argument to document-node() must be an element type descriptor");
+ return null;
+ }
+ NodeTest inner = parseKindTest();
+ expect(Token.RPAR);
+ nextToken();
+ return new DocumentNodeTest(inner);
+ }
+ case Type.PROCESSING_INSTRUCTION:
+ if (empty) {
+ return NodeKindTest.PROCESSING_INSTRUCTION;
+ } else if (t.currentToken == Token.STRING_LITERAL) {
+ String piName = Whitespace.trim(t.currentTokenValue);
+ if (!nameChecker.isValidNCName(piName)) {
+ // Became an error as a result of XPath erratum XP.E7
+ grumble("Processing instruction name must be a valid NCName", "XPTY0004");
+ } else {
+ nameCode = env.getNamePool().allocate("", "", piName);
+ }
+ } else if (t.currentToken == Token.NAME) {
+ try {
+ String[] parts = nameChecker.getQNameParts(t.currentTokenValue);
+ if (parts[0].length() == 0) {
+ nameCode = makeNameCode(parts[1], false);
+ } else {
+ grumble("Processing instruction name must not contain a colon");
+ }
+ } catch (QNameException e) {
+ grumble("Invalid processing instruction name. " + e.getMessage());
+ }
+ } else {
+ grumble("Processing instruction name must be a QName or a string literal");
+ }
+ nextToken();
+ expect(Token.RPAR);
+ nextToken();
+ return new NameTest(Type.PROCESSING_INSTRUCTION, nameCode, env.getNamePool());
+
+ case Type.ATTRIBUTE:
+ // drop through
+
+ case Type.ELEMENT:
+ String nodeName = "";
+ if (empty) {
+ return NodeKindTest.makeNodeKindTest(primaryType);
+ } else if (t.currentToken == Token.STAR || t.currentToken == Token.MULT) {
+ // allow for both representations of "*" to be safe
+ if (schemaDeclaration) {
+ grumble("schema-element() and schema-attribute() must specify an actual name, not '*'");
+ return null;
+ }
+ nameCode = -1;
+ } else if (t.currentToken == Token.NAME) {
+ nodeName = t.currentTokenValue;
+ nameCode = makeNameCode(t.currentTokenValue, primaryType == Type.ELEMENT);
+ } else {
+ grumble("Unexpected " + Token.tokens[t.currentToken] + " after '(' in SequenceType");
+ }
+ String suri = null;
+ if (nameCode != -1) {
+ suri = env.getNamePool().getURI(nameCode);
+ }
+ nextToken();
+ if (t.currentToken == Token.RPAR) {
+ nextToken();
+ if (nameCode == -1) {
+ // element(*) or attribute(*)
+ return NodeKindTest.makeNodeKindTest(primaryType);
+ } else {
+ NodeTest nameTest;
+ if (primaryType == Type.ATTRIBUTE) {
+ // attribute(N) or schema-attribute(N)
+ if (schemaDeclaration) {
+ // schema-attribute(N)
+ SchemaDeclaration attributeDecl =
+ env.getConfiguration().getAttributeDeclaration(nameCode & 0xfffff);
+ if (!env.isImportedSchema(suri)) {
+ grumble("No schema has been imported for namespace '" + suri + '\'', "XPST0008");
+ }
+ if (attributeDecl == null) {
+ grumble("There is no declaration for attribute @" + nodeName + " in an imported schema", "XPST0008");
+ return null;
+ } else {
+ return attributeDecl.makeSchemaNodeTest();
+ }
+ } else {
+ nameTest = new NameTest(Type.ATTRIBUTE, nameCode, env.getNamePool());
+ return nameTest;
+ }
+ } else {
+ // element(N) or schema-element(N)
+ if (schemaDeclaration) {
+ // schema-element(N)
+ if (!env.isImportedSchema(suri)) {
+ grumble("No schema has been imported for namespace '" + suri + '\'', "XPST0008");
+ }
+ SchemaDeclaration elementDecl =
+ env.getConfiguration().getElementDeclaration(nameCode & 0xfffff);
+ if (elementDecl == null) {
+ grumble("There is no declaration for element <" + nodeName + "> in an imported schema", "XPST0008");
+ return null;
+ } else {
+ return elementDecl.makeSchemaNodeTest();
+ }
+ } else {
+ nameTest = new NameTest(Type.ELEMENT, nameCode, env.getNamePool());
+ return nameTest;
+ }
+ }
+ }
+ } else if (t.currentToken == Token.COMMA) {
+ if (schemaDeclaration) {
+ grumble("schema-element() and schema-attribute() must have one argument only");
+ return null;
+ }
+ nextToken();
+ NodeTest result;
+ if (t.currentToken == Token.STAR) {
+ grumble("'*' is no longer permitted as the second argument of element() and attribute()");
+ return null;
+ } else if (t.currentToken == Token.NAME) {
+ SchemaType schemaType;
+ contentType = makeNameCode(t.currentTokenValue, true) & NamePool.FP_MASK;
+ String uri = env.getNamePool().getURI(contentType);
+ String lname = env.getNamePool().getLocalName(contentType);
+
+ if (uri.equals(NamespaceConstant.SCHEMA)) {
+ schemaType = env.getConfiguration().getSchemaType(contentType);
+ } else {
+ if (!env.isImportedSchema(uri)) {
+ grumble("No schema has been imported for namespace '" + uri + '\'', "XPST0008");
+ }
+ schemaType = env.getConfiguration().getSchemaType(contentType);
+ }
+ if (schemaType == null) {
+ grumble("Unknown type name " + lname, "XPST0008");
+ return null;
+ }
+ if (primaryType == Type.ATTRIBUTE && schemaType.isComplexType()) {
+ warning("An attribute cannot have a complex type");
+ }
+ ContentTypeTest typeTest = new ContentTypeTest(primaryType, schemaType, env.getConfiguration(), false);
+ if (nameCode == -1) {
+ // this represents element(*,T) or attribute(*,T)
+ result = typeTest;
+ if (primaryType == Type.ATTRIBUTE) {
+ nextToken();
+ } else {
+ // assert (primaryType == Type.ELEMENT);
+ nextToken();
+ if (t.currentToken == Token.QMARK) {
+ typeTest.setNillable(true);
+ nextToken();
+ }
+ }
+ } else {
+ if (primaryType == Type.ATTRIBUTE) {
+ NodeTest nameTest = new NameTest(Type.ATTRIBUTE, nameCode, env.getNamePool());
+ result = new CombinedNodeTest(nameTest, Token.INTERSECT, typeTest);
+ nextToken();
+ } else {
+ // assert (primaryType == Type.ELEMENT);
+ NodeTest nameTest = new NameTest(Type.ELEMENT, nameCode, env.getNamePool());
+ result = new CombinedNodeTest(nameTest, Token.INTERSECT, typeTest);
+ nextToken();
+ if (t.currentToken == Token.QMARK) {
+ typeTest.setNillable(true);
+ nextToken();
+ }
+ }
+ }
+ } else {
+ grumble("Unexpected " + Token.tokens[t.currentToken] + " after ',' in SequenceType");
+ return null;
+ }
+
+ expect(Token.RPAR);
+ nextToken();
+ return result;
+ } else {
+ grumble("Expected ')' or ',' in SequenceType");
+ }
+ return null;
+ default:
+ // can't happen!
+ grumble("Unknown node kind");
+ return null;
+ }
+ }
+
+ /**
+ * Ask whether the syntax namespace-node() is allowed in a node kind test.
+ *
+ * @return false (currently allowed only in XQuery 3.0)
+ */
+
+ protected boolean isNamespaceTestAllowed() {
+ return allowXPath30Syntax;
+ }
+
+ /**
+ * Get a system type - that is, one whose name is a keyword rather than a QName. This includes the node
+ * kinds such as element and attribute, and the generic types node() and item()
+ *
+ * @param name the name of the system type, for example "element" or "comment"
+ * @return the integer constant denoting the type, for example {@link Type#ITEM} or {@link Type#ELEMENT}
+ * @throws XPathException if the name is not recognized
+ */
+ private int getSystemType(String name) throws XPathException {
+ if ("item".equals(name)) {
+ return Type.ITEM;
+ } else if ("document-node".equals(name)) {
+ return Type.DOCUMENT;
+ } else if ("element".equals(name)) {
+ return Type.ELEMENT;
+ } else if ("schema-element".equals(name)) {
+ return Type.ELEMENT;
+ } else if ("attribute".equals(name)) {
+ return Type.ATTRIBUTE;
+ } else if ("schema-attribute".equals(name)) {
+ return Type.ATTRIBUTE;
+ } else if ("text".equals(name)) {
+ return Type.TEXT;
+ } else if ("comment".equals(name)) {
+ return Type.COMMENT;
+ } else if ("processing-instruction".equals(name)) {
+ return Type.PROCESSING_INSTRUCTION;
+ } else if ("namespace-node".equals(name)) {
+ return Type.NAMESPACE;
+ } else if ("node".equals(name)) {
+ return Type.NODE;
+ } else {
+ grumble("Unknown type " + name);
+ return -1;
+ }
+ }
+
+ /**
+ * Parse a map expression. Requires XPath/XQuery 3.0
+ * Provisional syntax
+ * map { expr := expr (, expr := expr )*} }
+ *
+ * @return the map expression
+ * @throws XPathException if a static error occurs
+ */
+
+ /*@NotNull*/
+ protected Expression parseMapExpression() throws XPathException {
+ grumble("map expressions require XPath 3.0/XQuery 3.0 to be enabled");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Parse a function call.
+ * function-name '(' ( Expression (',' Expression )* )? ')'
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@NotNull*/
+ protected Expression parseFunctionCall() throws XPathException {
+
+ String fname = t.currentTokenValue;
+ int offset = t.currentTokenStartOffset;
+ ArrayList<Expression> args = new ArrayList<Expression>(10);
+
+ StructuredQName functionName = resolveFunctionName(fname);
+ IntSet placeMarkers = null;
+
+ // the "(" has already been read by the Tokenizer: now parse the arguments
+
+ nextToken();
+ if (t.currentToken != Token.RPAR) {
+ while (true) {
+ Expression arg = parseFunctionArgument();
+ if (arg == null) {
+ // this is a "?" placemarker
+ if (placeMarkers == null) {
+ placeMarkers = new IntArraySet();
+ }
+ placeMarkers.add(args.size());
+ arg = Literal.makeEmptySequence(); // a convenient fiction
+ }
+ args.add(arg);
+ if (t.currentToken == Token.COMMA) {
+ nextToken();
+ } else {
+ break;
+ }
+ }
+ expect(Token.RPAR);
+ }
+ nextToken();
+
+ if (scanOnly) {
+ return new StringLiteral(StringValue.EMPTY_STRING);
+ }
+
+ Expression[] arguments = new Expression[args.size()];
+ args.toArray(arguments);
+
+ if (placeMarkers != null) {
+ return makeCurriedFunction(offset, functionName, arguments, placeMarkers);
+ }
+
+ Expression fcall;
+ try {
+ fcall = env.getFunctionLibrary().bind(functionName, args.size(), arguments, env, defaultContainer);
+ } catch (XPathException err) {
+ if (err.getErrorCodeQName() == null) {
+ err.setErrorCode("XPST0017");
+ err.setIsStaticError(true);
+ }
+ if (functionName.isInNamespace(NamespaceConstant.MAP_FUNCTIONS_2011)) {
+ grumble("Saxon currently implements the XSLT 3.0 map functions in namespace " + NamespaceConstant.MAP_FUNCTIONS);
+ } else {
+ grumble(err.getMessage(), err.getErrorCodeQName(), offset);
+ }
+ return new ErrorExpression();
+ }
+ if (fcall == null) {
+ return reportMissingFunction(offset, functionName, arguments);
+ }
+ // A QName or NOTATION constructor function must be given the namespace context now
+ if (fcall instanceof CastExpression &&
+ ((AtomicType) fcall.getItemType(env.getConfiguration().getTypeHierarchy())).isNamespaceSensitive()) {
+ ((CastExpression) fcall).setNamespaceResolver(new SavedNamespaceContext(env.getNamespaceResolver()));
+ }
+ // There are special rules for certain functions appearing in a pattern
+ if (language == XSLT_PATTERN) {
+ if (fcall instanceof RegexGroup) {
+ return Literal.makeEmptySequence();
+ } else if (fcall instanceof CurrentGroup) {
+ grumble("The current-group() function cannot be used in a pattern",
+ "XTSE1060", offset);
+ return new ErrorExpression();
+ } else if (fcall instanceof CurrentGroupingKey) {
+ grumble("The current-grouping-key() function cannot be used in a pattern",
+ "XTSE1070", offset);
+ return new ErrorExpression();
+ }
+ }
+ setLocation(fcall, offset);
+ for (Expression argument : arguments) {
+ fcall.adoptChildExpression(argument);
+ }
+
+ return makeTracer(offset, fcall, Location.FUNCTION_CALL, functionName);
+
+ }
+
+ /*@NotNull*/
+ public Expression reportMissingFunction(int offset, /*@NotNull*/ StructuredQName functionName, /*@NotNull*/ Expression[] arguments) throws XPathException {
+ String msg = "Cannot find a matching " + arguments.length +
+ "-argument function named " + functionName.getClarkName() + "()";
+ if (env.getConfiguration().getBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
+ boolean existsWithDifferentArity = false;
+ for (int i=0; i<arguments.length+5; i++) {
+ if (i != arguments.length) {
+ if (env.getFunctionLibrary().isAvailable(functionName, i)) {
+ existsWithDifferentArity = true;
+ break;
+ }
+ }
+ }
+ if (existsWithDifferentArity) {
+ msg += ". The namespace URI and local name are recognized, but the number of arguments is wrong";
+ } else {
+ String actualURI = functionName.getURI();
+ String similarNamespace = NamespaceConstant.findSimilarNamespace(actualURI);
+ if (similarNamespace != null) {
+ if (similarNamespace.equals(actualURI)) {
+ if (similarNamespace.equals(NamespaceConstant.SAXON) && env.getConfiguration().getEditionCode().equals("HE")) {
+ msg += ". Saxon extension functions are not available under Saxon-HE";
+ } else if (similarNamespace.equals(NamespaceConstant.SAXON) &&
+ !env.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.PROFESSIONAL_EDITION)) {
+ msg += ". Saxon extension functions require a Saxon-PE or Saxon-EE license";
+ } else {
+ msg += ". There is no Saxon extension function with the local name " + functionName.getLocalPart();
+ }
+ } else {
+ msg += ". Perhaps the intended namespace was '" + similarNamespace + "'";
+ }
+ } else if (actualURI.contains("java")) {
+ if (env.getConfiguration().getEditionCode().equals("HE")) {
+ msg += ". Note that direct calls to Java methods are not available under Saxon-HE";
+ } else {
+ msg += ". For diagnostics on calls to Java methods, use the -TJ command line option " +
+ "or set the Configuration property FeatureKeys.TRACE_EXTERNAL_FUNCTIONS";
+ }
+ } else if (actualURI.startsWith("clitype:")) {
+ if (env.getConfiguration().getEditionCode().equals("HE")) {
+ msg += ". Note that direct calls to external .NET methods are not available under Saxon-HE";
+ } else {
+ msg += ". For diagnostics on calls to .NET methods, use the -TJ command line option " +
+ "or call processor.SetProperty(\"http://saxon.sf.net/feature/trace-external-functions\", \"true\")";
+ }
+ }
+ }
+ } else {
+ msg += ". External function calls have been disabled";
+ }
+ if (env.isInBackwardsCompatibleMode()) {
+ // treat this as a dynamic error to be reported only if the function call is executed
+ XPathException err = new XPathException(msg, "XTDE1425");
+ ErrorExpression exp = new ErrorExpression(err);
+ setLocation(exp);
+ return exp;
+ }
+ grumble(msg, "XPST0017", offset);
+ return new ErrorExpression();
+ }
+
+ /**
+ * Interpret a function name, returning it as a resolved QName
+ *
+ * @param fname the lexical QName used as the function name; or an EQName presented
+ * by the tokenizer as a name in Clark notation
+ * @return the Structured QName obtained by resolving any prefix in the function name
+ * @throws XPathException if the supplied name is not a valid QName or if its prefix
+ * is not in scope
+ */
+
+ /*@NotNull*/
+ protected StructuredQName resolveFunctionName(/*@NotNull*/ String fname) throws XPathException {
+ StructuredQName functionName;
+ String uri;
+ String local;
+ int offset = t.currentTokenStartOffset;
+ if (fname.startsWith("{")) {
+ if (!allowXPath30Syntax) {
+ grumble("Expanded QName syntax requires XPath 3.0/XQuery 3.0");
+ }
+ functionName = StructuredQName.fromClarkName(fname);
+ uri = functionName.getURI();
+ local = functionName.getLocalPart();
+ } else {
+ String[] parts;
+ try {
+ parts = nameChecker.getQNameParts(fname);
+ } catch (QNameException e) {
+ grumble("Function name is not a valid QName: " + fname + "()", "XPST0003", offset);
+ throw new XPathException(""); // unreachable instruction
+ }
+ local = parts[1];
+ if (parts[0].length() == 0) {
+ uri = env.getDefaultFunctionNamespace();
+ } else {
+ try {
+ uri = env.getURIForPrefix(parts[0]);
+ } catch (XPathException err) {
+ grumble(err.getMessage(), "XPST0081", offset);
+ throw err; // unreachable instruction
+ }
+ }
+ functionName = new StructuredQName(parts[0], uri, local);
+ }
+
+ if (uri.equals(NamespaceConstant.SCHEMA)) {
+ ItemType t = Type.getBuiltInItemType(uri, local);
+ if (t instanceof BuiltInAtomicType && !env.isAllowedBuiltInType((BuiltInAtomicType) t)) {
+ grumble("The type " + fname + " is not recognized by a Basic XSLT Processor. ", "XPST0080", offset);
+ throw new XPathException(""); // unreachable instruction
+ }
+ }
+ if (local.equals("serialize") && uri.equals(NamespaceConstant.FN) && env instanceof QueryModule) {
+ String defaultUri = (allowXPath30Syntax ? NamespaceConstant.XQUERY : "");
+ StructuredQName serializationQName = makeStructuredQName("serialization", defaultUri);
+ if (((QueryModule) env).getFeaturesProhibited().contains(serializationQName)) {
+ grumble("The fn:serialize function is not allowed when the serialization feature is disabled (see line number: " + getTokenizer().getLineNumber() + ")", "XQST0128");
+ }
+ }
+ return functionName;
+ }
+
+ /**
+ * Parse an argument to a function call. Separate method so it can
+ * be overridden. With higher-order-function syntax in XPath 3.0/XQuery 3.0,
+ * this returns null if the pseudo-argument "?" is found.
+ *
+ * @return the Expression used as the argument, or null if the argument is the place-holder "?"
+ * @throws XPathException if the argument expression does not parse correctly
+ */
+
+ /*@Nullable*/
+ public Expression parseFunctionArgument() throws XPathException {
+ return parseExprSingle();
+ }
+
+ /**
+ * Parse a literal function item (introduced in XQuery 1.1)
+ * Syntax: QName # integer
+ * The QName and # have already been read
+ *
+ * @return an ExternalObject representing the function item
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error is encountered
+ */
+
+ /*@NotNull*/
+ protected Expression parseLiteralFunctionItem() throws XPathException {
+ grumble("Literal function items are allowed only in XPath 3.0 / XQuery 3.0");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Parse an inline function
+ * "function" "(" ParamList? ")" ("as" SequenceType)? EnclosedExpr
+ * On entry, "function (" has already been read
+ * @param annotations the function annotations, which have already been read, or null if there are none
+ * @return the parsed inline function
+ * @throws XPathException if a syntax error is found
+ */
+
+ /*@NotNull*/
+ protected Expression parseInlineFunction(Map<StructuredQName, Annotation> annotations) throws XPathException {
+ grumble("Inline functions are allowed only in XQuery 3.0");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Process a function call in which one or more of the argument positions are
+ * represented as "?" placemarkers (indicating partial application or currying)
+ *
+ * @param offset the position of the expression in the source text
+ * @param name the function name (as if there were no currying)
+ * @param args the arguments (with EmptySequence in the placemarker positions)
+ * @param placeMarkers the positions of the placemarkers @return the curried function
+ * @return the curried function
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ protected Expression makeCurriedFunction(
+ int offset, StructuredQName name, Expression[] args, IntSet placeMarkers)
+ throws XPathException {
+ grumble("Partial function application is allowed only in XPath/XQuery 3.0");
+ return new ErrorExpression();
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // Routines for handling range variables
+ //////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Get the stack of in-scope range variables
+ *
+ * @return the stack of variables
+ */
+
+ public Stack<Binding> getRangeVariables() {
+ return rangeVariables;
+ }
+
+ /**
+ * Set a new stack of in-scope range variables
+ *
+ * @param variables the stack of variables
+ */
+
+ public void setRangeVariables(Stack<Binding> variables) {
+ this.rangeVariables = variables;
+ }
+
+ /**
+ * Declare a range variable (record its existence within the parser).
+ * A range variable is a variable declared within an expression, as distinct
+ * from a variable declared in the context.
+ *
+ * @param declaration the variable declaration to be added to the stack
+ * @throws XPathException if any error is encountered
+ */
+
+ public void declareRangeVariable(Binding declaration) throws XPathException {
+ rangeVariables.push(declaration);
+ }
+
+ /**
+ * Note when the most recently declared range variable has gone out of scope
+ */
+
+ public void undeclareRangeVariable() {
+ rangeVariables.pop();
+ }
+
+ /**
+ * Locate a range variable with a given name. (By "range variable", we mean a
+ * variable declared within the expression where it is used.)
+ *
+ * @param qName identifies the name of the range variable
+ * @return null if not found (this means the variable is probably a
+ * context variable); otherwise the relevant RangeVariable
+ */
+
+ /*@Nullable*/
+ protected Binding findRangeVariable(StructuredQName qName) {
+ for (int v = rangeVariables.size() - 1; v >= 0; v--) {
+ Binding b = rangeVariables.elementAt(v);
+ if (b.getVariableQName().equals(qName)) {
+ return b;
+ }
+ }
+ return null; // not an in-scope range variable
+ }
+
+ /**
+ * Set the range variable stack. Used when parsing a nested subexpression
+ * inside an attribute constructor.
+ *
+ * @param stack the stack to be used for local variables declared within the expression
+ */
+
+ public void setRangeVariableStack(Stack<Binding> stack) {
+ rangeVariables = stack;
+ }
+
+ /**
+ * Make a NameCode, using the static context for namespace resolution
+ *
+ * @param qname The name as written, in the form "[prefix:]localname"; alternatively,
+ * a QName in Clark notation ({uri}local)
+ * @param useDefault Defines the action when there is no prefix. If
+ * true, use the default namespace URI for element names. If false,
+ * use no namespace URI (as for attribute names).
+ * @return the namecode, which can be used to identify this name in the
+ * name pool
+ * @throws XPathException if the name is invalid, or the prefix
+ * undeclared
+ */
+
+ public final int makeNameCode(/*@NotNull*/ String qname, boolean useDefault) throws XPathException {
+ if (scanOnly) {
+ return StandardNames.XML_SPACE;
+ }
+ if (qname.startsWith("{")) {
+ return env.getNamePool().allocateClarkName(qname);
+ }
+ try {
+ String[] parts = nameChecker.getQNameParts(qname);
+ String prefix = parts[0];
+ if (prefix.length() == 0) {
+ if (useDefault) {
+ String uri = env.getDefaultElementNamespace();
+ return env.getNamePool().allocate("", uri, qname);
+ } else {
+ return env.getNamePool().allocate("", "", qname);
+ }
+ } else {
+ try {
+ String uri = env.getURIForPrefix(prefix);
+ return env.getNamePool().allocate(prefix, uri, parts[1]);
+ } catch (XPathException err) {
+ grumble(err.getMessage(), err.getErrorCodeQName(), -1);
+ return -1;
+ }
+ }
+ } catch (QNameException e) {
+ grumble(e.getMessage());
+ return -1;
+ }
+ }
+
+ /**
+ * Make a NameCode, using the static context for namespace resolution.
+ * This variant of the method does not call "grumble" to report any errors
+ * to the ErrorListener, it only reports errors by throwing exceptions. This
+ * allows the caller to control the message output.
+ *
+ * @param qname The name as written, in the form "[prefix:]localname"
+ * @param defaultUri Defines the action when there is no prefix. If
+ * true, use the default namespace URI for element names. If false,
+ * use no namespace URI (as for attribute names).
+ * @return the namecode, which can be used to identify this name in the
+ * name pool
+ * @throws XPathException if the name is invalid, or the prefix
+ * undeclared
+ * @throws QNameException if the name is not a lexically valid QName
+ */
+
+ public final StructuredQName makeStructuredQNameSilently(/*@NotNull*/ String qname, String defaultUri)
+ throws XPathException, QNameException {
+ if (scanOnly) {
+ return new StructuredQName("", NamespaceConstant.SAXON, "dummy");
+ }
+ String[] parts = nameChecker.getQNameParts(qname);
+ String prefix = parts[0];
+ if (prefix.length() == 0) {
+ return new StructuredQName("", defaultUri, qname);
+ } else {
+ String uri = env.getURIForPrefix(prefix);
+ return new StructuredQName(prefix, uri, parts[1]);
+ }
+ }
+
+ /**
+ * Make a Structured QName, using the static context for namespace resolution
+ *
+ *
+ *
+ * @param qname The name as written, in the form "[prefix:]localname"; alternatively, a QName in
+ * Clark format ({uri}local)
+ * @param defaultUri The URI to be used if the name is written as a localname with no prefix
+ * @return the QName as an instance of StructuredQName
+ * @throws XPathException if the name is invalid, or the prefix
+ * undeclared
+ */
+
+ /*@NotNull*/
+ public final StructuredQName makeStructuredQName(/*@NotNull*/ String qname, String defaultUri) throws XPathException {
+ if (scanOnly) {
+ return new StructuredQName("", NamespaceConstant.SAXON, "dummy");
+ }
+ if (qname.startsWith("{")) {
+ return StructuredQName.fromClarkName(qname);
+ }
+ try {
+ String[] parts = nameChecker.getQNameParts(qname);
+ String prefix = parts[0];
+ if (prefix.length() == 0) {
+ return new StructuredQName("", defaultUri, qname);
+ } else {
+ try {
+ String uri = env.getURIForPrefix(prefix);
+ return new StructuredQName(prefix, uri, parts[1]);
+ } catch (XPathException err) {
+ grumble(err.getMessage(), err.getErrorCodeQName(), -1);
+ throw err; // dummy instruction to mollify the compiler
+ }
+ }
+ } catch (QNameException e) {
+ grumble(e.getMessage());
+ throw new XPathException(e); // dummy instruction to mollify the compiler
+ }
+ }
+
+ /**
+ * Make a FingerprintedQName, using the static context for namespace resolution
+ *
+ * @param qname The name as written, in the form "[prefix:]localname"; alternatively, a QName in
+ * Clark format ({uri}local)
+ * @param useDefault Defines the action when there is no prefix. If
+ * true, use the default namespace URI for element names. If false,
+ * use no namespace URI (as for attribute names).
+ * @return the fingerprinted QName
+ * @throws XPathException if the name is invalid, or the prefix
+ * undeclared
+ * @throws QNameException if the supplied qname is not a lexically valid QName
+ */
+
+ /*@NotNull*/
+ public final NodeName makeNodeName(/*@NotNull*/ String qname, boolean useDefault) throws XPathException, QNameException {
+ if (scanOnly) {
+ return new NoNamespaceName("dummy");
+ }
+ if (qname.startsWith("{")) {
+ return FingerprintedQName.fromClarkName(qname);
+ }
+ String[] parts = nameChecker.getQNameParts(qname);
+ String prefix = parts[0];
+ if (prefix.length() == 0) {
+ if (useDefault) {
+ String uri = env.getDefaultElementNamespace();
+ int nc = env.getNamePool().allocate("", uri, qname);
+ return new FingerprintedQName("", uri, qname, nc);
+ } else {
+ int nc = env.getNamePool().allocate("", "", qname);
+ return new NoNamespaceName(qname, nc);
+ }
+ } else {
+ String uri = env.getURIForPrefix(prefix);
+ int nc = env.getNamePool().allocate(prefix, uri, parts[1]);
+ return new FingerprintedQName(prefix, uri, parts[1], nc);
+ }
+ }
+
+
+ /**
+ * Make a NameTest, using the static context for namespace resolution
+ *
+ * @param nodeType the type of node required (identified by a constant in
+ * class Type)
+ * @param qname the lexical QName of the required node; alternatively,
+ * a QName in Clark notation ({uri}local)
+ * @param useDefault true if the default namespace should be used when
+ * the QName is unprefixed
+ * @return a NameTest, representing a pattern that tests for a node of a
+ * given node kind and a given name
+ * @throws XPathException if the QName is invalid
+ */
+
+ /*@NotNull*/
+ public NameTest makeNameTest(short nodeType, /*@NotNull*/ String qname, boolean useDefault)
+ throws XPathException {
+ int nameCode = makeNameCode(qname, useDefault);
+ return new NameTest(nodeType, nameCode, env.getNamePool());
+ }
+
+ /**
+ * Make a NamespaceTest (name:*)
+ *
+ * @param nodeType integer code identifying the type of node required
+ * @param prefix the namespace prefix
+ * @return the NamespaceTest, a pattern that matches all nodes in this
+ * namespace
+ * @throws XPathException if the namespace prefix is not declared
+ */
+
+ /*@NotNull*/
+ public NamespaceTest makeNamespaceTest(short nodeType, /*@NotNull*/ String prefix)
+ throws XPathException {
+ if (scanOnly) {
+ // return an arbitrary namespace if we're only doing a syntax check
+ return new NamespaceTest(env.getNamePool(), nodeType, NamespaceConstant.SAXON);
+ }
+
+ String uri = "";
+ if (prefix.charAt(0) == '{') {
+ // EQName wildcard syntax "uri":* delivered by the tokenizer as {uri}*
+ int closeBrace = prefix.indexOf('}');
+ uri = prefix.substring(1, closeBrace);
+ } else {
+ try {
+ uri = env.getURIForPrefix(prefix);
+ } catch (XPathException e) {
+ // env.getURIForPrefix can return a dynamic error
+ grumble(e.getMessage(), "XPST0081");
+ }
+ }
+
+ return new NamespaceTest(env.getNamePool(), nodeType, uri);
+
+ }
+
+ /**
+ * Make a LocalNameTest (*:name)
+ *
+ * @param nodeType the kind of node to be matched
+ * @param localName the requred local name
+ * @return a LocalNameTest, a pattern which matches all nodes of a given
+ * local name, regardless of namespace
+ * @throws XPathException if the local name is invalid
+ */
+
+ /*@NotNull*/
+ public LocalNameTest makeLocalNameTest(short nodeType, String localName)
+ throws XPathException {
+ if (!nameChecker.isValidNCName(localName)) {
+ grumble("Local name [" + localName + "] contains invalid characters");
+ }
+ return new LocalNameTest(env.getNamePool(), nodeType, localName);
+ }
+
+ /**
+ * Set location information on an expression. At present this consists of a simple
+ * line number. Needed mainly for XQuery.
+ *
+ * @param exp the expression whose location information is to be set
+ */
+
+ protected void setLocation(/*@NotNull*/ Expression exp) {
+ setLocation(exp, t.currentTokenStartOffset);
+ }
+
+ /**
+ * Set location information on an expression. At present only the line number
+ * is retained. Needed mainly for XQuery. This version of the method supplies an
+ * explicit offset (character position within the expression or query), which the tokenizer
+ * can convert to a line number and column number.
+ *
+ * @param exp the expression whose location information is to be set
+ * @param offset the character position within the expression (ignoring newlines)
+ */
+
+ public void setLocation(/*@NotNull*/ Expression exp, int offset) {
+ // Although we could get the column position from the offset, we choose not to retain this,
+ // and only use the line number
+ if (exp != null) {
+ int line = t.getLineNumber(offset);
+ if (exp.getLocationId() == -1) {
+ int loc = env.getLocationMap().allocateLocationId(env.getSystemId(), line);
+ exp.setLocationId(loc);
+ // add a temporary container to provide location information
+ }
+ if (exp.getContainer() == null) {
+ exp.setContainer(defaultContainer);
+ }
+ }
+ }
+
+ /**
+ * Set location information on a clause of a FLWOR expression. At present only the line number
+ * is retained. Needed mainly for XQuery. This version of the method supplies an
+ * explicit offset (character position within the expression or query), which the tokenizer
+ * can convert to a line number and column number.
+ *
+ * @param clause the clause whose location information is to be set
+ * @param offset the character position within the expression (ignoring newlines)
+ */
+
+ public void setLocation(/*@NotNull*/ Clause clause, int offset) {
+ // Although we could get the column position from the offset, we choose not to retain this,
+ // and only use the line number
+ int line = t.getLineNumber(offset);
+ int loc = env.getLocationMap().allocateLocationId(env.getSystemId(), line);
+ clause.setLocationId(loc);
+ }
+
+
+ /**
+ * If tracing, wrap an expression in a trace instruction
+ *
+ * @param startOffset the position of the expression in the soruce
+ * @param exp the expression to be wrapped
+ * @param construct integer constant identifying the kind of construct
+ * @param qName the name of the construct (if applicable)
+ * @return the expression that does the tracing
+ */
+
+ public Expression makeTracer(int startOffset, Expression exp, int construct, /*@Nullable*/ StructuredQName qName) {
+ if (codeInjector != null) {
+ return codeInjector.inject(exp, env, construct, qName);
+ } else {
+ return exp;
+ }
+ }
+
+ /**
+ * Test whether the current token is a given keyword.
+ *
+ * @param s The string to be compared with the current token
+ * @return true if they are the same
+ */
+
+ protected boolean isKeyword(String s) {
+ return (t.currentToken == Token.NAME && t.currentTokenValue.equals(s));
+ }
+
+ /**
+ * Normalize an EQName. This is written in the source code in the form "uri":local, but by the
+ * time it gets here it has been converted to Clark format {uri}local. This method collapses
+ * whitespace within the URI
+ *
+ * @param s the EQName in the form of a Clark name
+ * @return the normalized EQName
+ * @throws XPathException so that the XQuery implementation in a subclass can do so.
+ */
+
+ protected String normalizeEQName(String s) throws XPathException {
+ // overridden for XQuery
+ if (!Whitespace.containsWhitespace(s)) {
+ return s;
+ }
+ StructuredQName sq = StructuredQName.fromClarkName(s);
+ CharSequence uri = Whitespace.collapseWhitespace(sq.getURI());
+ return "{" + uri + "}" + sq.getLocalPart();
+ }
+
+ /**
+ * Set that we are parsing in "scan only"
+ *
+ * @param scanOnly true if parsing is to proceed in scan-only mode. In this mode
+ * namespace bindings are not yet known, so no attempt is made to look up namespace
+ * prefixes.
+ */
+
+ public void setScanOnly(boolean scanOnly) {
+ this.scanOnly = scanOnly;
+ }
+
+ /**
+ * A Container used on a temporary basis to hold an expression while it is being parsed
+ */
+
+ protected static class TemporaryContainer implements Container, LocationProvider, Serializable {
+ private LocationMap map;
+ private int locationId;
+ private Executable executable;
+
+ public TemporaryContainer(LocationMap map, int locationId) {
+ this.map = map;
+ this.locationId = locationId;
+ }
+
+ /**
+ * Get the granularity of the container.
+ *
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 0;
+ }
+
+ public void setExecutable(Executable exec) {
+ executable = exec;
+ }
+
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ public LocationProvider getLocationProvider() {
+ return map;
+ }
+
+ /*@Nullable*/
+ public String getPublicId() {
+ return null;
+ }
+
+ /*@Nullable*/
+ public String getSystemId() {
+ return map.getSystemId(locationId);
+ }
+
+ public int getLineNumber() {
+ return map.getLineNumber(locationId);
+ }
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /*@Nullable*/
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ *
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+
+ public int getHostLanguage() {
+ return Configuration.XPATH;
+ }
+
+ }
+
+}
+
+/*
+
+The following copyright notice is copied from the licence for xt, from which the
+original version of this module was derived:
+--------------------------------------------------------------------------------
+Copyright (c) 1998, 1999 James Clark
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL JAMES CLARK BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of James Clark shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from James Clark.
+---------------------------------------------------------------------------
+*/
\ No newline at end of file
diff --git a/sf/saxon/expr/parser/ExpressionTool.java b/sf/saxon/expr/parser/ExpressionTool.java
new file mode 100644
index 0000000..de453f8
--- /dev/null
+++ b/sf/saxon/expr/parser/ExpressionTool.java
@@ -0,0 +1,1420 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.SequenceOutputter;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.flwor.Clause;
+import net.sf.saxon.expr.flwor.FLWORExpression;
+import net.sf.saxon.expr.flwor.LocalVariableBinding;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.sort.ConditionalSorter;
+import net.sf.saxon.expr.sort.DocumentSorter;
+import net.sf.saxon.functions.Current;
+import net.sf.saxon.functions.EscapeURI;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.PatternSponsor;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import javax.xml.transform.SourceLocator;
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class, ExpressionTool, contains a number of useful static methods
+ * for manipulating expressions. Most importantly, it provides the factory
+ * method make() for constructing a new expression
+ */
+
+public class ExpressionTool {
+
+ public static final int UNDECIDED = -1;
+ public static final int NO_EVALUATION_NEEDED = 0;
+ public static final int EVALUATE_VARIABLE = 1;
+ public static final int MAKE_CLOSURE = 3;
+ public static final int MAKE_MEMO_CLOSURE = 4;
+ public static final int RETURN_EMPTY_SEQUENCE = 5;
+ public static final int EVALUATE_AND_MATERIALIZE_VARIABLE = 6;
+ public static final int CALL_EVALUATE_ITEM = 7;
+ public static final int ITERATE_AND_MATERIALIZE = 8;
+ public static final int PROCESS = 9;
+ public static final int LAZY_TAIL_EXPRESSION = 10;
+ public static final int SHARED_APPEND_EXPRESSION = 11;
+ public static final int MAKE_INDEXED_VARIABLE = 12;
+ public static final int MAKE_SINGLETON_CLOSURE = 13;
+ public static final int EVALUATE_SUPPLIED_PARAMETER = 14;
+
+ private ExpressionTool() {
+ }
+
+ /**
+ * Parse an XPath expression. This performs the basic analysis of the expression against the
+ * grammar, it binds variable references and function calls to variable definitions and
+ * function definitions, and it performs context-independent expression rewriting for
+ * optimization purposes.
+ *
+ * @param expression The expression (as a character string)
+ * @param env An object giving information about the compile-time
+ * context of the expression
+ * @param container The expression's container
+ * @param start position of the first significant character in the expression
+ * @param terminator The token that marks the end of this expression; typically
+ * Token.EOF, but may for example be a right curly brace
+ * @param lineNumber the line number of the start of the expression
+ * @param codeInjector true allows injection of tracing, debugging, or performance monitoring code; null if
+ * not required
+ * @return an object of type Expression
+ * @throws XPathException if the expression contains a static error
+ */
+
+ /*@NotNull*/
+ public static Expression make(/*@NotNull*/ String expression, StaticContext env,
+ Container container, int start, int terminator, int lineNumber,
+ /*@Nullable*/ CodeInjector codeInjector) throws XPathException {
+ ExpressionParser parser = env.getConfiguration().newExpressionParser("XP", false, env.getXPathLanguageLevel());
+ parser.setDefaultContainer(container);
+ if (codeInjector != null) {
+ parser.setCodeInjector(codeInjector);
+ }
+ if (terminator == -1) {
+ terminator = Token.EOF;
+ }
+ Expression exp = parser.parse(expression, start, terminator, lineNumber, env);
+ exp = ExpressionVisitor.make(env, exp.getExecutable()).simplify(exp);
+ return exp;
+ }
+
+ /**
+ * Copy location information (the line number and reference to the container) from one expression
+ * to another
+ *
+ * @param from the expression containing the location information
+ * @param to the expression to which the information is to be copied
+ */
+
+ public static void copyLocationInfo(Expression from, Expression to) {
+ if (from != null && to != null) {
+ if(to.getLocationId() == -1) {
+ to.setLocationId(from.getLocationId());
+ }
+ to.setContainer(from.getContainer());
+ }
+ }
+
+ /**
+ * Remove unwanted sorting from an expression, at compile time
+ *
+ * @param opt the expression optimizer
+ * @param exp the expression to be optimized
+ * @param retainAllNodes true if there is a need to retain exactly those nodes returned by exp
+ * even if there are duplicates; false if the caller doesn't mind whether duplicate nodes
+ * are retained or eliminated
+ * @return the expression after rewriting
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error is found while doing the rewrite
+ */
+
+ public static Expression unsorted(Optimizer opt, Expression exp, boolean retainAllNodes)
+ throws XPathException {
+ if (exp instanceof Literal) {
+ return exp; // fast exit
+ }
+ PromotionOffer offer = new PromotionOffer(opt);
+ offer.action = PromotionOffer.UNORDERED;
+ offer.retainAllNodes = retainAllNodes;
+ return exp.promote(offer, null);
+ }
+
+ /**
+ * Remove unwanted sorting from an expression, at compile time, if and only if it is known
+ * that the result of the expression will be homogeneous (all nodes, or all atomic values).
+ * This is done when we need the effective boolean value of a sequence: the EBV of a
+ * homogenous sequence does not depend on its order, but this is not true when atomic
+ * values and nodes are mixed: (N, AV) is true, but (AV, N) is an error.
+ *
+ * @param opt the expression optimizer
+ * @param exp the expression to be optimized
+ * @return the expression after rewriting
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error is found while doing the rewrite
+ */
+
+ public static Expression unsortedIfHomogeneous(Optimizer opt, Expression exp)
+ throws XPathException {
+ if (exp instanceof Literal) {
+ return exp; // fast exit
+ }
+ if (exp.getItemType(opt.getConfiguration().getTypeHierarchy()) instanceof AnyItemType) {
+ return exp;
+ } else {
+ PromotionOffer offer = new PromotionOffer(opt);
+ offer.action = PromotionOffer.UNORDERED;
+ offer.retainAllNodes = false;
+ return exp.promote(offer, null);
+ }
+ }
+
+ /**
+ * Determine the method of evaluation to be used when lazy evaluation of an expression is
+ * preferred. This method is called at compile time, after all optimizations have been done,
+ * to determine the preferred strategy for lazy evaluation, depending on the type of expression.
+ *
+ * @param exp the expression to be evaluated
+ * @return an integer constant identifying the evaluation mode
+ */
+
+ public static int lazyEvaluationMode(Expression exp) {
+ if (exp instanceof Literal) {
+ return NO_EVALUATION_NEEDED;
+
+ } else if (exp instanceof VariableReference) {
+ return EVALUATE_VARIABLE;
+
+ } else if (exp instanceof SuppliedParameterReference) {
+ return EVALUATE_SUPPLIED_PARAMETER;
+
+ } else if ((exp.getDependencies() &
+ (StaticProperty.DEPENDS_ON_POSITION |
+ StaticProperty.DEPENDS_ON_LAST |
+ StaticProperty.DEPENDS_ON_CURRENT_ITEM |
+ StaticProperty.DEPENDS_ON_CURRENT_GROUP |
+ StaticProperty.DEPENDS_ON_REGEX_GROUP)) != 0) {
+ // we can't save these values in the closure, so we evaluate
+ // the expression now if they are needed
+ return eagerEvaluationMode(exp);
+
+ } else if (exp instanceof ErrorExpression) {
+ return CALL_EVALUATE_ITEM;
+ // evaluateItem() on an error expression throws the latent exception
+
+ } else if (!Cardinality.allowsMany(exp.getCardinality())) {
+ // singleton expressions are always evaluated eagerly
+ return eagerEvaluationMode(exp);
+
+ } else if (exp instanceof TailExpression) {
+ // Treat tail recursion as a special case, to avoid creating a deeply-nested
+ // tree of Closures. If this expression is a TailExpression, and its first
+ // argument is also a TailExpression, we combine the two TailExpressions into
+ // one and return a closure over that.
+ TailExpression tail = (TailExpression) exp;
+ Expression base = tail.getBaseExpression();
+ if (base instanceof VariableReference) {
+ return LAZY_TAIL_EXPRESSION;
+ } else {
+ return MAKE_CLOSURE;
+ }
+
+ } else if (exp instanceof Block && ((Block) exp).isCandidateForSharedAppend()) {
+ // If the expression is a Block, that is, it is appending a value to a sequence,
+ // then we have the opportunity to use a shared list underpinning the old value and
+ // the new. This takes precedence over lazy evaluation (it would be possible to do this
+ // lazily, but more difficult). We currently do this for any Block that has a variable
+ // reference as one of its subexpressions. The most common case is that the first argument is a reference
+ // to an argument of recursive function, where the recursive function returns the result of
+ // appending to the sequence.
+ return SHARED_APPEND_EXPRESSION;
+
+ } else {
+ // create a Closure, a wrapper for the expression and its context
+ return MAKE_CLOSURE;
+ }
+ }
+
+ /**
+ * Determine the method of evaluation to be used when lazy evaluation of an expression is
+ * preferred. This method is called at compile time, after all optimizations have been done,
+ * to determine the preferred strategy for lazy evaluation, depending on the type of expression.
+ *
+ * @param exp the expression to be evaluated
+ * @return an integer constant identifying the evaluation mode
+ */
+
+ public static int eagerEvaluationMode(Expression exp) {
+ if (exp instanceof Literal && !(((Literal) exp).getValue() instanceof Closure)) {
+ return NO_EVALUATION_NEEDED;
+ }
+ if (exp instanceof VariableReference) {
+ return EVALUATE_AND_MATERIALIZE_VARIABLE;
+ }
+ int m = exp.getImplementationMethod();
+ if ((m & Expression.EVALUATE_METHOD) != 0) {
+ return CALL_EVALUATE_ITEM;
+ } else if ((m & Expression.ITERATE_METHOD) != 0) {
+ return ITERATE_AND_MATERIALIZE;
+ } else {
+ return PROCESS;
+ }
+ }
+
+
+ /**
+ * Do lazy evaluation of an expression. This will return a value, which may optionally
+ * be a SequenceIntent, which is a wrapper around an iterator over the value of the expression.
+ *
+ * @param exp the expression to be evaluated
+ * @param evaluationMode the evaluation mode for this expression
+ * @param context the run-time evaluation context for the expression. If
+ * the expression is not evaluated immediately, then parts of the
+ * context on which the expression depends need to be saved as part of
+ * the Closure
+ * @param ref an indication of how the value will be used. The value 1 indicates that the value
+ * is only expected to be used once, so that there is no need to keep it in memory. A small value >1
+ * indicates multiple references, so the value will be saved when first evaluated. The special value
+ * FILTERED indicates a reference within a loop of the form $x[predicate], indicating that the value
+ * should be saved in a way that permits indexing.
+ * @return a value: either the actual value obtained by evaluating the
+ * expression, or a Closure containing all the information needed to
+ * evaluate it later
+ * @throws XPathException if any error occurs in evaluating the
+ * expression
+ */
+
+ public static Sequence evaluate(Expression exp, int evaluationMode, XPathContext context, int ref)
+ throws XPathException {
+ switch (evaluationMode) {
+
+ case NO_EVALUATION_NEEDED:
+ return ((Literal) exp).getValue();
+
+ case EVALUATE_VARIABLE:
+ return ((VariableReference) exp).evaluateVariable(context);
+
+ case EVALUATE_SUPPLIED_PARAMETER:
+ return ((SuppliedParameterReference) exp).evaluateVariable(context);
+
+ case MAKE_CLOSURE:
+ return Closure.make(exp, context, ref);
+ //return new SequenceExtent(exp.iterate(context));
+
+ case MAKE_MEMO_CLOSURE:
+ return Closure.make(exp, context, (ref == 1 ? 10 : ref));
+
+ case MAKE_SINGLETON_CLOSURE:
+ return new SingletonClosure(exp, context);
+
+ case RETURN_EMPTY_SEQUENCE:
+ return EmptySequence.getInstance();
+
+ case EVALUATE_AND_MATERIALIZE_VARIABLE:
+ Sequence v = ((VariableReference) exp).evaluateVariable(context);
+ if (v instanceof Closure) {
+ return SequenceExtent.makeSequenceExtent(v.iterate());
+ } else {
+ return v;
+ }
+
+ case CALL_EVALUATE_ITEM:
+ Item item = exp.evaluateItem(context);
+ if (item == null) {
+ return EmptySequence.getInstance();
+ } else {
+ return item;
+ }
+
+ case UNDECIDED:
+ case ITERATE_AND_MATERIALIZE:
+ if (ref == FilterExpression.FILTERED) {
+ return context.getConfiguration().makeSequenceExtent(exp, ref, context);
+ } else {
+ return SequenceExtent.makeSequenceExtent(exp.iterate(context));
+ }
+
+ case PROCESS:
+ Controller controller = context.getController();
+ SequenceReceiver saved = context.getReceiver();
+ SequenceOutputter seq = controller.allocateSequenceOutputter(20);
+ seq.getPipelineConfiguration().setHostLanguage(exp.getHostLanguage());
+ context.setReceiver(seq);
+ seq.open();
+ exp.process(context);
+ seq.close();
+ context.setReceiver(saved);
+ Sequence val = seq.getSequence();
+ seq.reset();
+ return val;
+
+ case LAZY_TAIL_EXPRESSION: {
+ TailExpression tail = (TailExpression) exp;
+ VariableReference vr = (VariableReference) tail.getBaseExpression();
+ Sequence base = evaluate(vr, EVALUATE_VARIABLE, context, ref);
+ if (base instanceof MemoClosure) {
+ SequenceIterator it = base.iterate();
+ base = SequenceTool.toGroundedValue(it);
+ }
+ if (base instanceof IntegerRange) {
+ long start = ((IntegerRange) base).getStart() + tail.getStart() - 1;
+ long end = ((IntegerRange) base).getEnd();
+ if (start == end) {
+ return Int64Value.makeIntegerValue(end);
+ } else if (start > end) {
+ return EmptySequence.getInstance();
+ } else {
+ return new IntegerRange(start, end);
+ }
+ }
+ if (base instanceof SequenceExtent) {
+ return new SequenceExtent(
+ (SequenceExtent) base,
+ tail.getStart() - 1,
+ ((SequenceExtent) base).getLength() - tail.getStart() + 1);
+ }
+
+ return Closure.make(tail, context, ref);
+ }
+
+ case SHARED_APPEND_EXPRESSION: {
+ if (exp instanceof Block) {
+ Block block = (Block) exp;
+ Expression[] children = block.getChildren();
+ List<GroundedValue> subsequences = new ArrayList<GroundedValue>(children.length);
+ for (Expression child : children) {
+ if (Cardinality.allowsMany(child.getCardinality())) {
+ subsequences.add(SequenceTool.toGroundedValue(child.iterate(context)));
+ } else {
+ Item j = child.evaluateItem(context);
+ if (j != null) {
+ subsequences.add(j instanceof GroundedValue ? ((GroundedValue)j) : new SingletonItem(j));
+ }
+ }
+ }
+ return new Chain(subsequences);
+ } else {
+ // it's not a Block: it must have been rewritten after deciding to use this evaluation mode
+ return SequenceExtent.makeSequenceExtent(exp.iterate(context));
+ }
+ }
+
+ case MAKE_INDEXED_VARIABLE:
+ return context.getConfiguration().obtainOptimizer().makeIndexedValue(exp.iterate(context));
+
+ default:
+ throw new IllegalArgumentException("Unknown evaluation mode " + evaluationMode);
+
+ }
+ }
+
+ /**
+ * Do lazy evaluation of an expression. This will return a value, which may optionally
+ * be a SequenceIntent, which is a wrapper around an iterator over the value of the expression.
+ *
+ * @param exp the expression to be evaluated
+ * @param context the run-time evaluation context for the expression. If
+ * the expression is not evaluated immediately, then parts of the
+ * context on which the expression depends need to be saved as part of
+ * the Closure
+ * @param ref an indication of how the value will be used. The value 1 indicates that the value
+ * is only expected to be used once, so that there is no need to keep it in memory. A small value >1
+ * indicates multiple references, so the value will be saved when first evaluated. The special value
+ * FILTERED indicates a reference within a loop of the form $x[predicate], indicating that the value
+ * should be saved in a way that permits indexing.
+ * @return a value: either the actual value obtained by evaluating the
+ * expression, or a Closure containing all the information needed to
+ * evaluate it later
+ * @throws XPathException if any error occurs in evaluating the
+ * expression
+ */
+
+ public static Sequence lazyEvaluate(Expression exp, XPathContext context, int ref) throws XPathException {
+ final int evaluationMode = lazyEvaluationMode(exp);
+ return evaluate(exp, evaluationMode, context, ref);
+ }
+
+ /**
+ * Evaluate an expression now; lazy evaluation is not permitted in this case
+ *
+ * @param exp the expression to be evaluated
+ * @param context the run-time evaluation context
+ * @return the result of evaluating the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public static Sequence eagerEvaluate(Expression exp, XPathContext context) throws XPathException {
+ final int evaluationMode = eagerEvaluationMode(exp);
+ return evaluate(exp, evaluationMode, context, 10);
+ }
+
+ /**
+ * Scan an expression to find and mark any recursive tail function calls
+ *
+ * @param exp the expression to be analyzed
+ * @param qName the name of the containing function
+ * @param arity the arity of the containing function
+ * @return 0 if no tail call was found; 1 if a tail call to a different function was found;
+ * 2 if a tail call to the specified function was found. In this case the
+ * UserFunctionCall object representing the tail function call will also have been marked as
+ * a tail call.
+ */
+
+ public static int markTailFunctionCalls(Expression exp, StructuredQName qName, int arity) {
+ return exp.markTailFunctionCalls(qName, arity);
+ }
+
+ /**
+ * Construct indent string, for diagnostic output
+ *
+ * @param level the indentation level (the number of spaces to return)
+ * @return a string of "level*2" spaces
+ */
+
+ public static String indent(int level) {
+ FastStringBuffer fsb = new FastStringBuffer(level);
+ for (int i = 0; i < level; i++) {
+ fsb.append(" ");
+ }
+ return fsb.toString();
+ }
+
+ /**
+ * Determine whether an expression contains a LocalParamSetter subexpression
+ *
+ * @param exp the expression to be tested
+ */
+
+ public static boolean containsLocalParam(Expression exp) {
+ return contains(exp, true, new ExpressionPredicate() {
+ public boolean matches(Expression e) {
+ return e instanceof LocalParamSetter;
+ }
+ });
+// if (exp instanceof LocalParamSetter) {
+// return true;
+// }
+// for (Iterator<SubExpressionInfo> iter = exp.iterateSubExpressionInfo(); iter.hasNext(); ) {
+// SubExpressionInfo info = iter.next();
+// if (info.hasSameFocus && containsLocalParam(info.expression)) {
+// return true;
+// }
+// }
+// return false;
+ }
+
+ public static boolean containsGroupVariableReference(Expression exp) {
+ return contains(exp, false, new ExpressionPredicate() {
+ public boolean matches(Expression e) {
+ return e instanceof GroupVariableReference;
+ }
+ });
+ }
+
+ /**
+ * Test whether a given expression is, or contains, at any depth, an expression that satisfies a supplied
+ * condition
+ *
+ * @param exp the given expression
+ * @param sameFocusOnly if true, only subexpressions evaluated in the same focus are searched. If false,
+ * all subexpressions are searched
+ * @param predicate the condition to be satisfied
+ * @return true if the given expression is or contains an expression that satisfies the condition.
+ */
+
+ public static boolean contains(Expression exp, boolean sameFocusOnly, ExpressionPredicate predicate) {
+ if (predicate.matches(exp)) {
+ return true;
+ }
+ for (Iterator<SubExpressionInfo> iter = exp.iterateSubExpressionInfo(); iter.hasNext(); ) {
+ SubExpressionInfo info = iter.next();
+ if ((info.hasSameFocus || !sameFocusOnly) && contains(info.expression, sameFocusOnly, predicate)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static interface ExpressionPredicate {
+ public boolean matches(Expression e);
+ }
+
+ /**
+ * Allocate slot numbers to range variables
+ *
+ * @param exp the expression whose range variables need to have slot numbers assigned
+ * @param nextFree the next slot number that is available for allocation
+ * @param frame a SlotManager object that is used to track the mapping of slot numbers
+ * to variable names for debugging purposes. May be null.
+ * @return the next unallocated slot number.
+ */
+
+ public static int allocateSlots(Expression exp, int nextFree, SlotManager frame) {
+ if (exp instanceof Assignation) {
+ ((Assignation) exp).setSlotNumber(nextFree);
+ int count = ((Assignation) exp).getRequiredSlots();
+ nextFree += count;
+ if (frame != null) {
+ frame.allocateSlotNumber(((Assignation) exp).getVariableQName());
+ if (count == 2) {
+ frame.allocateSlotNumber(((ForExpression) exp).getPositionVariableName());
+ }
+ }
+ }
+ if (exp instanceof ForEachGroup) {
+ ForEachGroup feg = (ForEachGroup) exp;
+ if (feg.getGroupBinding() != null) {
+ feg.getGroupBinding().setSlotNumber(nextFree++);
+ }
+ if (feg.getKeyBinding() != null) {
+ feg.getKeyBinding().setSlotNumber(nextFree++);
+ }
+ }
+ if (exp instanceof FLWORExpression) {
+ for (Clause c : ((FLWORExpression) exp).getClauseList()) {
+ for (LocalVariableBinding b : c.getRangeVariables()) {
+ b.setSlotNumber(nextFree++);
+ frame.allocateSlotNumber(b.getVariableQName());
+ }
+ }
+ }
+ if (exp instanceof VariableReference) {
+ VariableReference var = (VariableReference) exp;
+ Binding binding = var.getBinding();
+ if (exp instanceof LocalVariableReference) {
+ ((LocalVariableReference) var).setSlotNumber(binding.getLocalSlotNumber());
+ }
+ if (binding instanceof Assignation && binding.getLocalSlotNumber() < 0) {
+ // This indicates something badly wrong: we've found a variable reference on the tree, that's
+ // bound to a variable declaration that is no longer on the tree. All we can do is print diagnostics.
+ // The most common reason for this failure is that the declaration of the variable was removed
+ // from the tree in the mistaken belief that there were no references to the variable. Variable
+ // references are counted during the typeCheck phase, so this can happen if typeCheck() fails to
+ // visit some branch of the expression tree.
+ Assignation decl = (Assignation) binding;
+ String msg = "*** Internal Saxon error: local variable encountered whose binding has been deleted";
+ System.err.println(msg);
+ System.err.println("Variable name: " + decl.getVariableName());
+ System.err.println("Line number of reference: " + var.getLineNumber() + " in " + var.getSystemId());
+ System.err.println("Line number of declaration: " + decl.getLineNumber() + " in " + decl.getSystemId());
+ System.err.println("DECLARATION:");
+ try {
+ decl.explain(System.err);
+ } catch (Exception err) {
+ // ignore the secondary error
+ }
+ throw new IllegalStateException(msg);
+ }
+
+ }
+
+ if (exp instanceof PatternSponsor) {
+ nextFree = ((PatternSponsor) exp).getPattern().allocateSlots(frame, nextFree);
+ } else {
+ for (Iterator children = exp.iterateSubExpressions(); children.hasNext(); ) {
+ Expression child = (Expression) children.next();
+ nextFree = allocateSlots(child, nextFree, frame);
+ }
+ }
+
+ return nextFree;
+
+ // Note, we allocate a distinct slot to each range variable, even if the
+ // scopes don't overlap. This isn't strictly necessary, but might help
+ // debugging.
+ }
+
+ /**
+ * Determine the effective boolean value of a sequence, given an iterator over the sequence
+ *
+ * @param iterator An iterator over the sequence whose effective boolean value is required
+ * @return the effective boolean value
+ * @throws XPathException if a dynamic error occurs
+ */
+ public static boolean effectiveBooleanValue(SequenceIterator<? extends Item> iterator) throws XPathException {
+ Item first = iterator.next();
+ if (first == null) {
+ return false;
+ }
+ if (first instanceof NodeInfo) {
+ iterator.close();
+ return true;
+ } else if (first instanceof AtomicValue) {
+ if (first instanceof BooleanValue) {
+ if (iterator.next() != null) {
+ ebvError("a sequence of two or more items starting with a boolean");
+ }
+ return ((BooleanValue) first).getBooleanValue();
+ } else if (first instanceof StringValue) { // includes anyURI value
+ if (iterator.next() != null) {
+ ebvError("a sequence of two or more items starting with a string");
+ }
+ return (!((StringValue) first).isZeroLength());
+ } else if (first instanceof NumericValue) {
+ if (iterator.next() != null) {
+ ebvError("a sequence of two or more items starting with a numeric value");
+ }
+ final NumericValue n = (NumericValue) first;
+ return (n.compareTo(0) != 0) && !n.isNaN();
+ } else {
+ ebvError("a sequence starting with an atomic value other than a boolean, number, string, or URI");
+ return false;
+ }
+ } else if (first instanceof FunctionItem) {
+ ebvError("a sequence starting with a function item");
+ return false;
+ } else if (first instanceof ObjectValue) {
+ if (iterator.next() != null) {
+ ebvError("a sequence of two or more items starting with an external object value");
+ }
+ return true;
+ }
+ ebvError("a sequence starting with an item of unknown kind");
+ return false;
+ }
+
+ /**
+ * Determine the effective boolean value of a single item
+ *
+ * @param item the item whose effective boolean value is required
+ * @return the effective boolean value
+ * @throws XPathException if a dynamic error occurs
+ */
+ public static boolean effectiveBooleanValue(Item item) throws XPathException {
+ if (item == null) {
+ return false;
+ }
+ if (item instanceof NodeInfo) {
+ return true;
+ } else {
+ if (item instanceof BooleanValue) {
+ return ((BooleanValue) item).getBooleanValue();
+ } else if (item instanceof StringValue) { // includes anyURI value
+ return !((StringValue) item).isZeroLength();
+ } else if (item instanceof NumericValue) {
+ final NumericValue n = (NumericValue) item;
+ return (n.compareTo(0) != 0) && !n.isNaN();
+ } else if (item instanceof ObjectValue) {
+ // return true if external object reference is not null
+ return (((ObjectValue) item).getObject() != null);
+ } else {
+ ebvError("an item other than a boolean, number, string, or URI");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Report an error in computing the effective boolean value of an expression
+ *
+ * @param reason the nature of the error
+ * @throws XPathException
+ */
+
+ public static void ebvError(String reason) throws XPathException {
+ XPathException err = new XPathException("Effective boolean value is not defined for " + reason);
+ err.setErrorCode("FORG0006");
+ err.setIsTypeError(true);
+ throw err;
+ }
+
+ /**
+ * Ask whether an expression has a dependency on the focus
+ *
+ * @param exp the expression
+ * @return true if the value of the expression depends on the context item, position, or size
+ */
+
+ public static boolean dependsOnFocus(Expression exp) {
+ return ((exp.getDependencies() & StaticProperty.DEPENDS_ON_FOCUS) != 0);
+ }
+
+ /**
+ * Determine whether an expression depends on any one of a set of variables
+ *
+ * @param exp the expression being tested
+ * @param bindingList the set of variables being tested
+ * @return true if the expression depends on one of the given variables
+ */
+
+ public static boolean dependsOnVariable(Expression exp, final Binding[] bindingList) {
+ return !(bindingList == null || bindingList.length == 0) &&
+ contains(exp, false, new ExpressionPredicate() {
+ public boolean matches(Expression e) {
+ if (e instanceof VariableReference) {
+ for (Binding binding : bindingList) {
+ if (((VariableReference) e).getBinding() == binding) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ });
+ // if (e instanceof VariableReference) {
+// for (Binding binding : bindingList) {
+// if (((VariableReference) e).getBinding() == binding) {
+// return true;
+// }
+// }
+// return false;
+//// } else if ((e.getDependencies() & StaticProperty.DEPENDS_ON_LOCAL_VARIABLES) == 0) {
+//// return false;
+// } else {
+// for (Iterator children = e.iterateSubExpressions(); children.hasNext(); ) {
+// Expression child = (Expression) children.next();
+// if (dependsOnVariable(child, bindingList)) {
+// return true;
+// }
+// }
+// return false;
+// }
+ }
+
+ /**
+ * Gather a list of all the variable bindings on which a given expression depends
+ *
+ * @param e the expression being tested
+ * @param list a list to which the bindings are to be added. The items in this list must
+ * implement {@link Binding}
+ */
+
+ public static void gatherReferencedVariables(Expression e, List<Binding> list) {
+ if (e instanceof VariableReference) {
+ Binding binding = ((VariableReference) e).getBinding();
+ if (!list.contains(binding)) {
+ list.add(binding);
+ }
+ } else {
+ for (Iterator children = e.iterateSubExpressions(); children.hasNext(); ) {
+ Expression child = (Expression) children.next();
+ gatherReferencedVariables(child, list);
+ }
+ }
+ }
+
+ /**
+ * Determine whether the expression contains any variable references or calls to user-written functions
+ *
+ * @param exp the expression being tested
+ * @return true if the expression includes a variable reference or function call, or an XSLT construct
+ * equivalent to a function call (e.g call-template). Also returns true if the expression includes
+ * a variable binding element, as (a) this is likely to mean it also contains a reference, and (b)
+ * it also needs to be caught on the same paths.
+ */
+
+ public static boolean refersToVariableOrFunction(Expression exp) {
+ return contains(exp, false, new ExpressionPredicate() {
+ public boolean matches(Expression e) {
+ return (e instanceof VariableReference || e instanceof UserFunctionCall || e instanceof Binding
+ || e instanceof CallTemplate || e instanceof ApplyTemplates
+ || e instanceof ApplyImports);
+ }
+ });
+// if (e instanceof VariableReference || e instanceof UserFunctionCall || e instanceof Binding
+// || e instanceof CallTemplate || e instanceof ApplyTemplates || e instanceof ApplyImports || e instanceof NextMatch) {
+// return true;
+// } else {
+// for (Iterator children = e.iterateSubExpressions(); children.hasNext(); ) {
+// Expression child = (Expression) children.next();
+// if (refersToVariableOrFunction(child)) {
+// return true;
+// }
+// }
+// }
+// return false;
+ }
+
+ /**
+ * Determine whether an expression contains a call on the function with a given fingerprint
+ *
+ * @param exp The expression being tested
+ * @param qName The name of the function
+ * @return true if the expression contains a call on the function
+ */
+
+ public static boolean callsFunction(Expression exp, final StructuredQName qName) {
+ return contains(exp, false, new ExpressionPredicate() {
+ public boolean matches(Expression e) {
+ return e instanceof FunctionCall && (((FunctionCall) e).getFunctionName().equals(qName));
+ }
+ });
+// if (exp instanceof FunctionCall && (((FunctionCall) exp).getFunctionName().equals(qName))) {
+// return true;
+// }
+// Iterator iter = exp.iterateSubExpressions();
+// while (iter.hasNext()) {
+// Expression e = (Expression) iter.next();
+// if (callsFunction(e, qName)) {
+// return true;
+// }
+// }
+// return false;
+ }
+
+ /**
+ * Gather a list of all the user-defined functions which a given expression calls directly
+ *
+ * @param e the expression being tested
+ * @param list a list of the functions that are called. The items in this list must
+ * be objects of class {@link UserFunction}
+ */
+
+ public static void gatherCalledFunctions(Expression e, List<UserFunction> list) {
+ if (e instanceof UserFunctionCall) {
+ UserFunction function = ((UserFunctionCall) e).getFunction();
+ if (!list.contains(function)) {
+ list.add(function);
+ }
+ } else {
+ for (Iterator children = e.iterateSubExpressions(); children.hasNext(); ) {
+ Expression child = (Expression) children.next();
+ gatherCalledFunctions(child, list);
+ }
+ }
+ }
+
+ /**
+ * Gather a list of the names of the user-defined functions which a given expression calls directly
+ *
+ * @param e the expression being tested
+ * @param list a list of the functions that are called. The items in this list are strings in the format
+ * "{uri}local/arity"
+ */
+
+ public static void gatherCalledFunctionNames(Expression e, List list) {
+ if (e instanceof UserFunctionCall) {
+ StructuredQName name = ((UserFunctionCall) e).getFunctionName();
+ int arity = ((UserFunctionCall) e).getNumberOfArguments();
+ String key = name.getClarkName() + "/" + arity;
+ if (!list.contains(key)) {
+ list.add(key);
+ }
+ } else {
+ for (Iterator children = e.iterateSubExpressions(); children.hasNext(); ) {
+ Expression child = (Expression) children.next();
+ gatherCalledFunctionNames(child, list);
+ }
+ }
+ }
+
+
+ /**
+ * Reset cached static properties within a subtree, meaning that they have to be
+ * recalulated next time they are required
+ *
+ * @param exp the root of the subtree within which static properties should be reset
+ */
+
+ public static void resetPropertiesWithinSubtree(Expression exp) {
+ exp.resetLocalStaticProperties();
+ for (Iterator children = exp.iterateSubExpressions(); children.hasNext(); ) {
+ Expression child = (Expression) children.next();
+ resetPropertiesWithinSubtree(child);
+ }
+ }
+
+ /**
+ * Resolve calls to the XSLT current() function within an expression
+ *
+ * @param exp the expression within which calls to current() should be resolved
+ * @param config the Saxon configuration
+ * @return the expression after resolving calls to current()
+ * @throws XPathException if a static error is detected
+ */
+
+ public static Expression resolveCallsToCurrentFunction(Expression exp, Configuration config)
+ throws XPathException {
+ if (callsFunction(exp, Current.FN_CURRENT)) {
+ LetExpression let = new LetExpression();
+ let.setVariableQName(
+ new StructuredQName("saxon", NamespaceConstant.SAXON, "current" + exp.hashCode()));
+ let.setRequiredType(SequenceType.SINGLE_ITEM);
+ let.setSequence(new CurrentItemExpression());
+ PromotionOffer offer = new PromotionOffer(config.obtainOptimizer());
+ offer.action = PromotionOffer.REPLACE_CURRENT;
+ offer.containingExpression = let;
+ exp = exp.promote(offer, null);
+ let.setAction(exp);
+ return let;
+ } else {
+ return exp;
+ }
+ }
+
+ /**
+ * Get a list of all references to a particular variable within a subtree
+ *
+ * @param exp the expression at the root of the subtree
+ * @param binding the variable binding whose references are sought
+ * @param list a list to be populated with the references to this variable
+ */
+
+ public static void gatherVariableReferences(Expression exp, Binding binding, List<VariableReference> list) {
+ if (exp instanceof VariableReference &&
+ ((VariableReference) exp).getBinding() == binding) {
+ list.add((VariableReference) exp);
+ } else {
+ for (Iterator iter = exp.iterateSubExpressions(); iter.hasNext(); ) {
+ gatherVariableReferences((Expression) iter.next(), binding, list);
+ }
+ }
+ }
+
+ /**
+ * Callback for selecting expressions in the tree
+ */
+
+ public interface ExpressionSelector {
+ boolean matches(Expression exp);
+ }
+
+ /**
+ * Replace all selected subexpressions within a subtree
+ *
+ * @param exp the expression at the root of the subtree
+ * @param selector callback to determine whether a subexpression is selected
+ * @param replacement the expression to be used in place of the variable reference
+ * @return true if the expression has been changed, at any level
+ */
+
+ public static boolean replaceSelectedSubexpressions(Expression exp, ExpressionSelector selector, Expression replacement) {
+ boolean found = false;
+ for (Iterator<Expression> iter = exp.iterateSubExpressions(); iter.hasNext(); ) {
+ Expression child = iter.next();
+ if (selector.matches(child)) {
+ found |= exp.replaceSubExpression(child, replacement);
+ } else {
+ found |= replaceSelectedSubexpressions(child, selector, replacement);
+ }
+ }
+ if (found) {
+ exp.resetLocalStaticProperties();
+ }
+ return found;
+ }
+
+
+ /**
+ * Replace all references to a particular variable within a subtree
+ *
+ * @param exp the expression at the root of the subtree
+ * @param binding the variable binding whose references are sought
+ * @param replacement the expression to be used in place of the variable reference
+ * @return true if the expression has been changed, at any level
+ */
+
+ public static boolean replaceVariableReferences(Expression exp, final Binding binding, Expression replacement) {
+ ExpressionSelector selector = new ExpressionSelector() {
+ public boolean matches(Expression child) {
+ return child instanceof VariableReference && ((VariableReference) child).getBinding() == binding;
+ }
+ };
+ return replaceSelectedSubexpressions(exp, selector, replacement);
+// boolean found = false;
+// for (Iterator<Expression> iter = exp.iterateSubExpressions(); iter.hasNext(); ) {
+// Expression child = iter.next();
+// if (child instanceof VariableReference && ((VariableReference) child).getBinding() == binding) {
+// found |= exp.replaceSubExpression(child, replacement);
+// } else {
+// found |= replaceVariableReferences(child, binding, replacement);
+// }
+// }
+// if (found) {
+// exp.resetLocalStaticProperties();
+// }
+// return found;
+ }
+
+ /**
+ * Determine how often a variable is referenced. This is the number of times
+ * it is referenced at run-time: so a reference in a loop counts as "many". This code
+ * currently handles local variables (Let expressions) and function parameters. It is
+ * not currently used for XSLT template parameters. It's not the end of the world if
+ * the answer is wrong (unless it's wrongly given as zero), but if wrongly returned as
+ * 1 then the variable will be repeatedly evaluated.
+ *
+ * @param exp the expression within which variable references are to be counted
+ * @param binding identifies the variable of interest
+ * @param inLoop true if the expression is within a loop, in which case a reference counts as many.
+ * This should be set to false on the initial call, it may be set to true on an internal recursive
+ * call
+ * @return the number of references. The interesting values are 0, 1, "many" (represented
+ * by any value >1), and the special value FILTERED, which indicates that there are
+ * multiple references and one or more of them is of the form $x[....] indicating that an
+ * index might be useful.
+ */
+
+ public static int getReferenceCount(Expression exp, Binding binding, boolean inLoop) {
+ int rcount = 0;
+ if (exp instanceof VariableReference && ((VariableReference) exp).getBinding() == binding) {
+ if (((VariableReference) exp).isFiltered()) {
+ return FilterExpression.FILTERED;
+ } else {
+ rcount += (inLoop ? 10 : 1);
+ }
+ } else if ((exp.getDependencies() & StaticProperty.DEPENDS_ON_LOCAL_VARIABLES) == 0) {
+ return 0;
+ } else {
+ for (Iterator<SubExpressionInfo> iter = exp.iterateSubExpressionInfo(); iter.hasNext(); ) {
+ SubExpressionInfo info = iter.next();
+ Expression child = info.expression;
+ boolean childLoop = inLoop || info.isEvaluatedRepeatedly;
+ rcount += getReferenceCount(child, binding, childLoop);
+ if (rcount >= FilterExpression.FILTERED) {
+ break;
+ }
+ }
+ }
+ return rcount;
+ }
+
+ /**
+ * Get the size of an expression tree (the number of subexpressions it contains)
+ *
+ * @param exp the expression whose size is required
+ * @return the size of the expression tree, as the number of nodes
+ */
+
+ public static int expressionSize(Expression exp) {
+ int total = 1;
+ for (Iterator iter = exp.iterateSubExpressions(); iter.hasNext(); ) {
+ total += expressionSize((Expression) iter.next());
+ }
+ return total;
+ }
+
+
+ /**
+ * Rebind all variable references to a binding
+ *
+ * @param exp the expression whose contained variable references are to be rebound
+ * @param oldBinding the old binding for the variable references
+ * @param newBinding the new binding to which the variables should be rebound
+ */
+
+ public static void rebindVariableReferences(
+ Expression exp, Binding oldBinding, Binding newBinding) {
+ if (exp instanceof VariableReference) {
+ if (((VariableReference) exp).getBinding() == oldBinding) {
+ ((VariableReference) exp).fixup(newBinding);
+ }
+ } else {
+ Iterator iter = exp.iterateSubExpressions();
+ while (iter.hasNext()) {
+ Expression e = (Expression) iter.next();
+ rebindVariableReferences(e, oldBinding, newBinding);
+ }
+ }
+ }
+
+ /**
+ * Make a mapping expression. The resulting expression will include logic to check that the first operand
+ * returns nodes, and that the expression as a whole is homogeneous, unless the caller requests otherwise.
+ *
+ * @param start the start expression (the first operand of "/")
+ * @param step the step expression (the second operand of "/")
+ * @param sortAndDeduplicate set to true if this is a path expression ("/") where the result needs to be
+ * homogenous, and needs to be sorted and deduplicated if it consists of nodes. Set to false if the caller
+ * knows that this is not necessary (typically because it has already been checked).
+ * @return the resulting expression.
+ */
+
+ /*@NotNull*/
+ public static Expression makePathExpression(
+ /*@NotNull*/ Expression start, /*@NotNull*/ Expression step, boolean sortAndDeduplicate) {
+
+// if (sortAndDeduplicate) {
+// // if the final expression is to be sorted and deduplicated, then there is no need to apply this process
+// // to the results of subexpressions
+ // code removed 2011-11-01 MHK - causes test expr36 to fail
+// start = removeSorting(start);
+// step = removeSorting(step);
+// }
+
+ // the expression /.. is sometimes used to represent the empty node-set. Applying this simplification
+ // now avoids generating warnings for this case.
+ if (start instanceof RootExpression && step instanceof ParentNodeExpression) {
+ return Literal.makeEmptySequence();
+ }
+
+ SlashExpression expr = new SlashExpression(start, step);
+
+ // If start is a path expression such as a, and step is b/c, then
+ // instead of a/(b/c) we construct (a/b)/c. This is because it often avoids
+ // a sort.
+
+ // The "/" operator in XPath 2.0 is not always left-associative. Problems
+ // can occur if position() and last() are used on the rhs, or if node-constructors
+ // appear, e.g. //b/../<d/>. So we only do this rewrite if the step is a path
+ // expression in which both operands are axis expressions optionally with predicates
+
+ if (step instanceof SlashExpression) {
+ SlashExpression stepPath = (SlashExpression) step;
+ if (isFilteredAxisPath(stepPath.getControllingExpression()) && isFilteredAxisPath(stepPath.getControlledExpression())) {
+ expr.setStartExpression(ExpressionTool.makePathExpression(start, stepPath.getControllingExpression(), false));
+ expr.setStepExpression(stepPath.getControlledExpression());
+ }
+ }
+
+ if (sortAndDeduplicate) {
+ // The HomogeneityChecker not only checks homogeneity, but also sorts nodes into document order
+ return new HomogeneityChecker(expr);
+ } else {
+ return expr;
+ }
+ }
+
+ /**
+ * Determine whether an expression is an axis step with optional filter predicates.
+ *
+ * @param exp the expression to be examined
+ * @return true if the supplied expression is an AxisExpression, or an AxisExpression wrapped by one
+ * or more filter expressions
+ */
+
+ private static boolean isFilteredAxisPath(Expression exp) {
+ return unfilteredExpression(exp) instanceof AxisExpression;
+ }
+
+ /**
+ * Get the expression that remains after removing any filter predicates
+ * @param exp the expression to be examined
+ * @return the expression underlying exp after removing any predicates
+ */
+
+ public static Expression unfilteredExpression(Expression exp) {
+ if (exp instanceof FilterExpression) {
+ return unfilteredExpression(((FilterExpression) exp).getControllingExpression());
+ } else if (exp instanceof SingleItemFilter) {
+ return unfilteredExpression(((SingleItemFilter) exp).getBaseExpression());
+ } else {
+ return exp;
+ }
+ }
+
+ /**
+ * Remove any wrapping expression that sorts or deduplicates a sequence, or checks its homogeneity
+ *
+ * @param exp the expression to be de-sorted
+ * @return the expression without any sorting
+ */
+
+ private static Expression removeSorting(Expression exp) {
+ if (exp instanceof DocumentSorter) {
+ return ((DocumentSorter) exp).getBaseExpression();
+ } else if (exp instanceof ConditionalSorter) {
+ return ((ConditionalSorter) exp).getDocumentSorter().getBaseExpression();
+// } else if (exp instanceof Reverse) {
+// return ((Reverse)exp).getArguments()[0];
+ } else if (exp instanceof HomogeneityChecker) {
+ return ((HomogeneityChecker) exp).getBaseExpression();
+ } else {
+ return exp;
+ }
+ }
+
+
+ /**
+ * Try to factor out dependencies on the context item, by rewriting an expression f(.) as
+ * let $dot := . return f($dot). This is not always possible, for example where f() is an extension
+ * function call that uses XPathContext as an implicit argument. However, doing this increases the
+ * chances of distributing a "where" condition in a FLWOR expression to the individual input sequences
+ * selected by the "for" clauses.
+ *
+ * @param exp the expression from which references to "." should be factored out if possible
+ * @param contextItemType the static type of the context item
+ * @return either the expression, after binding "." to a local variable and replacing all references to it;
+ * or null, if no changes were made.
+ */
+
+ public static Expression tryToFactorOutDot(Expression exp, ItemType contextItemType) {
+ if (exp instanceof ContextItemExpression) {
+ return null;
+ } else if (exp instanceof LetExpression && ((LetExpression) exp).getSequence() instanceof ContextItemExpression) {
+ Expression action = ((LetExpression) exp).getAction();
+ boolean changed = factorOutDot(action, (LetExpression) exp);
+ if (changed) {
+ exp.resetLocalStaticProperties();
+ }
+ return exp;
+ } else if ((exp.getDependencies() &
+ (StaticProperty.DEPENDS_ON_CONTEXT_ITEM | StaticProperty.DEPENDS_ON_CONTEXT_DOCUMENT)) != 0) {
+ LetExpression let = new LetExpression();
+ let.setVariableQName(
+ new StructuredQName("saxon", NamespaceConstant.SAXON, "dot" + exp.hashCode()));
+ let.setRequiredType(SequenceType.makeSequenceType(contextItemType, StaticProperty.EXACTLY_ONE));
+ let.setSequence(new ContextItemExpression());
+ let.setAction(exp);
+ boolean changed = factorOutDot(exp, let);
+ if (changed) {
+ return let;
+ } else {
+ return exp;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Replace references to the context item with references to a variable
+ *
+ * @param exp the expression in which the replacement is to take place
+ * @param variable the declaration of the variable
+ * @return true if replacement has taken place (at any level)
+ */
+
+ public static boolean factorOutDot(Expression exp, Binding variable) {
+ boolean changed = false;
+ if ((exp.getDependencies() &
+ (StaticProperty.DEPENDS_ON_CONTEXT_ITEM | StaticProperty.DEPENDS_ON_CONTEXT_DOCUMENT)) != 0) {
+ Iterator<SubExpressionInfo> iter = exp.iterateSubExpressionInfo();
+ while (iter.hasNext()) {
+ SubExpressionInfo info = iter.next();
+ if (info.hasSameFocus) {
+ Expression child = info.expression;
+ if (child instanceof ContextItemExpression) {
+ VariableReference ref = new VariableReference(variable);
+ copyLocationInfo(child, ref);
+ exp.replaceSubExpression(child, ref);
+ changed = true;
+ } else if (child instanceof ParentNodeExpression ||
+ child instanceof AxisExpression ||
+ child instanceof RootExpression) {
+ VariableReference ref = new VariableReference(variable);
+ copyLocationInfo(child, ref);
+ Expression path = ExpressionTool.makePathExpression(ref, child, false);
+ exp.replaceSubExpression(child, path);
+ changed = true;
+ } else {
+ changed |= factorOutDot(child, variable);
+ }
+ }
+ }
+ }
+ if (changed) {
+ exp.resetLocalStaticProperties();
+ }
+ return changed;
+ }
+
+ /**
+ * Determine whether the expression is either an updating expression, or an expression that is permitted
+ * in a context where updating expressions are allowed
+ *
+ * @param exp the expression under test
+ * @return true if the expression is an updating expression, or an empty sequence, or a call on error()
+ */
+
+ public static boolean isAllowedInUpdatingContext(Expression exp) {
+ return exp.isUpdatingExpression() || exp.isVacuousExpression();
+ }
+
+ /**
+ * Replace the Nth subexpression of an expression
+ *
+ * @param target the parent expression whose subexpression is to be replaced
+ * @param n the index of the subexpression to be replaced (starting at zero)
+ * @param replacement the replacement subexpression
+ */
+
+ public static void replaceNthSubexpression(Expression target, int n, Expression replacement) {
+ int i = 0;
+ boolean found;
+ for (Iterator<Expression> iter = target.iterateSubExpressions(); iter.hasNext(); ) {
+ Expression sub = iter.next();
+ if (i++ == n) {
+ found = target.replaceSubExpression(sub, replacement);
+ if (found) {
+ return;
+ }
+ }
+ }
+ throw new IllegalStateException("Failed to replace subexpression of " + target.getClass() +
+ " with " + replacement.toString());
+
+ }
+
+
+ public static String getCurrentDirectory() {
+ String dir;
+ try {
+ dir = System.getProperty("user.dir");
+ } catch (Exception geterr) {
+ // this doesn't work when running an applet
+ return null;
+ }
+ if (!(dir.endsWith("/"))) {
+ dir = dir + '/';
+ }
+
+ URI currentDirectoryURL = new File(dir).toURI();
+ return currentDirectoryURL.toString();
+ }
+
+ /**
+ * Determine the base URI of an expression, so that it can be saved on the expression tree for use
+ * when the expression is evaluated
+ *
+ * @param env the static context
+ * @param locator location of the expression for error messages
+ * @param fail if true, the method throws an exception when there is no absolute base URI; otherwise, the
+ * method returns null
+ * @return the absolute base URI of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ public static URI getBaseURI(StaticContext env, SourceLocator locator, boolean fail) throws XPathException {
+ URI expressionBaseURI = null;
+ String base = null;
+ try {
+ base = env.getBaseURI();
+ if (base == null) {
+ base = getCurrentDirectory();
+ }
+ if (base != null) {
+ expressionBaseURI = new URI(base);
+ }
+ } catch (URISyntaxException e) {
+ // perhaps escaping special characters will fix the problem
+
+ String esc = EscapeURI.iriToUri(base).toString();
+ try {
+ expressionBaseURI = new URI(esc);
+ } catch (URISyntaxException e2) {
+ // don't fail unless the base URI is actually needed (it usually isn't)
+ expressionBaseURI = null;
+ }
+
+ if (expressionBaseURI == null && fail) {
+ XPathException err = new XPathException("The base URI " + Err.wrap(env.getBaseURI(), Err.URI) +
+ " is not a valid URI");
+ err.setLocator(locator);
+ throw err;
+ }
+ }
+ return expressionBaseURI;
+ }
+
+ /**
+ * Display an expression adding parentheses if it is possible they are necessary
+ * because the expression has sub-expressions
+ */
+
+ public static String parenthesize(Expression exp) {
+ if (exp.iterateSubExpressions().hasNext()) {
+ return "(" + exp.toString() + ")";
+ } else {
+ return exp.toString();
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/parser/ExpressionVisitor.java b/sf/saxon/expr/parser/ExpressionVisitor.java
new file mode 100644
index 0000000..036af3b
--- /dev/null
+++ b/sf/saxon/expr/parser/ExpressionVisitor.java
@@ -0,0 +1,403 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.TypeCheckerEnvironment;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.flwor.FLWORExpression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+
+import javax.xml.transform.SourceLocator;
+import java.util.Iterator;
+import java.util.Stack;
+
+/**
+ * The ExpressionVisitor supports the various phases of processing of an expression tree which require
+ * a recursive walk of the tree structure visiting each node in turn. In maintains a stack holding the
+ * ancestor nodes of the node currently being visited.
+ */
+
+public class ExpressionVisitor implements TypeCheckerEnvironment {
+
+ private Stack<Expression> expressionStack;
+ private Executable executable;
+ private StaticContext staticContext;
+ private Configuration configuration;
+ private boolean optimizeForStreaming = false;
+
+ /**
+ * Create an ExpressionVisitor
+ */
+
+ public ExpressionVisitor() {
+ expressionStack = new Stack<Expression>();
+ }
+
+ /**
+ * Get the Saxon configuration
+ *
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ /**
+ * Set the Saxon configuration
+ *
+ * @param configuration the Saxon configuration
+ */
+
+
+ public void setConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ /**
+ * Get the Executable containing the expressions being visited
+ *
+ * @return the Executable
+ */
+
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Set the Executable containing the expressions being visited
+ *
+ * @param executable the Executable
+ */
+
+ public void setExecutable(Executable executable) {
+ this.executable = executable;
+ }
+
+ public CollationMap getCollationMap() {
+ return executable.getCollationTable();
+ }
+
+ /**
+ * Get the stack containing all the expressions currently being visited
+ *
+ * @return the expression stack holding all the containing expressions of the current expression;
+ * the objects on this Stack are instances of {@link Expression}
+ */
+
+ public Stack<Expression> getExpressionStack() {
+ return expressionStack;
+ }
+
+ /**
+ * Set the stack used to hold the expressions being visited
+ *
+ * @param expressionStack the expression stack
+ */
+
+ public void setExpressionStack(Stack<Expression> expressionStack) {
+ this.expressionStack = expressionStack;
+ }
+
+ /**
+ * Get the static context for the expressions being visited. Note: this may not reflect all changes
+ * in static context (e.g. namespace context, base URI) applying to nested expressions
+ *
+ * @return the static context
+ */
+
+ public StaticContext getStaticContext() {
+ return staticContext;
+ }
+
+ /**
+ * Set the static context for the expressions being visited. Note: this may not reflect all changes
+ * in static context (e.g. namespace context, base URI) applying to nested expressions
+ *
+ * @param staticContext the static context
+ */
+
+ public void setStaticContext(StaticContext staticContext) {
+ this.staticContext = staticContext;
+ }
+
+ /**
+ * Get the current expression, the one being visited
+ *
+ * @return the current expression
+ */
+
+ public Expression getCurrentExpression() {
+ return expressionStack.peek();
+ }
+
+ /**
+ * Factory method: make an expression visitor
+ *
+ * @param env the static context
+ * @param exec the executable
+ * @return the new expression visitor
+ */
+
+ public static ExpressionVisitor make(StaticContext env, Executable exec) {
+ ExpressionVisitor visitor = new ExpressionVisitor();
+ visitor.setStaticContext(env);
+ visitor.setExecutable(exec);
+ visitor.setConfiguration(env.getConfiguration());
+ return visitor;
+ }
+
+ /**
+ * Issue a warning message
+ *
+ * @param message the message
+ */
+
+ public void issueWarning(String message, SourceLocator locator) {
+ staticContext.issueWarning(message, locator);
+ }
+
+ /**
+ * Create a dynamic context suitable for early evaluation of constant subexpressions
+ */
+
+ public XPathContext makeDynamicContext() {
+ return staticContext.makeEarlyEvaluationContext();
+ }
+
+ /**
+ * Simplify an expression, via the ExpressionVisitor
+ *
+ * @param exp the expression to be simplified. Possibly null.
+ * @return the simplified expression. Returns null if and only if the supplied expression is null.
+ * @throws XPathException if any error occurs
+ */
+
+ public Expression simplify(/*@Nullable*/ Expression exp) throws XPathException {
+ if (exp != null) {
+ expressionStack.push(exp);
+ Expression exp2 = exp.simplify(this);
+ if (exp2 != exp) {
+ ExpressionTool.copyLocationInfo(exp, exp2);
+ }
+ expressionStack.pop();
+ return exp2;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Type check an expression, via the ExpressionVisitor
+ *
+ * @param exp the expression to be typechecked
+ * @param contextItemType the static type of the context item for this expression. The argument can be
+ * set to null to indicate that it is known that the context item will be absent.
+ * @return the expression that results from type checking (this may be wrapped in expressions that
+ * perform dynamic checking of the item type or cardinality, or that perform atomization or numeric
+ * promotion)
+ * @throws XPathException if static type checking fails, that is, if the expression cannot possibly
+ * deliver a value of the required type
+ */
+
+ public Expression typeCheck(Expression exp, /*@Nullable*/ ContextItemType contextItemType) throws XPathException {
+ if (exp != null) {
+ expressionStack.push(exp);
+ Expression exp2;
+
+ try {
+ exp2 = exp.typeCheck(this, contextItemType);
+ } catch (XPathException e) {
+ if (!e.isReportableStatically()) {
+ getStaticContext().issueWarning("Evaluation will always throw a dynamic error: " + e.getMessage(), exp);
+ exp2 = new ErrorExpression(e);
+ } else {
+ throw e;
+ }
+ }
+ if (exp2 != exp) {
+ ExpressionTool.copyLocationInfo(exp, exp2);
+ }
+ expressionStack.pop();
+ return exp2;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Tell the visitor to optimize expressions for evaluation in a streaming environment
+ *
+ * @param option true if optimizing for streaming
+ */
+
+ public void setOptimizeForStreaming(boolean option) {
+ optimizeForStreaming = option;
+ }
+
+ /**
+ * Ask whether the visitor is to optimize expressions for evaluation in a streaming environment
+ *
+ * @return true if optimizing for streaming
+ */
+
+ public boolean isOptimizeForStreaming() {
+ return optimizeForStreaming;
+ }
+
+ /**
+ * Optimize an expression, via the ExpressionVisitor
+ *
+ * @param exp the expression to be typechecked
+ * @param contextItemType the static type of the context item for this expression. Passing null indicates
+ * that the context item will always be absent
+ * @return the rewritten expression
+ * @throws XPathException if a static error is found
+ */
+
+ public Expression optimize(Expression exp,
+ /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (exp != null) {
+ expressionStack.push(exp);
+ Expression exp2 = null;
+
+ try {
+ exp2 = exp.optimize(this, contextItemType);
+ } catch (XPathException e) {
+ if (!e.isReportableStatically()) {
+ getStaticContext().issueWarning("Evaluation will always throw a dynamic error: " + e.getMessage(), exp);
+ exp2 = new ErrorExpression(e);
+ } else {
+ throw e;
+ }
+ }
+ if (exp2 != exp) {
+ ExpressionTool.copyLocationInfo(exp, exp2);
+ }
+ expressionStack.pop();
+ return exp2;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the parent expression of the current expression in the expression tree
+ *
+ * @return the parent of the current expression (or null if this is the root)
+ */
+
+ public Expression getParentExpression() {
+ int pos = expressionStack.size() - 2;
+ if (pos > 0) {
+ return expressionStack.get(pos);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return true if the current expression at the top of the visitor's stack is evaluated repeatedly
+ * when a given ancestor expression is evaluated once
+ *
+ * @param ancestor the ancestor expression. May be null, in which case the search goes all the way
+ * to the base of the stack.
+ * @return true if the current expression is evaluated repeatedly
+ */
+
+ public boolean isLoopingSubexpression(/*@Nullable*/ Expression ancestor) {
+ int top = expressionStack.size() - 1;
+ while (true) {
+ if (top <= 0) {
+ return false;
+ }
+ Expression parent = expressionStack.get(top - 1);
+ if (hasLoopingSubexpression(parent, (expressionStack.get(top)))) {
+ return true;
+ }
+ if (parent == ancestor) {
+ return false;
+ }
+ top--;
+ }
+ }
+
+ public boolean isLoopingReference(Binding binding, VariableReference ref) {
+ int top = expressionStack.size() - 1;
+ while (true) {
+ if (top <= 0) {
+ // haven't found the binding on the stack, so the safe thing is to assume we're in a loop
+ return true;
+ }
+ Expression parent = expressionStack.get(top - 1);
+ if (parent instanceof FLWORExpression) {
+ if (parent.hasVariableBinding(binding)) {
+ // The variable is declared in one of the clauses of the FLWOR expression
+ return ((FLWORExpression) parent).hasLoopingVariableReference(binding);
+ } else {
+ // The variable is declared outside the FLWOR expression
+ if (hasLoopingSubexpression(parent, (expressionStack.get(top)))) {
+ return true;
+ }
+ }
+ } else {
+ if (hasLoopingSubexpression(parent, (expressionStack.get(top)))) {
+ return true;
+ }
+ if (parent.hasVariableBinding(binding)) {
+ return false;
+ }
+ }
+ top--;
+ }
+ }
+
+ private static boolean hasLoopingSubexpression(Expression parent, Expression child) {
+ for (Iterator<SubExpressionInfo> iter = parent.iterateSubExpressionInfo(); iter.hasNext();) {
+ SubExpressionInfo info = iter.next();
+ if (info.expression == child) {
+ return info.isEvaluatedRepeatedly;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Reset the static properties for the current expression and for all its containing expressions.
+ * This should be done whenever the expression is changed in a way that might
+ * affect the properties. It causes the properties to be recomputed next time they are needed.
+ */
+
+ public final void resetStaticProperties() {
+ for (Expression exp : expressionStack) {
+ exp.resetLocalStaticProperties();
+ }
+ }
+
+ /**
+ * A data structure that represents the required type of the context item, together
+ * with information about whether it is known to be present or absent or whether it
+ * is not known statically whether it is present or absent.
+ */
+
+ public static class ContextItemType {
+ public ItemType itemType;
+ public boolean contextMaybeUndefined;
+
+ public ContextItemType(ItemType itemType, boolean maybeUndefined) {
+ this.itemType = itemType;
+ this.contextMaybeUndefined = maybeUndefined;
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/parser/Optimizer.java b/sf/saxon/expr/parser/Optimizer.java
new file mode 100644
index 0000000..91a7280
--- /dev/null
+++ b/sf/saxon/expr/parser/Optimizer.java
@@ -0,0 +1,443 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.ApplyTemplates;
+import net.sf.saxon.expr.instruct.Choose;
+import net.sf.saxon.expr.instruct.Template;
+import net.sf.saxon.expr.sort.DocumentSorter;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.style.XSLTemplate;
+import net.sf.saxon.trans.RuleTarget;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.type.UnionType;
+
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class performs optimizations that vary between different versions of the Saxon product.
+ * The optimizer is obtained from the Saxon Configuration. This class is the version used in Saxon-B,
+ * which in most cases does no optimization at all: the methods are provided so that they can be
+ * overridden in Saxon-EE.
+ */
+public class Optimizer implements Serializable {
+
+ public static final int NO_OPTIMIZATION = 0;
+ public static final int FULL_OPTIMIZATION = 10;
+
+ /*@NotNull*/ protected Configuration config;
+ private int optimizationLevel = FULL_OPTIMIZATION;
+ private boolean tracing;
+
+ /**
+ * Create an Optimizer.
+ * @param config the Saxon configuration
+ */
+
+ public Optimizer(Configuration config) {
+ this.config = config;
+ this.tracing = config.getBooleanProperty(FeatureKeys.TRACE_OPTIMIZER_DECISIONS);
+ }
+
+ /**
+ * Get the Saxon configuration object
+ * @return the configuration
+ */
+
+ /*@NotNull*/
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Set the optimization level
+ * @param level the optimization level, between 0 (no optimization) and 10 (full optimization).
+ * Currently all values greater than zero have the same effect as full optimization
+ */
+
+ public void setOptimizationLevel(int level) {
+ if (level<NO_OPTIMIZATION || level>FULL_OPTIMIZATION) {
+ throw new IllegalArgumentException("Optimization level");
+ }
+ optimizationLevel = level;
+ }
+
+ /**
+ * Get the optimization level
+ * @return the optimization level, between 0 (no optimization) and 10 (full optimization).
+ * Currently all values greater than zero have the same effect as full optimization
+ */
+
+ public int getOptimizationLevel() {
+ return optimizationLevel;
+ }
+
+ /**
+ * Simplify a GeneralComparison expression
+ * @param gc the GeneralComparison to be simplified
+ * @param backwardsCompatible true if in 1.0 compatibility mode
+ * @return the simplified expression
+ */
+
+ public BinaryExpression optimizeGeneralComparison(GeneralComparison gc, boolean backwardsCompatible) {
+ return gc;
+ }
+
+ /**
+ * Attempt to optimize a copy operation. Return null if no optimization is possible.
+ * @param select the expression that selects the items to be copied
+ * @return null if no optimization is possible, or an expression that does an optimized
+ * copy of these items otherwise
+ */
+
+ /*@Nullable*/
+ public Expression optimizeCopy(Expression select) throws XPathException {
+ final TypeHierarchy th = config.getTypeHierarchy();
+ if (select.getItemType(th).isPlainType()) {
+ return select;
+ }
+ return null;
+ }
+
+
+ /**
+ * Examine a path expression to see whether it can be replaced by a call on the key() function;
+ * if so, generate an appropriate key definition and return the call on key(). If not, return null.
+ * @param pathExp The path expression to be converted.
+ * @param visitor The expression visitor
+ * @return the optimized expression, or null if no optimization is possible
+ */
+
+ public Expression convertPathExpressionToKey(SlashExpression pathExp, ExpressionVisitor visitor)
+ throws XPathException {
+ return null;
+ }
+
+ /**
+ * Try converting a filter expression to a call on the key function. Return the supplied
+ * expression unchanged if not possible
+ *
+ * @param f the filter expression to be converted
+ * @param visitor the expression visitor, which must be currently visiting the filter expression f
+ * @param indexFirstOperand true if the first operand of the filter comparison is to be indexed;
+ * false if it is the second operand
+ * @param contextIsDoc true if the context item is known to be a document node
+ * @return the optimized expression, or the unchanged expression f if no optimization is possible
+ */
+
+ public Expression tryIndexedFilter(FilterExpression f, ExpressionVisitor visitor, boolean indexFirstOperand, boolean contextIsDoc) {
+ return f;
+ }
+
+ /**
+ * Convert a path expression such as a/b/c[predicate] into a filter expression
+ * of the form (a/b/c)[predicate]. This is possible whenever the predicate is non-positional.
+ * The conversion is useful in the case where the path expression appears inside a loop,
+ * where the predicate depends on the loop variable but a/b/c does not.
+ * @param pathExp the path expression to be converted
+ * @param th the type hierarchy cache
+ * @return the resulting filterexpression if conversion is possible, or null if not
+ */
+
+ public FilterExpression convertToFilterExpression(SlashExpression pathExp, TypeHierarchy th)
+ throws XPathException {
+ return null;
+ }
+
+ /**
+ * Test whether a filter predicate is indexable.
+ * @param filter the predicate expression
+ * @return 0 if not indexable; +1 if the predicate is in the form expression=value; -1 if it is in
+ * the form value=expression
+ */
+
+ public int isIndexableFilter(Expression filter) {
+ return 0;
+ }
+
+ /**
+ * Create an indexed value
+ * @param iter the iterator that delivers the sequence of values to be indexed
+ * @return the indexed value
+ * @throws UnsupportedOperationException: this method should not be called in Saxon-B
+ */
+
+ public Sequence makeIndexedValue(SequenceIterator iter) throws XPathException {
+ throw new UnsupportedOperationException("Indexing requires Saxon-EE");
+ }
+
+ /**
+ * Determine whether it is possible to rearrange an expression so that all references to a given
+ * variable are replaced by a reference to ".". This is true of there are no references to the variable
+ * within a filter predicate or on the rhs of a "/" operator.
+ * @param exp the expression in question
+ * @param binding an array of bindings defining range variables; the method tests that there are no
+ * references to any of these variables within a predicate or on the rhs of "/"
+ * @return true if the variable reference can be replaced
+ */
+
+ public boolean isVariableReplaceableByDot(Expression exp, Binding[] binding) {
+ // TODO: the fact that a variable reference appears inside a predicate (etc) shouldn't stop us
+ // rewriting a where clause as a predicate. We just have to bind a new variable:
+ // for $x in P where abc[n = $x/m] ==> for $x in P[let $z := . return abc[n = $z/m]
+ // We could probably do this in all cases and then let $z be optimized away where appropriate
+ if (exp instanceof ContextSwitchingExpression) {
+ Expression start = ((ContextSwitchingExpression)exp).getControllingExpression();
+ Expression step = ((ContextSwitchingExpression)exp).getControlledExpression();
+ return isVariableReplaceableByDot(start, binding) &&
+ !ExpressionTool.dependsOnVariable(step, binding);
+ } else {
+ Iterator iter = exp.iterateSubExpressions();
+ while (iter.hasNext()) {
+ Expression sub = (Expression)iter.next();
+ if (!isVariableReplaceableByDot(sub, binding)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+
+ /**
+ * Make a conditional document sorter. This optimization is attempted
+ * when a DocumentSorter is wrapped around a path expression
+ * @param sorter the document sorter
+ * @param path the path expression
+ * @return the original sorter unchanged when no optimization is possible, which is always the
+ * case in Saxon-B
+ */
+
+ public Expression makeConditionalDocumentSorter(DocumentSorter sorter, SlashExpression path) {
+ return sorter;
+ }
+
+ /**
+ * Replace a function call by the body of the function, assuming all conditions for inlining
+ * the function are satisfied
+ * @param functionCall the functionCall expression
+ * @param visitor the expression visitor
+ * @param contextItemType the context item type
+ * @return either the original expression unchanged, or an expression that consists of the inlined
+ * function body, with all function parameters bound as required. In Saxon-B, function inlining is
+ * not supported, so the original functionCall is always returned unchanged
+ */
+
+ public Expression tryInlineFunctionCall(
+ UserFunctionCall functionCall, ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) {
+ return functionCall;
+ }
+
+ /**
+ * Identify expressions within a function or template body that can be promoted to be
+ * evaluated as global variables.
+ * @param body the body of the template or function
+ * @param visitor the expression visitor
+ * @return the expression after subexpressions have been promoted to global variables; or null if
+ * nothing has changed
+ */
+
+ public Expression promoteExpressionsToGlobal(Expression body, ExpressionVisitor visitor)
+ throws XPathException {
+ return null;
+ }
+
+ /**
+ * Try to convert a Choose expression into a switch
+ * @param choose the Choose expression
+ * @param env the static context
+ * @return the result of optimizing this (the original expression if no optimization was possible)
+ */
+
+ public Expression trySwitch(Choose choose, StaticContext env) {
+ return choose;
+ }
+
+ /**
+ * Extract subexpressions from the body of a function that can be evaluated
+ * as global variables
+ * @param body the body of the function
+ * @param offer The PromotionOffer. Will be marked to indicate whether any action was taken
+ * @return a reference to the new global variable if a variable has been created, or null if not
+ */
+
+ public Expression extractGlobalVariables(Expression body, ExpressionVisitor visitor, PromotionOffer offer)
+ throws XPathException {
+ return null;
+ }
+
+ /**
+ * Make a streaming applyTemplates instruction. Supported in Saxon-EE only
+ * @param inst the unoptimized applyTemplates instruction
+ * @param reasonsForFailure a list to which diagnostic messages will be added
+ * @return the expression converted for streaming
+ * @throws XPathException if an error is detected
+ */
+
+ public Expression makeStreamingApplyTemplates(ApplyTemplates inst, List<String> reasonsForFailure) throws XPathException {
+ return inst;
+ }
+
+ /**
+ * Generate the inversion of the expression comprising the body of a template rules.
+ * Supported in Saxon-EE only
+ * @param pattern the match pattern of this template rule
+ * @param template the template to be inverted
+ * @param nodeTest the static item type of the context node of the template
+ */
+
+ public RuleTarget makeInversion(Pattern pattern, Template template, NodeTest nodeTest) throws XPathException {
+ return null;
+ }
+
+ /**
+ * In streaming mode, make the copy operation applied to subexpressions of a complex-content
+ * sequence constructor into explicit copy-of operations.
+ */
+
+ public void makeCopyOperationsExplicit(Expression parent, Expression child) throws XPathException {
+ // no action unless streaming
+ }
+
+ /**
+ * Check the streamability of a template
+ * @param sourceTemplate the source of the template in the stylesheet tree
+ * @param compiledTemplate the compiled template
+ * @throws XPathException if the template is declared streamable but does not satisfy the straming rules
+ */
+
+ public void checkStreamability(XSLTemplate sourceTemplate, Template compiledTemplate) throws XPathException {
+ // no action unless streaming
+ }
+
+ /**
+ * Make an expression that casts to a union type. Not available in Saxon-HE
+ */
+
+ public Expression makeCastToUnion(Expression operand, SchemaType targetType, boolean allowsEmpty) {
+ throw new UnsupportedOperationException("Cast to union is not supported in Saxon-HE");
+ }
+
+ /**
+ * Make an expression that casts to a list type. Not available in Saxon-HE
+ */
+
+ public Expression makeCastToList(Expression operand, SchemaType targetType, boolean allowsEmpty) {
+ throw new UnsupportedOperationException("Cast to List is not supported in Saxon-HE");
+ }
+
+ /**
+ * Make an expression castable check to a union type. Not available in Saxon-HE
+ */
+
+ public Expression makeCastableToUnion(Expression operand, SchemaType targetType, boolean allowsEmpty) {
+ throw new UnsupportedOperationException("Cast to List is not supported in Saxon-HE");
+ }
+
+ /**
+ * Make an expression castable check to a union type. Not available in Saxon-HE
+ */
+
+ public Expression makeCastableToList(Expression operand, SchemaType targetType, boolean allowsEmpty) {
+ throw new UnsupportedOperationException("Cast to List is not supported in Saxon-HE");
+ }
+
+ /**
+ * In streaming mode, optimizer a ForExpression for streaming
+ * @param expr the expression to be optimized
+ * @return the optimized expression
+ */
+
+ public Expression optimizeForExpressionForStreaming(ForExpression expr) throws XPathException {
+ return expr;
+ }
+
+ /**
+ * In streaming mode, optimizer a QuantifiedExpression for streaming
+ * @param expr the expression to be optimized
+ * @return the optimized expression
+ */
+
+ public Expression optimizeQuantifiedExpressionForStreaming(QuantifiedExpression expr) throws XPathException {
+ return expr;
+ }
+
+ public FunctionItem makeCastToUnion(final UnionType type, final NamespaceResolver resolver) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Generate a multi-threaded version of an instruction.
+ * Supported in Saxon-EE only; ignored with no action in Saxon-HE and Saxon-PE
+ * @param instruction the instruction to be multi-threaded
+ * @return the multi-threaded version of the instruction
+ */
+
+ public Expression generateMultithreadedInstruction(Expression instruction) {
+ return instruction;
+ }
+
+ /**
+ * Generate Java byte code for an expression
+ *
+ * @param expr the expression to be compiled
+ * @param objectName the name of the object (e.g. function) being compiled
+ * @param evaluationMethods The evaluation modes for which code is generated. Currently a subset of
+* {@link net.sf.saxon.expr.Expression#PROCESS_METHOD}, {@link net.sf.saxon.expr.Expression#ITERATE_METHOD}. If no code is generated for
+ * */
+
+ public Expression compileToByteCode(Expression expr, String objectName, int evaluationMethods) {
+ return null;
+ }
+
+ /**
+ * Trace optimization actions
+ * @param message the message to be displayed
+ * @param exp the expression after being rewritten
+ */
+
+ public void trace(String message, Expression exp) {
+ if (tracing) {
+ PrintStream err = getConfiguration().getStandardErrorOutput();
+ err.println("OPT ======================================");
+ err.println("OPT : At line " + exp.getLineNumber() + " of " + exp.getSystemId());
+ err.println("OPT : " + message);
+ err.println("OPT ====== Expression after rewrite ======");
+ exp.explain(err);
+ err.println("\nOPT ======================================");
+ }
+ }
+
+ /**
+ * Trace optimization actions
+ * @param message the message to be displayed
+ */
+
+ public void trace(String message) {
+ if (tracing) {
+ PrintStream err = getConfiguration().getStandardErrorOutput();
+ err.println("OPT ======================================");
+ err.println("OPT : " + message);
+ err.println("OPT ======================================");
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/parser/PathMap.java b/sf/saxon/expr/parser/PathMap.java
new file mode 100644
index 0000000..e560367
--- /dev/null
+++ b/sf/saxon/expr/parser/PathMap.java
@@ -0,0 +1,863 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.functions.Doc;
+import net.sf.saxon.functions.DocumentFn;
+import net.sf.saxon.functions.ResolveURI;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.query.StaticQueryContext;
+import net.sf.saxon.query.XQueryExpression;
+import net.sf.saxon.sxpath.XPathEvaluator;
+import net.sf.saxon.sxpath.XPathExpression;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.PrintStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * A PathMap is a description of all the paths followed by an expression.
+ * It is a set of trees. Each tree contains as its root an expression that selects
+ * nodes without any dependency on the context. The arcs in the tree are axis steps.
+ * So the expression doc('a.xml')/a[b=2]/c has a single root (the call on doc()), with
+ * a single arc representing child::a, this leads to a node which has two further arcs
+ * representing child::b and child::c. Because element b is atomized, there will also be
+ * an arc for the step descendant::text() indicating the requirement to access the text
+ * nodes of the element.
+ *
+ * <p>The current implementation works only for XPath 2.0 expressions (for example, constructs
+ * like xsl:for-each-group are not handled.)</p>
+ *
+ * <p>This class, together with the overloaded method
+ * {@link net.sf.saxon.expr.Expression#addToPathMap(PathMap, PathMap.PathMapNodeSet)} can be
+ * seen as an implementation of the static path analysis algorithm given in section 4 of
+ * <a href="http://www-db.research.bell-labs.com/user/simeon/xml_projection.pdf">A. Marian and J. Simeon,
+ * Projecting XML Documents, VLDB 2003</a>.</p>
+ */
+
+public class PathMap {
+
+ /*@NotNull*/ private List<PathMapRoot> pathMapRoots = new ArrayList<PathMapRoot>();
+ /*@NotNull*/ private HashMap<Binding, PathMapNodeSet> pathsForVariables =
+ new HashMap<Binding, PathMapNodeSet>(); // a map from a variable Binding to a PathMapNodeSet
+
+ /**
+ * A node in the path map. A node holds a set of arcs, each representing a link to another
+ * node in the path map.
+ */
+
+ public static class PathMapNode {
+ List<PathMapArc> arcs;
+ private boolean returnable;
+ private boolean atomized;
+ private boolean hasUnknownDependencies;
+
+ /**
+ * Create a node in the PathMap (initially with no arcs)
+ */
+
+ private PathMapNode() {
+ arcs = new ArrayList<PathMapArc>();
+ }
+
+ /**
+ * Create a new arc
+ * @param axis the axis of this step
+ * @param test the node test of this step
+ * @return the newly-constructed target of the new arc
+ */
+
+ public PathMapNode createArc(byte axis, /*@NotNull*/ NodeTest test) {
+ for (PathMapArc a : arcs) {
+ if (a.getAxis() == axis && a.getNodeTest().equals(test)) {
+ return a.getTarget();
+ }
+ }
+ PathMapNode target = new PathMapNode();
+ PathMapArc arc = new PathMapArc(axis, test, target);
+ arcs.add(arc);
+ return target;
+ }
+
+ /**
+ * Create a new arc to an existing target
+ * @param axis the axis of this step
+ * @param test the node test of this step
+ * @param target the target node of the new arc
+ */
+
+ public void createArc(byte axis, /*@NotNull*/ NodeTest test, /*@NotNull*/ PathMapNode target) {
+ for (PathMapArc a : arcs) {
+ if (a.getAxis() == axis && a.getNodeTest().equals(test) && a.getTarget() == target) {
+ // TODO: if it's a different target, then merge the two targets into one. XMark Q8
+ a.getTarget().setReturnable(a.getTarget().isReturnable() || target.isReturnable());
+ if (target.isAtomized()) {
+ a.getTarget().setAtomized();
+ }
+ return;
+ }
+ }
+ PathMapArc arc = new PathMapArc(axis, test, target);
+ arcs.add(arc);
+ }
+
+ /**
+ * Get the arcs emanating from this node in the PathMap
+ * @return the arcs, each representing an AxisStep. The order of arcs in the array is undefined.
+ */
+
+ public PathMapArc[] getArcs() {
+ return arcs.toArray(new PathMapArc[arcs.size()]);
+ }
+
+ /**
+ * Indicate that the node represents a value that is returnable as the result of the
+ * supplied expression, rather than merely a node that is visited en route
+ * @param returnable true if the node represents a final result of the expression
+ */
+
+ public void setReturnable(boolean returnable) {
+ this.returnable = returnable;
+ }
+
+ /**
+ * Ask whether the node represents a value that is returnable as the result of the
+ * supplied expression, rather than merely a node that is visited en route
+ * @return true if the node represents a final result of the expression
+ */
+
+ public boolean isReturnable() {
+ return returnable;
+ }
+
+ /**
+ * Test whether there are any returnable nodes reachable from this node by
+ * zero or more arcs
+ * @return true if any arcs lead to a pathmap node representing a returnable XDM node
+ */
+
+ public boolean hasReachableReturnables() {
+ if (isReturnable()) {
+ return true;
+ }
+ for (PathMapArc arc : arcs) {
+ if (arc.getTarget().hasReachableReturnables()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Indicate that the typed value or string value of the node reached by this path
+ * will be used. Note that because this is being used only to determine navigation paths,
+ * the property does not need to be set when nodes other than element or document nodes
+ * are atomized.
+ */
+
+ public void setAtomized() {
+ this.atomized = true;
+ }
+
+ /**
+ * Ask whether the typed value (or string value) of the node reached by this path
+ * will be required.
+ * @return true if the typed value or string value of the node is required
+ */
+
+ public boolean isAtomized() {
+ return atomized;
+ }
+
+ /**
+ * Indicate that the path has unknown dependencies, typically because a node reached
+ * by the path is supplied as an argument to a user-defined function
+ */
+
+ public void setHasUnknownDependencies() {
+ hasUnknownDependencies = true;
+ }
+
+ /**
+ * Ask whether the path has unknown dependencies, typically because a node reached
+ * by the path is supplied as an argument to a user-defined function
+ * @return true if the path has unknown dependencies
+ */
+
+ public boolean hasUnknownDependencies() {
+ return hasUnknownDependencies;
+ }
+
+ /**
+ * Determine whether the path is entirely within a streamable snapshot of a streamed document:
+ * that is, it must only navigate to ancestors and to attributes of ancestors
+ * @return true if this path performs navigation other than to ancestors and their attributes
+ */
+
+ public boolean allPathsAreWithinStreamableSnapshot() {
+ if (hasUnknownDependencies() || isReturnable() || isAtomized()) {
+ return false;
+ }
+ for (PathMapArc arc : arcs) {
+ int axis = arc.getAxis();
+ if (axis == AxisInfo.ATTRIBUTE) {
+ PathMapNode next = arc.getTarget();
+ if (next.isReturnable()) {
+ return false;
+ }
+ if (next.getArcs().length != 0 && !next.allPathsAreWithinStreamableSnapshot()) {
+ return false;
+ }
+ } else if (axis == AxisInfo.SELF || axis == AxisInfo.ANCESTOR || axis == AxisInfo.ANCESTOR_OR_SELF ||
+ axis == AxisInfo.PARENT) {
+ PathMapNode next = arc.getTarget();
+ if (next.isAtomized()) {
+ return false;
+ }
+ if (!next.allPathsAreWithinStreamableSnapshot()) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * A root node in the path map. A root node represents either (a) a subexpression that is the first step in
+ * a path expression, or (b) a subexpression that is not the first step in a path, but which returns nodes
+ * (for example, a call on the doc() function).
+ */
+
+ public static class PathMapRoot extends PathMapNode {
+
+ private Expression rootExpression;
+ private boolean isDownwardsOnly;
+
+ /**
+ * Create a PathMapRoot
+ * @param root the expression at the root of a path
+ */
+ private PathMapRoot(Expression root) {
+ this.rootExpression = root;
+ }
+
+ /**
+ * Get the root expression
+ * @return the expression at the root of the path
+ */
+ public Expression getRootExpression() {
+ return rootExpression;
+ }
+
+ }
+
+ /**
+ * An arc joining two nodes in the path map. The arc has a target (destination) node, and is
+ * labelled with an AxisExpression representing a step in a path expression
+ */
+
+ public static class PathMapArc {
+ private PathMapNode target;
+ private byte axis;
+ private NodeTest test;
+
+ /**
+ * Create a PathMapArc
+ * @param axis the axis (a constant from class {@link net.sf.saxon.om.AxisInfo}
+ * @param test the node test
+ * @param target the node reached by following this arc
+ */
+ private PathMapArc(byte axis, /*@NotNull*/ NodeTest test, /*@NotNull*/ PathMapNode target) {
+ this.axis = axis;
+ this.test = test;
+ this.target = target;
+ }
+
+ /**
+ * Get the Axis associated with this arc
+ * @return the axis, a constant from class {@link net.sf.saxon.om.AxisInfo}
+ */
+
+ public byte getAxis() {
+ return axis;
+ }
+
+ /**
+ * Get the NodeTest associated with this arc
+ * @return the NodeTest
+ */
+
+ public NodeTest getNodeTest() {
+ return test;
+ }
+
+ /**
+ * Get the target node representing the destination of this arc
+ * @return the target node
+ */
+
+ public PathMapNode getTarget() {
+ return target;
+ }
+ }
+
+ /**
+ * A (mutable) set of nodes in the path map
+ */
+
+ public static class PathMapNodeSet extends HashSet<PathMapNode> {
+
+ /**
+ * Create an initially-empty set of path map nodes
+ */
+
+ public PathMapNodeSet() {}
+
+ /**
+ * Create a set of path map nodes that initially contains a single node
+ * @param singleton the single node to act as the initial content
+ */
+
+ public PathMapNodeSet(PathMapNode singleton) {
+ add(singleton);
+ }
+
+ /**
+ * Create an arc from each node in this node set to a corresponding newly-created
+ * target node
+ * @param axis the axis of the step defining the transition
+ * @param test the node test of the step defining the transition
+ * @return the set of new target nodes
+ */
+
+ /*@NotNull*/ public PathMapNodeSet createArc(byte axis, /*@NotNull*/ NodeTest test) {
+ PathMapNodeSet targetSet = new PathMapNodeSet();
+ for (PathMapNode node : this) {
+ targetSet.add(node.createArc(axis, test));
+ }
+ return targetSet;
+ }
+
+ /**
+ * Combine two node sets into one
+ * @param nodes the set of nodes to be added to this set
+ */
+
+ public void addNodeSet(/*@Nullable*/ PathMapNodeSet nodes) {
+ if (nodes != null) {
+ for (PathMapNode node : nodes) {
+ add(node);
+ }
+ }
+ }
+
+ /**
+ * Set the atomized property on all nodes in this nodeset
+ */
+
+ public void setAtomized() {
+ for (PathMapNode node : this) {
+ node.setAtomized();
+ }
+ }
+
+ /**
+ * Set the returnable property on all nodes in this nodeset
+ */
+
+ public void setReturnable(boolean isReturned) {
+ for (PathMapNode node : this) {
+ node.setReturnable(isReturned);
+ }
+ }
+
+ /**
+ * Test whether there are any returnable nodes reachable from nodes in this nodeset
+ */
+
+ public boolean hasReachableReturnables() {
+ for (PathMapNode node : this) {
+ if (node.hasReachableReturnables()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether the path is entirely within a streamable snapshot of a streamed document:
+ * that is, it must only navigate to ancestors and to attributes of ancestors
+ */
+
+ public boolean allPathsAreWithinStreamableSnapshot() {
+ for (PathMapNode node : this) {
+ if (!node.allPathsAreWithinStreamableSnapshot()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Indicate that all the descendants of the nodes in this nodeset are required
+ */
+
+ public void addDescendants() {
+ for (PathMapNode node : this) {
+ node.createArc(AxisInfo.DESCENDANT, AnyNodeTest.getInstance());
+ }
+ }
+
+ /**
+ * Indicate that all the nodes have unknown dependencies
+ */
+
+ public void setHasUnknownDependencies() {
+ for (PathMapNode node : this) {
+ node.setHasUnknownDependencies();
+ }
+ }
+
+ }
+
+ /**
+ * Create the PathMap for an expression
+ * @param exp the expression whose PathMap is required
+ */
+
+ public PathMap(/*@NotNull*/ Expression exp) {
+ PathMapNodeSet finalNodes = exp.addToPathMap(this, null);
+ if (finalNodes != null) {
+ for (PathMapNode node : finalNodes) {
+ node.setReturnable(true);
+ }
+ }
+ }
+
+ /**
+ * Make a new root node in the path map. However, if there is already a root for the same
+ * expression, the existing root for that expression is returned.
+ * @param exp the expression represented by this root node
+ * @return the new root node
+ */
+
+ public PathMapRoot makeNewRoot(/*@NotNull*/ Expression exp) {
+ for (PathMapRoot r : pathMapRoots) {
+ if (exp.equals(r.getRootExpression())) {
+ return r;
+ }
+ }
+ PathMapRoot root = new PathMapRoot(exp);
+ pathMapRoots.add(root);
+ return root;
+ }
+
+ /**
+ * Get all the root expressions from the path map
+ * @return an array containing the root expressions
+ */
+
+ public PathMapRoot[] getPathMapRoots() {
+ return pathMapRoots.toArray(new PathMapRoot[pathMapRoots.size()]);
+ }
+
+ /**
+ * Register the path used when evaluating a given variable binding
+ * @param binding the variable binding
+ * @param nodeset the set of PathMap nodes reachable when evaluating that variable
+ */
+
+ public void registerPathForVariable(Binding binding, PathMapNodeSet nodeset) {
+ pathsForVariables.put(binding, nodeset);
+ }
+
+ /**
+ * Get the path used when evaluating a given variable binding
+ * @param binding the variable binding
+ * @return the set of PathMap nodes reachable when evaluating that variable
+ */
+
+ public PathMapNodeSet getPathForVariable(Binding binding) {
+ return pathsForVariables.get(binding);
+ }
+
+ /**
+ * Get the path map root for the context document
+ * @return the path map root for the context document if there is one, or null if none is found.
+ * @throws IllegalStateException if there is more than one path map root for the context document
+ */
+
+ /*@Nullable*/ public PathMapRoot getContextDocumentRoot() {
+ //System.err.println("BEFORE REDUCTION:");
+ //map.diagnosticDump(System.err);
+ PathMap.PathMapRoot[] roots = getPathMapRoots();
+ PathMapRoot contextRoot = null;
+ for (PathMapRoot root : roots) {
+ PathMapRoot newRoot = reduceToDownwardsAxes(root);
+ if (newRoot.getRootExpression() instanceof RootExpression) {
+ if (contextRoot != null) {
+ throw new IllegalStateException("More than one context document root found in path map");
+ } else {
+ contextRoot = newRoot;
+ }
+ }
+ }
+ //System.err.println("AFTER REDUCTION:");
+ //map.diagnosticDump(System.err);
+ return contextRoot;
+ }
+
+ /**
+ * Get the path map root for the context item
+ * @return the path map root for the context item if there is one, or null if none is found.
+ * @throws IllegalStateException if there is more than one path map root for the context item
+ */
+
+ /*@Nullable*/ public PathMapRoot getContextItemRoot() {
+ //System.err.println("BEFORE REDUCTION:");
+ //map.diagnosticDump(System.err);
+ PathMap.PathMapRoot[] roots = getPathMapRoots();
+ PathMapRoot contextRoot = null;
+ for (PathMapRoot root : roots) {
+ if (root.getRootExpression() instanceof ContextItemExpression) {
+ if (contextRoot != null) {
+ throw new IllegalStateException("More than one context document root found in path map");
+ } else {
+ contextRoot = root;
+ }
+ }
+ }
+ return contextRoot;
+ }
+
+ /**
+ * Get the path map root for a call on the doc() or document() function with a given literal argument
+ * @param requiredUri the literal argument we are looking for
+ * @return the path map root for the specified document if there is one, or null if none is found.
+ * @throws IllegalStateException if there is more than one path map root for the specified document
+ */
+
+ /*@Nullable*/ public PathMapRoot getRootForDocument(/*@NotNull*/ String requiredUri) {
+ //System.err.println("BEFORE REDUCTION:");
+ //map.diagnosticDump(System.err);
+ PathMap.PathMapRoot[] roots = getPathMapRoots();
+ PathMapRoot requiredRoot = null;
+ for (PathMapRoot root : roots) {
+ PathMapRoot newRoot = reduceToDownwardsAxes(root);
+ Expression exp = newRoot.getRootExpression();
+ String baseUri;
+ if (exp instanceof Doc) {
+ baseUri = ((Doc) exp).getStaticBaseURI();
+ } else if (exp instanceof DocumentFn) {
+ baseUri = ((DocumentFn) exp).getStaticBaseURI();
+ } else {
+ continue;
+ }
+ Expression arg = ((SystemFunctionCall) exp).getArguments()[0];
+ String suppliedUri = null;
+ if (arg instanceof Literal) {
+ try {
+ String argValue = ((Literal) arg).getValue().getStringValue();
+ if (baseUri == null) {
+ if (new URI(argValue).isAbsolute()) {
+ suppliedUri = argValue;
+ } else {
+ suppliedUri = null;
+ }
+ } else {
+ suppliedUri = ResolveURI.makeAbsolute(argValue, baseUri).toString();
+ }
+ } catch (URISyntaxException err) {
+ suppliedUri = null;
+ } catch (XPathException err) {
+ suppliedUri = null;
+ }
+ }
+ if (requiredUri.equals(suppliedUri)) {
+ if (requiredRoot != null) {
+ throw new IllegalStateException("More than one document root found in path map for " + requiredUri);
+ } else {
+ requiredRoot = newRoot;
+ }
+ }
+ }
+ //System.err.println("AFTER REDUCTION:");
+ //map.diagnosticDump(System.err);
+ return requiredRoot;
+ }
+
+ /**
+ * Given a PathMapRoot, simplify the tree rooted at this node so that
+ * it only contains downwards selections: specifically, so that the only axes
+ * used are child, attribute, namespace, and descendant. If the root expression
+ * is a ContextItemExpression (that is, the path can start at any node) then it is rebased
+ * to start at a root node, which means in effect that a path such as a/b/c is treated
+ * as //a/b/c.
+ * @param root the root of the path to be simplified
+ * @return the path map root after converting the tree to use downwards axes only
+ */
+
+ public PathMapRoot reduceToDownwardsAxes(/*@NotNull*/ PathMapRoot root) {
+ // If the path is rooted at an arbitrary context node, we rebase it to be rooted at the
+ // document root. This involves changing the root to a RootExpression, and changing the axis
+ // for initial steps from child to descendant where necessary
+ if (root.isDownwardsOnly) {
+ return root;
+ }
+ PathMapRoot newRoot = root;
+ if (root.getRootExpression() instanceof ContextItemExpression) {
+ RootExpression slash = new RootExpression();
+ slash.setContainer(root.getRootExpression().getContainer());
+ //root.setRootExpression(slash);
+ newRoot = makeNewRoot(slash);
+ for (int i=root.arcs.size()-1; i>=0; i--) {
+ PathMapArc arc = root.arcs.get(i);
+ byte axis = arc.getAxis();
+ switch (axis) {
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.NAMESPACE: {
+ PathMapNode newTarget = new PathMapNode();
+ newTarget.arcs.add(arc);
+ newRoot.createArc(AxisInfo.DESCENDANT, NodeKindTest.ELEMENT, newTarget);
+ break;
+ }
+ default: {
+ newRoot.createArc(AxisInfo.DESCENDANT_OR_SELF, arc.getNodeTest(), arc.getTarget());
+ break;
+ }
+ }
+ }
+ for (int i=0; i<pathMapRoots.size(); i++) {
+ if (pathMapRoots.get(i) == root) {
+ pathMapRoots.remove(i); break;
+ }
+ }
+ }
+ // Now process the tree of paths recursively, rewriting all axes in terms of downwards
+ // selections, if necessary as downward selections from the root
+ Stack<PathMapNode> nodeStack = new Stack<PathMapNode>();
+ nodeStack.push(newRoot);
+ reduceToDownwardsAxes(newRoot, nodeStack);
+ newRoot.isDownwardsOnly = true;
+ return newRoot;
+ }
+
+ /**
+ * Supporting method for {@link #reduceToDownwardsAxes(PathMap.PathMapRoot)}
+ * @param root the root of the path being simplified
+ * @param nodeStack the sequence of nodes by which the current node in the path map was reached.
+ * The node at the bottom of the stack is the root.
+ */
+
+ private void reduceToDownwardsAxes(/*@NotNull*/ PathMapRoot root, /*@NotNull*/ Stack<PathMapNode> nodeStack) {
+ //PathMapArc lastArc = (PathMapArc)arcStack.peek();
+ //byte lastAxis = lastArc.getStep().getAxis();
+ PathMapNode node = nodeStack.peek();
+ if (node.hasUnknownDependencies()) {
+ root.setHasUnknownDependencies();
+ }
+
+ for (int i=0; i<node.arcs.size(); i++) {
+ nodeStack.push((node.arcs.get(i)).getTarget());
+ reduceToDownwardsAxes(root, nodeStack);
+ nodeStack.pop();
+ }
+
+ for (int i=node.arcs.size()-1; i>=0; i--) {
+ PathMapArc thisArc = node.arcs.get(i);
+ //AxisExpression axisStep = thisArc.getStep();
+ PathMapNode grandParent =
+ (nodeStack.size() < 2 ? null : nodeStack.get(nodeStack.size()-2));
+ byte lastAxis = -1;
+ if (grandParent != null) {
+ for (PathMapArc arc1 : grandParent.arcs) {
+ PathMapArc arc = (arc1);
+ if (arc.getTarget() == node) {
+ lastAxis = arc.getAxis();
+ }
+ }
+ }
+ switch (thisArc.getAxis()) {
+
+ case AxisInfo.ANCESTOR_OR_SELF:
+ case AxisInfo.DESCENDANT_OR_SELF:
+ if (thisArc.getNodeTest() == NodeKindTest.DOCUMENT) {
+ // This is typically an absolute path expression appearing within a predicate
+ node.arcs.remove(i);
+ for (PathMapArc arc : thisArc.getTarget().arcs) {
+ root.arcs.add(arc);
+ }
+ break;
+ } else {
+ // fall through
+ }
+
+ case AxisInfo.ANCESTOR:
+ case AxisInfo.FOLLOWING:
+ case AxisInfo.PRECEDING: {
+ // replace the axis by a downwards axis from the root
+ if (thisArc.getAxis() != AxisInfo.DESCENDANT_OR_SELF) {
+ root.createArc(AxisInfo.DESCENDANT_OR_SELF, thisArc.getNodeTest(), thisArc.getTarget());
+ node.arcs.remove(i);
+ }
+ break;
+ }
+
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.CHILD:
+ case AxisInfo.DESCENDANT:
+ case AxisInfo.NAMESPACE:
+ // no action
+ break;
+
+ case AxisInfo.FOLLOWING_SIBLING:
+ case AxisInfo.PRECEDING_SIBLING: {
+ if (grandParent != null) {
+ grandParent.createArc(lastAxis, thisArc.getNodeTest(), thisArc.getTarget());
+ node.arcs.remove(i);
+ break;
+ } else {
+ root.createArc(AxisInfo.CHILD, thisArc.getNodeTest(), thisArc.getTarget());
+ node.arcs.remove(i);
+ break;
+ }
+ }
+ case AxisInfo.PARENT: {
+
+ if (lastAxis == AxisInfo.CHILD || lastAxis == AxisInfo.ATTRIBUTE || lastAxis == AxisInfo.NAMESPACE) {
+ // ignore the parent step - it leads to somewhere we have already been.
+ // But it might become a returned node
+ if (node.isReturnable()) {
+ grandParent.setReturnable(true);
+ }
+ // any paths after the parent step need to be attached to the grandparent
+
+ PathMapNode target = thisArc.getTarget();
+ for (int a=0; a<target.arcs.size(); a++) {
+ grandParent.arcs.add(target.arcs.get(a));
+ }
+ node.arcs.remove(i);
+ } else if (lastAxis == AxisInfo.DESCENDANT) {
+ if (thisArc.getTarget().arcs.isEmpty()) {
+ grandParent.createArc(AxisInfo.DESCENDANT_OR_SELF, thisArc.getNodeTest());
+ } else {
+ grandParent.createArc(AxisInfo.DESCENDANT_OR_SELF, thisArc.getNodeTest(), thisArc.getTarget());
+ }
+ node.arcs.remove(i);
+ } else {
+ // don't try to be precise about a/b/../../c
+ if (thisArc.getTarget().arcs.isEmpty()) {
+ root.createArc(AxisInfo.DESCENDANT_OR_SELF, thisArc.getNodeTest());
+ } else {
+ root.createArc(AxisInfo.DESCENDANT_OR_SELF, thisArc.getNodeTest(), thisArc.getTarget());
+ }
+ node.arcs.remove(i);
+ }
+ break;
+ }
+ case AxisInfo.SELF: {
+ // This step can't take us anywhere we haven't been, so delete it
+ node.arcs.remove(i);
+ break;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Display a printed representation of the path map
+ * @param out the output stream to which the output will be written
+ */
+
+ public void diagnosticDump(/*@NotNull*/ PrintStream out) {
+ for (int i=0; i<pathMapRoots.size(); i++) {
+ out.println("\nROOT EXPRESSION " + i);
+ PathMapRoot mapRoot = pathMapRoots.get(i);
+ if (mapRoot.hasUnknownDependencies()) {
+ out.println(" -- has unknown dependencies --");
+ }
+ Expression exp = mapRoot.rootExpression;
+ exp.explain(out);
+ out.println("\nTREE FOR EXPRESSION " + i);
+ showArcs(out, mapRoot, 2);
+ }
+ }
+
+ /**
+ * Internal helper method called by diagnosticDump, to show the arcs emanating from a node.
+ * Each arc is shown as a representation of the axis step, followed optionally by "@" if the
+ * node reached by the arc is atomized, followed optionally by "#" if the
+ * node reached by the arc is a final returnable node.
+ * @param out the output stream
+ * @param node the node in the path map whose arcs are to be displayed
+ * @param indent the indentation level in the output
+ */
+
+ private void showArcs(/*@NotNull*/ PrintStream out, /*@NotNull*/ PathMapNode node, int indent) {
+ String pad = " ".substring(0, indent);
+ List<PathMapArc> arcs = node.arcs;
+ for (PathMapArc arc : arcs) {
+ out.println(pad + AxisInfo.axisName[arc.axis] +
+ "::" +
+ arc.test.toString() +
+ (arc.target.isAtomized() ? " @" : "") +
+ (arc.target.isReturnable() ? " #" : "") +
+ (arc.target.hasUnknownDependencies() ? " ...??" : ""));
+ showArcs(out, arc.target, indent + 2);
+ }
+ }
+
+ /**
+ * Main method for testing
+ * @param args Takes one argument, the XPath expression to be analyzed
+ * @throws Exception
+ */
+
+ public static void main(String[] args) throws Exception {
+ Configuration config = new Configuration();
+ Expression exp;
+ if (args[0].equals("xpath")) {
+ XPathEvaluator xpath = new XPathEvaluator(config);
+ XPathExpression xpexp = xpath.createExpression(args[1]);
+ exp = xpexp.getInternalExpression();
+ } else if (args[0].equals("xquery")) {
+ StaticQueryContext sqc = config.newStaticQueryContext();
+ sqc.setBaseURI(new File(args[1]).toURI().toString());
+ XQueryExpression xqe = sqc.compileQuery(new FileReader(args[1]));
+ exp = xqe.getExpression();
+ } else {
+ throw new IllegalArgumentException("first argument must be xpath or xquery");
+ }
+ exp.explain(System.err);
+ PathMap initialPath = new PathMap(exp);
+ initialPath.diagnosticDump(System.err);
+
+ PathMapRoot[] roots = initialPath.getPathMapRoots();
+ for (PathMapRoot root : roots) {
+ initialPath.reduceToDownwardsAxes(root);
+ }
+ System.err.println("AFTER REDUCTION:");
+ initialPath.diagnosticDump(System.err);
+ }
+
+}
+
diff --git a/sf/saxon/expr/parser/PromotionOffer.java b/sf/saxon/expr/parser/PromotionOffer.java
new file mode 100644
index 0000000..b0d00e7
--- /dev/null
+++ b/sf/saxon/expr/parser/PromotionOffer.java
@@ -0,0 +1,318 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.LocalParamSetter;
+import net.sf.saxon.expr.sort.DocumentSorter;
+import net.sf.saxon.functions.Current;
+import net.sf.saxon.functions.Reverse;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.SequenceType;
+
+/**
+* PromotionOffer is an object used transiently during compilation of an expression. It contains
+* information passed by a containing expression to its subexpressions, when looking for subexpressions
+* that can be promoted to a higher level because they are not dependent on the context established
+* by the containing expression. The object is also used to pass back return information when the
+* promotion actually takes place.
+*/
+
+public class PromotionOffer {
+
+ /**
+ * FOCUS_INDEPENDENT requests promotion of all non-trivial subexpressions that don't depend on the
+ * focus. This is typically used to extract subexpressions from a filter predicate. The offer is
+ * optional - each subexpression can decide whether it's worth the trouble of promoting itself.
+ * The offer is normally passed on to subexpressions, except subexpressions that are evaluated
+ * with a different focus
+ */
+
+ public static final int FOCUS_INDEPENDENT = 10;
+
+ /**
+ * RANGE_INDEPENDENT requests promotion of all non-trivial subexpressions that don't depend on a
+ * specified range variable. This is typically used to extract subexpressions from the action of
+ * a for expression or the condition of a some/every quantified expression. The offer is
+ * optional - each subexpression can decide whether it's worth the trouble of promoting itself.
+ * The offer is normally passed on to subexpressions, except subexpressions that are evaluated
+ * with a different focus or a different range variable, because these may have other dependencies
+ * that prevent their promotion.
+ */
+
+ public static final int RANGE_INDEPENDENT = 11;
+
+ /**
+ * Inline variable references causes all references to a variable V to be replaced by the
+ * expression E. The variable is supplied in the "binding" property; the replacement expression
+ * in the containingExpression property. A special case is where the replacement expression is
+ * a ContextItemExpression; in this case the offer is not passed on to subexpressions where
+ * the context is different.
+ */
+
+ public static final int INLINE_VARIABLE_REFERENCES = 12;
+
+ /**
+ * UNORDERED indicates that the containing expression does not require the results
+ * to be delivered in any particular order. The boolean mustEliminateDuplicates
+ * is set if duplicate items in the result are not allowed.
+ */
+
+ public static final int UNORDERED = 13;
+
+ /**
+ * REPLACE_CURRENT causes calls to the XSLT current() function to be replaced by
+ * reference to a variable. The variable binding is the single member of the array bindingList
+ */
+
+ public static final int REPLACE_CURRENT = 14;
+
+ /**
+ * EXTRACT_GLOBAL_VARIABLES identifies subexpressions that are not constant, but have no dependencies
+ * other than on global variables or parameters (they must also be non-creative). Such expressions can
+ * be extracted from a function or template and converted into global variables. This optimization is done
+ * in Saxon-EE only.
+ */
+
+ public static final int EXTRACT_GLOBAL_VARIABLES = 15;
+
+ /**
+ * The optimizer in use
+ */
+
+ private Optimizer optimizer;
+
+ /**
+ * The expression visitor in use
+ */
+
+ public ExpressionVisitor visitor;
+
+ /**
+ * action is one of the possible promotion actions, FOCUS_INDEPENDENT, RANGE_INDEPENDENT,
+ * INLINE_VARIABLE_REFERENCES, UNORDERED, EXTRACT_GLOBAL_VARIABLES
+ */
+
+ public int action;
+
+ /**
+ * In the case of FOCUS_INDEPENDENT, "promoteDocumentDependent" is a boolean that, when set to
+ * true, indicates that it is safe to promote a subexpression that depends on the context document
+ * but not on other aspects of the focus. This is the case, for example, in a filter expression when
+ * it is known that all the nodes selected by the expression will be in the same document - as happens
+ * when the filter is applied to a path expression. This allows subexpressions such as key() to be
+ * promoted
+ */
+
+ public boolean promoteDocumentDependent = false;
+
+ /**
+ * In the case of FOCUS_INDEPENDENT, "promoteXSLTFunctions" is a boolean that, when set to true, indicates
+ * that it is safe to promote XSLT functions such as current(). This flag is set when rewriting XPath expressions
+ * and is unset when rewriting XSLT templates.
+ */
+
+ public boolean promoteXSLTFunctions = true;
+
+ /**
+ * In the case of UNORDERED, "retainAllNodes" is a boolean that is set to
+ * true if the nodes can be delivered in any order so long as the right number of nodes
+ * are delivered. If this boolean is false, the caller doesn't care whether duplicate nodes
+ * are retained or whether they are eliminated.
+ */
+
+ public boolean retainAllNodes = true;
+
+ /**
+ * In the case of RANGE_INDEPENDENT, "binding" identifies the range variables whose dependencies
+ * we are looking for. For INLINE_VARIABLE_REFERENCES it is a single Binding that we are aiming to inline
+ */
+
+ /*@Nullable*/ public Binding[] bindingList;
+
+ /**
+ * When a promotion offer is made, containingExpression identifies the level to which the promotion
+ * should occur. When a subexpression is promoted, an expression of the form let $VAR := SUB return ORIG
+ * is created, and this replaces the original containingExpression within the PromotionOffer.
+ */
+
+ public Expression containingExpression;
+
+ /**
+ * Flag that is set if the offer has been accepted, that is, if the expression has changed
+ */
+
+ public boolean accepted = false;
+
+ /**
+ * Create a PromotionOffer for use with a particular Optimizer
+ * @param optimizer the optimizer
+ */
+
+ public PromotionOffer(Optimizer optimizer) {
+ this.optimizer = optimizer;
+ }
+
+ /**
+ * Get the optimizer in use
+ * @return the optimizer
+ */
+
+ public Optimizer getOptimizer() {
+ return optimizer;
+ }
+
+ /**
+ * Method to test whether a subexpression qualifies for promotion, and if so, to
+ * accept the promotion.
+ * @param parent
+ * @param child the subexpression in question
+ * @return if promotion was done, returns the expression that should be used in place
+ * of the child expression. If no promotion was done, returns null. If promotion is
+ * determined not to be necessary for this subtree, returns the supplied child expression
+ * unchanged
+ */
+
+ public Expression accept(Expression parent, Expression child) throws XPathException {
+ switch (action) {
+ case RANGE_INDEPENDENT: {
+ int properties = child.getSpecialProperties();
+ if (((properties & StaticProperty.NON_CREATIVE) != 0) &&
+ !ExpressionTool.dependsOnVariable(child, bindingList) &&
+ (child.getDependencies() &
+ (StaticProperty.HAS_SIDE_EFFECTS | StaticProperty.DEPENDS_ON_ASSIGNABLE_GLOBALS)) == 0) {
+ return promote(parent, child);
+ }
+ break;
+ }
+
+ case FOCUS_INDEPENDENT: {
+ int dependencies = child.getDependencies();
+ int properties = child.getSpecialProperties();
+ if (!promoteXSLTFunctions && ((dependencies & StaticProperty.DEPENDS_ON_XSLT_CONTEXT) != 0)) {
+ break;
+ }
+ if (ExpressionTool.dependsOnVariable(child, bindingList)) {
+ break;
+ }
+ if ((dependencies &
+ (StaticProperty.HAS_SIDE_EFFECTS | StaticProperty.DEPENDS_ON_ASSIGNABLE_GLOBALS)) != 0) {
+ break;
+ }
+ if ((dependencies & StaticProperty.DEPENDS_ON_FOCUS) == 0 &&
+ (properties & StaticProperty.NON_CREATIVE) != 0) {
+ return promote(parent, child);
+ } else if (promoteDocumentDependent &&
+ (dependencies & StaticProperty.DEPENDS_ON_NON_DOCUMENT_FOCUS) == 0 &&
+ (properties & StaticProperty.NON_CREATIVE) != 0) {
+ return promote(parent, child);
+ }
+ break;
+ }
+
+ case REPLACE_CURRENT: {
+ if (child instanceof Current) {
+ LocalVariableReference var = new LocalVariableReference((Assignation)containingExpression);
+ ExpressionTool.copyLocationInfo(child, var);
+ return var;
+ } else if (!ExpressionTool.callsFunction(child, Current.FN_CURRENT)) {
+ return child;
+ }
+ break;
+ }
+
+ case INLINE_VARIABLE_REFERENCES: {
+ if (child instanceof VariableReference &&
+ ((VariableReference)child).getBinding() == bindingList[0]) {
+ try {
+ Expression copy = containingExpression.copy();
+ ExpressionTool.copyLocationInfo(child, copy);
+ return copy;
+ } catch (UnsupportedOperationException err) {
+ // If we can't make a copy, return the original. This is safer than it seems,
+ // because on the paths where this happens, we are merely moving the expression from
+ // one place to another, not replicating it
+ return containingExpression;
+ }
+ }
+ break;
+ }
+ case UNORDERED: {
+ if (child instanceof Reverse) {
+ return ((Reverse)child).getArguments()[0];
+ } else if (child instanceof DocumentSorter && !retainAllNodes) {
+ return ((DocumentSorter)child).getBaseExpression();
+ }
+ break;
+ }
+ case EXTRACT_GLOBAL_VARIABLES:
+ if (!(child instanceof Literal || child instanceof LocalParamSetter ||
+ (child == containingExpression) ||
+ ExpressionTool.containsLocalParam(child)) &&
+ (child.getDependencies()&~StaticProperty.DEPENDS_ON_RUNTIME_ENVIRONMENT) == 0 &&
+ (child.getSpecialProperties() & StaticProperty.NON_CREATIVE) != 0) {
+ return optimizer.extractGlobalVariables(child, visitor, this);
+ }
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Unknown promotion action " + action);
+ }
+ return null;
+ }
+
+ /**
+ * Method to promote a subexpression. A LetExpression is created which binds the child expression
+ * to a system-created variable, and then returns the original expression, with the child expression
+ * replaced by a reference to the variable.
+ * @param child the expression to be promoted
+ * @return the expression that results from the promotion, if any took place
+ */
+
+ private Expression promote(Expression parent, Expression child) {
+ final TypeHierarchy th = optimizer.getConfiguration().getTypeHierarchy();
+
+ // if the expression being promoted is an operand of "=", make the variable an indexed variable
+ boolean indexed = false;
+ //Expression parent = containingExpression.findParentOf(child);
+ if (parent instanceof GeneralComparison && ((GeneralComparison)parent).getOperator() == Token.EQUALS) {
+ indexed = true;
+ }
+
+ LetExpression let = new LetExpression();
+ let.setVariableQName(new StructuredQName("zz", NamespaceConstant.SAXON, "zz" + let.hashCode()));
+ SequenceType type = SequenceType.makeSequenceType(child.getItemType(th), child.getCardinality());
+ let.setRequiredType(type);
+ ExpressionTool.copyLocationInfo(containingExpression, let);
+ //let.setSequence(LazyExpression.makeLazyExpression(child));
+ let.setSequence(child);
+ let.setEvaluationMode(
+ Cardinality.allowsMany(child.getCardinality()) ?
+ ExpressionTool.MAKE_MEMO_CLOSURE :
+ ExpressionTool.MAKE_SINGLETON_CLOSURE);
+ let.setAction(containingExpression);
+ let.adoptChildExpression(containingExpression);
+ if (indexed) {
+ let.setIndexedVariable();
+ }
+ containingExpression = let;
+ accepted = true;
+ LocalVariableReference var = new LocalVariableReference(let);
+ int properties = child.getSpecialProperties()&StaticProperty.NOT_UNTYPED_ATOMIC;
+ var.setStaticType(type, null, properties);
+ ExpressionTool.copyLocationInfo(containingExpression, var);
+ return var;
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/parser/RoleLocator.java b/sf/saxon/expr/parser/RoleLocator.java
new file mode 100644
index 0000000..3d03b60
--- /dev/null
+++ b/sf/saxon/expr/parser/RoleLocator.java
@@ -0,0 +1,211 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.type.ItemType;
+
+import java.io.Serializable;
+
+/**
+ * A RoleLocator identifies the role in which an expression is used, for example as
+ * the third argument of the concat() function. This information is stored in an
+ * ItemChecker or CardinalityChecker so that good diagnostics can be
+ * achieved when run-time type errors are detected.
+ */
+public class RoleLocator implements Serializable {
+
+ private int kind;
+ private Object operation; // always either a String or a StructuredQName
+ private int operand;
+ private String errorCode = "XPTY0004"; // default error code for type errors
+
+ public static final int FUNCTION = 0;
+ public static final int BINARY_EXPR = 1;
+ public static final int TYPE_OP = 2;
+ public static final int VARIABLE = 3;
+ public static final int INSTRUCTION = 4;
+ public static final int FUNCTION_RESULT = 5;
+ public static final int ORDER_BY = 6;
+ public static final int TEMPLATE_RESULT = 7;
+ public static final int PARAM = 8;
+ public static final int UNARY_EXPR = 9;
+ public static final int UPDATING_EXPR = 10;
+ public static final int GROUPING_KEY = 11;
+ public static final int EVALUATE_RESULT = 12;
+ public static final int CONTEXT_ITEM = 13;
+ public static final int AXIS_STEP = 14;
+
+ /**
+ * Create information about the role of a subexpression within its parent expression
+ * @param kind the kind of parent expression, e.g. a function call or a variable reference
+ * @param operation the name of the object in the parent expression, e.g. a function name or
+ * instruction name. May be expressed either as a String or as a {@link net.sf.saxon.om.StructuredQName}.
+ * For a string, the special format element/attribute is recognized, for example xsl:for-each/select,
+ * to identify the role of an XPath expression in a stylesheet.
+ * @param operand Ordinal position of this subexpression, e.g. the position of an argument in
+ * a function call
+ */
+
+ public RoleLocator(int kind, Object operation, int operand) {
+ if (!(operation instanceof String || operation instanceof StructuredQName)) {
+ throw new IllegalArgumentException("operation");
+ }
+ this.kind = kind;
+ this.operation = operation;
+ this.operand = operand;
+ }
+
+ /**
+ * Set the error code to be produced if a type error is detected
+ * @param code The error code
+ */
+
+ public void setErrorCode(/*@Nullable*/ String code) {
+ if (code != null) {
+ this.errorCode = code;
+ }
+ }
+
+ /**
+ * Get the error code to be produced if a type error is detected
+ * @return code The error code
+ */
+
+ public String getErrorCode() {
+ return errorCode;
+ }
+
+ /**
+ * Ask whether the error code represents a type error
+ * @return true if the error is treated as a type error
+ */
+
+ public boolean isTypeError() {
+ return !errorCode.startsWith("FORG") && !errorCode.equals("XPDY0050");
+ }
+
+ /**
+ * Construct and return the error message indicating a type error
+ * @return the constructed error message
+ */
+ public String getMessage() {
+ String name;
+ if (operation instanceof String) {
+ name = (String)operation;
+ } else {
+ name = ((StructuredQName)operation).getDisplayName();
+ }
+
+ switch (kind) {
+ case FUNCTION:
+ if (name.equals("saxon:call")) {
+ if (operand==0) {
+ return "target of dynamic function call";
+ } else {
+ return ordinal(operand) + " argument of dynamic function call";
+ }
+ } else {
+ return ordinal(operand+1) + " argument of " +
+ (name.length()==0 ? "anonymous function" : name + "()");
+ }
+ case BINARY_EXPR:
+ return ordinal(operand+1) + " operand of '" + name + '\'';
+ case UNARY_EXPR:
+ return "operand of '-'";
+ case TYPE_OP:
+ return "value in '" + name + "' expression";
+ case VARIABLE:
+ if (name.equals("saxon:context-item")) {
+ return "context item";
+ } else {
+ return "value of variable $" + name;
+ }
+ case INSTRUCTION:
+ int slash = name.indexOf('/');
+ String attributeName = "";
+ if (slash >= 0) {
+ attributeName = name.substring(slash+1);
+ name = name.substring(0, slash);
+ }
+ return '@' + attributeName + " attribute of " + name;
+ case FUNCTION_RESULT:
+ if (name.length() == 0) {
+ return "result of anonymous function";
+ } else {
+ return "result of function " + name + "()";
+ }
+ case TEMPLATE_RESULT:
+ return "result of template " + name;
+ case ORDER_BY:
+ return ordinal(operand+1) + " sort key";
+ case PARAM:
+ return "value of parameter $" + name;
+ case UPDATING_EXPR:
+ return "value of " + ordinal(operand+1) + " operand of " + name + " expression";
+ case GROUPING_KEY:
+ return "value of the grouping key";
+ case EVALUATE_RESULT:
+ return "result of the expression {" + name + "} evaluated by xsl:evaluate";
+ case CONTEXT_ITEM:
+ return "the context item";
+ case AXIS_STEP:
+ return "the context item for the " + operation + " axis";
+ default:
+ return "";
+ }
+ }
+
+ /**
+ * Construct the part of the message giving the required item type
+ *
+ * @param requiredItemType the item type required by the context of a particular expression
+ * @return a message of the form "Required item type of X is Y"
+ */
+
+ public String composeRequiredMessage(ItemType requiredItemType) {
+ return "Required item type of " + getMessage() +
+ " is " + requiredItemType.toString();
+ }
+
+ /**
+ * Construct a full error message
+ *
+ * @param requiredItemType the item type required by the context of a particular expression
+ * @param suppliedItemType the item type inferred by static analysis of an expression
+ * @return a message of the form "Required item type of A is R; supplied value has item type S"
+ */
+
+ public String composeErrorMessage(ItemType requiredItemType, ItemType suppliedItemType) {
+ return "Required item type of " + getMessage() +
+ " is " + requiredItemType.toString() +
+ "; supplied value has item type " +
+ suppliedItemType.toString();
+ }
+
+ /**
+ * Get the ordinal representation of a number (used to identify which argument of a function
+ * is in error)
+ * @param n the cardinal number
+ * @return the ordinal representation
+ */
+ public static String ordinal(int n) {
+ switch(n) {
+ case 1:
+ return "first";
+ case 2:
+ return "second";
+ case 3:
+ return "third";
+ default:
+ // we can live with 21th, 22th... How many functions have >20 arguments?
+ return n + "th";
+ }
+ }
+}
+
diff --git a/sf/saxon/expr/parser/Token.java b/sf/saxon/expr/parser/Token.java
new file mode 100644
index 0000000..7d22eb1
--- /dev/null
+++ b/sf/saxon/expr/parser/Token.java
@@ -0,0 +1,803 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import java.util.HashMap;
+
+/**
+ * This class holds static constants and methods defining the lexical tokens used in
+ * XPath and XQuery, and associated keywords.
+ */
+
+public abstract class Token {
+
+ /**
+ * Token numbers. Those in the range 0 to 100 are tokens that can be followed
+ * by a name or expression; those in the range 101 to 200 are tokens that can be
+ * followed by an binary operator.
+ */
+
+ /**
+ * Pseudo-token representing the end of the expression
+ */
+ public static final int EOF = 0;
+ /**
+ * "union" or "|" token
+ */
+ public static final int UNION = 1;
+ /**
+ * Forwards "/"
+ */
+ public static final int SLASH = 2;
+ /**
+ * At token, "@"
+ */
+ public static final int AT = 3;
+ /**
+ * Left square bracket
+ */
+ public static final int LSQB = 4;
+ /**
+ * Left parenthesis
+ */
+ public static final int LPAR = 5;
+ /**
+ * Equals token ("=")
+ */
+ public static final int EQUALS = 6;
+ /**
+ * Comma token
+ */
+ public static final int COMMA = 7;
+ /**
+ * Double forwards slash, "//"
+ */
+ public static final int SLASH_SLASH = 8;
+ /**
+ * Operator "or"
+ */
+ public static final int OR = 9;
+ /**
+ * Operator "and"
+ */
+ public static final int AND = 10;
+ /**
+ * Operator ">"
+ */
+ public static final int GT = 11;
+ /**
+ * Operator "<"
+ */
+ public static final int LT = 12;
+ /**
+ * Operator ">="
+ */
+ public static final int GE = 13;
+ /**
+ * Operator "<="
+ */
+ public static final int LE = 14;
+ /**
+ * Operator "+"
+ */
+ public static final int PLUS = 15;
+ /**
+ * Binary minus operator
+ */
+ public static final int MINUS = 16;
+ /**
+ * Multiply operator, "*" when used in an operator context
+ */
+ public static final int MULT = 17;
+ /**
+ * Operator "div"
+ */
+ public static final int DIV = 18;
+ /**
+ * Operator "mod"
+ */
+ public static final int MOD = 19;
+ /**
+ * Operator "is"
+ */
+ public static final int IS = 20;
+ /**
+ * "$" symbol
+ */
+ public static final int DOLLAR = 21;
+ /**
+ * Operator not-equals. That is, "!="
+ */
+ public static final int NE = 22;
+ /**
+ * Operator "intersect"
+ */
+ public static final int INTERSECT = 23;
+ /**
+ * Operator "except"
+ */
+ public static final int EXCEPT = 24;
+ /**
+ * Keyword "return"
+ */
+ public static final int RETURN = 25;
+ /**
+ * Ketword "then"
+ */
+ public static final int THEN = 26;
+ /**
+ * Keyword "else"
+ */
+ public static final int ELSE = 27;
+ /**
+ * Keyword "where"
+ */
+ public static final int WHERE = 28;
+ /**
+ * Operator "to"
+ */
+ public static final int TO = 29;
+ /**
+ * Operator "||"
+ */
+ public static final int CONCAT = 30;
+ /**
+ * Keyword "in"
+ */
+ public static final int IN = 31;
+ /**
+ * Keyword "some"
+ */
+ public static final int SOME = 32;
+ /**
+ * Keyword "every"
+ */
+ public static final int EVERY = 33;
+ /**
+ * Keyword "satisfies"
+ */
+ public static final int SATISFIES = 34;
+ /**
+ * Token representing the name of a function and the following "(" symbol
+ */
+ public static final int FUNCTION = 35;
+
+ /**
+ * Token representing the name of an axis and the following "::" symbol
+ */
+ public static final int AXIS = 36;
+ /**
+ * Keyword "if"
+ */
+ public static final int IF = 37;
+ /**
+ * Operator "<<"
+ */
+ public static final int PRECEDES = 38;
+ /**
+ * Operator ">>"
+ */
+ public static final int FOLLOWS = 39;
+ /**
+ * Operator "!"
+ */
+ public static final int BANG = 40;
+ /**
+ * "::" symbol
+ */
+ public static final int COLONCOLON = 41;
+ /**
+ * ":*" symbol
+ */
+ public static final int COLONSTAR = 42;
+ /**
+ * Token representing a function name and the following "#" symbol
+ */
+ public static final int INLINE_FUNCTION_LITERAL = 43; //GAP HERE
+ /**
+ * # symbol
+ */
+ public static final int HASH = 44;
+ /**
+ * operator "instance of"
+ */
+ public static final int INSTANCE_OF = 45;
+ /**
+ * operator "cast as"
+ */
+ public static final int CAST_AS = 46;
+ /**
+ * operator "treat as"
+ */
+ public static final int TREAT_AS = 47;
+ /**
+ * Tilde, used in XSLT patterns
+ */
+ public static final int TILDE = 48;
+ /**
+ * operator "eq"
+ */
+ public static final int FEQ = 50; // "Fortran" style comparison operators eq, ne, etc
+ /**
+ * operator "ne"
+ */
+ public static final int FNE = 51;
+ /**
+ * operator "gt"
+ */
+ public static final int FGT = 52;
+ /**
+ * operator "lt"
+ */
+ public static final int FLT = 53;
+ /**
+ * operator "ge"
+ */
+ public static final int FGE = 54;
+ /**
+ * opeartor "le"
+ */
+ public static final int FLE = 55;
+ /**
+ * operator "idiv"
+ */
+ public static final int IDIV = 56;
+ /**
+ * operator "castable as"
+ */
+ public static final int CASTABLE_AS = 57;
+ /**
+ * ":=" symbol (XQuery only)
+ */
+ public static final int ASSIGN = 58;
+ /**
+ * "{" symbol (XQuery only)
+ */
+ public static final int LCURLY = 59;
+ /**
+ * composite token: <keyword "{"> (XQuery only)
+ */
+ public static final int KEYWORD_CURLY = 60;
+ /**
+ * composite token <'element' QNAME> (XQuery only)
+ */
+ public static final int ELEMENT_QNAME = 61;
+ /**
+ * composite token <'attribute' QNAME> (XQuery only)
+ */
+ public static final int ATTRIBUTE_QNAME = 62;
+ /**
+ * composite token <'pi' QNAME> (XQuery only)
+ */
+ public static final int PI_QNAME = 63;
+ /**
+ * composite token <'namespace' QNAME> (XQuery only)
+ */
+ public static final int NAMESPACE_QNAME = 64;
+ /**
+ * Keyword "typeswitch"
+ */
+ public static final int TYPESWITCH = 65;
+ /**
+ * Keyword "switch" (XQuery 1.1)
+ */
+ public static final int SWITCH = 66;
+ /**
+ * Keyword "case"
+ */
+ public static final int CASE = 67;
+ /**
+ * Keyword "modify"
+ */
+ public static final int MODIFY = 68;
+
+ /**
+ * Node kind, e.g. "node()" or "comment()"
+ */
+ public static final int NODEKIND = 69;
+ /**
+ * "*:" token
+ */
+ public static final int SUFFIX = 70; // e.g. *:suffix - the suffix is actually a separate token
+ /**
+ * "as" (in XQuery Update rename expression)
+ */
+ public static final int AS = 71;
+ /*
+ * "group by" (XQuery 3.0)
+ */
+ public static final int GROUP_BY = 72;
+ /**
+ * "for tumbling" (XQuery 3.0)
+ */
+ public static final int FOR_TUMBLING = 73;
+ /**
+ * "for sliding" (XQuery 3.0)
+ */
+ public static final int FOR_SLIDING = 74;
+
+
+ // The following tokens are used only in the query prolog. They are categorized
+ // as operators on the basis that a following name is treated as a name rather than
+ // an operator.
+
+
+ /**
+ * "xquery version"
+ */
+ public static final int XQUERY_VERSION = 80;
+ /**
+ * "xquery encoding"
+ */
+ public static final int XQUERY_ENCODING = 81;
+ /**
+ * "declare namespace"
+ */
+ public static final int DECLARE_NAMESPACE = 82;
+ /**
+ * "declare default"
+ */
+ public static final int DECLARE_DEFAULT = 83;
+ /**
+ * "declare construction"
+ */
+ public static final int DECLARE_CONSTRUCTION = 84;
+ /**
+ * "declare base-uri"
+ */
+ public static final int DECLARE_BASEURI = 85;
+ /**
+ * "declare boundary-space"
+ */
+ public static final int DECLARE_BOUNDARY_SPACE = 86;
+ /**
+ * "declare decimal-format"
+ */
+ public static final int DECLARE_DECIMAL_FORMAT = 87;
+ /**
+ * "import schema"
+ */
+ public static final int IMPORT_SCHEMA = 88;
+ /**
+ * "import module"
+ */
+ public static final int IMPORT_MODULE = 89;
+ /**
+ * "declare variable"
+ */
+ public static final int DECLARE_VARIABLE = 90;
+ /**
+ * "declare context"
+ */
+ public static final int DECLARE_CONTEXT = 91;
+ /**
+ * "declare function"
+ */
+ public static final int DECLARE_FUNCTION = 92;
+ /**
+ * "module namespace"
+ */
+ public static final int MODULE_NAMESPACE = 93;
+ /**
+ * Various compound symbols supporting XQuery validation expression
+ */
+ public static final int VALIDATE = 94;
+ public static final int VALIDATE_STRICT = 95;
+ public static final int VALIDATE_LAX = 96;
+ public static final int VALIDATE_TYPE = 97;
+ /**
+ * percent sign '%'
+ */
+ public static final int PERCENT = 98;
+
+ /**
+ * "declare xmlspace"
+ */
+ public static final int DECLARE_ORDERING = 100;
+
+ /**
+ * "declare copy-namespaces"
+ */
+ public static final int DECLARE_COPY_NAMESPACES = 101;
+ /**
+ * "declare option"
+ */
+ public static final int DECLARE_OPTION = 102;
+ /**
+ * "declare revalidation"
+ */
+ public static final int DECLARE_REVALIDATION = 110;
+ /**
+ * "insert node/nodes"
+ */
+ public static final int INSERT_NODE = 111;
+ /**
+ * "delete node/nodes"
+ */
+ public static final int DELETE_NODE = 112;
+ /**
+ * "replace node/nodes"
+ */
+ public static final int REPLACE_NODE = 113;
+ /**
+ * "replace value"
+ */
+ public static final int REPLACE_VALUE = 114;
+ /**
+ * "rename node"
+ */
+ public static final int RENAME_NODE = 115;
+ /**
+ * "first into"
+ */
+ public static final int FIRST_INTO = 116;
+ /**
+ * "last into"
+ */
+ public static final int LAST_INTO = 117;
+ /**
+ * "after"
+ */
+ public static final int AFTER = 118;
+ /**
+ * "before"
+ */
+ public static final int BEFORE = 119;
+ /**
+ * "into"
+ */
+ public static final int INTO = 120;
+ /**
+ * "with"
+ */
+ public static final int WITH = 121;
+ /**
+ * "declare updating [function]"
+ */
+ public static final int DECLARE_UPDATING = 122;
+ /**
+ * declare %
+ */
+ public static final int DECLARE_ANNOTATED = 123;
+// /**
+// * declare deterministic [function]"
+// */
+// public static final int DECLARE_DETERMINISTIC = 123;
+// /**
+// * declare nondeterministic [function]"
+// */
+// public static final int DECLARE_NONDETERMINISTIC = 124;
+// /**
+// * declare private [function]"
+// */
+// public static final int DECLARE_PRIVATE = 125;
+// /**
+// * declare public [function]"
+// */
+// public static final int DECLARE_PUBLIC = 126;
+ /**
+ * semicolon separator
+ */
+ public static final int SEMICOLON = 149;
+
+
+ /**
+ * Constant identifying the token number of the last token to be classified as an operator
+ */
+ static int LAST_OPERATOR = 150;
+
+ // Tokens that set "operator" context, so an immediately following "div" is recognized
+ // as an operator, not as an element name
+
+ /**
+ * Name token (a QName, in general)
+ */
+ public static final int NAME = 201;
+ /**
+ * String literal
+ */
+ public static final int STRING_LITERAL = 202;
+ /**
+ * Right square bracket
+ */
+ public static final int RSQB = 203;
+ /**
+ * Right parenthesis
+ */
+ public static final int RPAR = 204;
+ /**
+ * "." symbol
+ */
+ public static final int DOT = 205;
+ /**
+ * ".." symbol
+ */
+ public static final int DOTDOT = 206;
+ /**
+ * "*" symbol when used as a wildcard
+ */
+ public static final int STAR = 207;
+ /**
+ * "prefix:*" token
+ */
+ public static final int PREFIX = 208; // e.g. prefix:*
+ /**
+ * Numeric literal
+ */
+ public static final int NUMBER = 209;
+
+ /**
+ * "for" keyword
+ */
+ public static final int FOR = 211;
+
+ /**
+ * Keyword "default"
+ */
+ public static final int DEFAULT = 212;
+ /**
+ * Question mark symbol. That is, "?"
+ */
+ public static final int QMARK = 213;
+ /**
+ * "}" symbol (XQuery only)
+ */
+ public static final int RCURLY = 215;
+ /**
+ * "let" keyword (XQuery only)
+ */
+ public static final int LET = 216;
+ /**
+ * "<" at the start of a tag (XQuery only). The pseudo-XML syntax that
+ * follows is read character-by-character by the XQuery parser
+ */
+ public static final int TAG = 217;
+ /**
+ * A token representing an XQuery pragma.
+ * This construct "(# .... #)" is regarded as a single token, for the QueryParser to sort out.
+ */
+ public static final int PRAGMA = 218;
+ /**
+ * "copy" keyword
+ */
+ public static final int COPY = 219;
+ /**
+ * "copy" keyword
+ */
+ public static final int COUNT = 220;
+
+ /**
+ * Unary minus sign
+ */
+ public static final int NEGATE = 299; // unary minus: not actually a token, but we
+ // use token numbers to identify operators.
+
+
+ /**
+ * The following strings are used to represent tokens in error messages
+ */
+
+ public final static String[] tokens = new String[300];
+
+ static {
+ tokens[EOF] = "<eof>";
+ tokens[UNION] = "|";
+ tokens[SLASH] = "/";
+ tokens[AT] = "@";
+ tokens[TILDE] = "~";
+ tokens[LSQB] = "[";
+ tokens[LPAR] = "(";
+ tokens[EQUALS] = "=";
+ tokens[COMMA] = ",";
+ tokens[SLASH_SLASH] = "//";
+ tokens[OR] = "or";
+ tokens[AND] = "and";
+ tokens[GT] = ">";
+ tokens[LT] = "<";
+ tokens[GE] = ">=";
+ tokens[LE] = "<=";
+ tokens[PLUS] = "+";
+ tokens[MINUS] = "-";
+ tokens[MULT] = "*";
+ tokens[DIV] = "div";
+ tokens[MOD] = "mod";
+ tokens[IS] = "is";
+ tokens[DOLLAR] = "$";
+ tokens[NE] = "!=";
+ tokens[BANG] = "!";
+ tokens[CONCAT] = "||";
+ tokens[INTERSECT] = "intersect";
+ tokens[EXCEPT] = "except";
+ tokens[RETURN] = "return";
+ tokens[THEN] = "then";
+ tokens[ELSE] = "else";
+ tokens[TO] = "to";
+ tokens[IN] = "in";
+ tokens[SOME] = "some";
+ tokens[EVERY] = "every";
+ tokens[SATISFIES] = "satisfies";
+ tokens[FUNCTION] = "<function>(";
+ tokens[AXIS] = "<axis>";
+ tokens[IF] = "if(";
+ tokens[PRECEDES] = "<<";
+ tokens[FOLLOWS] = ">>";
+ tokens[COLONCOLON] = "::";
+ tokens[COLONSTAR] = ":*";
+ tokens[HASH] = "#";
+ tokens[INSTANCE_OF] = "instance of";
+ tokens[CAST_AS] = "cast as";
+ tokens[TREAT_AS] = "treat as";
+ tokens[FEQ] = "eq";
+ tokens[FNE] = "ne";
+ tokens[FGT] = "gt";
+ tokens[FGE] = "ge";
+ tokens[FLT] = "lt";
+ tokens[FLE] = "le";
+ tokens[IDIV] = "idiv";
+ tokens[CASTABLE_AS] = "castable as";
+ tokens[ASSIGN] = ":=";
+ tokens[SWITCH] = "switch";
+ tokens[TYPESWITCH] = "typeswitch";
+ tokens[CASE] = "case";
+ tokens[DEFAULT] = "default";
+ //tokens [ AS_FIRST ] = "as first";
+ //tokens [ AS_LAST ] = "as last";
+ tokens[AFTER] = "after";
+ tokens[BEFORE] = "before";
+ tokens[INTO] = "into";
+ tokens[WITH] = "with";
+ tokens[MODIFY] = "modify";
+ tokens[AS] = "as";
+
+
+ tokens[NAME] = "<name>";
+ tokens[STRING_LITERAL] = "<string-literal>";
+ tokens[RSQB] = "]";
+ tokens[RPAR] = ")";
+ tokens[DOT] = ".";
+ tokens[DOTDOT] = "..";
+ tokens[STAR] = "*";
+ tokens[PREFIX] = "<prefix:*>";
+ tokens[NUMBER] = "<numeric-literal>";
+ tokens[NODEKIND] = "<node-type>()";
+ tokens[FOR] = "for";
+ tokens[SUFFIX] = "<*:local-name>";
+ tokens[QMARK] = "?";
+ tokens[LCURLY] = "{";
+ tokens[KEYWORD_CURLY] = "<keyword> {";
+ tokens[RCURLY] = "}";
+ tokens[LET] = "let";
+ tokens[VALIDATE] = "validate {";
+ tokens[TAG] = "<element>";
+ tokens[PRAGMA] = "(# ... #)";
+ tokens[SEMICOLON] = ";";
+ tokens[COPY] = "copy";
+ tokens[NEGATE] = "-";
+ tokens[PERCENT] = "%";
+ }
+
+ /**
+ * Lookup table for composite (two-keyword) tokens
+ */
+ /*@NotNull*/ public static HashMap<String, Integer> doubleKeywords = new HashMap<String, Integer>(30);
+ /**
+ * Pseudo-token representing the start of the expression
+ */
+ public static final int UNKNOWN = -1;
+
+ private Token() {
+ }
+
+ static {
+ mapDouble("instance of", INSTANCE_OF);
+ mapDouble("cast as", CAST_AS);
+ mapDouble("treat as", TREAT_AS);
+ mapDouble("castable as", CASTABLE_AS);
+ mapDouble("group by", GROUP_BY);
+ mapDouble("for tumbling", FOR_TUMBLING);
+ mapDouble("for sliding", FOR_SLIDING);
+ mapDouble("xquery version", XQUERY_VERSION);
+ mapDouble("xquery encoding", XQUERY_ENCODING);
+ mapDouble("declare namespace", DECLARE_NAMESPACE);
+ mapDouble("declare default", DECLARE_DEFAULT);
+ mapDouble("declare construction", DECLARE_CONSTRUCTION);
+ mapDouble("declare base-uri", DECLARE_BASEURI);
+ mapDouble("declare boundary-space", DECLARE_BOUNDARY_SPACE);
+ mapDouble("declare decimal-format", DECLARE_DECIMAL_FORMAT);
+ mapDouble("declare ordering", DECLARE_ORDERING);
+ mapDouble("declare copy-namespaces", DECLARE_COPY_NAMESPACES);
+ mapDouble("declare option", DECLARE_OPTION);
+ mapDouble("declare revalidation", DECLARE_REVALIDATION);
+ mapDouble("import schema", IMPORT_SCHEMA);
+ mapDouble("import module", IMPORT_MODULE);
+ mapDouble("declare variable", DECLARE_VARIABLE);
+ mapDouble("declare context", DECLARE_CONTEXT);
+ mapDouble("declare function", DECLARE_FUNCTION);
+ mapDouble("declare updating", DECLARE_UPDATING);
+ mapDouble("module namespace", MODULE_NAMESPACE);
+ mapDouble("validate strict", VALIDATE_STRICT);
+ mapDouble("validate lax", VALIDATE_LAX);
+ mapDouble("validate type", VALIDATE_TYPE);
+ mapDouble("insert node", INSERT_NODE);
+ mapDouble("insert nodes", INSERT_NODE);
+ mapDouble("delete node", DELETE_NODE);
+ mapDouble("delete nodes", DELETE_NODE);
+ mapDouble("replace node", REPLACE_NODE);
+ mapDouble("replace value", REPLACE_VALUE);
+ mapDouble("rename node", RENAME_NODE);
+ mapDouble("rename nodes", RENAME_NODE);
+ mapDouble("first into", FIRST_INTO);
+ mapDouble("last into", LAST_INTO);
+ }
+
+ private static void mapDouble(String doubleKeyword, int token) {
+ doubleKeywords.put(doubleKeyword, token);
+ tokens[token] = doubleKeyword;
+ }
+
+ /**
+ * Return the inverse of a relational operator, so that "a op b" can be
+ * rewritten as "b inverse(op) a"
+ * @param operator the operator whose inverse is required
+ * @return the inverse operator
+ */
+
+ public static int inverse(int operator) {
+ switch (operator) {
+ case LT:
+ return GT;
+ case LE:
+ return GE;
+ case GT:
+ return LT;
+ case GE:
+ return LE;
+ case FLT:
+ return FGT;
+ case FLE:
+ return FGE;
+ case FGT:
+ return FLT;
+ case FGE:
+ return FLE;
+ default:
+ return operator;
+ }
+ }
+
+ /**
+ * Return the negation of a relational operator, so that "a op b" can be
+ * rewritten as not(b op' a)
+ * @param operator the operator to be negated
+ * @return the negated operator
+ */
+
+ public static int negate(int operator) {
+ switch (operator) {
+ case FEQ:
+ return FNE;
+ case FNE:
+ return FEQ;
+ case FLT:
+ return FGE;
+ case FLE:
+ return FGT;
+ case FGT:
+ return FLE;
+ case FGE:
+ return FLT;
+ default:
+ throw new IllegalArgumentException("Invalid operator for negate()");
+ }
+ }
+
+ public static boolean isOrderedOperator(int operator) {
+ return operator != FEQ && operator != FNE;
+ }
+}
+
diff --git a/sf/saxon/expr/parser/Tokenizer.java b/sf/saxon/expr/parser/Tokenizer.java
new file mode 100644
index 0000000..e935495
--- /dev/null
+++ b/sf/saxon/expr/parser/Tokenizer.java
@@ -0,0 +1,1116 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tokenizer for expressions and inputs.
+ *
+ * This code was originally derived from James Clark's xt, though it has been greatly modified since.
+ * See copyright notice at end of file.
+ */
+
+
+ at SuppressWarnings({"StringEquality"})
+public final class Tokenizer {
+
+
+ private int state = DEFAULT_STATE;
+ // we may need to make this a stack at some time
+
+ /**
+ * Initial default state of the Tokenizer
+ */
+ public static final int DEFAULT_STATE = 0;
+
+ /**
+ * State in which a name is NOT to be merged with what comes next, for example "("
+ */
+ public static final int BARE_NAME_STATE = 1;
+
+ /**
+ * State in which the next thing to be read is a SequenceType
+ */
+ public static final int SEQUENCE_TYPE_STATE = 2;
+ /**
+ * State in which the next thing to be read is an operator
+ */
+
+ public static final int OPERATOR_STATE = 3;
+
+ /**
+ * The starting line number (for XPath in XSLT, the line number in the stylesheet)
+ */
+ public int startLineNumber;
+ /**
+ * The number identifying the most recently read token
+ */
+ public int currentToken = Token.EOF;
+ /**
+ * The string value of the most recently read token
+ */
+ /*@Nullable*/ public String currentTokenValue = null;
+ /**
+ * The position in the input expression where the current token starts
+ */
+ public int currentTokenStartOffset = 0;
+ /**
+ * The number of the next token to be returned
+ */
+ private int nextToken = Token.EOF;
+ /**
+ * The string value of the next token to be returned
+ */
+ private String nextTokenValue = null;
+ /**
+ * The position in the expression of the start of the next token
+ */
+ private int nextTokenStartOffset = 0;
+ /**
+ * The string being parsed
+ */
+ public String input;
+ /**
+ * The current position within the input string
+ */
+ public int inputOffset = 0;
+ /**
+ * The length of the input string
+ */
+ private int inputLength;
+ /**
+ * The line number (within the expression) of the current token
+ */
+ private int lineNumber = 1;
+ /**
+ * The line number (within the expression) of the next token
+ */
+ private int nextLineNumber = 1;
+
+ /**
+ * List containing the positions (offsets in the input string) at which newline characters
+ * occur
+ */
+
+ private List newlineOffsets = null;
+
+ /**
+ * The token number of the token that preceded the current token
+ */
+ private int precedingToken = Token.UNKNOWN;
+
+ /**
+ * Flag to disallow "union" as a synonym for "|" when parsing XSLT 2.0 patterns
+ */
+
+ public boolean disallowUnionKeyword;
+
+ /**
+ * Flag to indicate that this is XQuery as distinct from XPath
+ */
+
+ public boolean isXQuery = false;
+
+ public Tokenizer() {}
+
+ /**
+ * Get the current tokenizer state
+ * @return the current state
+ */
+
+ public int getState() {
+ return state;
+ }
+
+ /**
+ * Set the tokenizer into a special state
+ * @param state the new state
+ */
+
+ public void setState(int state) {
+ this.state = state;
+ if (state==DEFAULT_STATE) {
+ // force the followsOperator() test to return true
+ precedingToken = Token.UNKNOWN;
+ currentToken = Token.UNKNOWN;
+ } else if (state==OPERATOR_STATE) {
+ precedingToken = Token.RPAR;
+ currentToken = Token.RPAR;
+ }
+ }
+
+ //
+ // Lexical analyser for expressions, queries, and XSLT patterns
+ //
+
+ /**
+ * Prepare a string for tokenization.
+ * The actual tokens are obtained by calls on next()
+ *
+ * @param input the string to be tokenized
+ * @param start start point within the string
+ * @param end end point within the string (last character not read):
+ * -1 means end of string
+ * @param lineNumber the linenumber in the source where the expression appears
+ * @throws XPathException if a lexical error occurs, e.g. unmatched
+ * string quotes
+ */
+ public void tokenize(String input, int start, int end, int lineNumber) throws XPathException {
+ nextToken = Token.EOF;
+ nextTokenValue = null;
+ nextTokenStartOffset = 0;
+ inputOffset = start;
+ this.input = input;
+ startLineNumber = lineNumber;
+ this.lineNumber = lineNumber;
+ nextLineNumber = lineNumber;
+ if (end==-1) {
+ inputLength = input.length();
+ } else {
+ inputLength = end;
+ }
+
+ // The tokenizer actually reads one token ahead. The raw lexical analysis performed by
+ // the lookAhead() method does not (in general) distinguish names used as QNames from names
+ // used for operators, axes, and functions. The next() routine further refines names into the
+ // correct category, by looking at the following token. In addition, it combines compound tokens
+ // such as "instance of" and "cast as".
+
+ lookAhead();
+ next();
+ }
+
+ //diagnostic version of next(): change real version to realnext()
+ //
+ //public void next() throws XPathException {
+ // realnext();
+ // System.err.println("Token: " + currentToken + "[" + tokens[currentToken] + "]");
+ //}
+
+ /**
+ * Get the next token from the input expression. The type of token is returned in the
+ * currentToken variable, the string value of the token in currentTokenValue.
+ *
+ * @throws XPathException if a lexical error is detected
+ */
+
+ public void next() throws XPathException {
+ precedingToken = currentToken;
+ currentToken = nextToken;
+ currentTokenValue = nextTokenValue;
+ if (currentTokenValue==null) {
+ currentTokenValue="";
+ }
+ currentTokenStartOffset = nextTokenStartOffset;
+ lineNumber = nextLineNumber;
+
+ // disambiguate the current token based on the tokenizer state
+
+ switch (currentToken) {
+ case Token.NAME:
+ int optype = getBinaryOp(currentTokenValue);
+ if (optype!=Token.UNKNOWN && !followsOperator(precedingToken)) {
+ currentToken = optype;
+ }
+ break;
+ case Token.LT:
+ if (isXQuery && followsOperator(precedingToken)) {
+ currentToken = Token.TAG;
+ }
+ break;
+ case Token.STAR:
+ if (!followsOperator(precedingToken)) {
+ currentToken = Token.MULT;
+ }
+ break;
+ }
+
+ if (currentToken == Token.TAG || currentToken == Token.RCURLY) {
+ // No lookahead after encountering "<" at the start of an XML-like tag.
+ // After an RCURLY, the parser must do an explicit lookahead() to continue
+ // tokenizing; otherwise it can continue with direct character reading
+ return;
+ }
+
+ int oldPrecedingToken = precedingToken;
+ lookAhead();
+
+ if (currentToken == Token.NAME) {
+ if (state == BARE_NAME_STATE) {
+ return;
+ }
+ if (oldPrecedingToken == Token.DOLLAR) {
+ return;
+ }
+ switch (nextToken) {
+ case Token.LPAR:
+ int op = getBinaryOp(currentTokenValue);
+ // the test on followsOperator() is to cater for an operator being used as a function name,
+ // e.g. is(): see XQTS test K-FunctionProlog-66
+ if (op == Token.UNKNOWN || followsOperator(oldPrecedingToken)) {
+ currentToken = getFunctionType(currentTokenValue);
+ lookAhead(); // swallow the "("
+ } else {
+ currentToken = op;
+ }
+ break;
+
+ case Token.LCURLY:
+ if (!(state == SEQUENCE_TYPE_STATE)) {
+ currentToken = Token.KEYWORD_CURLY;
+ lookAhead(); // swallow the "{"
+ }
+ break;
+
+ case Token.COLONCOLON:
+ lookAhead();
+ currentToken = Token.AXIS;
+ break;
+
+ case Token.HASH:
+ lookAhead();
+ currentToken = Token.INLINE_FUNCTION_LITERAL;
+ break;
+
+ case Token.COLONSTAR:
+ lookAhead();
+ currentToken = Token.PREFIX;
+ break;
+
+ case Token.DOLLAR:
+ if (currentTokenValue.equals("for")) {
+ currentToken = Token.FOR;
+ } else if (currentTokenValue.equals("some")) {
+ currentToken = Token.SOME;
+ } else if (currentTokenValue.equals("every")) {
+ currentToken = Token.EVERY;
+ } else if (currentTokenValue.equals("let")) {
+ currentToken = Token.LET;
+ } else if (currentTokenValue.equals("count")) {
+ currentToken = Token.COUNT;
+ } else if (currentTokenValue.equals("copy")) {
+ currentToken = Token.COPY;
+ }
+ break;
+
+ case Token.PERCENT:
+ if (currentTokenValue.equals("declare")) {
+ currentToken = Token.DECLARE_ANNOTATED;
+ }
+ break;
+
+ case Token.NAME:
+ int candidate = -1;
+ if (currentTokenValue.equals("element")) {
+ candidate = Token.ELEMENT_QNAME;
+ } else if (currentTokenValue.equals("attribute")) {
+ candidate = Token.ATTRIBUTE_QNAME;
+ } else if (currentTokenValue.equals("processing-instruction")) {
+ candidate = Token.PI_QNAME;
+ } else if (currentTokenValue.equals("namespace")) {
+ candidate = Token.NAMESPACE_QNAME;
+ }
+ if (candidate != -1) {
+ // <'element' QName '{'> constructor
+ // <'attribute' QName '{'> constructor
+ // <'processing-instruction' QName '{'> constructor
+ // <'namespace' QName '{'> constructor
+
+ String qname = nextTokenValue;
+ String saveTokenValue = currentTokenValue;
+ int savePosition = inputOffset;
+ lookAhead();
+ if (nextToken == Token.LCURLY) {
+ currentToken = candidate;
+ currentTokenValue = qname;
+ lookAhead();
+ return;
+ } else {
+ // backtrack (we don't have 2-token lookahead; this is the
+ // only case where it's needed. So we backtrack instead.)
+ currentToken = Token.NAME;
+ currentTokenValue = saveTokenValue;
+ inputOffset = savePosition;
+ nextToken = Token.NAME;
+ nextTokenValue = qname;
+ }
+
+ }
+ String composite = currentTokenValue + ' ' + nextTokenValue;
+ Integer val = Token.doubleKeywords.get(composite);
+ if (val==null) {
+ break;
+ } else {
+ currentToken = val.intValue();
+ currentTokenValue = composite;
+ // some tokens are actually triples
+// if (currentToken == Token.AS_FIRST || currentToken == Token.AS_LAST) {
+// lookAhead();
+// if (nextToken != Token.NAME || !nextTokenValue.equals("into")) {
+// throw new XPathException("After '" + composite + "', expected 'into'");
+// }
+// nextToken = currentToken; // to reestablish after-operator state
+// } else
+ if (currentToken == Token.REPLACE_VALUE) {
+ // this one's a quadruplet - "replace value of node"
+ lookAhead();
+ if (nextToken != Token.NAME || !nextTokenValue.equals("of")) {
+ throw new XPathException("After '" + composite + "', expected 'of'");
+ }
+ lookAhead();
+ if (nextToken != Token.NAME || !nextTokenValue.equals("node")) {
+ throw new XPathException("After 'replace value of', expected 'node'");
+ }
+ nextToken = currentToken; // to reestablish after-operator state
+ }
+ lookAhead();
+ return;
+ }
+ default:
+ // no action needed
+ }
+ }
+ }
+
+ /**
+ * Force the current token to be treated as an operator if possible
+ */
+
+ public void treatCurrentAsOperator() {
+ switch (currentToken) {
+ case Token.NAME:
+ int optype = getBinaryOp(currentTokenValue);
+ if (optype!=Token.UNKNOWN) {
+ currentToken = optype;
+ }
+ break;
+ case Token.STAR:
+ currentToken = Token.MULT;
+ break;
+ }
+ }
+
+ /**
+ * Look ahead by one token. This method does the real tokenization work.
+ * The method is normally called internally, but the XQuery parser also
+ * calls it to resume normal tokenization after dealing with pseudo-XML
+ * syntax.
+ * @throws XPathException if a lexical error occurs
+ */
+ public void lookAhead() throws XPathException {
+ precedingToken = nextToken;
+ nextTokenValue = null;
+ nextTokenStartOffset = inputOffset;
+ for (;;) {
+ if (inputOffset >= inputLength) {
+ nextToken = Token.EOF;
+ return;
+ }
+ char c = input.charAt(inputOffset++);
+ switch (c) {
+ case '/':
+ if (inputOffset < inputLength
+ && input.charAt(inputOffset) == '/') {
+ inputOffset++;
+ nextToken = Token.SLASH_SLASH;
+ return;
+ }
+ nextToken = Token.SLASH;
+ return;
+ case ':':
+ if (inputOffset < inputLength) {
+ if (input.charAt(inputOffset) == ':') {
+ inputOffset++;
+ nextToken = Token.COLONCOLON;
+ return;
+ } else if (input.charAt(inputOffset) == '=') {
+ nextToken = Token.ASSIGN;
+ inputOffset++;
+ return;
+ }
+ }
+ throw new XPathException("Unexpected colon at start of token");
+ case '@':
+ nextToken = Token.AT;
+ return;
+ case '~':
+ nextToken = Token.TILDE;
+ return;
+ case '?':
+ nextToken = Token.QMARK;
+ return;
+ case '[':
+ nextToken = Token.LSQB;
+ return;
+ case ']':
+ nextToken = Token.RSQB;
+ return;
+ case '{':
+ nextToken = Token.LCURLY;
+ return;
+ case '}':
+ nextToken = Token.RCURLY;
+ return;
+ case ';':
+ nextToken = Token.SEMICOLON;
+ state = DEFAULT_STATE;
+ return;
+ case '%':
+ nextToken = Token.PERCENT;
+ return;
+ case '(':
+ if (inputOffset < inputLength && input.charAt(inputOffset) == '#') {
+ inputOffset++;
+ int pragmaStart = inputOffset;
+ int nestingDepth = 1;
+ while (nestingDepth > 0 && inputOffset < (inputLength-1)) {
+ if (input.charAt(inputOffset) == '\n') {
+ incrementLineNumber();
+ } else if (input.charAt(inputOffset) == '#' &&
+ input.charAt(inputOffset+1) == ')') {
+ nestingDepth--;
+ inputOffset++;
+ } else if (input.charAt(inputOffset) == '(' &&
+ input.charAt(inputOffset+1) == '#') {
+ nestingDepth++;
+ inputOffset++;
+ }
+ inputOffset++;
+ }
+ if (nestingDepth > 0) {
+ throw new XPathException("Unclosed XQuery pragma");
+ }
+ nextToken = Token.PRAGMA;
+ nextTokenValue = input.substring(pragmaStart, inputOffset-2 );
+ return;
+ }
+ if (inputOffset < inputLength && input.charAt(inputOffset) == ':') {
+ // XPath comment syntax is (: .... :)
+ // Comments may be nested, and may now be empty
+ inputOffset++;
+ int nestingDepth = 1;
+ while (nestingDepth > 0 && inputOffset < (inputLength-1)) {
+ if (input.charAt(inputOffset) == '\n') {
+ incrementLineNumber();
+ } else if (input.charAt(inputOffset) == ':' &&
+ input.charAt(inputOffset+1) == ')') {
+ nestingDepth--;
+ inputOffset++;
+ } else if (input.charAt(inputOffset) == '(' &&
+ input.charAt(inputOffset+1) == ':') {
+ nestingDepth++;
+ inputOffset++;
+ }
+ inputOffset++;
+ }
+ if (nestingDepth > 0) {
+ throw new XPathException("Unclosed XPath comment");
+ }
+ lookAhead();
+ } else {
+ nextToken = Token.LPAR;
+ }
+ return;
+ case ')':
+ nextToken = Token.RPAR;
+ return;
+ case '+':
+ nextToken = Token.PLUS;
+ return;
+ case '-':
+ nextToken = Token.MINUS; // not detected if part of a name
+ return;
+ case '=':
+ nextToken = Token.EQUALS;
+ return;
+ case '!':
+ if (inputOffset < inputLength
+ && input.charAt(inputOffset) == '=') {
+ inputOffset++;
+ nextToken = Token.NE;
+ return;
+ }
+ nextToken = Token.BANG;
+ return;
+ case '*':
+ // disambiguation of MULT and STAR is now done later
+ if (inputOffset < inputLength
+ && input.charAt(inputOffset) == ':'
+ && !(inputOffset+1 < inputLength && input.charAt(inputOffset+1) == '=')) {
+ inputOffset++;
+ nextToken = Token.SUFFIX;
+ // we leave the parser to get the following name as a separate
+ // token, but first check there's no intervening white space or comments
+ if (inputOffset < inputLength) {
+ char ahead = input.charAt(inputOffset);
+ if (" \r\t\n(".indexOf(ahead) >= 0) {
+ throw new XPathException("Whitespace and comments are not allowed after '*:'");
+ }
+ }
+ return;
+ }
+ nextToken = Token.STAR;
+ return;
+ case ',':
+ nextToken = Token.COMMA;
+ return;
+ case '$':
+ nextToken = Token.DOLLAR;
+ return;
+ case '|':
+ if (inputOffset < inputLength && input.charAt(inputOffset) == '|') {
+ inputOffset++;
+ nextToken = Token.CONCAT;
+ return;
+ }
+ nextToken = Token.UNION;
+ return;
+ case '#':
+ nextToken = Token.HASH;
+ return;
+ case '<':
+ if (inputOffset < inputLength
+ && input.charAt(inputOffset) == '=') {
+ inputOffset++;
+ nextToken = Token.LE;
+ return;
+ }
+ if (inputOffset < inputLength
+ && input.charAt(inputOffset) == '<') {
+ inputOffset++;
+ nextToken = Token.PRECEDES;
+ return;
+ }
+ nextToken = Token.LT;
+ return;
+ case '>':
+ if (inputOffset < inputLength
+ && input.charAt(inputOffset) == '=') {
+ inputOffset++;
+ nextToken = Token.GE;
+ return;
+ }
+ if (inputOffset < inputLength
+ && input.charAt(inputOffset) == '>') {
+ inputOffset++;
+ nextToken = Token.FOLLOWS;
+ return;
+ }
+ nextToken = Token.GT;
+ return;
+ case '.':
+ if (inputOffset < inputLength
+ && input.charAt(inputOffset) == '.') {
+ inputOffset++;
+ nextToken = Token.DOTDOT;
+ return;
+ }
+ if (inputOffset == inputLength
+ || input.charAt(inputOffset) < '0'
+ || input.charAt(inputOffset) > '9') {
+ nextToken = Token.DOT;
+ return;
+ }
+ // otherwise drop through: we have a number starting with a decimal point
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ // The logic here can return some tokens that are not legitimate numbers,
+ // for example "23e" or "1.0e+". However, this will only happen if the XPath
+ // expression as a whole is syntactically incorrect.
+ // These errors will be caught by the numeric constructor.
+ boolean allowE = true;
+ boolean allowSign = false;
+ boolean allowDot = true;
+ boolean endOfNum = false;
+ numloop:
+ while (!endOfNum) {
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ allowSign = false;
+ break;
+ case '.':
+ if (allowDot) {
+ allowDot = false;
+ allowSign = false;
+ } else {
+ inputOffset--;
+ break numloop;
+ }
+ break;
+ case 'E': case 'e':
+ if (allowE) {
+ allowSign = true;
+ allowE = false;
+ } else {
+ inputOffset--;
+ break numloop;
+ }
+ break;
+ case '+': case '-':
+ if (allowSign) {
+ allowSign = false;
+ } else {
+ inputOffset--;
+ break numloop;
+ }
+ break;
+ default:
+ if (('a' <= c && c <= 'z') || c>127) {
+ // this prevents the famous "10div 3"
+ throw new XPathException("Separator needed after numeric literal");
+ }
+ inputOffset--;
+ break numloop;
+ }
+ if (inputOffset >= inputLength) break;
+ c = input.charAt(inputOffset++);
+ }
+ nextTokenValue = input.substring(nextTokenStartOffset, inputOffset);
+ nextToken = Token.NUMBER;
+ return;
+ case '"':
+ case '\'':
+ nextTokenValue = "";
+ while (true) {
+ inputOffset = input.indexOf(c, inputOffset);
+ if (inputOffset < 0) {
+ inputOffset = nextTokenStartOffset + 1;
+ throw new XPathException("Unmatched quote in expression");
+ }
+ nextTokenValue += input.substring(nextTokenStartOffset + 1, inputOffset++);
+ if (inputOffset < inputLength) {
+ char n = input.charAt(inputOffset);
+ if (n == c) {
+ // Doubled delimiters
+ nextTokenValue += c;
+ nextTokenStartOffset = inputOffset;
+ inputOffset++;
+
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+
+ // maintain line number if there are newlines in the string
+ if (nextTokenValue.indexOf('\n') >= 0) {
+ for (int i = 0; i<nextTokenValue.length(); i++) {
+ if (nextTokenValue.charAt(i) == '\n') {
+ incrementLineNumber(nextTokenStartOffset+i+1);
+ }
+ }
+ }
+ //nextTokenValue = nextTokenValue.intern();
+ nextToken = Token.STRING_LITERAL;
+ return;
+ case '\n':
+ incrementLineNumber();
+ // drop through
+ case ' ':
+ case '\t':
+ case '\r':
+ nextTokenStartOffset = inputOffset;
+ break;
+ case 'Q':
+ if (inputOffset < inputLength && input.charAt(inputOffset) == '{') {
+ // EQName, revised syntax as per bug 15399
+ int close = input.indexOf('}', inputOffset++);
+ String uri = input.substring(inputOffset, close);
+ inputOffset = close+1;
+ int start = inputOffset;
+ boolean isStar = false;
+ while(inputOffset < inputLength) {
+ char c2 = input.charAt(inputOffset);
+ if (c2 > 0x80 || Character.isLetterOrDigit(c2) || c2=='_' || c2=='.' || c2=='-') {
+ inputOffset++;
+ } else if (c2=='*' && (start == inputOffset)) {
+ inputOffset++;
+ isStar = true;
+ break;
+ } else {
+ break;
+ }
+ }
+ String localName = input.substring(start, inputOffset);
+ nextTokenValue = "{" + uri + "}" + localName;
+ // Reuse Token.NAME because EQName is allowed anywhere that QName is allowed
+ nextToken = (isStar ? Token.PREFIX : Token.NAME);
+ return;
+
+
+ }
+ /* else fall through */
+ default:
+ if (c < 0x80 && !Character.isLetter(c)) {
+ throw new XPathException("Invalid character '" + c + "' in expression");
+ }
+ /* fall through */
+ case '_':
+ loop:
+ for (;inputOffset < inputLength; inputOffset++) {
+ c = input.charAt(inputOffset);
+ switch (c) {
+ case ':':
+ if (inputOffset+1 < inputLength) {
+ char nc = input.charAt(inputOffset+1);
+ if (nc == ':') {
+ nextTokenValue = input.substring(nextTokenStartOffset, inputOffset);
+ //nextTokenValue = nextTokenValue.intern();
+ nextToken = Token.AXIS;
+ inputOffset+=2;
+ return;
+ } else if (nc == '*') {
+ nextTokenValue = input.substring(nextTokenStartOffset, inputOffset);
+ //nextTokenValue = nextTokenValue.intern();
+ nextToken = Token.PREFIX;
+ inputOffset+=2;
+ return;
+ } else if (nc == '=') {
+ // as in "let $x:=2"
+ nextTokenValue = input.substring(nextTokenStartOffset, inputOffset);
+ //nextTokenValue = nextTokenValue.intern();
+ nextToken = Token.NAME;
+ return;
+ }
+ }
+ break;
+ case '.':
+ case '-':
+ case '_':
+ break;
+
+ default:
+ if (c < 0x80 && !Character.isLetterOrDigit(c))
+ break loop;
+ break;
+ }
+ }
+ nextTokenValue = input.substring(nextTokenStartOffset, inputOffset);
+ //nextTokenValue = nextTokenValue.intern();
+ nextToken = Token.NAME;
+ return;
+ }
+ }
+ }
+
+ /**
+ * Identify a binary operator
+ *
+ * @param s String representation of the operator - must be interned
+ * @return the token number of the operator, or UNKNOWN if it is not a
+ * known operator
+ */
+
+ private int getBinaryOp(String s) {
+ switch(s.length()) {
+ case 2:
+ if (s.equals("or")) return Token.OR;
+ if (s.equals("is")) return Token.IS;
+ if (s.equals("to")) return Token.TO;
+ if (s.equals("in")) return Token.IN;
+ if (s.equals("eq")) return Token.FEQ;
+ if (s.equals("ne")) return Token.FNE;
+ if (s.equals("gt")) return Token.FGT;
+ if (s.equals("ge")) return Token.FGE;
+ if (s.equals("lt")) return Token.FLT;
+ if (s.equals("le")) return Token.FLE;
+ if (s.equals("as")) return Token.AS;
+ break;
+ case 3:
+ if (s.equals("and")) return Token.AND;
+ if (s.equals("div")) return Token.DIV;
+ if (s.equals("mod")) return Token.MOD;
+ break;
+ case 4:
+ if (s.equals("idiv")) return Token.IDIV;
+ if (s.equals("then")) return Token.THEN;
+ if (s.equals("else")) return Token.ELSE;
+ if (s.equals("case")) return Token.CASE;
+ if (s.equals("into")) return Token.INTO;
+ if (s.equals("with")) return Token.WITH;
+ break;
+ case 5:
+ if (s.equals("where")) return Token.WHERE;
+ if (s.equals("union") && !disallowUnionKeyword) return Token.UNION;
+ if (s.equals("after")) return Token.AFTER;
+ break;
+ case 6:
+ if (s.equals("except")) return Token.EXCEPT;
+ if (s.equals("return")) return Token.RETURN;
+ if (s.equals("before")) return Token.BEFORE;
+ if (s.equals("modify")) return Token.MODIFY;
+ break;
+ case 7:
+ if (s.equals("default")) return Token.DEFAULT;
+ case 9:
+ if (s.equals("intersect")) return Token.INTERSECT;
+ if (s.equals("satisfies")) return Token.SATISFIES;
+ break;
+ }
+ return Token.UNKNOWN;
+ }
+
+ /**
+ * Distinguish nodekind names, "if", and function names, which are all
+ * followed by a "("
+ *
+ * @param s the name - must be interned
+ * @return the token number
+ */
+
+ private static int getFunctionType(String s) {
+ switch(s.length()) {
+ case 2:
+ if (s.equals("if")) return Token.IF;
+ break;
+ case 3:
+ if (s.equals("map")) return Token.NODEKIND; // map() in XPath 3.0
+ break;
+ case 4:
+ if (s.equals("node")) return Token.NODEKIND;
+ if (s.equals("item")) return Token.NODEKIND;
+ if (s.equals("text")) return Token.NODEKIND;
+ break;
+ case 6:
+ if (s.equals("switch")) return Token.SWITCH; // XQuery 3.0
+ break;
+ case 7:
+ if (s.equals("element")) return Token.NODEKIND;
+ if (s.equals("comment")) return Token.NODEKIND;
+ break;
+ case 8:
+ if (s.equals("function")) return Token.NODEKIND; // Higher order functions in XPath 2.0
+ break;
+ case 9:
+ if (s.equals("attribute")) return Token.NODEKIND;
+ break;
+ case 10:
+ if (s.equals("typeswitch")) return Token.TYPESWITCH;
+ break;
+ case 13:
+ if (s.equals("document-node")) return Token.NODEKIND;
+ break;
+ case 14:
+ if (s.equals("empty-sequence")) return Token.NODEKIND;
+ if (s.equals("namespace-node")) return Token.NODEKIND;
+ if (s.equals("schema-element")) return Token.NODEKIND;
+ break;
+ default:
+ if (s.equals("schema-attribute")) return Token.NODEKIND;
+ if (s.equals("processing-instruction")) return Token.NODEKIND;
+ break;
+ }
+ return Token.FUNCTION;
+ }
+
+ /**
+ * Test whether the previous token is an operator
+ * @param precedingToken the token to be tested
+ * @return true if the previous token is an operator token
+ */
+
+ private boolean followsOperator(int precedingToken) {
+ return precedingToken <= Token.LAST_OPERATOR;
+ }
+
+ /**
+ * Read next character directly. Used by the XQuery parser when parsing pseudo-XML syntax
+ * @return the next character from the input
+ * @throws StringIndexOutOfBoundsException if an attempt is made to read beyond
+ * the end of the string. This will only occur in the event of a syntax error in the
+ * input.
+ */
+
+ public char nextChar() throws StringIndexOutOfBoundsException {
+ char c = input.charAt(inputOffset++);
+ //c = normalizeLineEnding(c);
+ if (c=='\n') {
+ incrementLineNumber();
+ lineNumber++;
+ }
+ return c;
+ }
+
+ /**
+ * Increment the line number, making a record of where in the input string the newline character occurred.
+ */
+
+ private void incrementLineNumber() {
+ nextLineNumber++;
+ if (newlineOffsets==null) {
+ newlineOffsets = new ArrayList(20);
+ }
+ newlineOffsets.add(Integer.valueOf(inputOffset-1));
+ }
+
+ /**
+ * Increment the line number, making a record of where in the input string the newline character occurred.
+ * @param offset the place in the input string where the newline occurred
+ */
+
+ public void incrementLineNumber(int offset) {
+ nextLineNumber++;
+ if (newlineOffsets==null) {
+ newlineOffsets = new ArrayList(20);
+ }
+ newlineOffsets.add(Integer.valueOf(offset));
+ }
+
+ /**
+ * Step back one character. If this steps back to a previous line, adjust the line number.
+ */
+
+ public void unreadChar() {
+ if (input.charAt(--inputOffset) == '\n') {
+ nextLineNumber--;
+ lineNumber--;
+ if (newlineOffsets != null) {
+ newlineOffsets.remove(newlineOffsets.size()-1);
+ }
+ }
+ }
+
+ /**
+ * Get the most recently read text (for use in an error message)
+ * @param offset the offset of the offending token, if known, or -1 to use the current offset
+ * @return a chunk of text leading up to the error
+ */
+
+ public String recentText(int offset) {
+ if (offset == -1) {
+ // if no offset was supplied, we want the text immediately before the current reading position
+ if (inputOffset > inputLength) {
+ inputOffset = inputLength;
+ }
+ if (inputOffset < 34) {
+ return input.substring(0, inputOffset);
+ } else {
+ return Whitespace.collapseWhitespace(
+ "..." + input.substring(inputOffset-30, inputOffset)).toString();
+ }
+ } else {
+ // if a specific offset was supplied, we want the text *starting* at that offset
+ int end = offset + 30;
+ if (end > inputLength) {
+ end = inputLength;
+ }
+ return Whitespace.collapseWhitespace(
+ (offset > 0 ? "..." : "") +
+ input.substring(offset, end)).toString();
+ }
+ }
+
+ /**
+ * Get the line number of the current token
+ * @return the line number
+ */
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Get the column number of the current token
+ * @return the column number
+ */
+
+ public int getColumnNumber() {
+ return (int)(getLineAndColumn(currentTokenStartOffset)&0x7fffffff);
+ }
+
+ /**
+ * Get the line and column number corresponding to a given offset in the input expression,
+ * as a long value with the line number in the top half
+ * and the column number in the lower half
+ * @param offset the byte offset in the expression
+ * @return the line and column number, packed together
+ */
+
+ public long getLineAndColumn(int offset) {
+ if (newlineOffsets==null) {
+ return ((long)startLineNumber) << 32 | (long)offset;
+ }
+ for (int line=newlineOffsets.size()-1; line>=0; line--) {
+ int nloffset = ((Integer)newlineOffsets.get(line)).intValue();
+ if (offset > nloffset) {
+ return ((long)(line+startLineNumber+1)<<32) | ((long)(offset - nloffset));
+ }
+ }
+ return ((long)startLineNumber) << 32 | (long)(offset+1);
+ }
+
+ /**
+ * Return the line number corresponding to a given offset in the expression
+ * @param offset the byte offset in the expression
+ * @return the line number
+ */
+
+ public int getLineNumber(int offset) {
+ return (int)((getLineAndColumn(offset))>>32);
+ }
+
+ /**
+ * Return the column number corresponding to a given offset in the expression
+ * @param offset the byte offset in the expression
+ * @return the column number
+ */
+
+ public int getColumnNumber(int offset) {
+ return (int)((getLineAndColumn(offset))&0x7fffffff);
+ }
+
+}
+
+/*
+
+The following copyright notice is copied from the licence for xt, from which the
+original version of this module was derived:
+--------------------------------------------------------------------------------
+Copyright (c) 1998, 1999 James Clark
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL JAMES CLARK BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of James Clark shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from James Clark.
+---------------------------------------------------------------------------
+*/
\ No newline at end of file
diff --git a/sf/saxon/expr/parser/TypeChecker.java b/sf/saxon/expr/parser/TypeChecker.java
new file mode 100644
index 0000000..a7ebe56
--- /dev/null
+++ b/sf/saxon/expr/parser/TypeChecker.java
@@ -0,0 +1,743 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.parser;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.TypeCheckerEnvironment;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.functions.NumberFn;
+import net.sf.saxon.functions.StringFn;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.SequenceExtent;
+import net.sf.saxon.value.SequenceType;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * This class provides Saxon's type checking capability. It contains a static method,
+ * staticTypeCheck, which is called at compile time to perform type checking of
+ * an expression. This class is never instantiated.
+ */
+
+public final class TypeChecker {
+
+ // Class is not instantiated
+ private TypeChecker() {
+ }
+
+ /**
+ * Check an expression against a required type, modifying it if necessary.
+ * <p/>
+ * <p>This method takes the supplied expression and checks to see whether it is
+ * known statically to conform to the specified type. There are three possible
+ * outcomes. If the static type of the expression is a subtype of the required
+ * type, the method returns the expression unchanged. If the static type of
+ * the expression is incompatible with the required type (for example, if the
+ * supplied type is integer and the required type is string) the method throws
+ * an exception (this results in a compile-time type error being reported). If
+ * the static type is a supertype of the required type, then a new expression
+ * is constructed that evaluates the original expression and checks the dynamic
+ * type of the result; this new expression is returned as the result of the
+ * method.</p>
+ * <p/>
+ * <p>The rules applied are those for function calling in XPath, that is, the rules
+ * that the argument of a function call must obey in relation to the signature of
+ * the function. Some contexts require slightly different rules (for example,
+ * operands of polymorphic operators such as "+"). In such cases this method cannot
+ * be used.</p>
+ * <p/>
+ * <p>Note that this method does <b>not</b> do recursive type-checking of the
+ * sub-expressions.</p>
+ *
+ * @param supplied The expression to be type-checked
+ * @param req The required type for the context in which the expression is used
+ * @param backwardsCompatible True if XPath 1.0 backwards compatibility mode is applicable
+ * @param role Information about the role of the subexpression within the
+ * containing expression, used to provide useful error messages
+ * @param visitor An expression visitor
+ * @return The original expression if it is type-safe, or the expression
+ * wrapped in a run-time type checking expression if not.
+ * @throws XPathException if the supplied type is statically inconsistent with the
+ * required type (that is, if they have no common subtype)
+ */
+
+ public static Expression staticTypeCheck(Expression supplied,
+ SequenceType req,
+ boolean backwardsCompatible,
+ RoleLocator role,
+ final TypeCheckerEnvironment visitor)
+ throws XPathException {
+
+ // System.err.println("Static Type Check on expression (requiredType = " + req + "):"); supplied.display(10);
+
+ if (supplied.implementsStaticTypeCheck()) {
+ return supplied.staticTypeCheck(req, backwardsCompatible, role, visitor);
+ }
+
+ Expression exp = supplied;
+ final TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+
+ final ItemType reqItemType = req.getPrimaryType();
+ int reqCard = req.getCardinality();
+ boolean allowsMany = Cardinality.allowsMany(reqCard);
+
+ ItemType suppliedItemType = null;
+ // item type of the supplied expression: null means not yet calculated
+ int suppliedCard = -1;
+ // cardinality of the supplied expression: -1 means not yet calculated
+
+ boolean cardOK = (reqCard == StaticProperty.ALLOWS_ZERO_OR_MORE);
+ // Unless the required cardinality is zero-or-more (no constraints).
+ // check the static cardinality of the supplied expression
+ if (!cardOK) {
+ suppliedCard = exp.getCardinality();
+ cardOK = Cardinality.subsumes(reqCard, suppliedCard);
+ // May later find that cardinality is not OK after all, if atomization takes place
+ }
+
+ boolean itemTypeOK = reqItemType instanceof AnyItemType;
+ // Unless the required item type and content type are ITEM (no constraints)
+ // check the static item type against the supplied expression.
+ // NOTE: we don't currently do any static inference regarding the content type
+ if (!itemTypeOK) {
+ suppliedItemType = exp.getItemType(th);
+ if (suppliedItemType instanceof ErrorType) {
+ // supplied type is empty-sequence(): this can violate a cardinality constraint but not an item type constraint
+ itemTypeOK = true;
+ } else {
+ if (reqItemType == null || suppliedItemType == null) {
+ throw new NullPointerException();
+ }
+ int relation = th.relationship(reqItemType, suppliedItemType);
+ itemTypeOK = relation == TypeHierarchy.SAME_TYPE || relation == TypeHierarchy.SUBSUMES;
+ }
+ }
+
+
+ // Handle the special rules for 1.0 compatibility mode
+ if (backwardsCompatible && !allowsMany) {
+ // rule 1
+ if (Cardinality.allowsMany(suppliedCard)) {
+ Expression cexp = FirstItemExpression.makeFirstItemExpression(exp);
+ cexp.adoptChildExpression(exp);
+ exp = cexp;
+ suppliedCard = StaticProperty.ALLOWS_ZERO_OR_ONE;
+ cardOK = Cardinality.subsumes(reqCard, suppliedCard);
+ }
+ if (!itemTypeOK) {
+ // rule 2
+ if (reqItemType.equals(BuiltInAtomicType.STRING)) {
+ StringFn fn = (StringFn) SystemFunctionCall.makeSystemFunction("string", new Expression[]{exp});
+ try {
+ exp = visitor.typeCheck(visitor.simplify(fn), new ExpressionVisitor.ContextItemType(AnyItemType.getInstance(), true));
+ } catch (XPathException err) {
+ err.maybeSetLocation(exp);
+ throw err.makeStatic();
+ }
+ suppliedItemType = BuiltInAtomicType.STRING;
+ suppliedCard = StaticProperty.EXACTLY_ONE;
+ cardOK = Cardinality.subsumes(reqCard, suppliedCard);
+ itemTypeOK = true;
+ }
+ // rule 3
+ if (reqItemType.equals(BuiltInAtomicType.NUMERIC) || reqItemType.equals(BuiltInAtomicType.DOUBLE)) {
+ NumberFn fn = (NumberFn) SystemFunctionCall.makeSystemFunction("number", new Expression[]{exp});
+ try {
+ exp = visitor.typeCheck(visitor.simplify(fn), new ExpressionVisitor.ContextItemType(AnyItemType.getInstance(), true));
+ } catch (XPathException err) {
+ err.maybeSetLocation(exp);
+ throw err.makeStatic();
+ }
+ suppliedItemType = BuiltInAtomicType.DOUBLE;
+ suppliedCard = StaticProperty.EXACTLY_ONE;
+ cardOK = Cardinality.subsumes(reqCard, suppliedCard);
+ itemTypeOK = true;
+ }
+ }
+ }
+
+ if (!itemTypeOK) {
+ // Now apply the conversions needed in 2.0 mode
+
+ if (reqItemType.isPlainType()) {
+
+ // rule 1: Atomize
+ if (!(suppliedItemType.isPlainType()) &&
+ !(suppliedCard == StaticProperty.EMPTY)) {
+ if (!suppliedItemType.isAtomizable()) {
+ XPathException err = new XPathException(
+ "An atomic value is required for the " + role.getMessage() +
+ ", but the supplied value cannot be atomized", supplied);
+ err.setErrorCode("FOTY0013");
+ err.setIsTypeError(true);
+ err.setLocator(exp);
+ throw err;
+ }
+ exp = Atomizer.makeAtomizer(exp);
+ Expression cexp = visitor.simplify(exp);
+ ExpressionTool.copyLocationInfo(exp, cexp);
+ exp = cexp;
+ suppliedItemType = exp.getItemType(th);
+ suppliedCard = exp.getCardinality();
+ cardOK = Cardinality.subsumes(reqCard, suppliedCard);
+ }
+
+ // rule 2: convert untypedAtomic to the required type
+ //TODO: merge common code between 2a & 2b
+
+ // 2a: all supplied values are untyped atomic. Convert if necessary, and we're finished.
+
+ if ((suppliedItemType.equals(BuiltInAtomicType.UNTYPED_ATOMIC))
+ && !(reqItemType.equals(BuiltInAtomicType.UNTYPED_ATOMIC) || reqItemType.equals(BuiltInAtomicType.ANY_ATOMIC))) {
+
+ if (((SimpleType) reqItemType).isNamespaceSensitive()) {
+ // See spec bug 11964
+ XPathException err = new XPathException(
+ "An untyped atomic value cannot be converted to a QName or NOTATION as required for the " + role.getMessage(), supplied);
+ err.setErrorCode("XPTY0117");
+ err.setIsTypeError(true);
+ err.setLocator(exp);
+ throw err;
+ }
+ Expression cexp = UntypedSequenceConverter.makeUntypedSequenceConverter(visitor.getConfiguration(), exp, (PlainType) reqItemType);
+ ExpressionTool.copyLocationInfo(exp, cexp);
+ try {
+ if (exp instanceof Literal) {
+ exp = Literal.makeLiteral(
+ new SequenceExtent(cexp.iterate(visitor.makeDynamicContext())));
+ } else {
+ exp = cexp;
+ }
+ } catch (XPathException err) {
+ err.maybeSetLocation(exp);
+ err.setErrorCode(role.getErrorCode());
+ throw err.makeStatic();
+ }
+ itemTypeOK = true;
+ suppliedItemType = reqItemType;
+ }
+
+ // 2b: some supplied values are untyped atomic. Convert these to the required type; but
+ // there may be other values in the sequence that won't convert and still need to be checked
+
+ if ((suppliedItemType.equals(BuiltInAtomicType.ANY_ATOMIC))
+ && !(reqItemType.equals(BuiltInAtomicType.UNTYPED_ATOMIC) || reqItemType.equals(BuiltInAtomicType.ANY_ATOMIC))
+ && (exp.getSpecialProperties() & StaticProperty.NOT_UNTYPED_ATOMIC) == 0) {
+
+ Expression conversion;
+ if (((SimpleType) reqItemType).isNamespaceSensitive()) {
+ conversion = UntypedSequenceConverter.makeUntypedSequenceRejector(visitor.getConfiguration(), exp, (PlainType) reqItemType);
+ } else {
+ conversion = UntypedSequenceConverter.makeUntypedSequenceConverter(visitor.getConfiguration(), exp, (PlainType) reqItemType);
+ }
+ ExpressionTool.copyLocationInfo(exp, conversion);
+ try {
+ if (exp instanceof Literal) {
+ exp = Literal.makeLiteral(
+ new SequenceExtent(conversion.iterate(visitor.makeDynamicContext())));
+ } else {
+ exp = conversion;
+ }
+ suppliedItemType = exp.getItemType(th);
+ } catch (XPathException err) {
+ err.maybeSetLocation(exp);
+ throw err.makeStatic();
+ }
+ }
+
+ // Rule 3a: numeric promotion decimal -> float -> double
+
+ if (reqItemType instanceof AtomicType) {
+ int rt = ((AtomicType) reqItemType).getFingerprint();
+ if ((rt == StandardNames.XS_DOUBLE &&
+ th.relationship(suppliedItemType, BuiltInAtomicType.NUMERIC) != TypeHierarchy.DISJOINT)) {
+ Expression cexp = makePromoterToDouble(exp, visitor);
+ ExpressionTool.copyLocationInfo(exp, cexp);
+ exp = cexp;
+ try {
+ exp = visitor.typeCheck(visitor.simplify(exp), new ExpressionVisitor.ContextItemType(AnyItemType.getInstance(), true));
+ } catch (XPathException err) {
+ err.maybeSetLocation(exp);
+ throw err.makeStatic();
+ }
+ suppliedItemType = BuiltInAtomicType.DOUBLE;
+ suppliedCard = -1;
+
+ } else if (rt == StandardNames.XS_FLOAT &&
+ th.relationship(suppliedItemType, BuiltInAtomicType.NUMERIC) != TypeHierarchy.DISJOINT &&
+ !th.isSubType(suppliedItemType, BuiltInAtomicType.DOUBLE)) {
+ Expression cexp = makePromoterToFloat(exp, visitor);
+ ExpressionTool.copyLocationInfo(exp, cexp);
+ exp = cexp;
+ try {
+ exp = visitor.typeCheck(visitor.simplify(exp), new ExpressionVisitor.ContextItemType(AnyItemType.getInstance(), true));
+ } catch (XPathException err) {
+ err.maybeSetLocation(exp);
+ throw err.makeStatic();
+ }
+ suppliedItemType = BuiltInAtomicType.FLOAT;
+ suppliedCard = -1;
+
+ }
+
+ // Rule 3b: promotion from anyURI -> string
+
+ if (rt == StandardNames.XS_STRING && th.isSubType(suppliedItemType, BuiltInAtomicType.ANY_URI)) {
+ suppliedItemType = BuiltInAtomicType.STRING;
+ itemTypeOK = true;
+ // we don't generate code to do a run-time type conversion; rather, we rely on
+ // operators and functions that accept a string to also accept an xs:anyURI. This
+ // is straightforward, because anyURIValue is a subclass of StringValue
+ }
+ }
+
+ } else if (reqItemType instanceof FunctionItemType && !((FunctionItemType) reqItemType).isMapType()) {
+ if (!(suppliedItemType instanceof FunctionItemType)) {
+ exp = new ItemChecker(exp, th.getGenericFunctionItemType(), role);
+ suppliedItemType = th.getGenericFunctionItemType();
+ }
+ exp = makeFunctionSequenceCoercer(exp, (FunctionItemType) reqItemType, visitor, role);
+ itemTypeOK = true;
+
+ } else if (reqItemType instanceof ExternalObjectType &&
+ Sequence.class.isAssignableFrom(((ExternalObjectType)reqItemType).getJavaClass()) &&
+ reqCard == StaticProperty.EXACTLY_ONE) {
+ // special case: allow an extension function to call an instance method on the implementation type of an XDM value
+ // we leave the conversion to be sorted out at run-time
+ itemTypeOK = true;
+ }
+
+ }
+
+ // If both the cardinality and item type are statically OK, return now.
+ if (itemTypeOK && cardOK) {
+ return exp;
+ }
+
+ // If we haven't evaluated the cardinality of the supplied expression, do it now
+ if (suppliedCard == -1) {
+ suppliedCard = exp.getCardinality();
+ if (!cardOK) {
+ cardOK = Cardinality.subsumes(reqCard, suppliedCard);
+ }
+ }
+
+ // If an empty sequence was explicitly supplied, and empty sequence is allowed,
+ // then the item type doesn't matter
+ if (cardOK && suppliedCard == StaticProperty.EMPTY) {
+ return exp;
+ }
+
+ // If the supplied value is () and () isn't allowed, fail now
+ if (suppliedCard == StaticProperty.EMPTY && ((reqCard & StaticProperty.ALLOWS_ZERO) == 0)) {
+ XPathException err = new XPathException("An empty sequence is not allowed as the " + role.getMessage(), supplied);
+ err.setErrorCode(role.getErrorCode());
+ err.setIsTypeError(role.isTypeError());
+ err.setLocator(exp);
+ throw err;
+ }
+
+ // Try a static type check. We only throw it out if the call cannot possibly succeed, unless
+ // pessimistic type checking is enabled
+
+ int relation = (itemTypeOK ? TypeHierarchy.SUBSUMED_BY : th.relationship(suppliedItemType, reqItemType));
+ boolean pessimistic = false;
+ // Pessimistic type checking is not working. It needs at the very least an error type (for use by
+ // fn:error and by ErrorExpression) that satisfies any other type.
+ if (pessimistic) {
+ if (relation != TypeHierarchy.SAME_TYPE && relation != TypeHierarchy.SUBSUMED_BY) {
+ XPathException err = new XPathException("Required item type of " + role.getMessage() +
+ " is " + reqItemType.toString() +
+ "; supplied value has inferred item type " +
+ suppliedItemType.toString() +
+ ". Pessimistic static typing is enabled, so this is an error", supplied);
+ err.setErrorCode(role.getErrorCode());
+ err.setIsTypeError(role.isTypeError());
+ err.setLocator(supplied);
+ throw err;
+ }
+ if (!Cardinality.subsumes(reqCard, suppliedCard)) {
+ XPathException err = new XPathException("Required cardinality of " + role.getMessage() +
+ " is " + Cardinality.toString(reqCard) +
+ "; supplied value has inferred cardinality " +
+ Cardinality.toString(suppliedCard) +
+ ". Pessimistic static typing is enabled, so this is an error", supplied);
+ err.setErrorCode(role.getErrorCode());
+ err.setIsTypeError(role.isTypeError());
+ err.setLocator(supplied);
+ throw err;
+ }
+ } else {
+ if (relation == TypeHierarchy.DISJOINT) {
+ // The item types may be disjoint, but if both the supplied and required types permit
+ // an empty sequence, we can't raise a static error. Raise a warning instead.
+ if (Cardinality.allowsZero(suppliedCard) &&
+ Cardinality.allowsZero(reqCard)) {
+ if (suppliedCard != StaticProperty.EMPTY) {
+ String msg = "Required item type of " + role.getMessage() +
+ " is " + reqItemType.toString() +
+ "; supplied value has item type " +
+ suppliedItemType.toString() +
+ ". The expression can succeed only if the supplied value is an empty sequence.";
+ visitor.issueWarning(msg, supplied);
+ }
+ } else {
+ XPathException err = new XPathException("Required item type of " + role.getMessage() +
+ " is " + reqItemType.toString() +
+ "; supplied value has item type " +
+ suppliedItemType.toString(), supplied);
+ err.setErrorCode(role.getErrorCode());
+ err.setIsTypeError(role.isTypeError());
+ err.setLocator(supplied);
+ throw err;
+ }
+ }
+ }
+
+ // Unless the type is guaranteed to match, add a dynamic type check,
+ // unless the value is already known in which case we might as well report
+ // the error now.
+
+ if (!(relation == TypeHierarchy.SAME_TYPE || relation == TypeHierarchy.SUBSUMED_BY)) {
+ if (exp instanceof Literal) {
+ XPathException err = new XPathException("Required item type of " + role.getMessage() +
+ " is " + reqItemType.toString() +
+ "; supplied value has item type " +
+ suppliedItemType.toString(), supplied);
+ err.setErrorCode(role.getErrorCode());
+ err.setIsTypeError(role.isTypeError());
+ err.setLocator(supplied);
+ throw err;
+ }
+ Expression cexp = new ItemChecker(exp, reqItemType, role);
+ ExpressionTool.copyLocationInfo(exp, cexp);
+ exp = cexp;
+ }
+
+ if (!cardOK) {
+ if (exp instanceof Literal) {
+ XPathException err = new XPathException("Required cardinality of " + role.getMessage() +
+ " is " + Cardinality.toString(reqCard) +
+ "; supplied value has cardinality " +
+ Cardinality.toString(suppliedCard), supplied);
+ err.setIsTypeError(role.isTypeError());
+ err.setErrorCode(role.getErrorCode());
+ err.setLocator(supplied);
+ throw err;
+ } else {
+ Expression cexp = CardinalityChecker.makeCardinalityChecker(exp, reqCard, role);
+ ExpressionTool.copyLocationInfo(exp, cexp);
+ exp = cexp;
+ }
+ }
+
+ return exp;
+ }
+
+ private static Expression makeFunctionSequenceCoercer(Expression exp, FunctionItemType reqItemType, final TypeCheckerEnvironment visitor, RoleLocator role) throws XPathException {
+ // Apply function coercion as defined in XPath 3.0. To avoid retaining the entire static context
+ // at run-time, we create a cut-down version of the expression visitor with just enough information
+ // to do type-checking and nothing much else
+
+ TypeCheckerEnvironment tce = new TypeCheckerEnvironment() {
+ final Configuration config = visitor.getConfiguration();
+ final CollationMap collationMap = visitor.getCollationMap();
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ public void issueWarning(String message, SourceLocator locator) {
+ // no action
+ }
+
+ public CollationMap getCollationMap() {
+ return collationMap;
+ }
+
+ public XPathContext makeDynamicContext() {
+ return new EarlyEvaluationContext(config, collationMap);
+ }
+
+ public Expression simplify(Expression exp) throws XPathException {
+ return exp;
+ }
+
+ public Expression typeCheck(Expression exp, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return exp;
+ }
+ };
+ exp = reqItemType.makeFunctionSequenceCoercer(exp, role, tce);
+ return exp;
+ }
+
+ /**
+ * Check an expression against a required type, modifying it if necessary. This
+ * is a variant of the method {@link #staticTypeCheck} used for expressions that
+ * declare variables in XQuery. In these contexts, conversions such as numeric
+ * type promotion and atomization are not allowed.
+ *
+ * @param supplied The expression to be type-checked
+ * @param req The required type for the context in which the expression is used
+ * @param role Information about the role of the subexpression within the
+ * containing expression, used to provide useful error messages
+ * @param env The static context containing the types being checked. At present
+ * this is used only to locate a NamePool
+ * @return The original expression if it is type-safe, or the expression
+ * wrapped in a run-time type checking expression if not.
+ * @throws XPathException if the supplied type is statically inconsistent with the
+ * required type (that is, if they have no common subtype)
+ */
+
+ public static Expression strictTypeCheck(Expression supplied,
+ SequenceType req,
+ RoleLocator role,
+ StaticContext env)
+ throws XPathException {
+
+ // System.err.println("Strict Type Check on expression (requiredType = " + req + "):"); supplied.display(10);
+
+ Expression exp = supplied;
+ final TypeHierarchy th = env.getConfiguration().getTypeHierarchy();
+
+ ItemType reqItemType = req.getPrimaryType();
+ int reqCard = req.getCardinality();
+
+ ItemType suppliedItemType = null;
+ // item type of the supplied expression: null means not yet calculated
+ int suppliedCard = -1;
+ // cardinality of the supplied expression: -1 means not yet calculated
+
+ boolean cardOK = (reqCard == StaticProperty.ALLOWS_ZERO_OR_MORE);
+ // Unless the required cardinality is zero-or-more (no constraints).
+ // check the static cardinality of the supplied expression
+ if (!cardOK) {
+ suppliedCard = exp.getCardinality();
+ cardOK = Cardinality.subsumes(reqCard, suppliedCard);
+ }
+
+ boolean itemTypeOK = req.getPrimaryType() instanceof AnyItemType;
+ // Unless the required item type and content type are ITEM (no constraints)
+ // check the static item type against the supplied expression.
+ // NOTE: we don't currently do any static inference regarding the content type
+ if (!itemTypeOK) {
+ suppliedItemType = exp.getItemType(th);
+ int relation = th.relationship(reqItemType, suppliedItemType);
+ itemTypeOK = relation == TypeHierarchy.SAME_TYPE || relation == TypeHierarchy.SUBSUMES;
+ }
+
+ // If both the cardinality and item type are statically OK, return now.
+ if (itemTypeOK && cardOK) {
+ return exp;
+ }
+
+ // If we haven't evaluated the cardinality of the supplied expression, do it now
+ if (suppliedCard == -1) {
+ if (suppliedItemType instanceof ErrorType) {
+ suppliedCard = StaticProperty.EMPTY;
+ } else {
+ suppliedCard = exp.getCardinality();
+ }
+ if (!cardOK) {
+ cardOK = Cardinality.subsumes(reqCard, suppliedCard);
+ }
+ }
+
+ // If an empty sequence was explicitly supplied, and empty sequence is allowed,
+ // then the item type doesn't matter
+ if (cardOK && suppliedCard == StaticProperty.EMPTY) {
+ return exp;
+ }
+
+ // If we haven't evaluated the item type of the supplied expression, do it now
+ if (suppliedItemType == null) {
+ suppliedItemType = exp.getItemType(th);
+ }
+
+ if (suppliedCard == StaticProperty.EMPTY && ((reqCard & StaticProperty.ALLOWS_ZERO) == 0)) {
+ XPathException err = new XPathException("An empty sequence is not allowed as the " + role.getMessage(), supplied);
+ err.setErrorCode(role.getErrorCode());
+ err.setIsTypeError(role.isTypeError());
+ err.setLocator(exp);
+ throw err;
+ }
+
+ // Try a static type check. We only throw it out if the call cannot possibly succeed.
+
+ int relation = th.relationship(suppliedItemType, reqItemType);
+ if (relation == TypeHierarchy.DISJOINT) {
+ // The item types may be disjoint, but if both the supplied and required types permit
+ // an empty sequence, we can't raise a static error. Raise a warning instead.
+ if (Cardinality.allowsZero(suppliedCard) &&
+ Cardinality.allowsZero(reqCard)) {
+ if (suppliedCard != StaticProperty.EMPTY) {
+ String msg = "Required item type of " + role.getMessage() +
+ " is " + reqItemType.toString() +
+ "; supplied value has item type " +
+ suppliedItemType.toString() +
+ ". The expression can succeed only if the supplied value is an empty sequence.";
+ env.issueWarning(msg, supplied);
+ }
+ } else {
+ XPathException err = new XPathException("Required item type of " + role.getMessage() +
+ " is " + reqItemType.toString() +
+ "; supplied value has item type " +
+ suppliedItemType.toString(), supplied);
+ err.setErrorCode(role.getErrorCode());
+ err.setIsTypeError(role.isTypeError());
+ err.setLocator(exp);
+ throw err;
+ }
+ }
+
+ // Unless the type is guaranteed to match, add a dynamic type check,
+ // unless the value is already known in which case we might as well report
+ // the error now.
+
+ if (!(relation == TypeHierarchy.SAME_TYPE || relation == TypeHierarchy.SUBSUMED_BY)) {
+ Expression cexp = new ItemChecker(exp, reqItemType, role);
+ cexp.adoptChildExpression(exp);
+ exp = cexp;
+ }
+
+ if (!cardOK) {
+ if (exp instanceof Literal) {
+ XPathException err = new XPathException("Required cardinality of " + role.getMessage() +
+ " is " + Cardinality.toString(reqCard) +
+ "; supplied value has cardinality " +
+ Cardinality.toString(suppliedCard), supplied);
+ err.setIsTypeError(role.isTypeError());
+ err.setErrorCode(role.getErrorCode());
+ throw err;
+ } else {
+ Expression cexp = CardinalityChecker.makeCardinalityChecker(exp, reqCard, role);
+ cexp.adoptChildExpression(exp);
+ exp = cexp;
+ }
+ }
+
+ return exp;
+ }
+
+ /**
+ * Test whether a given value conforms to a given type
+ *
+ * @param val the value
+ * @param requiredType the required type
+ * @param context XPath dynamic context
+ * @return an XPathException describing the error condition if the value doesn't conform;
+ * or null if it does.
+ * @throws XPathException if a failure occurs reading the value
+ */
+
+ /*@Nullable*/
+ public static XPathException testConformance(
+ Sequence val, SequenceType requiredType, XPathContext context)
+ throws XPathException {
+ ItemType reqItemType = requiredType.getPrimaryType();
+ final Configuration config = context.getConfiguration();
+ final TypeHierarchy th = config.getTypeHierarchy();
+ SequenceIterator iter = val.iterate();
+ int count = 0;
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ count++;
+ if (!reqItemType.matchesItem(item, false, config)) {
+ XPathException err = new XPathException("Required type is " + reqItemType +
+ "; supplied value has type " + new SequenceExtent(val.iterate()).getItemType(th));
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ return err;
+ }
+ }
+
+ int reqCardinality = requiredType.getCardinality();
+ if (count == 0 && !Cardinality.allowsZero(reqCardinality)) {
+ XPathException err = new XPathException(
+ "Required type does not allow empty sequence, but supplied value is empty");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ return err;
+ }
+ if (count > 1 && !Cardinality.allowsMany(reqCardinality)) {
+ XPathException err = new XPathException(
+ "Required type requires a singleton sequence; supplied value contains " + count + " items");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ return err;
+ }
+ if (count > 0 && reqCardinality == StaticProperty.EMPTY) {
+ XPathException err = new XPathException(
+ "Required type requires an empty sequence, but supplied value is non-empty");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ return err;
+ }
+ return null;
+ }
+
+ /**
+ * Test whether a given expression is capable of returning a value that has an effective boolean
+ * value.
+ *
+ * @param exp the given expression
+ * @param th the type hierarchy cache
+ * @return null if the expression is OK (optimistically), an exception object if not
+ */
+
+ public static XPathException ebvError(Expression exp, TypeHierarchy th) {
+ if (Cardinality.allowsZero(exp.getCardinality())) {
+ return null;
+ }
+ ItemType t = exp.getItemType(th);
+ if (th.relationship(t, Type.NODE_TYPE) == TypeHierarchy.DISJOINT &&
+ th.relationship(t, BuiltInAtomicType.BOOLEAN) == TypeHierarchy.DISJOINT &&
+ th.relationship(t, BuiltInAtomicType.STRING) == TypeHierarchy.DISJOINT &&
+ th.relationship(t, BuiltInAtomicType.ANY_URI) == TypeHierarchy.DISJOINT &&
+ th.relationship(t, BuiltInAtomicType.UNTYPED_ATOMIC) == TypeHierarchy.DISJOINT &&
+ th.relationship(t, BuiltInAtomicType.NUMERIC) == TypeHierarchy.DISJOINT &&
+ !(t instanceof ExternalObjectType)) {
+ XPathException err = new XPathException(
+ "Effective boolean value is defined only for sequences containing " +
+ "booleans, strings, numbers, URIs, or nodes");
+ err.setErrorCode("FORG0006");
+ err.setIsTypeError(true);
+ return err;
+ }
+ return null;
+ }
+
+ private static Expression makePromoterToDouble(Expression exp, TypeCheckerEnvironment visitor) {
+ AtomicSequenceConverter asc = new AtomicSequenceConverter(exp, BuiltInAtomicType.DOUBLE);
+ Converter converter = new Converter.PromoterToDouble();
+ converter.setConversionRules(visitor.getConfiguration().getConversionRules());
+ asc.setConverter(converter);
+ ExpressionTool.copyLocationInfo(exp, asc);
+ return asc;
+ }
+
+ private static Expression makePromoterToFloat(Expression exp, TypeCheckerEnvironment visitor) {
+ AtomicSequenceConverter asc = new AtomicSequenceConverter(exp, BuiltInAtomicType.FLOAT);
+ Converter converter = new Converter.PromoterToFloat();
+ converter.setConversionRules(visitor.getConfiguration().getConversionRules());
+ asc.setConverter(converter);
+ ExpressionTool.copyLocationInfo(exp, asc);
+ return asc;
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/parser/package.html b/sf/saxon/expr/parser/package.html
new file mode 100644
index 0000000..e63d50b
--- /dev/null
+++ b/sf/saxon/expr/parser/package.html
@@ -0,0 +1,26 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.expr.parser</title>
+
+</head>
+ <body>
+ <p>This package contains classes associated with parsing and general static analysis of XPath expressions.</p>
+
+
+
+
+ <p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+28 November 2011</i></p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/expr/sort/AlphanumericCollator.java b/sf/saxon/expr/sort/AlphanumericCollator.java
new file mode 100644
index 0000000..fd67c9f
--- /dev/null
+++ b/sf/saxon/expr/sort/AlphanumericCollator.java
@@ -0,0 +1,144 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.math.BigInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A Comparer that treats strings as an alternating sequence of alpha parts and numeric parts. The
+ * alpha parts are compared using a base collation supplied as a parameter; the numeric parts are
+ * compared numerically. "Numeric" here means a sequence of consecutive ASCII digits 0-9.
+ * <p>
+ * Note: this StringCollator produces an ordering that is not compatible with equals().
+ * </p>
+ */
+
+public class AlphanumericCollator implements StringCollator, java.io.Serializable {
+
+ private StringCollator baseCollator;
+ private static Pattern pattern = Pattern.compile("\\d+");
+
+ /**
+ * Create an alphanumeric collation
+ * @param base the collation used to compare the alphabetic parts of the string
+ */
+
+ public AlphanumericCollator(StringCollator base) {
+ baseCollator = base;
+ }
+
+ /**
+ * Compare two objects.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ */
+
+ public int compareStrings(String s1, String s2) {
+ int pos1 = 0;
+ int pos2 = 0;
+ Matcher m1 = pattern.matcher(s1);
+ Matcher m2 = pattern.matcher(s2);
+ while (true) {
+
+ // find the next number in each string
+
+ boolean b1 = m1.find(pos1);
+ boolean b2 = m2.find(pos2);
+ int m1start = (b1 ? m1.start() : s1.length());
+ int m2start = (b2 ? m2.start() : s2.length());
+
+ // compare an alphabetic pair (even if zero-length)
+
+ int c = baseCollator.compareStrings(s1.substring(pos1, m1start), s2.substring(pos2, m2start));
+ if (c != 0) {
+ return c;
+ }
+
+ // if one match found a number and the other didn't, exit accordingly
+
+ if (b1 && !b2) {
+ return +1;
+ } else if (b2 && !b1) {
+ return -1;
+ } else if (!b1 && !b2) {
+ return 0;
+ }
+
+ // a number was found in each of the strings: compare the numbers
+
+ BigInteger n1 = new BigInteger(s1.substring(m1start, m1.end()));
+ BigInteger n2 = new BigInteger(s2.substring(m2start, m2.end()));
+ c = n1.compareTo(n2);
+ if (c != 0) {
+ return c;
+ }
+
+ // the numbers are equal: move on to the next part of the string
+
+ pos1 = m1.end();
+ pos2 = m2.end();
+ }
+ }
+
+ /**
+ * Compare two strings for equality. This may be more efficient than using compareStrings and
+ * testing whether the result is zero, but it must give the same result
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return true if and only if the strings are considered equal,
+ */
+
+ public boolean comparesEqual(String s1, String s2) {
+ return compareStrings(s1, s2) == 0;
+ }
+
+ /**
+ * Get a collation key for two Strings. The essential property of collation keys
+ * is that if two values are equal under the collation, then the collation keys are
+ * compare correctly under the equals() method.
+ */
+
+ public Object getCollationKey(/*@NotNull*/ String s) {
+ // The string is normalized by removing leading zeros in a numeric component
+ FastStringBuffer sb = new FastStringBuffer(s.length()*2);
+ int pos1 = 0;
+ Matcher m1 = pattern.matcher(s);
+ while (true) {
+
+ // find the next number in the string
+
+ boolean b1 = m1.find(pos1);
+ int m1start = (b1 ? m1.start() : s.length());
+
+ // handle an alphabetic part (even if zero-length)
+
+ sb.append(s.substring(pos1, m1start));
+
+ // reached end?
+
+ if (!b1) {
+ return sb.toString();
+ }
+
+ // handle a numeric part
+
+ int n1 = Integer.parseInt(s.substring(m1start, m1.end()));
+ sb.append(n1 + "");
+
+ // move on to the next part of the string
+
+ pos1 = m1.end();
+ }
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/AtomicComparer.java b/sf/saxon/expr/sort/AtomicComparer.java
new file mode 100644
index 0000000..394bef1
--- /dev/null
+++ b/sf/saxon/expr/sort/AtomicComparer.java
@@ -0,0 +1,79 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.value.AtomicValue;
+
+import java.io.Serializable;
+
+/**
+ * Interface representing an object that can be used to compare two XPath atomic values for equality or
+ * for ordering.
+ */
+
+public interface AtomicComparer extends Serializable {
+
+ /**
+ * Get the collation used by this AtomicComparer if any
+ * @return the collation used for comparing strings, or null if not applicable
+ */
+
+ public StringCollator getCollator();
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context);
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ * @param a the first object to be compared. It is intended that this should be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the java.util.Comparable
+ * interface.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ * @throws NoDynamicContextException if this comparer required access to dynamic context information,
+ * notably the implicit timezone, and this information is not available. In general this happens if a
+ * context-dependent comparison is attempted at compile-time, and it signals the compiler to generate
+ * code that tries again at run-time.
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) throws NoDynamicContextException;
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ * @param a the first object to be compared.
+ * @param b the second object to be compared.
+ * @return true if the values are equal, false if not
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) throws NoDynamicContextException;
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal
+ * according to the XPath eq operator, then their comparison keys are equal according to the Java
+ * equals() method, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) throws NoDynamicContextException;
+}
diff --git a/sf/saxon/expr/sort/AtomicSortComparer.java b/sf/saxon/expr/sort/AtomicSortComparer.java
new file mode 100644
index 0000000..1c7b81d
--- /dev/null
+++ b/sf/saxon/expr/sort/AtomicSortComparer.java
@@ -0,0 +1,248 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Platform;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.*;
+
+/**
+ * An AtomicComparer used for comparing atomic values of arbitrary item types. It encapsulates
+ * a collator that is used when the values to be compared are strings. It also supports
+ * a separate method for testing equality of items, which can be used for data types that
+ * are not ordered.
+ *
+ * The AtomicSortComparer is identical to the GenericAtomicComparer except for its handling
+ * of NaN: it treats NaN values as lower than any other value, and as equal to each other.
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class AtomicSortComparer implements AtomicComparer {
+
+ private StringCollator collator;
+ private transient XPathContext context;
+ private int itemType;
+
+ /**
+ * Factory method to get an atomic comparer suitable for sorting or for grouping (operations in which
+ * NaN is considered equal to NaN)
+ * @param collator Collating comparer to be used when comparing strings. This argument may be null
+ * if the itemType excludes the possibility of comparing strings. If the method is called at compile
+ * time, this should be a SimpleCollation so that it can be cloned at run-time.
+ * @param itemType the primitive item type of the values to be compared
+ * @param context Dynamic context (may be an EarlyEvaluationContext)
+ * @return a suitable AtomicComparer
+ */
+
+ public static AtomicComparer makeSortComparer(StringCollator collator, int itemType, XPathContext context) {
+ switch (itemType) {
+ case StandardNames.XS_STRING:
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_ANY_URI:
+ if (collator instanceof CodepointCollator) {
+ return CodepointCollatingComparer.getInstance();
+ } else {
+ return new CollatingAtomicComparer(collator, Configuration.getPlatform());
+ }
+ case StandardNames.XS_INTEGER:
+ case StandardNames.XS_DECIMAL:
+ return DecimalSortComparer.getDecimalSortComparerInstance();
+ case StandardNames.XS_DOUBLE:
+ case StandardNames.XS_FLOAT:
+ case StandardNames.XS_NUMERIC:
+ return DoubleSortComparer.getInstance();
+ case StandardNames.XS_DATE_TIME:
+ case StandardNames.XS_DATE:
+ case StandardNames.XS_TIME:
+ return new CalendarValueComparer(context);
+ default:
+ // use the general-purpose comparer that handles all types
+ return new AtomicSortComparer(collator, itemType, context);
+ }
+
+ }
+
+ protected AtomicSortComparer(/*@Nullable*/ StringCollator collator, int itemType, XPathContext context) {
+ this.collator = collator;
+ if (collator == null) {
+ this.collator = CodepointCollator.getInstance();
+ }
+ this.context = context;
+ this.itemType = itemType;
+ }
+
+ public StringCollator getCollator() {
+ return collator;
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ *
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ return new AtomicSortComparer(collator, itemType, context);
+ }
+
+ /**
+ * Get the underlying StringCollator
+ * @return the underlying collator
+ */
+
+ public StringCollator getStringCollator() {
+ return collator;
+ }
+
+ /**
+ * Get the requested item type
+ * @return the item type
+ */
+
+ public int getItemType() {
+ return itemType;
+ }
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ * @param a the first object to be compared. It is intended that this should normally be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the java.util.Comparable
+ * interface.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+
+ if (a == null) {
+ if (b == null) {
+ return 0;
+ } else {
+ return -1;
+ }
+ } else if (b == null) {
+ return +1;
+ }
+
+ // System.err.println("Comparing " + a.getClass() + "(" + a + ") with " + b.getClass() + "(" + b + ") using " + collator);
+
+ if (a instanceof UntypedAtomicValue) {
+ return ((UntypedAtomicValue)a).compareTo(b, collator, context);
+ } else if (b instanceof UntypedAtomicValue) {
+ return -((UntypedAtomicValue)b).compareTo(a, collator, context);
+ } else if (a.isNaN()) {
+ return (b.isNaN() ? 0 : -1);
+ } else if (b.isNaN()) {
+ return +1;
+ } else if (a instanceof StringValue && b instanceof StringValue) {
+ if (collator instanceof CodepointCollator) {
+ return ((CodepointCollator)collator).compareCS(a.getStringValueCS(), b.getStringValueCS());
+ } else {
+ return collator.compareStrings(a.getStringValue(), b.getStringValue());
+ }
+ } else {
+ Comparable ac = (Comparable)a.getXPathComparable(true, collator, context);
+ Comparable bc = (Comparable)b.getXPathComparable(true, collator, context);
+ if (ac == null || bc == null) {
+ return compareNonComparables(a, b);
+ } else {
+ return ac.compareTo(bc);
+ }
+ }
+ }
+
+ /**
+ * Compare two values that are known to be non-comparable. In the base class this method
+ * throws a ClassCastException. In a subclass it is overridden to return
+ * {@link net.sf.saxon.om.SequenceTool#INDETERMINATE_ORDERING}
+ */
+
+ protected int compareNonComparables(AtomicValue a, AtomicValue b) {
+ throw new ClassCastException("Values are not comparable (" +
+ Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ')');
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ *
+ * @param a the first object to be compared. It is intended that this should be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the equals() method.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return true if the values are equal, false if not
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+ return compareAtomicValues(a, b) == 0;
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal,
+ * then their comparison keys are equal, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) throws NoDynamicContextException {
+ if (a instanceof NumericValue) {
+ if (((NumericValue)a).isNaN()) {
+ // Deal with NaN specially. For this function, NaN is considered equal to itself
+ return new ComparisonKey(StandardNames.XS_NUMERIC, COLLATION_KEY_NaN);
+ } else {
+ return new ComparisonKey(StandardNames.XS_NUMERIC, a);
+ }
+ } else if (a instanceof StringValue) {
+ final Platform platform = Configuration.getPlatform();
+ if (platform.canReturnCollationKeys(collator)) {
+ return new ComparisonKey(StandardNames.XS_STRING,
+ collator.getCollationKey(a.getStringValue()));
+ } else {
+ return new ComparisonKey(StandardNames.XS_STRING, a);
+ }
+ } else if (a instanceof CalendarValue) {
+ CalendarValue cv = (CalendarValue)a;
+ if (cv.hasTimezone()) {
+ return new ComparisonKey(a.getItemType().getPrimitiveType(), a);
+ } else {
+ cv = (CalendarValue)cv.copyAsSubType((AtomicType) cv.getItemType());
+ cv.setTimezoneInMinutes(context.getImplicitTimezone());
+ return new ComparisonKey(cv.getItemType().getPrimitiveType(), cv);
+ }
+ } else if (a instanceof DurationValue) {
+ // dayTimeDuration and yearMonthDuration are comparable in the special case of the zero duration
+ return new ComparisonKey(StandardNames.XS_DURATION, a);
+ } else {
+ return new ComparisonKey(a.getItemType().getPrimitiveType(), a);
+ }
+ }
+
+ public static StructuredQName COLLATION_KEY_NaN =
+ new StructuredQName("saxon", "http://saxon.sf.net/collation-key", "NaN");
+ // The logic here is to choose a value that compares equal to itself but not equal to any other
+ // number. We use StructuredQName because it has a simple equals() method.
+
+}
+
diff --git a/sf/saxon/expr/sort/CalendarValueComparer.java b/sf/saxon/expr/sort/CalendarValueComparer.java
new file mode 100644
index 0000000..89a4959
--- /dev/null
+++ b/sf/saxon/expr/sort/CalendarValueComparer.java
@@ -0,0 +1,96 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.CalendarValue;
+
+/**
+ * A comparer specifically for comparing two date, time, or dateTime values
+ */
+public class CalendarValueComparer implements AtomicComparer {
+
+ private transient XPathContext context;
+
+ public CalendarValueComparer(XPathContext context) {
+ this.context = context;
+ }
+
+ /*@Nullable*/ public StringCollator getCollator() {
+ return null;
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ *
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ * @throws net.sf.saxon.trans.NoDynamicContextException
+ * if the context is an "early evaluation" (compile-time) context
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ return new CalendarValueComparer(context);
+ }
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ *
+ * @param a the first object to be compared. It is intended that this should be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the java.util.Comparable
+ * interface.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+ if (a == null) {
+ return (b == null ? 0 : -1);
+ } else if (b == null) {
+ return +1;
+ }
+ return ((CalendarValue)a).compareTo((CalendarValue)b, context);
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ *
+ * @param a the first object to be compared. It is intended that this should be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the equals() method.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return true if the values are equal, false if not
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+ return compareAtomicValues(a, b) == 0;
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal,
+ * then their comparison keys are equal, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) throws NoDynamicContextException {
+ return ((CalendarValue)a).getComparisonKey(context);
+ }
+}
+
diff --git a/sf/saxon/expr/sort/CaseFirstCollator.java b/sf/saxon/expr/sort/CaseFirstCollator.java
new file mode 100644
index 0000000..ee58be1
--- /dev/null
+++ b/sf/saxon/expr/sort/CaseFirstCollator.java
@@ -0,0 +1,113 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.lib.StringCollator;
+
+
+/**
+ * A StringCollator that sorts lowercase before uppercase, or vice versa.
+ *
+ * <p>Case is irrelevant, unless the strings are equal ignoring
+ * case, in which case lowercase comes first.</p>
+ *
+ * @author Michael H. Kay
+ */
+
+public class CaseFirstCollator implements StringCollator, java.io.Serializable {
+
+ private StringCollator baseCollator;
+ private boolean upperFirst;
+
+ /**
+ * Create a CaseFirstCollator
+ * @param base the base collator, which determines how characters are sorted irrespective of case
+ * @param upperFirst true if uppercase precedes lowercase, false otherwise
+ */
+
+ public CaseFirstCollator(StringCollator base, boolean upperFirst) {
+ this.baseCollator = base;
+ this.upperFirst = upperFirst;
+ }
+
+ /**
+ * Compare two string objects: case is irrelevant, unless the strings are equal ignoring
+ * case, in which case lowercase comes first.
+ *
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are of the wrong type for this Comparer
+ */
+
+ public int compareStrings(String a, String b) {
+ int diff = baseCollator.compareStrings(a, b);
+ if (diff != 0) {
+ return diff;
+ }
+
+ // This is doing a character-by-character comparison, which isn't really right.
+ // There might be a sequence of letters constituting a single collation unit.
+
+ int i = 0;
+ int j = 0;
+ while (true) {
+ // Skip characters that are equal in the two strings
+ while (i < a.length() && j < b.length() && a.charAt(i) == b.charAt(j)) {
+ i++;
+ j++;
+ }
+ // Skip non-letters in the first string
+ while (i < a.length() && !Character.isLetter(a.charAt(i))) {
+ i++;
+ }
+ // Skip non-letters in the second string
+ while (j < b.length() && !Character.isLetter(b.charAt(j))) {
+ j++;
+ }
+ // If we've got to the end of either string, treat the strings as equal
+ if (i >= a.length()) {
+ return 0;
+ }
+ if (j >= b.length()) {
+ return 0;
+ }
+ // If one of the characters is upper/lower case and the other isn't, the issue is decided
+ boolean aFirst = (upperFirst ? Character.isUpperCase(a.charAt(i++)) : Character.isLowerCase(a.charAt(i++)));
+ boolean bFirst = (upperFirst ? Character.isUpperCase(b.charAt(j++)) : Character.isLowerCase(b.charAt(j++)));
+ if (aFirst && !bFirst) {
+ return -1;
+ }
+ if (bFirst && !aFirst) {
+ return +1;
+ }
+ }
+ }
+
+ /**
+ * Compare two strings for equality. This may be more efficient than using compareStrings and
+ * testing whether the result is zero, but it must give the same result
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return true if and only if the strings are considered equal,
+ */
+
+ public boolean comparesEqual(String s1, /*@NotNull*/ String s2) {
+ return compareStrings(s1, s2) == 0;
+ }
+
+ /**
+ * Get a collation key for two Strings. The essential property of collation keys
+ * is that if two values are equal under the collation, then the collation keys are
+ * compare correctly under the equals() method.
+ */
+
+ public Object getCollationKey(String s) {
+ return baseCollator.getCollationKey(s);
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/CodepointCollatingComparer.java b/sf/saxon/expr/sort/CodepointCollatingComparer.java
new file mode 100644
index 0000000..1ac3426
--- /dev/null
+++ b/sf/saxon/expr/sort/CodepointCollatingComparer.java
@@ -0,0 +1,124 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * An AtomicComparer used for comparing strings, untypedAtomic values, and URIs using the Unicode codepoint
+ * collation.
+ * A CodepointCollatingComparer is used when it is known in advance that both operands will be of these
+ * types, and when the collation is the unicode codepoint collation.
+ * This enables all conversions and promotions to be bypassed: the string values of both operands
+ * are simply extracted and passed to the collator for comparison.
+ *
+ * <p>The difference between using this class and using the underlying CodepointCollator directly is that
+ * the compare() method in this class expects two instances of AtomicValue as its operands, whereas the
+ * underlying class expects two instances of java.lang.String. This class makes use of the extra information
+ * held in the wrapping StringValue object, specifically, the knowledge of whether the string contains
+ * surrogate pairs.</p>
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class CodepointCollatingComparer implements AtomicComparer {
+
+ private static CodepointCollator collator = CodepointCollator.getInstance();
+
+ private static CodepointCollatingComparer THE_INSTANCE = new CodepointCollatingComparer();
+
+ /**
+ * Get the singular instance of this class
+ * @return the singleton instance
+ */
+
+ public static CodepointCollatingComparer getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private CodepointCollatingComparer() {}
+
+ public StringCollator getCollator() {
+ return collator;
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ *
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ return this;
+ }
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ * @param a the first object to be compared. This must be either be an instance
+ * of AtomicValue, or null to represent an empty sequence. Empty collates before non-empty.
+ * @param b the second object to be compared. This must be either be an instance
+ * of AtomicValue, or null to represent an empty sequence.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public int compareAtomicValues(/*@Nullable*/ AtomicValue a, AtomicValue b) {
+ if (a == null) {
+ return (b == null ? 0 : -1);
+ } else if (b == null) {
+ return +1;
+ }
+ StringValue as = (StringValue)a;
+ StringValue bs = (StringValue)b;
+ //if (as.containsSurrogatePairs() || bs.containsSurrogatePairs()) {
+ return collator.compareCS(as.getStringValueCS(), bs.getStringValueCS());
+ //} else {
+ // optimize to use UTF-16 binary comparison
+ // return as.getStringValue().compareTo(bs.getStringValue());
+ //}
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality. The values must be instances of xs:string or a type
+ * derived from xs:string. The method will also handle xs:untypedAtomic and xs:anyURI values.
+ * @param a the first object to be compared.
+ * @param b the second object to be compared.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if either value is not xs:string or a subtype
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) {
+ StringValue as = (StringValue)a;
+ StringValue bs = (StringValue)b;
+ return as.codepointEquals(bs);
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal
+ * under the XPath eq operator, then their comparison keys are equal under the Java equals()
+ * function, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) {
+ StringValue as = (StringValue)a;
+ return new ComparisonKey(StandardNames.XS_STRING, as.getStringValue());
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/sort/CodepointCollator.java b/sf/saxon/expr/sort/CodepointCollator.java
new file mode 100644
index 0000000..b6c737c
--- /dev/null
+++ b/sf/saxon/expr/sort/CodepointCollator.java
@@ -0,0 +1,179 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.lib.SubstringMatcher;
+
+import java.io.Serializable;
+
+
+/**
+ * A collating sequence that uses Unicode codepoint ordering
+ */
+
+public class CodepointCollator implements StringCollator, SubstringMatcher, Serializable {
+
+ private static CodepointCollator theInstance = new CodepointCollator();
+
+ public static CodepointCollator getInstance() {
+ return theInstance;
+ }
+
+ /**
+ * Compare two string objects.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are of the wrong type for this Comparer
+ */
+
+ public int compareStrings(String a, String b) {
+ //return ((String)a).compareTo((String)b);
+ // Note that Java does UTF-16 code unit comparison, which is not the same as Unicode codepoint comparison
+ // except in the "equals" case. So we have to do a character-by-character comparison
+ return compareCS((String)a, (String)b);
+ }
+
+ /**
+ * Compare two CharSequence objects. This is hand-coded to avoid converting the objects into
+ * Strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are of the wrong type for this Comparer
+ */
+
+ public int compareCS(CharSequence a, CharSequence b) {
+ int alen = a.length();
+ int blen = b.length();
+ int i = 0;
+ int j = 0;
+ while (true) {
+ if (i == alen) {
+ if (j == blen) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ if (j == blen) {
+ return +1;
+ }
+ // Following code is needed when comparing a BMP character against a surrogate pair
+ // Note: we could do this comparison without fully computing the codepoint, but it's a very rare case
+ int nexta = (int)a.charAt(i++);
+ if (nexta >= 55296 && nexta <= 56319) {
+ nexta = ((nexta - 55296) * 1024) + ((int)a.charAt(i++) - 56320) + 65536;
+ }
+ int nextb = (int)b.charAt(j++);
+ if (nextb >= 55296 && nextb <= 56319) {
+ nextb = ((nextb - 55296) * 1024) + ((int)b.charAt(j++) - 56320) + 65536;
+ }
+ int c = nexta - nextb;
+ if (c != 0) {
+ return c;
+ }
+ }
+ }
+
+ /**
+ * Test whether one string is equal to another, according to the rules
+ * of the XPath compare() function. The result is true if and only if the
+ * compare() method returns zero: but the implementation may be more efficient
+ * than calling compare and testing the result for zero
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return true iff s1 equals s2
+ */
+
+ public boolean comparesEqual(String s1, String s2) {
+ return s1.equals(s2);
+ }
+
+ /**
+ * Test whether one string contains another, according to the rules
+ * of the XPath contains() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return true iff s1 contains s2
+ */
+
+ public boolean contains(String s1, String s2) {
+ return s1.indexOf(s2) >= 0;
+ }
+
+ /**
+ * Test whether one string ends with another, according to the rules
+ * of the XPath ends-with() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return true iff s1 ends with s2
+ */
+
+ public boolean endsWith(String s1, String s2) {
+ return s1.endsWith(s2);
+ }
+
+ /**
+ * Test whether one string starts with another, according to the rules
+ * of the XPath starts-with() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return true iff s1 starts with s2
+ */
+
+ public boolean startsWith(String s1, String s2) {
+ return s1.startsWith(s2);
+ }
+
+ /**
+ * Return the part of a string after a given substring, according to the rules
+ * of the XPath substring-after() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return the part of s1 that follows the first occurrence of s2
+ */
+
+ public String substringAfter(String s1, String s2) {
+ int i = s1.indexOf(s2);
+ if (i<0) {
+ return "";
+ }
+ return s1.substring(i+s2.length());
+ }
+
+ /**
+ * Return the part of a string before a given substring, according to the rules
+ * of the XPath substring-before() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return the part of s1 that precedes the first occurrence of s2
+ */
+
+ public String substringBefore(/*@NotNull*/ String s1, String s2) {
+ int j = s1.indexOf(s2);
+ if (j<0) {
+ return "";
+ }
+ return s1.substring(0, j);
+ }
+
+ /**
+ * Get a collation key for two Strings. The essential property of collation keys
+ * is that if two values are equal under the collation, then the collation keys are
+ * compare correctly under the equals() method.
+ */
+
+ public Object getCollationKey(String s) {
+ return s;
+ }
+}
+
diff --git a/sf/saxon/expr/sort/CollatingAtomicComparer.java b/sf/saxon/expr/sort/CollatingAtomicComparer.java
new file mode 100644
index 0000000..b52052f
--- /dev/null
+++ b/sf/saxon/expr/sort/CollatingAtomicComparer.java
@@ -0,0 +1,126 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.Platform;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * An AtomicComparer used for comparing strings, untypedAtomic values, and URIs using a collation.
+ * A CollatingAtomicComparer is used when it is known in advance that both operands will be of these
+ * types. This enables all conversions and promotions to be bypassed: the string values of both operands
+ * are simply extracted and passed to the collator for comparison.
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class CollatingAtomicComparer implements AtomicComparer {
+
+ private StringCollator collator;
+ private boolean canReturnCollationKeys;
+
+ /**
+ * Create an GenericAtomicComparer
+ * @param collator the collation to be used. If the method is called at compile time, this should
+ * be a SimpleCollation so that it can be cloned at run-time.
+ * @param platform used to obtain collation keys.
+ */
+
+ public CollatingAtomicComparer(/*@Nullable*/ StringCollator collator, Platform platform) {
+
+ if (collator == null) {
+ this.collator = CodepointCollator.getInstance();
+ } else {
+ this.collator = collator;
+ }
+ canReturnCollationKeys = platform.canReturnCollationKeys(this.collator);
+ }
+
+
+ public StringCollator getCollator() {
+ return collator;
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ *
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ return this;
+ }
+
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ * @param a the first object to be compared. It is intended that this should be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the java.util.Comparable
+ * interface.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) {
+ if (a == null) {
+ if (b == null) {
+ return 0;
+ } else {
+ return -1;
+ }
+ } else if (b == null) {
+ return +1;
+ }
+
+ return collator.compareStrings(a.getStringValue(), b.getStringValue());
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ * @param a the first object to be compared. It is intended that this should be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the equals() method.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) {
+ return compareAtomicValues(a, b) == 0;
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal,
+ * then their comparison keys are equal, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) {
+ if (canReturnCollationKeys) {
+ return new ComparisonKey(StandardNames.XS_STRING, collator.getCollationKey(a.getStringValue()));
+ } else {
+ return new ComparisonKey(StandardNames.XS_STRING, a.getStringValue());
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/sort/ComparableAtomicValueComparer.java b/sf/saxon/expr/sort/ComparableAtomicValueComparer.java
new file mode 100644
index 0000000..9d96f13
--- /dev/null
+++ b/sf/saxon/expr/sort/ComparableAtomicValueComparer.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.value.AtomicValue;
+
+
+/**
+ * A comparer for comparing two "ordinary" atomic values, where the values implement the Comparable
+ * interface and the equals() method with the appropriate XPath semantics. This rules out use of
+ * collations, conversion of untyped atomic values, and context dependencies such as implicit timezone.
+ */
+public class ComparableAtomicValueComparer implements AtomicComparer {
+
+ private static ComparableAtomicValueComparer THE_INSTANCE = new ComparableAtomicValueComparer();
+
+ /**
+ * Get the singleton instance of this class
+ * @return the singleton instance of this class
+ */
+
+ public static ComparableAtomicValueComparer getInstance() {
+ return THE_INSTANCE;
+ }
+
+ protected ComparableAtomicValueComparer() {}
+
+ /*@Nullable*/ public StringCollator getCollator() {
+ return null;
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ *
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ return this;
+ }
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ *
+ * @param a the first object to be compared. This must be an AtomicValue and it must implement
+ * Comparable with context-free XPath comparison semantics
+ * @param b the second object to be compared. This must be an AtomicValue and it must implement
+ * Comparable with context-free XPath comparison semantics
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) {
+ if (a == null) {
+ return (b == null ? 0 : -1);
+ } else if (b == null) {
+ return +1;
+ }
+ return ((Comparable)a).compareTo(b);
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ * @param a the first object to be compared. This must be an AtomicValue and it must implement
+ * equals() with context-free XPath comparison semantics
+ * @param b the second object to be compared. This must be an AtomicValue and it must implement
+ * equals() with context-free XPath comparison semantics
+ * @return true if the values are equal, false if not
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) {
+ return a.equals(b);
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal,
+ * then their comparison keys are equal, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) {
+ return new ComparisonKey(a.getPrimitiveType().getFingerprint(), a);
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/sort/ComparisonKey.java b/sf/saxon/expr/sort/ComparisonKey.java
new file mode 100644
index 0000000..8e18d92
--- /dev/null
+++ b/sf/saxon/expr/sort/ComparisonKey.java
@@ -0,0 +1,55 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+/**
+ * An object used as a comparison key. Two XPath atomic values are equal under the "eq" operator
+ * if and only if their comparison keys are equal under the Java equals() method.
+ */
+
+public class ComparisonKey {
+ int category;
+ Object value;
+
+ /**
+ * Create a comparison key for a value in a particular category. The "category" here represents a
+ * set of primitive types that allow mutual comparison (so all numeric values are in the same category).
+ * @param category the category
+ * @param value the value within the category
+ */
+
+ public ComparisonKey(int category, Object value) {
+ this.category = category;
+ this.value = value;
+ }
+
+ /**
+ * Test if two comparison keys are equal
+ * @param other the other comparison key
+ * @return true if they are equal
+ * @throws ClassCastException if the other object is not a ComparisonKey
+ */
+ public boolean equals(Object other) {
+ if (other instanceof ComparisonKey) {
+ ComparisonKey otherKey = (ComparisonKey)other;
+ return category == otherKey.category &&
+ value.equals(otherKey.value);
+ } else {
+ throw new ClassCastException("Cannot compare a ComparisonKey to an object of a different class");
+ }
+ }
+
+ /**
+ * Get a hashcode for a comparison key. If two comparison keys are equal, they must have the same hash code.
+ * @return the hash code.
+ */
+ public int hashCode() {
+ return value.hashCode() ^ category;
+ }
+
+}
diff --git a/sf/saxon/expr/sort/ConditionalSorter.java b/sf/saxon/expr/sort/ConditionalSorter.java
new file mode 100644
index 0000000..9042167
--- /dev/null
+++ b/sf/saxon/expr/sort/ConditionalSorter.java
@@ -0,0 +1,305 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import com.saxonica.bytecode.ConditionalSorterCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.Iterator;
+
+/**
+ * An expression that sorts an underlying sequence into document order if some condition is true, or that
+ * returns the sequence "as is" (knowing that it doesn't need sorting) if the condition is false.
+ */
+public class ConditionalSorter extends Expression {
+
+ /*@NotNull*/
+ private Expression condition;
+ /*@NotNull*/
+ private DocumentSorter documentSorter;
+
+ /**
+ * Create a conditional document sorter
+ * @param condition the conditional expression
+ * @param sorter the sorting expression
+ */
+
+ public ConditionalSorter(/*@NotNull*/ Expression condition, /*@NotNull*/ DocumentSorter sorter) {
+ this.condition = condition;
+ documentSorter = sorter;
+ }
+
+ /**
+ * Get the condition under which the nodes need to be sorted
+ * @return the condition (an expression)
+ */
+
+ /*@NotNull*/
+ public Expression getCondition() {
+ return condition;
+ }
+
+ /**
+ * Get the document sorter, which sorts the nodes if the condition is true
+ * @return the document sorter
+ */
+
+ /*@NotNull*/
+ public DocumentSorter getDocumentSorter() {
+ return documentSorter;
+ }
+
+ /**
+ * Perform type checking of an expression and its subexpressions. This is the second phase of
+ * static optimization.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ condition = visitor.typeCheck(condition, contextItemType);
+ Expression sorter = visitor.typeCheck(documentSorter, contextItemType);
+ if (sorter instanceof DocumentSorter) {
+ documentSorter = (DocumentSorter)sorter;
+ return this;
+ } else {
+ return sorter;
+ }
+ }
+
+ /**
+ * Determine the static cardinality of the expression. This establishes how many items
+ * there will be in the result of the expression, at compile time (i.e., without
+ * actually evaluating the result.
+ *
+ * @return one of the values Cardinality.ONE_OR_MORE,
+ * Cardinality.ZERO_OR_MORE, Cardinality.EXACTLY_ONE,
+ * Cardinality.ZERO_OR_ONE, Cardinality.EMPTY. This default
+ * implementation returns ZERO_OR_MORE (which effectively gives no
+ * information).
+ */
+
+ public int getCardinality() {
+ return documentSorter.getCardinality();
+ }
+
+
+ /**
+ * Compute the special properties of this expression. These properties are denoted by a bit-significant
+ * integer, possible values are in class {@link net.sf.saxon.expr.StaticProperty}. The "special" properties are properties
+ * other than cardinality and dependencies, and most of them relate to properties of node sequences, for
+ * example whether the nodes are in document order.
+ *
+ * @return the special properties, as a bit-significant integer
+ */
+
+ protected int computeSpecialProperties() {
+ return condition.getSpecialProperties()
+ | StaticProperty.ORDERED_NODESET
+ & ~StaticProperty.REVERSE_DOCUMENT_ORDER;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided directly. The other methods will always be available
+ * indirectly, using an implementation that relies on one of the other methods.
+ *
+ * @return the implementation method, for example {@link #ITERATE_METHOD} or {@link #EVALUATE_METHOD} or
+ * {@link #PROCESS_METHOD}
+ */
+
+ public int getImplementationMethod() {
+ return ITERATE_METHOD;
+ }
+
+
+ /**
+ * Get the immediate sub-expressions of this expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(condition, documentSorter);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (condition == original) {
+ condition = replacement;
+ found = true;
+ }
+ if (documentSorter == original) {
+ documentSorter = (DocumentSorter)replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ @Override
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression expr1 = visitor.optimize(condition, contextItemType);
+ if(condition != expr1){
+ condition = expr1;
+ this.resetLocalStaticProperties();
+ }
+ if(Literal.isConstantBoolean(condition, true)){
+ return documentSorter;
+ }else if(Literal.isConstantBoolean(condition, false)){
+ return documentSorter.getBaseExpression();
+ }
+ return this;
+ }
+
+ /**
+ * Compute the static cardinality of this expression
+ *
+ * @return the computed cardinality, as one of the values {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_ONE},
+ * {@link net.sf.saxon.expr.StaticProperty#EXACTLY_ONE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ONE_OR_MORE},
+ * {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_MORE}
+ */
+
+ protected int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new ConditionalSorter(condition.copy(), (DocumentSorter)documentSorter.copy());
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ *
+ * @param out the expression presenter used to display the structure
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("conditionalSort");
+ condition.explain(out);
+ documentSorter.explain(out);
+ out.endElement();
+ }
+
+ /**
+ * Determine the data type of the expression, if possible. All expression return
+ * sequences, in general; this method determines the type of the items within the
+ * sequence, assuming that (a) this is known in advance, and (b) it is the same for
+ * all items in the sequence.
+ * <p/>
+ * <p>This method should always return a result, though it may be the best approximation
+ * that is available at the time.</p>
+ *
+ * @param th the type hierarchy cache
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER,
+ * Type.NODE, or Type.ITEM (meaning not known at compile time)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return documentSorter.getItemType(th);
+ }
+
+
+ /**
+ * Offer promotion for this subexpression. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @return if the offer is not accepted, return this expression unchanged.
+ * Otherwise return the result of rewriting the expression to promote
+ * this subexpression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ condition = doPromotion(condition, offer);
+ Expression e = doPromotion(documentSorter, offer);
+ if (e instanceof DocumentSorter) {
+ return this;
+ } else {
+ return e;
+ }
+ }
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ boolean b = condition.effectiveBooleanValue(context);
+ if (b) {
+ return documentSorter.iterate(context);
+ } else {
+ return documentSorter.getBaseExpression().iterate(context);
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the ConditionalSorter expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ConditionalSorterCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/sort/DecimalSortComparer.java b/sf/saxon/expr/sort/DecimalSortComparer.java
new file mode 100644
index 0000000..d19532d
--- /dev/null
+++ b/sf/saxon/expr/sort/DecimalSortComparer.java
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * An AtomicComparer used for sorting values that are known to be instances of xs:decimal (including xs:integer),
+ * It also supports a separate method for getting a collation key to test equality of items
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class DecimalSortComparer extends ComparableAtomicValueComparer {
+
+ private static DecimalSortComparer THE_INSTANCE = new DecimalSortComparer();
+
+ public static DecimalSortComparer getDecimalSortComparerInstance() {
+ return THE_INSTANCE;
+ }
+
+ private DecimalSortComparer() {}
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal as defined
+ * by the XPath eq operator, then their comparison keys are equal as defined by the Java equals() method,
+ * and vice versa. There is no requirement that the comparison keys should reflect the ordering of the
+ * underlying objects.
+ */
+
+ /*@NotNull*/ public ComparisonKey getComparisonKey(AtomicValue a) {
+ return new ComparisonKey(StandardNames.XS_NUMERIC, a);
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/DescendingComparer.java b/sf/saxon/expr/sort/DescendingComparer.java
new file mode 100644
index 0000000..d28e372
--- /dev/null
+++ b/sf/saxon/expr/sort/DescendingComparer.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * A Comparer used for comparing descending keys. This simply returns the inverse of the result
+ * delivered by the base comparer.
+ */
+
+public class DescendingComparer implements AtomicComparer, java.io.Serializable {
+
+ private AtomicComparer baseComparer;
+
+ public DescendingComparer(AtomicComparer base) {
+ baseComparer = base;
+ }
+
+ /**
+ * Get the underlying (ascending) comparer
+ * @return the underlying (ascending) comparer
+ */
+
+ public AtomicComparer getBaseComparer() {
+ return baseComparer;
+ }
+
+ public StringCollator getCollator() {
+ return baseComparer.getCollator();
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ * @throws NoDynamicContextException if the context is an "early evaluation" (compile-time) context
+ */
+
+ /*@NotNull*/ public AtomicComparer provideContext(XPathContext context) {
+ AtomicComparer newBase = baseComparer.provideContext(context);
+ if (newBase != baseComparer) {
+ return new DescendingComparer(newBase);
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Compare two objects.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are of the wrong type for this Comparer
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+ return 0 - baseComparer.compareAtomicValues(a, b);
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ *
+ * @param a the first object to be compared. It is intended that this should be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the equals() method.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return true if the values are equal, false if not
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+ return baseComparer.comparesEqual(a, b);
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal
+ * according to the XPath eq operator, then their comparison keys are equal according to the Java
+ * equals() method, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) throws NoDynamicContextException {
+ return baseComparer.getComparisonKey(a);
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/DocumentOrderIterator.java b/sf/saxon/expr/sort/DocumentOrderIterator.java
new file mode 100644
index 0000000..3b18118
--- /dev/null
+++ b/sf/saxon/expr/sort/DocumentOrderIterator.java
@@ -0,0 +1,127 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceExtent;
+
+/**
+* DocumentOrderIterator takes as input an iteration of nodes in any order, and
+* returns as output an iteration of the same nodes in document order, eliminating
+* any duplicates.
+*/
+
+public final class DocumentOrderIterator implements SequenceIterator, Sortable {
+
+ private SequenceIterator iterator;
+ private SequenceExtent sequence;
+ private ItemOrderComparer comparer;
+ /*@Nullable*/ private NodeInfo current = null;
+ private int position = 0;
+
+ /**
+ * Iterate over a sequence in document order.
+ */
+
+ public DocumentOrderIterator(SequenceIterator base, ItemOrderComparer comparer) throws XPathException {
+
+ this.comparer = comparer;
+
+ sequence = new SequenceExtent(base);
+ //System.err.println("sort into document order: sequence length = " + sequence.getLength());
+ if (sequence.getLength()>1) {
+ //QuickSort.sort(this, 0, sequence.getLength()-1);
+ GenericSorter.quickSort(0, sequence.getLength(), this);
+ //GenericSorter.mergeSort(0, sequence.getLength(), this);
+ }
+ iterator = sequence.iterate();
+ }
+
+ /**
+ * Private constructor used only by getAnother()
+ */
+
+ private DocumentOrderIterator() {}
+
+ /**
+ * Compare two nodes in document sequence
+ * (needed to implement the Sortable interface)
+ */
+
+ public int compare(int a, int b) {
+ //System.err.println("compare " + a + " with " + b);
+ return comparer.compare((NodeInfo)sequence.itemAt(a),
+ (NodeInfo)sequence.itemAt(b));
+ }
+
+ /**
+ * Swap two nodes (needed to implement the Sortable interface)
+ */
+
+ public void swap(int a, int b) {
+ sequence.swap(a, b);
+ }
+
+ // Implement the SequenceIterator as a wrapper around the underlying iterator
+ // over the sequenceExtent, but looking ahead to remove duplicates.
+
+ public Item next() throws XPathException {
+ while (true) {
+ NodeInfo next = (NodeInfo)iterator.next();
+ if (next == null) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ if (current != null && next.isSameNodeInfo(current)) {
+ continue;
+ } else {
+ position++;
+ current = next;
+ return current;
+ }
+ }
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ DocumentOrderIterator another = new DocumentOrderIterator();
+ another.iterator = iterator.getAnother(); // don't need to sort it again
+ return another;
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/DocumentSorter.java b/sf/saxon/expr/sort/DocumentSorter.java
new file mode 100644
index 0000000..f62554a
--- /dev/null
+++ b/sf/saxon/expr/sort/DocumentSorter.java
@@ -0,0 +1,180 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import com.saxonica.bytecode.DocumentSorterCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.List;
+
+
+/**
+ * A DocumentSorter is an expression that sorts a sequence of nodes into
+ * document order.
+ */
+public class DocumentSorter extends UnaryExpression {
+
+ private ItemOrderComparer comparer;
+
+ public DocumentSorter(Expression base) {
+ super(base);
+ int props = base.getSpecialProperties();
+ if (((props & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) ||
+ (props & StaticProperty.SINGLE_DOCUMENT_NODESET) != 0) {
+ comparer = LocalOrderComparer.getInstance();
+ } else {
+ comparer = GlobalOrderComparer.getInstance();
+ }
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "documentSort";
+ }
+
+ public ItemOrderComparer getComparer() {
+ return comparer;
+ }
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ operand = visitor.simplify(operand);
+ if ((operand.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) {
+ // this can happen as a result of further simplification
+ return operand;
+ }
+ return this;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ operand = visitor.optimize(operand, contextItemType);
+ if ((operand.getSpecialProperties() & StaticProperty.ORDERED_NODESET) != 0) {
+ // this can happen as a result of further simplification
+ return operand;
+ }
+ if (operand instanceof SlashExpression) {
+ return visitor.getConfiguration().obtainOptimizer().makeConditionalDocumentSorter(
+ this, (SlashExpression)operand);
+ }
+ return this;
+ }
+
+
+ public int computeSpecialProperties() {
+ return operand.getSpecialProperties() | StaticProperty.ORDERED_NODESET;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ return new DocumentSorter(getBaseExpression().copy());
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ operand = doPromotion(operand, offer);
+ return this;
+ }
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ return getBaseExpression().toPattern(config, is30);
+ }
+
+//#ifdefined STREAM
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ return getBaseExpression().toStreamingPattern(config, reasonForFailure);
+ }
+
+//#endif
+
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ //System.err.println("** SORTING **");
+ return new DocumentOrderIterator(operand.iterate(context), comparer);
+ }
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ return operand.effectiveBooleanValue(context);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the DocumentSorter expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new DocumentSorterCompiler();
+ }
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("sortAndDeduplicate");
+ out.emitAttribute("intraDocument", comparer instanceof LocalOrderComparer ? "true" : "false");
+ operand.explain(out);
+ out.endElement();
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/DoubleSortComparer.java b/sf/saxon/expr/sort/DoubleSortComparer.java
new file mode 100644
index 0000000..bbbf754
--- /dev/null
+++ b/sf/saxon/expr/sort/DoubleSortComparer.java
@@ -0,0 +1,118 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.NumericValue;
+
+/**
+ * An AtomicComparer used for sorting values that are known to be numeric.
+ * It also supports a separate method for getting a collation key to test equality of items.
+ * This comparator treats NaN values as equal to each other, and less than any other value.
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class DoubleSortComparer implements AtomicComparer {
+
+ private static DoubleSortComparer THE_INSTANCE = new DoubleSortComparer();
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular instance
+ */
+
+ public static DoubleSortComparer getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private DoubleSortComparer() {
+
+ }
+
+ /*@Nullable*/ public StringCollator getCollator() {
+ return null;
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ *
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ return this;
+ }
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type.
+ * @param a the first object to be compared. It is intended that this should normally be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the java.util.Comparable
+ * interface.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) {
+ if (a == null) {
+ if (b == null) {
+ return 0;
+ } else {
+ return -1;
+ }
+ } else if (b == null) {
+ return +1;
+ }
+
+ NumericValue an = (NumericValue)a;
+ NumericValue bn = (NumericValue)b;
+
+ if (an.isNaN()) {
+ return (bn.isNaN() ? 0 : -1);
+ } else if (bn.isNaN()) {
+ return +1;
+ }
+
+ return an.compareTo(bn);
+ }
+
+ /**
+ * Test whether two values compare equal. Note that for this comparer, NaN is considered equal to itself
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) {
+ return compareAtomicValues(a, b) == 0;
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal as defined
+ * by the XPath eq operator, then their comparison keys are equal as defined by the Java equals() method,
+ * and vice versa. There is no requirement that the comparison keys should reflect the ordering of the
+ * underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) {
+ if (((NumericValue)a).isNaN()) {
+ // Deal with NaN specially. For sorting and similar operations, NaN is considered equal to itself
+ return new ComparisonKey(StandardNames.XS_NUMERIC, AtomicSortComparer.COLLATION_KEY_NaN);
+ } else {
+ return new ComparisonKey(StandardNames.XS_NUMERIC, a);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/EmptyGreatestComparer.java b/sf/saxon/expr/sort/EmptyGreatestComparer.java
new file mode 100644
index 0000000..c8e74cc
--- /dev/null
+++ b/sf/saxon/expr/sort/EmptyGreatestComparer.java
@@ -0,0 +1,126 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * A Comparer that modifies a base comparer by sorting empty key values and NaN values last (greatest),
+ * as opposed to the default which sorts them first.
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class EmptyGreatestComparer implements AtomicComparer, java.io.Serializable {
+
+ private AtomicComparer baseComparer;
+
+ /**
+ * Create an EmptyGreatestComparer
+ * @param baseComparer the comparer used to compare non-empty values (which typically sorts empty
+ * as least)
+ */
+
+ public EmptyGreatestComparer(AtomicComparer baseComparer) {
+ this.baseComparer = baseComparer;
+ }
+
+ /**
+ * Get the underlying comparer (which compares empty least)
+ * @return the base comparer
+ */
+
+ public AtomicComparer getBaseComparer() {
+ return baseComparer;
+ }
+
+ public StringCollator getCollator() {
+ return baseComparer.getCollator();
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ AtomicComparer newBase = baseComparer.provideContext(context);
+ if (newBase != baseComparer) {
+ return new EmptyGreatestComparer(newBase);
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ * @param a the first object to be compared. It is intended that this should normally be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the java.util.Comparable
+ * interface.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public int compareAtomicValues(/*@Nullable*/ AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+ if (a == null) {
+ if (b == null) {
+ return 0;
+ } else {
+ return +1;
+ }
+ } else if (b == null) {
+ return -1;
+ }
+
+ if (a.isNaN()) {
+ return (b.isNaN() ? 0 : +1);
+ } else if (b.isNaN()) {
+ return -1;
+ }
+
+ return baseComparer.compareAtomicValues(a, b);
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ *
+ * @param a the first object to be compared.
+ * @param b the second object to be compared.
+ * @return true if the values are equal, false if not
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+ return (a==null && b==null) || baseComparer.comparesEqual(a, b);
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal
+ * according to the XPath eq operator, then their comparison keys are equal according to the Java
+ * equals() method, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) throws NoDynamicContextException {
+ return (a==null ? new ComparisonKey(Type.EMPTY, "()") : baseComparer.getComparisonKey(a));
+ }
+}
+
diff --git a/sf/saxon/expr/sort/EmptyIntIterator.java b/sf/saxon/expr/sort/EmptyIntIterator.java
new file mode 100644
index 0000000..2ae0c29
--- /dev/null
+++ b/sf/saxon/expr/sort/EmptyIntIterator.java
@@ -0,0 +1,52 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.z.IntIterator;
+
+/**
+ * An iterator over a zero-length sequence of integers
+ */
+public class EmptyIntIterator implements IntIterator {
+
+ private static EmptyIntIterator THE_INSTANCE = new EmptyIntIterator();
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular instance
+ */
+
+ /*@NotNull*/ public static EmptyIntIterator getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private EmptyIntIterator() {}
+
+
+ /**
+ * Test whether there are any more integers in the sequence
+ *
+ * @return true if there are more integers to come
+ */
+
+ public boolean hasNext() {
+ return false;
+ }
+
+ /**
+ * Return the next integer in the sequence. The result is undefined unless hasNext() has been called
+ * and has returned true.
+ *
+ * @return the next integer in the sequence
+ */
+
+ public int next() {
+ return 0;
+ }
+}
+
diff --git a/sf/saxon/expr/sort/EqualityComparer.java b/sf/saxon/expr/sort/EqualityComparer.java
new file mode 100644
index 0000000..5039ad2
--- /dev/null
+++ b/sf/saxon/expr/sort/EqualityComparer.java
@@ -0,0 +1,93 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.value.AtomicValue;
+
+
+/**
+ * A comparer for comparing two atomic values where (a) equals is defined, and is implemented
+ * using the Java equals() method, and (b) ordering is not defined, and results in a dynamic error.
+ */
+public class EqualityComparer implements AtomicComparer {
+
+ public static EqualityComparer THE_INSTANCE = new EqualityComparer();
+
+ /**
+ * Get the singleton instance of this class
+ * @return the singleton instance of this class
+ */
+
+ public static EqualityComparer getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private EqualityComparer() {}
+
+ /*@Nullable*/ public StringCollator getCollator() {
+ return null;
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ return this;
+ }
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ *
+ * @param a the first object to be compared. It is intended that this should be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the java.util.Comparable
+ * interface.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) {
+ throw new ClassCastException("Values are not comparable");
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ * @param a the first object to be compared. This must be an AtomicValue and it must implement
+ * equals() with context-free XPath comparison semantics
+ * @param b the second object to be compared. This must be an AtomicValue and it must implement
+ * equals() with context-free XPath comparison semantics
+ * @return true if the values are equal, false if not
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) {
+ return a.equals(b);
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal,
+ * then their comparison keys are equal, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) {
+ return new ComparisonKey(a.getPrimitiveType().getFingerprint(), a);
+ }
+}
+
diff --git a/sf/saxon/expr/sort/GenericAtomicComparer.java b/sf/saxon/expr/sort/GenericAtomicComparer.java
new file mode 100644
index 0000000..b920263
--- /dev/null
+++ b/sf/saxon/expr/sort/GenericAtomicComparer.java
@@ -0,0 +1,232 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Platform;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.CalendarValue;
+import net.sf.saxon.value.StringValue;
+
+import java.io.Serializable;
+
+/**
+ * An AtomicComparer used for comparing atomic values of arbitrary item types. It encapsulates
+ * a Collator that is used when the values to be compared are strings. It also supports
+ * a separate method for testing equality of items, which can be used for data types that
+ * are not ordered.
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class GenericAtomicComparer implements AtomicComparer, Serializable {
+
+ private StringCollator collator;
+ private transient XPathContext context;
+
+ /**
+ * Create an GenericAtomicComparer
+ * @param collator the collation to be used
+ * @param conversionContext a context, used when converting untyped atomic values to the target type.
+ */
+
+ public GenericAtomicComparer(/*@Nullable*/ StringCollator collator, XPathContext conversionContext) {
+ this.collator = collator;
+ if (collator == null) {
+ this.collator = CodepointCollator.getInstance();
+ }
+ context = conversionContext;
+ }
+
+ /**
+ * Factory method to make a GenericAtomicComparer for values of known types
+ * @param type0 primitive type of the first operand
+ * @param type1 primitive type of the second operand
+ * @param collator the collation to be used, if any. This is supplied as a SimpleCollation object
+ * which encapsulated both the collation URI and the collation itself.
+ * @param context the dynamic context
+ * @return a GenericAtomicComparer for values of known types
+ */
+
+ public static AtomicComparer makeAtomicComparer(
+ BuiltInAtomicType type0, BuiltInAtomicType type1, StringCollator collator, XPathContext context) {
+ int fp0 = type0.getFingerprint();
+ int fp1 = type1.getFingerprint();
+ if (fp0 == fp1) {
+ switch (fp0) {
+ case StandardNames.XS_DATE_TIME:
+ case StandardNames.XS_DATE:
+ case StandardNames.XS_TIME:
+ case StandardNames.XS_G_DAY:
+ case StandardNames.XS_G_MONTH:
+ case StandardNames.XS_G_YEAR:
+ case StandardNames.XS_G_MONTH_DAY:
+ case StandardNames.XS_G_YEAR_MONTH:
+ return new CalendarValueComparer(context);
+
+ case StandardNames.XS_BOOLEAN:
+ case StandardNames.XS_DAY_TIME_DURATION:
+ case StandardNames.XS_YEAR_MONTH_DURATION:
+ return ComparableAtomicValueComparer.getInstance();
+
+ case StandardNames.XS_BASE64_BINARY:
+ case StandardNames.XS_HEX_BINARY:
+ case StandardNames.XS_QNAME:
+ case StandardNames.XS_NOTATION:
+ return EqualityComparer.getInstance();
+
+ }
+ }
+
+ if (type0.isPrimitiveNumeric() && type1.isPrimitiveNumeric()) {
+ return ComparableAtomicValueComparer.getInstance();
+ }
+
+ if ((fp0 == StandardNames.XS_STRING ||
+ fp0 == StandardNames.XS_UNTYPED_ATOMIC ||
+ fp0 == StandardNames.XS_ANY_URI) &&
+ (fp1 == StandardNames.XS_STRING ||
+ fp1 == StandardNames.XS_UNTYPED_ATOMIC ||
+ fp1 == StandardNames.XS_ANY_URI)) {
+ if (collator instanceof CodepointCollator) {
+ return CodepointCollatingComparer.getInstance();
+ } else {
+ return new CollatingAtomicComparer(collator, Configuration.getPlatform());
+ }
+ }
+ return new GenericAtomicComparer(collator, context);
+ }
+
+ public StringCollator getCollator() {
+ return collator;
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ *
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ return new GenericAtomicComparer(collator, context);
+ }
+
+ /**
+ * Get the underlying string collator
+ * @return the string collator
+ */
+
+ public StringCollator getStringCollator() {
+ return collator;
+ }
+
+ /**
+ * Compare two AtomicValue objects according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ * @param a the first object to be compared. It is intended that this should be an instance
+ * of AtomicValue, though this restriction is not enforced. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the java.util.Comparable
+ * interface.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a < b, 0 if a = b, >0 if a > b
+ * @throws ClassCastException if the objects are not comparable
+ * @throws NoDynamicContextException if this comparer required access to dynamic context information,
+ * notably the implicit timezone, and this information is not available. In general this happens if a
+ * context-dependent comparison is attempted at compile-time, and it signals the compiler to generate
+ * code that tries again at run-time.
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+
+ // System.err.println("Comparing " + a.getClass() + "(" + a + ") with " + b.getClass() + "(" + b + ") using " + collator);
+
+ if (a == null) {
+ return (b == null ? 0 : -1);
+ } else if (b == null) {
+ return +1;
+ }
+
+ if (a instanceof StringValue && b instanceof StringValue) {
+ if (collator instanceof CodepointCollator) {
+ return ((CodepointCollator)collator).compareCS(a.getStringValueCS(), b.getStringValueCS());
+ } else {
+ return collator.compareStrings(a.getStringValue(), b.getStringValue());
+ }
+ } else {
+ Comparable ac = (Comparable)a.getXPathComparable(true, collator, context);
+ Comparable bc = (Comparable)b.getXPathComparable(true, collator, context);
+ if (ac == null || bc == null) {
+ throw new ClassCastException("Objects are not comparable (" +
+ Type.displayTypeName(a) + ", " + Type.displayTypeName(b) + ')');
+ } else {
+ return ac.compareTo(bc);
+ }
+ }
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared as if they were strings; if different semantics are wanted, the conversion
+ * must be done by the caller.
+ * @param a the first object to be compared. If it is a StringValue, the
+ * collator is used to compare the values, otherwise the value must implement the equals() method.
+ * @param b the second object to be compared. This must be comparable with the first object: for
+ * example, if one is a string, they must both be strings.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) throws NoDynamicContextException {
+ // System.err.println("Comparing " + a.getClass() + ": " + a + " with " + b.getClass() + ": " + b);
+ if (a instanceof StringValue && b instanceof StringValue) {
+ return collator.comparesEqual(a.getStringValue(), b.getStringValue());
+ } else if (a instanceof CalendarValue && b instanceof CalendarValue) {
+ return ((CalendarValue)a).compareTo((CalendarValue)b, context) == 0;
+ } else {
+ Object ac = a.getXPathComparable(false, collator, context);
+ Object bc = b.getXPathComparable(false, collator, context);
+ return ac.equals(bc);
+ }
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal,
+ * then their comparison keys are equal, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) {
+
+ if (a instanceof StringValue) {
+ Platform platform = Configuration.getPlatform();
+ if (platform.canReturnCollationKeys(collator)) {
+ return new ComparisonKey(StandardNames.XS_STRING,
+ collator.getCollationKey(a.getStringValue()));
+ } else {
+ return new ComparisonKey(StandardNames.XS_STRING, a.getStringValue());
+ }
+ } else {
+ return new ComparisonKey(StandardNames.XS_STRING, a);
+ }
+ }
+
+
+}
+
+
diff --git a/sf/saxon/expr/sort/GenericSorter.java b/sf/saxon/expr/sort/GenericSorter.java
new file mode 100644
index 0000000..84441f0
--- /dev/null
+++ b/sf/saxon/expr/sort/GenericSorter.java
@@ -0,0 +1,500 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+/*
+Copyright ? 1999 CERN - European Organization for Nuclear Research.
+Permission to use, copy, moNumbererdify, distribute and sell this software and its documentation for any purpose
+is hereby granted without fee, provided that the above copyright notice appear in all copies and
+that both that copyright notice and this permission notice appear in supporting documentation.
+CERN makes no representations about the suitability of this software for any purpose.
+It is provided "as is" without expressed or implied warranty.
+*/
+
+/**
+ * Modified by Michael Kay to use the Saxon Sortable interface rather than a separate IntComparator and Swapper
+ */
+
+
+/**
+Generically sorts arbitrary shaped data (for example multiple arrays, 1,2 or 3-d matrices, and so on) using a
+quicksort or mergesort. This class addresses two problems, namely
+<ul>
+ <li><i>Sorting multiple arrays in sync</i>
+ <li><i>Sorting by multiple sorting criteria</i> (primary, secondary, tertiary,
+ ...)
+</ul>
+<h4>Sorting multiple arrays in sync</h4>
+<p>
+Assume we have three arrays X, Y and Z. We want to sort all three arrays by
+ X (or some arbitrary comparison function). For example, we have<br>
+ <tt>X=[3, 2, 1], Y=[3.0, 2.0, 1.0], Z=[6.0, 7.0, 8.0]</tt>. The output should
+ be <tt><br>
+ X=[1, 2, 3], Y=[1.0, 2.0, 3.0], Z=[8.0, 7.0, 6.0]</tt>. </p>
+<p>How can we achive this? Here are several alternatives. We could ... </p>
+<ol>
+ <li> make a list of Point3D objects, sort the list as desired using a comparison
+ function, then copy the results back into X, Y and Z. The classic object-oriented
+ way. </li>
+ <li>make an index list [0,1,2,...,N-1], sort the index list using a comparison function,
+ then reorder the elements of X,Y,Z as defined by the index list. Reordering
+ cannot be done in-place, so we need to copy X to some temporary array, then
+ copy in the right order back from the temporary into X. Same for Y and Z.
+ </li>
+ <li> use a generic quicksort or mergesort which, whenever two elements in X are swapped,
+ also swaps the corresponding elements in Y and Z. </li>
+</ol>
+Alternatives 1 and 2 involve quite a lot of copying and allocate significant amounts
+of temporary memory. Alternative 3 involves more swapping, more polymorphic message dispatches, no copying and does not need any temporary memory.
+<p> This class implements alternative 3. It operates on arbitrary shaped data.
+ In fact, it has no idea what kind of data it is sorting. Comparisons and swapping
+ are delegated to user provided objects which know their data and can do the
+ job.
+<p> Lets call the generic data <tt>g</tt> (it may be one array, three linked lists
+ or whatever). This class takes a user comparison function operating on two indexes
+ <tt>(a,b)</tt>, namely an {@link Sortable}. The comparison function determines
+ whether <tt>g[a]</tt> is equal, less or greater than <tt>g[b]</tt>. The sort,
+ depending on its implementation, can decide to swap the data at index <tt>a</tt>
+ with the data at index <tt>b</tt>. It calls a user provided {@link Sortable}
+ object that knows how to swap the data of these indexes.
+<p>The following snippet shows how to solve the problem.
+<table>
+<td class="PRE">
+<pre>
+final int[] x;
+final double[] y;
+final double[] z;
+
+x = new int[] {3, 2, 1 };
+y = new double[] {3.0, 2.0, 1.0};
+z = new double[] {6.0, 7.0, 8.0};
+
+
+// this one knows how to swap two indexes (a,b)
+Swapper swapper = new Swapper() {
+ public void swap(int a, int b) {
+ int t1; double t2, t3;
+ t1 = x[a]; x[a] = x[b]; x[b] = t1;
+ t2 = y[a]; y[a] = y[b]; y[b] = t2;
+ t3 = z[a]; z[a] = z[b]; z[b] = t3;
+ }
+};
+// simple comparison: compare by X and ignore Y,Z<br>
+IntComparator comp = new IntComparator() {
+ public int compare(int a, int b) {
+ return x[a]==x[b] ? 0 : (x[a]<x[b] ? -1 : 1);
+ }
+};
+
+System.out.println("before:");
+System.out.println("X="+Arrays.toString(x));
+System.out.println("Y="+Arrays.toString(y));
+System.out.println("Z="+Arrays.toString(z));
+
+GenericSorting.quickSort(0, X.length, comp, swapper);
+// GenericSorting.mergeSort(0, X.length, comp, swapper);
+
+System.out.println("after:");
+System.out.println("X="+Arrays.toString(x));
+System.out.println("Y="+Arrays.toString(y));
+System.out.println("Z="+Arrays.toString(z));
+</pre>
+</td>
+</table>
+<h4>Sorting by multiple sorting criterias (primary, secondary, tertiary, ...)</h4>
+<p>Assume again we have three arrays X, Y and Z. Now we want to sort all three
+ arrays, primarily by Y, secondarily by Z (if Y elements are equal). For example,
+ we have<br>
+ <tt>X=[6, 7, 8, 9], Y=[3.0, 2.0, 1.0, 3.0], Z=[5.0, 4.0, 4.0, 1.0]</tt>. The
+ output should be <tt><br>
+ X=[8, 7, 9, 6], Y=[1.0, 2.0, 3.0, 3.0], Z=[4.0, 4.0, 1.0, 5.0]</tt>. </p>
+<p>Here is how to solve the problem. All code in the above example stays the same,
+ except that we modify the comparison function as follows</p>
+<table>
+<td class="PRE">
+<pre>
+//compare by Y, if that doesn't help, reside to Z
+IntComparator comp = new IntComparator() {
+ public int compare(int a, int b) {
+ if (y[a]==y[b]) return z[a]==z[b] ? 0 : (z[a]<z[b] ? -1 : 1);
+ return y[a]<y[b] ? -1 : 1;
+ }
+};
+</pre>
+</td>
+</table>
+
+<h4>Notes</h4>
+<p></p>
+<p> Sorts involving floating point data and not involving comparators, like, for
+ example provided in the JDK {@link java.util.Arrays} and in the Colt
+ (cern.colt.Sorting) handle floating point numbers in special ways to guarantee
+ that NaN's are swapped to the end and -0.0 comes before 0.0. Methods delegating
+ to comparators cannot do this. They rely on the comparator. Thus, if such boundary
+ cases are an issue for the application at hand, comparators explicitly need
+ to implement -0.0 and NaN aware comparisons. Remember: <tt>-0.0 < 0.0 == false</tt>,
+ <tt>(-0.0 == 0.0) == true</tt>, as well as <tt>5.0 < Double.NaN == false</tt>,
+ <tt>5.0 > Double.NaN == false</tt>. Same for <tt>float</tt>.
+<h4>Implementation </h4>
+<p>The quicksort is a derivative of the JDK 1.2 V1.26 algorithms (which are, in
+ turn, based on Bentley's and McIlroy's fine work).
+ The mergesort is a derivative of the JAL algorithms, with optimisations taken from the JDK algorithms.
+Both quick and merge sort are "in-place", i.e. do not allocate temporary memory (helper arrays).
+Mergesort is <i>stable</i> (by definition), while quicksort is not.
+A stable sort is, for example, helpful, if matrices are sorted successively
+by multiple columns. It preserves the relative position of equal elements.
+
+ at author wolfgang.hoschek at cern.ch
+ at version 1.0, 03-Jul-99
+*/
+public class GenericSorter extends Object {
+
+ private static final int SMALL = 7;
+ private static final int MEDIUM = 7;
+ private static final int LARGE = 40;
+
+
+ /**
+ * Makes this class non instantiable, but still let's others inherit from it.
+ */
+ protected GenericSorter() {}
+
+ /**
+ * Sorts the specified range of elements according
+ * to the order induced by the specified comparator. All elements in the
+ * range must be <i>mutually comparable</i> by the specified comparator
+ * (that is, <tt>c.compare(a, b)</tt> must not throw an
+ * exception for any indexes <tt>a</tt> and
+ * <tt>b</tt> in the range).<p>
+ *
+ * The sorting algorithm is a tuned quicksort,
+ * adapted from Jon L. Bentley and M. Douglas McIlroy's "Engineering a
+ * Sort Function", Software-Practice and Experience, Vol. 23(11)
+ * P. 1249-1265 (November 1993). For details, see
+ * http://citeseer.ist.psu.edu/bentley93engineering.html.
+ * This algorithm offers n*log(n) performance on many data sets that cause other
+ * quicksorts to degrade to quadratic performance.
+ *
+ * @param fromIndex the index of the first element (inclusive) to be sorted.
+ * @param toIndex the index of the last element (exclusive) to be sorted.
+ * @param c the comparator to determine the order of the generic data;
+ * an object that knows how to swap the elements at any two indexes (a,b).
+ *
+ */
+ public static void quickSort(int fromIndex, int toIndex, /*@NotNull*/ Sortable c) {
+ quickSort1(fromIndex, toIndex-fromIndex, c);
+ }
+
+ /**
+ * Sorts the specified sub-array into ascending order.
+ */
+ private static void quickSort1(int off, int len, Sortable comp) {
+ // Insertion sort on smallest arrays
+ if (len < SMALL) {
+ for (int i=off; i<len+off; i++)
+ for (int j=i; j>off && (comp.compare(j-1,j)>0); j--) {
+ comp.swap(j, j-1);
+ }
+ return;
+ }
+
+ // Choose a partition element, v
+ int m = off + (len >>> 1); // len/2; // Small arrays, middle element
+
+ if (len > MEDIUM) {
+ int l = off;
+ int n = off + len - 1;
+ if (len > LARGE) { // Big arrays, pseudomedian of 9
+ int s = len >>> 3; // len/8;
+ l = med3(l, l+s, l+2*s, comp);
+ m = med3(m-s, m, m+s, comp);
+ n = med3(n-2*s, n-s, n, comp);
+ }
+// m = med3(l, m, n, comp); // Mid-size, med of 3
+ // manually inlined (most time is spent near the leafs of the recursion tree)
+ //a = comp.compare(l,m);
+ //b = comp.compare(l,n);
+ int c = comp.compare(m,n);
+ m = (comp.compare(l,m)<0 ?
+ (c<0 ? m : comp.compare(l,n)<0 ? n : l) :
+ (c>0 ? m : comp.compare(l,n)>0 ? n : l));
+ }
+ //long v = x[m];
+
+ // Establish Invariant: v* (<v)* (>v)* v*
+ int a = off, b = a, c = off + len - 1, d = c;
+ while (true) {
+ int comparison;
+ while (b <= c && ((comparison=comp.compare(b,m))<=0)) {
+ if (comparison == 0) {
+ if (a==m) m = b; // pivot is moving target; DELTA to JDK !!!
+ else if (b==m) m = a; // pivot is moving target; DELTA to JDK !!!
+ comp.swap(a++, b);
+ }
+ b++;
+ }
+ while (c >= b && ((comparison=comp.compare(c,m))>=0)) {
+ if (comparison == 0) {
+ if (c==m) m = d; // pivot is moving target; DELTA to JDK !!!
+ else if (d==m) m = c; // pivot is moving target; DELTA to JDK !!!
+ comp.swap(c, d--);
+ }
+ c--;
+ }
+ if (b > c) break;
+ if (b==m) m = d; // pivot is moving target; DELTA to JDK !!!
+ else if (c==m) m = c; // pivot is moving target; DELTA to JDK !!!
+ comp.swap(b++, c--);
+ }
+
+ // Swap partition elements back to middle
+
+ int s = Math.min(a-off, b-a );
+ // vecswap(swapper, off, b-s, s);
+ // manually inlined
+ int aa = off; int bb = b-s;
+ while (--s >= 0) comp.swap(aa++, bb++);
+ int n = off + len;
+ s = Math.min(d-c, n-d-1);
+ // vecswap(swapper, b, n-s, s); // manually inlined
+ aa = b; bb = n-s;
+ while (--s >= 0) comp.swap(aa++, bb++);
+
+ // Recursively sort non-partition-elements
+ if ((s = b-a) > 1)
+ quickSort1(off, s, comp);
+ if ((s = d-c) > 1)
+ quickSort1(n-s, s, comp);
+ }
+
+ /**
+ * Returns the index of the median of the three indexed elements.
+ */
+ private static int med3(int a, int b, int c, Sortable comp) {
+ int bc = comp.compare(b,c);
+ return (comp.compare(a,b)<0 ?
+ (bc<0 ? b : comp.compare(a,c)<0 ? c : a) :
+ (bc>0 ? b : comp.compare(a,c)>0 ? c : a));
+ }
+
+
+// /**
+// * Swaps x[a .. (a+n-1)] with x[b .. (b+n-1)].
+// */
+// private static void vecswap(Swapper swapper, int a, int b, int n) {
+// for (int i=0; i<n; i++, a++, b++) swapper.swap(a, b);
+// }
+
+ /**
+ * Sorts the specified range of elements according
+ * to the order induced by the specified comparator. All elements in the
+ * range must be <i>mutually comparable</i> by the specified comparator
+ * (that is, <tt>c.compare(a, b)</tt> must not throw an
+ * exception for any indexes <tt>a</tt> and
+ * <tt>b</tt> in the range).<p>
+ *
+ * This sort is guaranteed to be <i>stable</i>: equal elements will
+ * not be reordered as a result of the sort.<p>
+ *
+ * The sorting algorithm is a modified mergesort (in which the merge is
+ * omitted if the highest element in the low sublist is less than the
+ * lowest element in the high sublist). This algorithm offers guaranteed
+ * n*log(n) performance, and can approach linear performance on nearly
+ * sorted lists.
+ *
+ * @param fromIndex the index of the first element (inclusive) to be sorted.
+ * @param toIndex the index of the last element (exclusive) to be sorted.
+ * @param c the comparator to determine the order of the generic data;
+ * an object that knows how to swap the elements at any two indexes (a,b).
+ *
+ */
+ public static void mergeSort(int fromIndex, int toIndex, Sortable c) {
+ /*
+ * We retain the same method signature as quickSort. Given only a
+ * comparator and swapper we do not know how to copy and move elements
+ * from/to temporary arrays. Hence, in contrast to the JDK mergesorts
+ * this is an "in-place" mergesort, i.e. does not allocate any temporary
+ * arrays. A non-inplace mergesort would be faster in most cases, but
+ * would require non-intuitive delegate objects. Remember that an
+ * in-place merge phase requires N logN swaps, while an out-of-place
+ * merge phase requires only N swaps. This doesn't matter much if swaps
+ * are cheap and comparisons are expensive. Nonetheless this can
+ * certainly be suboptimal.
+ */
+
+
+ // Insertion sort on smallest arrays
+ if (toIndex - fromIndex < SMALL) {
+ for (int i = fromIndex; i < toIndex; i++) {
+ for (int j = i; j > fromIndex && (c.compare(j - 1, j) > 0); j--) {
+ c.swap(j, j - 1);
+ }
+ }
+ return;
+ }
+
+ // Recursively sort halves
+ int mid = (fromIndex + toIndex) >>> 1; // (fromIndex + toIndex) / 2;
+ mergeSort(fromIndex, mid, c);
+ mergeSort(mid, toIndex, c);
+
+ // If list is already sorted, nothing left to do. This is an
+ // optimization that results in faster sorts for nearly ordered lists.
+ if (c.compare(mid - 1, mid) <= 0) return;
+
+ // Merge sorted halves
+ inplaceMerge(fromIndex, mid, toIndex, c);
+ }
+
+ /**
+ * Transforms two consecutive sorted ranges into a single sorted
+ * range. The initial ranges are <code>[first, middle)</code>
+ * and <code>[middle, last)</code>, and the resulting range is
+ * <code>[first, last)</code>.
+ * Elements in the first input range will precede equal elements in the
+ * second.
+ */
+ private static void inplaceMerge(int first, int middle, int last, Sortable comp) {
+ if (first >= middle || middle >= last)
+ return;
+ if (last - first == 2) {
+ if (comp.compare(middle, first)<0) {
+ comp.swap(first,middle);
+ }
+ return;
+ }
+ int firstCut;
+ int secondCut;
+ if (middle - first > last - middle) {
+ firstCut = first + ((middle - first) >>> 1); // first + ((middle - first) / 2);
+ // secondCut = lower_bound(middle, last, firstCut, comp);
+ // manually inlined for speed (speedup = 2)
+ int _first = middle;
+ int len = last - _first;
+ while (len > 0) {
+ int half = len >>> 1; // len / 2;
+ int mid = _first + half;
+ if (comp.compare(mid, firstCut)<0) {
+ _first = mid + 1;
+ len -= half + 1;
+ }
+ else {
+ len = half;
+ }
+ }
+ secondCut = _first;
+ }
+ else {
+ secondCut = middle + ((last - middle) >>> 1); // middle + ((last - middle) / 2);
+ // firstCut = upper_bound(first, middle, secondCut, comp);
+ // manually inlined for speed (speedup = 2)
+ int _first = first;
+ int len = middle - _first;
+ while (len > 0) {
+ int half = len >>> 1; // len / 2;
+ int mid = _first + half;
+ if (comp.compare(secondCut, mid)<0) {
+ len = half;
+ }
+ else {
+ _first = mid + 1;
+ len -= half + 1;
+ }
+ }
+ firstCut = _first;
+ }
+
+ // rotate(firstCut, middle, secondCut, swapper);
+ // is manually inlined for speed
+ // (hotspot compiler inlining in recursive methods seems to work only for
+ // small call depths, even if methods are "static private")
+ // speedup = 1.7
+ // begin inline
+ int first2 = firstCut; int middle2 = middle; int last2 = secondCut;
+ if (middle2 != first2 && middle2 != last2) {
+ int first1 = first2; int last1 = middle2;
+ while (first1 < --last1) comp.swap(first1++,last1);
+ first1 = middle2; last1 = last2;
+ while (first1 < --last1) comp.swap(first1++,last1);
+ first1 = first2; last1 = last2;
+ while (first1 < --last1) comp.swap(first1++,last1);
+ }
+ // end inline
+
+ middle = firstCut + (secondCut - middle);
+ inplaceMerge(first, firstCut, middle, comp);
+ inplaceMerge(middle, secondCut, last, comp);
+ }
+
+// /**
+// * Performs a binary search on an already-sorted range: finds the first
+// * position where an element can be inserted without violating the ordering.
+// * Sorting is by a user-supplied comparison function.
+// * @param array Array containing the range.
+// * @param first Beginning of the range.
+// * @param last One past the end of the range.
+// * @param x Element to be searched for.
+// * @param comp Comparison function.
+// * @return The largest index i such that, for every j in the
+// * range <code>[first, i)</code>,
+// * <code>comp.apply(array[j], x)</code> is
+// * <code>true</code>.
+// * @see Sorting#upper_bound
+// * @see Sorting#equal_range
+// * @see Sorting#binary_search
+// */
+// private static int lower_bound(int first, int last, int x, IntComparator comp) {
+// int len = last - first;
+// while (len > 0) {
+// int half = len >>> 1; // len / 2;
+// int middle = first + half;
+// if (comp.compare(middle, x)<0) {
+// first = middle + 1;
+// len -= half + 1;
+// }
+// else {
+// len = half;
+// }
+// }
+// return first;
+// }
+//
+// /**
+// * Performs a binary search on an already-sorted range: finds the last
+// * position where an element can be inserted without violating the ordering.
+// * Sorting is by a user-supplied comparison function.
+// * @param array Array containing the range.
+// * @param first Beginning of the range.
+// * @param last One past the end of the range.
+// * @param x Element to be searched for.
+// * @param comp Comparison function.
+// * @return The largest index i such that, for every j in the
+// * range <code>[first, i)</code>,
+// * <code>comp.apply(x, array[j])</code> is
+// * <code>false</code>.
+// * @see Sorting#lower_bound
+// * @see Sorting#equal_range
+// * @see Sorting#binary_search
+// */
+// private static int upper_bound(int first, int last, int x, IntComparator comp) {
+// int len = last - first;
+// while (len > 0) {
+// int half = len >>> 1; // len / 2;
+// int middle = first + half;
+// if (comp.compare(x, middle)<0) {
+// len = half;
+// }
+// else {
+// first = middle + 1;
+// len -= half + 1;
+// }
+// }
+// return first;
+// }
+
+}
diff --git a/sf/saxon/expr/sort/GlobalOrderComparer.java b/sf/saxon/expr/sort/GlobalOrderComparer.java
new file mode 100644
index 0000000..98e2b83
--- /dev/null
+++ b/sf/saxon/expr/sort/GlobalOrderComparer.java
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+
+import java.io.Serializable;
+
+/**
+ * A Comparer used for comparing nodes in document order. This
+ * comparer is used when there is no guarantee that the nodes being compared
+ * come from the same document
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public final class GlobalOrderComparer implements ItemOrderComparer, Serializable {
+
+ private static GlobalOrderComparer instance = new GlobalOrderComparer();
+
+ /**
+ * Get an instance of a GlobalOrderComparer. The class maintains no state
+ * so this returns the same instance every time.
+ */
+
+ public static GlobalOrderComparer getInstance() {
+ return instance;
+ }
+
+ public int compare(Item a, /*@NotNull*/ Item b) {
+ if (a==b) {
+ return 0;
+ }
+ long d1 = ((NodeInfo)a).getDocumentNumber();
+ long d2 = ((NodeInfo)b).getDocumentNumber();
+ if (d1 == d2) {
+ return ((NodeInfo)a).compareOrder(((NodeInfo)b));
+ }
+ return Long.signum(d1 - d2);
+ }
+}
+
diff --git a/sf/saxon/expr/sort/GroupAdjacentIterator.java b/sf/saxon/expr/sort/GroupAdjacentIterator.java
new file mode 100644
index 0000000..97a62c5
--- /dev/null
+++ b/sf/saxon/expr/sort/GroupAdjacentIterator.java
@@ -0,0 +1,252 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import com.saxonica.stream.ManualGroupIterator;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.AtomicArray;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ListIterator;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A GroupAdjacentIterator iterates over a sequence of groups defined by
+ * xsl:for-each-group group-adjacent="x". The groups are returned in
+ * order of first appearance.
+ * <p/>
+ * Each step of this iterator advances to the first item of the next group,
+ * leaving the members of that group in a saved list.
+ */
+
+public class GroupAdjacentIterator implements GroupIterator, LookaheadIterator {
+
+ private SequenceIterator population;
+ private Expression keyExpression;
+ private StringCollator collator;
+ private AtomicComparer comparer;
+ private XPathContext baseContext;
+ private XPathContext runningContext;
+ private List<ComparisonKey> currentComparisonKey;
+ private AtomicSequence currentKey;
+ private AtomicSequence currentKeyItems;
+ private List<Item> currentMembers;
+ private List<ComparisonKey> nextComparisonKey;
+ private List<AtomicValue> nextKey = null;
+ private Item next;
+ private Item current = null;
+ private int position = 0;
+ private int groupSlot = -1;
+ private int keySlot = -1;
+ private boolean composite = false;
+
+ public GroupAdjacentIterator(SequenceIterator population, Expression keyExpression,
+ XPathContext baseContext, AtomicComparer comparer, boolean composite)
+ throws XPathException {
+ this.population = population;
+ this.keyExpression = keyExpression;
+ this.baseContext = baseContext;
+ this.runningContext = baseContext.newMinorContext();
+ runningContext.setCurrentIterator(population);
+ this.comparer = comparer;
+ this.composite = composite;
+ next = population.next();
+ if (next != null) {
+ nextKey = getKey(runningContext);
+ nextComparisonKey = getComparisonKey(nextKey);
+ }
+ }
+
+ private List<AtomicValue> getKey(XPathContext context) throws XPathException {
+ List<AtomicValue> key = new ArrayList<AtomicValue>();
+ SequenceIterator iter = keyExpression.iterate(context);
+ while (true) {
+ AtomicValue val = (AtomicValue) iter.next();
+ if (val == null) {
+ break;
+ }
+ key.add(val);
+ }
+ return key;
+ }
+
+ private List<ComparisonKey> getComparisonKey(List<AtomicValue> key) throws XPathException {
+ List<ComparisonKey> ckey = new ArrayList<ComparisonKey>(key.size());
+ for (AtomicValue aKey : key) {
+ ckey.add(comparer.getComparisonKey(aKey));
+ }
+ return ckey;
+ }
+
+ public void setGroupSlot(int groupSlot) {
+ this.groupSlot = groupSlot;
+ }
+
+ public void setKeySlot(int keySlot) {
+ this.keySlot = keySlot;
+ }
+
+ private void advance() throws XPathException {
+ currentMembers = new ArrayList<Item>(20);
+ currentMembers.add(current);
+ while (true) {
+ Item nextCandidate = population.next();
+ if (nextCandidate == null) {
+ break;
+ }
+ List<AtomicValue> newKey = getKey(runningContext);
+ List<ComparisonKey> newComparisonKey = getComparisonKey(newKey);
+
+ try {
+ if (currentComparisonKey.equals(newComparisonKey)) {
+ currentMembers.add(nextCandidate);
+ } else {
+ next = nextCandidate;
+ nextComparisonKey = newComparisonKey;
+ nextKey = newKey;
+ return;
+ }
+ } catch (ClassCastException e) {
+ String message = "Grouping key values are of non-comparable types";
+ if (currentKeyItems.getLength() != 0 && !newKey.isEmpty()) {
+ String t1 = Type.displayTypeName(currentKeyItems.itemAt(0));
+ String t2 = Type.displayTypeName(newKey.get(0));
+ if (!t1.equals(t2)) {
+ message += " (" + t1 + " and " + t2 + ")";
+ }
+ }
+ XPathException err = new XPathException(message);
+ err.setIsTypeError(true);
+ err.setXPathContext(runningContext);
+ throw err;
+ }
+ }
+ next = null;
+ nextKey = null;
+ }
+
+ public AtomicSequence getCurrentGroupingKey() {
+ return currentKey;
+ }
+
+ public SequenceIterator iterateCurrentGroup() {
+ return new ListIterator(currentMembers);
+ }
+
+ public boolean hasCurrentGroup() {
+ return groupSlot < 0;
+ }
+
+ public boolean hasCurrentGroupingKey() {
+ return keySlot < 0;
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public Item next() throws XPathException {
+ if (next == null) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ current = next;
+ if (nextKey.size() == 1) {
+ currentKey = nextKey.get(0);
+ } else {
+ currentKey = new AtomicArray(nextKey.toArray(new AtomicValue[nextKey.size()]));
+ }
+ currentComparisonKey = nextComparisonKey;
+ position++;
+ advance();
+ if (groupSlot >= 0) {
+ runningContext.setLocalVariable(groupSlot, new SequenceExtent(currentMembers));
+ }
+ if (keySlot >= 0) {
+ runningContext.setLocalVariable(keySlot, currentKey);
+ }
+ return current;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ population.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new GroupAdjacentIterator(population.getAnother(), keyExpression, baseContext, comparer, composite);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+
+
+//#if EE==true
+
+ public ManualGroupIterator getSnapShot(XPathContext context) throws XPathException {
+ return new ManualGroupAdjacentIterator();
+ }
+
+ private class ManualGroupAdjacentIterator extends ManualGroupIterator {
+ AtomicSequence cKey = currentKey;
+ List<Item> cMembers = currentMembers;
+ XPathContext savedcontext = runningContext.newMinorContext();
+
+ ManualGroupAdjacentIterator() {
+ super(current, position);
+ setLastPositionFinder(new LastPositionFinder<Item>() {
+ public int getLength() throws XPathException {
+ return savedcontext.getLast();
+ }
+ });
+ }
+
+ public AtomicSequence getCurrentGroupingKey() {
+ return cKey;
+ }
+
+ public SequenceIterator<? extends Item> iterateCurrentGroup() throws XPathException {
+ return new ListIterator(cMembers);
+ }
+
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/sort/GroupByIterator.java b/sf/saxon/expr/sort/GroupByIterator.java
new file mode 100644
index 0000000..6b7ee77
--- /dev/null
+++ b/sf/saxon/expr/sort/GroupByIterator.java
@@ -0,0 +1,357 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import com.saxonica.stream.ManualGroupIterator;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ListIterator;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A GroupByIterator iterates over a sequence of groups defined by
+ * xsl:for-each-group group-by="x". The groups are returned in
+ * order of first appearance. Note that an item can appear in several groups;
+ * indeed, an item may be the leading item of more than one group, which means
+ * that knowing the leading item is not enough to know the current group.
+ * <p/>
+ * <p>The GroupByIterator acts as a SequenceIterator, where successive calls of
+ * next() return the leading item of each group in turn. The current item of
+ * the iterator is therefore the leading item of the current group. To get access
+ * to all the members of the current group, the method iterateCurrentGroup() is used;
+ * this underpins the current-group() function in XSLT. The grouping key for the
+ * current group is available via the getCurrentGroupingKey() method.</p>
+ */
+
+public class GroupByIterator implements GroupIterator, LastPositionFinder, LookaheadIterator {
+
+ // The implementation of group-by is not pipelined. All the items in the population
+ // are read at the start, their grouping keys are calculated, and the groups are formed
+ // in memory as a hash table indexed by the grouping key. This hash table is then
+ // flattened into three parallel lists: a list of groups (each group being represented
+ // as a list of items in population order), a list of grouping keys, and a list of
+ // the initial items of the groups.
+
+ private SequenceIterator population;
+ protected Expression keyExpression;
+ private StringCollator collator;
+ private XPathContext keyContext;
+ private int position = 0;
+
+ // Main data structure holds one entry for each group. The entry is also an ArrayList,
+ // which contains the Items that are members of the group, in population order.
+ // The groups are arranged in order of first appearance within the population.
+ protected List<List<Item>> groups = new ArrayList<List<Item>>(40);
+
+ // This parallel structure identifies the grouping key for each group. The list
+ // corresponds one-to-one with the list of groups.
+ protected List<AtomicSequence> groupKeys = new ArrayList<AtomicSequence>(40);
+
+ // An AtomicComparer is used to do the comparisons of individual atomic values
+ protected AtomicComparer comparer;
+
+ // Stack frame slots for the group and the grouping key, or -1 if not used
+ protected int groupSlot = -1;
+ protected int keySlot = -1;
+ protected boolean composite;
+
+ /**
+ * Create a GroupByIterator
+ *
+ * @param population iterator over the population to be grouped
+ * @param keyExpression the expression used to calculate the grouping key
+ * @param keyContext dynamic context for calculating the grouping key
+ * @param comparer Collation to be used for comparing grouping keys
+ * @throws XPathException if an error occurs
+ */
+
+ public GroupByIterator(SequenceIterator population, Expression keyExpression,
+ XPathContext keyContext, AtomicComparer comparer, boolean composite)
+ throws XPathException {
+ this.population = population;
+ this.keyExpression = keyExpression;
+ this.keyContext = keyContext;
+ this.comparer = comparer;
+ this.composite = composite;
+ if (composite) {
+ buildIndexedGroupsComposite();
+ } else {
+ buildIndexedGroups();
+ }
+ }
+
+ public GroupByIterator() {
+ }
+
+ public void setGroupSlot(int groupSlot) {
+ this.groupSlot = groupSlot;
+ }
+
+ public void setKeySlot(int keySlot) {
+ this.keySlot = keySlot;
+ }
+
+ /**
+ * Build the grouping table forming groups of items with equal keys.
+ * This form of grouping allows a member of the population to be present in zero
+ * or more groups, one for each value of the grouping key.
+ *
+ * @throws XPathException if any error occurs
+ */
+
+ private void buildIndexedGroups() throws XPathException {
+ HashMap<ComparisonKey, List<Item>> index = new HashMap<ComparisonKey, List<Item>>(40);
+ XPathContext c2 = keyContext.newMinorContext();
+ c2.setCurrentIterator(population);
+ while (true) {
+ Item item = population.next();
+ if (item == null) {
+ break;
+ }
+
+ SequenceIterator keys = keyExpression.iterate(c2);
+ boolean firstKey = true;
+ while (true) {
+ AtomicValue key = (AtomicValue) keys.next();
+ if (key == null) {
+ break;
+ }
+ ComparisonKey comparisonKey = comparer.getComparisonKey(key);
+ List<Item> g = index.get(comparisonKey);
+ if (g == null) {
+ List<Item> newGroup = new ArrayList<Item>(20);
+ newGroup.add(item);
+ groups.add(newGroup);
+ groupKeys.add(key);
+ index.put(comparisonKey, newGroup);
+ } else {
+ if (firstKey) {
+ g.add(item);
+ } else {
+ // if this is not the first key value for this item, we
+ // check whether the item is already in this group before
+ // adding it again. If it is in this group, then we know
+ // it will be at the end.
+ if (g.get(g.size() - 1) != item) {
+ g.add(item);
+ }
+ }
+ }
+ firstKey = false;
+ }
+
+ }
+ }
+
+ /**
+ * Build the grouping table forming groups of items with equal keys.
+ * This form of grouping allows the grouping key to be a sequence of atomic values.
+ *
+ * @throws XPathException if any error occurs
+ */
+
+ private void buildIndexedGroupsComposite() throws XPathException {
+ HashMap<List<ComparisonKey>, List<Item>> index = new HashMap<List<ComparisonKey>, List<Item>>(40);
+ XPathContext c2 = keyContext.newMinorContext();
+ c2.setCurrentIterator(population);
+ while (true) {
+ Item item = population.next();
+ if (item == null) {
+ break;
+ }
+ SequenceIterator keys = keyExpression.iterate(c2);
+ List<ComparisonKey> ckList = new ArrayList<ComparisonKey>();
+ List<AtomicValue> compositeKey = new ArrayList<AtomicValue>();
+ while (true) {
+ AtomicValue key = (AtomicValue) keys.next();
+ if (key == null) {
+ break;
+ }
+ compositeKey.add(key);
+ ComparisonKey comparisonKey = comparer.getComparisonKey(key);
+ ckList.add(comparisonKey);
+ }
+
+ List<Item> g = index.get(ckList);
+ if (g == null) {
+ List<Item> newGroup = new ArrayList<Item>(20);
+ newGroup.add(item);
+ groups.add(newGroup);
+ groupKeys.add(new AtomicArray(compositeKey.toArray(new AtomicValue[compositeKey.size()])));
+ index.put(ckList, newGroup);
+ } else {
+ g.add(item);
+ }
+ }
+ }
+
+
+ /**
+ * Get the value of the grouping key for the current group
+ *
+ * @return the grouping key
+ */
+
+ public synchronized AtomicSequence getCurrentGroupingKey() {
+ AtomicSequence val = groupKeys.get(position - 1);
+ if (val == null) {
+ return EmptyAtomicSequence.getInstance();
+ } else {
+ return val;
+ }
+ }
+
+ /**
+ * Get an iterator over the items in the current group
+ *
+ * @return the iterator
+ */
+
+ public SequenceIterator iterateCurrentGroup() {
+ return new ListIterator(groups.get(position - 1));
+ }
+
+ public boolean hasCurrentGroup() {
+ return groupSlot < 0;
+ }
+
+ public boolean hasCurrentGroupingKey() {
+ return keySlot < 0;
+ }
+
+ /**
+ * Get the contents of the current group as a java List
+ *
+ * @return the contents of the current group
+ */
+
+ public List getCurrentGroup() {
+ return groups.get(position - 1);
+ }
+
+ public boolean hasNext() {
+ return position < groups.size();
+ }
+
+ /*@Nullable*/
+ public Item next() throws XPathException {
+ if (position >= 0 && position < groups.size()) {
+ if (groupSlot >= 0) {
+ keyContext.setLocalVariable(groupSlot, new SequenceExtent(groups.get(position)));
+ }
+ if (keySlot >= 0) {
+ keyContext.setLocalVariable(keySlot, groupKeys.get(position));
+ }
+ position++;
+ return current();
+ } else {
+ position = -1;
+ return null;
+ }
+ }
+
+ public Item current() {
+ if (position < 1) {
+ return null;
+ }
+ // return the initial item of the current group
+ return groups.get(position - 1).get(0);
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ XPathContext c2 = keyContext.newMinorContext();
+ return new GroupByIterator(population.getAnother(), keyExpression, c2, comparer, composite);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LAST_POSITION_FINDER | LOOKAHEAD;
+ }
+
+ /**
+ * Get the last position (that is, the number of groups)
+ */
+
+ public int getLength() throws XPathException {
+ return groups.size();
+ }
+
+//#if EE==true
+
+ public ManualGroupIterator getSnapShot(XPathContext context) throws XPathException {
+ return new ManualGroupByIterator();
+ }
+
+ public class ManualGroupByIterator extends ManualGroupIterator {
+
+ List<Item> currentGroup = groups.get(position - 1);
+ AtomicSequence currentGroupingKey = groupKeys.get(position-1);
+
+ public ManualGroupByIterator() {
+ super(groups.get(position - 1).get(0), position);
+ setLastPositionFinder(new LastPositionFinder<Item>() {
+ public int getLength() throws XPathException {
+ return groups.size();
+ }
+ });
+ }
+
+ public SequenceIterator<? extends Item> iterateCurrentGroup() throws XPathException {
+ return new ListIterator<Item>(currentGroup);
+ }
+
+ public AtomicSequence getCurrentGroupingKey() {
+ return currentGroupingKey;
+ }
+
+
+ @Override
+ public UnfailingIterator getAnother() {
+ return this;
+ }
+
+ @Override
+ public int getLength() throws XPathException {
+ return groups.size();
+ }
+
+
+ }
+//#endif
+
+
+}
+
diff --git a/sf/saxon/expr/sort/GroupEndingIterator.java b/sf/saxon/expr/sort/GroupEndingIterator.java
new file mode 100644
index 0000000..b90fb0e
--- /dev/null
+++ b/sf/saxon/expr/sort/GroupEndingIterator.java
@@ -0,0 +1,66 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+
+import java.util.ArrayList;
+
+/**
+ * A GroupEndingIterator iterates over a sequence of groups defined by
+ * xsl:for-each-group group-ending-with="x". The groups are returned in
+ * order of first appearance.
+ */
+
+public class GroupEndingIterator extends GroupMatchingIterator implements GroupIterator, LookaheadIterator {
+
+ public GroupEndingIterator(SequenceIterator population, Pattern endPattern,
+ XPathContext context)
+ throws XPathException {
+ this.population = population;
+ this.pattern = endPattern;
+ baseContext = context;
+ runningContext = context.newMinorContext();
+ runningContext.setCurrentIterator(population);
+ // the first item in the population always starts a new group
+ next = population.next();
+ }
+
+ protected void advance() throws XPathException {
+ currentMembers = new ArrayList(20);
+ currentMembers.add(current);
+
+ next = current;
+ while (next != null) {
+ if (pattern.matches(next, runningContext)) {
+ next = population.next();
+ if (next != null) {
+ break;
+ }
+ } else {
+ next = population.next();
+ if (next != null) {
+ currentMembers.add(next);
+ }
+ }
+ }
+ }
+
+ /*@NotNull*/ public SequenceIterator getAnother() throws XPathException {
+ return new GroupEndingIterator(population.getAnother(), pattern, baseContext);
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/sort/GroupIterator.java b/sf/saxon/expr/sort/GroupIterator.java
new file mode 100644
index 0000000..39fb51b
--- /dev/null
+++ b/sf/saxon/expr/sort/GroupIterator.java
@@ -0,0 +1,85 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import com.saxonica.stream.ManualGroupIterator;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A GroupIterator is an iterator that iterates over a sequence of groups.
+ * The normal methods such as next() and current() always deliver the leading item
+ * of the group. Additional methods are available to get the grouping key for the
+ * current group (only applicable to group-by and group-adjacent), and to get all the
+ * members of the current group.
+ */
+
+public interface GroupIterator extends SequenceIterator {
+
+ /**
+ * Set a local variable slot to hold the value of the current group
+ * @param groupSlot the stack frame position of the variable holding the current group
+ */
+
+ public void setGroupSlot(int groupSlot);
+
+ /**
+ * Set a local variable slot to hold the value of the current grouping key
+ * @param keySlot the stack frame position of the variable holding the current grouping key
+ */
+
+ public void setKeySlot(int keySlot);
+
+ /**
+ * Get the grouping key of the current group
+ * @return the current grouping key in the case of group-by or group-adjacent,
+ * or null in the case of group-starting-with and group-ending-with
+ */
+
+ /*@Nullable*/ public AtomicSequence getCurrentGroupingKey();
+
+ /**
+ * Get an iterator over the members of the current group, in population
+ * order. This must always be a clean iterator, that is, an iterator that
+ * starts at the first item of the group.
+ * @return an iterator over all the members of the current group, in population
+ * order.
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ public SequenceIterator<? extends Item> iterateCurrentGroup() throws XPathException;
+
+ /**
+ * Ask whether this iterator has a current group
+ * @return false if the controlling xsl:for-each-group element binds a group variable, true if it
+ * delivers results using the current-group() function
+ */
+
+ public boolean hasCurrentGroup();
+
+ /**
+ * Ask whether this iterator has a current grouping key
+ * @return false if the controlling xsl:for-each-group element binds a grouping-key variable, true if it
+ * delivers results using the current-grouping-key() function
+ */
+
+ public boolean hasCurrentGroupingKey();
+
+//#if EE==true
+
+ /**
+ * Get a sequence which is a snapshot of this sequence at the current position
+ */
+ public ManualGroupIterator getSnapShot(XPathContext context) throws XPathException;
+
+//#endif
+}
+
diff --git a/sf/saxon/expr/sort/GroupMatchingIterator.java b/sf/saxon/expr/sort/GroupMatchingIterator.java
new file mode 100644
index 0000000..0904d49
--- /dev/null
+++ b/sf/saxon/expr/sort/GroupMatchingIterator.java
@@ -0,0 +1,140 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import com.saxonica.stream.ManualGroupIterator;
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ListIterator;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.List;
+
+/**
+ * A GroupMatchingIterator contains code shared between GroupStartingIterator and GroupEndingIterator
+ */
+
+public abstract class GroupMatchingIterator implements LookaheadIterator, GroupIterator {
+
+ protected SequenceIterator population;
+ protected Pattern pattern;
+ protected XPathContext baseContext;
+ protected XPathContext runningContext;
+ protected List currentMembers;
+ /*@Nullable*/ protected Item next;
+ protected Item current = null;
+ protected int position = 0;
+ private int groupSlot = -1;
+
+ public void setGroupSlot(int groupSlot) {
+ this.groupSlot = groupSlot;
+ }
+
+ public void setKeySlot(int keySlot) {
+ // never used for this grouping method
+ }
+
+
+ protected abstract void advance() throws XPathException;
+
+ public AtomicSequence getCurrentGroupingKey() {
+ return null;
+ }
+
+ public SequenceIterator iterateCurrentGroup() {
+ return new ListIterator(currentMembers);
+ }
+
+ public boolean hasCurrentGroup() {
+ return groupSlot < 0;
+ }
+
+ public boolean hasCurrentGroupingKey() {
+ return false;
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ public Item next() throws XPathException {
+ if (next != null) {
+ current = next;
+ position++;
+ advance();
+ if (groupSlot >= 0) {
+ runningContext.setLocalVariable(groupSlot, new SequenceExtent(currentMembers));
+ }
+ return current;
+ } else {
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ population.close();
+ }
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+
+//#if EE==true
+
+ public ManualGroupIterator getSnapShot(XPathContext context) throws XPathException {
+ return new ManualGroupMatchingIterator();
+ }
+
+ private class ManualGroupMatchingIterator extends ManualGroupIterator {
+ List<Item> cMembers = currentMembers;
+ XPathContext savedcontext = runningContext.newMinorContext();
+
+ ManualGroupMatchingIterator() {
+ super(current, position);
+ setLastPositionFinder(new LastPositionFinder<Item>() {
+ public int getLength() throws XPathException {
+ return savedcontext.getLast();
+ }
+ });
+ }
+
+ public AtomicSequence getCurrentGroupingKey() {
+ return null;
+ }
+
+ public SequenceIterator<? extends Item> iterateCurrentGroup() throws XPathException {
+ return new ListIterator(cMembers);
+ }
+
+ public boolean hasCurrentGroupingKey() {
+ return false;
+ }
+
+
+ }
+
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/sort/GroupStartingIterator.java b/sf/saxon/expr/sort/GroupStartingIterator.java
new file mode 100644
index 0000000..a38d191
--- /dev/null
+++ b/sf/saxon/expr/sort/GroupStartingIterator.java
@@ -0,0 +1,62 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+
+import java.util.ArrayList;
+
+/**
+ * A GroupStartingIterator iterates over a sequence of groups defined by
+ * xsl:for-each-group group-starting-with="x". The groups are returned in
+ * order of first appearance.
+ */
+
+public class GroupStartingIterator extends GroupMatchingIterator implements LookaheadIterator, GroupIterator {
+
+ public GroupStartingIterator(SequenceIterator population, Pattern startPattern,
+ XPathContext context)
+ throws XPathException {
+ this.population = population;
+ this.pattern = startPattern;
+ baseContext = context;
+ runningContext = context.newMinorContext();
+ runningContext.setCurrentIterator(population);
+ // the first item in the population always starts a new group
+ next = population.next();
+ }
+
+ protected void advance() throws XPathException {
+ currentMembers = new ArrayList(10);
+ currentMembers.add(current);
+ while (true) {
+ Item nextCandidate = population.next();
+ if (nextCandidate == null) {
+ break;
+ }
+ if (pattern.matches(nextCandidate, runningContext)) {
+ next = nextCandidate;
+ return;
+ } else {
+ currentMembers.add(nextCandidate);
+ }
+ }
+ next = null;
+ }
+
+ /*@NotNull*/ public SequenceIterator getAnother() throws XPathException {
+ return new GroupStartingIterator(population.getAnother(), pattern, baseContext);
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/GroupToBeSorted.java b/sf/saxon/expr/sort/GroupToBeSorted.java
new file mode 100644
index 0000000..acd80a9
--- /dev/null
+++ b/sf/saxon/expr/sort/GroupToBeSorted.java
@@ -0,0 +1,30 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+
+/**
+ * This class is a specialization of ObjectToBeSorted for use when the sequence
+ * being sorted is a sequence of groups. The group is represented by its initial
+ * item, but the object holds in addition the value of the grouping key, and an
+ * iterator over the items in the group.
+ */
+public class GroupToBeSorted extends ObjectToBeSorted<Item> {
+
+ /*@Nullable*/ public AtomicSequence currentGroupingKey;
+ public SequenceIterator currentGroupIterator;
+
+ public GroupToBeSorted(int numberOfSortKeys) {
+ super(numberOfSortKeys);
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/ItemOrderComparer.java b/sf/saxon/expr/sort/ItemOrderComparer.java
new file mode 100644
index 0000000..39b8e41
--- /dev/null
+++ b/sf/saxon/expr/sort/ItemOrderComparer.java
@@ -0,0 +1,31 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.om.Item;
+
+import java.io.Serializable;
+
+/**
+ * A Comparer used for comparing nodes in document order, or items in merge order
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public interface ItemOrderComparer extends Serializable {
+
+ /**
+ * Compare two objects.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ */
+
+ public int compare(Item a, Item b);
+
+}
+
diff --git a/sf/saxon/expr/sort/ItemToBeSorted.java b/sf/saxon/expr/sort/ItemToBeSorted.java
new file mode 100644
index 0000000..62166b7
--- /dev/null
+++ b/sf/saxon/expr/sort/ItemToBeSorted.java
@@ -0,0 +1,23 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.om.Item;
+
+/**
+ * This class is a specialization of class ObjectToBeSorted for use when
+ * the sequence being sorted is a sequence of items (including tuples, which
+ * are represented as items).
+ */
+public class ItemToBeSorted extends ObjectToBeSorted<Item> {
+
+ public ItemToBeSorted(int numberOfSortKeys) {
+ super(numberOfSortKeys);
+ }
+}
+
diff --git a/sf/saxon/expr/sort/LRUCache.java b/sf/saxon/expr/sort/LRUCache.java
new file mode 100644
index 0000000..882f90d
--- /dev/null
+++ b/sf/saxon/expr/sort/LRUCache.java
@@ -0,0 +1,89 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * An LRU cache, based on <code>LinkedHashMap</code>.
+ * Synthesized and simplified from various published examples of the genre.
+ * The methods are not synchronized.
+ */
+public class LRUCache<K,V> {
+
+ private Map<K,V> map;
+
+ /**
+ * Creates a new LRU cache.
+ *
+ * @param cacheSize the maximum number of entries that will be kept in this cache.
+ */
+ public LRUCache(final int cacheSize) {
+ this(cacheSize, false);
+ }
+
+ /**
+ * Creates a new LRU cache, with the option of making it thread-safe
+ *
+ * @param cacheSize the maximum number of entries that will be kept in this cache.
+ * @param concurrent set to true if concurrent access is required, so that access will
+ * be synchronized
+ */
+ public LRUCache(final int cacheSize, boolean concurrent) {
+ map = new LinkedHashMap<K,V>(cacheSize, 0.75f, true) {
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return cacheSize < super.size();
+ }
+ };
+ if (concurrent) {
+ map = Collections.synchronizedMap(map);
+ }
+ }
+
+ /**
+ * Retrieves an entry from the cache.<br>
+ * The retrieved entry becomes the most recently used entry.
+ *
+ * @param key the key whose associated value is to be returned.
+ * @return the value associated to this key, or null if no value with this key exists in the cache.
+ */
+ public V get(K key) {
+ return map.get(key);
+ }
+
+ /**
+ * Adds an entry to this cache.
+ * If the cache is full, the LRU (least recently used) entry is dropped.
+ *
+ * @param key the key with which the specified value is to be associated.
+ * @param value a value to be associated with the specified key.
+ */
+ public void put(K key, V value) {
+ map.put(key, value);
+ }
+
+ /**
+ * Clear the cache
+ */
+ public void clear() {
+ map.clear();
+ }
+
+ /**
+ * Get the number of entries in the cache
+ * @return the number of entries
+ */
+
+ public int size() {
+ return map.size();
+ }
+
+
+}
\ No newline at end of file
diff --git a/sf/saxon/expr/sort/LocalOrderComparer.java b/sf/saxon/expr/sort/LocalOrderComparer.java
new file mode 100644
index 0000000..3361983
--- /dev/null
+++ b/sf/saxon/expr/sort/LocalOrderComparer.java
@@ -0,0 +1,42 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+
+import java.io.Serializable;
+
+/**
+ * A Comparer used for comparing nodes in document order. This
+ * comparer assumes that the nodes being compared come from the same document
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public final class LocalOrderComparer implements ItemOrderComparer, Serializable {
+
+ private static LocalOrderComparer instance = new LocalOrderComparer();
+
+ /**
+ * Get an instance of a LocalOrderComparer. The class maintains no state
+ * so this returns the same instance every time.
+ */
+
+ /*@NotNull*/ public static LocalOrderComparer getInstance() {
+ return instance;
+ }
+
+ public int compare(Item a, Item b) {
+ NodeInfo n1 = (NodeInfo)a;
+ NodeInfo n2 = (NodeInfo)b;
+ return n1.compareOrder(n2);
+ }
+}
+
diff --git a/sf/saxon/expr/sort/NumericComparer.java b/sf/saxon/expr/sort/NumericComparer.java
new file mode 100644
index 0000000..fe28240
--- /dev/null
+++ b/sf/saxon/expr/sort/NumericComparer.java
@@ -0,0 +1,155 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.type.StringToDouble;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.DoubleValue;
+import net.sf.saxon.value.NumericValue;
+
+/**
+ * A Comparer used for comparing sort keys when data-type="number". The items to be
+ * compared are converted to numbers, and the numbers are then compared directly. NaN values
+ * compare equal to each other, and equal to an empty sequence, but less than anything else.
+ * <p/>
+ * This class is used in XSLT only, so there is no need to handle XQuery's "empty least" vs
+ * "empty greatest" options.
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class NumericComparer implements AtomicComparer, java.io.Serializable {
+
+ private static NumericComparer THE_INSTANCE = new NumericComparer();
+ protected StringToDouble converter = StringToDouble.getInstance();
+
+ public static NumericComparer getInstance() {
+ return THE_INSTANCE;
+ }
+
+ protected NumericComparer() {
+ }
+
+ /*@Nullable*/ public StringCollator getCollator() {
+ return null;
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ return this;
+ }
+
+ /**
+ * Compare two Items by converting them to numbers and comparing the numeric values. If either
+ * value cannot be converted to a number, it is treated as NaN, and compares less that the other
+ * (two NaN values compare equal).
+ * @param a the first Item to be compared.
+ * @param b the second Item to be compared.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not Items
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) {
+ double d1, d2;
+
+ if (a instanceof NumericValue) {
+ d1 = ((NumericValue)a).getDoubleValue();
+ } else if (a == null) {
+ d1 = Double.NaN;
+ } else {
+ try {
+ d1 = converter.stringToNumber(a.getStringValueCS());
+ } catch (NumberFormatException err) {
+ d1 = Double.NaN;
+ }
+ }
+
+ if (b instanceof NumericValue) {
+ d2 = ((NumericValue)b).getDoubleValue();
+ } else if (b == null) {
+ d2 = Double.NaN;
+ } else {
+ try {
+ d2 = converter.stringToNumber(b.getStringValueCS());
+ } catch (NumberFormatException err) {
+ d2 = Double.NaN;
+ }
+ }
+
+ if (Double.isNaN(d1)) {
+ if (Double.isNaN(d2)) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ if (Double.isNaN(d2)) {
+ return +1;
+ }
+ if (d1 < d2) return -1;
+ if (d1 > d2) return +1;
+ return 0;
+
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ *
+ * @param a the first object to be compared.
+ * @param b the second object to be compared.
+ * @return true if the values are equal, false if not
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, AtomicValue b) {
+ return compareAtomicValues(a, b) == 0;
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal
+ * according to the XPath eq operator, then their comparison keys are equal according to the Java
+ * equals() method, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) {
+ if (a instanceof NumericValue) {
+ return new ComparisonKey(StandardNames.XS_NUMERIC, toDoubleValue(((NumericValue)a)));
+ } else if (a == null) {
+ return new ComparisonKey(StandardNames.XS_NUMERIC, "NaN");
+ } else {
+ try {
+ double d = converter.stringToNumber(a.getStringValueCS());
+ return new ComparisonKey(StandardNames.XS_NUMERIC, new DoubleValue(d));
+ } catch (NumberFormatException err) {
+ return new ComparisonKey(StandardNames.XS_NUMERIC, "NaN");
+ }
+ }
+ }
+
+ private static DoubleValue toDoubleValue(NumericValue nv) {
+ if (nv instanceof DoubleValue) {
+ return ((DoubleValue)nv);
+ } else {
+ return new DoubleValue(nv.getDoubleValue());
+ }
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/NumericComparer11.java b/sf/saxon/expr/sort/NumericComparer11.java
new file mode 100644
index 0000000..42f6a49
--- /dev/null
+++ b/sf/saxon/expr/sort/NumericComparer11.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.value.StringToDouble11;
+
+/**
+ * A Comparer used for comparing sort keys when data-type="number". The items to be
+ * compared are converted to numbers, and the numbers are then compared directly. NaN values
+ * compare equal to each other, and equal to an empty sequence, but less than anything else.
+ * <p/>
+ * This class is used in XSLT only, so there is no need to handle XQuery's "empty least" vs
+ * "empty greatest" options.
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class NumericComparer11 extends NumericComparer {
+
+ private static NumericComparer11 THE_INSTANCE = new NumericComparer11();
+
+ /*@NotNull*/ public static NumericComparer getInstance() {
+ return THE_INSTANCE;
+ }
+
+ protected NumericComparer11() {
+ converter = StringToDouble11.getInstance();
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/sort/ObjectToBeSorted.java b/sf/saxon/expr/sort/ObjectToBeSorted.java
new file mode 100644
index 0000000..f1a367b
--- /dev/null
+++ b/sf/saxon/expr/sort/ObjectToBeSorted.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * This class represents a member of a sequence that is being sorted. The sequence may contain
+ * items, tuples, groups, or anything else. An instance of this class holds the object itself, the
+ * values of the sort keys, and the original position of the item in the input sequence (which is needed
+ * to achieve stable sorting.)
+ */
+public class ObjectToBeSorted<T> {
+
+ public T value;
+ public AtomicValue[] sortKeyValues;
+ public int originalPosition;
+
+ public ObjectToBeSorted(int numberOfSortKeys) {
+ sortKeyValues = new AtomicValue[numberOfSortKeys];
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/RuleBasedSubstringMatcher.java b/sf/saxon/expr/sort/RuleBasedSubstringMatcher.java
new file mode 100644
index 0000000..495fd52
--- /dev/null
+++ b/sf/saxon/expr/sort/RuleBasedSubstringMatcher.java
@@ -0,0 +1,288 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.lib.SubstringMatcher;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.text.CollationElementIterator;
+import java.text.RuleBasedCollator;
+
+/**
+ * This class wraps a RuleBasedCollator to provide a SubstringMatcher. This
+ * users the facilities offered by the RuleBasedCollator to implement the XPath
+ * functions contains(), starts-with(), ends-with(), substring-before(), and
+ * substring-after().
+ */
+public class RuleBasedSubstringMatcher extends SimpleCollation implements SubstringMatcher {
+
+ //private transient RuleBasedCollator collator;
+
+ /**
+ * Create a RuleBasedSubstringMatcher
+ * @param collator the collation to be used
+ */
+
+ public RuleBasedSubstringMatcher(RuleBasedCollator collator) {
+ super(collator);
+ //this.collator = collator;
+ }
+
+ private RuleBasedCollator getRuleBasedCollator() {
+ return (RuleBasedCollator)getCollation();
+ }
+
+ /**
+ * Test whether one string is equal to another, according to the rules
+ * of the XPath compare() function. The result is true if and only if the
+ * compare() method returns zero: but the implementation may be more efficient
+ * than calling compare and testing the result for zero
+ *
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return true iff s1 equals s2
+ */
+
+// public boolean comparesEqual(String s1, String s2) {
+// return collator.compare(s1, s2) == 0;
+// }
+
+ /**
+ * Test whether one string contains another, according to the rules
+ * of the XPath contains() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return true iff s1 contains s2
+ */
+
+ public boolean contains(String s1, String s2) {
+ RuleBasedCollator collator = getRuleBasedCollator();
+ CollationElementIterator iter1 = collator.getCollationElementIterator(s1);
+ CollationElementIterator iter2 = collator.getCollationElementIterator(s2);
+ return collationContains(iter1, iter2, null, false);
+ }
+
+ /**
+ * Test whether one string ends with another, according to the rules
+ * of the XPath ends-with() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return true iff s1 ends with s2
+ */
+
+ public boolean endsWith(String s1, String s2) {
+ RuleBasedCollator collator = getRuleBasedCollator();
+ CollationElementIterator iter1 = collator.getCollationElementIterator(s1);
+ CollationElementIterator iter2 = collator.getCollationElementIterator(s2);
+ return collationContains(iter1, iter2, null, true);
+ }
+
+ /**
+ * Test whether one string starts with another, according to the rules
+ * of the XPath starts-with() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return true iff s1 starts with s2
+ */
+
+ public boolean startsWith(String s1, String s2) {
+ RuleBasedCollator collator = getRuleBasedCollator();
+ CollationElementIterator iter1 = collator.getCollationElementIterator(s1);
+ CollationElementIterator iter2 = collator.getCollationElementIterator(s2);
+ return collationStartsWith(iter1, iter2);
+ }
+
+ /**
+ * Return the part of a string after a given substring, according to the rules
+ * of the XPath substring-after() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return the part of s1 that follows the first occurrence of s2
+ */
+
+ public String substringAfter(String s1, String s2) {
+ RuleBasedCollator collator = getRuleBasedCollator();
+ CollationElementIterator iter1 = collator.getCollationElementIterator(s1);
+ CollationElementIterator iter2 = collator.getCollationElementIterator(s2);
+ int[] ia = new int[2];
+ boolean ba = collationContains(iter1, iter2, ia, false);
+ if (ba) {
+ return s1.substring(ia[1]);
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * Return the part of a string before a given substring, according to the rules
+ * of the XPath substring-before() function
+ *
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return the part of s1 that precedes the first occurrence of s2
+ */
+
+ public String substringBefore(String s1, String s2) {
+ RuleBasedCollator collator = getRuleBasedCollator();
+ CollationElementIterator iter1 = collator.getCollationElementIterator(s1);
+ CollationElementIterator iter2 = collator.getCollationElementIterator(s2);
+ int[] ib = new int[2];
+ boolean bb = collationContains(iter1, iter2, ib, false);
+ if (bb) {
+ return s1.substring(0, ib[0]);
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * Determine whether one string starts with another, under the terms of a given
+ * collating sequence.
+ * @param s0 iterator over the collation elements of the containing string
+ * @param s1 iterator over the collation elements of the contained string
+ * @return true if the first string starts with the second
+ */
+
+ private boolean collationStartsWith(CollationElementIterator s0,
+ CollationElementIterator s1) {
+ while (true) {
+ int e0, e1;
+ do {
+ e1 = s1.next();
+ } while (e1 == 0);
+ if (e1 == -1) {
+ return true;
+ }
+ do {
+ e0 = s0.next();
+ } while (e0 == 0);
+ if (e0 != e1) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Determine whether one string contains another, under the terms of a given
+ * collating sequence. If matchAtEnd=true, the match must be at the end of the first
+ * string.
+ * @param s0 iterator over the collation elements of the containing string
+ * @param s1 iterator over the collation elements of the contained string
+ * @param offsets may be null, but if it is supplied, it must be an array of two
+ * integers which, if the function returns true, will contain the start position of the
+ * first matching substring, and the offset of the first character after the first
+ * matching substring. This is not available for matchAtEnd=true
+ * @param matchAtEnd true if the match is required to be at the end of the string
+ * @return true if the first string contains the second
+ */
+
+ private boolean collationContains(CollationElementIterator s0,
+ CollationElementIterator s1,
+ /*@Nullable*/ int[] offsets,
+ boolean matchAtEnd) {
+ int e0, e1;
+ do {
+ e1 = s1.next();
+ } while (e1 == 0);
+ if (e1 == -1) {
+ return true;
+ }
+ e0 = -1;
+ while (true) {
+ // scan the first string to find a matching character
+ while (e0 != e1) {
+ do {
+ e0 = s0.next();
+ } while (e0 == 0);
+ if (e0 == -1) {
+ // hit the end, no match
+ return false;
+ }
+ }
+ // matched first character, note the position of the possible match
+ int start = s0.getOffset();
+ if (collationStartsWith(s0, s1)) {
+ if (matchAtEnd) {
+ do {
+ e0 = s0.next();
+ } while (e0 == 0);
+ if (e0 == -1) {
+ // the match is at the end
+ return true;
+ }
+ // else ignore this match and keep looking
+ } else {
+ if (offsets != null) {
+ offsets[0] = start-1;
+ offsets[1] = s0.getOffset();
+ }
+ return true;
+ }
+ }
+ // reset the position and try again
+ s0.setOffset(start);
+
+ // workaround for a difference between JDK 1.4.0 and JDK 1.4.1
+ if (s0.getOffset() != start) {
+ // JDK 1.4.0 takes this path
+ s0.next();
+ }
+ s1.reset();
+ e0 = -1;
+ do {
+ e1 = s1.next();
+ } while (e1 == 0);
+ // loop round to try again
+ }
+ }
+
+
+
+ /**
+ * Get a collation key for two Strings. The essential property of collation keys
+ * is that if two values are equal under the collation, then the collation keys are
+ * compare correctly under the equals() method.
+ */
+
+ public Object getCollationKey(String s) {
+ return getRuleBasedCollator().getCollationKey(s);
+ }
+
+
+ /**
+ * Test program to output the sequence of collation element iterators for a given input string
+ * @param args command line arguments (collationURI, test-string)
+ */
+ public static void main(String[] args) throws Exception {
+ String rules = " ='-'='*'< a < b < c < d < e < f < g < h < i < j < k < l < m < n < o < p < q < r < s < t < u < v < w < x < y < z";
+ RuleBasedCollator collator = new RuleBasedCollator(rules);
+
+ for (int i=0; i<args.length; i++) {
+ System.err.println(args[i]);
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ CollationElementIterator iter = collator.getCollationElementIterator(args[i]);
+ while (true) {
+ int e = iter.next();
+ if (e==-1) {
+ break;
+ }
+ sb.append(e+" ");
+ }
+ System.err.println(sb.toString());
+ }
+
+
+ }
+
+
+}
+
diff --git a/sf/saxon/expr/sort/SimpleCollation.java b/sf/saxon/expr/sort/SimpleCollation.java
new file mode 100644
index 0000000..46998c6
--- /dev/null
+++ b/sf/saxon/expr/sort/SimpleCollation.java
@@ -0,0 +1,93 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Platform;
+import net.sf.saxon.lib.StringCollator;
+
+import java.util.Comparator;
+
+/**
+ * A simple collation that just wraps a suppled Comparator
+ */
+
+public class SimpleCollation implements StringCollator {
+
+ private Comparator collation;
+ /*@NotNull*/ private static Platform platform = Configuration.getPlatform();
+
+ /**
+ * Create a SimpleCollation
+ * @param collation the Comparator that does the actual string comparison
+ */
+
+ public SimpleCollation(Comparator collation) {
+ this.collation = collation;
+ }
+
+ /**
+ * Compares its two arguments for order. Returns a negative integer,
+ * zero, or a positive integer as the first argument is less than, equal
+ * to, or greater than the second.<p>
+ * <p/>
+
+ * @param o1 the first object to be compared.
+ * @param o2 the second object to be compared.
+ * @return a negative integer, zero, or a positive integer as the
+ * first argument is less than, equal to, or greater than the
+ * second.
+ * @throws ClassCastException if the arguments' types prevent them from
+ * being compared by this Comparator.
+ */
+ public int compareStrings(String o1, String o2) {
+ return collation.compare(o1, o2);
+ }
+
+ /**
+ * Compare two strings for equality. This may be more efficient than using compareStrings and
+ * testing whether the result is zero, but it must give the same result
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return true if and only if the strings are considered equal,
+ */
+
+ public boolean comparesEqual(String s1, String s2) {
+ return collation.compare(s1, s2) == 0;
+ }
+
+ /**
+ * Get the underlying comparator
+ * @return the underlying comparator
+ */
+
+ public Comparator getCollation() {
+ return collation;
+ }
+
+ /**
+ * Set the underlying comparator
+ * @param collation the underlying comparator
+ */
+
+ public void setCollation(Comparator collation) {
+ this.collation = collation;
+ }
+
+ /**
+ * Get a collation key for a String. The essential property of collation keys
+ * is that if two values are equal under the collation, then the collation keys are
+ * compare correctly under the equals() method.
+ */
+
+ public Object getCollationKey(String s) {
+ return platform.getCollationKey(this, s);
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/SortExpression.java b/sf/saxon/expr/sort/SortExpression.java
new file mode 100644
index 0000000..0b748ba
--- /dev/null
+++ b/sf/saxon/expr/sort/SortExpression.java
@@ -0,0 +1,609 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SortExpressionCompiler;
+import com.saxonica.stream.adjunct.StreamingAdjunct;
+import com.saxonica.stream.adjunct.UnsupportedOperationAdjunct;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Expression equivalent to the imaginary syntax
+ * expr sortby (sort-key)+
+ */
+
+public class SortExpression extends Expression
+ implements SortKeyEvaluator {
+
+ /*@Nullable*/ private Expression select = null;
+ private SortKeyDefinition[] sortKeyDefinitions = null;
+ private transient AtomicComparer[] comparators = null;
+ // created early if all comparators can be created statically
+ // transient because Java RuleBasedCollator is not serializable
+
+ /**
+ * Create a sort expression
+ * @param select the expression whose result is to be sorted
+ * @param sortKeys the set of sort key definitions to be used, in major to minor order
+ */
+
+ public SortExpression(Expression select, SortKeyDefinition[] sortKeys) {
+ this.select = select;
+ sortKeyDefinitions = sortKeys;
+ Iterator children = iterateSubExpressions();
+ while (children.hasNext()) {
+ Expression exp = (Expression) children.next();
+ adoptChildExpression(exp);
+ }
+ }
+
+ /**
+ * Get a name identifying the kind of expression, in terms meaningful to a user.
+ * @return a name identifying the kind of expression, in terms meaningful to a user.
+ * The name will always be in the form of a lexical XML QName, and should match the name used
+ * in explain() output displaying the expression.
+ */
+
+ public String getExpressionName() {
+ return "sort";
+ }
+
+ /**
+ * Get the expression defining the sequence being sorted
+ * @return the expression whose result is to be sorted
+ */
+
+ public Expression getBaseExpression() {
+ return select;
+ }
+
+ /**
+ * Get the sort key definitions
+ * @return the sort key definitions, one per sort key
+ */
+
+ public SortKeyDefinition[] getSortKeyDefinitions() {
+ return sortKeyDefinitions;
+ }
+
+ /**
+ * Get the comparators, if known statically. Otherwise, return null.
+ * @return The comparators, if they have been allocated; otherwise null
+ */
+
+ public AtomicComparer[] getComparators() {
+ return comparators;
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return iterateSubExpressions(true);
+ }
+
+ private Iterator<Expression> iterateSubExpressions(boolean includeSortKey) {
+ List<Expression> list = new ArrayList<Expression>(8);
+ list.add(select);
+ for (SortKeyDefinition sortKeyDefinition : sortKeyDefinitions) {
+ if (includeSortKey || sortKeyDefinition.isSetContextForSortKey()) {
+ list.add(sortKeyDefinition.getSortKey());
+ }
+ Expression e = sortKeyDefinition.order;
+ if (e != null) {
+ list.add(e);
+ }
+ e = sortKeyDefinition.caseOrder;
+ if (e != null) {
+ list.add(e);
+ }
+ e = sortKeyDefinition.dataTypeExpression;
+ if (e != null) {
+ list.add(e);
+ }
+ e = sortKeyDefinition.language;
+ if (e != null) {
+ list.add(e);
+ }
+ e = sortKeyDefinition.collationName;
+ if (e != null) {
+ list.add(e);
+ }
+ e = sortKeyDefinition.stable;
+ if (e != null) {
+ list.add(e);
+ }
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ List<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>(8);
+ list.add(new SubExpressionInfo(select, true, false, INHERITED_CONTEXT));
+ for (SortKeyDefinition sortKeyDefinition : sortKeyDefinitions) {
+ list.add(new SubExpressionInfo(sortKeyDefinition.getSortKey(), !sortKeyDefinition.isSetContextForSortKey(), true, NODE_VALUE_CONTEXT));
+ Expression e = sortKeyDefinition.order;
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ e = sortKeyDefinition.caseOrder;
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ e = sortKeyDefinition.dataTypeExpression;
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ e = sortKeyDefinition.language;
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ e = sortKeyDefinition.collationName;
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ e = sortKeyDefinition.stable;
+ if (e != null) {
+ list.add(new SubExpressionInfo(e, true, false, NODE_VALUE_CONTEXT));
+ }
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet target = select.addToPathMap(pathMap, pathMapNodeSet);
+ if (sortKeyDefinitions != null) {
+ for (SortKeyDefinition sortKeyDefinition : sortKeyDefinitions) {
+ if (sortKeyDefinition.isSetContextForSortKey()) {
+ sortKeyDefinition.getSortKey().addToPathMap(pathMap, target);
+ } else {
+ sortKeyDefinition.getSortKey().addToPathMap(pathMap, pathMapNodeSet);
+ }
+ Expression e = sortKeyDefinition.getOrder();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ e = sortKeyDefinition.getCaseOrder();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ e = sortKeyDefinition.getDataTypeExpression();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ e = sortKeyDefinition.getLanguage();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ e = sortKeyDefinition.getCollationNameExpression();
+ if (e != null) {
+ e.addToPathMap(pathMap, pathMapNodeSet);
+ }
+ }
+ }
+ return target;
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ if (select == original) {
+ select = replacement;
+ found = true;
+ }
+ for (SortKeyDefinition sortKeyDefinition : sortKeyDefinitions) {
+ if (sortKeyDefinition.getSortKey() == original) {
+ sortKeyDefinition.setSortKey(replacement, true);
+ found = true;
+ }
+ if (sortKeyDefinition.getOrder() == original) {
+ sortKeyDefinition.setOrder(replacement);
+ found = true;
+ }
+ if (sortKeyDefinition.getCaseOrder() == original) {
+ sortKeyDefinition.setCaseOrder(replacement);
+ found = true;
+ }
+ if (sortKeyDefinition.getDataTypeExpression() == original) {
+ sortKeyDefinition.setDataTypeExpression(replacement);
+ found = true;
+ }
+ if (sortKeyDefinition.getLanguage() == original) {
+ sortKeyDefinition.setLanguage(replacement);
+ found = true;
+ }
+ if (sortKeyDefinition.collationName == original) {
+ sortKeyDefinition.collationName = replacement;
+ found = true;
+ }
+ if (sortKeyDefinition.stable == original) {
+ sortKeyDefinition.stable = replacement;
+ found = true;
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Simplify an expression
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ select = visitor.simplify(select);
+ return this;
+ }
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression select2 = visitor.typeCheck(select, contextItemType);
+ if (select2 != select) {
+ adoptChildExpression(select2);
+ select = select2;
+ }
+ ItemType sortedItemType = select.getItemType(visitor.getConfiguration().getTypeHierarchy());
+
+ boolean allKeysFixed = true;
+ for (SortKeyDefinition sortKeyDefinition : sortKeyDefinitions) {
+ if (!(sortKeyDefinition.isFixed())) {
+ allKeysFixed = false;
+ break;
+ }
+ }
+
+ if (allKeysFixed) {
+ comparators = new AtomicComparer[sortKeyDefinitions.length];
+ }
+
+ for (int i = 0; i < sortKeyDefinitions.length; i++) {
+ Expression sortKey = sortKeyDefinitions[i].getSortKey();
+ if (sortKeyDefinitions[i].isSetContextForSortKey()) {
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(sortedItemType, false);
+ sortKey = visitor.typeCheck(sortKey, cit);
+ } else {
+ sortKey = visitor.typeCheck(sortKey, contextItemType);
+ }
+ if (visitor.getStaticContext().isInBackwardsCompatibleMode()) {
+ sortKey = FirstItemExpression.makeFirstItemExpression(sortKey);
+ } else {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:sort/select", 0);
+ role.setErrorCode("XTTE1020");
+ sortKey = TypeChecker.staticTypeCheck(sortKey, SequenceType.OPTIONAL_ATOMIC, false, role, visitor);
+ //sortKey = CardinalityChecker.makeCardinalityChecker(sortKey, StaticProperty.ALLOWS_ZERO_OR_ONE, role);
+ }
+ sortKeyDefinitions[i].setSortKey(sortKey, sortKeyDefinitions[i].isSetContextForSortKey());
+ sortKeyDefinitions[i].typeCheck(visitor, contextItemType);
+ if (sortKeyDefinitions[i].isFixed()) {
+ AtomicComparer comp = sortKeyDefinitions[i].makeComparator(
+ visitor.getStaticContext().makeEarlyEvaluationContext());
+ sortKeyDefinitions[i].setFinalComparator(comp);
+ if (allKeysFixed) {
+ comparators[i] = comp;
+ }
+ }
+ if (sortKeyDefinitions[i].isSetContextForSortKey() && !ExpressionTool.dependsOnFocus(sortKey)) {
+ visitor.getStaticContext().issueWarning(
+ "Sort key will have no effect because its value does not depend on the context item",
+ sortKey);
+ }
+
+ }
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression select2 = visitor.optimize(select, contextItemType);
+ if (select2 != select) {
+ adoptChildExpression(select2);
+ select = select2;
+ }
+ // optimize the sort keys
+ ExpressionVisitor.ContextItemType cit;
+ if (sortKeyDefinitions[0].isSetContextForSortKey()) {
+ ItemType sortedItemType = select.getItemType(visitor.getConfiguration().getTypeHierarchy());
+ cit = new ExpressionVisitor.ContextItemType(sortedItemType, false);
+ } else {
+ cit = contextItemType;
+ }
+ for (SortKeyDefinition sortKeyDefinition : sortKeyDefinitions) {
+ Expression sortKey = sortKeyDefinition.getSortKey();
+ sortKey = visitor.optimize(sortKey, cit);
+ sortKeyDefinition.setSortKey(sortKey, true);
+ }
+ if (Cardinality.allowsMany(select.getCardinality())) {
+ return this;
+ } else {
+ return select;
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ SortKeyDefinition[] sk2 = new SortKeyDefinition[sortKeyDefinitions.length];
+ for (int i=0; i<sortKeyDefinitions.length; i++) {
+ sk2[i] = sortKeyDefinitions[i].copy();
+ }
+ SortExpression se2 = new SortExpression(select.copy(), sk2);
+ se2.comparators = comparators;
+ return se2;
+ }
+
+ /**
+ * Offer promotion for this subexpression. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent the parent of this expression in the expression tree
+ * @return if the offer is not accepted, return this expression unchanged.
+ * Otherwise return the result of rewriting the expression to promote
+ * this subexpression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Expression exp = offer.accept(parent, this);
+ if (exp != null) {
+ return exp;
+ } else {
+ select = doPromotion(select, offer);
+ for (SortKeyDefinition sortKeyDefinition : sortKeyDefinitions) {
+ final Expression sk2 = sortKeyDefinition.getSortKey().promote(offer, parent);
+ sortKeyDefinition.setSortKey(sk2, true);
+ if (sortKeyDefinition.order != null) {
+ sortKeyDefinition.order = sortKeyDefinition.order.promote(offer, parent);
+ }
+ if (sortKeyDefinition.stable != null) {
+ sortKeyDefinition.stable = sortKeyDefinition.stable.promote(offer, parent);
+ }
+ if (sortKeyDefinition.caseOrder != null) {
+ sortKeyDefinition.caseOrder = sortKeyDefinition.caseOrder.promote(offer, parent);
+ }
+ if (sortKeyDefinition.dataTypeExpression != null) {
+ sortKeyDefinition.dataTypeExpression = sortKeyDefinition.dataTypeExpression.promote(offer, parent);
+ }
+ if (sortKeyDefinition.language != null) {
+ sortKeyDefinition.language = sortKeyDefinition.language.promote(offer, parent);
+ }
+ if (sortKeyDefinition.collationName != null) {
+ sortKeyDefinition.collationName = sortKeyDefinition.collationName.promote(offer, parent);
+ }
+ }
+ return this;
+ }
+ }
+
+ /**
+ * Test whether a given expression is one of the sort keys
+ * @param child the given expression
+ * @return true if the given expression is one of the sort keys
+ */
+
+ public boolean isSortKey(Expression child) {
+ for (SortKeyDefinition sortKeyDefinition : sortKeyDefinitions) {
+ Expression exp = sortKeyDefinition.getSortKey();
+ if (exp == child) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine the static cardinality
+ */
+
+ public int computeCardinality() {
+ return select.getCardinality();
+ }
+
+ /**
+ * Determine the data type of the items returned by the expression, if possible
+ *
+ * @param th the type hierarchy cache
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER, Type.NODE,
+ * or Type.ITEM (meaning not known in advance)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return select.getItemType(th);
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-significant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ int props = 0;
+ if ((select.getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
+ props |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
+ }
+ if ((select.getSpecialProperties() & StaticProperty.SINGLE_DOCUMENT_NODESET) != 0) {
+ props |= StaticProperty.SINGLE_DOCUMENT_NODESET;
+ }
+ if ((select.getSpecialProperties() & StaticProperty.NON_CREATIVE) != 0) {
+ props |= StaticProperty.NON_CREATIVE;
+ }
+ return props;
+ }
+
+ /**
+ * Enumerate the results of the expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+
+ SequenceIterator iter = select.iterate(context);
+ if (iter instanceof EmptyIterator) {
+ return iter;
+ }
+
+ AtomicComparer[] comps = comparators;
+ if (comparators == null) {
+ comps = new AtomicComparer[sortKeyDefinitions.length];
+ for (int s = 0; s < sortKeyDefinitions.length; s++) {
+ AtomicComparer comp = sortKeyDefinitions[s].getFinalComparator();
+ if (comp == null) {
+ comp = sortKeyDefinitions[s].makeComparator(context);
+ }
+ comps[s] = comp;
+ }
+ }
+ iter = new SortedIterator(context, iter, this, comps, sortKeyDefinitions[0].isSetContextForSortKey());
+ ((SortedIterator) iter).setHostLanguage(getHostLanguage());
+ return iter;
+ }
+
+ /**
+ * Callback for evaluating the sort keys
+ */
+
+ public AtomicValue evaluateSortKey(int n, XPathContext c) throws XPathException {
+ return (AtomicValue)sortKeyDefinitions[n].getSortKey().evaluateItem(c);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Sort expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SortExpressionCompiler();
+ }
+//#endif
+
+//#ifdefined STREAM
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public StreamingAdjunct getStreamingAdjunct() {
+ return new UnsupportedOperationAdjunct();
+ }
+
+//#endif
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("sort");
+ out.startSubsidiaryElement("select");
+ select.explain(out);
+ out.endSubsidiaryElement();
+ for (SortKeyDefinition sortKeyDefinition : sortKeyDefinitions) {
+ out.startSubsidiaryElement("by");
+ sortKeyDefinition.getSortKey().explain(out);
+ out.endSubsidiaryElement();
+ }
+ out.endElement();
+ }
+}
+
diff --git a/sf/saxon/expr/sort/SortKeyDefinition.java b/sf/saxon/expr/sort/SortKeyDefinition.java
new file mode 100644
index 0000000..f1c74ec
--- /dev/null
+++ b/sf/saxon/expr/sort/SortKeyDefinition.java
@@ -0,0 +1,621 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.flwor.ExpressionProcessor;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.StringConverter;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.type.ValidationFailure;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Properties;
+
+/**
+ * A SortKeyDefinition defines one component of a sort key. <BR>
+ *
+ * Note that most attributes defining the sort key can be attribute value templates,
+ * and can therefore vary from one invocation to another. We hold them as expressions.
+ */
+
+// TODO: optimise also for the case where the attributes depend only on global variables
+// or parameters, in which case the same AtomicComparer can be used for the duration of a
+// transformation.
+
+// TODO: at present the SortKeyDefinition is evaluated to obtain a AtomicComparer, which can
+// be used to compare two sort keys. It would be more efficient to use a Collator to
+// obtain collation keys for all the items to be sorted, as these can be compared more
+// efficiently.
+
+
+public class SortKeyDefinition implements Serializable {
+
+ private static StringLiteral defaultOrder = new StringLiteral("ascending");
+ private static StringLiteral defaultCaseOrder = new StringLiteral("#default");
+ private static StringLiteral defaultLanguage = new StringLiteral(StringValue.EMPTY_STRING);
+
+ /*@Nullable*/ protected Expression sortKey;
+ protected Expression order = defaultOrder;
+ protected Expression dataTypeExpression = null;
+ // used when the type is not known till run-time
+ protected Expression caseOrder = defaultCaseOrder;
+ protected Expression language = defaultLanguage;
+ protected Expression collationName = null;
+ protected Expression stable = null; // not actually used, but present so it can be validated
+ protected StringCollator collation;
+ protected String baseURI; // needed in case collation URI is relative
+ protected boolean emptyLeast = true;
+ protected boolean backwardsCompatible = false;
+ protected boolean setContextForSortKey = false;
+
+ private transient AtomicComparer finalComparator = null;
+ // Note, the "collation" defines the collating sequence for the sort key. The
+ // "finalComparator" is what is actually used to do comparisons, after taking into account
+ // ascending/descending, caseOrder, etc.
+
+ // The comparer is transient because a RuleBasedCollator is not serializable. This means that
+ // when a stylesheet is compiled, the finalComparator is discarded, which means a new finalComparator will be
+ // constructed for each sort at run-time.
+
+ /**
+ * Set the expression used as the sort key
+ * @param exp the sort key select expression
+ * @param setContext set to true if the sort key is to be evaluated with the
+ * item-being-sorted as the context item (as in XSLT); false if the context item
+ * is not to be set (as in XQuery)
+ */
+
+ public void setSortKey(Expression exp, boolean setContext) {
+ sortKey = exp;
+ setContextForSortKey = setContext;
+ }
+
+ /**
+ * Get the expression used as the sort key
+ * @return the sort key select expression
+ */
+
+ public Expression getSortKey() {
+ return sortKey;
+ }
+
+ /**
+ * Ask whether the sortkey is to be evaluated with the item-being-sorted
+ * as the context item
+ * @return true if the context needs to be set for evaluating the sort key
+ */
+
+ public boolean isSetContextForSortKey() {
+ return setContextForSortKey;
+ }
+
+
+ /**
+ * Set the order. This is supplied as an expression which must evaluate to "ascending"
+ * or "descending". If the order is fixed, supply e.g. new StringValue("ascending").
+ * Default is "ascending".
+ * @param exp the expression that determines the order (always a literal in XQuery, but
+ * can be defined by an AVT in XSLT)
+ */
+
+ public void setOrder(Expression exp) {
+ order = exp;
+ }
+
+ /**
+ * Get the expression that defines the order as ascending or descending
+ * @return the expression that determines the order (always a literal in XQuery, but
+ * can be defined by an AVT in XSLT)
+ */
+
+ public Expression getOrder() {
+ return order;
+ }
+
+ /**
+ * Set the data type. This is supplied as an expression which must evaluate to "text",
+ * "number", or a QName. If the data type is fixed, the valus should be supplied using
+ * setDataType() and not via this method.
+ * @param exp the expression that defines the data type, as used in XSLT 1.0
+ */
+
+ public void setDataTypeExpression(Expression exp) {
+ dataTypeExpression = exp;
+ }
+
+ /**
+ * Get the expression that defines the data type of the sort keys
+ * @return the expression that defines the data type, as used in XSLT 1.0
+ */
+
+ public Expression getDataTypeExpression() {
+ return dataTypeExpression;
+ }
+
+ /**
+ * Set the case order. This is supplied as an expression which must evaluate to "upper-first"
+ * or "lower-first" or "#default". If the order is fixed, supply e.g. new StringValue("lower-first").
+ * Default is "#default".
+ * @param exp the expression that defines the case order
+ */
+
+ public void setCaseOrder(Expression exp) {
+ caseOrder = exp;
+ }
+
+ /**
+ * Get the expression that defines the case order of the sort keys.
+ * @return the expression that defines the case order, whose run-time value will be "upper-first",
+ * "lower-first", or "#default".
+ */
+
+ public Expression getCaseOrder() {
+ return caseOrder;
+ }
+
+ /**
+ * Set the language. This is supplied as an expression which evaluates to the language name.
+ * If the order is fixed, supply e.g. new StringValue("de").
+ * @param exp the expression that determines the language
+ */
+
+ public void setLanguage(Expression exp) {
+ language = exp;
+ }
+
+ /**
+ * Get the expression that defines the language of the sort keys
+ * @return exp the expression that determines the language
+ */
+
+ public Expression getLanguage() {
+ return language;
+ }
+
+ /**
+ * Set the collation name (specifically, an expression which when evaluated returns the collation URI).
+ * @param collationName the expression that determines the collation name
+ */
+
+ public void setCollationNameExpression(Expression collationName) {
+ this.collationName = collationName;
+ }
+
+ /**
+ * Get the selected collation name
+ * (specifically, an expression which when evaluated returns the collation URI).
+ * @return the expression that determines the collation name
+ */
+
+ public Expression getCollationNameExpression() {
+ return collationName;
+ }
+
+ /**
+ * Set the collation to be used
+ * @param collation A StringCollator, which encapsulates both the collation URI and the collating function
+ */
+
+ public void setCollation(StringCollator collation) {
+ this.collation = collation;
+ }
+
+ /**
+ * Get the collation to be used
+ * @return A StringCollator, which encapsulates both the collation URI and the collating function
+ */
+
+ public StringCollator getCollation() {
+ return collation;
+ }
+
+ /**
+ * Set the base URI of the expression. This is needed to handle the case where a collation URI
+ * evaluated at run-time turns out to be a relative URI.
+ * @param baseURI the static base URI of the expression
+ */
+
+ public void setBaseURI(String baseURI) {
+ this.baseURI = baseURI;
+ }
+
+ /**
+ * Get the static base URI of the expression. This is needed to handle the case where a collation URI
+ * evaluated at run-time turns out to be a relative URI.
+ * @return the static base URI of the expression
+ */
+
+ public String getBaseURI() {
+ return baseURI;
+ }
+
+ /**
+ * Set whether this sort key definition is stable
+ * @param stable the expression that determines whether the sort key definition is stable
+ * (it evaluates to the string "yes" or "no".
+ */
+
+ public void setStable(Expression stable) {
+ this.stable = stable;
+ }
+
+ /**
+ * Ask whether this sort key definition is stable
+ * @return the expression that determines whether the sort key definition is stable
+ * (it evaluates to the string "yes" or "no".
+ */
+
+ public Expression getStable() {
+ return stable;
+ }
+
+ /**
+ * Set whether this sort key is evaluated in XSLT 1.0 backwards compatibility mode
+ * @param compatible true if backwards compatibility mode is selected
+ */
+
+ public void setBackwardsCompatible(boolean compatible) {
+ backwardsCompatible = compatible;
+ }
+
+ /**
+ * Ask whether this sort key is evaluated in XSLT 1.0 backwards compatibility mode
+ * @return true if backwards compatibility mode was selected
+ */
+
+ public boolean isBackwardsCompatible() {
+ return backwardsCompatible;
+ }
+
+ /**
+ * Set whether empty sequence comes before other values or after them
+ * @param emptyLeast true if () is considered lower than any other value
+ */
+
+ public void setEmptyLeast(boolean emptyLeast) {
+ this.emptyLeast = emptyLeast;
+ }
+
+ /**
+ * Ask whether empty sequence comes before other values or after them
+ * @return true if () is considered lower than any other value
+ */
+
+ public boolean getEmptyLeast() {
+ return emptyLeast;
+ }
+
+ /**
+ * Ask whether the sort key definition is fixed, that is, whether all the information needed
+ * to create a Comparator is known statically
+ * @return true if all information needed to create a Comparator is known statically
+ */
+
+ public boolean isFixed() {
+ return (order instanceof Literal &&
+ (dataTypeExpression == null ||
+ dataTypeExpression instanceof Literal) &&
+ caseOrder instanceof Literal &&
+ language instanceof Literal &&
+ (stable == null || stable instanceof Literal) &&
+ (collationName == null || collationName instanceof Literal));
+ }
+
+
+
+ /**
+ * Simplify this sort key definition
+ * @param visitor the expression visitor
+ * @return the simplified sort key definition
+ * @throws XPathException if any failure occurs
+ */
+
+ public SortKeyDefinition simplify(ExpressionVisitor visitor) throws XPathException {
+ sortKey = visitor.simplify(sortKey);
+ order = visitor.simplify(order);
+ dataTypeExpression = visitor.simplify(dataTypeExpression);
+ caseOrder = visitor.simplify(caseOrder);
+ language = visitor.simplify(language);
+ stable = visitor.simplify(stable);
+ collationName = visitor.simplify(collationName);
+ return this;
+ }
+
+ /**
+ * Copy this SortKeyDefinition
+ */
+
+ public SortKeyDefinition copy() {
+ SortKeyDefinition sk2 = new SortKeyDefinition();
+ sk2.setSortKey(copy(sortKey), true);
+ sk2.setOrder(copy(order));
+ sk2.setDataTypeExpression(copy(dataTypeExpression));
+ sk2.setCaseOrder(copy(caseOrder));
+ sk2.setLanguage(copy(language));
+ sk2.setStable(copy(stable));
+ sk2.setCollationNameExpression(copy(collationName));
+ sk2.collation = collation;
+ sk2.emptyLeast = emptyLeast;
+ sk2.baseURI = baseURI;
+ sk2.backwardsCompatible = backwardsCompatible;
+ sk2.finalComparator = finalComparator;
+ sk2.setContextForSortKey = setContextForSortKey;
+ return sk2;
+ }
+
+ private Expression copy(Expression in) {
+ return (in == null ? null : in.copy());
+ }
+
+ /**
+ * Type-check this sort key definition (all properties other than the sort key
+ * select expression, when it has a different dynamic context)
+ * @param visitor the expression visitor
+ * @param contextItemType the type of the context item
+ * @throws XPathException if any failure occurs
+ */
+
+ public void typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ order = visitor.typeCheck(order, contextItemType);
+ dataTypeExpression = visitor.typeCheck(dataTypeExpression, contextItemType);
+ caseOrder = visitor.typeCheck(caseOrder, contextItemType);
+ language = visitor.typeCheck(language, contextItemType);
+ stable = visitor.typeCheck(stable, contextItemType);
+ collationName = visitor.typeCheck(collationName, contextItemType);
+ if (!setContextForSortKey) {
+ sortKey = visitor.typeCheck(sortKey, contextItemType);
+ }
+
+ if (language instanceof StringLiteral && ((StringLiteral)language).getStringValue().length() != 0) {
+ ValidationFailure vf = StringConverter.STRING_TO_LANGUAGE.validate(((StringLiteral) language).getStringValue());
+ if (vf != null) {
+ throw new XPathException("The lang attribute of xsl:sort must be a valid language code", "XTDE0030");
+ }
+ }
+ }
+
+ /**
+ * Process the subexpressions of this clause
+ *
+ * @param processor the expression processor used to process the subexpressions
+ */
+
+ public void processSubExpressions(ExpressionProcessor processor) throws XPathException {
+ order = processor.processExpression(order);
+ if (dataTypeExpression != null) {
+ dataTypeExpression = processor.processExpression(dataTypeExpression);
+ }
+ if (caseOrder != null) {
+ caseOrder = processor.processExpression(caseOrder);
+ }
+ if (language != null) {
+ language = processor.processExpression(language);
+ }
+ if (stable != null) {
+ stable = processor.processExpression(stable);
+ }
+ if (collationName != null) {
+ collationName = processor.processExpression(collationName);
+ }
+ sortKey = processor.processExpression(sortKey);
+ }
+
+ /**
+ * Allocate an AtomicComparer to perform the comparisons described by this sort key component.
+ * The AtomicComparer takes into account not only the collation, but also parameters
+ * such as order=descending and handling of empty sequence and NaN (the result of the compare()
+ * method of the comparator is +1 if the second item is to sort after the first item).
+ * The AtomicComparer is allocated at compile time if possible (during typeCheck), otherwise
+ * at run-time.
+ * @param context the dynamic evaluation context
+ * @return an AtomicComparer suitable for making the sort comparisons
+ */
+
+ public AtomicComparer makeComparator(XPathContext context) throws XPathException {
+
+ String orderX = order.evaluateAsString(context).toString();
+
+ final Configuration config = context.getConfiguration();
+ final TypeHierarchy th = config.getTypeHierarchy();
+
+ AtomicComparer atomicComparer;
+ StringCollator stringCollator;
+ if (collation != null) {
+ stringCollator = collation;
+ } else if (collationName != null) {
+ String cname = collationName.evaluateAsString(context).toString();
+ URI collationURI;
+ try {
+ collationURI = new URI(cname);
+ if (!collationURI.isAbsolute()) {
+ if (baseURI == null) {
+ throw new XPathException("Collation URI is relative, and base URI is unknown");
+ } else {
+ URI base = new URI(baseURI);
+ collationURI = base.resolve(collationURI);
+ }
+ }
+ } catch (URISyntaxException err) {
+ throw new XPathException("Collation name " + cname + " is not a valid URI: " + err);
+ }
+ try {
+ stringCollator = context.getCollation(collationURI.toString());
+ } catch (XPathException e) {
+ if ("FOCH0002".equals(e.getErrorCodeLocalPart())) {
+ e.setErrorCode("XTDE1035");
+ }
+ throw e;
+ }
+ } else {
+ String caseOrderX = caseOrder.evaluateAsString(context).toString();
+ String languageX = language.evaluateAsString(context).toString();
+ Properties props = new Properties();
+ if (languageX.length() != 0) {
+ ValidationFailure vf = StringConverter.STRING_TO_LANGUAGE.validate(languageX);
+ if (vf != null) {
+ throw new XPathException("The lang attribute of xsl:sort must be a valid language code", "XTDE0030");
+ }
+ props.setProperty("lang", languageX);
+ }
+ if (!caseOrderX.equals("#default")) {
+ props.setProperty("case-order", caseOrderX);
+ }
+ stringCollator = Configuration.getPlatform().makeCollation(config, props, "");
+ // TODO: build a URI allowing the collation to be reconstructed
+ }
+
+
+
+ if (dataTypeExpression==null) {
+ atomicComparer = AtomicSortComparer.makeSortComparer(stringCollator,
+ sortKey.getItemType(th).getAtomizedItemType().getPrimitiveType(), context);
+ if (!emptyLeast) {
+ atomicComparer = new EmptyGreatestComparer(atomicComparer);
+ }
+ } else {
+ String dataType = dataTypeExpression.evaluateAsString(context).toString();
+ if (dataType.equals("text")) {
+ atomicComparer = AtomicSortComparer.makeSortComparer(stringCollator,
+ StandardNames.XS_STRING, context);
+ atomicComparer = new TextComparer(atomicComparer);
+ } else if (dataType.equals("number")) {
+ if (context.getConfiguration().getXsdVersion() == Configuration.XSD10) {
+ atomicComparer = NumericComparer.getInstance();
+ } else {
+ atomicComparer = NumericComparer11.getInstance();
+ }
+ } else {
+ XPathException err = new XPathException("data-type on xsl:sort must be 'text' or 'number'");
+ err.setErrorCode("XTDE0030");
+ throw err;
+ }
+ }
+
+ if (stable != null) {
+ StringValue stableVal = (StringValue)stable.evaluateItem(context);
+ String s = Whitespace.trim(stableVal.getStringValue());
+ if (s.equals("yes") || s.equals("no")) {
+ // no action
+ } else {
+ XPathException err = new XPathException("Value of 'stable' on xsl:sort must be 'yes' or 'no'");
+ err.setErrorCode("XTDE0030");
+ throw err;
+ }
+ }
+
+ if (orderX.equals("ascending")) {
+ return atomicComparer;
+ } else if (orderX.equals("descending")) {
+ return new DescendingComparer(atomicComparer);
+ } else {
+ XPathException err1 = new XPathException("order must be 'ascending' or 'descending'");
+ err1.setErrorCode("XTDE0030");
+ throw err1;
+ }
+ }
+
+ /**
+ * Set the comparator which is used to compare two values according to this sort key. The comparator makes the final
+ * decision whether one value sorts before or after another: this takes into account the data type, the collation,
+ * whether empty comes first or last, whether the sort order is ascending or descending.
+ *
+ * <p>This method is called at compile time if all these factors are known at compile time.
+ * It must not be called at run-time, except to reconstitute a finalComparator that has been
+ * lost by virtue of serialization .</p>
+ * @param comp the Atomic Comparer to be used
+ */
+
+ public void setFinalComparator(AtomicComparer comp) {
+ finalComparator = comp;
+ }
+
+ /**
+ * Get the comparator which is used to compare two values according to this sort key. This method
+ * may be called either at compile time or at run-time. If no comparator has been allocated,
+ * it returns null. It is then necessary to allocate a comparator using the {@link #makeComparator}
+ * method.
+ * @return the Atomic Comparer to be used
+ */
+
+ public AtomicComparer getFinalComparator() {
+ return finalComparator;
+ }
+
+ public SortKeyDefinition fix(XPathContext context) throws XPathException{
+ SortKeyDefinition newSKD = this.copy();
+
+ newSKD.setLanguage(new StringLiteral(this.getLanguage().evaluateAsString(context)));
+ newSKD.setOrder(new StringLiteral(this.getOrder().evaluateAsString(context)));
+
+ if(collationName!=null){
+ newSKD.setCollationNameExpression(new StringLiteral(this.getCollationNameExpression().evaluateAsString(context)));
+ }
+
+ newSKD.setCaseOrder(new StringLiteral(this.getCaseOrder().evaluateAsString(context)));
+
+ if(dataTypeExpression!=null){
+ newSKD.setDataTypeExpression(new StringLiteral(this.getDataTypeExpression().evaluateAsString(context)));
+ }
+ newSKD.setSortKey(new ContextItemExpression(), true);
+
+ return newSKD;
+ }
+
+ /**
+ * Compare two SortKeyDefinition values for equality. This compares the sortKeys and attribute values.
+ * @param other SortKeyDefinition
+ * @return boolean
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof SortKeyDefinition) {
+ SortKeyDefinition skd2 = (SortKeyDefinition)other;
+ return sortKey.hashCode() == skd2.getSortKey().hashCode() && hashCode() == skd2.hashCode();
+
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get a hashcode to reflect the equals() method
+ * @return a hashcode based sortkey attribute values.
+ */
+
+ public int hashCode() {
+ int h = 0;
+ h ^= order.hashCode();
+ h ^= caseOrder.hashCode();
+ h ^= language.hashCode();
+
+ if(dataTypeExpression!=null){
+ h ^= dataTypeExpression.hashCode();
+ }
+ if(stable!=null){
+ h ^= stable.hashCode();
+ }
+ if(collationName!=null){
+ h ^= collationName.hashCode();
+
+ }
+ return h;
+ }
+
+
+
+}
+
diff --git a/sf/saxon/expr/sort/SortKeyEvaluator.java b/sf/saxon/expr/sort/SortKeyEvaluator.java
new file mode 100644
index 0000000..40fa9a0
--- /dev/null
+++ b/sf/saxon/expr/sort/SortKeyEvaluator.java
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * Callback interface used to evaluate sort keys. An instance of this class is passed to the
+ * SortedIterator, and is used whenever a sort key value needs to be computed.
+ */
+
+public interface SortKeyEvaluator {
+
+ /**
+ * Evaluate the n'th sort key of the context item
+ */
+
+ /*@Nullable*/ public AtomicValue evaluateSortKey(int n, XPathContext context) throws XPathException;
+}
+
diff --git a/sf/saxon/expr/sort/Sortable.java b/sf/saxon/expr/sort/Sortable.java
new file mode 100644
index 0000000..f084754
--- /dev/null
+++ b/sf/saxon/expr/sort/Sortable.java
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+
+
+/**
+ * A Sortable is an object that can be sorted using the QuickSort method.
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public interface Sortable {
+
+ /**
+ * Compare two objects within this Sortable, identified by their position.
+ * @return <0 if obj[a]<obj[b], 0 if obj[a]=obj[b], >0 if obj[a]>obj[b]
+ */
+
+ public int compare(int a, int b);
+
+ /**
+ * Swap two objects within this Sortable, identified by their position.
+ */
+
+ public void swap(int a, int b);
+
+}
+
diff --git a/sf/saxon/expr/sort/SortedGroupIterator.java b/sf/saxon/expr/sort/SortedGroupIterator.java
new file mode 100644
index 0000000..007a24f
--- /dev/null
+++ b/sf/saxon/expr/sort/SortedGroupIterator.java
@@ -0,0 +1,166 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import com.saxonica.stream.ManualGroupIterator;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceExtent;
+
+/**
+ * A SortedGroupIterator is a modified SortedIterator. It sorts a sequence of groups,
+ * and is itself a GroupIterator. The modifications retain extra information about
+ * the items being sorted. The items are each the leading item of a group, and as well
+ * as the item itself, the iterator preserves information about the group: specifically,
+ * an iterator over the items in the group, and the value of the grouping key (if any).
+ */
+
+public class SortedGroupIterator extends SortedIterator implements GroupIterator {
+
+ int groupSlot = -1;
+ int keySlot = -1;
+
+
+ public SortedGroupIterator(XPathContext context,
+ GroupIterator base,
+ SortKeyEvaluator sortKeyEvaluator,
+ AtomicComparer[] comparators
+ ) {
+ super(context, base, sortKeyEvaluator, comparators, true);
+ setHostLanguage(Configuration.XSLT);
+ }
+
+ public void setGroupSlot(int groupSlot) {
+ this.groupSlot = groupSlot;
+ }
+
+ public void setKeySlot(int keySlot) {
+ this.keySlot = keySlot;
+ }
+
+ /**
+ * Override the method that builds the array of values and sort keys.
+ *
+ * @throws XPathException
+ */
+
+ protected void buildArray() throws XPathException {
+ int allocated;
+ if ((base.getProperties() & SequenceIterator.LAST_POSITION_FINDER) != 0) {
+ allocated = ((LastPositionFinder) base).getLength();
+ } else {
+ allocated = 100;
+ }
+
+ values = new GroupToBeSorted[allocated];
+ count = 0;
+
+ XPathContextMajor c2 = context.newContext();
+ c2.setCurrentIterator(base);
+ c2.setCurrentGroupIterator((GroupIterator) base);
+ if (sortKeyEvaluator instanceof InstructionInfo) {
+ c2.setOrigin((InstructionInfo) sortKeyEvaluator);
+ }
+ // this provides the context for evaluating the sort key
+
+ // initialise the array with data
+
+ while (true) {
+ Item item = base.next();
+ if (item == null) {
+ break;
+ }
+ if (count == allocated) {
+ allocated *= 2;
+ GroupToBeSorted[] nk2 = new GroupToBeSorted[allocated];
+ System.arraycopy((GroupToBeSorted[]) values, 0, nk2, 0, count);
+ values = nk2;
+ }
+ GroupToBeSorted gtbs = new GroupToBeSorted(comparators.length);
+ values[count] = gtbs;
+ gtbs.value = item;
+ for (int n = 0; n < comparators.length; n++) {
+ gtbs.sortKeyValues[n] = sortKeyEvaluator.evaluateSortKey(n, c2);
+ }
+ gtbs.originalPosition = count++;
+ gtbs.currentGroupingKey = ((GroupIterator) base).getCurrentGroupingKey();
+ gtbs.currentGroupIterator = ((GroupIterator) base).iterateCurrentGroup();
+ }
+ }
+
+ @Override
+ public Item next() throws XPathException {
+ Item next = super.next();
+ if (next != null) {
+ if (groupSlot >= 0) {
+ context.setLocalVariable(groupSlot, new SequenceExtent(((GroupToBeSorted) values[position - 1]).currentGroupIterator.getAnother()));
+ }
+ if (keySlot >= 0) {
+ context.setLocalVariable(keySlot, ((GroupToBeSorted) values[position - 1]).currentGroupingKey);
+ }
+ }
+ return next;
+ }
+
+ /*@Nullable*/
+ public AtomicSequence getCurrentGroupingKey() {
+ return ((GroupToBeSorted) values[position - 1]).currentGroupingKey;
+ }
+
+ public SequenceIterator iterateCurrentGroup() throws XPathException {
+ SequenceIterator iter = ((GroupToBeSorted) values[position - 1]).currentGroupIterator;
+ return iter.getAnother();
+ }
+
+ public boolean hasCurrentGroup() {
+ return groupSlot < 0;
+ }
+
+ public boolean hasCurrentGroupingKey() {
+ return keySlot < 0;
+ }
+
+//#if EE==true
+
+ public ManualGroupIterator getSnapShot(XPathContext context) throws XPathException {
+ return new ManualSortedGroupIterator();
+ }
+
+ private class ManualSortedGroupIterator extends ManualGroupIterator {
+
+ ManualSortedGroupIterator() {
+
+ super((Item) values[position - 1].value, position);
+ setLastPositionFinder(new LastPositionFinder<Item>() {
+ public int getLength() throws XPathException {
+ return values.length;
+ }
+ });
+ }
+
+ public AtomicSequence getCurrentGroupingKey() {
+ return ((GroupToBeSorted) values[position() - 1]).currentGroupingKey;
+ }
+
+ public SequenceIterator<? extends Item> iterateCurrentGroup() throws XPathException {
+ SequenceIterator iter = ((GroupToBeSorted) values[position() - 1]).currentGroupIterator;
+ return iter.getAnother();
+ }
+
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/expr/sort/SortedIterator.java b/sf/saxon/expr/sort/SortedIterator.java
new file mode 100644
index 0000000..096e087
--- /dev/null
+++ b/sf/saxon/expr/sort/SortedIterator.java
@@ -0,0 +1,312 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.ErrorIterator;
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+* Class to do a sorted iteration
+*/
+
+public class SortedIterator implements SequenceIterator, LastPositionFinder, LookaheadIterator, Sortable {
+
+ // the items to be sorted
+ protected SequenceIterator base;
+
+ // the call-back function used to evaluate sort keys
+ protected SortKeyEvaluator sortKeyEvaluator;
+
+ // the comparators corresponding to these sort keys
+ protected AtomicComparer[] comparators;
+
+ // The items and keys are read into an array (nodeKeys) for sorting. This
+ // array contains one "record" representing each node: the "record" contains
+ // first, the Item itself, then an entry for each of its sort keys, in turn;
+ // the last sort key is the position of the Item in the original sequence.
+ protected ObjectToBeSorted[] values;
+
+ // The number of items to be sorted. -1 means not yet known.
+ protected int count = -1;
+
+ // The next item to be delivered from the sorted iteration
+ protected int position = 0;
+
+ // The context for the evaluation of sort keys
+ protected XPathContext context;
+
+ // The host language (XSLT, XQuery, XPath). Used only to decide which error code to use on dynamic errors.
+ private int hostLanguage;
+
+ private SortedIterator(){}
+
+ /**
+ * Create a sorted iterator
+ * @param context the dynamic XPath evaluation context
+ * @param base an iterator over the sequence to be sorted
+ * @param sortKeyEvaluator an object that allows the n'th sort key for a given item to be evaluated
+ * @param comparators an array of AtomicComparers, one for each sort key, for comparing sort key values
+ * @param createNewContext
+ */
+
+ public SortedIterator(XPathContext context, SequenceIterator base,
+ SortKeyEvaluator sortKeyEvaluator, AtomicComparer[] comparators, boolean createNewContext) {
+ if (createNewContext) {
+ this.context = context.newMinorContext();
+ this.context.setCurrentIterator(base);
+ } else {
+ this.context = context;
+ }
+ this.base = base;
+ this.sortKeyEvaluator = sortKeyEvaluator;
+ this.comparators = new AtomicComparer[comparators.length];
+ for (int n=0; n<comparators.length; n++) {
+ this.comparators[n] = comparators[n].provideContext(context);
+ }
+
+ // Avoid doing the sort until the user wants the first item. This is because
+ // sometimes the user only wants to know whether the collection is empty.
+ }
+
+ /**
+ * Set the host language
+ * @param language the host language (for example {@link Configuration#XQUERY})
+ */
+
+ public void setHostLanguage(int language) {
+ hostLanguage = language;
+ }
+
+ /**
+ * Determine whether there are more items to come. Note that this operation
+ * is stateless and it is not necessary (or usual) to call it before calling
+ * next(). It is used only when there is an explicit need to tell if we
+ * are at the last element.
+ * <p/>
+ * This method must not be called unless the result of getProperties() on the iterator
+ * includes the bit setting {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}
+ *
+ * @return true if there are more items in the sequence
+ */
+
+ public boolean hasNext() {
+ if (position < 0) {
+ return false;
+ }
+ if (count < 0) {
+ // haven't started sorting yet
+ if (base instanceof LookaheadIterator) {
+ return ((LookaheadIterator)base).hasNext();
+ } else {
+ try {
+ doSort();
+ return count > 0;
+ } catch (XPathException err) {
+ // can't return the exception now; but we can rely on the fact that
+ // (a) it wouldn't have failed unless there was something to sort, and
+ // (b) it's going to fail again when next() is called
+ count = -1;
+ base = new ErrorIterator(err);
+ return true;
+ }
+ }
+ } else {
+ return (position < count);
+ }
+ }
+
+ /**
+ * Get the next item, in sorted order
+ */
+
+ /*@Nullable*/ public Item next() throws XPathException {
+ if (position < 0) {
+ return null;
+ }
+ if (count<0) {
+ doSort();
+ }
+ if (position < count) {
+ return (Item)values[(position++)].value;
+ } else {
+ position = -1;
+ return null;
+ }
+ }
+
+ public Item current() {
+ if (position < 1) {
+ return null;
+ }
+ return (Item)values[position-1].value;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public int getLength() throws XPathException {
+ if (count<0) {
+ doSort();
+ }
+ return count;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ // make sure the sort has been done, so that multiple iterators over the
+ // same sorted data only do the sorting once.
+ if (count<0) {
+ doSort();
+ }
+ SortedIterator s = new SortedIterator();
+ // the new iterator is the same as the old ...
+ s.base = base.getAnother();
+ s.sortKeyEvaluator = sortKeyEvaluator;
+ s.comparators = comparators;
+ s.values = values;
+ s.count = count;
+ s.context = context;
+ s.position = 0;
+ return s;
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LAST_POSITION_FINDER;
+ }
+
+ /**
+ * Create an array holding the items to be sorted and the values of their sort keys
+ * @throws XPathException
+ */
+
+ protected void buildArray() throws XPathException {
+ int allocated;
+ if ((base.getProperties() & SequenceIterator.LAST_POSITION_FINDER) != 0) {
+ allocated = ((LastPositionFinder)base).getLength();
+ } else {
+ allocated = 100;
+ }
+
+ values = new ItemToBeSorted[allocated];
+ count = 0;
+
+ // initialise the array with data
+
+ while (true) {
+ Item item = base.next();
+ if (item == null) {
+ break;
+ }
+ if (count==allocated) {
+ allocated *= 2;
+ ItemToBeSorted[] nk2 = new ItemToBeSorted[allocated];
+ System.arraycopy(values, 0, nk2, 0, count);
+ values = nk2;
+ }
+ ItemToBeSorted itbs = new ItemToBeSorted(comparators.length);
+ values[count] = itbs;
+ itbs.value = item;
+ // TODO: delay evaluating the sort keys until we know they are needed. Often the 2nd and subsequent
+ // sort key values will never be used. The only problem is with sort keys that depend on position().
+ for (int n=0; n<comparators.length; n++) {
+ itbs.sortKeyValues[n] = sortKeyEvaluator.evaluateSortKey(n, context);
+ }
+ // make the sort stable by adding the record number
+ itbs.originalPosition = count++;
+ }
+
+ // If there's lots of unused space, reclaim it
+
+ if (allocated * 2 < count || (allocated - count) > 2000) {
+ ObjectToBeSorted[] nk2 = new ObjectToBeSorted[count];
+ System.arraycopy(values, 0, nk2, 0, count);
+ values = nk2;
+ }
+ }
+
+ private void doSort() throws XPathException {
+ buildArray();
+ if (count<2) return;
+
+ // sort the array
+
+ //QuickSort.sort(this, 0, count-1);
+ try {
+ GenericSorter.quickSort(0, count, this);
+ } catch (ClassCastException e) {
+ //e.printStackTrace();
+ XPathException err = new XPathException("Non-comparable types found while sorting: " + e.getMessage());
+ if (hostLanguage == Configuration.XSLT) {
+ err.setErrorCode("XTDE1030");
+ } else {
+ err.setErrorCode("XPTY0004");
+ }
+ throw err;
+ }
+ //GenericSorter.mergeSort(0, count, this);
+ }
+
+ /**
+ * Compare two items in sorted sequence
+ * (needed to implement the Sortable interface)
+ * @return <0 if obj[a]<obj[b], 0 if obj[a]=obj[b], >0 if obj[a]>obj[b]
+ */
+
+ public int compare(int a, int b) {
+ try {
+ for (int i=0; i<comparators.length; i++) {
+ int comp = comparators[i].compareAtomicValues(
+ (AtomicValue)values[a].sortKeyValues[i], (AtomicValue)values[b].sortKeyValues[i]);
+ if (comp != 0) {
+ // we have found a difference, so we can return
+ return comp;
+ }
+ }
+ } catch (NoDynamicContextException e) {
+ throw new AssertionError("Sorting without dynamic context: " + e.getMessage());
+ }
+
+ // all sort keys equal: return the items in their original order
+
+ return values[a].originalPosition - values[b].originalPosition;
+ }
+
+ /**
+ * Swap two items (needed to implement the Sortable interface)
+ */
+
+ public void swap(int a, int b) {
+ ObjectToBeSorted temp = values[a];
+ values[a] = values[b];
+ values[b] = temp;
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/TextComparer.java b/sf/saxon/expr/sort/TextComparer.java
new file mode 100644
index 0000000..1507e4f
--- /dev/null
+++ b/sf/saxon/expr/sort/TextComparer.java
@@ -0,0 +1,110 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.expr.sort;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * A Comparer used for comparing sort keys when data-type="text". The items to be
+ * compared are converted to strings, and the strings are then compared using an
+ * underlying collator
+ *
+ * @author Michael H. Kay
+ *
+ */
+
+public class TextComparer implements AtomicComparer, java.io.Serializable {
+
+ private AtomicComparer baseComparer;
+
+ public TextComparer(AtomicComparer baseComparer) {
+ this.baseComparer = baseComparer;
+ }
+
+ /**
+ * Get the underlying comparer (which doesn't do conversion to string)
+ */
+
+ public AtomicComparer getBaseComparer() {
+ return baseComparer;
+ }
+
+ public StringCollator getCollator() {
+ return baseComparer.getCollator();
+ }
+
+ /**
+ * Supply the dynamic context in case this is needed for the comparison
+ * @param context the dynamic evaluation context
+ * @return either the original AtomicComparer, or a new AtomicComparer in which the context
+ * is known. The original AtomicComparer is not modified
+ * @throws net.sf.saxon.trans.NoDynamicContextException if the context is an "early evaluation" (compile-time) context
+ */
+
+ public AtomicComparer provideContext(XPathContext context) {
+ AtomicComparer newBase = baseComparer.provideContext(context);
+ if (newBase != baseComparer) {
+ return new TextComparer(newBase);
+ } else {
+ return this;
+ }
+ }
+
+
+ /**
+ * Compare two Items by converting them to strings and comparing the string values.
+ * @param a the first Item to be compared.
+ * @param b the second Item to be compared.
+ * @return <0 if a<b, 0 if a=b, >0 if a>b
+ * @throws ClassCastException if the objects are not Items, or are items that cannot be convered
+ * to strings (e.g. QNames)
+ */
+
+ public int compareAtomicValues(AtomicValue a, AtomicValue b) throws ClassCastException, NoDynamicContextException {
+ return baseComparer.compareAtomicValues(toStringValue(a), toStringValue(b));
+ }
+
+ private StringValue toStringValue(AtomicValue a) {
+ if (a instanceof StringValue) {
+ return ((StringValue)a);
+ } else {
+ return new StringValue((a == null ? "" : a.getStringValue()));
+ }
+ }
+
+ /**
+ * Compare two AtomicValue objects for equality according to the rules for their data type. UntypedAtomic
+ * values are compared by converting to the type of the other operand.
+ *
+ * @param a the first object to be compared.
+ * @param b the second object to be compared.
+ * @return true if the values are equal, false if not
+ * @throws ClassCastException if the objects are not comparable
+ */
+
+ public boolean comparesEqual(AtomicValue a, /*@NotNull*/ AtomicValue b) throws NoDynamicContextException {
+ return compareAtomicValues(a, b) == 0;
+ }
+
+ /**
+ * Get a comparison key for an object. This must satisfy the rule that if two objects are equal
+ * according to the XPath eq operator, then their comparison keys are equal according to the Java
+ * equals() method, and vice versa. There is no requirement that the
+ * comparison keys should reflect the ordering of the underlying objects.
+ */
+
+ public ComparisonKey getComparisonKey(AtomicValue a) throws NoDynamicContextException{
+ return baseComparer.getComparisonKey(toStringValue(a));
+ }
+
+}
+
diff --git a/sf/saxon/expr/sort/package.html b/sf/saxon/expr/sort/package.html
new file mode 100644
index 0000000..b671275
--- /dev/null
+++ b/sf/saxon/expr/sort/package.html
@@ -0,0 +1,36 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.expr.sort</title>
+</head>
+
+<body>
+
+<p>This package provides utility routines for sorting and grouping.
+Specifically, it contains a QuickSort implementation,
+and some AtomicComparer objects that handle character and numeric comparisons, together
+with the important DocumentOrderIterator which iterates over a set of nodes in document
+order.</p>
+
+<p>The package also
+contains the classes to support the implementation of the XSLT 2.0 instruction
+ xsl:for-each-group, and some utilities for maintaining sets and maps whose keys
+ are unboxed integer values.</p>
+
+
+<hr>
+
+
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+29 November 2011</i></p>
+</body>
+</html>
diff --git a/sf/saxon/functions/Abs.java b/sf/saxon/functions/Abs.java
new file mode 100644
index 0000000..834f95d
--- /dev/null
+++ b/sf/saxon/functions/Abs.java
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.NumericValue;
+
+/**
+* This class supports the abs() function
+*/
+
+public final class Abs extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate the function
+ */
+
+ /*@Nullable*/ public NumericValue evaluateItem(XPathContext context) throws XPathException {
+ NumericValue val0 = (NumericValue)argument[0].evaluateItem(context);
+ if (val0 == null) {
+ return null;
+ }
+ return val0.abs();
+ }
+
+ /**
+ * Determine the cardinality of the function.
+ */
+
+ public int computeCardinality() {
+ return argument[0].getCardinality();
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NumericValue val0 = (NumericValue)arguments[0].head();
+ if (val0 == null) {
+ return EmptySequence.getInstance();
+ }
+ return val0.abs();
+ }
+}
+
diff --git a/sf/saxon/functions/Adjust.java b/sf/saxon/functions/Adjust.java
new file mode 100644
index 0000000..eb128be
--- /dev/null
+++ b/sf/saxon/functions/Adjust.java
@@ -0,0 +1,94 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.CalendarValue;
+import net.sf.saxon.value.DayTimeDurationValue;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+* This class implements the XPath 2.0 functions
+ * adjust-date-to-timezone(), adjust-time-timezone(), and adjust-dateTime-timezone().
+*/
+
+
+public class Adjust extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate in a general context
+ */
+
+ /*@Nullable*/ public CalendarValue evaluateItem(XPathContext context) throws XPathException {
+ CalendarValue in = (CalendarValue)argument[0].evaluateItem(context);
+ if (in==null) {
+ return null;
+ }
+ int nargs = argument.length;
+ if (nargs==1) {
+ return in.adjustTimezone(context.getImplicitTimezone());
+ } else {
+ AtomicValue av2 = (AtomicValue)argument[1].evaluateItem(context);
+ if (av2==null) {
+ return in.removeTimezone();
+ }
+ return adjustToExplicitTimezone(in, (DayTimeDurationValue)av2, context);
+ }
+ }
+
+ private CalendarValue adjustToExplicitTimezone(CalendarValue in, DayTimeDurationValue tz, XPathContext context) throws XPathException {
+ long microseconds = tz.getLengthInMicroseconds();
+ if (microseconds%60000000 != 0) {
+ XPathException err = new XPathException("Timezone is not an integral number of minutes", "FODT0003");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ int tzminutes = (int)(microseconds / 60000000);
+ if (Math.abs(tzminutes) > 14*60) {
+ XPathException err = new XPathException("Timezone out of range (-14:00 to +14:00)", "FODT0003");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ return in.adjustTimezone(tzminutes);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ CalendarValue in = (CalendarValue)arguments[0].head();
+ if (in == null) {
+ return EmptySequence.getInstance();
+ }
+ if (arguments.length == 1) {
+ return in.adjustTimezone(context.getImplicitTimezone());
+ } else {
+ DayTimeDurationValue tz = (DayTimeDurationValue)arguments[1].head();
+ if (tz == null) {
+ return in.removeTimezone();
+ } else {
+ return adjustToExplicitTimezone(in, tz, context);
+ }
+ }
+ }
+}
+
diff --git a/sf/saxon/functions/Aggregate.java b/sf/saxon/functions/Aggregate.java
new file mode 100644
index 0000000..e1e2216
--- /dev/null
+++ b/sf/saxon/functions/Aggregate.java
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This abstract class provides functionality common to the sum(), avg(), count(),
+ * exists(), and empty() functions. These all take a sequence as input and produce a singleton
+ * as output; and they are all independent of the ordering of the items in the input.
+*/
+
+public abstract class Aggregate extends SystemFunctionCall {
+
+ /**
+ * Static analysis: prevent sorting of the argument
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ argument[0] = ExpressionTool.unsorted(opt, argument[0], true);
+ // we don't care about the order of the results, but we do care about how many nodes there are
+ }
+
+//#ifdefined STREAM
+ /**
+ * Get the "sweep" of this expression as defined in the W3C streamability specifications.
+ * This provides an assessment of stylesheet code against the W3C criteria for guaranteed
+ * streamability, and is implemented to allow these criteria to be tested. It is not the
+ * case that all expression that emerge as streamable from this analysis are currently
+ * capable of being streamed by Saxon
+ *
+ * @param syntacticContext one of the values {@link #NAVIGATION_CONTEXT},
+ * {@link #NODE_VALUE_CONTEXT}, {@link #INHERITED_CONTEXT}, {@link #INSPECTION_CONTEXT}
+ * @param allowExtensions if false, the definition of "guaranteed streamability" in the
+ * W3C specification is used. If true, Saxon extensions are permitted, which make some
+ * @param reasons the caller may supply a list, in which case the implementation may add to this
+ * list a message explaining why the construct is not streamable, suitable for inclusion in an
+ * error message.
+ * @return one of the values {@link #W3C_MOTIONLESS}, {@link #W3C_CONSUMING},
+ * {@link #W3C_GROUP_CONSUMING}, {@link #W3C_FREE_RANGING}
+ */
+// @Override
+// public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+// //return argument[0].getStreamability(ATOMIZING_CONTEXT, allowExtensions, reasons);
+// return W3C_CONSUMING;
+// }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/Available.java b/sf/saxon/functions/Available.java
new file mode 100644
index 0000000..4fe0ec1
--- /dev/null
+++ b/sf/saxon/functions/Available.java
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* This class supports the XSLT element-available, function-available, and type-available functions.
+*/
+
+public abstract class Available extends SystemFunctionCall {
+
+ protected NamespaceResolver nsContext;
+ private transient boolean checked = false;
+
+
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ // the second time checkArguments is called, it's a global check so the static context is inaccurate
+ if (checked) {
+ return;
+ }
+ checked = true;
+ super.checkArguments(visitor);
+ if (!(argument[0] instanceof Literal &&
+ (argument.length==1 || argument[1] instanceof Literal))) {
+ // we need to save the namespace context
+ nsContext = visitor.getStaticContext().getNamespaceResolver();
+ }
+ }
+
+
+
+}
+
diff --git a/sf/saxon/functions/Average.java b/sf/saxon/functions/Average.java
new file mode 100644
index 0000000..ca6282c
--- /dev/null
+++ b/sf/saxon/functions/Average.java
@@ -0,0 +1,167 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.stream.adjunct.AverageAdjunct;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.StringConverter;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.*;
+
+
+/**
+ * Implementation of the fn:avg function
+ */
+public class Average extends Aggregate implements Callable {
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | ITEM_FEED_METHOD;
+ }
+
+
+ /**
+ * Determine the item type of the value returned by the function
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ ItemType base = Atomizer.getAtomizedItemType(argument[0], false, th);
+ if (base.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ return BuiltInAtomicType.DOUBLE;
+ } else if (base.getPrimitiveType() == StandardNames.XS_INTEGER) {
+ return BuiltInAtomicType.DECIMAL;
+ } else {
+ return base;
+ }
+ }
+
+ /**
+ * Determine the cardinality of the function.
+ */
+
+ public int computeCardinality() {
+ if (!Cardinality.allowsZero(argument[0].getCardinality())) {
+ return StaticProperty.EXACTLY_ONE;
+ } else {
+ return super.computeCardinality();
+ }
+ }
+
+//#ifdefined STREAM
+
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public AverageAdjunct getStreamingAdjunct() {
+ return new AverageAdjunct();
+ }
+//#endif
+
+ /**
+ * Evaluate the function
+ */
+
+ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+ try {
+ return average(argument[0].iterate(context), context);
+ } catch (XPathException err) {
+ err.maybeSetContext(context);
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+
+ /**
+ * Calculate average
+ * @param iter iterator over the items to be totalled
+ * @param context the XPath dynamic context
+ * @return the average of the values
+ * @throws XPathException in the event of a dynamic error, for example if the input sequence contains
+ * a mix of numbers and durations
+ */
+
+ /*@Nullable*/ public static AtomicValue average(SequenceIterator iter, XPathContext context)
+ throws XPathException {
+ ConversionRules rules = context.getConfiguration().getConversionRules();
+ StringConverter toDouble = rules.getStringConverter(BuiltInAtomicType.DOUBLE);
+ int count = 0;
+ AtomicValue item = (AtomicValue)iter.next();
+ if (item == null) {
+ // the sequence is empty
+ return null;
+ }
+ count++;
+ if (item instanceof UntypedAtomicValue) {
+ item = toDouble.convert(item).asAtomic();
+ }
+ if (item instanceof NumericValue) {
+ while (true) {
+ AtomicValue next = (AtomicValue)iter.next();
+ if (next == null) {
+ //return ((NumericValue)item).arithmetic(Token.DIV, new Int64Value(count), context);
+ return ArithmeticExpression.compute(item, Calculator.DIV, new Int64Value(count), context);
+ }
+ count++;
+ if (next instanceof UntypedAtomicValue) {
+ next = toDouble.convert(next).asAtomic();
+ } else if (!(next instanceof NumericValue)) {
+ throw new XPathException(
+ "Input to avg() contains a mix of numeric and non-numeric values", "FORG0006");
+ }
+ //item = ((NumericValue)item).arithmetic(Token.PLUS, (NumericValue)next, context);
+ item = ArithmeticExpression.compute(item, Calculator.PLUS, next, context);
+ if (item.isNaN() && item instanceof DoubleValue) {
+ // take an early bath, once we've got a double NaN it's not going to change
+ return item;
+ }
+ }
+ } else if (item instanceof DurationValue) {
+ while (true) {
+ AtomicValue next = (AtomicValue)iter.next();
+ if (next == null) {
+ return ((DurationValue)item).multiply(1.0/count);
+ }
+ count++;
+ if (!(next instanceof DurationValue)) {
+ throw new XPathException(
+ "Input to avg() contains a mix of duration and non-duration values", "FORG0006");
+ }
+ item = ((DurationValue)item).add((DurationValue)next);
+ }
+ } else {
+ throw new XPathException(
+ "Input to avg() contains a value that is neither numeric, nor a duration", "FORG0006");
+ }
+ }
+
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ try {
+ return average(arguments[0].iterate(), context);
+ } catch (XPathException err) {
+ err.maybeSetContext(context);
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/functions/BaseURI.java b/sf/saxon/functions/BaseURI.java
new file mode 100644
index 0000000..6ccd2b7
--- /dev/null
+++ b/sf/saxon/functions/BaseURI.java
@@ -0,0 +1,87 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.BaseURICompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AnyURIValue;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+ * This class implements the fn:base-uri() function in XPath 2.0
+ */
+
+public class BaseURI extends SystemFunctionCall implements Callable {
+
+ /**
+ * Simplify and validate.
+ * This is a pure function so it can be simplified in advance if the arguments are known
+ *
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ return simplifyArguments(visitor);
+ }
+
+ /**
+ * Evaluate the function at run-time
+ */
+
+ /*@Nullable*/
+ public AnyURIValue evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo node = (NodeInfo) argument[0].evaluateItem(c);
+ if (node == null) {
+ return null;
+ }
+ String s = node.getBaseURI();
+ if (s == null) {
+ return null;
+ }
+ return new AnyURIValue(s);
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo node = getDefaultArgumentNode(context, arguments, "fn:base-uri()");
+
+ if (node == null) {
+ return EmptySequence.getInstance();
+ }
+
+ String s = node.getBaseURI();
+ if (s == null) {
+ return EmptySequence.getInstance();
+ }
+ return new AnyURIValue(s);
+
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the BaseURI expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new BaseURICompiler();
+ }
+ //#endif
+
+}
+
diff --git a/sf/saxon/functions/BooleanFn.java b/sf/saxon/functions/BooleanFn.java
new file mode 100644
index 0000000..37cb162
--- /dev/null
+++ b/sf/saxon/functions/BooleanFn.java
@@ -0,0 +1,193 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.BooleanFnCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+
+/**
+* This class supports the XPath function boolean()
+*/
+
+
+public class BooleanFn extends SystemFunctionCall implements Negatable, Callable {
+
+ /**
+ * Static analysis: prevent sorting of the argument
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ XPathException err = TypeChecker.ebvError(argument[0], visitor.getConfiguration().getTypeHierarchy());
+ if (err != null) {
+ err.setLocator(this);
+ throw err;
+ }
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ argument[0] = ExpressionTool.unsortedIfHomogeneous(opt, argument[0]);
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e == this) {
+ Expression ebv = rewriteEffectiveBooleanValue(argument[0], visitor, contextItemType);
+ if (ebv != null) {
+ ebv = ebv.optimize(visitor, contextItemType);
+ if (ebv.getItemType(visitor.getConfiguration().getTypeHierarchy()) == BuiltInAtomicType.BOOLEAN &&
+ ebv.getCardinality() == StaticProperty.EXACTLY_ONE) {
+ return ebv;
+ } else {
+ argument[0] = ebv;
+ adoptChildExpression(ebv);
+ return this;
+ }
+ }
+ }
+ return e;
+ }
+
+ /**
+ * Check whether this specific instance of the expression is negatable
+ *
+ * @return true if it is
+ */
+
+ public boolean isNegatable(ExpressionVisitor visitor) {
+ return true;
+ }
+
+ /**
+ * Create an expression that returns the negation of this expression
+ * @return the negated expression
+ */
+
+ public Expression negate() {
+ return SystemFunctionCall.makeSystemFunction("not", getArguments());
+ }
+
+ /**
+ * Optimize an expression whose effective boolean value is required. It is appropriate
+ * to apply this rewrite to any expression whose value will be obtained by calling
+ * the Expression.effectiveBooleanValue() method (and not otherwise)
+ * @param exp the expression whose EBV is to be evaluated
+ * @param visitor an expression visitor
+ * @param contextItemType the type of the context item for this expression
+ * @return an expression that returns the EBV of exp, or null if no optimization was possible
+ * @throws XPathException if static errors are found
+ */
+
+ public static Expression rewriteEffectiveBooleanValue(
+ Expression exp, ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Configuration config = visitor.getConfiguration();
+ TypeHierarchy th = config.getTypeHierarchy();
+ exp = ExpressionTool.unsortedIfHomogeneous(config.obtainOptimizer(), exp);
+ if (exp instanceof ValueComparison) {
+ ValueComparison vc = (ValueComparison)exp;
+ if (vc.getResultWhenEmpty() == null) {
+ vc.setResultWhenEmpty(BooleanValue.FALSE);
+ }
+ return exp;
+ } else if (exp instanceof BooleanFn) {
+ return ((BooleanFn)exp).getArguments()[0];
+ } else if (th.isSubType(exp.getItemType(th), BuiltInAtomicType.BOOLEAN) &&
+ exp.getCardinality() == StaticProperty.EXACTLY_ONE) {
+ return exp;
+ } else if (exp instanceof Count) {
+ // rewrite boolean(count(x)) => exists(x)
+ FunctionCall exists = SystemFunctionCall.makeSystemFunction("exists", ((Count) exp).getArguments());
+ assert exists != null;
+ exists.setLocationId(exp.getLocationId());
+ return exists.optimize(visitor, contextItemType);
+ } else if (exp.getItemType(th) instanceof NodeTest) {
+ // rewrite boolean(x) => exists(x)
+ FunctionCall exists = SystemFunctionCall.makeSystemFunction("exists", new Expression[]{exp});
+ assert exists != null;
+ exists.setLocationId(exp.getLocationId());
+ return exists.optimize(visitor, contextItemType);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Evaluate the effective boolean value
+ */
+
+ public boolean effectiveBooleanValue(XPathContext c) throws XPathException {
+ try {
+ return argument[0].effectiveBooleanValue(c);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(c);
+ throw e;
+ }
+ }
+
+ public Sequence call(XPathContext c, Sequence[] arguments) throws XPathException {
+ try {
+ boolean bValue = ExpressionTool.effectiveBooleanValue(arguments[0].iterate());
+ return BooleanValue.get(bValue);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(c);
+ throw e;
+ }
+ }
+
+ //#ifdefined BYTECODE
+ /**
+ * Return the compiler of the BooleanFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new BooleanFnCompiler();
+ }
+ //#endif
+
+
+}
\ No newline at end of file
diff --git a/sf/saxon/functions/Ceiling.java b/sf/saxon/functions/Ceiling.java
new file mode 100644
index 0000000..fd783a7
--- /dev/null
+++ b/sf/saxon/functions/Ceiling.java
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.RoundingCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.NumericValue;
+
+/**
+* This class supports the ceiling(), floor(), round(), and round-to-half-even() functions,
+ * and also the abs() function
+*/
+
+public final class Ceiling extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate the function
+ */
+
+ /*@Nullable*/ public NumericValue evaluateItem(XPathContext context) throws XPathException {
+ NumericValue val0 = (NumericValue)argument[0].evaluateItem(context);
+ if (val0 == null) {
+ return null;
+ }
+ return val0.ceiling();
+ }
+
+ /**
+ * Determine the cardinality of the function.
+ */
+
+ public int computeCardinality() {
+ return argument[0].getCardinality();
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NumericValue val0 = (NumericValue)arguments[0].head();
+ if (val0 == null) {
+ return EmptySequence.getInstance();
+ }
+ return val0.ceiling();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Ceiling expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new RoundingCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/CodepointEqual.java b/sf/saxon/functions/CodepointEqual.java
new file mode 100644
index 0000000..cdfc53c
--- /dev/null
+++ b/sf/saxon/functions/CodepointEqual.java
@@ -0,0 +1,52 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * Implements the XPath 2.0 fn:codepoint-equal() function.
+ *
+ * <p>Compares two strings using the unicode codepoint collation. (The function was introduced
+ * specifically to allow URI comparison: URIs are promoted to strings when necessary.) </p>
+ */
+
+public class CodepointEqual extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate the expression
+ */
+
+ /*@Nullable*/ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ StringValue op1 = (StringValue)argument[0].evaluateItem(context);
+ StringValue op2 = (StringValue)argument[1].evaluateItem(context);
+ return codepointEqual(op1, op2);
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue op1 = (StringValue)arguments[0].head();
+ StringValue op2 = (StringValue)arguments[1].head();
+ BooleanValue b = codepointEqual(op1, op2);
+ return (b == null ? EmptySequence.getInstance() : b);
+ }
+
+ private static BooleanValue codepointEqual(StringValue s1, StringValue s2) {
+ if (s1 == null || s2 == null) {
+ return null;
+ }
+ return BooleanValue.get(s1.getStringValue().equals(s2.getStringValue()));
+ }
+
+}
+
diff --git a/sf/saxon/functions/CodepointsToString.java b/sf/saxon/functions/CodepointsToString.java
new file mode 100644
index 0000000..7438d84
--- /dev/null
+++ b/sf/saxon/functions/CodepointsToString.java
@@ -0,0 +1,99 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.CodepointsToStringCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.NumericValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * This class implements the function fn:codepoints-to-string()
+ */
+
+public class CodepointsToString extends SystemFunctionCall implements Callable {
+
+ /**
+ * Pre-evaluate a function at compile time. Functions that do not allow
+ * pre-evaluation, or that need access to context information, can override this method.
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ final XPathContext context = visitor.getStaticContext().makeEarlyEvaluationContext();
+ return new StringLiteral(
+ unicodeToString(((Literal)argument[0]).iterate(),
+ context.getConfiguration().getNameChecker()));
+ }
+
+ /**
+ * Evaluate
+ */
+
+ public StringValue evaluateItem(/*@NotNull*/ XPathContext c) throws XPathException {
+ return StringValue.makeStringValue(
+ unicodeToString(argument[0].iterate(c), c.getConfiguration().getNameChecker()));
+ }
+
+ /**
+ * Return the Unicode string corresponding to a given sequence of Unicode code values
+ * @param chars iterator delivering the characters as integer values
+ * @param checker used to test whether a character is valid in the appropriate XML version
+ * @return the sequence of characters as a CharSequence
+ * @throws net.sf.saxon.trans.XPathException if any of the integers is not the codepoint of a valid XML character
+ */
+
+ public static CharSequence unicodeToString(SequenceIterator chars, NameChecker checker) throws XPathException {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ while (true) {
+ NumericValue nextInt = (NumericValue)chars.next();
+ if (nextInt == null) {
+ return sb.condense();
+ }
+ long next = nextInt.longValue();
+ if (next < 0 || next > Integer.MAX_VALUE || !checker.isValidChar((int)next)) {
+ throw new XPathException("Invalid XML character [x " + Integer.toHexString((int)next) + ']', "FOCH0001");
+ }
+ sb.appendWideChar((int)next);
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ SequenceIterator chars = arguments[0].iterate();
+ return new StringValue(unicodeToString(chars, context.getConfiguration().getNameChecker()));
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the CodepointsToString expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new CodepointsToStringCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/CollatingFunction.java b/sf/saxon/functions/CollatingFunction.java
new file mode 100644
index 0000000..0812267
--- /dev/null
+++ b/sf/saxon/functions/CollatingFunction.java
@@ -0,0 +1,331 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.AtomicSortComparer;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.StringValue;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Abstract superclass for all functions that take an optional collation argument
+ */
+
+// Supports string comparison using a collation
+
+public abstract class CollatingFunction extends SystemFunctionCall {
+
+ // The default collation, or if it is known statically, the actual collation requested in the call
+ protected StringCollator staticCollation = null;
+ // The base URI from the static context
+ private URI expressionBaseURI = null;
+ // The AtomicComparer to be used if known statically (not used by all collating functions)
+ private AtomicComparer atomicComparer = null;
+
+ /**
+ * Bind aspects of the static context on which the particular function depends
+ *
+ * @param env the static context of the function call
+ * @throws net.sf.saxon.trans.XPathException
+ * if execution with this static context will inevitably fail
+ */
+ @Override
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ expressionBaseURI = ExpressionTool.getBaseURI(env, this, false);
+ staticCollation = env.getCollation(env.getDefaultCollationName());
+ }
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+
+ protected abstract int getCollationArgument();
+
+ @Override
+ public void checkArguments(ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ preEvaluateCollation(visitor.getStaticContext());
+ }
+
+ /**
+ * Get the saved static base URI
+ * @return the static base URI
+ */
+
+ public URI getExpressionBaseURI() {
+ return expressionBaseURI;
+ }
+
+ /**
+ * Get the collation if known statically, as a StringCollator object
+ * @return a StringCollator. Return null if the collation is not known statically.
+ */
+
+ public StringCollator getStaticCollation() {
+ return staticCollation;
+ }
+
+ /**
+ * Pre-evaluate the collation argument if its value is known statically
+ * @throws XPathException if execution of the function is bound to fail
+ */
+
+ private void preEvaluateCollation(StaticContext env) throws XPathException {
+ if ((getDetails().properties & StandardFunction.DCOLL) == 0) {
+ final Expression collationExp = argument[getNumberOfArguments() - 1];
+ final GroundedValue collationVal = (collationExp instanceof Literal ? ((Literal)collationExp).getValue() : null);
+ if (collationVal instanceof AtomicValue) {
+ // Collation is supplied as a constant
+ String collationName = ((AtomicValue)collationVal).getStringValue();
+ URI collationURI;
+ try {
+ collationURI = new URI(collationName);
+ if (!collationURI.isAbsolute()) {
+ if (expressionBaseURI == null) {
+ XPathException err = new XPathException("The collation name is a relative URI, but the base URI is unknown");
+ err.setErrorCode("XPST0001");
+ err.setIsStaticError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ URI base = expressionBaseURI;
+ collationURI = base.resolve(collationURI);
+ }
+ collationName = collationURI.toString();
+ StringCollator selectedCollation = env.getCollation(collationName);
+ if (selectedCollation == null) {
+ XPathException err = new XPathException("Unknown collation " + Err.wrap(collationName, Err.URI));
+ err.setErrorCode("FOCH0002");
+ //err.setIsStaticError(true);
+ err.setLocator(this);
+ throw err;
+ } else {
+ staticCollation = selectedCollation;
+ }
+ // following lines removed 28/1/2013: breaks JUnit test CzechCollation
+// Expression[] args = new Expression[argument.length - 1];
+// System.arraycopy(argument, 0, args, 0, args.length);
+// argument = args;
+
+ } catch (URISyntaxException e) {
+ XPathException err = new XPathException("Collation name '" + collationName + "' is not a valid URI");
+ err.setErrorCode("FOCH0002");
+ //err.setIsStaticError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ } else {
+ staticCollation = null;
+ // collation isn't known until run-time
+ }
+ } else {
+ // Use the default collation : no action needed
+ }
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ CollatingFunction d = (CollatingFunction)super.copy();
+ d.expressionBaseURI = expressionBaseURI;
+ d.staticCollation = staticCollation;
+ d.atomicComparer = atomicComparer;
+ return d;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof CollatingFunction &&
+ super.equals(o) &&
+ equalOrNull(expressionBaseURI, ((CollatingFunction)o).expressionBaseURI) &&
+ equalOrNull(staticCollation, ((CollatingFunction)o).staticCollation);
+ }
+
+
+ /**
+ * Get a collator suitable for comparing strings. Returns the collator specified in the
+ * given function argument if present, otherwise returns the default collator. This method is
+ * called by subclasses at run time.
+ *
+ * <p>This method is used only when evaluating static function calls.</p>
+ *
+ *
+ * @param context The dynamic context
+ * @return a StringCollator
+ * @throws XPathException if a failure occurs evaluating the collation argument, or if the
+ * specified collation is not recognized
+ */
+
+ protected StringCollator getCollator(XPathContext context) throws XPathException {
+ if (staticCollation != null) {
+ return staticCollation;
+ }
+ int arg = getCollationArgument();
+ AtomicValue av = (AtomicValue) argument[arg].evaluateItem(context);
+ StringValue collationValue = (StringValue) av;
+ String collationName = collationValue.getStringValue();
+ try {
+ collationName = expandCollationURI(collationName, expressionBaseURI);
+ } catch (XPathException err) {
+ err.setLocator(this);
+ }
+ StringCollator collator = context.getCollation(collationName);
+ if (collator == null) {
+ throw new XPathException("Unrecognized collation: " + collationName, "FOCH0002");
+ }
+ return collator;
+ }
+
+ /**
+ * Get a collator suitable for comparing strings. Returns the collator specified in the
+ * given function argument if present, otherwise returns the default collator. This method is
+ * called by subclasses at run time.
+ *
+ * <p>This method is used only when evaluating dynamic function calls.</p>
+ *
+ * @param arguments The arguments supplied to the function call
+ * @param arg The position of the argument (base 0) containing the collation name
+ * @param context The dynamic context
+ * @return a StringCollator
+ * @throws XPathException if a failure occurs evaluating the collation argument, or if the
+ * specified collation is not recognized
+ */
+
+ protected StringCollator getCollatorFromLastArgument(Sequence[] arguments, int arg, XPathContext context)
+ throws XPathException {
+ if (arguments.length > arg) {
+ // collation is specified explicitly in the call
+ String collationName = arguments[arg].head().getStringValue();
+ try {
+ collationName = expandCollationURI(collationName, expressionBaseURI);
+ } catch (XPathException err) {
+ err.setLocator(this);
+ }
+ return context.getCollation(collationName);
+ } else {
+ // use the default collation
+ return staticCollation;
+ }
+ }
+
+ /**
+ * Expand a collation URI, which may be a relative URI reference
+ *
+ * @param collationName the collation URI as provided
+ * @param expressionBaseURI the base URI against which the collation URI will be resolved if it is relative
+ * @return the resolved (expanded) absolute collation URI
+ * @throws XPathException if the collation URI cannot be resolved
+ */
+
+ public static String expandCollationURI(String collationName, URI expressionBaseURI) throws XPathException {
+ try {
+ URI collationURI = new URI(collationName);
+ if (!collationURI.isAbsolute()) {
+ if (expressionBaseURI == null) {
+ throw new XPathException("Cannot resolve relative collation URI '" + collationName +
+ "': unknown or invalid base URI", "FOCH0002");
+ }
+ collationURI = expressionBaseURI.resolve(collationURI);
+ collationName = collationURI.toString();
+ }
+ } catch (URISyntaxException e) {
+ throw new XPathException("Collation name '" + collationName + "' is not a valid URI", "FOCH0002");
+ }
+ return collationName;
+ }
+
+ /**
+ * During static analysis, if types are known and the collation is known, pre-allocate a comparer
+ * for comparing atomic values. Called by some collating functions during type-checking
+ * @param type0 the type of the first comparand
+ * @param type1 the type of the second comparand
+ * @param env the static context
+ * @param NaNequalsNaN true if two NaN values are to be considered equal
+ */
+
+ protected void preAllocateComparer(AtomicType type0, AtomicType type1, StaticContext env, boolean NaNequalsNaN) {
+ StringCollator collation;
+ if (argument.length <= getCollationArgument()) {
+ // use the default collation
+ collation = staticCollation;
+ } else if (argument[argument.length-1] instanceof StringLiteral) {
+ // use the collation given as a string literal in the last argument
+ String collationName = ((StringLiteral)argument[argument.length-1]).getStringValue();
+ try {
+ collationName = expandCollationURI(collationName, expressionBaseURI);
+ collation = env.getCollation(collationName);
+ if (collation == null) {
+ throw new XPathException("Unrecognized collation: " + collationName, "FOCH0002");
+ }
+ } catch (XPathException err) {
+ err.setLocator(this);
+ argument[argument.length-1] = new ErrorExpression(err);
+ return;
+ }
+ } else {
+ return;
+ }
+
+ if (NaNequalsNaN) {
+ atomicComparer = AtomicSortComparer.makeSortComparer(
+ collation, type0.getPrimitiveType(), env.makeEarlyEvaluationContext());
+ } else {
+ atomicComparer = GenericAtomicComparer.makeAtomicComparer(
+ (BuiltInAtomicType)type0.getBuiltInBaseType(), (BuiltInAtomicType)type1.getBuiltInBaseType(),
+ staticCollation, env.makeEarlyEvaluationContext());
+ }
+ }
+
+
+ /**
+ * Get the pre-allocated atomic comparer, if available
+ * @return the preallocated atomic comparer, or null
+ */
+
+ public AtomicComparer getPreAllocatedAtomicComparer() {
+ return atomicComparer;
+ }
+
+ /**
+ * During evaluation, get the pre-allocated atomic comparer if available, or allocate a new one otherwise
+ * @return the pre-allocated comparer if one is available; otherwise, a newly allocated one, using the specified
+ * StringCollator for comparing strings
+ */
+
+ protected AtomicComparer getAtomicComparer(StringCollator collator, XPathContext context) {
+ if (atomicComparer != null) {
+ return atomicComparer.provideContext(context);
+ } else {
+ return new GenericAtomicComparer(collator, context);
+ }
+ }
+}
+
diff --git a/sf/saxon/functions/Collection.java b/sf/saxon/functions/Collection.java
new file mode 100644
index 0000000..598b9af
--- /dev/null
+++ b/sf/saxon/functions/Collection.java
@@ -0,0 +1,190 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.lib.CollectionURIResolver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.AnyURIValue;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+
+/**
+ * Implement the fn:collection() function. This is responsible for calling the
+ * registered {@link CollectionURIResolver}. For the effect of the default
+ * system-supplied CollectionURIResolver, see {@link net.sf.saxon.lib.StandardCollectionURIResolver}
+ */
+
+public class Collection extends SystemFunctionCall implements Callable {
+
+ /**
+ * URI representing a collection that is always empty, regardless of any collection URI resolver
+ */
+ public static String EMPTY_COLLECTION = "http://saxon.sf.net/collection/empty";
+
+ /*@Nullable*/
+ private String expressionBaseURI = null;
+
+ public String getStaticBaseURI() {
+ return expressionBaseURI;
+ }
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ if (expressionBaseURI == null) {
+ super.checkArguments(visitor);
+ expressionBaseURI = visitor.getStaticContext().getBaseURI();
+ }
+ }
+
+ public int computeSpecialProperties() {
+ // See redmine bug 1652. We cannot assume that the nodes will be in document order because we can't assume
+ // they will all be "new" documents. We can't even assume that they will be distinct.
+ return (super.computeSpecialProperties() & ~StaticProperty.NON_CREATIVE) | StaticProperty.PEER_NODESET;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ *
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ return addDocToPathMap(pathMap, pathMapNodeSet);
+ }
+
+
+ /**
+ * Iterate over the contents of the collection
+ *
+ * @param context the dynamic context
+ * @return an iterator, whose items will always be nodes (typically but not necessarily document nodes)
+ * @throws XPathException
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<Item> iterate(final XPathContext context) throws XPathException {
+
+ String href;
+
+ if (getNumberOfArguments() == 0) {
+ // No arguments supplied: this gets the default collection
+ href = context.getController().getDefaultCollection();
+ } else {
+ Item arg = argument[0].evaluateItem(context);
+ if (arg == null) {
+ href = context.getController().getDefaultCollection();
+ } else {
+ href = arg.getStringValue();
+ }
+ }
+
+ if (EMPTY_COLLECTION.equals(href)) {
+ return EmptyIterator.emptyIterator();
+ }
+ CollectionURIResolver resolver = context.getConfiguration().getCollectionURIResolver();
+ SequenceIterator iter;
+ try {
+ iter = resolver.resolve(href, expressionBaseURI, context);
+ } catch (XPathException e) {
+ e.setLocator(this);
+ if (e.getErrorCodeQName() == null) {
+ e.setErrorCode("FODC0002");
+ }
+ throw e;
+ }
+
+ return getResolverResults(iter, expressionBaseURI, context, this);
+ }
+
+ /**
+ * Return the results of iterating over the results returned by the CollectionURIResolver.
+ * <p>Note, this method is called by generated code</p>
+ *
+ * @param iter iterator over the results of the CollectionURIResolver
+ * @param baseURI the base URI
+ * @param context the dynamic context
+ * @param locator location of the instruction
+ * @return an iterator over the documents in the collection.
+ */
+
+ public static SequenceIterator getResolverResults(
+ SequenceIterator iter, final String baseURI, final XPathContext context, final SourceLocator locator) {
+ if (iter == null) {
+ return EmptyIterator.getInstance();
+ } else {
+ ItemMappingFunction imf = new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ if (item instanceof NodeInfo) {
+ return item;
+ } else if (item instanceof AnyURIValue) {
+ return DocumentFn.makeDoc(
+ item.getStringValue(),
+ baseURI,
+ context,
+ locator);
+ } else if (item instanceof Source) {
+ return context.getConfiguration().buildDocument(((Source) item));
+ } else {
+ throw new XPathException("Value returned by CollectionURIResolver must be a Source or an anyURI");
+ }
+ }
+ };
+ return new ItemMappingIterator(iter, imf);
+ }
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ String href;
+
+ if (getNumberOfArguments() == 0) {
+ // No arguments supplied: this gets the default collection
+ href = context.getConfiguration().getDefaultCollection();
+ } else {
+ Item arg = arguments[0].head();
+ if (arg == null) {
+ href = context.getConfiguration().getDefaultCollection();
+ } else {
+ href = arg.getStringValue();
+ }
+ }
+
+ CollectionURIResolver resolver = context.getConfiguration().getCollectionURIResolver();
+ SequenceIterator iter;
+ try {
+ iter = resolver.resolve(href, expressionBaseURI, context);
+ } catch (XPathException e) {
+ e.setLocator(this);
+ throw e;
+ }
+ return SequenceTool.toLazySequence(getResolverResults(iter, expressionBaseURI, context, this));
+ }
+
+
+ // TODO: provide control over error recovery (etc) through options in the catalog file.
+
+}
+
diff --git a/sf/saxon/functions/Compare.java b/sf/saxon/functions/Compare.java
new file mode 100644
index 0000000..0285047
--- /dev/null
+++ b/sf/saxon/functions/Compare.java
@@ -0,0 +1,108 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * This class implements the XPath 2.0 fn:compare() function
+ */
+
+// Supports string comparison using a collation
+
+public class Compare extends CollatingFunction implements Callable {
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 2;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return new IntegerValue[] {Int64Value.MINUS_ONE, Int64Value.PLUS_ONE};
+ }
+
+ @Override
+ public void checkArguments(ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType type0 = argument[0].getItemType(th);
+ ItemType type1 = argument[1].getItemType(th);
+ if (type0 instanceof AtomicType && type1 instanceof AtomicType) {
+ preAllocateComparer((AtomicType)type0, (AtomicType)type1, visitor.getStaticContext(), false);
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ /*@Nullable*/ public Int64Value evaluateItem(XPathContext context) throws XPathException {
+ StringValue arg0 = (StringValue)argument[0].evaluateItem(context);
+ StringValue arg1 = (StringValue)argument[1].evaluateItem(context);
+ AtomicComparer comparer = getPreAllocatedAtomicComparer();
+ if (comparer == null) {
+ comparer = getAtomicComparer(getCollator(context), context);
+ }
+ return compare(arg0, arg1, comparer);
+ }
+
+ private static Int64Value compare(StringValue s1, StringValue s2, AtomicComparer comparer) throws XPathException {
+ if (s1==null || s2==null) {
+ return null;
+ }
+ int result = comparer.compareAtomicValues(s1, s2);
+ if (result < 0) {
+ return Int64Value.MINUS_ONE;
+ } else if (result > 0) {
+ return Int64Value.PLUS_ONE;
+ } else {
+ return Int64Value.ZERO;
+ }
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue arg0 = (StringValue)arguments[0].head();
+ StringValue arg1 = (StringValue)arguments[1].head();
+ StringCollator collator = getCollatorFromLastArgument(arguments, 2, context);
+ GenericAtomicComparer comparer = new GenericAtomicComparer(collator, context);
+ return compare(arg0, arg1, comparer);
+ }
+
+}
+
diff --git a/sf/saxon/functions/CompileTimeFunction.java b/sf/saxon/functions/CompileTimeFunction.java
new file mode 100644
index 0000000..b7d519e
--- /dev/null
+++ b/sf/saxon/functions/CompileTimeFunction.java
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* Abtract class representing a function call that is always rewritten at compile time:
+* it can never be executed
+*/
+
+public abstract class CompileTimeFunction extends SystemFunctionCall {
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing.
+ * (this is because the default implementation of preEvaluate() calls evaluate() which
+ * is not available for these functions)
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Evaluate as a single item
+ */
+
+ public final Item evaluateItem(XPathContext c) throws XPathException {
+ throw new IllegalStateException("Function " + getName(c) + " should have been resolved at compile time");
+ }
+
+ /**
+ * Iterate over the results of the function
+ */
+
+ /*@NotNull*/
+ public final SequenceIterator iterate(XPathContext c) {
+ throw new IllegalStateException("Function " + getName(c) + " should have been resolved at compile time");
+ }
+
+ /*@NotNull*/ private String getName(XPathContext c) {
+ return getDisplayName();
+ }
+
+}
+
diff --git a/sf/saxon/functions/Component.java b/sf/saxon/functions/Component.java
new file mode 100644
index 0000000..5729e9c
--- /dev/null
+++ b/sf/saxon/functions/Component.java
@@ -0,0 +1,160 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ComponentCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+
+/**
+ * This class supports the get_X_from_Y functions defined in XPath 2.0
+ */
+
+public class Component extends SystemFunctionCall {
+
+ public static final int YEAR = 1;
+ public static final int MONTH = 2;
+ public static final int DAY = 3;
+ public static final int HOURS = 4;
+ public static final int MINUTES = 5;
+ public static final int SECONDS = 6;
+ public static final int TIMEZONE = 7;
+ public static final int LOCALNAME = 8;
+ public static final int NAMESPACE = 9;
+ public static final int PREFIX = 10;
+ public static final int MICROSECONDS = 11; // internal use only
+ public static final int WHOLE_SECONDS = 12; // internal use only
+ public static final int YEAR_ALLOWING_ZERO = 13; // internal use only
+
+ int component;
+
+ /**
+ * Set the details of this type of function
+ *
+ * @param entry information giving details of the function signature
+ */
+ @Override
+ public void setDetails(StandardFunction.Entry entry) {
+ super.setDetails(entry);
+ component = (operation >> 16) & 0xffff;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ switch (component) {
+ case YEAR:
+ return new IntegerValue[]{Int64Value.makeIntegerValue(-100000), Int64Value.makeIntegerValue(+100000)};
+ case MONTH:
+ return new IntegerValue[]{Int64Value.makeIntegerValue(-11), Int64Value.makeIntegerValue(+11)};
+ case DAY:
+ return new IntegerValue[]{Int64Value.makeIntegerValue(-31), Int64Value.makeIntegerValue(+31)};
+ case HOURS:
+ return new IntegerValue[]{Int64Value.makeIntegerValue(-24), Int64Value.makeIntegerValue(+24)};
+ case MINUTES:
+ return new IntegerValue[]{Int64Value.makeIntegerValue(-59), Int64Value.makeIntegerValue(+59)};
+ case SECONDS:
+ return new IntegerValue[]{Int64Value.makeIntegerValue(-59), Int64Value.makeIntegerValue(+59)};
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Get the required component
+ */
+
+ public int getRequiredComponent() {
+ return component;
+ }
+
+ /**
+ * Get the required component name as a string
+ */
+
+ public String getRequiredComponentAsString() {
+ String[] components = {"", "YEAR", "MONTH", "DAY", "HOURS", "MINUTES", "SECONDS",
+ "TIMEZONE", "LOCALNAME", "NAMESPACE", "PREFIX", "MICROSECONDS",
+ "WHOLE_SECONDS", "YEAR_ALLOWING_ZERO"};
+ return components[component];
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+ AtomicValue arg = (AtomicValue)argument[0].evaluateItem(context);
+
+ if (arg == null) {
+ return null;
+ }
+
+ return arg.getComponent(component);
+
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Component c = (Component)super.copy();
+ c.component = (c.operation >> 16) & 0xffff;
+ return c;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return ((AtomicValue)arguments[0].head()).getComponent(component);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Component expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ComponentCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/Concat.java b/sf/saxon/functions/Concat.java
new file mode 100644
index 0000000..cd1041e
--- /dev/null
+++ b/sf/saxon/functions/Concat.java
@@ -0,0 +1,160 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ConcatCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.functions.hof.SpecificFunctionType;
+import net.sf.saxon.event.ComplexContentOutputter;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.SubExpressionInfo;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.FunctionItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Implementation of the fn:concat() function
+ */
+
+
+public class Concat extends SystemFunctionCall implements Callable {
+
+//#ifdefined HOF
+ /**
+ * Get the item type of the function item
+ *
+ * @param th the type hierarchy cache
+ * @return the function item's type
+ */
+ @Override
+ public FunctionItemType getFunctionItemType(TypeHierarchy th) {
+ SequenceType[] argTypes = new SequenceType[getNumberOfArguments()];
+ Arrays.fill(argTypes, SequenceType.OPTIONAL_ATOMIC);
+ return new SpecificFunctionType(argTypes, SequenceType.SINGLE_STRING);
+ }
+//#endif
+
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ for (Sequence arg : arguments) {
+ Item item = arg.head();
+ if (item != null) {
+ fsb.append(arg.head().getStringValueCS());
+ }
+ }
+ return new StringValue(fsb);
+ }
+
+
+ /**
+ * Get the required type of the nth argument
+ */
+
+ protected SequenceType getRequiredType(int arg) {
+ return getDetails().argumentTypes[0];
+ // concat() is a special case
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ List<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>(argument.length);
+ for (Expression anArgument : argument) {
+ list.add(new SubExpressionInfo(anArgument, true, false, getDetails().syntacticContext[0]));
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Evaluate the function in a string context
+ */
+
+ public CharSequence evaluateAsString(XPathContext c) throws XPathException {
+ return evaluateItem(c).getStringValueCS();
+ }
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ int numArgs = argument.length;
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ for (int i=0; i<numArgs; i++) {
+ AtomicValue val = (AtomicValue)argument[i].evaluateItem(c);
+ if (val!=null) {
+ sb.append(val.getStringValueCS());
+ }
+ }
+ return StringValue.makeStringValue(sb.condense());
+ }
+
+ /**
+ * Process the instruction in push mode. This avoids constructing the concatenated string
+ * in memory, instead each argument can be sent straight to the serializer.
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(/*@NotNull*/ XPathContext context) throws XPathException {
+ SequenceReceiver out = context.getReceiver();
+ if (out instanceof ComplexContentOutputter) {
+ // This optimization is only safe if the output forms part of document or element content
+ int numArgs = argument.length;
+ // Start and end with an empty string to force space separation from any previous or following outputs
+ out.append(StringValue.EMPTY_STRING, 0, 0);
+ boolean empty = true;
+ for (int i=0; i<numArgs; i++) {
+ AtomicValue val = (AtomicValue)argument[i].evaluateItem(context);
+ if (val!=null) {
+ out.characters(val.getStringValueCS(), 0, 0);
+ empty = false;
+ }
+ }
+ if (!empty) {
+ out.append(StringValue.EMPTY_STRING, 0, 0);
+ }
+ } else {
+ out.append(evaluateItem(context), 0, 0);
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Concat expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ConcatCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/ConstructorFunctionLibrary.java b/sf/saxon/functions/ConstructorFunctionLibrary.java
new file mode 100644
index 0000000..5b3bd8f
--- /dev/null
+++ b/sf/saxon/functions/ConstructorFunctionLibrary.java
@@ -0,0 +1,243 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.functions.hof.AtomicConstructorFunctionItem;
+import com.saxonica.functions.hof.CallableFunctionItem;
+import com.saxonica.functions.hof.SpecificFunctionType;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SavedNamespaceContext;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * The ConstructorFunctionLibrary represents the collection of constructor functions for atomic types. These
+ * are provided for the built-in types such as xs:integer and xs:date, and also for user-defined atomic types.
+ */
+
+public class ConstructorFunctionLibrary implements FunctionLibrary {
+
+ private Configuration config;
+
+ /**
+ * Create a SystemFunctionLibrary
+ *
+ * @param config the Configuration
+ */
+
+ public ConstructorFunctionLibrary(Configuration config) {
+ this.config = config;
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ * <p/>
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ * @throws net.sf.saxon.trans.XPathException
+ * in the event of certain errors, for example attempting to get a function
+ * that is private
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext) throws XPathException {
+ if (arity != 1) {
+ return null;
+ }
+ final String uri = functionName.getURI();
+ final String localName = functionName.getLocalPart();
+ int fp = config.getNamePool().allocate("", uri, localName);
+ final SchemaType type = config.getSchemaType(fp);
+ if (type == null || type.isComplexType()) {
+ return null;
+ }
+ final NamespaceResolver resolver = (((SimpleType)type).isNamespaceSensitive() ? staticContext.getNamespaceResolver() : null);
+ if (type instanceof AtomicType) {
+ return new AtomicConstructorFunctionItem((AtomicType)type, resolver);
+ } else if (type instanceof ListType) {
+ SimpleType itemType = ((ListType)type).getItemType();
+// return new ListConstructorFunctionItem((ListType)type, resolver);
+ Callable callable = new Callable() {
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ AtomicValue value = (AtomicValue)arguments[0].head();
+ if (value == null) {
+ return EmptySequence.getInstance();
+ }
+ if (value instanceof StringValue && !(value instanceof AnyURIValue)) {
+ return CastToList.cast(value.getStringValue(), (ListType) type, resolver, context.getConfiguration().getConversionRules());
+ }
+ throw new XPathException("When casting to list, the input must be of type xs:string or xs:untypedAtomic", "XPTY0004");
+ }
+ };
+ SpecificFunctionType functionType;
+ if (itemType.isAtomicType()) {
+ functionType = new SpecificFunctionType(
+ new SequenceType[]{SequenceType.OPTIONAL_ATOMIC},
+ SequenceType.makeSequenceType((AtomicType)itemType, StaticProperty.ALLOWS_ZERO_OR_MORE));
+ } else {
+ functionType = new SpecificFunctionType(
+ new SequenceType[]{SequenceType.OPTIONAL_ATOMIC},
+ SequenceType.ATOMIC_SEQUENCE);
+ }
+ return new CallableFunctionItem(null, 1, callable, functionType);
+ } else {
+ return staticContext.getConfiguration().obtainOptimizer().makeCastToUnion((UnionType) type, resolver);
+ }
+ }
+
+ //#endif
+
+
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ if (arity != 1) {
+ return false;
+ }
+ final String uri = functionName.getURI();
+ final String localName = functionName.getLocalPart();
+ int fp = config.getNamePool().allocate("", uri, localName);
+ final SchemaType type = config.getSchemaType(fp);
+ if (type == null || type.isComplexType()) {
+ return false;
+ }
+ if (type.isAtomicType() && ((AtomicType)type).isAbstract()) {
+ return false;
+ }
+ if (type == AnySimpleType.getInstance()) {
+ return false;
+ } if (type.isAtomicType() && ((AtomicType)type).isAbstract()) {
+ return false;
+ }
+ if (type == AnySimpleType.getInstance()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Bind a static function call, given the URI and local parts of the function name,
+ * and the list of expressions supplied as arguments. This method is called at compile
+ * time.
+ *
+ *
+ *
+ * @param functionName The QName of the function
+ * @param arity The number of arguments
+ * @param arguments The expressions supplied statically in the function call. The intention is
+ * that the static type of the arguments (obtainable via getItemType() and getCardinality() may
+ * be used as part of the binding algorithm.
+ * @param env The static context
+ * @param container A container to provide location information for the expression
+ * @return An object representing the constructor function to be called, if one is found;
+ * null if no constructor function was found matching the required name and arity.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a function is found with the required name and arity, but
+ * the implementation of the function cannot be loaded or used; or if an error occurs
+ * while searching for the function; or if this function library "owns" the namespace containing
+ * the function call, but no function was found.
+ */
+
+ public Expression bind(StructuredQName functionName, int arity, Expression[] arguments, StaticContext env, Container container)
+ throws XPathException {
+ final String uri = functionName.getURI();
+ final String localName = functionName.getLocalPart();
+ boolean builtInNamespace = uri.equals(NamespaceConstant.SCHEMA);
+ if (builtInNamespace) {
+ // it's a constructor function: treat it as shorthand for a cast expression
+ if (arguments.length != 1) {
+ throw new XPathException("A constructor function must have exactly one argument");
+ }
+ SimpleType type = Type.getBuiltInSimpleType(uri, localName);
+ if (type != null) {
+ if (type.isAtomicType()) {
+ if (((AtomicType)type).isAbstract()) {
+ XPathException err = new XPathException("Abstract type used in constructor function: {" + uri + '}' + localName);
+ err.setErrorCode("XPST0017");
+ err.setIsStaticError(true);
+ throw err;
+ } else {
+ CastExpression cast = new CastExpression(arguments[0], (AtomicType)type, true);
+ if (arguments[0] instanceof StringLiteral) {
+ cast.setOperandIsStringLiteral(true);
+ }
+ if (type.isNamespaceSensitive()) {
+ cast.setNamespaceResolver(new SavedNamespaceContext(env.getNamespaceResolver()));
+ }
+ cast.setContainer(container);
+ return cast;
+ }
+ } else if (type == ErrorType.getInstance()) {
+ return new CastToUnion(arguments[0], ErrorType.getInstance(), true);
+ } else {
+ assert type.isListType();
+ return new CastToList(arguments[0], (ListType) type, true);
+ }
+ } else {
+ XPathException err = new XPathException("Unknown constructor function: {" + uri + '}' + localName);
+ err.setErrorCode("XPST0017");
+ err.setIsStaticError(true);
+ throw err;
+ }
+
+ }
+
+ // Now see if it's a constructor function for a user-defined type
+
+ if (arguments.length == 1) {
+ int fp = config.getNamePool().getFingerprint(uri, localName);
+ if (fp != -1) {
+ SchemaType st = config.getSchemaType(fp);
+ if (st instanceof SimpleType) {
+ if (st instanceof AtomicType) {
+ Expression cast = new CastExpression(arguments[0], (AtomicType) st, true);
+ cast.setContainer(container);
+ return cast;
+ } else if (st instanceof ListType && DecimalValue.THREE.equals(env.getXPathLanguageLevel())) {
+ return new CastToList(arguments[0], (ListType) st, true);
+ } else if (((SimpleType)st).isUnionType() && DecimalValue.THREE.equals(env.getXPathLanguageLevel())) {
+ // we have a cast to a union type
+ Expression cast = env.getConfiguration().obtainOptimizer().makeCastToUnion(arguments[0], st, true);
+ cast.setContainer(container);
+ return cast;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ *
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ public FunctionLibrary copy() {
+ return this;
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/functions/Contains.java b/sf/saxon/functions/Contains.java
new file mode 100644
index 0000000..a656955
--- /dev/null
+++ b/sf/saxon/functions/Contains.java
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ContainsCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.RuleBasedSubstringMatcher;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.lib.SubstringMatcher;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.StringValue;
+
+import java.text.RuleBasedCollator;
+
+/**
+ * Implements the fn:contains() function
+ */
+public class Contains extends CollatingFunction implements Callable {
+
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 2;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public BooleanValue evaluateItem(/*@NotNull*/ XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ * @param context The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ try {
+ StringValue arg0 = (StringValue)argument[0].evaluateItem(context);
+ StringValue arg1 = (StringValue)argument[1].evaluateItem(context);
+ return contains(arg0, arg1, getCollator(context));
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+ private static boolean contains(StringValue arg0, StringValue arg1, StringCollator collator) throws XPathException{
+ if (arg1==null || arg1.isZeroLength()) {
+ return true;
+ }
+ if (arg0==null || arg0.isZeroLength()) {
+ return false;
+ }
+ String s0 = arg0.getStringValue();
+ String s1 = arg1.getStringValue();
+ if (collator instanceof CodepointCollator) {
+ // fast path for this common case
+ return s0.indexOf(s1, 0) >= 0;
+ } else {
+ if (collator instanceof SimpleCollation &&
+ ((SimpleCollation)collator).getCollation() instanceof RuleBasedCollator) {
+ collator = new RuleBasedSubstringMatcher((RuleBasedCollator)((SimpleCollation)collator).getCollation());
+ }
+
+ if (collator instanceof SubstringMatcher) {
+ return ((SubstringMatcher)collator).contains(s0, s1);
+ } else {
+ throw new XPathException("The collation requested for fn:contains does not support substring matching", "FOCH0004");
+ }
+ }
+ }
+
+
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ try {
+ StringValue s0 = (StringValue)arguments[0].head();
+ StringValue s1 = (StringValue)arguments[1].head();
+ return BooleanValue.get( contains(s0, s1, getCollatorFromLastArgument(arguments, 2, context)));
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Contains expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ContainsCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/Count.java b/sf/saxon/functions/Count.java
new file mode 100644
index 0000000..294c7fd
--- /dev/null
+++ b/sf/saxon/functions/Count.java
@@ -0,0 +1,114 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.CountCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.util.CannotCompileException;
+import com.saxonica.stream.adjunct.CountAdjunct;
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+
+/**
+ * Implementation of the fn:count function
+ */
+public class Count extends Aggregate {
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | WATCH_METHOD;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return new IntegerValue[]{Int64Value.ZERO, MAX_SEQUENCE_LENGTH};
+ }
+
+//#ifdefined STREAM
+
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public CountAdjunct getStreamingAdjunct() {
+ return new CountAdjunct();
+ }
+//#endif
+
+ /**
+ * Evaluate the function
+ */
+
+ public Int64Value evaluateItem(XPathContext context) throws XPathException {
+ SequenceIterator iter = argument[0].iterate(context);
+ return new Int64Value(count(iter));
+ }
+
+ /**
+ * Get the number of items in a sequence identified by a SequenceIterator
+ * @param iter The SequenceIterator. This method moves the current position
+ * of the supplied iterator; if this isn't safe, make a copy of the iterator
+ * first by calling getAnother(). The supplied iterator must be positioned
+ * before the first item (there must have been no call on next()).
+ * @return the number of items in the underlying sequence
+ * @throws net.sf.saxon.trans.XPathException if a failure occurs reading the input sequence
+ */
+
+ public static int count(/*@NotNull*/ SequenceIterator iter) throws XPathException {
+ if ((iter.getProperties() & SequenceIterator.LAST_POSITION_FINDER) != 0) {
+ return ((LastPositionFinder)iter).getLength();
+ } else {
+ int n = 0;
+ while (iter.next() != null) {
+ n++;
+ }
+ return n;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return new Int64Value(count(arguments[0].iterate()));
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public ExpressionCompiler getExpressionCompiler() throws CannotCompileException {
+ return new CountCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/Current.java b/sf/saxon/functions/Current.java
new file mode 100644
index 0000000..89a49ed
--- /dev/null
+++ b/sf/saxon/functions/Current.java
@@ -0,0 +1,65 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Implement the XSLT current() function
+ */
+
+public class Current extends CompileTimeFunction {
+
+ /**
+ * The name of the Current function
+ */
+
+ /*@NotNull*/ public static final StructuredQName FN_CURRENT =
+ new StructuredQName("", NamespaceConstant.FN, "current");
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ return StaticProperty.CONTEXT_DOCUMENT_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET |
+ StaticProperty.ORDERED_NODESET |
+ StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Determine the dependencies
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CURRENT_ITEM | StaticProperty.DEPENDS_ON_LOCAL_VARIABLES;
+ // the expression will be replaced by a local variable, so record the dependency now
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ throw new XPathException("Dynamic evaluation of the current() function is not supported");
+ }
+}
+
diff --git a/sf/saxon/functions/CurrentDateTime.java b/sf/saxon/functions/CurrentDateTime.java
new file mode 100644
index 0000000..9ac16f0
--- /dev/null
+++ b/sf/saxon/functions/CurrentDateTime.java
@@ -0,0 +1,82 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Converter;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.DateTimeValue;
+
+/**
+* This class implements the XPath 2.0 functions
+ * current-date(), current-time(), and current-dateTime(), as
+ * well as the function implicit-timezone(). The value that is required
+ * is inferred from the type of result required.
+*/
+
+
+public class CurrentDateTime extends SystemFunctionCall implements Callable {
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * (because the value of the expression depends on the runtime context)
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Determine the dependencies
+ */
+
+ public int getIntrinsicDependencies() {
+ // current date/time is part of the context, but it is fixed for a transformation, so
+ // we don't need to manage it as a dependency: expressions using it can be freely
+ // rearranged
+ return StaticProperty.DEPENDS_ON_RUNTIME_ENVIRONMENT;
+ }
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+ final DateTimeValue dt = DateTimeValue.getCurrentDateTime(context);
+ final TypeHierarchy th = context.getConfiguration().getTypeHierarchy();
+ final int targetType = getItemType(th).getPrimitiveType();
+ switch (targetType) {
+ case StandardNames.XS_DATE_TIME:
+ return dt;
+ case StandardNames.XS_DATE:
+ return Converter.DATE_TIME_TO_DATE.convert(dt).asAtomic();
+ case StandardNames.XS_TIME:
+ return Converter.DATE_TIME_TO_TIME.convert(dt).asAtomic();
+ case StandardNames.XS_DAY_TIME_DURATION:
+ case StandardNames.XS_DURATION:
+ return dt.getComponent(Component.TIMEZONE);
+ default:
+ throw new IllegalArgumentException("Wrong target type for current date/time");
+ }
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return evaluateItem(context);
+ }
+
+}
+
diff --git a/sf/saxon/functions/CurrentGroup.java b/sf/saxon/functions/CurrentGroup.java
new file mode 100644
index 0000000..a2e1e5e
--- /dev/null
+++ b/sf/saxon/functions/CurrentGroup.java
@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.sort.GroupIterator;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.DecimalValue;
+
+/**
+* Implements the XSLT function current-group()
+*/
+
+public class CurrentGroup extends SystemFunctionCall implements Callable {
+
+ boolean is30 = false;
+
+ @Override
+ public void checkArguments(ExpressionVisitor visitor) throws XPathException {
+ is30 = visitor.getStaticContext().getXPathLanguageLevel().equals(DecimalValue.THREE);
+ super.checkArguments(visitor);
+ }
+
+ /**
+ * Determine the dependencies
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CURRENT_GROUP;
+ }
+
+ /**
+ * Return an iteration over the result sequence
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext c) throws XPathException {
+ GroupIterator gi = c.getCurrentGroupIterator();
+ if (gi==null || !gi.hasCurrentGroup()) {
+ if (is30) {
+ XPathException err = new XPathException("There is no current group", "XTDE1061");
+ err.setLocator(this);
+ throw err;
+ } else {
+ return EmptyIterator.emptyIterator();
+ }
+ }
+ return gi.iterateCurrentGroup();
+ }
+
+ /**
+ * Evaluate the expression
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments /*@NotNull*/) throws XPathException {
+ return SequenceTool.toLazySequence(iterate(context));
+ }
+}
+
diff --git a/sf/saxon/functions/CurrentGroupingKey.java b/sf/saxon/functions/CurrentGroupingKey.java
new file mode 100644
index 0000000..049b585
--- /dev/null
+++ b/sf/saxon/functions/CurrentGroupingKey.java
@@ -0,0 +1,82 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.sort.GroupIterator;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.DecimalValue;
+
+
+/**
+ * Implements the XSLT function current-grouping-key()
+ */
+
+public class CurrentGroupingKey extends SystemFunctionCall implements Callable {
+
+ boolean is30 = false;
+
+ @Override
+ public void checkArguments(ExpressionVisitor visitor) throws XPathException {
+ is30 = visitor.getStaticContext().getXPathLanguageLevel().equals(DecimalValue.THREE);
+ super.checkArguments(visitor);
+ }
+
+ /**
+ * Determine the dependencies
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CURRENT_GROUP;
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ /*@NotNull*/
+ @Override
+ public SequenceIterator iterate(XPathContext c) throws XPathException {
+ GroupIterator gi = c.getCurrentGroupIterator();
+ if (gi == null || !gi.hasCurrentGroupingKey()) {
+ if (is30) {
+ XPathException err = new XPathException("There is no current grouping key", "XTDE1071");
+ err.setLocator(this);
+ throw err;
+ } else {
+ return EmptyIterator.emptyIterator();
+ }
+ }
+ return gi.getCurrentGroupingKey().iterate();
+
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return SequenceTool.toLazySequence(iterate(context));
+ }
+
+}
+
diff --git a/sf/saxon/functions/Data.java b/sf/saxon/functions/Data.java
new file mode 100644
index 0000000..4e812b0
--- /dev/null
+++ b/sf/saxon/functions/Data.java
@@ -0,0 +1,78 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+* Implement XPath function fn:data()
+*/
+
+public class Data extends CompileTimeFunction implements Callable {
+
+ /**
+ * Simplify and validate.
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ if (argument.length==0) {
+ argument = new Expression[1];
+ argument[0] = new ContextItemExpression();
+ }
+ Expression a = Atomizer.makeAtomizer(argument[0]);
+ ExpressionTool.copyLocationInfo(this, a);
+ return visitor.simplify(a);
+ }
+
+ @Override
+ public int getIntrinsicDependencies() {
+ if (getNumberOfArguments() == 0) {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ } else {
+ return super.getIntrinsicDependencies();
+ }
+ }
+
+ /**
+ * Evaluate the expression. (Used for run-time evaluation via function-lookup().)
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ Sequence arg;
+ if (arguments.length == 0) {
+ arg = context.getContextItem();
+ } else {
+ arg = arguments[0];
+ }
+ if (arg instanceof Item) {
+ if (arg instanceof NodeInfo) {
+ return ((NodeInfo)arg).atomize();
+ } else if (arg instanceof AtomicValue) {
+ return arg;
+ } else {
+ throw new XPathException("Cannot atomize a function item or external value", "FOTY0017");
+ }
+ } else {
+ SequenceIterator a = Atomizer.getAtomizingIterator(arg.iterate(), false);
+ return SequenceTool.toLazySequence(a);
+ }
+ }
+}
+
diff --git a/sf/saxon/functions/DateTimeConstructor.java b/sf/saxon/functions/DateTimeConstructor.java
new file mode 100644
index 0000000..e6ee8f5
--- /dev/null
+++ b/sf/saxon/functions/DateTimeConstructor.java
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.DateTimeConstructorCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.DateTimeValue;
+import net.sf.saxon.value.DateValue;
+import net.sf.saxon.value.TimeValue;
+
+
+/**
+* This class supports the dateTime($date, $time) function
+*/
+
+public class DateTimeConstructor extends SystemFunctionCall {
+
+ /**
+ * Evaluate the expression
+ */
+
+ public DateTimeValue evaluateItem(XPathContext context) throws XPathException {
+ DateValue arg0 = (DateValue)argument[0].evaluateItem(context);
+ TimeValue arg1 = (TimeValue)argument[1].evaluateItem(context);
+ try {
+ return DateTimeValue.makeDateTimeValue(arg0, arg1);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ DateValue arg0 = (DateValue)arguments[0].head();
+ TimeValue arg1 = (TimeValue)arguments[1].head();
+ try {
+ return DateTimeValue.makeDateTimeValue(arg0, arg1);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the DateTimeConstructor expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new DateTimeConstructorCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/DeepEqual.java b/sf/saxon/functions/DeepEqual.java
new file mode 100644
index 0000000..f0ce556
--- /dev/null
+++ b/sf/saxon/functions/DeepEqual.java
@@ -0,0 +1,627 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.EarlyEvaluationContext;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.tiny.WhitespaceTextImpl;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import javax.xml.transform.TransformerException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * XSLT 2.0 deep-equal() function.
+ * Supports deep comparison of two sequences (of nodes and/or atomic values)
+ * optionally using a collation
+ */
+
+public class DeepEqual extends CollatingFunction implements Callable {
+
+ /**
+ * Flag indicating that two elements should only be considered equal if they have the same
+ * in-scope namespaces
+ */
+ public static final int INCLUDE_NAMESPACES = 1;
+
+ /**
+ * Flag indicating that two element or attribute nodes are considered equal only if their
+ * names use the same namespace prefix
+ */
+ public static final int INCLUDE_PREFIXES = 1<<1;
+
+ /**
+ * Flag indicating that comment children are taken into account when comparing element or document nodes
+ */
+ public static final int INCLUDE_COMMENTS = 1<<2;
+
+ /**
+ * Flag indicating that processing instruction nodes are taken into account when comparing element or document nodes
+ */
+ public static final int INCLUDE_PROCESSING_INSTRUCTIONS = 1<<3;
+
+ /**
+ * Flag indicating that whitespace text nodes are ignored when comparing element nodes
+ */
+ public static final int EXCLUDE_WHITESPACE_TEXT_NODES = 1<<4;
+
+ /**
+ * Flag indicating that elements and attributes should always be compared according to their string
+ * value, not their typed value
+ */
+ public static final int COMPARE_STRING_VALUES = 1<<5;
+
+ /**
+ * Flag indicating that elements and attributes must have the same type annotation to be considered
+ * deep-equal
+ */
+ public static final int COMPARE_ANNOTATIONS = 1<<6;
+
+ /**
+ * Flag indicating that a warning message explaining the reason why the sequences were deemed non-equal
+ * should be sent to the ErrorListener
+ */
+ public static final int WARNING_IF_FALSE = 1<<7;
+
+ /**
+ * Flag indicating that adjacent text nodes in the top-level sequence are to be merged
+ */
+
+ public static final int JOIN_ADJACENT_TEXT_NODES = 1<<8;
+
+ /**
+ * Flag indicating that the is-id and is-idref flags are to be compared
+ */
+
+ public static final int COMPARE_ID_FLAGS = 1<<9;
+
+ /**
+ * Flag indicating that the variety of the type of a node is to be ignored (for example, a mixed content
+ * node can compare equal to an element-only content node
+ */
+
+ public static final int EXCLUDE_VARIETY = 1<<10;
+
+ @Override
+ public void checkArguments(ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType type0 = argument[0].getItemType(th);
+ ItemType type1 = argument[1].getItemType(th);
+ if (type0 instanceof AtomicType && type1 instanceof AtomicType) {
+ preAllocateComparer((AtomicType)type0, (AtomicType)type1, visitor.getStaticContext(), false);
+ }
+ }
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 2;
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+
+ SequenceIterator op1 = argument[0].iterate(context);
+ SequenceIterator op2 = argument[1].iterate(context);
+ AtomicComparer comparer = getPreAllocatedAtomicComparer();
+ if (comparer == null) {
+ comparer = getAtomicComparer(getCollator(context), context);
+ } else if(!(context instanceof EarlyEvaluationContext)) {
+ comparer = comparer.provideContext(context);
+ }
+ try {
+ return BooleanValue.get(deepEquals(op1, op2, comparer, context, 0));
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+ /*
+ * Evaluate the expression
+ * Common-up code.
+ */
+ private BooleanValue deepEqual(SequenceIterator op1, SequenceIterator op2, AtomicComparer comparer, XPathContext context) throws XPathException {
+ try {
+ return BooleanValue.get(deepEquals(op1, op2, comparer, context, 0));
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+ /**
+ * Determine when two sequences are deep-equal
+ * @param op1 the first sequence
+ * @param op2 the second sequence
+ * @param collator the collator to be used
+ * @param context the XPathContext item
+ * @param flags bit-significant integer giving comparison options. Always zero for standard
+ * F+O deep-equals comparison.
+ * @return true if the sequences are deep-equal
+ * @throws XPathException if either sequence contains a function item
+ */
+
+ public static boolean deepEquals(SequenceIterator op1, SequenceIterator op2,
+ AtomicComparer collator, XPathContext context, int flags)
+ throws XPathException {
+ boolean result = true;
+ String reason = null;
+
+
+ try {
+
+ if ((flags & JOIN_ADJACENT_TEXT_NODES) != 0) {
+ op1 = mergeAdjacentTextNodes(op1);
+ op2 = mergeAdjacentTextNodes(op2);
+ }
+ while (true) {
+ Item item1 = op1.next();
+ Item item2 = op2.next();
+
+ if (item1 == null && item2 == null) {
+ break;
+ }
+
+ if (item1 == null || item2 == null) {
+ result = false;
+ if (item1 == null) {
+ reason = "Second sequence is longer (first sequence length = " + op2.position() + ")";
+ } else {
+ reason = "First sequence is longer (second sequence length = " + op1.position() + ")";
+ }
+ if (item1 instanceof WhitespaceTextImpl || item2 instanceof WhitespaceTextImpl) {
+ reason += " (the first extra node is whitespace text)";
+ }
+ break;
+ }
+
+ if (item1 instanceof FunctionItem || item2 instanceof FunctionItem) {
+ if (item1 instanceof FunctionItem && item2 instanceof FunctionItem) {
+ // two maps can be deep-equal
+ //XPathContext context = new EarlyEvaluationContext(config, config.getCollationMap());
+ boolean fe = ((FunctionItem)item1).deepEquals(((FunctionItem)item2), context, collator, flags);
+ if (!fe) {
+ result = false;
+ reason = "maps at position " + op1.position() + " differ";
+ break;
+ }
+ return fe;
+ } else {
+ throw new XPathException("Argument to deep-equal() contains a function item", "FOTY0015");
+ }
+ }
+
+ if (item1 instanceof ObjectValue || item2 instanceof ObjectValue) {
+ if (item1 instanceof ObjectValue && item2 instanceof ObjectValue) {
+ boolean oe = item1.equals(item2);
+ if (!oe) {
+ result = false;
+ reason = "external objects at position " + op1.position() + " differ";
+ break;
+ }
+ return oe;
+ } else {
+ result = false;
+ reason = "external object at position " + op1.position();
+ break;
+ }
+ }
+
+ if (item1 instanceof NodeInfo) {
+ if (item2 instanceof NodeInfo) {
+ if (!deepEquals((NodeInfo)item1, (NodeInfo)item2, collator, context.getConfiguration(), flags)) {
+ result = false;
+ reason = "nodes at position " + op1.position() + " differ";
+ break;
+ }
+ } else {
+ result = false;
+ reason = "comparing a node to an atomic value at position " + op1.position();
+ break;
+ }
+ } else {
+ if (item2 instanceof NodeInfo) {
+ result = false;
+ reason = "comparing an atomic value to a node at position " + op1.position();
+ break;
+ } else {
+ AtomicValue av1 = ((AtomicValue)item1);
+ AtomicValue av2 = ((AtomicValue)item2);
+ if (av1.isNaN() && av2.isNaN()) {
+ // treat as equal, no action
+ } else if (!collator.comparesEqual(av1, av2)) {
+ result = false;
+ reason = "atomic values at position " + op1.position() + " differ";
+ break;
+ }
+ }
+ }
+ } // end while
+
+ } catch (ClassCastException err) {
+ // this will happen if the sequences contain non-comparable values
+ // comparison errors are masked
+ //err.printStackTrace();
+ result = false;
+ reason = "sequences contain non-comparable values";
+ }catch(NoDynamicContextException err){
+ throw err;
+ }
+ catch (XPathException err) {
+ // comparison errors are masked
+ if ("FOTY0015".equals(err.getErrorCodeLocalPart()) && NamespaceConstant.ERR.equals(err.getErrorCodeNamespace())) {
+ throw err;
+ }
+ result = false;
+ reason = "error occurred while comparing two values (" + err.getMessage() + ')';
+ }
+
+ if (!result) {
+ explain(context.getConfiguration(), reason, flags, null, null);
+ // config.getErrorListener().warning(
+ // new XPathException("deep-equal(): " + reason)
+ // );
+ }
+
+ return result;
+ }
+
+ /*
+ * Determine whether two nodes are deep-equal
+ */
+
+ private static boolean deepEquals(NodeInfo n1, NodeInfo n2,
+ AtomicComparer comparer, Configuration config, int flags)
+ throws XPathException {
+ // shortcut: a node is always deep-equal to itself
+ if (n1.isSameNodeInfo(n2)) return true;
+
+ if (n1.getNodeKind() != n2.getNodeKind()) {
+ explain(config, "node kinds differ: comparing " + Type.displayTypeName(n1) + " to " + Type.displayTypeName(n2), flags, n1, n2);
+ return false;
+ }
+
+ final NamePool pool = config.getNamePool();
+ switch (n1.getNodeKind()) {
+ case Type.ELEMENT:
+ if (n1.getFingerprint() != n2.getFingerprint()) {
+ explain(config, "element names differ: " + config.getNamePool().getClarkName(n1.getFingerprint()) +
+ " != " + config.getNamePool().getClarkName(n2.getFingerprint()), flags, n1, n2);
+ return false;
+ }
+ if (((flags & INCLUDE_PREFIXES) != 0) && (n1.getNameCode() != n2.getNameCode())) {
+ explain(config, "element prefixes differ: " + n1.getPrefix() +
+ " != " + n2.getPrefix(), flags, n1, n2);
+ return false;
+ }
+ AxisIterator a1 = n1.iterateAxis(AxisInfo.ATTRIBUTE);
+ AxisIterator a2 = n2.iterateAxis(AxisInfo.ATTRIBUTE);
+ if (Count.count(a1.getAnother()) != Count.count(a2)) {
+ explain(config, "elements have different number of attributes", flags, n1, n2);
+ return false;
+ }
+ while (true) {
+ NodeInfo att1 = a1.next();
+ if (att1 == null) break;
+
+ AxisIterator a2iter = n2.iterateAxis(AxisInfo.ATTRIBUTE,
+ new NameTest(Type.ATTRIBUTE, att1.getFingerprint(), pool));
+ NodeInfo att2 = a2iter.next();
+
+ if (att2==null) {
+ explain(config, "one element has an attribute " +
+ config.getNamePool().getClarkName(att1.getFingerprint()) +
+ ", the other does not", flags, n1, n2);
+ return false;
+ }
+ if (!deepEquals(att1, att2, comparer, config, flags)) {
+ deepEquals(att1, att2, comparer, config, flags);
+ explain(config, "elements have different values for the attribute " +
+ config.getNamePool().getClarkName(att1.getFingerprint()), flags, n1, n2);
+ return false;
+ }
+ }
+ if ((flags & INCLUDE_NAMESPACES) != 0) {
+ HashSet<NamespaceBinding> ns1 = new HashSet<NamespaceBinding>(10);
+ HashSet<NamespaceBinding> ns2 = new HashSet<NamespaceBinding>(10);
+ AxisIterator it1 = n1.iterateAxis(AxisInfo.NAMESPACE);
+ while (true) {
+ NodeInfo nn1 = it1.next();
+ if (nn1 == null) {
+ break;
+ }
+ NamespaceBinding nscode1 = new NamespaceBinding(nn1.getLocalPart(), nn1.getStringValue());
+ ns1.add(nscode1);
+ }
+ AxisIterator it2 = n2.iterateAxis(AxisInfo.NAMESPACE);
+ while (true) {
+ NodeInfo nn2 = it2.next();
+ if (nn2 == null) {
+ break;
+ }
+ NamespaceBinding nscode2 = new NamespaceBinding(nn2.getLocalPart(), nn2.getStringValue());
+ ns2.add(nscode2);
+ }
+ if (!ns1.equals(ns2)) {
+ explain(config, "elements have different in-scope namespaces", flags, n1, n2);
+ return false;
+ }
+ }
+
+ if ((flags & COMPARE_ANNOTATIONS) != 0) {
+ if (!n1.getSchemaType().equals(n2.getSchemaType())) {
+ explain(config, "elements have different type annotation", flags, n1, n2);
+ return false;
+ }
+ }
+
+ if ((flags & EXCLUDE_VARIETY) == 0) {
+ if (n1.getSchemaType().isComplexType() != n2.getSchemaType().isComplexType()) {
+ explain(config, "one element has complex type, the other simple", flags, n1, n2);
+ return false;
+ }
+
+ if (n1.getSchemaType().isComplexType()) {
+ int variety1 = ((ComplexType)n1.getSchemaType()).getVariety();
+ int variety2 = ((ComplexType)n2.getSchemaType()).getVariety();
+ if (variety1 != variety2) {
+ explain(config, "both elements have complex type, but a different variety", flags, n1, n2);
+ return false;
+ }
+ }
+ }
+
+ if ((flags & COMPARE_STRING_VALUES) == 0) {
+ final SchemaType type1 = n1.getSchemaType();
+ final SchemaType type2 = n2.getSchemaType();
+ final boolean isSimple1 = type1.isSimpleType() || ((ComplexType)type1).isSimpleContent();
+ final boolean isSimple2 = type2.isSimpleType() || ((ComplexType)type2).isSimpleContent();
+ if (isSimple1 != isSimple2) {
+ explain(config, "one element has a simple type, the other does not", flags, n1, n2);
+ return false;
+ }
+ if (isSimple1 && isSimple2) {
+ final SequenceIterator v1 = n1.atomize().iterate();
+ final SequenceIterator v2 = n2.atomize().iterate();
+ return deepEquals(v1, v2, comparer, config.getConversionContext(), flags);
+ }
+ }
+
+ if ((flags & COMPARE_ID_FLAGS) != 0) {
+ if (n1.isId() != n2.isId()) {
+ explain(config, "one element is an ID, the other is not", flags, n1, n2);
+ return false;
+ }
+ if (n1.isIdref() != n2.isIdref()) {
+ explain(config, "one element is an IDREF, the other is not", flags, n1, n2);
+ return false;
+ }
+ }
+ // fall through
+ case Type.DOCUMENT:
+ AxisIterator c1 = n1.iterateAxis(AxisInfo.CHILD);
+ AxisIterator c2 = n2.iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo d1 = c1.next();
+ while (d1 != null && isIgnorable(d1, flags)) {
+ d1 = c1.next();
+ }
+ NodeInfo d2 = c2.next();
+ while (d2 != null && isIgnorable(d2, flags)) {
+ d2 = c2.next();
+ }
+ if (d1 == null || d2 == null) {
+ boolean r = (d1 == d2);
+ if (!r) {
+ explain(config, "nodes have different numbers of children", flags, n1, n2);
+ }
+ return r;
+ }
+ if (!deepEquals(d1, d2, comparer, config, flags)) {
+ return false;
+ }
+ }
+
+ case Type.ATTRIBUTE:
+ if (n1.getFingerprint() != n2.getFingerprint()) {
+ explain(config, "attribute names differ: " +
+ config.getNamePool().getClarkName(n1.getFingerprint()) +
+ " != " + config.getNamePool().getClarkName(n2.getFingerprint()), flags, n1, n2);
+ return false;
+ }
+ if (((flags & INCLUDE_PREFIXES) != 0) && (n1.getNameCode() != n2.getNameCode())) {
+ explain(config, "attribute prefixes differ: " + n1.getPrefix() +
+ " != " + n2.getPrefix(), flags, n1, n2);
+ return false;
+ }
+ if ((flags & COMPARE_ANNOTATIONS) != 0) {
+ if (!n1.getSchemaType().equals(n2.getSchemaType())) {
+ explain(config, "attributes have different type annotations", flags, n1, n2);
+ return false;
+ }
+ }
+ boolean ar;
+ if ((flags & COMPARE_STRING_VALUES) == 0) {
+ ar = deepEquals(n1.atomize().iterate(), n2.atomize().iterate(), comparer, config.getConversionContext(), 0);
+ } else {
+ ar = comparer.comparesEqual(
+ new StringValue(n1.getStringValueCS()),
+ new StringValue(n2.getStringValueCS()));
+ }
+ if (!ar) {
+ explain(config, "attribute values differ", flags, n1, n2);
+ return false;
+ }
+ if ((flags & COMPARE_ID_FLAGS) != 0) {
+ if (n1.isId() != n2.isId()) {
+ explain(config, "one attribute is an ID, the other is not", flags, n1, n2);
+ return false;
+ }
+ if (n1.isIdref() != n2.isIdref()) {
+ explain(config, "one attribute is an IDREF, the other is not", flags, n1, n2);
+ return false;
+ }
+ }
+ return true;
+
+
+ case Type.PROCESSING_INSTRUCTION:
+ case Type.NAMESPACE:
+ if (n1.getFingerprint() != n2.getFingerprint()) {
+ explain(config, Type.displayTypeName(n1) + " names differ", flags, n1, n2);
+ return false;
+ }
+ // drop through
+ case Type.TEXT:
+ case Type.COMMENT:
+ boolean vr = (comparer.comparesEqual((AtomicValue)n1.atomize(), (AtomicValue)n2.atomize()));
+ if (!vr && ((flags & WARNING_IF_FALSE) != 0)) {
+ String v1 = n1.atomize().getStringValue();
+ String v2 = n2.atomize().getStringValue();
+ String message = "";
+ if (v1.length() != v2.length()) {
+ message = "lengths (" + v1.length() + "," + v2.length() + ")";
+ }
+ int min = Math.min(v1.length(), v2.length());
+
+ if (v1.substring(0, min).equals(v2.substring(0, min))) {
+ message += " different at char " + min + "(\"" +
+ StringValue.diagnosticDisplay((v1.length() > v2.length() ? v1 : v2).substring(min)) + "\")";
+ } else if (v1.charAt(0) != v2.charAt(0)) {
+ message += " different at start " + "(\"" +
+ v1.substring(0, Math.min(v1.length(), 10)) + "\", \"" +
+ v2.substring(0, Math.min(v2.length(), 10)) + "\")";
+ } else {
+ for (int i=1; i<min; i++) {
+ if (!v1.substring(0, i).equals(v2.substring(0, i))) {
+ message += " different at char " + i + "(\"" +
+ v1.substring(i, Math.min(v1.length(), i+10)) + "\", \"" +
+ v2.substring(i, Math.min(v2.length(), i+10)) + "\")";
+ break;
+ }
+ }
+ }
+ explain(config, Type.displayTypeName(n1) + " values differ (" +
+ Navigator.getPath(n1) + ", " + Navigator.getPath(n2) + "): " +
+ message, flags, n1, n2);
+ }
+ return vr;
+
+ default:
+ throw new IllegalArgumentException("Unknown node type");
+ }
+ }
+
+ private static boolean isIgnorable(NodeInfo node, int flags) {
+ final int kind = node.getNodeKind();
+ if (kind == Type.COMMENT) {
+ return (flags & INCLUDE_COMMENTS)==0;
+ } else if (kind == Type.PROCESSING_INSTRUCTION) {
+ return (flags & INCLUDE_PROCESSING_INSTRUCTIONS)==0;
+ } else if (kind == Type.TEXT) {
+ return ((flags & EXCLUDE_WHITESPACE_TEXT_NODES)!=0) &&
+ Whitespace.isWhite(node.getStringValueCS());
+ }
+ return false;
+ }
+
+ private static void explain(Configuration config, String message, int flags, NodeInfo n1, NodeInfo n2) {
+ try {
+ if ((flags & WARNING_IF_FALSE) != 0) {
+ config.getErrorListener().warning(new XPathException("deep-equal() " +
+ (n1 != null && n2 != null ?
+ "comparing " + Navigator.getPath(n1) + " to " + Navigator.getPath(n2) + ": " :
+ ": ") +
+ message));
+ }
+ } catch (TransformerException e) {
+ //
+ }
+ }
+
+ private static SequenceIterator mergeAdjacentTextNodes(SequenceIterator in) throws XPathException {
+ Configuration config = null;
+ List<Item> items = new ArrayList<Item>(20);
+ boolean prevIsText = false;
+ FastStringBuffer textBuffer = new FastStringBuffer(FastStringBuffer.SMALL);
+ while (true) {
+ Item next = in.next();
+ if (next == null) {
+ break;
+ }
+ if (next instanceof NodeInfo && ((NodeInfo)next).getNodeKind() == Type.TEXT) {
+ textBuffer.append(next.getStringValueCS());
+ prevIsText = true;
+ config = ((NodeInfo)next).getConfiguration();
+ } else {
+ if (prevIsText) {
+ Orphan textNode = new Orphan(config);
+ textNode.setNodeKind(Type.TEXT);
+ textNode.setStringValue(textBuffer.toString()); // must copy the buffer before reusing it
+ items.add(textNode);
+ textBuffer.setLength(0);
+ }
+ prevIsText = false;
+ items.add(next);
+ }
+ }
+ if (prevIsText) {
+ Orphan textNode = new Orphan(config);
+ textNode.setNodeKind(Type.TEXT);
+ textNode.setStringValue(textBuffer.toString()); // must copy the buffer before reusing it
+ items.add(textNode);
+ }
+ SequenceExtent<Item> extent = new SequenceExtent<Item>(items);
+ return extent.iterate();
+ }
+
+ /**
+ * Execute a dynamic call to the function
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences.
+ * @return the result of the evaluation, in the form of a Sequence. It is the responsibility
+ * of the callee to ensure that the type of result conforms to the expected result type.
+ * @throws XPathException
+ */
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringCollator collator = getCollatorFromLastArgument(arguments, 2, context);
+ GenericAtomicComparer comparer = new GenericAtomicComparer(collator, context);
+ return deepEqual(arguments[0].iterate(), arguments[1].iterate(), comparer, context);
+ }
+}
+
diff --git a/sf/saxon/functions/DefaultCollation.java b/sf/saxon/functions/DefaultCollation.java
new file mode 100644
index 0000000..f3f3af2
--- /dev/null
+++ b/sf/saxon/functions/DefaultCollation.java
@@ -0,0 +1,62 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.*;
+
+/**
+* Implement the XPath 2.0 default-collation() function
+*/
+
+public class DefaultCollation extends CompileTimeFunction {
+
+ String defaultCollation;
+
+ /**
+ * Bind aspects of the static context on which the particular function depends
+ *
+ * @param env the static context of the function call
+ * @throws net.sf.saxon.trans.XPathException
+ * if execution with this static context will inevitably fail
+ */
+ @Override
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ defaultCollation = env.getDefaultCollationName();
+ }
+
+ /**
+ * Pre-evaluate the function
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ String s = visitor.getStaticContext().getDefaultCollationName();
+ return new StringLiteral(s);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return new StringValue(defaultCollation);
+ }
+}
+
diff --git a/sf/saxon/functions/DistinctValues.java b/sf/saxon/functions/DistinctValues.java
new file mode 100644
index 0000000..74c463e
--- /dev/null
+++ b/sf/saxon/functions/DistinctValues.java
@@ -0,0 +1,212 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.stream.adjunct.DistinctValuesAdjunct;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.AtomicSortComparer;
+import net.sf.saxon.expr.sort.ComparisonKey;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.AtomicValue;
+
+import java.util.HashSet;
+
+/**
+* The XPath 2.0 distinct-values() function
+*/
+
+public class DistinctValues extends CollatingFunction implements Callable {
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 1;
+ }
+
+ @Override
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType itemType = argument[0].getItemType(th);
+ if (itemType instanceof AtomicType) {
+ preAllocateComparer((AtomicType)itemType, (AtomicType)itemType, visitor.getStaticContext(), true);
+ }
+ }
+
+ /**
+ * Get the AtomicComparer allocated at compile time.
+ * @return the AtomicComparer if one has been allocated at compile time; return null
+ * if the collation is not known until run-time
+ */
+
+ public AtomicComparer getAtomicComparer() {
+ return getPreAllocatedAtomicComparer();
+ }
+
+//#ifdefined STREAM
+
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public DistinctValuesAdjunct getStreamingAdjunct() {
+ return new DistinctValuesAdjunct();
+ }
+//#endif
+
+ /**
+ * Evaluate the function to return an iteration of selected values or nodes.
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ AtomicComparer comparer = getPreAllocatedAtomicComparer();
+ if (comparer != null) {
+ comparer = comparer.provideContext(context);
+ } else {
+ int type = argument[0].getItemType(context.getConfiguration().getTypeHierarchy()).getPrimitiveType();
+ comparer = AtomicSortComparer.makeSortComparer(getCollator(context), type, context);
+ }
+ SequenceIterator iter = argument[0].iterate(context);
+ return new DistinctIterator(iter, comparer);
+ }
+
+ /**
+ * Iterator class to return the distinct values in a sequence
+ */
+
+ public static class DistinctIterator implements SequenceIterator {
+
+ private SequenceIterator base;
+ private AtomicComparer comparer;
+ private int position;
+ /*@Nullable*/ private AtomicValue current;
+ private HashSet<ComparisonKey> lookup = new HashSet<ComparisonKey>(40);
+
+ /**
+ * Create an iterator over the distinct values in a sequence
+ * @param base the input sequence. This must return atomic values only.
+ * @param comparer The comparer used to obtain comparison keys from each value;
+ * these comparison keys are themselves compared using equals().
+ */
+
+ public DistinctIterator(SequenceIterator base, AtomicComparer comparer) {
+ this.base = base;
+ this.comparer = comparer;
+ position = 0;
+ }
+
+ /**
+ * Get the next item in the sequence. <BR>
+ *
+ * @return the next item, or null if there are no more items.
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error occurs retrieving the next item
+ */
+
+ public Item next() throws XPathException {
+ while (true) {
+ AtomicValue nextBase = (AtomicValue)base.next();
+ if (nextBase==null) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ ComparisonKey key = comparer.getComparisonKey(nextBase);
+ if (lookup.add(key)) {
+ // returns true if newly added (if not, keep looking)
+ current = nextBase;
+ position++;
+ return nextBase;
+ }
+ }
+ }
+
+ /**
+ * Get the current value in the sequence (the one returned by the
+ * most recent call on next()). This will be null before the first
+ * call of next().
+ *
+ * @return the current item, the one most recently returned by a call on
+ * next(); or null, if next() has not been called, or if the end
+ * of the sequence has been reached.
+ */
+
+ public Item current() {
+ return current;
+ }
+
+ /**
+ * Get the current position. This will be zero before the first call
+ * on next(), otherwise it will be the number of times that next() has
+ * been called.
+ *
+ * @return the current position, the position of the item returned by the
+ * most recent call of next()
+ */
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Get another SequenceIterator that iterates over the same items as the original,
+ * but which is repositioned at the start of the sequence.
+ *
+ * @return a SequenceIterator that iterates over the same items,
+ * positioned before the first item
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error occurs
+ */
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new DistinctIterator(base.getAnother(), comparer);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringCollator collator = getCollatorFromLastArgument(arguments, 1, context);
+ AtomicComparer comp = AtomicSortComparer.makeSortComparer(collator, StandardNames.XS_ANY_ATOMIC_TYPE, context);
+ return SequenceTool.toLazySequence(new DistinctIterator(arguments[0].iterate(), comp));
+ }
+
+}
+
diff --git a/sf/saxon/functions/Doc.java b/sf/saxon/functions/Doc.java
new file mode 100644
index 0000000..72cdd16
--- /dev/null
+++ b/sf/saxon/functions/Doc.java
@@ -0,0 +1,256 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.DocCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.SingletonItem;
+
+/**
+ * Implement the fn:doc() function - a simplified form of the Document function
+ */
+
+public class Doc extends SystemFunctionCall implements Callable {
+
+ /*@Nullable*/ private String expressionBaseURI = null;
+ private boolean readOnce = false;
+
+ /**
+ * Indicate that the document will be read once only (or that it should be treated as if it
+ * is read once only. This means (a) the document will not be held in memory after all references
+ * to it go out of scope, and (b) if the query or transformation tries to read it again, it will get a new
+ * copy, with different node identities, and potentially with different content. It also means that the
+ * document is eligible for document projection.
+ *
+ * @param once true if this document is to be treated as being read once only
+ */
+
+ public void setReadOnce(boolean once) {
+ readOnce = once;
+ }
+
+ /**
+ * Ask whether this document has been marked as being read once only.
+ *
+ * @return true if the document has been marked as being read once only
+ */
+
+ public boolean isReadOnce() {
+ return readOnce;
+ }
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ if (expressionBaseURI == null) {
+ super.checkArguments(visitor);
+ expressionBaseURI = visitor.getStaticContext().getBaseURI();
+ }
+ }
+
+ /**
+ * Get the static base URI of the expression
+ *
+ * @return the static base URI
+ */
+
+ public String getStaticBaseURI() {
+ return expressionBaseURI;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation unless a configuration option has been
+ * set to allow early evaluation.
+ *
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ Configuration config = visitor.getConfiguration();
+ if (((Boolean) config.getConfigurationProperty(
+ FeatureKeys.PRE_EVALUATE_DOC_FUNCTION)).booleanValue()) {
+ try {
+ AtomicValue hrefVal = (AtomicValue) argument[0].evaluateItem(null);
+ if (hrefVal == null) {
+ return null;
+ }
+ String href = hrefVal.getStringValue();
+ if (href.indexOf('#') >= 0) {
+ return this;
+ }
+ NodeInfo item = DocumentFn.preLoadDoc(href, expressionBaseURI, config, this);
+ if (item != null) {
+ return Literal.makeLiteral(new SingletonItem(item));
+ }
+ } catch (Exception err) {
+ // ignore the exception and try again at run-time
+ return this;
+ }
+ }
+ return this;
+ }
+
+ public int computeCardinality() {
+ return argument[0].getCardinality() & ~StaticProperty.ALLOWS_MANY;
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ return addDocToPathMap(pathMap, pathMapNodeSet);
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Doc d = (Doc) super.copy();
+ d.expressionBaseURI = expressionBaseURI;
+ d.readOnce = readOnce;
+ return d;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof Doc) && super.equals(o)
+ && equalOrNull(expressionBaseURI, ((Doc) o).expressionBaseURI)
+ && readOnce == ((Doc) o).readOnce;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @return the result of evaluating the expression (a document node)
+ * @throws XPathException
+ */
+
+ public NodeInfo evaluateItem(XPathContext context) throws XPathException {
+ return doc(context);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ AtomicValue hrefVal = (AtomicValue) arguments[0].head();
+ if (hrefVal == null) {
+ return null;
+ }
+ String href = hrefVal.getStringValue();
+ NodeInfo item = DocumentFn.makeDoc(href, expressionBaseURI, context, this);
+ if (item == null) {
+ // we failed to read the document
+ dynamicError("Failed to load document " + href, "FODC0002", context);
+ return null;
+ }
+ return item;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ return StaticProperty.ORDERED_NODESET |
+ StaticProperty.PEER_NODESET |
+ StaticProperty.NON_CREATIVE |
+ StaticProperty.SINGLE_DOCUMENT_NODESET;
+ // Declaring it as a peer node-set expression avoids sorting of expressions such as
+ // doc(XXX)/a/b/c
+ // The doc() function might appear to be creative: but it isn't, because multiple calls
+ // with the same arguments will produce identical results.
+ }
+
+ private NodeInfo doc(XPathContext context) throws XPathException {
+ AtomicValue hrefVal = (AtomicValue) argument[0].evaluateItem(context);
+ if (hrefVal == null) {
+ return null;
+ }
+ String href = hrefVal.getStringValue();
+ NodeInfo item = DocumentFn.makeDoc(href, expressionBaseURI, context, this);
+ if (item == null) {
+ // we failed to read the document
+ dynamicError("Failed to load document " + href, "FODC0002", context);
+ return null;
+ }
+ return item;
+ }
+
+ /**
+ * Copy the document identified by this expression to a given Receiver. This method is used only when it is
+ * known that the document is being copied, because there is then no problem about node identity.
+ *
+ * @param context the XPath dynamic context
+ * @param out the destination to which the document will be sent
+ */
+
+ public void sendDocument(XPathContext context, Receiver out) throws XPathException {
+ AtomicValue hrefVal = (AtomicValue) argument[0].evaluateItem(context);
+ if (hrefVal == null) {
+ return;
+ }
+ String href = hrefVal.getStringValue();
+ try {
+ DocumentFn.sendDoc(href, expressionBaseURI, context, this, out);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ if (e.getErrorCodeQName() == null) {
+ e.setErrorCode("FODC0002");
+ }
+ throw e;
+ }
+ }
+
+//#ifdefined BYTECODE
+
+ /**
+ * Return the compiler of the Doc expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new DocCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/DocAvailable.java b/sf/saxon/functions/DocAvailable.java
new file mode 100644
index 0000000..94a8426
--- /dev/null
+++ b/sf/saxon/functions/DocAvailable.java
@@ -0,0 +1,165 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.BooleanValue;
+
+/**
+ * Implement the fn:doc-available() function
+ */
+
+public class DocAvailable extends SystemFunctionCall implements Callable {
+
+ /*@Nullable*/ private String expressionBaseURI = null;
+
+ /**
+ * Bind aspects of the static context on which the particular function depends
+ *
+ * @param env the static context of the function call
+ * @throws net.sf.saxon.trans.XPathException
+ * if execution with this static context will inevitably fail
+ */
+ @Override
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ expressionBaseURI = env.getBaseURI();
+ }
+
+ /**
+ * Get the static base URI of the expression
+ */
+
+ public String getStaticBaseURI() {
+ return expressionBaseURI;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+ @Override
+ public Expression copy() {
+ DocAvailable fn = (DocAvailable)super.copy();
+ fn.expressionBaseURI = expressionBaseURI;
+ return fn;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return super.equals(o) &&
+ equalOrNull(expressionBaseURI, ((DocAvailable)o).expressionBaseURI);
+ }
+
+ /**
+ * Evaluate the expression
+ * @param context
+ * @return the result of evaluating the expression (a BooleanValue)
+ * @throws net.sf.saxon.trans.XPathException
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ AtomicValue hrefVal = (AtomicValue)argument[0].evaluateItem(context);
+ return BooleanValue.get(isDocAvailable(hrefVal, context));
+ }
+
+ private boolean isDocAvailable(AtomicValue hrefVal, XPathContext context) throws XPathException {
+ if (hrefVal==null) {
+ return false;
+ }
+ String href = hrefVal.getStringValue();
+ return docAvailable(href, context);
+
+// // suppress all error messages while attempting to fetch the document
+// Controller controller = context.getController();
+// ErrorListener old = controller.getErrorListener();
+// controller.setErrorListener(ErrorDiscarder.THE_INSTANCE);
+// boolean b = docAvailable(href, context);
+// controller.setErrorListener(old);
+// return b;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return BooleanValue.get(isDocAvailable((AtomicValue)arguments[0].head(), context));
+ }
+
+// public static class ErrorDiscarder implements ErrorListener {
+// public static ErrorDiscarder THE_INSTANCE = new ErrorDiscarder();
+// public void warning(TransformerException exception) {}
+// public void error(TransformerException exception) {}
+// public void fatalError(TransformerException exception) {}
+//
+// }
+
+ public boolean docAvailable(String href, XPathContext context) throws XPathException {
+ try {
+ DocumentURI documentKey = DocumentFn.computeDocumentKey(href, expressionBaseURI, context);
+ DocumentPool pool = context.getController().getDocumentPool();
+ if (pool.isMarkedUnavailable(documentKey)) {
+ return false;
+ }
+ DocumentInfo doc = pool.find(documentKey);
+ if (doc != null) {
+ return true;
+ }
+ Item item = DocumentFn.makeDoc(href, expressionBaseURI, context, this);
+ if (item != null) {
+ return true;
+ } else {
+ // The document does not exist; ensure that this remains the case
+ pool.markUnavailable(documentKey);
+ return false;
+ }
+ } catch (XPathException e) {
+ return false;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the DocAvaiable expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+// @Override
+// public ExpressionCompiler getExpressionCompiler() {
+// return new DocAvailableCompiler();
+// }
+//#endif
+
+
+}
+
diff --git a/sf/saxon/functions/DocumentFn.java b/sf/saxon/functions/DocumentFn.java
new file mode 100644
index 0000000..61435d9
--- /dev/null
+++ b/sf/saxon/functions/DocumentFn.java
@@ -0,0 +1,794 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.Sender;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.sort.DocumentOrderIterator;
+import net.sf.saxon.expr.sort.GlobalOrderComparer;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.RelativeURIResolver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.NonDelegatingURIResolver;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.SingletonItem;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.dom.DOMSource;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+
+/**
+ * Implements the XSLT document() function
+ */
+
+public class DocumentFn extends SystemFunctionCall implements Callable {
+
+
+ /*@Nullable*/ private String expressionBaseURI = null;
+
+ /**
+ * Method called during static type checking
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ if (expressionBaseURI == null) {
+ // only do this once. The second call supplies an env pointing to the containing
+ // xsl:template, which has a different base URI (and in a simplified stylesheet, has no base URI)
+ super.checkArguments(visitor);
+ expressionBaseURI = visitor.getStaticContext().getBaseURI();
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ argument[0] = ExpressionTool.unsorted(opt, argument[0], false);
+ }
+ }
+
+ /**
+ * Determine the static cardinality
+ */
+
+ public int computeCardinality() {
+ Expression expression = argument[0];
+ if (Cardinality.allowsMany(expression.getCardinality())) {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ } else {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+ // may have to revise this if the argument can be a list-valued element or attribute
+ }
+
+ /**
+ * Get the base URI from the static context
+ * @return the base URI
+ */
+
+ public String getStaticBaseURI() {
+ return expressionBaseURI;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ return StaticProperty.ORDERED_NODESET |
+ StaticProperty.PEER_NODESET |
+ StaticProperty.NON_CREATIVE;
+ // Declaring it as a peer node-set expression avoids sorting of expressions such as
+ // document(XXX)/a/b/c
+ // The document() function might appear to be creative: but it isn't, because multiple calls
+ // with the same arguments will produce identical results.
+ }
+
+ /**
+ * preEvaluate: the document() function can be evaluated at compile time if (a) the argument
+ * is a string literal, and (b) the option {@link FeatureKeys#PRE_EVALUATE_DOC_FUNCTION} is set.
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ Configuration config = visitor.getConfiguration();
+ if (getNumberOfArguments() == 1 &&
+ ((Boolean)config.getConfigurationProperty(FeatureKeys.PRE_EVALUATE_DOC_FUNCTION)).booleanValue()) {
+ try {
+ AtomicValue hrefVal = (AtomicValue)argument[0].evaluateItem(null);
+ if (hrefVal==null) {
+ return null;
+ }
+ String href = hrefVal.getStringValue();
+ if (href.indexOf('#') >= 0) {
+ return this;
+ }
+ NodeInfo item = DocumentFn.preLoadDoc(href, expressionBaseURI, config, this);
+ if (item!=null) {
+ return Literal.makeLiteral(new SingletonItem(item));
+ }
+ } catch (Exception err) {
+ // ignore the exception and try again at run-time
+ return this;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the set of nodes in the path map that are affected
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ return addDocToPathMap(pathMap, pathMapNodeSet);
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ DocumentFn d = (DocumentFn)super.copy();
+ d.expressionBaseURI = expressionBaseURI;
+ return d;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof DocumentFn) && super.equals(o)
+ && equalOrNull(expressionBaseURI, ((DocumentFn)o).expressionBaseURI);
+ }
+
+
+
+ /**
+ * iterate() handles evaluation of the function:
+ * it returns a sequence of Document nodes
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ int numArgs = argument.length;
+
+ SequenceIterator hrefSequence = argument[0].iterate(context);
+ String baseURI = null;
+ if (numArgs==2) {
+ // we can trust the type checking: it must be a node
+ NodeInfo base = (NodeInfo)argument[1].evaluateItem(context);
+ baseURI = base.getBaseURI();
+ }
+
+ DocumentMappingFunction map = new DocumentMappingFunction(context);
+ map.baseURI = baseURI;
+ map.stylesheetURI = expressionBaseURI;
+ map.locator = this;
+
+ ItemMappingIterator iter = new ItemMappingIterator(hrefSequence, map);
+
+ Expression expression = argument[0];
+ if (Cardinality.allowsMany(expression.getCardinality())) {
+ return new DocumentOrderIterator(iter, GlobalOrderComparer.getInstance());
+ // this is to make sure we eliminate duplicates: two href's might be the same
+ } else {
+ return iter;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ int numArgs = argument.length;
+
+ SequenceIterator hrefSequence = arguments[0].iterate();
+ String baseURI = null;
+ if (numArgs==2) {
+ // we can trust the type checking: it must be a node
+ NodeInfo base = (NodeInfo)arguments[1].head();
+ baseURI = base.getBaseURI();
+ }
+
+ DocumentMappingFunction map = new DocumentMappingFunction(context);
+ map.baseURI = baseURI;
+ map.stylesheetURI = expressionBaseURI;
+ map.locator = this;
+
+ ItemMappingIterator iter = new ItemMappingIterator(hrefSequence, map);
+
+ Expression expression = argument[0];
+ if (Cardinality.allowsMany(expression.getCardinality())) {
+ return SequenceTool.toLazySequence(new DocumentOrderIterator(iter, GlobalOrderComparer.getInstance()));
+ // this is to make sure we eliminate duplicates: two href's might be the same
+ } else {
+ return SequenceTool.toLazySequence(iter);
+ }
+ }
+
+ private static class DocumentMappingFunction implements ItemMappingFunction {
+
+ public String baseURI;
+ public String stylesheetURI;
+ public SourceLocator locator;
+ public XPathContext context;
+
+ public DocumentMappingFunction(XPathContext context) {
+ this.context = context;
+ }
+
+ public Item mapItem(Item item) throws XPathException {
+ String b = baseURI;
+ if (b==null) {
+ if (item instanceof NodeInfo) {
+ b = ((NodeInfo)item).getBaseURI();
+ } else {
+ b = stylesheetURI;
+ }
+ }
+ try {
+ return makeDoc(item.getStringValue(), b, context, locator);
+ } catch (XPathException xerr) {
+ if (!xerr.hasBeenReported()) {
+ xerr.maybeSetLocation(locator);
+ String code = (xerr.getException() instanceof URISyntaxException) ? "FODC0005" : "FODC0002";
+ xerr.maybeSetErrorCode(code);
+ try {
+ context.getController().recoverableError(xerr);
+ } catch (XPathException err2) {
+ throw xerr;
+ }
+ }
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Supporting routine to load one external document given a URI (href) and a baseURI. This is used
+ * in the normal case when a document is loaded at run-time (that is, when a Controller is available)
+ * @param href the relative URI
+ * @param baseURI the base URI
+ * @param c the dynamic XPath context
+ * @param locator used to identify the location of the instruction in event of error
+ * @return the root of the constructed document, or the selected element within the document
+ * if a fragment identifier was supplied
+ * @throws XPathException if reading or parsing the document fails
+ */
+
+ public static NodeInfo makeDoc(String href, String baseURI, XPathContext c, SourceLocator locator)
+ throws XPathException {
+
+ Configuration config = c.getConfiguration();
+
+ // If the href contains a fragment identifier, strip it out now
+ //System.err.println("Entering makeDoc " + href);
+ int hash = href.indexOf('#');
+
+ String fragmentId = null;
+ if (hash>=0) {
+ if (hash==href.length()-1) {
+ // # sign at end - just ignore it
+ href = href.substring(0, hash);
+ } else {
+ fragmentId = href.substring(hash+1);
+ href = href.substring(0, hash);
+ if (!config.getNameChecker().isValidNCName(fragmentId)) {
+ XPathException de = new XPathException("The fragment identifier " + Err.wrap(fragmentId) + " is not a valid NCName");
+ de.setErrorCode("XTRE1160");
+ de.setXPathContext(c);
+ de.setLocator(locator);
+ throw de;
+ }
+ }
+ }
+
+ Controller controller = c.getController();
+ if (controller == null) {
+ throw new XPathException("doc() function is not available in this environment");
+ }
+
+ // Resolve relative URI
+ DocumentURI documentKey = computeDocumentKey(href, baseURI, c);
+
+ // see if the document is already loaded
+
+ DocumentInfo doc = config.getGlobalDocumentPool().find(documentKey);
+ if (doc != null) {
+ return doc;
+ }
+
+ DocumentPool pool = controller.getDocumentPool();
+ doc = pool.find(documentKey);
+ if (doc != null) {
+ return getFragment(doc, fragmentId, c, locator);
+ }
+
+ // check that the document was not written by this transformation
+
+ if (!controller.checkUniqueOutputDestination(documentKey)) {
+ pool.markUnavailable(documentKey);
+ XPathException err = new XPathException(
+ "Cannot read a document that was written during the same transformation: " + documentKey);
+ err.setXPathContext(c);
+ err.setErrorCode("XTRE1500");
+ err.setLocator(locator);
+ throw err;
+ }
+
+ try {
+
+ if (pool.isMarkedUnavailable(documentKey)) {
+ XPathException err = new XPathException(
+ "Document has been marked not available: " + documentKey);
+ err.setXPathContext(c);
+ err.setErrorCode("FODC0002");
+ throw err;
+ }
+
+ // Get a Source from the URIResolver
+
+ Source source = resolveURI(href, baseURI, documentKey.toString(), controller);
+
+
+ //System.err.println("URI resolver returned " + source.getClass() + " " + source.getSystemId());
+ source = config.getSourceResolver().resolveSource(source, config);
+ //System.err.println("Resolved source " + source.getClass() + " " + source.getSystemId());
+
+ DocumentInfo newdoc;
+ if (source instanceof NodeInfo || source instanceof DOMSource) {
+ NodeInfo startNode = controller.prepareInputTree(source);
+ newdoc = startNode.getDocumentRoot();
+ } else {
+ Builder b = controller.makeBuilder();
+ Receiver s = b;
+ ParseOptions options = new ParseOptions(b.getPipelineConfiguration().getParseOptions());
+ options.setStripSpace(Whitespace.XSLT);
+ if (controller.getExecutable().stripsInputTypeAnnotations()) {
+ s = controller.getConfiguration().getAnnotationStripper(s);
+ }
+ options.setSchemaValidationMode(controller.getSchemaValidationMode());
+ PathMap map = controller.getPathMapForDocumentProjection();
+ if (map != null) {
+ PathMap.PathMapRoot pathRoot = map.getRootForDocument(documentKey.toString());
+ if (pathRoot != null && !pathRoot.isReturnable() && !pathRoot.hasUnknownDependencies()) {
+ options.addFilter(config.makeDocumentProjector(pathRoot));
+ }
+ }
+ s.setPipelineConfiguration(b.getPipelineConfiguration());
+ try {
+ Sender.send(source, s, options);
+ newdoc = (DocumentInfo)b.getCurrentRoot();
+ b.reset();
+ } finally {
+ if (options.isPleaseCloseAfterUse()) {
+ ParseOptions.close(source);
+ }
+ }
+ }
+ controller.registerDocument(newdoc, documentKey);
+ controller.addUnavailableOutputDestination(documentKey);
+ return getFragment(newdoc, fragmentId, c, locator);
+
+ } catch (TransformerException err) {
+ pool.markUnavailable(documentKey);
+ XPathException xerr = XPathException.makeXPathException(err);
+ xerr.maybeSetLocation(locator);
+ String code = (err.getException() instanceof URISyntaxException) ? "FODC0005" : "FODC0002";
+ xerr.maybeSetErrorCode(code);
+ throw xerr;
+ }
+ }
+
+ /**
+ * Call the URIResolver to resolve a URI
+ * @param href the supplied relative URI, stripped of any fragment identifier
+ * @param baseURI the base URI
+ * @param documentKey the absolute URI if already available, or null otherwise
+ * @param controller the Saxon controller
+ * @return a Source representing the document to be read
+ * @throws XPathException
+ */
+
+ public static Source resolveURI(String href, String baseURI, String documentKey, Controller controller)
+ throws XPathException {
+ URIResolver resolver = controller.getURIResolver();
+ Source source;
+
+ try {
+ if (resolver instanceof RelativeURIResolver && documentKey != null) {
+ source = ((RelativeURIResolver)resolver).dereference(documentKey);
+ } else {
+ source = resolver.resolve(href, baseURI);
+ }
+ } catch (Exception ex) {
+ XPathException de = new XPathException("Exception thrown by URIResolver", ex);
+ if (controller.getConfiguration().getBooleanProperty(FeatureKeys.TRACE_EXTERNAL_FUNCTIONS)) {
+ ex.printStackTrace();
+ }
+ throw de;
+ }
+
+ // if a user URI resolver returns null, try the standard one
+ // (Note, the standard URI resolver never returns null)
+ if (source==null && !(resolver instanceof NonDelegatingURIResolver)) {
+ resolver = controller.getStandardURIResolver();
+ try {
+ if (resolver instanceof RelativeURIResolver && documentKey != null) {
+ source = ((RelativeURIResolver)resolver).dereference(documentKey);
+ } else {
+ source = resolver.resolve(href, baseURI);
+ }
+ } catch (TransformerException ex) {
+ throw XPathException.makeXPathException(ex);
+ }
+ }
+ return source;
+
+ }
+
+ /**
+ * Compute a document key
+ */
+
+ protected static DocumentURI computeDocumentKey(String href, String baseURI, XPathContext c) throws XPathException {
+ // Resolve relative URI
+ Controller controller = c.getController();
+
+ URIResolver resolver = controller.getURIResolver();
+ if (resolver == null) {
+ resolver = controller.getStandardURIResolver();
+ }
+ return computeDocumentKey(href, baseURI, resolver);
+ }
+
+ /**
+ * Compute a document key (an absolute URI that can be used to see if a document is already loaded)
+ * @param href the relative URI
+ * @param baseURI the base URI
+ * @param resolver the URIResolver
+ */
+
+ public static DocumentURI computeDocumentKey(String href, String baseURI, URIResolver resolver) throws XPathException {
+ String documentKey;
+ if (resolver instanceof RelativeURIResolver) {
+ // If this is the case, the URIResolver is responsible for absolutization as well as dereferencing
+ try {
+ documentKey = ((RelativeURIResolver)resolver).makeAbsolute(href, baseURI);
+ } catch (TransformerException e) {
+ documentKey = '/' + href;
+ }
+ } else {
+ // Saxon takes charge of absolutization, leaving the user URIResolver to handle dereferencing only
+ if (baseURI==null) { // no base URI available
+ try {
+ // the href might be an absolute URL
+ documentKey = (new URI(href)).toString();
+ } catch (URISyntaxException err) {
+ // it isn't; but the URI resolver might know how to cope
+ documentKey = '/' + href;
+ }
+ } else if (href.length() == 0) {
+ // common case in XSLT, which java.net.URI#resolve() does not handle correctly
+ documentKey = baseURI;
+ } else {
+ try {
+ URI uri = new URI(baseURI).resolve(href);
+ documentKey = uri.toString();
+ } catch (URISyntaxException err) {
+ documentKey = baseURI + "/../" + href;
+ } catch (IllegalArgumentException err) {
+ documentKey = baseURI + "/../" + href;
+ }
+ }
+ }
+ return new DocumentURI(documentKey);
+ }
+
+ /**
+ * Supporting routine to load one external document given a URI (href) and a baseURI. This is used
+ * when the document is pre-loaded at compile time.
+ * @param href the relative URI. This must not contain a fragment identifier
+ * @param baseURI the base URI
+ * @param config the Saxon configuration
+ * @param locator used to identify the location of the instruction in event of error. May be null.
+ * @return the root of the constructed document, or the selected element within the document
+ * if a fragment identifier was supplied
+ */
+
+ public static NodeInfo preLoadDoc(String href, String baseURI, Configuration config, SourceLocator locator)
+ throws XPathException {
+
+ int hash = href.indexOf('#');
+ if (hash>=0) {
+ throw new XPathException("Fragment identifier not supported for preloaded documents");
+ }
+
+ // Resolve relative URI
+ String documentKey;
+ URIResolver resolver = config.getURIResolver();
+ if (resolver instanceof RelativeURIResolver) {
+ try {
+ documentKey = ((RelativeURIResolver)resolver).makeAbsolute(href, baseURI);
+ } catch (TransformerException e) {
+ documentKey = '/' + href;
+ baseURI = "";
+ }
+ } else {
+ if (baseURI==null) { // no base URI available
+ try {
+ // the href might be an absolute URL
+ documentKey = (new URI(href)).toString();
+ } catch (URISyntaxException err) {
+ // it isn't; but the URI resolver might know how to cope
+ documentKey = '/' + href;
+ baseURI = "";
+ }
+ } else if (href.length() == 0) {
+ // common case in XSLT, which java.net.URI#resolve() does not handle correctly
+ documentKey = baseURI;
+ } else {
+ try {
+ URI uri = new URI(baseURI).resolve(href);
+ documentKey = uri.toString();
+ } catch (URISyntaxException err) {
+ documentKey = baseURI + "/../" + href;
+ } catch (IllegalArgumentException err) {
+ documentKey = baseURI + "/../" + href;
+ }
+ }
+ }
+
+ // see if the document is already loaded
+
+ DocumentInfo doc = config.getGlobalDocumentPool().find(documentKey);
+ if (doc != null) {
+ return doc;
+ }
+
+ try {
+ // Get a Source from the URIResolver
+
+ URIResolver r = resolver;
+ Source source = null;
+ if (r != null) {
+ try {
+ source = r.resolve(href, baseURI);
+ } catch (Exception ex) {
+ XPathException de = new XPathException("Exception thrown by URIResolver", ex);
+ if (config.getBooleanProperty(FeatureKeys.TRACE_EXTERNAL_FUNCTIONS)) {
+ ex.printStackTrace();
+ }
+ de.setLocator(locator);
+ throw de;
+ }
+ }
+
+ // if a user URI resolver returns null, try the standard one
+ // (Note, the standard URI resolver never returns null)
+ if (source==null && !(r instanceof NonDelegatingURIResolver)) {
+ r = config.getSystemURIResolver();
+ source = r.resolve(href, baseURI);
+ }
+ //System.err.println("URI resolver returned " + source.getClass() + " " + source.getSystemId());
+ source = config.getSourceResolver().resolveSource(source, config);
+ //System.err.println("Resolved source " + source.getClass() + " " + source.getSystemId());
+
+ DocumentInfo newdoc = config.buildDocument(source);
+ config.getGlobalDocumentPool().add(newdoc, documentKey);
+ return newdoc;
+
+ } catch (TransformerException err) {
+ XPathException xerr = XPathException.makeXPathException(err);
+ xerr.setLocator(locator);
+ xerr.setErrorCode("FODC0002");
+ throw new XPathException(err);
+ }
+ }
+
+
+ /**
+ * Copy the documents identified by this expression to a given Receiver. This method is used only when it is
+ * known that the documents are being copied, because there is then no problem about node identity.
+ * @param context the XPath dynamic context
+ * @param out the destination to which the documents will be sent
+ */
+
+ public void sendDocuments(XPathContext context, Receiver out) throws XPathException {
+ SequenceIterator hrefSequence = argument[0].iterate(context);
+ String explicitBaseURI = null;
+ if (argument.length==2) {
+ // we can trust the type checking: it must be a node
+ NodeInfo base = (NodeInfo)argument[1].evaluateItem(context);
+ explicitBaseURI = base.getBaseURI();
+ }
+ while (true) {
+ Item href = hrefSequence.next();
+ if (href == null) {
+ break;
+ }
+ String base;
+ if (explicitBaseURI == null) {
+ if (href instanceof NodeInfo) {
+ base = ((NodeInfo)href).getBaseURI();
+ } else {
+ base = expressionBaseURI;
+ }
+ } else {
+ base = explicitBaseURI;
+ }
+ sendDoc(href.getStringValue(), base, context, this, out);
+ }
+ }
+
+ /**
+ * Supporting routine to push one external document given a URI (href) and a baseURI to a given Receiver.
+ * This method cannot handle fragment identifiers
+ * @param href the relative URI
+ * @param baseURL the base URI
+ * @param c the XPath dynamic context
+ * @param locator used to identify the lcoation of the instruction in case of error
+ * @param out the destination where the document is to be sent
+ */
+
+ public static void sendDoc(String href, String baseURL, XPathContext c,
+ SourceLocator locator, Receiver out) throws XPathException {
+
+ PipelineConfiguration pipe = out.getPipelineConfiguration();
+ if (pipe == null) {
+ pipe = c.getController().makePipelineConfiguration();
+ out.setPipelineConfiguration(pipe);
+ }
+
+ // Resolve relative URI
+
+ String documentKey;
+ if (baseURL==null) { // no base URI available
+ try {
+ // the href might be an absolute URL
+ documentKey = (new URI(href)).toString();
+ } catch (URISyntaxException err) {
+ // it isn't; but the URI resolver might know how to cope
+ documentKey = '/' + href;
+ baseURL = "";
+ }
+ } else if (href.length() == 0) {
+ // common case in XSLT, which java.net.URI#resolve() does not handle correctly
+ documentKey = baseURL;
+ } else {
+ try {
+ URI url = new URI(baseURL).resolve(href);
+ documentKey = url.toString();
+ } catch (URISyntaxException err) {
+ documentKey = baseURL + "/../" + href;
+ } catch (IllegalArgumentException err) {
+ documentKey = baseURL + "/../" + href;
+ }
+ }
+
+ Controller controller = c.getController();
+
+ // see if the document is already loaded
+
+ DocumentInfo doc = controller.getDocumentPool().find(documentKey);
+ Source source = null;
+ if (doc != null) {
+ source = doc;
+ } else {
+
+ try {
+ // Get a Source from the URIResolver
+
+ URIResolver r = controller.getURIResolver();
+ if (r != null) {
+ source = r.resolve(href, baseURL);
+ }
+
+ // if a user URI resolver returns null, try the standard one
+ // (Note, the standard URI resolver never returns null)
+ if (source==null) {
+ r = controller.getStandardURIResolver();
+ source = r.resolve(href, baseURL);
+ }
+ if (source instanceof NodeInfo || source instanceof DOMSource) {
+ NodeInfo startNode = controller.prepareInputTree(source);
+ source = startNode.getDocumentRoot();
+ }
+ } catch (TransformerException err) {
+ XPathException xerr = XPathException.makeXPathException(err);
+ xerr.setLocator(locator);
+ xerr.maybeSetErrorCode("FODC0005");
+ throw xerr;
+ }
+ }
+ ParseOptions options = new ParseOptions();
+ options.setStripSpace(Whitespace.XSLT);
+
+ if (controller.getExecutable().stripsInputTypeAnnotations()) {
+ out = controller.getConfiguration().getAnnotationStripper(out);
+ }
+ out.setPipelineConfiguration(pipe);
+ try {
+ Sender.send(source, out, options);
+ } catch (XPathException e) {
+ e.maybeSetLocation(locator);
+ e.maybeSetErrorCode("FODC0002");
+ throw e;
+ }
+ }
+
+
+ /**
+ * Resolve the fragment identifier within a URI Reference.
+ * Only "bare names" XPointers are recognized, that is, a fragment identifier
+ * that matches an ID attribute value within the target document.
+ * @param doc the document node
+ * @param fragmentId the fragment identifier (an ID value within the document)
+ * @param context the XPath dynamic context
+ * @return the element within the supplied document that matches the
+ * given id value; or null if no such element is found.
+ */
+
+ private static NodeInfo getFragment(DocumentInfo doc, String fragmentId, XPathContext context, SourceLocator locator)
+ throws XPathException {
+ // TODO: we only support one kind of fragment identifier. The rules say
+ // that the interpretation of the fragment identifier depends on media type,
+ // but we aren't getting the media type from the URIResolver.
+ if (fragmentId==null) {
+ return doc;
+ }
+ if (!context.getConfiguration().getNameChecker().isValidNCName(fragmentId)) {
+ XPathException err = new XPathException("Invalid fragment identifier in URI");
+ err.setXPathContext(context);
+ err.setErrorCode("XTRE1160");
+ err.setLocator(locator);
+ try {
+ context.getController().recoverableError(err);
+ } catch (XPathException dynamicError) {
+ throw err;
+ }
+ return doc;
+ }
+ return doc.selectID(fragmentId, false);
+ }
+
+}
+
diff --git a/sf/saxon/functions/DocumentUriFn.java b/sf/saxon/functions/DocumentUriFn.java
new file mode 100644
index 0000000..ff075a1
--- /dev/null
+++ b/sf/saxon/functions/DocumentUriFn.java
@@ -0,0 +1,95 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.DocumentPool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AnyURIValue;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+ * This class supports the document-uri() function
+ */
+
+public class DocumentUriFn extends SystemFunctionCall implements Callable {
+
+ /**
+ * Simplify and validate.
+ *
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ return simplifyArguments(visitor);
+ }
+
+ /**
+ * Evaluate the function in a string context
+ */
+
+ /*@Nullable*/
+ public AnyURIValue evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo node = (NodeInfo) argument[0].evaluateItem(c);
+ if (node == null) {
+ return null;
+ }
+ return getDocumentURI(node, c);
+ }
+
+ /**
+ * Evaluate the expression
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo node = getDefaultArgumentNode(context, arguments, "fn:document-uri()");;
+
+ if (node == null) {
+ return EmptySequence.getInstance();
+ }
+
+ AnyURIValue result = getDocumentURI(node, context);
+ return (result == null ? EmptySequence.getInstance() : result);
+ }
+
+ public static AnyURIValue getDocumentURI(NodeInfo node, XPathContext c) {
+ if (node.getNodeKind() == Type.DOCUMENT) {
+ final Controller controller = c.getController();
+ assert controller != null;
+ DocumentPool pool = controller.getDocumentPool();
+ String docURI = pool.getDocumentURI(node);
+ if (docURI == null) {
+ docURI = node.getSystemId();
+ }
+ if (docURI == null) {
+ return null;
+ } else if ("".equals(docURI)) {
+ return null;
+ } else {
+ return new AnyURIValue(docURI);
+ }
+ } else {
+ return null;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/functions/ElementAvailable.java b/sf/saxon/functions/ElementAvailable.java
new file mode 100644
index 0000000..6170697
--- /dev/null
+++ b/sf/saxon/functions/ElementAvailable.java
@@ -0,0 +1,115 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.style.XSLTStaticContext;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+* This class supports the XSLT element-available and function-available functions.
+*/
+
+public class ElementAvailable extends Available implements Callable {
+
+ /**
+ * preEvaluate: this method uses the static context to do early evaluation of the function
+ * if the argument is known (which is the normal case)
+ * @param visitor the expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ String lexicalQName = ((Literal)argument[0]).getValue().getStringValue();
+ StaticContext env = visitor.getStaticContext();
+ boolean b = ((XSLTStaticContext)env).isElementAvailable(lexicalQName);
+ return Literal.makeLiteral(BooleanValue.get(b));
+ }
+
+ /**
+ * Run-time evaluation.
+ */
+
+ public BooleanValue evaluateItem(/*@NotNull*/ XPathContext context) throws XPathException {
+ AtomicValue av1 = (AtomicValue)argument[0].evaluateItem(context);
+ StringValue nameValue = (StringValue)av1;
+ String lexicalName = nameValue.getStringValue();
+ boolean b = isElementAvailable(lexicalName, context);
+ return BooleanValue.get(b);
+
+ }
+
+ /**
+ * Determine at run-time whether a particular instruction is available. Returns true
+ * only in the case of XSLT instructions and Saxon extension instructions; returns false
+ * for user-defined extension instructions
+ * @param lexicalName the lexical QName of the element
+ * @param context the XPath evaluation context
+ * @return true if the instruction is available, in the sense of the XSLT element-available() function
+ */
+
+ private boolean isElementAvailable(String lexicalName, XPathContext context) throws XPathException {
+
+ // This is horribly inefficient. But hopefully it's hardly ever executed, because there
+ // is very little point calling element-available() with a dynamically-constructed argument.
+ // And the inefficiency is only incurred once, on the first call.
+
+ // Note: this requires the compile-time classes to be available at run-time; it will need
+ // changing if we ever want to build a run-time JAR file.
+
+ StructuredQName qName;
+ try {
+ if (lexicalName.indexOf(':') < 0) {
+ String uri = nsContext.getURIForPrefix("", true);
+ qName = new StructuredQName("", uri, lexicalName);
+ } else {
+ boolean is30 = context.getController().getExecutable().isAllowXPath30();
+ qName = StructuredQName.fromLexicalQName(lexicalName,
+ false, is30, context.getConfiguration().getNameChecker(),
+ nsContext);
+ }
+ } catch (XPathException e) {
+ e.setErrorCode("XTDE1440");
+ e.setLocator(this);
+ e.setXPathContext(context);
+ throw(e);
+ }
+
+ try {
+ PreparedStylesheet pss = (PreparedStylesheet)context.getController().getExecutable();
+ return pss.getStyleNodeFactory().isElementAvailable(qName.getURI(), qName.getLocalPart());
+ } catch (Exception err) {
+ //err.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ String lexicalQName = arguments[0].head().getStringValue();
+ boolean b = isElementAvailable(lexicalQName, context);
+ return BooleanValue.get(b);
+ }
+}
+
diff --git a/sf/saxon/functions/Empty.java b/sf/saxon/functions/Empty.java
new file mode 100644
index 0000000..53e7ed7
--- /dev/null
+++ b/sf/saxon/functions/Empty.java
@@ -0,0 +1,158 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.EmptyCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.EmptyAdjunct;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.value.BooleanValue;
+
+
+/**
+ * Implementation of the fn:exists function
+ */
+public class Empty extends Aggregate implements Negatable {
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | WATCH_METHOD;
+ }
+
+ /**
+ * Check whether this specific instance of the expression is negatable
+ * @return true if it is
+ */
+
+ public boolean isNegatable(ExpressionVisitor visitor) {
+ return true;
+ }
+
+ /**
+ * Return the negation of the expression
+ * @return the negation of the expression
+ */
+
+ public Expression negate() {
+ FunctionCall fc = SystemFunctionCall.makeSystemFunction("exists", getArguments());
+ fc.setLocationId(getLocationId());
+ return fc;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e2 = super.optimize(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ // See if we can deduce the answer from the cardinality
+ int c = argument[0].getCardinality();
+ if (c == StaticProperty.ALLOWS_ONE_OR_MORE) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ } else if (c == StaticProperty.ALLOWS_ZERO) {
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ }
+ argument[0] = ExpressionTool.unsorted(visitor.getConfiguration().obtainOptimizer(), argument[0], false);
+ // Rewrite
+ // empty(A|B) => empty(A) and empty(B)
+ if (argument[0] instanceof VennExpression) {
+ VennExpression v = (VennExpression)argument[0];
+ if (v.getOperator() == Token.UNION && !visitor.isOptimizeForStreaming()) {
+ FunctionCall e0 = SystemFunctionCall.makeSystemFunction("empty", new Expression[]{v.getOperands()[0]});
+ FunctionCall e1 = SystemFunctionCall.makeSystemFunction("empty", new Expression[]{v.getOperands()[1]});
+ return new AndExpression(e0, e1).optimize(visitor, contextItemType);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Evaluate the function in a boolean context
+ */
+
+ public boolean effectiveBooleanValue(XPathContext c) throws XPathException {
+ SequenceIterator iter = argument[0].iterate(c);
+ boolean result;
+ if ((iter.getProperties() & SequenceIterator.LOOKAHEAD) != 0) {
+ result = !((LookaheadIterator)iter).hasNext();
+ } else {
+ result = iter.next() == null;
+ }
+ iter.close();
+ return result;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return BooleanValue.get(arguments[0].head() == null);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Empty expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new EmptyCompiler();
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public EmptyAdjunct getStreamingAdjunct() {
+ return new EmptyAdjunct();
+ }
+
+ //#endif
+
+}
+
diff --git a/sf/saxon/functions/EndsWith.java b/sf/saxon/functions/EndsWith.java
new file mode 100644
index 0000000..ebd4471
--- /dev/null
+++ b/sf/saxon/functions/EndsWith.java
@@ -0,0 +1,135 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.StartsWithCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.RuleBasedSubstringMatcher;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.lib.SubstringMatcher;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.StringValue;
+
+import java.text.RuleBasedCollator;
+
+/**
+ * Implements the fn:ends-with() function
+ */
+public class EndsWith extends CollatingFunction implements Callable {
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 2;
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ * @param context The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ StringValue arg0 = (StringValue)argument[0].evaluateItem(context);
+ StringValue arg1 = (StringValue)argument[1].evaluateItem(context);
+ try {
+ return endsWith(arg0, arg1, getCollator(context));
+ } catch (XPathException err) {
+ err.maybeSetContext(context);
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+
+ public static boolean endsWith(StringValue arg0, StringValue arg1, StringCollator collator) throws XPathException {
+ if (arg1==null || arg1.isZeroLength()) {
+ return true;
+ }
+ if (arg0==null || arg0.isZeroLength()) {
+ return false;
+ }
+
+ String s0 = arg0.getStringValue();
+ String s1 = arg1.getStringValue();
+
+ if (collator instanceof CodepointCollator) {
+ // fast path for this common case
+ return s0.endsWith(s1);
+ } else {
+ if (collator instanceof SimpleCollation &&
+ ((SimpleCollation)collator).getCollation() instanceof RuleBasedCollator) {
+ collator = new RuleBasedSubstringMatcher((RuleBasedCollator)((SimpleCollation)collator).getCollation());
+ }
+
+ if (collator instanceof SubstringMatcher) {
+ return ((SubstringMatcher)collator).endsWith(s0, s1);
+ } else {
+ throw new XPathException("The collation requested for ends-with() does not support substring matching", "FOCH0004");
+ }
+ }
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public BooleanValue evaluateItem(/*@NotNull*/ XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ try {
+ StringValue s0 = (StringValue)arguments[0].head();
+ StringValue s1 = (StringValue)arguments[1].head();
+ StringCollator collator = getCollatorFromLastArgument(arguments, 2, context);
+ return BooleanValue.get(endsWith(s0, s1, collator));
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the EndsWith expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new StartsWithCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/Error.java b/sf/saxon/functions/Error.java
new file mode 100644
index 0000000..7725e60
--- /dev/null
+++ b/sf/saxon/functions/Error.java
@@ -0,0 +1,151 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.sxpath.XPathEvaluator;
+import net.sf.saxon.sxpath.XPathExpression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * Implement XPath function fn:error()
+ */
+
+public class Error extends SystemFunctionCall implements Callable {
+
+ @Override
+ public int computeSpecialProperties() {
+ return super.computeSpecialProperties() &~ StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Determine whether this is a vacuous expression as defined in the XQuery update specification
+ * @return true if this expression is vacuous
+ */
+
+ public boolean isVacuousExpression() {
+ return true;
+ }
+
+ /**
+ * Evaluation of the expression always throws an error
+ */
+
+ /*@Nullable*/ public Item evaluateItem(XPathContext context) throws XPathException {
+ int len =argument.length;
+
+ switch(len){
+ case 0 : return error(context, null, null, null);
+ case 1 : return error(context, (QNameValue)argument[0].evaluateItem(context), null, null);
+ case 2: return error(context, (QNameValue)argument[0].evaluateItem(context), (StringValue)argument[1].evaluateItem(context), null);
+ case 3: return error(context, (QNameValue)argument[0].evaluateItem(context), (StringValue)argument[1].evaluateItem(context), argument[2].iterate(context));
+ default:
+ return null;
+ }
+ }
+
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ *
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ evaluateItem(context);
+ }
+
+ public <EO extends Item> Item error(
+ XPathContext context,
+ /*@Nullable*/ QNameValue errorCode,
+ /*@Nullable*/ StringValue desc,
+ /*@Nullable*/ SequenceIterator<EO> errObject) throws XPathException{
+ QNameValue qname = null;
+ if (argument.length > 0) {
+ qname = errorCode;
+ }
+ if (qname == null) {
+ qname = new QNameValue("err", NamespaceConstant.ERR,
+ (argument.length == 1 ? "FOTY0004" : "FOER0000"),
+ BuiltInAtomicType.QNAME, null);
+ }
+ String description;
+ if (argument.length > 1) {
+ description = (desc == null ? "" : desc.getStringValue());
+ } else {
+ description = "Error signalled by application call on error()";
+ }
+ XPathException e = new XPathException(description);
+ e.setErrorCodeQName(qname.toStructuredQName());
+ e.setXPathContext(context);
+ e.setLocator(this);
+ if (argument.length > 2 && errObject != null) {
+ Sequence errorObject = SequenceExtent.makeSequenceExtent(errObject);
+ if (errorObject instanceof SingletonItem) {
+ Item root = ((SingletonItem)errorObject).asItem();
+ if ((root instanceof NodeInfo) && ((NodeInfo)root).getNodeKind() == Type.DOCUMENT) {
+ XPathEvaluator xpath = new XPathEvaluator();
+ XPathExpression exp = xpath.createExpression("/error/@module");
+ NodeInfo moduleAtt = (NodeInfo)exp.evaluateSingle((NodeInfo)root);
+ String module = (moduleAtt == null ? null : moduleAtt.getStringValue());
+ exp = xpath.createExpression("/error/@line");
+ NodeInfo lineAtt = (NodeInfo)exp.evaluateSingle((NodeInfo)root);
+ int line = (lineAtt == null ? -1 : Integer.parseInt(lineAtt.getStringValue()));
+ exp = xpath.createExpression("/error/@column");
+ NodeInfo columnAtt = (NodeInfo)exp.evaluateSingle((NodeInfo)root);
+ int column = (columnAtt == null ? -1 : Integer.parseInt(columnAtt.getStringValue()));
+ ExpressionLocation locator = new ExpressionLocation();
+ locator.setSystemId(module);
+ locator.setLineNumber(line);
+ locator.setColumnNumber(column);
+ e.setLocator(locator);
+ }
+ }
+ e.setErrorObject(errorObject);
+ }
+ throw e;
+ }
+
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ int len =argument.length;
+
+ switch(len){
+ case 0 : return error(context, null, null, null);
+ case 1 : return error(context, (QNameValue)arguments[0].head(), null, null);
+ case 2: return error(context, (QNameValue)arguments[0].head(), (StringValue)arguments[1].head(), null);
+ case 3: return error(context, (QNameValue)arguments[0].head(), (StringValue)arguments[1].head(), arguments[2].iterate());
+ default:
+ return null;
+ }
+ }
+}
+
diff --git a/sf/saxon/functions/EscapeURI.java b/sf/saxon/functions/EscapeURI.java
new file mode 100644
index 0000000..29b5c30
--- /dev/null
+++ b/sf/saxon/functions/EscapeURI.java
@@ -0,0 +1,260 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.serialize.HTMLURIEscaper;
+import net.sf.saxon.serialize.charcode.UTF8CharacterSet;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.StringValue;
+
+import java.util.Arrays;
+
+/**
+ * This class supports the functions encode-for-uri() and iri-to-uri()
+ */
+
+public class EscapeURI extends SystemFunctionCall implements Callable {
+
+ public static final int ENCODE_FOR_URI = 1;
+ public static final int IRI_TO_URI = 2;
+ public static final int HTML_URI = 3;
+
+ public static boolean[] allowedASCII = new boolean[128];
+
+ static {
+ Arrays.fill(allowedASCII, 0, 32, false);
+ Arrays.fill(allowedASCII, 33, 127, true);
+ allowedASCII[(int)'"'] = false;
+ allowedASCII[(int)'<'] = false;
+ allowedASCII[(int)'>'] = false;
+ allowedASCII[(int)'\\'] = false;
+ allowedASCII[(int)'^'] = false;
+ allowedASCII[(int)'`'] = false;
+ allowedASCII[(int)'{'] = false;
+ allowedASCII[(int)'|'] = false;
+ allowedASCII[(int)'}'] = false;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ Item item = argument[0].evaluateItem(c);
+ return evalEscapeURI(item, c);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return evalEscapeURI(arguments[0].head(), context);
+ }
+
+ private StringValue evalEscapeURI(/*@Nullable*/ Item item, XPathContext c) throws XPathException {
+ if (item == null) {
+ return StringValue.EMPTY_STRING;
+ }
+ final CharSequence s = item.getStringValueCS();
+ switch (operation) {
+ case ENCODE_FOR_URI:
+ return StringValue.makeStringValue(escape(s, "-_.~"));
+ case IRI_TO_URI:
+ return StringValue.makeStringValue(iriToUri(s));
+ case HTML_URI:
+ return StringValue.makeStringValue(HTMLURIEscaper.escapeURL(s, false, c.getConfiguration()));
+ default:
+ throw new UnsupportedOperationException("Unknown escape operation");
+ }
+ }
+
+ /**
+ * Escape special characters in a URI. The characters that are %HH-encoded are
+ * all non-ASCII characters
+ * @param s the URI to be escaped
+ * @return the %HH-encoded string
+ */
+
+ public static CharSequence iriToUri(CharSequence s) {
+ // NOTE: implements a late spec change which says that characters that are illegal in an IRI,
+ // for example "\", must be %-encoded.
+ if (allAllowedAscii(s)) {
+ // it's worth doing a prescan to avoid the cost of copying in the common all-ASCII case
+ return s;
+ }
+ FastStringBuffer sb = new FastStringBuffer(s.length()+20);
+ for (int i=0; i<s.length(); i++) {
+ final char c = s.charAt(i);
+ if (c>=0x7f || !allowedASCII[(int)c]) {
+ escapeChar(c, ((i+1)<s.length() ? s.charAt(i+1) : ' '), sb);
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb;
+ }
+
+ private static boolean allAllowedAscii(CharSequence s) {
+ for (int i=0; i<s.length(); i++) {
+ final char c = s.charAt(i);
+ if (c>=0x7f || !allowedASCII[(int)c]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Escape special characters in a URI. The characters that are %HH-encoded are
+ * all non-ASCII characters, plus all ASCII characters except (a) letter A-Z
+ * and a-z, (b) digits 0-9, and (c) characters listed in the allowedPunctuation
+ * argument
+ * @param s the URI to be escaped
+ * @param allowedPunctuation ASCII characters other than letters and digits that
+ * should NOT be %HH-encoded
+ * @return the %HH-encoded string
+ */
+
+ public static CharSequence escape(CharSequence s, String allowedPunctuation) {
+ FastStringBuffer sb = new FastStringBuffer(s.length());
+ for (int i=0; i<s.length(); i++) {
+ char c = s.charAt(i);
+ if ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9')) {
+ sb.append(c);
+ } else if (c<=0x20 || c>=0x7f) {
+ escapeChar(c, ((i+1)<s.length() ? s.charAt(i+1) : ' '), sb);
+ } else if (allowedPunctuation.indexOf(c) >= 0) {
+ sb.append(c);
+ } else {
+ escapeChar(c, ' ', sb);
+ }
+
+ }
+ return sb;
+ }
+
+ private static final String hex = "0123456789ABCDEF";
+
+ /**
+ * Escape a single character in %HH representation, or a pair of two chars representing
+ * a surrogate pair
+ * @param c the character to be escaped, or the first character of a surrogate pair
+ * @param c2 the second character of a surrogate pair
+ * @param sb the buffer to contain the escaped result
+ */
+
+ private static void escapeChar(char c, char c2, FastStringBuffer sb) {
+ byte[] array = new byte[4];
+ int used = UTF8CharacterSet.getUTF8Encoding(c, c2, array);
+ for (int b=0; b<used; b++) {
+ int v = (int)array[b] & 0xff;
+ sb.append('%');
+ sb.append(hex.charAt(v/16));
+ sb.append(hex.charAt(v%16));
+ }
+ }
+
+ /**
+ * Check that any percent-encoding within a URI is well-formed. The method assumes that a percent
+ * sign followed by two hex digits represents an octet of the UTF-8 representation of a character;
+ * any other percent sign is assumed to represent itself.
+ * @param uri the string to be checked for validity
+ * @throws XPathException if the string is not validly percent-encoded
+ */
+
+ public static void checkPercentEncoding(String uri) throws XPathException {
+ for (int i=0; i<uri.length();) {
+ char c = uri.charAt(i);
+ byte[] bytes;
+ // Note: we're translating the UTF-8 byte sequence but then not using the value
+ int expectedOctets;
+ if (c == '%') {
+ if (i+2 >= uri.length()) {
+ throw new XPathException("% sign in URI must be followed by two hex digits" +
+ Err.wrap(uri));
+ }
+ int h1 = hexDigits.indexOf(uri.charAt(i+1));
+ if (h1 > 15) {
+ h1 -= 6;
+ }
+
+ int h2 = hexDigits.indexOf(uri.charAt(i+2));
+ if (h2 > 15) {
+ h2 -= 6;
+ }
+ if (h1 >= 0 && h2 >= 0) {
+ int b = h1<<4 | h2;
+ expectedOctets = UTF8RepresentationLength[h1];
+ if (expectedOctets == -1) {
+ throw new XPathException("First %-encoded octet in URI is not valid as the start of a UTF-8 " +
+ "character: first two bits must not be '10'" +
+ Err.wrap(uri));
+ }
+ bytes = new byte[expectedOctets];
+ bytes[0] = (byte)b;
+ i+=3;
+ for (int q=1; q<expectedOctets; q++) {
+ if (i+2 > uri.length() || uri.charAt(i) != '%') {
+ throw new XPathException("Incomplete %-encoded UTF-8 octet sequence in URI " +
+ Err.wrap(uri));
+ }
+ h1 = hexDigits.indexOf(uri.charAt(i+1));
+ if (h1 > 15) {
+ h1 -= 6;
+ }
+
+ h2 = hexDigits.indexOf(uri.charAt(i+2));
+ if (h2 > 15) {
+ h2 -= 6;
+ }
+ if (h1 < 0 || h2 < 0) {
+ throw new XPathException("Invalid %-encoded UTF-8 octet sequence in URI" +
+ Err.wrap(uri));
+ }
+ if (UTF8RepresentationLength[h1] != -1) {
+ throw new XPathException("In a URI, a %-encoded UTF-8 octet after the first " +
+ "must have '10' as the first two bits" +
+ Err.wrap(uri));
+ }
+ b = h1<<4 | h2;
+ bytes[q] = (byte)b;
+ i += 3;
+ }
+ } else {
+ throw new XPathException("% sign in URI must be followed by two hex digits" +
+ Err.wrap(uri));
+ }
+ } else {
+ i++;
+ }
+
+ }
+
+ }
+
+ private static String hexDigits = "0123456789abcdefABCDEF";
+
+ // Length of a UTF8 byte sequence, as a function of the first nibble
+ private static int[] UTF8RepresentationLength = {1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 2, 2, 3, 4};
+}
+
diff --git a/sf/saxon/functions/ExecutableFunctionLibrary.java b/sf/saxon/functions/ExecutableFunctionLibrary.java
new file mode 100644
index 0000000..957e505
--- /dev/null
+++ b/sf/saxon/functions/ExecutableFunctionLibrary.java
@@ -0,0 +1,186 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.functions.hof.CallableFunctionItem;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.UserFunctionCall;
+import net.sf.saxon.expr.instruct.UserFunction;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * An ExecutableFunctionLibrary is a function library that contains definitions of functions for use at
+ * run-time. Normally functions are bound at compile-time; however there are various situations in which
+ * the information is needed dynamically, for example (a) to support the XSLT function-available() call
+ * (in the pathological case where the argument is not known statically), (b) to allow functions to be
+ * called from saxon:evaluate(), (c) to allow functions to be called from a debugging breakpoint.
+ *
+ * The objects actually held in the ExecutableFunctionLibrary are UserFunctionCall objects that have been
+ * prepared at compile time. These are function calls that do full dynamic type checking: that is, they
+ * are prepared on the basis that the static types of the arguments are all "item()*", meaning that full
+ * type checking is needed at run-time.
+ */
+
+public class ExecutableFunctionLibrary implements FunctionLibrary {
+
+ private transient Configuration config;
+ private HashMap<String, UserFunction> functions = new HashMap<String, UserFunction>(20);
+ // The key of the hash table is a String that combines the QName of the function with the arity.
+
+ /**
+ * Create the ExecutableFunctionLibrary
+ * @param config the Saxon configuration
+ */
+
+ public ExecutableFunctionLibrary(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Make a key that will uniquely identify a function
+ * @param functionName the name of the function
+ * @param arity the number of arguments
+ * @return the constructed key. This is of the form {uri}local/arity
+ */
+
+ private String makeKey(StructuredQName functionName, int arity) {
+ String uri = functionName.getURI();
+ String local = functionName.getLocalPart();
+ FastStringBuffer sb = new FastStringBuffer(uri.length() + local.length() + 8);
+ sb.append('{');
+ sb.append(uri);
+ sb.append('}');
+ sb.append(local);
+ sb.append("#" + arity);
+ return sb.toString();
+ }
+
+ /**
+ * Register a function with the function library
+ * @param fn the function to be registered
+ */
+
+ public void addFunction(UserFunction fn) {
+ functions.put(makeKey(fn.getFunctionName(), fn.getNumberOfArguments()), fn);
+ }
+
+ /**
+ * Bind a function, given the URI and local parts of the function name,
+ * and the list of expressions supplied as arguments. This method is called at compile
+ * time.
+ *
+ *
+ * @param functionName The name of the function to be called
+ * @param arity
+ * @param staticArgs The expressions supplied statically in the function call. The intention is
+ * that the static type of the arguments (obtainable via getItemType() and getCardinality() may
+ * be used as part of the binding algorithm.
+ * @param env the static evaluation context
+ * @param container
+ * @return An object representing the extension function to be called, if one is found;
+ * null if no extension function was found matching the required name and arity.
+ * @throws net.sf.saxon.trans.XPathException if a function is found with the required name and arity, but
+ * the implementation of the function cannot be loaded or used; or if an error occurs
+ * while searching for the function; or if this function library "owns" the namespace containing
+ * the function call, but no function was found.
+ */
+
+ public Expression bind(StructuredQName functionName, int arity, Expression[] staticArgs, StaticContext env, Container container)
+ throws XPathException {
+ UserFunction fn = functions.get(makeKey(functionName, staticArgs.length));
+ if (fn == null) {
+ return null;
+ }
+ ExpressionVisitor visitor = ExpressionVisitor.make(env, fn.getExecutable());
+ UserFunctionCall fc = new UserFunctionCall();
+ fc.setFunctionName(functionName);
+ fc.setArguments(staticArgs);
+ fc.setFunction(fn);
+ fc.checkFunctionCall(fn, visitor);
+ fc.setStaticType(fn.getResultType(config.getTypeHierarchy()));
+ return fc;
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ * <p/>
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ * @throws net.sf.saxon.trans.XPathException
+ * in the event of certain errors, for example attempting to get a function
+ * that is private
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext) throws XPathException {
+ final UserFunction uf = functions.get(makeKey(functionName, arity));
+ if (uf == null) {
+ return null;
+ } else {
+ return new CallableFunctionItem(uf);
+ }
+ }
+//#endif
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ return functions.get(makeKey(functionName, arity)) != null;
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ *
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ public FunctionLibrary copy() {
+ ExecutableFunctionLibrary efl = new ExecutableFunctionLibrary(config);
+ efl.functions = new HashMap<String, UserFunction>(functions);
+ return efl;
+ }
+
+ /**
+ * Iterate over all the functions defined in this function library. The objects
+ * returned by the iterator are of class {@link UserFunction}
+ * @return an iterator delivering the {@link UserFunction} objects representing
+ * the user-defined functions in a stylesheet or query
+ */
+
+ public Iterator<UserFunction> iterateFunctions() {
+ return functions.values().iterator();
+ }
+
+}
+
diff --git a/sf/saxon/functions/Exists.java b/sf/saxon/functions/Exists.java
new file mode 100644
index 0000000..1fedbb7
--- /dev/null
+++ b/sf/saxon/functions/Exists.java
@@ -0,0 +1,177 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExistsCompiler;
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.stream.adjunct.ExistsAdjunct;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.value.BooleanValue;
+
+
+
+/**
+ * Implementation of the fn:exists function
+ */
+public class Exists extends Aggregate implements Negatable, Callable {
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | WATCH_METHOD;
+ }
+
+ /**
+ * Check whether this specific instance of the expression is negatable
+ * @return true if it is
+ */
+
+ public boolean isNegatable(ExpressionVisitor visitor) {
+ return true;
+ }
+
+ /**
+ * Return the negation of the expression
+ * @return the negation of the expression
+ */
+
+ public Expression negate() {
+ FunctionCall fc = SystemFunctionCall.makeSystemFunction("empty", getArguments());
+ fc.setLocationId(getLocationId());
+ return fc;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e2 = super.optimize(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ // See if we can deduce the answer from the cardinality
+ int c = argument[0].getCardinality();
+ if (c == StaticProperty.ALLOWS_ONE_OR_MORE) {
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ } else if (c == StaticProperty.ALLOWS_ZERO) {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+ argument[0] = ExpressionTool.unsorted(visitor.getConfiguration().obtainOptimizer(), argument[0], false);
+ // Rewrite
+ // exists(A|B) => exists(A) or exists(B)
+ if (argument[0] instanceof VennExpression && !visitor.isOptimizeForStreaming()) {
+ VennExpression v = (VennExpression)argument[0];
+ if (v.getOperator() == Token.UNION) {
+ FunctionCall e0 = SystemFunctionCall.makeSystemFunction("exists", new Expression[]{v.getOperands()[0]});
+ FunctionCall e1 = SystemFunctionCall.makeSystemFunction("exists", new Expression[]{v.getOperands()[1]});
+ return new OrExpression(e0, e1).optimize(visitor, contextItemType);
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Evaluate the function in a boolean context
+ */
+
+ public boolean effectiveBooleanValue(XPathContext c) throws XPathException {
+ try {
+ return exists(argument[0].iterate(c));
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ err.maybeSetContext(c);
+ throw err;
+ }
+ }
+
+ private static boolean exists(SequenceIterator iter) throws XPathException {
+ boolean result;
+ if ((iter.getProperties() & SequenceIterator.LOOKAHEAD) != 0) {
+ result = ((LookaheadIterator)iter).hasNext();
+ } else {
+ result = iter.next() != null;
+ }
+ iter.close();
+ return result;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ try {
+ return BooleanValue.get(exists(arguments[0].iterate()));
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ err.maybeSetContext(context);
+ throw err;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Exists expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ExistsCompiler();
+ }
+//#endif
+
+//#ifdefined STREAM
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public ExistsAdjunct getStreamingAdjunct() {
+ return new ExistsAdjunct();
+ }
+
+ //#endif
+}
+
diff --git a/sf/saxon/functions/ExtensionFunctionFactory.java b/sf/saxon/functions/ExtensionFunctionFactory.java
new file mode 100644
index 0000000..7ea1012
--- /dev/null
+++ b/sf/saxon/functions/ExtensionFunctionFactory.java
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+/**
+ * This is a marker interface representing an abstract superclass of JavaExtensionFunctionFactory
+ * and DotNetExtensionFunctionFactory. These play equivalent roles in the system: that is, they
+ * are responsible for determining how the QNames of extension functions are bound to concrete
+ * implementation classes; but they do not share the same interface.
+ *
+ * <p>This interface was introduced in Saxon 8.9. Prior to that, <code>ExtensionFunctionFactory</code>
+ * was a concrete class - the class now named <code>JavaExtensionFunctionFactory</code>.
+ */
+
+public interface ExtensionFunctionFactory {
+}
+
diff --git a/sf/saxon/functions/False.java b/sf/saxon/functions/False.java
new file mode 100644
index 0000000..e1897c5
--- /dev/null
+++ b/sf/saxon/functions/False.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+
+/**
+ * Implement the XPath function fn:false()
+ */
+
+public class False extends SystemFunctionCall implements Callable {
+
+ @Override
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.FALSE;
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return BooleanValue.FALSE;
+ }
+}
+
diff --git a/sf/saxon/functions/Floor.java b/sf/saxon/functions/Floor.java
new file mode 100644
index 0000000..72379b9
--- /dev/null
+++ b/sf/saxon/functions/Floor.java
@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.RoundingCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.NumericValue;
+
+/**
+* This class supports the ceiling(), floor(), round(), and round-to-half-even() functions,
+ * and also the abs() function
+*/
+
+public final class Floor extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate the function
+ */
+
+ /*@Nullable*/ public NumericValue evaluateItem(XPathContext context) throws XPathException {
+
+ NumericValue val0 = (NumericValue)argument[0].evaluateItem(context);
+ if (val0 == null) {
+ return null;
+ }
+ return val0.floor();
+ }
+
+ /**
+ * Determine the cardinality of the function.
+ */
+
+ public int computeCardinality() {
+ return argument[0].getCardinality();
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NumericValue val0 = (NumericValue)arguments[0].head();
+ if (val0 == null) {
+ return EmptySequence.getInstance();
+ }
+ return val0.floor();
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Floor expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new RoundingCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/FormatDate.java b/sf/saxon/functions/FormatDate.java
new file mode 100644
index 0000000..c95d14d
--- /dev/null
+++ b/sf/saxon/functions/FormatDate.java
@@ -0,0 +1,791 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.SavedNamespaceContext;
+import net.sf.saxon.expr.number.Alphanumeric;
+import net.sf.saxon.expr.number.NamedTimeZone;
+import net.sf.saxon.expr.number.Numberer_en;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.Numberer;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Implement the format-date(), format-time(), and format-dateTime() functions
+ * in XSLT 2.0 and XQuery 1.1.
+ */
+
+public class FormatDate extends SystemFunctionCall implements Callable {
+
+ static String[] knownCalendars = {"AD", "AH", "AME", "AM", "AP", "AS", "BE", "CB", "CE", "CL", "CS", "EE", "FE", "ISO", "JE",
+ "KE", "KY", "ME", "MS", "NS", "OS", "RS", "SE", "SH", "SS", "TE", "VE", "VS"};
+
+ private NamespaceResolver nsContext = null;
+ private boolean is30 = false;
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ int numArgs = argument.length;
+ if (numArgs != 2 && numArgs != 5) {
+ throw new XPathException("Function " + getDisplayName() +
+ " must have either two or five arguments",
+ this);
+ }
+ super.checkArguments(visitor);
+ }
+
+ /**
+ * Bind aspects of the static context on which the particular function depends
+ *
+ * @param env the static context of the function call
+ */
+ @Override
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ is30 = env.getXPathLanguageLevel().equals(DecimalValue.THREE);
+ if (nsContext == null && getNumberOfArguments() > 2) {
+ nsContext = new SavedNamespaceContext(env.getNamespaceResolver());
+ }
+ }
+
+ /**
+ * Evaluate in a general context
+ */
+
+ /*@Nullable*/
+ public StringValue evaluateItem(XPathContext context) throws XPathException {
+ CalendarValue value = (CalendarValue) argument[0].evaluateItem(context);
+ if (value == null) {
+ return null;
+ }
+ String format = argument[1].evaluateItem(context).getStringValue();
+
+ StringValue calendarVal = null;
+ StringValue countryVal = null;
+ StringValue languageVal = null;
+ if (argument.length > 2) {
+ languageVal = (StringValue) argument[2].evaluateItem(context);
+ calendarVal = (StringValue) argument[3].evaluateItem(context);
+ countryVal = (StringValue) argument[4].evaluateItem(context);
+ }
+
+ String language = (languageVal == null ? null : languageVal.getStringValue());
+ String country = (countryVal == null ? null : countryVal.getStringValue());
+ CharSequence result = formatDate(value, format, language, country, context);
+ if (calendarVal != null) {
+ result = adjustCalendar(calendarVal, result, context);
+ }
+ return new StringValue(result);
+ }
+
+ private CharSequence adjustCalendar(StringValue calendarVal, CharSequence result, XPathContext context) throws XPathException {
+ StructuredQName cal;
+ try {
+ cal = StructuredQName.fromLexicalQName(calendarVal.getStringValue(), false,
+ is30, context.getConfiguration().getNameChecker(), nsContext);
+ } catch (XPathException e) {
+ XPathException err = new XPathException("Invalid calendar name. " + e.getMessage());
+ err.setErrorCode("FOFD1340");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+
+ if (cal.getURI().equals("")) {
+ String calLocal = cal.getLocalPart();
+ if (calLocal.equals("AD") || calLocal.equals("ISO")) {
+ // no action
+ } else if (Arrays.binarySearch(knownCalendars, calLocal) >= 0) {
+ result = "[Calendar: AD]" + result.toString();
+ } else {
+ XPathException err = new XPathException("Unknown no-namespace calendar: " + calLocal);
+ err.setErrorCode("FOFD1340");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ } else {
+ result = "[Calendar: AD]" + result.toString();
+ }
+ return result;
+ }
+
+ /**
+ * This method analyzes the formatting picture and delegates the work of formatting
+ * individual parts of the date.
+ *
+ * @param value the value to be formatted
+ * @param format the supplied format picture
+ * @param language the chosen language
+ * @param country the chosen country
+ * @param context the XPath dynamic evaluation context
+ * @return the formatted date/time
+ */
+
+ private static CharSequence formatDate(CalendarValue value, String format, String language, String country, XPathContext context)
+ throws XPathException {
+
+ Configuration config = context.getConfiguration();
+
+ boolean languageDefaulted = (language == null);
+ if (language == null) {
+ language = config.getDefaultLanguage();
+ }
+ if (country == null) {
+ country = config.getDefaultCountry();
+ }
+
+ Numberer numberer = config.makeNumberer(language, country);
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ if (numberer.getClass() == Numberer_en.class && !"en".equals(language) && !languageDefaulted) {
+ sb.append("[Language: en]");
+ }
+
+
+ int i = 0;
+ while (true) {
+ while (i < format.length() && format.charAt(i) != '[') {
+ sb.append(format.charAt(i));
+ if (format.charAt(i) == ']') {
+ i++;
+ if (i == format.length() || format.charAt(i) != ']') {
+ XPathException e = new XPathException("Closing ']' in date picture must be written as ']]'");
+ e.setErrorCode("XTDE1340");
+ e.setXPathContext(context);
+ throw e;
+ }
+ }
+ i++;
+ }
+ if (i == format.length()) {
+ break;
+ }
+ // look for '[['
+ i++;
+ if (i < format.length() && format.charAt(i) == '[') {
+ sb.append('[');
+ i++;
+ } else {
+ int close = (i < format.length() ? format.indexOf("]", i) : -1);
+ if (close == -1) {
+ XPathException e = new XPathException("Date format contains a '[' with no matching ']'");
+ e.setErrorCode("XTDE1340");
+ e.setXPathContext(context);
+ throw e;
+ }
+ String componentFormat = format.substring(i, close);
+ sb.append(formatComponent(value, Whitespace.removeAllWhitespace(componentFormat),
+ numberer, country, context));
+ i = close + 1;
+ }
+ }
+ return sb;
+ }
+
+ private static Pattern componentPattern =
+ Pattern.compile("([YMDdWwFHhmsfZzPCE])\\s*(.*)");
+
+ private static CharSequence formatComponent(CalendarValue value, CharSequence specifier,
+ Numberer numberer, String country, XPathContext context)
+ throws XPathException {
+ boolean ignoreDate = (value instanceof TimeValue);
+ boolean ignoreTime = (value instanceof DateValue);
+ DateTimeValue dtvalue = value.toDateTime();
+
+ Matcher matcher = componentPattern.matcher(specifier);
+ if (!matcher.matches()) {
+ XPathException error = new XPathException("Unrecognized date/time component [" + specifier + ']');
+ error.setErrorCode("XTDE1340");
+ error.setXPathContext(context);
+ throw error;
+ }
+ String component = matcher.group(1);
+ String format = matcher.group(2);
+ if (format == null) {
+ format = "";
+ }
+ boolean defaultFormat = false;
+ if ("".equals(format) || format.startsWith(",")) {
+ defaultFormat = true;
+ switch (component.charAt(0)) {
+ case 'F':
+ format = "Nn" + format;
+ break;
+ case 'P':
+ format = 'n' + format;
+ break;
+ case 'C':
+ case 'E':
+ format = 'N' + format;
+ break;
+ case 'm':
+ case 's':
+ format = "01" + format;
+ break;
+ case 'z':
+ case 'Z':
+ //format = "00:00" + format;
+ break;
+ default:
+ format = '1' + format;
+ }
+ }
+
+ switch (component.charAt(0)) {
+ case 'Y': // year
+ if (ignoreDate) {
+ XPathException error = new XPathException("In formatTime(): an xs:time value does not contain a year component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int year = dtvalue.getYear();
+ if (year < 0) {
+ year = 1 - year;
+ }
+ return formatNumber(component, year, format, defaultFormat, numberer, context);
+ }
+ case 'M': // month
+ if (ignoreDate) {
+ XPathException error = new XPathException("In formatTime(): an xs:time value does not contain a month component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int month = dtvalue.getMonth();
+ return formatNumber(component, month, format, defaultFormat, numberer, context);
+ }
+ case 'D': // day in month
+ if (ignoreDate) {
+ XPathException error = new XPathException("In formatTime(): an xs:time value does not contain a day component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int day = dtvalue.getDay();
+ return formatNumber(component, day, format, defaultFormat, numberer, context);
+ }
+ case 'd': // day in year
+ if (ignoreDate) {
+ XPathException error = new XPathException("In formatTime(): an xs:time value does not contain a day component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int day = DateValue.getDayWithinYear(dtvalue.getYear(), dtvalue.getMonth(), dtvalue.getDay());
+ return formatNumber(component, day, format, defaultFormat, numberer, context);
+ }
+ case 'W': // week of year
+ if (ignoreDate) {
+ XPathException error = new XPathException("In formatTime(): cannot obtain the week number from an xs:time value");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int week = DateValue.getWeekNumber(dtvalue.getYear(), dtvalue.getMonth(), dtvalue.getDay());
+ return formatNumber(component, week, format, defaultFormat, numberer, context);
+ }
+ case 'w': // week in month
+ if (ignoreDate) {
+ XPathException error = new XPathException("In formatTime(): cannot obtain the week number from an xs:time value");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int week = DateValue.getWeekNumberWithinMonth(dtvalue.getYear(), dtvalue.getMonth(), dtvalue.getDay());
+ return formatNumber(component, week, format, defaultFormat, numberer, context);
+ }
+ case 'H': // hour in day
+ if (ignoreTime) {
+ XPathException error = new XPathException("In formatDate(): an xs:date value does not contain an hour component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ Int64Value hour = (Int64Value) value.getComponent(Component.HOURS);
+ return formatNumber(component, (int) hour.longValue(), format, defaultFormat, numberer, context);
+ }
+ case 'h': // hour in half-day (12 hour clock)
+ if (ignoreTime) {
+ XPathException error = new XPathException("In formatDate(): an xs:date value does not contain an hour component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ Int64Value hour = (Int64Value) value.getComponent(Component.HOURS);
+ int hr = (int) hour.longValue();
+ if (hr > 12) {
+ hr = hr - 12;
+ }
+ if (hr == 0) {
+ hr = 12;
+ }
+ return formatNumber(component, hr, format, defaultFormat, numberer, context);
+ }
+ case 'm': // minutes
+ if (ignoreTime) {
+ XPathException error = new XPathException("In formatDate(): an xs:date value does not contain a minutes component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ Int64Value min = (Int64Value) value.getComponent(Component.MINUTES);
+ return formatNumber(component, (int) min.longValue(), format, defaultFormat, numberer, context);
+ }
+ case 's': // seconds
+ if (ignoreTime) {
+ XPathException error = new XPathException("In formatDate(): an xs:date value does not contain a seconds component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ IntegerValue sec = (IntegerValue) value.getComponent(Component.WHOLE_SECONDS);
+ return formatNumber(component, (int) sec.longValue(), format, defaultFormat, numberer, context);
+ }
+ case 'f': // fractional seconds
+ // ignore the format
+ if (ignoreTime) {
+ XPathException error = new XPathException("In formatDate(): an xs:date value does not contain a fractional seconds component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int micros = (int) ((Int64Value) value.getComponent(Component.MICROSECONDS)).longValue();
+ return formatNumber(component, micros, format, defaultFormat, numberer, context);
+ }
+ case 'z':
+ case 'Z':
+ return formatTimeZone(value.toDateTime(), component.charAt(0), format, country);
+
+ case 'F': // day of week
+ if (ignoreDate) {
+ XPathException error = new XPathException("In formatTime(): an xs:time value does not contain day-of-week component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int day = DateValue.getDayOfWeek(dtvalue.getYear(), dtvalue.getMonth(), dtvalue.getDay());
+ return formatNumber(component, day, format, defaultFormat, numberer, context);
+ }
+ case 'P': // am/pm marker
+ if (ignoreTime) {
+ XPathException error = new XPathException("In formatDate(): an xs:date value does not contain an am/pm component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int minuteOfDay = dtvalue.getHour() * 60 + dtvalue.getMinute();
+ return formatNumber(component, minuteOfDay, format, defaultFormat, numberer, context);
+ }
+ case 'C': // calendar
+ return numberer.getCalendarName("AD");
+ case 'E': // era
+ if (ignoreDate) {
+ XPathException error = new XPathException("In formatTime(): an xs:time value does not contain an AD/BC component");
+ error.setErrorCode("XTDE1350");
+ error.setXPathContext(context);
+ throw error;
+ } else {
+ int year = dtvalue.getYear();
+ return numberer.getEraName(year);
+ }
+ default:
+ XPathException e = new XPathException("Unknown formatDate/time component specifier '" + format.charAt(0) + '\'');
+ e.setErrorCode("XTDE1340");
+ e.setXPathContext(context);
+ throw e;
+ }
+ }
+
+ private static Pattern formatPattern =
+ //Pattern.compile("([^ot,]*?)([ot]?)(,.*)?"); // GNU Classpath has problems with this one
+ Pattern.compile("([^,]*)(,.*)?"); // Note, the group numbers are different from above
+
+ private static Pattern widthPattern =
+ Pattern.compile(",(\\*|[0-9]+)(\\-(\\*|[0-9]+))?");
+
+ private static Pattern alphanumericPattern =
+ Pattern.compile("([A-Za-z0-9]|\\p{L}|\\p{N})*");
+ // the first term is redundant, but GNU Classpath can't cope with the others...
+
+ private static Pattern digitsPattern =
+ Pattern.compile("\\p{Nd}*");
+
+ private static CharSequence formatNumber(String component, int value,
+ String format, boolean defaultFormat, Numberer numberer, XPathContext context)
+ throws XPathException {
+ Matcher matcher = formatPattern.matcher(format);
+ if (!matcher.matches()) {
+ XPathException error = new XPathException("Unrecognized format picture [" + component + format + ']');
+ error.setErrorCode("XTDE1340");
+ error.setXPathContext(context);
+ throw error;
+ }
+ //String primary = matcher.group(1);
+ //String modifier = matcher.group(2);
+ String primary = matcher.group(1);
+ String modifier = null;
+ if (primary.endsWith("t")) {
+ primary = primary.substring(0, primary.length() - 1);
+ modifier = "t";
+ } else if (primary.endsWith("o")) {
+ primary = primary.substring(0, primary.length() - 1);
+ modifier = "o";
+ }
+ String letterValue = ("t".equals(modifier) ? "traditional" : null);
+ String ordinal = ("o".equals(modifier) ? numberer.getOrdinalSuffixForDateTime(component) : null);
+ String widths = matcher.group(2); // was 3
+
+ if (!alphanumericPattern.matcher(primary).matches()) {
+ XPathException error = new XPathException("In format picture at '" + primary +
+ "', primary format must be alphanumeric");
+ error.setErrorCode("XTDE1340");
+ error.setXPathContext(context);
+ throw error;
+ }
+ int min = 1;
+ int max = Integer.MAX_VALUE;
+
+ if (widths == null || "".equals(widths)) {
+ if (digitsPattern.matcher(primary).matches()) {
+ int len = StringValue.getStringLength(primary);
+ if (len > 1) {
+ // "A format token containing leading zeroes, such as 001, sets the minimum and maximum width..."
+ // We interpret this literally: a format token of "1" does not set a maximum, because it would
+ // cause the year 2006 to be formatted as "6".
+ min = len;
+ max = len;
+ }
+ }
+ } else if (primary.equals("I") || primary.equals("i")) {
+ int[] range = getWidths(widths);
+ min = range[0];
+ max = Integer.MAX_VALUE;
+
+ String s = numberer.format(value, UnicodeString.makeUnicodeString(primary), null, letterValue, ordinal);
+ int len = StringValue.getStringLength(s);
+ while (len < min) {
+ s = s + ' ';
+ len++;
+ }
+ return s;
+ } else {
+ int[] range = getWidths(widths);
+ min = range[0];
+ max = range[1];
+ if (defaultFormat) {
+ // if format was defaulted, the explicit widths override the implicit format
+ if (primary.endsWith("1") && min != primary.length()) {
+ FastStringBuffer sb = new FastStringBuffer(min + 1);
+ for (int i = 1; i < min; i++) {
+ sb.append('0');
+ }
+ sb.append('1');
+ primary = sb.toString();
+ }
+ }
+ }
+
+ if ("P".equals(component)) {
+ // A.M./P.M. can only be formatted as a name
+ if (!("N".equals(primary) || "n".equals(primary) || "Nn".equals(primary))) {
+ primary = "n";
+ }
+ if (max == Integer.MAX_VALUE) {
+ // if no max specified, use 4. An explicit greater value allows use of "noon" and "midnight"
+ max = 4;
+ }
+ } else if ("f".equals(component)) {
+ // value is supplied as integer number of microseconds
+ String s;
+ if (value == 0) {
+ s = "0";
+ } else {
+ s = ((1000000 + value) + "").substring(1);
+ if (s.length() > max) {
+ DecimalValue dec = new DecimalValue(new BigDecimal("0." + s));
+ dec = (DecimalValue) dec.roundHalfToEven(max);
+ s = dec.getStringValue();
+ if (s.length() > 2) {
+ // strip the ".0"
+ s = s.substring(2);
+ } else {
+ // fractional seconds value was 0
+ s = "";
+ }
+ }
+ }
+ while (s.length() < min) {
+ s = s + '0';
+ }
+ while (s.length() > min && s.charAt(s.length() - 1) == '0') {
+ s = s.substring(0, s.length() - 1);
+ }
+ return s;
+ }
+
+ if ("N".equals(primary) || "n".equals(primary) || "Nn".equals(primary)) {
+ String s = "";
+ if ("M".equals(component)) {
+ s = numberer.monthName(value, min, max);
+ } else if ("F".equals(component)) {
+ s = numberer.dayName(value, min, max);
+ } else if ("P".equals(component)) {
+ s = numberer.halfDayName(value, min, max);
+ } else {
+ primary = "1";
+ }
+ if ("N".equals(primary)) {
+ return s.toUpperCase();
+ } else if ("n".equals(primary)) {
+ return s.toLowerCase();
+ } else {
+ return s;
+ }
+ }
+
+ String s = numberer.format(value, UnicodeString.makeUnicodeString(primary), null, letterValue, ordinal);
+ int len = StringValue.getStringLength(s);
+ while (len < min) {
+ // assert: this can only happen as a result of width specifiers, in which case we're using ASCII digits
+ s = ("00000000" + s).substring(s.length() + 8 - min);
+ len = StringValue.getStringLength(s);
+ }
+ if (len > max) {
+ // the year is the only field we allow to be truncated
+ if (component.charAt(0) == 'Y') {
+ if (len == s.length()) {
+ // no wide characters
+ s = s.substring(s.length() - max);
+ } else {
+ // assert: each character must be two bytes long
+ s = s.substring(s.length() - 2 * max);
+ }
+
+ }
+ }
+ return s;
+ }
+
+ private static int[] getWidths(String widths) throws XPathException {
+ try {
+ int min = -1;
+ int max = -1;
+
+ if (!"".equals(widths)) {
+ Matcher widthMatcher = widthPattern.matcher(widths);
+ if (widthMatcher.matches()) {
+ String smin = widthMatcher.group(1);
+ if (smin == null || "".equals(smin) || "*".equals(smin)) {
+ min = 1;
+ } else {
+ min = Integer.parseInt(smin);
+ }
+ String smax = widthMatcher.group(3);
+ if (smax == null || "".equals(smax) || "*".equals(smax)) {
+ max = Integer.MAX_VALUE;
+ } else {
+ max = Integer.parseInt(smax);
+ }
+ } else {
+ XPathException error = new XPathException("Unrecognized width specifier " + Err.wrap(widths, Err.VALUE));
+ error.setErrorCode("XTDE1340");
+ throw error;
+ }
+ }
+
+ if (min > max && max != -1) {
+ XPathException e = new XPathException("Minimum width in date/time picture exceeds maximum width");
+ e.setErrorCode("XTDE1340");
+ throw e;
+ }
+ int[] result = new int[2];
+ result[0] = min;
+ result[1] = max;
+ return result;
+ } catch (NumberFormatException err) {
+ XPathException e = new XPathException("Invalid integer used as width in date/time picture");
+ e.setErrorCode("XTDE1340");
+ throw e;
+ }
+ }
+
+ private static String formatTimeZone(DateTimeValue value, char component, String format, String country) throws XPathException {
+ int comma = format.lastIndexOf(',');
+ String widthModifier = "";
+ if (comma >= 0) {
+ widthModifier = format.substring(comma);
+ format = format.substring(0, comma);
+ }
+ if (!value.hasTimezone()) {
+ if (format.equals("Z")) {
+ return "J"; // military "local time"
+ } else {
+ return "";
+ }
+ }
+ if (format.length() == 0 && widthModifier.length() > 0) {
+ int[] widths = getWidths(widthModifier);
+ int min = widths[0];
+ int max = widths[1];
+ if (min <= 1) {
+ format = (max >= 4 ? "0:00" : "0");
+ } else if (min <= 4) {
+ format = (max >= 5 ? "00:00" : "00");;
+ } else {
+ format = "00:00";
+ }
+ }
+ if (format.length() == 0) {
+ format = "00:00";
+ }
+ int tz = value.getTimezoneInMinutes();
+ boolean useZforZero = format.endsWith("t");
+ if (useZforZero && tz == 0) {
+ return "Z";
+ }
+ if (useZforZero) {
+ format = format.substring(0, format.length() - 1);
+ }
+ int digits = 0;
+ int separators = 0;
+ int separatorChar = ':';
+ int zeroDigit = -1;
+ int[] expandedFormat = StringValue.expand(format);
+ for (int ch : expandedFormat) {
+ if (Character.isDigit(ch)) {
+ digits++;
+ if (zeroDigit < 0) {
+ zeroDigit = Alphanumeric.getDigitFamily(ch);
+ }
+ } else {
+ separators++;
+ separatorChar = ch;
+ }
+ }
+ int[] buffer = new int[10];
+ int used = 0;
+ if (digits > 0) {
+ // Numeric timezone formatting
+ if (component == 'z') {
+ buffer[0] = 'G';
+ buffer[1] = 'M';
+ buffer[2] = 'T';
+ used = 3;
+ }
+ boolean negative = tz < 0;
+ tz = java.lang.Math.abs(tz);
+ buffer[used++] = (negative ? '-' : '+');
+
+ int hour = tz / 60;
+ int minute = tz % 60;
+
+ boolean includeMinutes = minute != 0 || digits >= 3 || separators > 0;
+ boolean includeSep = (minute != 0 && digits <=2) || (separators > 0 && (minute != 0 || digits >= 3));
+
+ int hourDigits = (digits <= 2 ? digits : digits - 2);
+
+ if (hour > 9 || hourDigits >= 2) {
+ buffer[used++] = zeroDigit + hour / 10;
+ }
+ buffer[used++] = (hour % 10) + zeroDigit;
+
+ if (includeSep) {
+ buffer[used++] = separatorChar;
+ }
+ if (includeMinutes) {
+ buffer[used++] = minute / 10 + zeroDigit;
+ buffer[used++] = minute % 10 + zeroDigit;
+ }
+
+ return StringValue.contract(buffer, used).toString();
+ } else if (format.equals("Z")) {
+ // military timezone formatting
+ int hour = tz / 60;
+ int minute = tz % 60;
+ if (hour < -12 || hour > 12 || minute != 0) {
+ return formatTimeZone(value, 'Z', "00:00", country);
+ } else {
+ return Character.toString("YXWVUTSRQPONZABCDEFGHIKLM".charAt(hour + 12));
+ }
+ } else if (format.charAt(0) == 'N' || format.charAt(0) == 'n') {
+ return getNamedTimeZone(value, country, format);
+ } else {
+ return formatTimeZone(value, 'Z', "00:00", country);
+ }
+
+ }
+
+ private static String getNamedTimeZone(DateTimeValue value, String country, String format) throws XPathException {
+
+ int min = 1;
+ int comma = format.indexOf(',');
+ if (comma > 0) {
+ String widths = format.substring(comma);
+ int[] range = getWidths(widths);
+ min = range[0];
+ }
+ if (format.charAt(0) == 'N' || format.charAt(0) == 'n') {
+ if (min <= 5) {
+ String tzname = NamedTimeZone.getTimeZoneNameForDate(value, country);
+ if (format.charAt(0) == 'n') {
+ tzname = tzname.toLowerCase();
+ }
+ return tzname;
+ } else {
+ return NamedTimeZone.getOlsenTimeZoneName(value, country);
+ }
+ }
+ FastStringBuffer sbz = new FastStringBuffer(8);
+ value.appendTimezone(sbz);
+ return sbz.toString();
+ }
+
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ CalendarValue value = (CalendarValue) arguments[0].head();
+ if (value == null) {
+ return EmptySequence.getInstance();
+ }
+ String format = arguments[1].head().getStringValue();
+
+ StringValue calendarVal = null;
+ StringValue countryVal = null;
+ StringValue languageVal = null;
+ if (argument.length > 2) {
+ languageVal = (StringValue) arguments[2].head();
+ calendarVal = (StringValue) arguments[3].head();
+ countryVal = (StringValue) arguments[4].head();
+ }
+
+ String language = (languageVal == null ? null : languageVal.getStringValue());
+ String country = (countryVal == null ? null : countryVal.getStringValue());
+ CharSequence result = formatDate(value, format, language, country, context);
+ if (calendarVal != null) {
+ result = adjustCalendar(calendarVal, result, context);
+ }
+ return new StringValue(result);
+ }
+
+}
+
diff --git a/sf/saxon/functions/FormatNumber.java b/sf/saxon/functions/FormatNumber.java
new file mode 100644
index 0000000..64bca7b
--- /dev/null
+++ b/sf/saxon/functions/FormatNumber.java
@@ -0,0 +1,895 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SavedNamespaceContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.DecimalFormatManager;
+import net.sf.saxon.trans.DecimalSymbols;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * XSLT 2.0 implementation of format-number() function - removes the dependence on the JDK.
+ */
+
+public class FormatNumber extends SystemFunctionCall implements Callable {
+
+ /*@Nullable*/ private NamespaceResolver nsContext = null;
+ // held only if the third argument is present, and its value is not known statically
+
+ private DecimalFormatManager decimalFormatManager = null;
+ // held only if the decimalFormatSymbols cannot be determined statically
+
+ private DecimalSymbols decimalFormatSymbols = null;
+ // held only if the decimal format to use can be determined statically
+
+ private transient String picture = null;
+ // held transiently at compile time if the picture is known statically
+
+ private SubPicture[] subPictures = null;
+ // held if the picture is known statically
+
+ private transient boolean checked = false;
+ // the second time checkArguments is called, it's a global check so the static context is inaccurate
+
+ private NameChecker nameChecker = Name10Checker.getInstance();
+
+ private boolean is30;
+
+ /**
+ * Bind aspects of the static context on which the particular function depends
+ *
+ * @param env the static context of the function call
+ */
+ @Override
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ if (checked) {
+ return;
+ }
+ checked = true;
+ nsContext = new SavedNamespaceContext(env.getNamespaceResolver());
+ nameChecker = env.getConfiguration().getNameChecker();
+ decimalFormatManager = env.getDecimalFormatManager();
+ if (decimalFormatManager == null) {
+ // create a decimal format manager which will allow a "default default" format only
+ decimalFormatManager = new DecimalFormatManager();
+ }
+ decimalFormatSymbols = decimalFormatManager.getDefaultDecimalFormat();
+ is30 = env.getXPathLanguageLevel().equals(DecimalValue.THREE);
+ }
+
+ public void checkArguments(ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ if (argument[1] instanceof StringLiteral) {
+ // picture is known statically - optimize for this common case
+ picture = ((StringLiteral)argument[1]).getStringValue();
+ }
+ if (argument.length==3) {
+ if (argument[2] instanceof StringLiteral) {
+ // common case, decimal format name is supplied as a string literal
+
+ String lexicalName = ((StringLiteral)argument[2]).getStringValue();
+
+ StructuredQName qName;
+ try {
+ boolean is30 = visitor.getExecutable().isAllowXPath30();
+ qName = StructuredQName.fromLexicalQName(lexicalName, false, is30, nameChecker, nsContext);
+ } catch (XPathException e) {
+ XPathException se = new XPathException("Invalid decimal format name. " + e.getMessage());
+ se.setErrorCode("FODF1280");
+ throw se;
+ }
+
+ decimalFormatSymbols = decimalFormatManager.getNamedDecimalFormat(qName);
+ if (decimalFormatSymbols == null) {
+ XPathException se = new XPathException("Unknown decimal format name " + lexicalName);
+ se.setErrorCode("FODF1280");
+ throw se;
+ }
+ } else {
+ decimalFormatSymbols = null; // meaning not known statically
+ }
+ }
+ }
+
+ /**
+ * Analyze a picture string into two sub-pictures.
+ * @param picture the picture as written (possibly two subpictures separated by a semicolon)
+ * @param dfs the decimal format symbols
+ * @return an array of two sub-pictures, the positive and the negative sub-pictures respectively.
+ * If there is only one sub-picture, the second one is null.
+ * @throws XPathException if the picture is invalid
+ */
+
+ private SubPicture[] getSubPictures(String picture, DecimalSymbols dfs) throws XPathException {
+ int[] picture4 = StringValue.expand(picture);
+ SubPicture[] pics = new SubPicture[2];
+ if (picture4.length==0) {
+ XPathException err = new XPathException("format-number() picture is zero-length");
+ err.setErrorCode("FODF1310");
+ throw err;
+ }
+ int sep = -1;
+ for (int c=0; c<picture4.length; c++) {
+ if (picture4[c] == dfs.getPatternSeparator()) {
+ if (c==0) {
+ grumble("first subpicture is zero-length");
+ } else if (sep >= 0) {
+ grumble("more than one pattern separator");
+ } else if (sep == picture4.length-1) {
+ grumble("second subpicture is zero-length");
+ }
+ sep = c;
+ }
+ }
+
+ if (sep<0) {
+ pics[0] = new SubPicture(picture4, dfs);
+ pics[1] = null;
+ } else {
+ int[] pic0 = new int[sep];
+ System.arraycopy(picture4, 0, pic0, 0, sep);
+ int[] pic1 = new int[picture4.length - sep - 1];
+ System.arraycopy(picture4, sep+1, pic1, 0, picture4.length - sep - 1);
+ pics[0] = new SubPicture(pic0, dfs);
+ pics[1] = new SubPicture(pic1, dfs);
+ }
+ return pics;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing.
+ * We can't evaluate early because we don't have access to the DecimalFormatManager.
+ * @param visitor the expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ FormatNumber fn = (FormatNumber)super.copy();
+ fn.nsContext = nsContext;
+ fn.decimalFormatManager = decimalFormatManager;
+ fn.decimalFormatSymbols = decimalFormatSymbols;
+ fn.picture = picture;
+ fn.subPictures = subPictures;
+ return fn;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return super.equals(o) &&
+ equalOrNull(nsContext, ((FormatNumber)o).nsContext) &&
+ equalOrNull(decimalFormatManager, ((FormatNumber)o).decimalFormatManager) &&
+ equalOrNull(decimalFormatSymbols, ((FormatNumber)o).decimalFormatSymbols) &&
+ equalOrNull(picture, ((FormatNumber)o).picture) &&
+ equalOrNull(subPictures, ((FormatNumber)o).subPictures);
+
+ }
+
+ /**
+ * Evaluate in a context where a string is wanted
+ */
+
+ public CharSequence evaluateAsString(XPathContext context) throws XPathException {
+
+ int numArgs = argument.length;
+ DecimalSymbols dfs = decimalFormatSymbols;
+
+ AtomicValue av0 = (AtomicValue)argument[0].evaluateItem(context);
+ if (av0 == null) {
+ av0 = DoubleValue.NaN;
+ }
+ NumericValue number = (NumericValue)av0;
+
+ if (dfs == null) {
+ DecimalFormatManager dfm = decimalFormatManager;
+ if (numArgs==2) {
+ dfs = dfm.getDefaultDecimalFormat();
+ } else {
+ // the decimal-format name was given as a run-time expression
+ Item arg2 = argument[2].evaluateItem(context);
+ if (arg2 == null) {
+ dfs = dfm.getDefaultDecimalFormat();
+ } else {
+ String lexicalName = argument[2].evaluateItem(context).getStringValue();
+ StructuredQName qName;
+ try {
+ qName = StructuredQName.fromLexicalQName(lexicalName, false,
+ is30, context.getConfiguration().getNameChecker(), nsContext);
+ } catch (XPathException e) {
+ XPathException err = new XPathException("Invalid decimal format name. " + e.getMessage());
+ err.setErrorCode("FODF1280");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+
+ dfs = dfm.getNamedDecimalFormat(qName);
+ if (dfs==null) {
+ XPathException err = new XPathException("format-number function: decimal-format '" + lexicalName + "' is not defined");
+ err.setErrorCode("FODF1280");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ }
+ }
+ }
+ SubPicture[] pics = subPictures;
+ if (pics == null) {
+ String format = argument[1].evaluateItem(context).getStringValue();
+ pics = getSubPictures(format, dfs);
+ }
+ return formatNumber(number, pics, dfs);
+ }
+
+
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ return new StringValue(evaluateAsString(c));
+ }
+
+ /**
+ * Format a number, given the two subpictures and the decimal format symbols
+ * @param number the number to be formatted
+ * @param subPictures the negative and positive subPictures
+ * @param dfs the decimal format symbols to be used
+ * @return the formatted number
+ */
+
+ private CharSequence formatNumber(NumericValue number,
+ SubPicture[] subPictures,
+ DecimalSymbols dfs) {
+
+ NumericValue absN = number;
+ SubPicture pic;
+ String minusSign = "";
+ if (number.signum() < 0) {
+ absN = number.negate();
+ if (subPictures[1]==null) {
+ pic = subPictures[0];
+ minusSign = "" + unicodeChar(dfs.getMinusSign());
+ } else {
+ pic = subPictures[1];
+ }
+ } else {
+ pic = subPictures[0];
+ }
+
+ return pic.format(absN, dfs, minusSign);
+ }
+
+ private void grumble(String s) throws XPathException {
+ dynamicError("format-number picture: " + s, "FODF1310", null);
+ }
+
+ /**
+ * Convert a double to a BigDecimal. In general there will be several BigDecimal values that
+ * are equal to the supplied value, and the one we want to choose is the one with fewest non-zero
+ * digits. The algorithm used is rather pragmatic: look for a string of zeroes or nines, try rounding
+ * the number down or up as approriate, then convert the adjusted value to a double to see if it's
+ * equal to the original: if not, use the original value unchanged.
+ * @param value the double to be converted
+ * @param precision 2 for a double, 1 for a float
+ * @return the result of conversion to a double
+ */
+
+ public static BigDecimal adjustToDecimal(double value, int precision) {
+ final String zeros = (precision == 1 ? "00000" : "000000000");
+ final String nines = (precision == 1 ? "99999" : "999999999");
+ BigDecimal initial = BigDecimal.valueOf(value);
+ BigDecimal trial = null;
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.TINY);
+ DecimalValue.decimalToString(initial, fsb);
+ String s = fsb.toString();
+ int start = (s.charAt(0) == '-' ? 1 : 0);
+ int p = s.indexOf(".");
+ int i = s.lastIndexOf(zeros);
+ if (i > 0) {
+ if (p < 0 || i < p) {
+ // we're in the integer part
+ // try replacing all following digits with zeros and seeing if we get the same double back
+ FastStringBuffer sb = new FastStringBuffer(s.length());
+ sb.append(s.substring(0, i));
+ for (int n=i; n<s.length(); n++) {
+ sb.append(s.charAt(n)=='.' ? '.' : '0');
+ }
+ trial = new BigDecimal(sb.toString());
+ } else {
+ // we're in the fractional part
+ // try truncating the number before the zeros and seeing if we get the same double back
+ trial = new BigDecimal(s.substring(0, i));
+
+ }
+ } else {
+ i = s.indexOf(nines);
+ if (i >= 0) {
+ if (i == start) {
+ // number starts with 99999... or -99999. Try rounding up to 100000.. or -100000...
+ FastStringBuffer sb = new FastStringBuffer(s.length() + 1);
+ if (start == 1) {
+ sb.append('-');
+ }
+ sb.append('1');
+ for (int n=start; n<s.length(); n++) {
+ sb.append(s.charAt(n)=='.' ? '.' : '0');
+ }
+ trial = new BigDecimal(sb.toString());
+ } else {
+ // try rounding up
+ while (i >= 0 && (s.charAt(i) == '9' || s.charAt(i) == '.')) {
+ i--;
+ }
+ if (i < 0 || s.charAt(i) == '-') {
+ return initial; // can't happen: we've already handled numbers starting 99999..
+ } else if (p < 0 || i < p) {
+ // we're in the integer part
+ FastStringBuffer sb = new FastStringBuffer(s.length());
+ sb.append(s.substring(0, i));
+ sb.append((char)((int)s.charAt(i) + 1));
+ for (int n=i; n<s.length(); n++) {
+ sb.append(s.charAt(n)=='.' ? '.' : '0');
+ }
+ trial = new BigDecimal(sb.toString());
+ } else {
+ // we're in the fractional part - can ignore following digits
+ String s2 = s.substring(0, i) + (char)((int)s.charAt(i) + 1);
+ trial = new BigDecimal(s2);
+ }
+ }
+ }
+ }
+ if (trial != null && (precision==1 ? trial.floatValue() == value : trial.doubleValue() == value)) {
+ return trial;
+ } else {
+ return initial;
+ }
+ }
+
+
+ /**
+ * Inner class to represent one sub-picture (the negative or positive subpicture)
+ */
+
+ private class SubPicture implements Serializable {
+
+ int minWholePartSize = 0;
+ int maxWholePartSize = 0;
+ int minFractionPartSize = 0;
+ int maxFractionPartSize = 0;
+ boolean isPercent = false;
+ boolean isPerMille = false;
+ String prefix = "";
+ String suffix = "";
+ int[] wholePartGroupingPositions = null;
+ int[] fractionalPartGroupingPositions = null;
+
+ public SubPicture(int[] pic, DecimalSymbols dfs) throws XPathException {
+
+ final int percentSign = dfs.getPercent();
+ final int perMilleSign = dfs.getPerMille();
+ final int decimalSeparator = dfs.getDecimalSeparator();
+ final int groupingSeparator = dfs.getGroupingSeparator();
+ final int digitSign = dfs.getDigit();
+ final int zeroDigit = dfs.getZeroDigit();
+
+ List<Integer> wholePartPositions = null;
+ List<Integer> fractionalPartPositions = null;
+
+ boolean foundDigit = false;
+ boolean foundDecimalSeparator = false;
+ for (int ch : pic) {
+ if (ch == digitSign || ch == zeroDigit || (is30 && isInDigitFamily(ch, zeroDigit))) {
+ foundDigit = true;
+ break;
+ }
+ }
+ if (!foundDigit) {
+ grumble("subpicture contains no digit or zero-digit sign");
+ }
+
+ int phase = 0;
+ // phase = 0: passive characters at start
+ // phase = 1: digit signs in whole part
+ // phase = 2: zero-digit signs in whole part
+ // phase = 3: zero-digit signs in fractional part
+ // phase = 4: digit signs in fractional part
+ // phase = 5: passive characters at end
+
+ for (int c : pic) {
+ if (c == percentSign || c == perMilleSign) {
+ if (isPercent || isPerMille) {
+ grumble("Cannot have more than one percent or per-mille character in a sub-picture");
+ }
+ isPercent = (c == percentSign);
+ isPerMille = (c == perMilleSign);
+ switch (phase) {
+ case 0:
+ prefix += unicodeChar(c);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ phase = 5;
+ suffix += unicodeChar(c);
+ break;
+ }
+ } else if (c == digitSign) {
+ switch (phase) {
+ case 0:
+ case 1:
+ phase = 1;
+ maxWholePartSize++;
+ break;
+ case 2:
+ grumble("Digit sign must not appear after a zero-digit sign in the integer part of a sub-picture");
+ break;
+ case 3:
+ case 4:
+ phase = 4;
+ maxFractionPartSize++;
+ break;
+ case 5:
+ grumble("Passive character must not appear between active characters in a sub-picture");
+ break;
+ }
+ } else if (c == zeroDigit || (is30 && isInDigitFamily(c, zeroDigit))) {
+ switch (phase) {
+ case 0:
+ case 1:
+ case 2:
+ phase = 2;
+ minWholePartSize++;
+ maxWholePartSize++;
+ break;
+ case 3:
+ minFractionPartSize++;
+ maxFractionPartSize++;
+ break;
+ case 4:
+ grumble("Zero digit sign must not appear after a digit sign in the fractional part of a sub-picture");
+ break;
+ case 5:
+ grumble("Passive character must not appear between active characters in a sub-picture");
+ break;
+ }
+ } else if (c == decimalSeparator) {
+ switch (phase) {
+ case 0:
+ case 1:
+ case 2:
+ phase = 3;
+ foundDecimalSeparator = true;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ if (foundDecimalSeparator) {
+ grumble("There must only be one decimal separator in a sub-picture");
+ } else {
+ grumble("Decimal separator cannot come after a character in the suffix");
+ }
+ break;
+ }
+ } else if (c == groupingSeparator) {
+ switch (phase) {
+ case 0:
+ case 1:
+ case 2:
+ if (wholePartPositions == null) {
+ wholePartPositions = new ArrayList<Integer>(3);
+ }
+ wholePartPositions.add(maxWholePartSize);
+ // note these are positions from a false offset, they will be corrected later
+ break;
+ case 3:
+ case 4:
+ if (maxFractionPartSize == 0) {
+ grumble("Grouping separator cannot be adjacent to decimal separator");
+ }
+ if (fractionalPartPositions == null) {
+ fractionalPartPositions = new ArrayList<Integer>(3);
+ }
+ fractionalPartPositions.add(maxFractionPartSize);
+ break;
+ case 5:
+ grumble("Grouping separator found in suffix of sub-picture");
+ break;
+ }
+ } else { // passive character found
+ switch (phase) {
+ case 0:
+ prefix += unicodeChar(c);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ phase = 5;
+ suffix += unicodeChar(c);
+ break;
+ }
+ }
+ }
+
+ if (minWholePartSize == 0 && !foundDecimalSeparator) {
+ minWholePartSize = 1;
+ }
+
+ // System.err.println("minWholePartSize = " + minWholePartSize);
+ // System.err.println("maxWholePartSize = " + maxWholePartSize);
+ // System.err.println("minFractionPartSize = " + minFractionPartSize);
+ // System.err.println("maxFractionPartSize = " + maxFractionPartSize);
+
+ // Sort out the grouping positions
+
+ if (wholePartPositions != null) {
+ // convert to positions relative to the decimal separator
+ int n = wholePartPositions.size();
+ wholePartGroupingPositions = new int[n];
+ for (int i=0; i<n; i++) {
+ wholePartGroupingPositions[i] =
+ maxWholePartSize - wholePartPositions.get(n - i - 1);
+ }
+ if (n > 1) {
+ boolean regular = true;
+ int first = wholePartGroupingPositions[0];
+ for (int i=1; i<n; i++) {
+ if (wholePartGroupingPositions[i] != i * first) {
+ regular = false;
+ break;
+ }
+ }
+ if (regular) {
+ wholePartGroupingPositions = new int[1];
+ wholePartGroupingPositions[0] = first;
+ }
+ }
+ if (wholePartGroupingPositions[0] == 0) {
+ grumble("Cannot have a grouping separator adjacent to the decimal separator");
+ }
+ }
+
+ if (fractionalPartPositions != null) {
+ int n = fractionalPartPositions.size();
+ fractionalPartGroupingPositions = new int[n];
+ for (int i=0; i<n; i++) {
+ fractionalPartGroupingPositions[i] = fractionalPartPositions.get(i);
+ }
+ }
+ }
+
+ /**
+ * Format a number using this sub-picture
+ * @param value the absolute value of the number to be formatted
+ * @param dfs the decimal format symbols to be used
+ * @param minusSign the representation of a minus sign to be used
+ * @return the formatted number
+ */
+
+ public CharSequence format(NumericValue value, DecimalSymbols dfs, String minusSign) {
+
+ // System.err.println("Formatting " + value);
+
+ if (value.isNaN()) {
+ return dfs.getNaN(); // changed by W3C Bugzilla 2712
+ }
+
+ if ((value instanceof DoubleValue || value instanceof FloatValue) &&
+ Double.isInfinite(value.getDoubleValue())) {
+ return minusSign + prefix + dfs.getInfinity() + suffix;
+ }
+
+ int multiplier = 1;
+ if (isPercent) {
+ multiplier = 100;
+ } else if (isPerMille) {
+ multiplier = 1000;
+ }
+
+ if (multiplier != 1) {
+ try {
+ //value = value.arithmetic(Token.MULT, new Int64Value(multiplier), null);
+ value = (NumericValue)ArithmeticExpression.compute(
+ value, Calculator.TIMES, new Int64Value(multiplier), null);
+ } catch (XPathException e) {
+ value = new DoubleValue(value.getDoubleValue() * multiplier);
+ }
+ }
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+ if (value instanceof DoubleValue || value instanceof FloatValue) {
+ BigDecimal dec = adjustToDecimal(value.getDoubleValue(), 2);
+ formatDecimal(dec, sb);
+
+ //formatDouble(value.getDoubleValue(), sb);
+
+ } else if (value instanceof Int64Value || value instanceof BigIntegerValue) {
+ formatInteger(value, sb);
+
+ } else if (value instanceof DecimalValue) {
+ //noinspection RedundantCast
+ formatDecimal(((DecimalValue)value).getDecimalValue(), sb);
+ }
+
+ // System.err.println("Justified number: " + sb.toString());
+
+ // Map the digits and decimal point to use the selected characters
+
+ int[] ib = StringValue.expand(sb);
+ int ibused = ib.length;
+ int point = sb.indexOf('.');
+ if (point == -1) {
+ point = sb.length();
+ } else {
+ ib[point] = dfs.getDecimalSeparator();
+
+ // If there is no fractional part, delete the decimal point
+ if (maxFractionPartSize == 0) {
+ ibused--;
+ }
+ }
+
+ // Map the digits
+
+ if (dfs.getZeroDigit() != '0') {
+ int newZero = dfs.getZeroDigit();
+ for (int i=0; i<ibused; i++) {
+ int c = ib[i];
+ if (c>='0' && c<='9') {
+ ib[i] = (c-'0'+newZero);
+ }
+ }
+ }
+
+ // Add the whole-part grouping separators
+
+ if (wholePartGroupingPositions != null) {
+ if (wholePartGroupingPositions.length == 1) {
+ // grouping separators are at regular positions
+ int g = wholePartGroupingPositions[0];
+ int p = point - g;
+ while (p > 0) {
+ ib = insert(ib, ibused++, dfs.getGroupingSeparator(), p);
+ //sb.insert(p, unicodeChar(dfs.groupingSeparator));
+ p -= g;
+ }
+ } else {
+ // grouping separators are at irregular positions
+ for (int wholePartGroupingPosition : wholePartGroupingPositions) {
+ int p = point - wholePartGroupingPosition;
+ if (p > 0) {
+ ib = insert(ib, ibused++, dfs.getGroupingSeparator(), p);
+ //sb.insert(p, unicodeChar(dfs.groupingSeparator));
+ }
+ }
+ }
+ }
+
+ // Add the fractional-part grouping separators
+
+ if (fractionalPartGroupingPositions != null) {
+ // grouping separators are at irregular positions.
+ for (int i=0; i<fractionalPartGroupingPositions.length; i++) {
+ int p = point + 1 + fractionalPartGroupingPositions[i] + i;
+ if (p < ibused-1) {
+ ib = insert(ib, ibused++, dfs.getGroupingSeparator(), p);
+ //sb.insert(p, dfs.groupingSeparator);
+ } else {
+ break;
+ }
+ }
+ }
+
+ // System.err.println("Grouped number: " + sb.toString());
+
+ //sb.insert(0, prefix);
+ //sb.insert(0, minusSign);
+ //sb.append(suffix);
+ FastStringBuffer res = new FastStringBuffer(prefix.length() + minusSign.length() + suffix.length() + ibused);
+ res.append(minusSign);
+ res.append(prefix);
+ for (int i=0; i<ibused; i++) {
+ res.appendWideChar(ib[i]);
+ }
+ res.append(suffix);
+ return res;
+ }
+
+
+ /**
+ * Format a number supplied as a decimal
+ * @param dval the decimal value
+ * @param fsb the FastStringBuffer to contain the result
+ */
+ private void formatDecimal(BigDecimal dval, FastStringBuffer fsb) {
+ dval = dval.setScale(maxFractionPartSize, BigDecimal.ROUND_HALF_EVEN);
+ DecimalValue.decimalToString(dval, fsb);
+
+ int point = fsb.indexOf('.');
+ int intDigits;
+ if (point >= 0) {
+ int zz = maxFractionPartSize - minFractionPartSize;
+ while (zz>0) {
+ if (fsb.charAt(fsb.length()-1) == '0') {
+ fsb.setLength(fsb.length()-1);
+ zz--;
+ } else {
+ break;
+ }
+ }
+ intDigits = point;
+ if (fsb.charAt(fsb.length()-1) == '.') {
+ fsb.setLength(fsb.length()-1);
+ }
+ } else {
+ intDigits = fsb.length();
+ if (minFractionPartSize > 0) {
+ fsb.append('.');
+ for (int i=0; i<minFractionPartSize; i++) {
+ fsb.append('0');
+ }
+ }
+ }
+ if (minWholePartSize == 0 && intDigits == 1 && fsb.charAt(0) == '0') {
+ fsb.removeCharAt(0);
+ } else {
+ fsb.prependRepeated('0', minWholePartSize - intDigits);
+ }
+ }
+
+ /**
+ * Format a number supplied as a integer
+ * @param value the integer value
+ * @param fsb the FastStringBuffer to contain the result
+ */
+
+ private void formatInteger(NumericValue value, FastStringBuffer fsb) {
+ fsb.append(value.getStringValueCS());
+ int leadingZeroes = minWholePartSize - fsb.length();
+ fsb.prependRepeated('0', leadingZeroes);
+ if (minFractionPartSize != 0) {
+ fsb.append('.');
+ for (int i=0; i < minFractionPartSize; i++) {
+ fsb.append('0');
+ }
+ }
+ }
+
+
+ }
+
+ /**
+ * Convert a Unicode character (possibly >65536) to a String, using a surrogate pair if necessary
+ * @param ch the Unicode codepoint value
+ * @return a string representing the Unicode codepoint, either a string of one character or a surrogate pair
+ */
+
+ private static CharSequence unicodeChar(int ch) {
+ if (ch<65536) {
+ return "" + (char)ch;
+ }
+ else { // output a surrogate pair
+ //To compute the numeric value of the character corresponding to a surrogate
+ //pair, use this formula (all numbers are hex):
+ //(FirstChar - D800) * 400 + (SecondChar - DC00) + 10000
+ ch -= 65536;
+ char[] sb = new char[2];
+ sb[0] = ((char)((ch / 1024) + 55296));
+ sb[1] = ((char)((ch % 1024) + 56320));
+ return new CharSlice(sb, 0, 2);
+ }
+ }
+
+ /**
+ * Insert an integer into an array of integers. This may or may not modify the supplied array.
+ * @param array the initial array
+ * @param used the number of items in the initial array that are used
+ * @param value the integer to be inserted
+ * @param position the position of the new integer in the final array
+ * @return the new array, with the new integer inserted
+ */
+
+ private static int[] insert(int[] array, int used, int value, int position) {
+ if (used+1 > array.length) {
+ int[] a2 = new int[used+10];
+ System.arraycopy(array, 0, a2, 0, used);
+ array = a2;
+ }
+ System.arraycopy(array, position, array, position + 1, used - position);
+ array[position] = value;
+ return array;
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ int numArgs = arguments.length;
+ DecimalSymbols dfs = decimalFormatSymbols;
+
+ AtomicValue av0 = (AtomicValue)arguments[0].head();
+ if (av0 == null) {
+ av0 = DoubleValue.NaN;
+ }
+ NumericValue number = (NumericValue)av0;
+
+ if (dfs == null) {
+ DecimalFormatManager dfm = decimalFormatManager;
+ if (numArgs==2) {
+ dfs = dfm.getDefaultDecimalFormat();
+ } else {
+ // the decimal-format name was given as a run-time expression
+ Item arg2 = arguments[2].head();
+ if (arg2 == null) {
+ dfs = decimalFormatManager.getDefaultDecimalFormat();
+ } else {
+ String lexicalName = arguments[2].head().getStringValue();
+ StructuredQName qName;
+ try {
+ boolean is30 = context.getController().getExecutable().isAllowXPath30();
+ qName = StructuredQName.fromLexicalQName(lexicalName, false,
+ is30, context.getConfiguration().getNameChecker(), nsContext);
+ } catch (XPathException e) {
+ XPathException err = new XPathException("Invalid decimal format name. " + e.getMessage());
+ err.setErrorCode("FODF1280");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+
+ dfs = dfm.getNamedDecimalFormat(qName);
+ if (dfs==null) {
+ XPathException err = new XPathException("format-number function: decimal-format '" + lexicalName + "' is not defined");
+ err.setErrorCode("FODF1280");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ }
+ }
+ }
+ SubPicture[] pics = subPictures;
+ if (pics == null) {
+ String format = arguments[1].head().getStringValue();
+ pics = getSubPictures(format, dfs);
+ }
+ return new StringValue(formatNumber(number, pics, dfs));
+ }
+
+ private static boolean isInDigitFamily(int ch, int zeroDigit) {
+ return (ch >= zeroDigit && ch <= zeroDigit + 10);
+ }
+}
+
diff --git a/sf/saxon/functions/FunctionAvailable.java b/sf/saxon/functions/FunctionAvailable.java
new file mode 100644
index 0000000..dcbec21
--- /dev/null
+++ b/sf/saxon/functions/FunctionAvailable.java
@@ -0,0 +1,165 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.QNameException;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.NumericValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+* This class supports the XSLT element-available and function-available functions.
+*/
+
+public class FunctionAvailable extends Available implements Callable {
+
+ // For the rare case of dynamic evaluation of function-available(), we retain the static context
+ private StaticContext savedStaticContext = null;
+
+ /**
+ * Bind aspects of the static context on which the particular function depends
+ *
+ * @param env the static context of the function call
+ * @throws net.sf.saxon.trans.XPathException
+ * if execution with this static context will inevitably fail
+ */
+ @Override
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ this.savedStaticContext = env;
+ }
+
+ /**
+ * preEvaluate: this method uses the static context to do early evaluation of the function
+ * if the argument is known (which is the normal case)
+ * @param visitor the expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ String lexicalQName = ((Literal)argument[0]).getValue().getStringValue();
+ StaticContext env = visitor.getStaticContext();
+ savedStaticContext = null;
+ boolean b = false;
+ Configuration config = visitor.getConfiguration();
+
+ int minArity = 0;
+ int maxArity = 20;
+ if (argument.length == 2) {
+ minArity = (int)((NumericValue)argument[1].evaluateItem(env.makeEarlyEvaluationContext())).longValue();
+ maxArity = minArity;
+ }
+ try {
+ String[] parts = config.getNameChecker().getQNameParts(lexicalQName);
+ String prefix = parts[0];
+ String uri;
+ if (prefix.length() == 0) {
+ uri = env.getDefaultFunctionNamespace();
+ } else {
+ uri = env.getURIForPrefix(prefix);
+ }
+ StructuredQName functionName = new StructuredQName(prefix, uri, parts[1]);
+ for (int i=minArity; i<=maxArity; i++) {
+ if (env.getFunctionLibrary().isAvailable(functionName, i)) {
+ b = true;
+ break;
+ }
+ }
+ } catch (QNameException e) {
+ XPathException err = new XPathException(e.getMessage());
+ err.setErrorCode("XTDE1400");
+ throw err;
+ } catch (XPathException e2) {
+ if ("XTDE0290".equals(e2.getErrorCodeLocalPart())) {
+ e2.setErrorCode("XTDE1400");
+ }
+ throw e2;
+ }
+
+ return Literal.makeLiteral(BooleanValue.get(b));
+ }
+
+ /**
+ * Run-time evaluation. This is the only thing in the spec that requires information
+ * about in-scope functions to be available at run-time. However, we keep it because
+ * it's handy for some other things such as saxon:evaluate().
+ */
+
+ public BooleanValue evaluateItem(/*@NotNull*/ XPathContext context) throws XPathException {
+ AtomicValue av1 = (AtomicValue)argument[0].evaluateItem(context);
+ long arity = -1;
+ if (argument.length == 2) {
+ arity = ((NumericValue)argument[1].evaluateItem(context)).longValue();
+ }
+ StringValue nameValue = (StringValue)av1;
+ String lexicalName = nameValue.getStringValue();
+ boolean b = isFunctionAvailable(lexicalName, (int)arity, context);
+
+ return BooleanValue.get(b);
+
+ }
+
+ private boolean isFunctionAvailable(String lexicalName, int arity, XPathContext context) throws XPathException {
+ if (arity == -1) {
+ for (int i=0; i<20; i++) {
+ if (isFunctionAvailable(lexicalName, i, context)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ StructuredQName qName;
+ try {
+ if (lexicalName.indexOf(':') < 0) {
+ // we're in XSLT, where the default namespace for functions can't be changed
+ String uri = NamespaceConstant.FN;
+ qName = new StructuredQName("", uri, lexicalName);
+ } else {
+ boolean is30 = context.getController().getExecutable().isAllowXPath30();
+ qName = StructuredQName.fromLexicalQName(lexicalName,
+ false, is30, context.getConfiguration().getNameChecker(),
+ nsContext);
+ }
+ } catch (XPathException e) {
+ e.setErrorCode("XTDE1400");
+ e.setLocator(this);
+ e.setXPathContext(context);
+ throw e;
+ }
+
+ final FunctionLibrary lib = context.getController().getExecutable().getFunctionLibrary();
+ return lib.isAvailable(qName, arity);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ String lexicalQName = arguments[0].head().getStringValue();
+ int arity = -1;
+ if (arguments.length == 2) {
+ arity = (int)((NumericValue)arguments[1].head()).longValue();
+ }
+ return BooleanValue.get(isFunctionAvailable(lexicalQName, arity, context));
+ }
+}
+
diff --git a/sf/saxon/functions/FunctionLibrary.java b/sf/saxon/functions/FunctionLibrary.java
new file mode 100644
index 0000000..2803fcf
--- /dev/null
+++ b/sf/saxon/functions/FunctionLibrary.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Serializable;
+
+/**
+ * A FunctionLibrary handles the binding of function calls in XPath (or XQuery) expressions.
+ * There are a number of implementations of this
+ * class to handle different kinds of function: system functions, constructor functions, vendor-defined
+ * functions, Java extension functions, stylesheet functions, and so on. There is also an implementation
+ * {@link net.sf.saxon.functions.FunctionLibraryList} that allows a FunctionLibrary
+ * to be constructed by combining other FunctionLibrary objects.
+ */
+
+public interface FunctionLibrary extends Serializable {
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+
+ /*@Nullable*/
+ public boolean isAvailable(StructuredQName functionName, int arity);
+
+
+ /**
+ * Bind a function, given the URI and local parts of the function name,
+ * and the list of expressions supplied as arguments. This method is called at compile
+ * time.
+ *
+ *
+ * @param functionName the QName of the function being called
+ * @param arity The number of arguments supplied in the function call
+ * @param staticArgs May be null; if present, the length of the array must match the
+ * value of arity. Contains the expressions supplied statically in arguments to the function call.
+ * The intention is
+ * that the static type of the arguments (obtainable via getItemType() and getCardinality()) may
+ * be used as part of the binding algorithm. In some cases it may be possible for the function
+ * to be pre-evaluated at compile time, for example if these expressions are all constant values.
+ * <p>
+ * The conventions of the XPath language demand that the results of a function depend only on the
+ * values of the expressions supplied as arguments, and not on the form of those expressions. For
+ * example, the result of f(4) is expected to be the same as f(2+2). The actual expression is supplied
+ * here to enable the binding mechanism to select the most efficient possible implementation (including
+ * compile-time pre-evaluation where appropriate).
+ * @param env The static context of the function call
+ * @param container The container for the newly created Expression
+ * @return An expression equivalent to a call on the specified function, if one is found;
+ * null if no function was found matching the required name and arity.
+ * @throws net.sf.saxon.trans.XPathException if a function is found with the required name and arity, but
+ * the implementation of the function cannot be loaded or used; or if an error occurs
+ * while searching for the function.
+ */
+
+ /*@Nullable*/ public Expression bind(StructuredQName functionName, int arity, Expression[] staticArgs, StaticContext env, Container container)
+ throws XPathException;
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ public FunctionLibrary copy();
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ *
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ * @throws XPathException in the event of certain errors, for example attempting to get a function
+ * that is private
+ */
+
+ /*@Nullable*/
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext)
+ throws XPathException;
+//#endif
+
+
+}
\ No newline at end of file
diff --git a/sf/saxon/functions/FunctionLibraryList.java b/sf/saxon/functions/FunctionLibraryList.java
new file mode 100644
index 0000000..88a81dd
--- /dev/null
+++ b/sf/saxon/functions/FunctionLibraryList.java
@@ -0,0 +1,191 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.query.XQueryFunction;
+import net.sf.saxon.query.XQueryFunctionBinder;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A FunctionLibraryList is a list of FunctionLibraries. It is also a FunctionLibrary in its own right.
+ * When required, it searches the list of FunctionLibraries to find the required function.
+ */
+public class FunctionLibraryList implements FunctionLibrary, XQueryFunctionBinder {
+
+ public List<FunctionLibrary> libraryList = new ArrayList<FunctionLibrary>(8);
+
+ public FunctionLibraryList() {}
+
+ /**
+ * Add a new FunctionLibrary to the list of FunctionLibraries in this FunctionLibraryList. Note
+ * that libraries are searched in the order they are added to the list.
+ * @param lib A function library to be added to the list of function libraries to be searched.
+ * @return the position of the library in the list
+ */
+
+ public int addFunctionLibrary(FunctionLibrary lib) {
+ libraryList.add(lib);
+ return libraryList.size() - 1;
+ }
+
+ /**
+ * Get the n'th function library in the list
+ */
+
+ public FunctionLibrary get(int n) {
+ return libraryList.get(n);
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ * <p/>
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext) throws XPathException {
+ for (FunctionLibrary lib : libraryList) {
+ FunctionItem fi = lib.getFunctionItem(functionName, arity, staticContext);
+ if (fi != null) {
+ return fi;
+ }
+ }
+ return null;
+ }
+//#endif
+
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ for (FunctionLibrary lib : libraryList) {
+ if (lib.isAvailable(functionName, arity)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Bind an extension function, given the URI and local parts of the function name,
+ * and the list of expressions supplied as arguments. This method is called at compile
+ * time.
+ *
+ *
+ * @param functionName
+ * @param arity
+ * @param staticArgs The expressions supplied statically in arguments to the function call.
+ * The length of this array represents the arity of the function. The intention is
+ * that the static type of the arguments (obtainable via getItemType() and getCardinality() may
+ * be used as part of the binding algorithm. In some cases it may be possible for the function
+ * to be pre-evaluated at compile time, for example if these expressions are all constant values.
+ * @param env
+ * @param container
+ * @return An object representing the extension function to be called, if one is found;
+ * null if no extension function was found matching the required name and arity.
+ * @throws net.sf.saxon.trans.XPathException if a function is found with the required name and arity, but
+ * the implementation of the function cannot be loaded or used; or if an error occurs
+ * while searching for the function.
+ */
+
+ public Expression bind(StructuredQName functionName, int arity, Expression[] staticArgs, StaticContext env, Container container)
+ throws XPathException {
+ boolean debug = env.getConfiguration().getBooleanProperty(FeatureKeys.TRACE_EXTERNAL_FUNCTIONS);
+ PrintStream err = env.getConfiguration().getStandardErrorOutput();
+ if (debug) {
+ err.println("Looking for function " + functionName.getClarkName());
+ }
+ for (FunctionLibrary lib : libraryList) {
+ if (debug) {
+ err.println("Trying " + lib.getClass().getName());
+ }
+ Expression func = lib.bind(functionName, arity, staticArgs, env, container);
+ if (func != null) {
+ return func;
+ }
+ }
+ if (debug) {
+ err.println("Function " + functionName.getClarkName() + " not found!");
+ }
+ return null;
+ }
+
+ /**
+ * Get the function declaration corresponding to a given function name and arity
+ *
+ * @return the XQueryFunction if there is one, or null if not.
+ */
+
+ public XQueryFunction getDeclaration(StructuredQName functionName, Expression[] staticArgs) {
+ for (FunctionLibrary lib : libraryList) {
+ if (lib instanceof XQueryFunctionBinder) {
+ XQueryFunction func = ((XQueryFunctionBinder) lib).getDeclaration(functionName, staticArgs);
+ if (func != null) {
+ return func;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the list of contained FunctionLibraries. This method allows the caller to modify
+ * the library list, for example by adding a new FunctionLibrary at a chosen position,
+ * by removing a library from the list, or by changing the order of libraries in the list.
+ * Note that such changes may violate rules in the
+ * language specifications, or assumptions made within the product.
+ * @return a list whose members are of class FunctionLibrary
+ */
+
+ public List<FunctionLibrary> getLibraryList() {
+ return libraryList;
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ *
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ public FunctionLibrary copy() {
+ FunctionLibraryList fll = new FunctionLibraryList();
+ fll.libraryList = new ArrayList<FunctionLibrary>(libraryList.size());
+ for (int i=0; i<libraryList.size(); i++) {
+ fll.libraryList.add(libraryList.get(i).copy());
+ }
+ return fll;
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/functions/GenerateId.java b/sf/saxon/functions/GenerateId.java
new file mode 100644
index 0000000..87094eb
--- /dev/null
+++ b/sf/saxon/functions/GenerateId.java
@@ -0,0 +1,98 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.GenerateIdCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.StringValue;
+
+/**
+* This class supports the generate-id() function
+*/
+
+public class GenerateId extends SystemFunctionCall {
+
+ /**
+ * Simplify and validate.
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ return simplifyArguments(visitor);
+ }
+
+
+ /**
+ * Determine the special properties of this expression. The generate-id()
+ * function is a special case: it is considered creative if its operand
+ * is creative, so that generate-id(f()) is not taken out of a loop
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ return p & ~StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Evaluate the function in a string context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo node = (NodeInfo)argument[0].evaluateItem(c);
+ return generateId(node);
+ }
+
+ public static StringValue generateId(NodeInfo node) {
+ if (node==null) {
+ return StringValue.EMPTY_STRING;
+ }
+
+ FastStringBuffer buffer = new FastStringBuffer(FastStringBuffer.TINY);
+ node.generateId(buffer);
+ buffer.condense();
+ return new StringValue(buffer);
+
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo node = (arguments.length == 0 ? getContextNode(context) : (NodeInfo)arguments[0].head());
+ return generateId(node);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the GenerateId expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new GenerateIdCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/Id.java b/sf/saxon/functions/Id.java
new file mode 100644
index 0000000..769582a
--- /dev/null
+++ b/sf/saxon/functions/Id.java
@@ -0,0 +1,283 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.sort.DocumentOrderIterator;
+import net.sf.saxon.expr.sort.LocalOrderComparer;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+
+/**
+* The XPath id() or element-with-id() function
+* XPath 2.0 version: accepts any sequence as the first parameter; each item in the sequence
+* is taken as an IDREFS value, that is, a space-separated list of ID values.
+ * Also accepts an optional second argument to identify the target document, this
+ * defaults to the context node.
+*/
+
+
+public class Id extends SystemFunctionCall implements Callable {
+
+ public static final int ID = 0;
+ public static final int ELEMENT_WITH_ID = 1;
+
+ private boolean isSingletonId = false;
+
+ /**
+ * Simplify: add a second implicit argument, the context document
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ Id id = (Id)super.simplify(visitor);
+ if (argument.length == 1) {
+ id.addContextDocumentArgument(1, getFunctionName().getLocalPart());
+ }
+ return id;
+ }
+
+ /**
+ * Type-check the expression. This also calls preEvaluate() to evaluate the function
+ * if all the arguments are constant; functions that do not require this behavior
+ * can override the preEvaluate method.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (argument[1] instanceof RootExpression &&
+ contextItemType != null && contextItemType.itemType != null &&
+ contextItemType.itemType.isPlainType()) {
+ // intercept this to get better diagnostics
+ XPathException err = new XPathException(getFunctionName().getLocalPart() +
+ "() function called when the context item is not a node");
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ throw err;
+ }
+ return super.typeCheck(visitor, contextItemType);
+ }
+
+ /**
+ * Static analysis: prevent sorting of the argument
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ argument[0] = ExpressionTool.unsorted(opt, argument[0], false);
+ isSingletonId = !Cardinality.allowsMany(argument[0].getCardinality());
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ int prop = StaticProperty.ORDERED_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET |
+ StaticProperty.NON_CREATIVE;
+ if ((getNumberOfArguments() == 1) ||
+ (argument[1].getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
+ prop |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
+ }
+ return prop;
+ }
+
+
+ /**
+ * Add a representation of a doc() call or similar function to a PathMap.
+ * This is a convenience method called by the addToPathMap() methods for doc(), document(), collection()
+ * and similar functions. These all create a new root expression in the path map.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ argument[0].addToPathMap(pathMap, pathMapNodeSet);
+ PathMap.PathMapNodeSet target = argument[1].addToPathMap(pathMap, pathMapNodeSet);
+ // indicate that the function navigates to all elements in the document
+ target = target.createArc(AxisInfo.DESCENDANT, NodeKindTest.ELEMENT);
+ return target;
+ }
+
+ /**
+ * Evaluate the function to return an iteration of selected nodes.
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+
+ NodeInfo arg1;
+ try {
+ arg1 = (NodeInfo)argument[1].evaluateItem(context);
+ } catch (XPathException e) {
+ if (context.getContextItem() instanceof AtomicValue) {
+ // Override the unhelpful message that trickles down...
+ XPathException e2 = new XPathException("id() function called when the context item is not a node");
+ e2.setErrorCode("XPTY0004");
+ e2.setXPathContext(context);
+ e2.setLocator(this);
+ throw e2;
+ } else {
+ throw e;
+ }
+ }
+ arg1 = arg1.getRoot();
+ if (arg1.getNodeKind() != Type.DOCUMENT) {
+ dynamicError("In the " + getFunctionName().getLocalPart() + "() function," +
+ " the tree being searched must be one whose root is a document node", "FODC0001", context);
+ return null;
+ }
+ DocumentInfo doc = (DocumentInfo)arg1;
+
+ if (isSingletonId) {
+ AtomicValue arg = (AtomicValue)argument[0].evaluateItem(context);
+ if (arg==null) {
+ return EmptyIterator.getInstance();
+ }
+ String idrefs = arg.getStringValue();
+ return getIdSingle(doc, idrefs, operation);
+ } else {
+ SequenceIterator idrefs = argument[0].iterate(context);
+ return getIdMultiple(doc, idrefs, operation);
+ }
+ }
+
+ /**
+ * Get an iterator over the nodes that have an id equal to one of the values is a whitespace separated
+ * string
+ * @param doc The document to be searched
+ * @param idrefs a string containing zero or more whitespace-separated ID values to be found in the document
+ * @param operation either {@link #ID} or {@link #ELEMENT_WITH_ID}
+ * @return an iterator over the nodes whose ID is one of the specified values
+ * @throws XPathException
+ */
+
+ public static SequenceIterator getIdSingle(DocumentInfo doc, String idrefs, int operation) throws XPathException {
+ boolean white = false;
+ for (int i=idrefs.length()-1; i>=0; i--) {
+ char c = idrefs.charAt(i);
+ if (c <= 0x20 && (c == 0x20 || c == 0x09 || c == 0x0a || c == 0x0d)) {
+ white = true;
+ break;
+ }
+ }
+
+ if (white) {
+ StringTokenIterator tokens = new StringTokenIterator(idrefs);
+ IdMappingFunction map = new IdMappingFunction();
+ map.document = doc;
+ map.operation = operation;
+ SequenceIterator result = new MappingIterator(tokens, map);
+ return new DocumentOrderIterator(result, LocalOrderComparer.getInstance());
+ } else {
+ return SingletonIterator.makeIterator(doc.selectID(idrefs, operation == ELEMENT_WITH_ID));
+ }
+ }
+
+ /**
+ * Get an iterator over the nodes that have an id equal to one of the values is a set of whitespace separated
+ * strings
+ * @param doc The document to be searched
+ * @param idrefs an iterator over a set of strings each of which is a string containing
+ * zero or more whitespace-separated ID values to be found in the document
+ * @return an iterator over the nodes whose ID is one of the specified values
+ * @throws XPathException
+ */
+
+ public static SequenceIterator getIdMultiple(DocumentInfo doc, SequenceIterator idrefs, int operation) throws XPathException {
+ IdMappingFunction map = new IdMappingFunction();
+ map.document = doc;
+ map.operation = operation;
+ SequenceIterator result = new MappingIterator(idrefs, map);
+ return new DocumentOrderIterator(result, LocalOrderComparer.getInstance());
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo start = (arguments.length == 1 ? getContextNode(context) : (NodeInfo)arguments[1].head());
+ NodeInfo arg1 = start.getRoot();
+ if (arg1.getNodeKind() != Type.DOCUMENT) {
+ dynamicError("In the " + getFunctionName().getLocalPart() + "() function," +
+ " the tree being searched must be one whose root is a document node", "FODC0001", context);
+ return null;
+ }
+ DocumentInfo doc = (DocumentInfo)arg1;
+ SequenceIterator idrefs = arguments[0].iterate();
+ return SequenceTool.toLazySequence(getIdMultiple(doc, idrefs, operation));
+ }
+
+ private static class IdMappingFunction implements MappingFunction<StringValue, NodeInfo> {
+
+ public DocumentInfo document;
+ private int operation;
+
+ /**
+ * Evaluate the function for a single string value
+ * (implements the MappingFunction interface)
+ */
+
+ public SequenceIterator<NodeInfo> map(StringValue item) throws XPathException {
+
+ String idrefs = Whitespace.trim(item.getStringValueCS());
+
+ // If this value contains a space, we need to break it up into its
+ // separate tokens; if not, we can process it directly
+
+ if (Whitespace.containsWhitespace(idrefs)) {
+ StringTokenIterator tokens = new StringTokenIterator(idrefs);
+ IdMappingFunction submap = new IdMappingFunction();
+ submap.document = document;
+ submap.operation = operation;
+ return new MappingIterator(tokens, submap);
+
+ } else {
+ return SingletonIterator.makeIterator(document.selectID(idrefs, operation == ELEMENT_WITH_ID));
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/functions/Idref.java b/sf/saxon/functions/Idref.java
new file mode 100644
index 0000000..a1bdd7c
--- /dev/null
+++ b/sf/saxon/functions/Idref.java
@@ -0,0 +1,228 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.sort.DocumentOrderIterator;
+import net.sf.saxon.expr.sort.LocalOrderComparer;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.trans.KeyDefinitionSet;
+import net.sf.saxon.trans.KeyManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.StringValue;
+
+
+public class Idref extends SystemFunctionCall implements Callable {
+
+ private KeyDefinitionSet idRefKey;
+
+ /**
+ * Simplify: add a second implicit argument, the context document
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ Idref f = (Idref)super.simplify(visitor);
+ f.addContextDocumentArgument(1, "idref");
+ return f;
+ }
+
+
+ /**
+ * Type-check the expression. This also calls preEvaluate() to evaluate the function
+ * if all the arguments are constant; functions that do not require this behavior
+ * can override the preEvaluate method.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.typeCheck(visitor, contextItemType);
+ idRefKey = visitor.getExecutable().getKeyManager().getKeyDefinitionSet(
+ StandardNames.getStructuredQName(StandardNames.XS_IDREFS));
+ return e;
+ }
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ argument[0] = ExpressionTool.unsorted(opt, argument[0], false);
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ int prop = StaticProperty.ORDERED_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET |
+ StaticProperty.NON_CREATIVE;
+ if ((getNumberOfArguments() == 1) ||
+ (argument[1].getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
+ prop |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
+ }
+ return prop;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Idref i2 = (Idref)super.copy();
+ i2.idRefKey = idRefKey;
+ return i2;
+ }
+
+ /**
+ * Add a representation of a doc() call or similar function to a PathMap.
+ * This is a convenience method called by the addToPathMap() methods for doc(), document(), collection()
+ * and similar functions. These all create a new root expression in the path map.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ argument[0].addToPathMap(pathMap, pathMapNodeSet);
+ PathMap.PathMapNodeSet target = argument[1].addToPathMap(pathMap, pathMapNodeSet);
+ // indicate that the function navigates to all nodes in the document
+ target = target.createArc(AxisInfo.DESCENDANT, AnyNodeTest.getInstance());
+ return target;
+ }
+
+
+ /**
+ * Enumerate the results of the expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<NodeInfo> iterate(XPathContext context) throws XPathException {
+
+ Controller controller = context.getController();
+ assert controller != null;
+
+ NodeInfo arg2 = (NodeInfo)argument[1].evaluateItem(context);
+ assert arg2 != null;
+ arg2 = arg2.getRoot();
+ if (arg2.getNodeKind() != Type.DOCUMENT) {
+ dynamicError("In the idref() function," +
+ " the tree being searched must be one whose root is a document node", "FODC0001", context);
+ return EmptyIterator.emptyIterator();
+ }
+ DocumentInfo doc = (DocumentInfo)arg2;
+
+ // If the argument is a singleton, we evaluate the function
+ // directly; otherwise we recurse to evaluate it once for each Item
+ // in the sequence.
+
+ Expression expression = argument[0];
+ if (Cardinality.allowsMany(expression.getCardinality())) {
+ SequenceIterator keys = argument[0].iterate(context);
+ return getIdrefMultiple(doc, keys, context);
+
+ } else {
+ AtomicValue keyValue = (AtomicValue)argument[0].evaluateItem(context);
+ if (keyValue == null) {
+ return EmptyIterator.emptyIterator();
+ }
+ KeyManager keyManager = controller.getKeyManager();
+ return keyManager.selectByKey(idRefKey, doc, keyValue, context);
+
+ }
+ }
+
+ /**
+ * Get the result when multiple idref values are supplied. Note this is also called from
+ * compiled XQuery code.
+ * @param doc the document to be searched
+ * @param keys the idref values supplied
+ * @param context the dynamic execution context
+ * @return iterator over the result of the function
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public static SequenceIterator<NodeInfo> getIdrefMultiple(DocumentInfo doc, SequenceIterator keys, XPathContext context)
+ throws XPathException {
+ IdrefMappingFunction map = new IdrefMappingFunction();
+ map.document = doc;
+ map.keyContext = context;
+ map.keyManager = context.getController().getKeyManager();
+ map.keySet = map.keyManager.getKeyDefinitionSet(StandardNames.getStructuredQName(StandardNames.XS_IDREFS));
+ SequenceIterator allValues = new MappingIterator(keys, map);
+ return new DocumentOrderIterator(allValues, LocalOrderComparer.getInstance());
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo start = (arguments.length == 1 ? getContextNode(context) : (NodeInfo)arguments[1].head());
+ NodeInfo arg2 = start.getRoot();
+ if (arg2.getNodeKind() != Type.DOCUMENT) {
+ dynamicError("In the idref() function," +
+ " the tree being searched must be one whose root is a document node", "FODC0001", context);
+ return null;
+ }
+ DocumentInfo doc = (DocumentInfo)arg2;
+ return SequenceTool.toLazySequence(getIdrefMultiple(doc, arguments[0].iterate(), context));
+ }
+
+ private static class IdrefMappingFunction implements MappingFunction<StringValue, NodeInfo> {
+ public DocumentInfo document;
+ public XPathContext keyContext;
+ public KeyManager keyManager;
+ public KeyDefinitionSet keySet;
+
+ /**
+ * Implement the MappingFunction interface
+ */
+
+ public SequenceIterator<NodeInfo> map(StringValue item) throws XPathException {
+ KeyManager keyManager = keyContext.getController().getKeyManager();
+ return keyManager.selectByKey(keySet, document, item, keyContext);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/functions/InScopePrefixes.java b/sf/saxon/functions/InScopePrefixes.java
new file mode 100644
index 0000000..72c6d79
--- /dev/null
+++ b/sf/saxon/functions/InScopePrefixes.java
@@ -0,0 +1,96 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.NamespaceIterator;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.value.StringValue;
+
+import java.util.Iterator;
+
+/**
+* This class supports fuctions get-in-scope-prefixes()
+*/
+
+public class InScopePrefixes extends SystemFunctionCall implements Callable {
+
+ /**
+ * Iterator over the results of the expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(final XPathContext context) throws XPathException {
+ final NodeInfo element = (NodeInfo)argument[0].evaluateItem(context);
+ final Iterator<NamespaceBinding> iter = NamespaceIterator.iterateNamespaces(element);
+
+ return sequenceIterator(iter, context);
+
+ }
+
+ /*@Nullable*/ public SequenceIterator sequenceIterator(final Iterator<NamespaceBinding> iter, final XPathContext context){
+ return new SequenceIterator() {
+ private Item current = null;
+ private int position = 0;
+
+ public Item current() {
+ return current;
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return iterate(context);
+ }
+
+ public int getProperties() {
+ return 0;
+ }
+
+ public Item next() throws XPathException {
+ if (position == 0) {
+ current = new StringValue("xml");
+ position++;
+ return current;
+ } else if (iter.hasNext()) {
+ String prefix = iter.next().getPrefix();
+ if (prefix.length() == 0) {
+ current = StringValue.EMPTY_STRING;
+ } else {
+ current = new StringValue(prefix, BuiltInAtomicType.NCNAME);
+ }
+ position++;
+ return current;
+ } else {
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+ };
+
+ }
+
+ public Sequence call(final XPathContext context, Sequence[] arguments) throws XPathException {
+ final NodeInfo element = (NodeInfo)arguments[0].head();
+ final Iterator<NamespaceBinding> iter = NamespaceIterator.iterateNamespaces(element);
+
+ return SequenceTool.toLazySequence(sequenceIterator(iter, context));
+ }
+
+}
+
diff --git a/sf/saxon/functions/IndexOf.java b/sf/saxon/functions/IndexOf.java
new file mode 100644
index 0000000..69c0a8c
--- /dev/null
+++ b/sf/saxon/functions/IndexOf.java
@@ -0,0 +1,193 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+
+
+/**
+* The XPath 2.0 index-of() function
+*/
+
+
+public class IndexOf extends CollatingFunction implements Callable {
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 2;
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return new IntegerValue[]{Int64Value.PLUS_ONE, MAX_SEQUENCE_LENGTH};
+ }
+
+ @Override
+ public void checkArguments(ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType type0 = argument[0].getItemType(th);
+ ItemType type1 = argument[1].getItemType(th);
+ if (type0 instanceof AtomicType && type1 instanceof AtomicType) {
+ preAllocateComparer((AtomicType)type0, (AtomicType)type1, visitor.getStaticContext(), false);
+ }
+ }
+
+ /**
+ * Evaluate the function to return an iteration of selected nodes.
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ AtomicComparer comparer = getPreAllocatedAtomicComparer();
+ if (comparer == null) {
+ comparer = getAtomicComparer(getCollator(context), context);
+ }
+ SequenceIterator seq = argument[0].iterate(context);
+ AtomicValue val = (AtomicValue)argument[1].evaluateItem(context);
+ return new IndexIterator(seq, val, comparer);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringCollator collator = getCollatorFromLastArgument(arguments, 2, context);
+ GenericAtomicComparer comparer = new GenericAtomicComparer(collator, context);
+ SequenceIterator seq = arguments[0].iterate();
+ AtomicValue val = (AtomicValue)arguments[1].head();
+ return SequenceTool.toLazySequence(new IndexIterator(seq, val, comparer));
+ }
+
+ /**
+ * Iterator to return the index positions of selected items in a sequence
+ */
+
+ public static class IndexIterator implements SequenceIterator {
+
+ private SequenceIterator base;
+ private AtomicValue value;
+ private AtomicComparer comparer;
+ private int index = 0;
+ private int position = 0;
+ /*@Nullable*/ private Item current = null;
+ private BuiltInAtomicType primitiveTypeRequired;
+
+ /**
+ * Get an iterator returning the index positions of selected items in a sequence
+ * @param base The sequence to be searched
+ * @param value The value being sought
+ * @param comparer Comparer used to determine whether values match
+ */
+
+ public IndexIterator(SequenceIterator base, AtomicValue value, AtomicComparer comparer) {
+ this.base = base;
+ this.value = value;
+ this.comparer = comparer;
+ primitiveTypeRequired = value.getPrimitiveType();
+ }
+
+ public Item next() throws XPathException {
+ while (true) {
+ AtomicValue i = (AtomicValue)base.next();
+ if (i==null) break;
+ index++;
+ if (Type.isGuaranteedComparable(primitiveTypeRequired,
+ i.getPrimitiveType(), false)) {
+ try {
+ if (comparer.comparesEqual(i, value)) {
+ current = Int64Value.makeIntegerValue(index);
+ position++;
+ return current;
+ }
+ } catch (ClassCastException err) {
+ // non-comparable values are treated as not equal
+ // Exception shouldn't happen but we catch it anyway
+ }
+ }
+ }
+ current = null;
+ position = -1;
+ return null;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new IndexIterator(base.getAnother(), value, comparer);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link SequenceIterator#GROUNDED}, {@link SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+ }
+
+
+
+}
+
diff --git a/sf/saxon/functions/Insert.java b/sf/saxon/functions/Insert.java
new file mode 100644
index 0000000..d6e5037
--- /dev/null
+++ b/sf/saxon/functions/Insert.java
@@ -0,0 +1,153 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.NumericValue;
+
+/**
+* The XPath 2.0 insert-before() function
+*/
+
+
+public class Insert extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate the function to return an iteration of selected nodes.
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ SequenceIterator seq = argument[0].iterate(context);
+ AtomicValue n0 = (AtomicValue)argument[1].evaluateItem(context);
+ NumericValue n = (NumericValue)n0;
+ int pos = (int)n.longValue();
+ SequenceIterator ins = argument[2].iterate(context);
+ return new InsertIterator(seq, ins, pos);
+ }
+
+ /**
+ * Evaluate the expression as a general function call
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NumericValue n = (NumericValue)arguments[1].head();
+ int pos = (int)n.longValue();
+ return SequenceTool.toLazySequence(
+ new InsertIterator(arguments[0].iterate(), arguments[2].iterate(), pos));
+ }
+
+ /**
+ * Insertion iterator. This is supplied with an iterator over the base sequence,
+ * an iterator over the sequence to be inserted, and the insert position.
+ */
+
+ public static class InsertIterator implements SequenceIterator {
+
+ private SequenceIterator base;
+ private SequenceIterator insert;
+ private int insertPosition;
+ private int position = 0;
+ /*@Nullable*/ private Item current = null;
+ private boolean inserting = false;
+
+ public InsertIterator(SequenceIterator base, SequenceIterator insert, int insertPosition) {
+ this.base = base;
+ this.insert = insert;
+ this.insertPosition = (insertPosition<1 ? 1 : insertPosition);
+ this.inserting = (insertPosition==1);
+ }
+
+
+ public Item next() throws XPathException {
+ Item nextItem;
+ if (inserting) {
+ nextItem = insert.next();
+ if (nextItem == null) {
+ inserting = false;
+ nextItem = base.next();
+ }
+ } else {
+ if (position == insertPosition-1) {
+ nextItem = insert.next();
+ if (nextItem == null) {
+ nextItem = base.next();
+ } else {
+ inserting = true;
+ }
+ } else {
+ nextItem = base.next();
+ if (nextItem==null && position < insertPosition-1) {
+ inserting = true;
+ nextItem = insert.next();
+ }
+ }
+ }
+ if (nextItem == null) {
+ current = null;
+ position = -1;
+ return null;
+ } else {
+ current = nextItem;
+ position++;
+ return current;
+ }
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ insert.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new InsertIterator( base.getAnother(),
+ insert.getAnother(),
+ insertPosition);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+ }
+
+}
+
diff --git a/sf/saxon/functions/IntegratedFunctionCall.java b/sf/saxon/functions/IntegratedFunctionCall.java
new file mode 100644
index 0000000..4cdf53f
--- /dev/null
+++ b/sf/saxon/functions/IntegratedFunctionCall.java
@@ -0,0 +1,336 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.lib.ExtensionFunctionCall;
+import net.sf.saxon.lib.ExtensionFunctionDefinition;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceType;
+
+/**
+ * Expression representing a call to a user-written extension
+ * function implemented as a subtype of {@link ExtensionFunctionCall}
+ */
+public class IntegratedFunctionCall extends FunctionCall implements Callable {
+
+ private ExtensionFunctionCall function;
+ private SequenceType resultType = SequenceType.ANY_SEQUENCE;
+ private int state = 0;
+
+ public IntegratedFunctionCall(ExtensionFunctionCall function) {
+ this.function = function;
+ }
+
+ /**
+ * Get the ExtensionFunctionCall object supplied by the application
+ *
+ * @return the ExtensionFunctionCall object
+ */
+
+ public ExtensionFunctionCall getFunction() {
+ return function;
+ }
+
+
+ /**
+ * Get the container in which this expression is located. This will usually be a top-level construct
+ * such as a function or global variable, and XSLT template, or an XQueryExpression. In the case of
+ * free-standing XPath expressions it will be the StaticContext object
+ *
+ * @return the expression's container
+ */
+
+ @Override
+ public Container getContainer() {
+ return function.getContainer();
+ }
+
+ /**
+ * Mark an expression as being in a given Container. This link is used primarily for diagnostics:
+ * the container links to the location map held in the executable.
+ * <p/>
+ * <p>This affects the expression and all its subexpressions. Any subexpressions that are not in the
+ * same container are marked with the new container, and this proceeds recursively. However, any
+ * subexpression that is already in the correct container is not modified.</p>
+ *
+ * @param container The container of this expression.
+ */
+ @Override
+ public void setContainer(Container container) {
+ super.setContainer(function.getContainer());
+ function.setDefinition(function.getDefinition(), container);
+ }
+
+ /**
+ * Method supplied by each class of function to check arguments during parsing, when all
+ * the argument expressions have been read. This implementation of the method checks the arguments
+ * of the supplied function call against the argument types declared as part of the extension function
+ * definition, and generates code to do the conversion if necessary.
+ *
+ * @param visitor the expression visitor
+ * @throws net.sf.saxon.trans.XPathException
+ * if the arguments are statically determined to be incompatible
+ * with the declared argument types of the function.
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ ExtensionFunctionDefinition definition = function.getDefinition();
+ checkArgumentCount(definition.getMinimumNumberOfArguments(), definition.getMaximumNumberOfArguments());
+ final int args = getNumberOfArguments();
+ SequenceType[] declaredArgumentTypes = definition.getArgumentTypes();
+ if (declaredArgumentTypes == null || (args != 0 && declaredArgumentTypes.length == 0)) {
+ throw new XPathException("Integrated function " + getDisplayName() +
+ " failed to declare its argument types");
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ SequenceType[] actualArgumentTypes = new SequenceType[args];
+ for (int i = 0; i < args; i++) {
+ argument[i] = TypeChecker.staticTypeCheck(
+ argument[i],
+ i < declaredArgumentTypes.length ?
+ declaredArgumentTypes[i] :
+ declaredArgumentTypes[declaredArgumentTypes.length - 1],
+ false,
+ new RoleLocator(RoleLocator.FUNCTION, getFunctionName(), i),
+ visitor);
+
+ actualArgumentTypes[i] = SequenceType.makeSequenceType(
+ argument[i].getItemType(th),
+ argument[i].getCardinality());
+ }
+ resultType = definition.getResultType(actualArgumentTypes);
+ if (state == 0) {
+ function.supplyStaticContext(visitor.getStaticContext(), 0, getArguments());
+ }
+ state++;
+ }
+
+ /**
+ * Type-check the expression.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression exp = super.typeCheck(visitor, contextItemType);
+ if (exp instanceof IntegratedFunctionCall) {
+ Expression exp2 = ((IntegratedFunctionCall) exp).function.rewrite(visitor.getStaticContext(), argument);
+ if (exp2 == null) {
+ return exp;
+ } else {
+ ExpressionTool.copyLocationInfo(this, exp2);
+ return exp2.simplify(visitor).typeCheck(visitor, contextItemType).optimize(visitor, contextItemType);
+ }
+ }
+ return exp;
+ }
+
+ /**
+ * Pre-evaluate a function at compile time. Functions that do not allow
+ * pre-evaluation, or that need access to context information, can override this method.
+ *
+ * @param visitor an expression visitor
+ * @return the result of the early evaluation, or the original expression, or potentially
+ * a simplified expression
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Determine the data type of the expression, if possible. All expression return
+ * sequences, in general; this method determines the type of the items within the
+ * sequence, assuming that (a) this is known in advance, and (b) it is the same for
+ * all items in the sequence.
+ * <p/>
+ * <p>This method should always return a result, though it may be the best approximation
+ * that is available at the time.</p>
+ *
+ * @param th the type hierarchy cache
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER,
+ * Type.NODE, or Type.ITEM (meaning not known at compile time)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return resultType.getPrimaryType();
+ }
+
+ /**
+ * Compute the static cardinality of this expression
+ *
+ * @return the computed cardinality, as one of the values {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_ONE},
+ * {@link net.sf.saxon.expr.StaticProperty#EXACTLY_ONE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ONE_OR_MORE},
+ * {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_MORE}
+ */
+
+ protected int computeCardinality() {
+ return resultType.getCardinality();
+ }
+
+ /**
+ * Determine the intrinsic dependencies of an expression, that is, those which are not derived
+ * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
+ * on the context position, while (position()+1) does not. The default implementation
+ * of the method returns 0, indicating "no dependencies".
+ *
+ * @return a set of bit-significant flags identifying the "intrinsic"
+ * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
+ */
+
+ public int getIntrinsicDependencies() {
+ ExtensionFunctionDefinition definition = function.getDefinition();
+ return (definition.dependsOnFocus() ? StaticProperty.DEPENDS_ON_FOCUS : 0);
+ }
+
+ /**
+ * Compute the special properties of this expression. These properties are denoted by a bit-significant
+ * integer, possible values are in class {@link net.sf.saxon.expr.StaticProperty}. The "special" properties are properties
+ * other than cardinality and dependencies, and most of them relate to properties of node sequences, for
+ * example whether the nodes are in document order.
+ *
+ * @return the special properties, as a bit-significant integer
+ */
+
+ protected int computeSpecialProperties() {
+ ExtensionFunctionDefinition definition = function.getDefinition();
+ return (definition.hasSideEffects() ? StaticProperty.HAS_SIDE_EFFECTS : StaticProperty.NON_CREATIVE);
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ ExtensionFunctionCall newCall = function.getDefinition().makeCallExpression();
+ newCall.setDefinition(function.getDefinition(), function.getContainer());
+ function.copyLocalData(newCall);
+ IntegratedFunctionCall copy = new IntegratedFunctionCall(newCall);
+ Expression[] args = new Expression[getNumberOfArguments()];
+ for (int i = 0; i < args.length; i++) {
+ args[i] = argument[i].copy();
+ }
+ copy.setFunctionName(getFunctionName());
+ copy.setArguments(args);
+ copy.resultType = resultType;
+ copy.state = state;
+ return copy;
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ ExtensionFunctionDefinition definition = function.getDefinition();
+ Sequence[] argValues = new Sequence[getNumberOfArguments()];
+ for (int i = 0; i < argValues.length; i++) {
+ argValues[i] = SequenceTool.toLazySequence(argument[i].iterate(context));
+ }
+ final RoleLocator role = new RoleLocator(RoleLocator.FUNCTION_RESULT, getFunctionName().getDisplayName(), 0);
+ final Configuration config = context.getConfiguration();
+ SequenceIterator result;
+ try {
+ result = function.call(context, argValues).iterate();
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ throw e;
+ }
+ if (!definition.trustResultType()) {
+ int card = resultType.getCardinality();
+ if (card != StaticProperty.ALLOWS_ZERO_OR_MORE) {
+ result = new CardinalityCheckingIterator(result,
+ card,
+ role,
+ this);
+ }
+ final ItemType type = resultType.getPrimaryType();
+ if (type != AnyItemType.getInstance()) {
+ result = new ItemMappingIterator(result,
+ new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ if (!type.matchesItem(item, false, config)) {
+ String msg = "Item returned by integrated function " +
+ getFunctionName().getDisplayName() +
+ "() is not of declared item type. Actual type is " +
+ Type.getItemType(item, config.getTypeHierarchy()).toString() +
+ "; expected type is " + type.toString();
+ XPathException err = new XPathException(
+ msg);
+ err.setErrorCode("XPTY0004");
+ err.setLocator(IntegratedFunctionCall.this);
+ throw err;
+ }
+ return item;
+ }
+ }, true);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ Sequence[] argValues = new Sequence[getNumberOfArguments()];
+ for (int i = 0; i < argValues.length; i++) {
+ argValues[i] = SequenceTool.toLazySequence(argument[i].iterate(context));
+ }
+ try {
+ return function.effectiveBooleanValue(context, argValues);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ throw e;
+ }
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return function.call(context, arguments);
+ }
+
+}
+
diff --git a/sf/saxon/functions/IntegratedFunctionLibrary.java b/sf/saxon/functions/IntegratedFunctionLibrary.java
new file mode 100644
index 0000000..ba4742e
--- /dev/null
+++ b/sf/saxon/functions/IntegratedFunctionLibrary.java
@@ -0,0 +1,159 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.functions.hof.CallableFunctionItem;
+import com.saxonica.functions.hof.SpecificFunctionType;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.lib.ExtensionFunctionCall;
+import net.sf.saxon.lib.ExtensionFunctionDefinition;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.FunctionItemType;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+
+
+/**
+ * A library of integrated function calls, that is, user-written extension functions implemented
+ * as instances of the class IntegratedFunction.
+ */
+public class IntegratedFunctionLibrary implements FunctionLibrary, Serializable {
+
+ private HashMap<StructuredQName, ExtensionFunctionDefinition> functions =
+ new HashMap<StructuredQName, ExtensionFunctionDefinition>();
+
+ /**
+ * Register an integrated function with this function library
+ * @param function the implementation of the function (or set of functions)
+ */
+
+ public void registerFunction(ExtensionFunctionDefinition function) {
+ functions.put(function.getFunctionQName(), function);
+ }
+
+ /**
+ * Bind an extension function, given the URI and local parts of the function name,
+ * and the list of expressions supplied as arguments. This method is called at compile
+ * time.
+ *
+ *
+ * @param functionName the QName of the function being called
+ * @param arity
+ * @param staticArgs The expressions supplied statically in arguments to the function call.
+ * The length of this array represents the arity of the function. The intention is
+ * that the static type of the arguments (obtainable via getItemType() and getCardinality()) may
+ * be used as part of the binding algorithm. In some cases it may be possible for the function
+ * to be pre-evaluated at compile time, for example if these expressions are all constant values.
+ * <p/>
+ * The conventions of the XPath language demand that the results of a function depend only on the
+ * values of the expressions supplied as arguments, and not on the form of those expressions. For
+ * example, the result of f(4) is expected to be the same as f(2+2). The actual expression is supplied
+ * here to enable the binding mechanism to select the most efficient possible implementation (including
+ * compile-time pre-evaluation where appropriate).
+ * @param env The static context of the function call
+ * @param container
+ * @return An object representing the function to be called, if one is found;
+ * null if no function was found matching the required name and arity.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a function is found with the required name and arity, but
+ * the implementation of the function cannot be loaded or used; or if an error occurs
+ * while searching for the function.
+ */
+
+ public Expression bind(StructuredQName functionName, int arity, Expression[] staticArgs, StaticContext env, Container container)
+ throws XPathException {
+ ExtensionFunctionDefinition defn = functions.get(functionName);
+ if (defn == null) {
+ return null;
+ }
+ try {
+ return makeFunctionCall(defn, staticArgs, container);
+ } catch (Exception err) {
+ throw new XPathException("Failed to create call to extension function " + functionName.getDisplayName(), err);
+ }
+ }
+
+ public static Expression makeFunctionCall(ExtensionFunctionDefinition defn, Expression[] staticArgs, Container container) {
+ ExtensionFunctionCall f = defn.makeCallExpression();
+ f.setDefinition(defn, container);
+ IntegratedFunctionCall fc = new IntegratedFunctionCall(f);
+ fc.setFunctionName(defn.getFunctionQName());
+ fc.setArguments(staticArgs);
+ return fc;
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ * <p/>
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ * @throws net.sf.saxon.trans.XPathException
+ * in the event of certain errors, for example attempting to get a function
+ * that is private
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext) throws XPathException {
+ ExtensionFunctionDefinition defn = functions.get(functionName);
+ if (defn == null) {
+ return null;
+ }
+ try {
+ ExtensionFunctionCall f = defn.makeCallExpression();
+ FunctionItemType type = new SpecificFunctionType(
+ defn.getArgumentTypes(), defn.getResultType(defn.getArgumentTypes()));
+ return new CallableFunctionItem(functionName, arity, f, type);
+ } catch (Exception err) {
+ throw new XPathException("Failed to create call to extension function " + functionName.getDisplayName(), err);
+ }
+ }
+//#endif
+
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ ExtensionFunctionDefinition defn = functions.get(functionName);
+ return defn != null && defn.getMaximumNumberOfArguments() >= arity && defn.getMinimumNumberOfArguments() <= arity;
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ public FunctionLibrary copy() {
+ IntegratedFunctionLibrary lib = new IntegratedFunctionLibrary();
+ lib.functions = new HashMap<StructuredQName, ExtensionFunctionDefinition>(functions);
+ return lib;
+ }
+
+}
+
diff --git a/sf/saxon/functions/IsWholeNumber.java b/sf/saxon/functions/IsWholeNumber.java
new file mode 100644
index 0000000..216762f
--- /dev/null
+++ b/sf/saxon/functions/IsWholeNumber.java
@@ -0,0 +1,143 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.ExtensionFunctionCall;
+import net.sf.saxon.lib.ExtensionFunctionDefinition;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.NumericValue;
+import net.sf.saxon.value.SequenceType;
+
+/**
+ * This class implements the saxon:is-whole-number() extension function,
+ * which is specially-recognized by the system because calls are generated by the optimizer.
+ *
+ * <p>The function signature is <code>saxon:is-whole-number($arg as numeric?) as boolean</code></p>
+ *
+ * <p>The result is true if $arg is not empty and is equal to some integer.</p>
+*/
+
+public class IsWholeNumber extends ExtensionFunctionDefinition {
+
+ private static final StructuredQName qName =
+ new StructuredQName("", NamespaceConstant.SAXON, "is-whole-number");
+
+ /**
+ * Get the function name, as a QName
+ * @return the QName of the function
+ */
+
+ public StructuredQName getFunctionQName() {
+ return qName;
+ }
+
+ /**
+ * Get the minimum number of arguments required by the function
+ * @return the minimum number of arguments that must be supplied in a call to this function
+ */
+
+ public int getMinimumNumberOfArguments() {
+ return 1;
+ }
+
+ /**
+ * Get the maximum number of arguments allowed by the function
+ * @return the maximum number of arguments that may be supplied in a call to this function
+ */
+
+ public int getMaximumNumberOfArguments() {
+ return 1;
+ }
+
+ /**
+ * Get the required types for the arguments of this function, counting from zero
+ * @return the required types of the argument, as defined by the function signature. Normally
+ * this should be an array of size {@link #getMaximumNumberOfArguments()}; however for functions
+ * that allow a variable number of arguments, the array can be smaller than this, with the last
+ * entry in the array providing the required type for all the remaining arguments.
+ */
+
+ public SequenceType[] getArgumentTypes() {
+ return new SequenceType[]{SequenceType.OPTIONAL_NUMERIC};
+ }
+
+ /**
+ * Get the type of the result of the function
+ * @param suppliedArgumentTypes the static types of the arguments to the function.
+ * This is provided so that a more precise result type can be returned in the common
+ * case where the type of the result depends on the type of the first argument. The value
+ * will be null if the function call has no arguments.
+ * @return the return type of the function, as defined by its function signature
+ */
+
+ public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
+ return SequenceType.SINGLE_BOOLEAN;
+ }
+
+ /**
+ * Create a call on this function. This method is called by the compiler when it identifies
+ * a function call that calls this function.
+ */
+
+ /*@NotNull*/ public ExtensionFunctionCall makeCallExpression() {
+ return new IsWholeNumberCall();
+ }
+
+ private static class IsWholeNumberCall extends ExtensionFunctionCall {
+
+ /**
+ * Evaluate this function call at run-time
+ *
+ *
+ * @param context The XPath dynamic evaluation context
+ * @param arguments The values of the arguments to the function call. Each argument value (which is in general
+ * a sequence) is supplied in the form of an iterator over the items in the sequence. If required, the
+ * supplied sequence can be materialized by calling, for example, <code>new SequenceExtent(arguments[i])</code>.
+ * If the argument is always a singleton, then the single item may be obtained by calling
+ * <code>arguments[i].next()</code>. The implementation is not obliged to read all the items in each
+ * <code>SequenceIterator</code> if they are not required to compute the result; but if any SequenceIterator is not read
+ * to completion, it is good practice to call its close() method.
+ * @return an iterator over the results of the function. If the result is a single item, it can be
+ * returned in the form of a {@link net.sf.saxon.tree.iter.SingletonIterator}. If the result is an empty sequence,
+ * the method should return <code>EmptyIterator.getInstance()</code>
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during evaluation of the function. The Saxon run-time
+ * code will add information about the error location.
+ */
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context, arguments));
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context, Sequence[] arguments) throws XPathException {
+ NumericValue val = (NumericValue)arguments[0].head();
+ return val != null && val.isWholeNumber();
+ }
+
+
+ }
+
+}
+
diff --git a/sf/saxon/functions/KeyFn.java b/sf/saxon/functions/KeyFn.java
new file mode 100644
index 0000000..ff967f6
--- /dev/null
+++ b/sf/saxon/functions/KeyFn.java
@@ -0,0 +1,385 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.KeyFnCompiler;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.expr.sort.DocumentOrderIterator;
+import net.sf.saxon.expr.sort.LocalOrderComparer;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.style.ExpressionContext;
+import net.sf.saxon.trans.KeyDefinitionSet;
+import net.sf.saxon.trans.KeyManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Cardinality;
+
+
+public class KeyFn extends SystemFunctionCall {
+
+ /*@Nullable*/ private NamespaceResolver nsContext = null;
+ private KeyDefinitionSet staticKeySet = null; // null if name resolution is done at run-time
+ private transient boolean checked = false;
+ private transient boolean internal = false;
+ // the second time checkArguments is called, it's a global check so the static context is inaccurate
+
+ /**
+ * Get the key name, if known statically. If not known statically, return null.
+ * @return the key name if known, otherwise null
+ */
+
+ public StructuredQName getStaticKeyName() {
+ return (staticKeySet == null ? null : staticKeySet.getKeyName());
+ }
+
+ public KeyDefinitionSet getStaticKeySet(){
+
+ return staticKeySet;
+ }
+
+ public boolean getInternal(){
+ return internal;
+ }
+
+
+ public NamespaceResolver getNamespaceResolver(){
+ return nsContext;
+ }
+
+ /**
+ * Type-check the expression. This also calls preEvaluate() to evaluate the function
+ * if all the arguments are constant; functions that do not require this behavior
+ * can override the preEvaluate method.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ try {
+ return super.typeCheck(visitor, contextItemType);
+ } catch (XPathException err) {
+ if ("XPDY0002".equals(err.getErrorCodeLocalPart()) && argument[2] instanceof RootExpression) {
+ XPathException e = new XPathException("Cannot call the key() function when there is no context node");
+ e.setErrorCode("XTDE1270");
+ e.maybeSetLocation(this);
+ throw e;
+ }
+ throw err;
+ }
+ }
+
+ /**
+ * Non-standard constructor to create an internal call on key() with a known key definition
+ * @param keySet the set of KeyDefinitions (always a single KeyDefinition)
+ * @param name the name allocated to the key (first argument of the function)
+ * @param value the value being searched for (second argument of the function)
+ * @param doc the document being searched (third argument)
+ * @return a call on the key() function
+ */
+
+ public static KeyFn internalKeyCall(KeyDefinitionSet keySet, String name, Expression value, Expression doc) {
+ KeyFn k = new KeyFn();
+ k.argument = new Expression[] {new StringLiteral(name), value, doc};
+ k.staticKeySet = keySet;
+ k.checked = true;
+ k.internal = true;
+ k.setDetails(StandardFunction.getFunction("key", 3));
+ k.setFunctionName(FN_KEY);
+ k.adoptChildExpression(value);
+ k.adoptChildExpression(doc);
+ return k;
+ }
+
+ private final static StructuredQName FN_KEY = new StructuredQName("fn", NamespaceConstant.FN, "key");
+
+ /**
+ * Simplify: add a third implicit argument, the context document
+ * @param visitor the expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ if (!internal && !(visitor.getStaticContext() instanceof ExpressionContext)) {
+ throw new XPathException("The key() function is available only in XPath expressions within an XSLT stylesheet");
+ }
+ KeyFn f = (KeyFn)super.simplify(visitor);
+ if (argument.length == 2) {
+ f.addContextDocumentArgument(2, "key");
+ }
+ return f;
+ }
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ if (checked) return;
+ checked = true;
+ super.checkArguments(visitor);
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ argument[1] = ExpressionTool.unsorted(opt, argument[1], false);
+ if (argument[0] instanceof StringLiteral) {
+ // common case, key name is supplied as a constant
+ StructuredQName keyName;
+ try {
+ keyName = StructuredQName.fromLexicalQName(
+ ((StringLiteral)argument[0]).getStringValue(),
+ false, true,
+ visitor.getConfiguration().getNameChecker(),
+ visitor.getStaticContext().getNamespaceResolver());
+ } catch (XPathException e) {
+ XPathException err = new XPathException("Error in key name " +
+ ((StringLiteral)argument[0]).getStringValue() + ": " + e.getMessage());
+ err.setLocator(this);
+ err.setErrorCode("XTDE1260");
+ throw err;
+ }
+ staticKeySet = visitor.getExecutable().getKeyManager().getKeyDefinitionSet(keyName);
+ if (staticKeySet == null) {
+ XPathException err = new XPathException("Key " +
+ ((StringLiteral)argument[0]).getStringValue() + " has not been defined");
+ err.setLocator(this);
+ err.setErrorCode("XTDE1260");
+ throw err;
+ }
+ } else {
+ // we need to save the namespace context
+ nsContext = visitor.getStaticContext().getNamespaceResolver();
+ }
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-signficant. These properties are used for optimizations. In general, if
+ * a property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ int prop = StaticProperty.ORDERED_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET |
+ StaticProperty.NON_CREATIVE;
+ if ((getNumberOfArguments() == 2) ||
+ (argument[2].getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
+ prop |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
+ }
+ return prop;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * @param visitor the expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Add a representation of a doc() call or similar function to a PathMap.
+ * This is a convenience method called by the addToPathMap() methods for doc(), document(), collection()
+ * and similar functions. These all create a new root expression in the path map.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ argument[0].addToPathMap(pathMap, pathMapNodeSet);
+ argument[1].addToPathMap(pathMap, pathMapNodeSet);
+ PathMap.PathMapNodeSet target = argument[2].addToPathMap(pathMap, pathMapNodeSet);
+ // indicate that the function navigates to all nodes in the containing document
+ target = target.createArc(AxisInfo.ANCESTOR_OR_SELF, NodeKindTest.DOCUMENT);
+ return target.createArc(AxisInfo.DESCENDANT, AnyNodeTest.getInstance());
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ KeyFn k = (KeyFn)super.copy();
+ k.nsContext = nsContext;
+ k.staticKeySet = staticKeySet;
+ k.internal = internal;
+ k.checked = checked;
+ return k;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return this==o;
+ }
+
+ /**
+ * Enumerate the results of the expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+
+ Controller controller = context.getController();
+
+ Item arg2;
+ try {
+ arg2 = argument[2].evaluateItem(context);
+ } catch (XPathException e) {
+ String code = e.getErrorCodeLocalPart();
+ if ("XPDY0002".equals(code) && argument[2] instanceof RootExpression) {
+ dynamicError("Cannot call the key() function when there is no context node", "XTDE1270", context);
+ } else if ("XPDY0050".equals(code)) {
+ dynamicError("In the key() function," +
+ " the node supplied in the third argument (or the context node if absent)" +
+ " must be in a tree whose root is a document node", "XTDE1270", context);
+ } else if ("XPTY0020".equals(code) || "XPTY0019".equals(code)) {
+ dynamicError("Cannot call the key() function when the context item is an atomic value",
+ "XTDE1270", context);
+ }
+ throw e;
+ }
+
+ NodeInfo origin = (NodeInfo)arg2;
+ NodeInfo root = origin.getRoot();
+ if (root.getNodeKind() != Type.DOCUMENT) {
+ dynamicError("In the key() function," +
+ " the node supplied in the third argument (or the context node if absent)" +
+ " must be in a tree whose root is a document node", "XTDE1270", context);
+ return null;
+ }
+ DocumentInfo doc = (DocumentInfo)root;
+
+ KeyDefinitionSet selectedKeySet = staticKeySet;
+ if (selectedKeySet == null) {
+ String givenkeyname = argument[0].evaluateItem(context).getStringValue();
+ StructuredQName qName = null;
+ try {
+ boolean is30 = context.getController().getExecutable().isAllowXPath30();
+ qName = StructuredQName.fromLexicalQName(
+ givenkeyname, false, is30, controller.getConfiguration().getNameChecker(),
+ nsContext);
+ } catch (XPathException err) {
+ dynamicError("Invalid key name: " + err.getMessage(), "XTDE1260", context);
+ }
+ selectedKeySet = controller.getKeyManager().getKeyDefinitionSet(qName);
+ if (selectedKeySet == null) {
+ dynamicError("Key '" + givenkeyname + "' has not been defined", "XTDE1260", context);
+ return null;
+ }
+ }
+
+// if (internal) {
+// System.err.println("Using key " + fprint + " on doc " + doc);
+// }
+
+ // If the second argument is a singleton, we evaluate the function
+ // directly; otherwise we recurse to evaluate it once for each Item
+ // in the sequence.
+
+ Expression expression = argument[1];
+ SequenceIterator allResults;
+ if (Cardinality.allowsMany(expression.getCardinality())) {
+ final XPathContext keyContext = context;
+ final DocumentInfo document = doc;
+ final KeyManager keyManager = controller.getKeyManager();
+ final KeyDefinitionSet keySet = selectedKeySet;
+ MappingFunction map = new MappingFunction<AtomicValue, NodeInfo>() {
+ // Map a value to the sequence of nodes having that value as a key value
+ public SequenceIterator<NodeInfo> map(AtomicValue item) throws XPathException {
+ return keyManager.selectByKey(
+ keySet, document, item, keyContext);
+ }
+ };
+
+ SequenceIterator keys = argument[1].iterate(context);
+ SequenceIterator allValues = new MappingIterator(keys, map);
+ allResults = new DocumentOrderIterator(allValues, LocalOrderComparer.getInstance());
+ } else {
+ try {
+ AtomicValue keyValue = (AtomicValue)argument[1].evaluateItem(context);
+ if (keyValue == null) {
+ return EmptyIterator.getInstance();
+ }
+ KeyManager keyManager = controller.getKeyManager();
+ allResults = keyManager.selectByKey(selectedKeySet, doc, keyValue, context);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ throw e;
+ }
+ }
+ if (origin == doc) {
+ return allResults;
+ }
+ return new ItemMappingIterator(allResults, new SubtreeFilter(origin));
+ }
+
+ /**
+ * Mapping class to filter nodes that have the origin node as an ancestor-or-self
+ */
+
+ public static class SubtreeFilter implements ItemMappingFunction<NodeInfo, NodeInfo> {
+
+ private NodeInfo origin;
+
+ public SubtreeFilter(NodeInfo origin) {
+ this.origin = origin;
+ }
+
+ public NodeInfo mapItem(NodeInfo item) throws XPathException {
+ if (Navigator.isAncestorOrSelf(origin, item)) {
+ return item;
+ } else {
+ return null;
+ }
+ }
+
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the KeyFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new KeyFnCompiler();
+ }
+//#endif
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ throw new XPathException("Dynamic evaluation of fn:key() is not supported");
+ }
+
+}
+
+
diff --git a/sf/saxon/functions/Lang.java b/sf/saxon/functions/Lang.java
new file mode 100644
index 0000000..93a5c6b
--- /dev/null
+++ b/sf/saxon/functions/Lang.java
@@ -0,0 +1,160 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.value.BooleanValue;
+
+
+public class Lang extends SystemFunctionCall implements Callable {
+
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (argument.length==1) {
+ if (contextItemType == null) {
+ XPathException err = new XPathException("The context item for lang() is absent");
+ err.setErrorCode("XPDY0002");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ } else if (contextItemType instanceof AtomicType) {
+ XPathException err = new XPathException("The context item for lang() is not a node");
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ return super.typeCheck(visitor, contextItemType);
+ }
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public BooleanValue evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo target;
+ if (argument.length > 1) {
+ target = (NodeInfo)argument[1].evaluateItem(c);
+ } else {
+ target = getAndCheckContextItem(c);
+ }
+ final Item arg0Val = argument[0].evaluateItem(c);
+ final String testLang = (arg0Val==null ? "" : arg0Val.getStringValue());
+ boolean b = isLang(testLang, target);
+ return BooleanValue.get(b);
+ }
+
+ /**
+ * Determine the dependencies
+ */
+
+ public int getIntrinsicDependencies() {
+ return (argument.length == 1 ? StaticProperty.DEPENDS_ON_CONTEXT_ITEM : 0);
+ }
+
+ /**
+ * Test whether the context node has the given language attribute
+ * @param arglang the language being tested
+ * @param target the target node
+ */
+
+ public static boolean isLang(String arglang, NodeInfo target) {
+ String doclang = null;
+ NodeInfo node = target;
+
+ while(node!=null) {
+ doclang = node.getAttributeValue(NamespaceConstant.XML, "lang");
+ if (doclang!=null) {
+ break;
+ }
+ node = node.getParent();
+ if (node==null) {
+ return false;
+ }
+ }
+
+ if (doclang==null) {
+ return false;
+ }
+
+ while (true) {
+ if (arglang.equalsIgnoreCase(doclang)) {
+ return true;
+ }
+ int hyphen = doclang.indexOf("-");
+ if (hyphen<0) {
+ return false;
+ }
+ doclang = doclang.substring(0, hyphen);
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo target;
+ if (arguments.length > 1) {
+ target = (NodeInfo)arguments[1].head();
+ } else {
+ target = getAndCheckContextItem(context);
+ }
+ final Item arg0Val = arguments[0].head();
+ final String testLang = (arg0Val==null ? "" : arg0Val.getStringValue());
+ return BooleanValue.get(isLang(testLang, target));
+ }
+
+ /**
+ * Get the context item, checking that it exists and is a node
+ * @param context the XPath dynamic context
+ * @return the context node
+ * @throws XPathException if there is no context item or if the context item is not a node
+ */
+
+ private NodeInfo getAndCheckContextItem(XPathContext context) throws XPathException {
+ NodeInfo target;
+ Item current = context.getContextItem();
+ if (current==null) {
+ XPathException err = new XPathException("The context item for lang() is absent");
+ err.setErrorCode("XPDY0002");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ if (!(current instanceof NodeInfo)) {
+ XPathException err = new XPathException("The context item for lang() is not a node");
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ err.setXPathContext(context);
+ throw err;
+ }
+ target = (NodeInfo)current;
+ return target;
+ }
+}
+
diff --git a/sf/saxon/functions/Last.java b/sf/saxon/functions/Last.java
new file mode 100644
index 0000000..0c2f57a
--- /dev/null
+++ b/sf/saxon/functions/Last.java
@@ -0,0 +1,132 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.LastCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+
+import java.util.List;
+
+/**
+* Implement the XPath 2.0 function fn:last()
+*/
+
+public class Last extends SystemFunctionCall {
+
+ boolean contextPossiblyUndefined = true;
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return new IntegerValue[]{Int64Value.PLUS_ONE, MAX_SEQUENCE_LENGTH};
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (contextItemType == null || contextItemType.itemType == null) {
+ XPathException err = new XPathException("The context for last() is absent");
+ err.setErrorCode("XPDY0002");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ } else {
+ contextPossiblyUndefined = contextItemType.contextMaybeUndefined;
+ }
+ return super.typeCheck(visitor, contextItemType);
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Ask whether the context item may possibly be undefined
+ * @return true if it might be undefined
+ */
+
+ public boolean isContextPossiblyUndefined() {
+ return contextPossiblyUndefined;
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ if (reasons != null) {
+ reasons.add("Call to last() is not streamable");
+ }
+ return W3C_FREE_RANGING;
+ }
+//#endif
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public Int64Value evaluateItem(XPathContext c) throws XPathException {
+ return Int64Value.makeIntegerValue(c.getLast());
+ }
+
+ /**
+ * Determine the dependencies
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_LAST;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return Int64Value.makeIntegerValue(context.getLast());
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Last expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new LastCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/LocalNameFn.java b/sf/saxon/functions/LocalNameFn.java
new file mode 100644
index 0000000..ee5b797
--- /dev/null
+++ b/sf/saxon/functions/LocalNameFn.java
@@ -0,0 +1,91 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.LocalNameFnCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * This class supports the local-name() function
+ */
+
+public class LocalNameFn extends SystemFunctionCall {
+
+ /**
+ * Simplify and validate.
+ *
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ return simplifyArguments(visitor);
+ }
+
+ @Override
+ public int getIntrinsicDependencies() {
+ if (getNumberOfArguments() == 0) {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ } else {
+ return super.getIntrinsicDependencies();
+ }
+ }
+
+
+ /**
+ * Evaluate the function in a string context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo node = (NodeInfo) argument[0].evaluateItem(c);
+ if (node == null) {
+ return StringValue.EMPTY_STRING;
+ }
+ return new StringValue(node.getLocalPart());
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo node = (arguments.length == 0 ? getContextNode(context) : (NodeInfo)arguments[0].head());
+ if (node == null) {
+ return StringValue.EMPTY_STRING;
+ } else {
+ return new StringValue(node.getLocalPart());
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the LocalNameFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new LocalNameFnCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/LowerCase.java b/sf/saxon/functions/LowerCase.java
new file mode 100644
index 0000000..8eb1b5f
--- /dev/null
+++ b/sf/saxon/functions/LowerCase.java
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ForceCaseCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+
+/**
+* This class implements the fn:lower-case() function
+*/
+
+public class LowerCase extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ StringValue sv = (StringValue)argument[0].evaluateItem(c);
+ if (sv==null) {
+ return StringValue.EMPTY_STRING;
+ } else {
+ return StringValue.makeStringValue(sv.getStringValue().toLowerCase());
+ }
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue sv = (StringValue)arguments[0].head();
+ return (sv == null ? StringValue.EMPTY_STRING : StringValue.makeStringValue(sv.getStringValue().toLowerCase()));
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the LowerCase expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ForceCaseCompiler();
+ }
+//#endif
+
+
+}
+
diff --git a/sf/saxon/functions/Matches.java b/sf/saxon/functions/Matches.java
new file mode 100644
index 0000000..c2b64dd
--- /dev/null
+++ b/sf/saxon/functions/Matches.java
@@ -0,0 +1,310 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.MatchesCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.regex.ARegularExpression;
+import net.sf.saxon.regex.RegularExpression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+* This class implements the matches() function for regular expression matching
+*/
+
+public class Matches extends SystemFunctionCall {
+
+ private RegularExpression regexp;
+ private boolean allow30features = false;
+
+
+ /**
+ * Simplify and validate.
+ * This is a pure function so it can be simplified in advance if the arguments are known
+ * @return the simplified expression
+ * @throws XPathException if any error is found (e.g. invalid regular expression)
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ allow30features = DecimalValue.THREE.equals(visitor.getStaticContext().getXPathLanguageLevel());
+ Expression e = simplifyArguments(visitor);
+ // compile the regular expression once if possible
+ if (e == this) {
+ maybePrecompile(visitor);
+ }
+ return e;
+ }
+
+ /**
+ * Precompile the regular expression if possible
+ * @param visitor an expression visitor
+ */
+
+ private void maybePrecompile(ExpressionVisitor visitor) throws XPathException {
+ if (regexp == null) {
+ try {
+ regexp = tryToCompile(argument, 1, 2, visitor.getStaticContext());
+ } catch (XPathException err) {
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ }
+
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ // try once again to compile the regular expression once if possible
+ // (used when the regex has been identified as a constant as a result of earlier rewrites)
+ if (e == this) {
+ maybePrecompile(visitor);
+ }
+ return e;
+ }
+
+ /**
+ * Get the compiled regular expression, returning null if the regex has not been compiled
+ * @return the compiled regular expression, or null
+ */
+
+ public RegularExpression getCompiledRegularExpression() {
+ return regexp;
+ }
+
+ /**
+ * Evaluate the matches() function to give a Boolean value.
+ * @param c The dynamic evaluation context
+ * @return the result as a BooleanValue, or null to indicate the empty sequence
+ * @throws XPathException on an error
+ */
+
+ /*@Nullable*/ public BooleanValue evaluateItem(XPathContext c) throws XPathException {
+ AtomicValue sv0 = (AtomicValue)argument[0].evaluateItem(c);
+ if (sv0==null) {
+ sv0 = StringValue.EMPTY_STRING;
+ }
+
+ RegularExpression re = regexp;
+
+ if (re == null) {
+ AtomicValue pat = (AtomicValue)argument[1].evaluateItem(c);
+ CharSequence flags;
+ if (argument.length==2) {
+ flags = "";
+ } else {
+ AtomicValue sv2 = (AtomicValue)argument[2].evaluateItem(c);
+ if (sv2==null) return null;
+ flags = sv2.getStringValueCS();
+ }
+ return BooleanValue.get(evalMatches(sv0, pat, flags, c));
+ } else {
+ return BooleanValue.get(re.containsMatch(sv0.getStringValueCS()));
+ }
+ }
+
+ /**
+ * Interface used by compiled bytecode
+ * @param input the value to be tested
+ * @param regex the regular expression
+ * @param flags the flags
+ * @param context the dynamic context
+ * @return true if the string matches the regex
+ * @throws XPathException
+ */
+
+ public boolean evalMatches(AtomicValue input, AtomicValue regex, CharSequence flags, XPathContext context) throws XPathException {
+ RegularExpression re;
+
+ if (regex==null) {
+ return false;
+ }
+
+ try {
+ String lang = (allow30features ? "XP30" : "XP20");
+ if (context.getConfiguration().getXsdVersion() == Configuration.XSD11) {
+ lang += "/XSD11";
+ }
+ re = new ARegularExpression(regex.getStringValue(), flags.toString(), lang, null);
+
+ } catch (XPathException err) {
+ XPathException de = new XPathException(err);
+ de.maybeSetErrorCode("FORX0002");
+ de.setXPathContext(context);
+ throw de;
+ }
+ return re.containsMatch(input.getStringValueCS());
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ AtomicValue sv0 = (AtomicValue)arguments[0].head();
+ if (sv0==null) {
+ sv0 = StringValue.EMPTY_STRING;
+ }
+ AtomicValue pat = (AtomicValue)arguments[1].head();
+ CharSequence flags;
+ if (arguments.length==2) {
+ flags = "";
+ } else {
+ AtomicValue sv2 = (AtomicValue)arguments[2].head();
+ if (sv2==null) {
+ return EmptySequence.getInstance();
+ }
+ flags = sv2.getStringValueCS();
+ }
+ return BooleanValue.get(evalMatches(sv0, pat, flags, context));
+ }
+
+ /**
+ * Temporary test rig, used to submit bug report to Sun
+ */
+// public static void main(String[] args) throws Exception {
+//
+// matches("\u212a", "K");
+// matches("\u212a", "[A-Z]");
+// matches("\u212a", "I|J|K|L");
+// matches("\u212a", "[IJKL]");
+// matches("\u212a", "k");
+// matches("\u212a", "[a-z]");
+// matches("\u212a", "i|j|k|l");
+// matches("\u212a", "[ijkl]");
+// }
+//
+// private static void matches(String in, String pattern) {
+// System.err.println("Java version " + System.getProperty("java.version"));
+// int flags = Pattern.UNIX_LINES;
+// flags |= Pattern.CASE_INSENSITIVE;
+// flags |= Pattern.UNICODE_CASE;
+// Pattern p = Pattern.compile(pattern, flags);
+// boolean b = p.matcher(in).find();
+// System.err.println("Pattern " + pattern + ": " + (b ? " match" : "no match"));
+// }
+
+// Results of this test with JDK 1.5.0_05:
+//
+// Pattern K: match
+// Java version 1.5.0_05
+// Pattern [A-Z]: no match
+// Java version 1.5.0_05
+// Pattern I|J|K|L: match
+// Java version 1.5.0_05
+// Pattern [IJKL]: no match
+// Java version 1.5.0_05
+// Pattern k: match
+// Java version 1.5.0_05
+// Pattern [a-z]: match
+// Java version 1.5.0_05
+// Pattern i|j|k|l: match
+// Java version 1.5.0_05
+// Pattern [ijkl]: no match
+
+ /**
+ * Try to precompile the arguments to the function. This method is shared by
+ * the implementations of the three XPath functions matches(), replace(), and
+ * tokenize().
+ * @param args the supplied arguments to the function, as an array
+ * @param patternArg the position of the argument containing the regular expression
+ * @param flagsArg the position of the argument containing the flags
+ * @param env the static context
+ * @return the compiled regular expression, or null indicating that the information
+ * is not available statically so it cannot be precompiled
+ * @throws XPathException if any failure occurs, in particular, if the regular
+ * expression is invalid
+ */
+
+ public static RegularExpression tryToCompile(Expression[] args, int patternArg, int flagsArg, StaticContext env)
+ throws XPathException {
+ if (patternArg > args.length - 1) {
+ // too few arguments were supplied; the error will be reported in due course
+ return null;
+ }
+ String flagstr = null;
+ if (args.length-1 < flagsArg) {
+ flagstr = "";
+ } else if (args[flagsArg] instanceof StringLiteral) {
+ flagstr = ((StringLiteral)args[flagsArg]).getStringValue();
+ }
+
+ if (args[patternArg] instanceof StringLiteral && flagstr != null) {
+ try {
+ String in = ((StringLiteral)args[patternArg]).getStringValue();
+ String hostLang = (DecimalValue.THREE.equals(env.getXPathLanguageLevel()) ? "XP30": "XP20");
+ if (env.getConfiguration().getXsdVersion() == Configuration.XSD11) {
+ hostLang += "/XSD11";
+ }
+ List<String> warnings = new ArrayList<String>(1);
+ RegularExpression re = Configuration.getPlatform().compileRegularExpression(in, flagstr, hostLang, warnings);
+ for (String e : warnings) {
+ env.issueWarning(e, args[patternArg]);
+ }
+ return re;
+ } catch (XPathException err) {
+ err.maybeSetErrorCode("FORX0002");
+ throw err;
+ }
+ } else {
+ return null;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Matches expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new MatchesCompiler();
+ }
+//#endif
+
+
+}
+
diff --git a/sf/saxon/functions/Max.java b/sf/saxon/functions/Max.java
new file mode 100644
index 0000000..a4ea503
--- /dev/null
+++ b/sf/saxon/functions/Max.java
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Implementation of the fn:max() function with 1 or 2 arguments
+ */
+public class Max extends Minimax {
+
+ public Max() {
+ operation = MAX;
+ }
+
+ /**
+ * Evaluate the function
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringCollator collator = getCollatorFromLastArgument(arguments, 1, context);
+ GenericAtomicComparer comparer = new GenericAtomicComparer(collator, context);
+ return minimax(arguments[0].iterate(), MAX, comparer, false, context);
+ }
+}
+
diff --git a/sf/saxon/functions/Min.java b/sf/saxon/functions/Min.java
new file mode 100644
index 0000000..511335b
--- /dev/null
+++ b/sf/saxon/functions/Min.java
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Implementation of the fn:min() function with 1 or 2 arguments
+ */
+public class Min extends Minimax {
+
+ public Min() {
+ operation = MIN;
+ }
+
+ /**
+ * Evaluate the function
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringCollator collator = getCollatorFromLastArgument(arguments, 1, context);
+ GenericAtomicComparer comparer = new GenericAtomicComparer(collator, context);
+ return minimax(arguments[0].iterate(), MIN, comparer, false, context);
+ }
+}
+
diff --git a/sf/saxon/functions/Minimax.java b/sf/saxon/functions/Minimax.java
new file mode 100644
index 0000000..bbeb6d8
--- /dev/null
+++ b/sf/saxon/functions/Minimax.java
@@ -0,0 +1,464 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.MinimaxCompiler;
+import com.saxonica.stream.adjunct.MinimaxAdjunct;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.DescendingComparer;
+import net.sf.saxon.expr.sort.GenericAtomicComparer;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+
+/**
+ * This class implements the min() and max() functions
+ */
+
+public abstract class Minimax extends CollatingFunction {
+
+ public static final int MIN = 2;
+ public static final int MAX = 3;
+
+ private BuiltInAtomicType argumentType = BuiltInAtomicType.ANY_ATOMIC;
+ private boolean ignoreNaN = false;
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 1;
+ }
+
+ /**
+ * Indicate whether NaN values should be ignored. For the external min() and max() function, a
+ * NaN value in the input causes the result to be NaN. Internally, however, min() and max() are also
+ * used in such a way that NaN values should be ignored.
+ *
+ * @param ignore true if NaN values are to be ignored when computing the min or max.
+ */
+
+ public void setIgnoreNaN(boolean ignore) {
+ ignoreNaN = ignore;
+ }
+
+ /**
+ * Test whether NaN values are to be ignored
+ *
+ * @return true if NaN values are to be ignored. This is the case for internally-generated min() and max()
+ * functions used to support general comparisons
+ */
+
+ public boolean isIgnoreNaN() {
+ return ignoreNaN;
+ }
+
+
+
+ public AtomicComparer getComparer() {
+ return getPreAllocatedAtomicComparer();
+ }
+
+ public BuiltInAtomicType getArgumentType() {
+ return argumentType;
+ }
+
+ /**
+ * Get implementation method
+ *
+ * @return a value that indicates this function is capable of being streamed
+ */
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | ITEM_FEED_METHOD;
+ }
+
+ /**
+ * Static analysis: prevent sorting of the argument and preallocate a comparer if possible
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ argument[0] = ExpressionTool.unsorted(opt, argument[0], false);
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ ItemType type = argument[0].getItemType(th);
+ if (type instanceof AtomicType) {
+ if (type == BuiltInAtomicType.UNTYPED_ATOMIC) {
+ type = BuiltInAtomicType.DOUBLE;
+ }
+ preAllocateComparer((AtomicType)type, (AtomicType)type, visitor.getStaticContext(), false);
+ }
+ }
+
+ /**
+ * Determine the cardinality of the function.
+ */
+
+ public int computeCardinality() {
+ int c = super.computeCardinality();
+ if (!Cardinality.allowsZero(argument[0].getCardinality())) {
+ c = StaticProperty.EXACTLY_ONE;
+ }
+ return c;
+ }
+
+
+ /**
+ * Type-check the expression
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e2 = super.typeCheck(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ StaticContext env = visitor.getStaticContext();
+ if(Literal.isEmptySequence(argument[0])){
+ return argument[0];
+ }
+ argumentType = (BuiltInAtomicType) argument[0].getItemType(th).getPrimitiveItemType();
+
+ PlainType t0 = (PlainType) argument[0].getItemType(th);
+
+ if (t0.isExternalType()) {
+ XPathException err = new XPathException("Cannot perform computation involving external objects");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ err.setLocator(this);
+ throw err;
+ }
+
+ BuiltInAtomicType p0 = (BuiltInAtomicType) t0.getPrimitiveItemType();
+ if (p0.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ p0 = BuiltInAtomicType.DOUBLE;
+ }
+
+ //needsRuntimeCheck = p0.equals(BuiltInAtomicType.ANY_ATOMIC);
+
+// if (comparer == null) {
+// StringCollator comp = getDefaultCollation();
+// if (comp != null) {
+// comparer = GenericAtomicComparer.makeAtomicComparer(
+// p0, p0, comp, env.getConfiguration().getConversionContext());
+// }
+// }
+
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+ if (getNumberOfArguments() == 1) {
+ // test for a singleton: this often happens after (A<B) is rewritten as (min(A) lt max(B))
+ int card = argument[0].getCardinality();
+ if (!Cardinality.allowsMany(card) && th.isSubType(argument[0].getItemType(th), BuiltInAtomicType.NUMERIC)) {
+ return argument[0];
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Determine the item type of the value returned by the function
+ *
+ * @param th the type hierarchy cache
+ * @return the statically inferred type of the expression
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ ItemType t = Atomizer.getAtomizedItemType(argument[0], false, th);
+ if (t.getPrimitiveType() == StandardNames.XS_UNTYPED_ATOMIC) {
+ return BuiltInAtomicType.DOUBLE;
+ } else {
+ return t;
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Minimax m = (Minimax) super.copy();
+ m.argumentType = argumentType;
+ m.ignoreNaN = ignoreNaN;
+ return m;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return this==o;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+ AtomicComparer comparer = getPreAllocatedAtomicComparer();
+ if (comparer == null) {
+ comparer = getAtomicComparer(getCollator(context), context);
+ }
+ SequenceIterator iter = argument[0].iterate(context);
+ try {
+ return minimax(iter, operation, comparer, ignoreNaN, context);
+ } catch (XPathException err) {
+ err.setLocator(this);
+ throw err;
+ }
+ }
+
+ public AtomicComparer getAtomicComparer(XPathContext context) throws XPathException {
+ AtomicComparer comparer = getPreAllocatedAtomicComparer();
+ if (comparer != null) {
+ return comparer;
+ }
+ StringCollator collator = getCollator(context);
+ BuiltInAtomicType type = argumentType;
+ if (type.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ type = BuiltInAtomicType.DOUBLE;
+ }
+ return GenericAtomicComparer.makeAtomicComparer(type, type, collator, context);
+ }
+
+ /**
+ * Static method to evaluate the minimum or maximum of a sequence
+ *
+ * @param iter Iterator over the input sequence
+ * @param operation either {@link #MIN} or {@link #MAX}
+ * @param atomicComparer an AtomicComparer used to compare values
+ * @param ignoreNaN true if NaN values are to be ignored
+ * @param context dynamic evaluation context
+ * @return the min or max value in the sequence, according to the rules of the fn:min() or fn:max() functions
+ * @throws XPathException typically if non-comparable values are found in the sequence
+ */
+ /*@Nullable*/ public static AtomicValue minimax(SequenceIterator iter, int operation,
+ AtomicComparer atomicComparer, boolean ignoreNaN, XPathContext context)
+ throws XPathException {
+
+ TypeHierarchy th = context.getConfiguration().getTypeHierarchy();
+ ConversionRules rules = context.getConfiguration().getConversionRules();
+ StringToDouble converter = context.getConfiguration().getConversionRules().getStringToDoubleConverter();
+ boolean foundDouble = false;
+ boolean foundFloat = false;
+ boolean foundNaN = false;
+
+ // For the max function, reverse the collator
+ if (operation == MAX) {
+ atomicComparer = new DescendingComparer(atomicComparer);
+ }
+ atomicComparer = atomicComparer.provideContext(context);
+
+ // Process the sequence, retaining the min (or max) so far. This will be an actual value found
+ // in the sequence. At the same time, remember if a double and/or float has been encountered
+ // anywhere in the sequence, and if so, convert the min/max to double/float at the end. This is
+ // done to avoid problems if a decimal is converted first to a float and then to a double.
+
+ // Get the first value in the sequence, ignoring any NaN values if we are ignoring NaN values
+ AtomicValue min;
+ AtomicValue prim;
+
+ while (true) {
+ min = (AtomicValue) iter.next();
+ if (min == null) {
+ return null;
+ }
+ prim = min;
+ if (min instanceof UntypedAtomicValue) {
+ try {
+ min = new DoubleValue(converter.stringToNumber(min.getStringValueCS()));
+ prim = min;
+ foundDouble = true;
+ } catch (NumberFormatException e) {
+ XPathException de = new XPathException("Failure converting " + Err.wrap(min.getStringValueCS()) + " to a number");
+ de.setErrorCode("FORG0001");
+ de.setXPathContext(context);
+ throw de;
+ }
+ } else {
+ if (prim instanceof DoubleValue) {
+ foundDouble = true;
+ } else if (prim instanceof FloatValue) {
+ foundFloat = true;
+ }
+ }
+ if (prim.isNaN()) {
+ // if there's a NaN in the sequence, return NaN, unless ignoreNaN is set
+ if (ignoreNaN) {
+ //continue; // ignore the NaN and treat the next item as the first real one
+ } else if (prim instanceof DoubleValue) {
+ return min; // return double NaN
+ } else {
+ // we can't ignore a float NaN, because we might need to promote it to a double NaN
+ foundNaN = true;
+ min = FloatValue.NaN;
+ break;
+ }
+ } else {
+ if (!prim.getPrimitiveType().isOrdered(false)) {
+ XPathException de = new XPathException("Type " + prim.getPrimitiveType() + " is not an ordered type");
+ de.setErrorCode("FORG0006");
+ de.setIsTypeError(true);
+ de.setXPathContext(context);
+ throw de;
+ }
+ break; // process the rest of the sequence
+ }
+ }
+
+ AtomicType lowestCommonSuperType = min.getItemType();
+
+ while (true) {
+ AtomicValue test = (AtomicValue) iter.next();
+ if (test == null) {
+ break;
+ }
+ AtomicValue test2 = test;
+ prim = test2;
+ if (test instanceof UntypedAtomicValue) {
+ try {
+ test2 = new DoubleValue(converter.stringToNumber(test.getStringValueCS()));
+ if (foundNaN) {
+ return DoubleValue.NaN;
+ }
+ prim = test2;
+ foundDouble = true;
+ } catch (NumberFormatException e) {
+ XPathException de = new XPathException("Failure converting " + Err.wrap(test.getStringValueCS()) + " to a number");
+ de.setErrorCode("FORG0001");
+ de.setXPathContext(context);
+ throw de;
+ }
+ } else {
+ if (prim instanceof DoubleValue) {
+ if (foundNaN) {
+ return DoubleValue.NaN;
+ }
+ foundDouble = true;
+ } else if (prim instanceof FloatValue) {
+ foundFloat = true;
+ }
+ }
+ lowestCommonSuperType = (AtomicType) Type.getCommonSuperType(
+ lowestCommonSuperType, prim.getItemType(), th);
+ if (prim.isNaN()) {
+ // if there's a double NaN in the sequence, return NaN, unless ignoreNaN is set
+ if (ignoreNaN) {
+ //continue;
+ } else if (foundDouble) {
+ return DoubleValue.NaN;
+ } else {
+ // can't return float NaN until we know whether to promote it
+ foundNaN = true;
+ }
+ } else {
+ try {
+ if (atomicComparer.compareAtomicValues(prim, min) < 0) {
+ min = test2;
+ }
+ } catch (ClassCastException err) {
+ if (min.getItemType() == test2.getItemType()) {
+ // internal error
+ throw err;
+ } else {
+ XPathException de = new XPathException("Cannot compare " + min.getItemType() + " with " + test2.getItemType());
+ de.setErrorCode("FORG0006");
+ de.setIsTypeError(true);
+ de.setXPathContext(context);
+ throw de;
+ }
+ }
+ }
+ }
+ if (foundNaN) {
+ return FloatValue.NaN;
+ }
+ if (foundDouble) {
+ if (!(min instanceof DoubleValue)) {
+ min = Converter.convert(min, BuiltInAtomicType.DOUBLE, rules);
+ }
+ } else if (foundFloat) {
+ if (!(min instanceof FloatValue)) {
+ min = Converter.convert(min, BuiltInAtomicType.FLOAT, rules);
+ }
+ }
+ if (lowestCommonSuperType == BuiltInAtomicType.NUMERIC || min.getPrimitiveType() == lowestCommonSuperType) {
+ return min;
+ } else {
+ return Converter.convert(min, lowestCommonSuperType, rules);
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Minimax expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new MinimaxCompiler();
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public MinimaxAdjunct getStreamingAdjunct() {
+ return new MinimaxAdjunct();
+ }
+
+ //#endif
+
+}
+
diff --git a/sf/saxon/functions/NameFn.java b/sf/saxon/functions/NameFn.java
new file mode 100644
index 0000000..e26731a
--- /dev/null
+++ b/sf/saxon/functions/NameFn.java
@@ -0,0 +1,91 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NameFnCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * This class supports the name() function
+ */
+
+public class NameFn extends SystemFunctionCall {
+
+ /**
+ * Simplify and validate.
+ *
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ return simplifyArguments(visitor);
+ }
+
+ @Override
+ public int getIntrinsicDependencies() {
+ if (getNumberOfArguments() == 0) {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ } else {
+ return super.getIntrinsicDependencies();
+ }
+ }
+
+
+ /**
+ * Evaluate the function in a string context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo node = (NodeInfo) argument[0].evaluateItem(c);
+ if (node == null) {
+ return StringValue.EMPTY_STRING;
+ }
+ return new StringValue(node.getDisplayName());
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo node = (arguments.length == 0 ? getContextNode(context) : (NodeInfo)arguments[0].head());
+ if (node == null) {
+ return StringValue.EMPTY_STRING;
+ } else {
+ return new StringValue(node.getDisplayName());
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NameFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NameFnCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/NamespaceForPrefix.java b/sf/saxon/functions/NamespaceForPrefix.java
new file mode 100644
index 0000000..e77baf5
--- /dev/null
+++ b/sf/saxon/functions/NamespaceForPrefix.java
@@ -0,0 +1,78 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.value.AnyURIValue;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.StringValue;
+
+
+/**
+* This class supports the function namespace-uri-for-prefix()
+*/
+
+public class NamespaceForPrefix extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate the function
+ * @param context the XPath dynamic context
+ * @return the URI corresponding to the prefix supplied in the first argument, or null
+ * if the prefix is not in scope
+ * @throws XPathException if a failure occurs evaluating the arguments
+ */
+
+ public AnyURIValue evaluateItem(XPathContext context) throws XPathException {
+ NodeInfo element = (NodeInfo)argument[1].evaluateItem(context);
+ StringValue p = (StringValue)argument[0].evaluateItem(context);
+ return namespaceUriForPrefix(p, element);
+ }
+
+
+ /**
+ * Evaluate the expression
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ AnyURIValue result = namespaceUriForPrefix((StringValue)arguments[0].head(), (NodeInfo)arguments[1].head());
+ return (result==null ? EmptySequence.getInstance() : result);
+ }
+
+ /**
+ * Private supporting method
+ * @param p the prefix
+ * @param element the element node
+ * @return the corresponding namespace, or null if not in scope
+ */
+
+ /*@Nullable*/ private static AnyURIValue namespaceUriForPrefix(StringValue p, NodeInfo element) {
+ String prefix;
+ if (p == null) {
+ prefix = "";
+ } else {
+ prefix = p.getStringValue();
+ }
+ NamespaceResolver resolver = new InscopeNamespaceResolver(element);
+ String uri = resolver.getURIForPrefix(prefix, true);
+ if (uri == null || uri.length() == 0) {
+ return null;
+ }
+ return new AnyURIValue(uri);
+ }
+
+}
+
diff --git a/sf/saxon/functions/NamespaceUriFn.java b/sf/saxon/functions/NamespaceUriFn.java
new file mode 100644
index 0000000..33677be
--- /dev/null
+++ b/sf/saxon/functions/NamespaceUriFn.java
@@ -0,0 +1,91 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NamespaceUriFnCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AnyURIValue;
+
+/**
+ * This class supports the namespace-uri() function
+ */
+
+public class NamespaceUriFn extends SystemFunctionCall {
+
+ /**
+ * Simplify and validate.
+ *
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ return simplifyArguments(visitor);
+ }
+
+ @Override
+ public int getIntrinsicDependencies() {
+ if (getNumberOfArguments() == 0) {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ } else {
+ return super.getIntrinsicDependencies();
+ }
+ }
+
+
+ /**
+ * Evaluate the function in a string context
+ */
+
+ public AnyURIValue evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo node = (NodeInfo) argument[0].evaluateItem(c);
+ if (node == null) {
+ return AnyURIValue.EMPTY_URI;
+ }
+ return new AnyURIValue(node.getURI());
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo node = (arguments.length == 0 ? getContextNode(context) : (NodeInfo)arguments[0].head());
+ if (node == null) {
+ return AnyURIValue.EMPTY_URI;
+ } else {
+ return new AnyURIValue(node.getURI());
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NamespaceUriFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NamespaceUriFnCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/Nilled.java b/sf/saxon/functions/Nilled.java
new file mode 100644
index 0000000..362a931
--- /dev/null
+++ b/sf/saxon/functions/Nilled.java
@@ -0,0 +1,107 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+* This class supports the nilled() function
+*/
+
+public class Nilled extends SystemFunctionCall implements Callable {
+
+
+ /**
+ * Simplify and validate.
+ *
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ return simplifyArguments(visitor);
+ }
+
+ @Override
+ public int getIntrinsicDependencies() {
+ if (getNumberOfArguments() == 0) {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ } else {
+ return super.getIntrinsicDependencies();
+ }
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public BooleanValue evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo node;
+ node = (NodeInfo)argument[0].evaluateItem(c);
+ return getNilledProperty(node);
+ }
+
+ /**
+ * Determine whether a node has the nilled property
+ * @param node the node in question (if null, the function returns null)
+ * @return the value of the nilled accessor. Returns null for any node other than an
+ * element node. For an element node, returns true if the element has been validated and
+ * has an xsi:nil attribute whose value is true.
+ */
+
+ /*@Nullable*/ public static BooleanValue getNilledProperty(NodeInfo node) {
+ // TODO: if the type annotation is ANYTYPE, we need to keep an extra bit to represent the nilled
+ // property: it will be set only if validation has been performed. A newly-constructed element using
+ // validation="preserve" has nilled=false even if xsi:nil = true
+ if (node==null || node.getNodeKind() != Type.ELEMENT) {
+ return null;
+ }
+ return BooleanValue.get(node.isNilled());
+ }
+
+ /**
+ * Determine whether a node is nilled. Returns true if the value
+ * of the nilled property is true; false if the value is false or absent
+ */
+
+ public static boolean isNilled(NodeInfo node) {
+ BooleanValue b = getNilledProperty(node);
+ return b != null && b.getBooleanValue();
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+
+ NodeInfo node = getDefaultArgumentNode(context, arguments, "fn:nilled()");
+ if (node==null || node.getNodeKind() != Type.ELEMENT) {
+ return EmptySequence.getInstance();
+ }
+ return BooleanValue.get(isNilled(node));
+ }
+}
diff --git a/sf/saxon/functions/NodeNameFn.java b/sf/saxon/functions/NodeNameFn.java
new file mode 100644
index 0000000..c59916a
--- /dev/null
+++ b/sf/saxon/functions/NodeNameFn.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NodeNameFnCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.QNameValue;
+
+/**
+* This class supports the node-name() function
+*/
+
+public class NodeNameFn extends SystemFunctionCall {
+
+ /**
+ * Simplify and validate.
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ return simplifyArguments(visitor);
+ }
+
+ @Override
+ public int getIntrinsicDependencies() {
+ if (getNumberOfArguments() == 0) {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ } else {
+ return super.getIntrinsicDependencies();
+ }
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ /*@Nullable*/ public QNameValue evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo node = (NodeInfo)argument[0].evaluateItem(c);
+ return nodeName(node);
+ }
+
+ public static QNameValue nodeName(NodeInfo node) {
+ if (node==null) {
+ return null;
+ }
+ int nc = node.getNameCode();
+ if (nc == -1) {
+ return null;
+ }
+ return new QNameValue(node.getNamePool(), nc);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo node = (arguments.length == 0 ? getContextNode(context) : (NodeInfo)arguments[0].head());
+ QNameValue name = nodeName(node);
+ return (name == null ? EmptySequence.getInstance() : name);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NodeNameFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NodeNameFnCompiler();
+ }
+//#endif
+
+
+}
+
diff --git a/sf/saxon/functions/NormalizeSpace_0.java b/sf/saxon/functions/NormalizeSpace_0.java
new file mode 100644
index 0000000..5c5fe16
--- /dev/null
+++ b/sf/saxon/functions/NormalizeSpace_0.java
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NormalizeSpaceCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+/**
+ * Implement the XPath normalize-space() function with zero arguments
+ */
+
+public class NormalizeSpace_0 extends SystemFunctionCall implements Callable {
+
+ /**
+ * Determine the intrinsic dependencies of an expression, that is, those which are not derived
+ * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
+ * on the context position, while (position()+1) does not. The default implementation
+ * of the method returns 0, indicating "no dependencies".
+ *
+ * @return a set of bit-significant flags identifying the "intrinsic"
+ * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (contextItemType == null) {
+ XPathException err = new XPathException("The context item for normalize-space() is absent");
+ err.setErrorCode("XPDY0002");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ return super.typeCheck(visitor, contextItemType);
+ }
+
+ /**
+ * Pre-evaluate a function at compile time. Functions that do not allow
+ * pre-evaluation, or that need access to context information, can override this method.
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ Item item = c.getContextItem();
+ if (item == null) {
+ dynamicError("Context item for normalize-space() is absent", "XPDY0002", c);
+ return null;
+ }
+ return StringValue.makeStringValue(
+ Whitespace.collapseWhitespace(item.getStringValueCS()));
+
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ *
+ * <p>This method is implemented for normalize-space() because it is quite often used in a
+ * boolean context to test whether a value exists and is non-white, and because testing for the
+ * presence of non-white characters is a lot more efficient than constructing the normalized
+ * string, especially because of early-exit.</p>
+ *
+ * @param c The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext c) throws XPathException {
+ Item item = c.getContextItem();
+ if (item == null) {
+ dynamicError("Context item for normalize-space() is absent", "XPDY0002", c);
+ return false;
+ }
+ return !Whitespace.isWhite(item.getStringValueCS());
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return evaluateItem(context);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NormalizeSpace_0 expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NormalizeSpaceCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/NormalizeSpace_1.java b/sf/saxon/functions/NormalizeSpace_1.java
new file mode 100644
index 0000000..fa51f60
--- /dev/null
+++ b/sf/saxon/functions/NormalizeSpace_1.java
@@ -0,0 +1,139 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NormalizeSpaceCompiler;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+/**
+ * Implement the XPath normalize-space() function
+ */
+
+public class NormalizeSpace_1 extends SystemFunctionCall implements Callable {
+
+ /**
+ * Determine the intrinsic dependencies of an expression, that is, those which are not derived
+ * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
+ * on the context position, while (position()+1) does not. The default implementation
+ * of the method returns 0, indicating "no dependencies".
+ *
+ * @return a set of bit-significant flags identifying the "intrinsic"
+ * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
+ */
+
+ public int getIntrinsicDependencies() {
+ int d = super.getIntrinsicDependencies();
+ if (argument.length == 0) {
+ d |= StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ }
+ return d;
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (argument.length == 0 && contextItemType == null) {
+ XPathException err = new XPathException("The context item for normalize-space() is absent");
+ err.setErrorCode("XPDY0002");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ }
+ return super.typeCheck(visitor, contextItemType);
+ }
+
+ /**
+ * Pre-evaluate a function at compile time. Functions that do not allow
+ * pre-evaluation, or that need access to context information, can override this method.
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ if (argument.length == 0) {
+ return this;
+ } else {
+ return Literal.makeLiteral((AtomicValue)evaluateItem(
+ visitor.getStaticContext().makeEarlyEvaluationContext()));
+ }
+ }
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public Item evaluateItem(XPathContext c) throws XPathException {
+ StringValue sv = (StringValue)argument[0].evaluateItem(c);
+ return normalizeSpace(sv);
+ }
+
+ public static StringValue normalizeSpace(StringValue sv) {
+ if (sv==null) {
+ return StringValue.EMPTY_STRING;
+ }
+ return StringValue.makeStringValue(
+ Whitespace.collapseWhitespace(sv.getStringValueCS()));
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ *
+ * <p>This method is implemented for normalize-space() because it is quite often used in a
+ * boolean context to test whether a value exists and is non-white, and because testing for the
+ * presence of non-white characters is a lot more efficient than constructing the normalized
+ * string, especially because of early-exit.</p>
+ *
+ * @param c The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext c) throws XPathException {
+ AtomicValue sv = (AtomicValue)argument[0].evaluateItem(c);
+ if (sv==null) {
+ return false;
+ }
+ CharSequence cs = sv.getStringValueCS();
+ return !Whitespace.isWhite(cs);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return normalizeSpace((StringValue)arguments[0].head());
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NormalizeSpace_1 expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NormalizeSpaceCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/NormalizeUnicode.java b/sf/saxon/functions/NormalizeUnicode.java
new file mode 100644
index 0000000..37ff286
--- /dev/null
+++ b/sf/saxon/functions/NormalizeUnicode.java
@@ -0,0 +1,104 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.serialize.codenorm.Normalizer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CompressedWhitespace;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+/**
+ * Implement the XPath normalize-unicode() function
+ */
+
+public class NormalizeUnicode extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ StringValue sv = (StringValue)argument[0].evaluateItem(c);
+ if (sv==null) {
+ return StringValue.EMPTY_STRING;
+ }
+ String form = (argument.length == 1 ? "NFC" : Whitespace.trim(argument[1].evaluateAsString(c)));
+ return normalize(sv, form, c);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments /*@NotNull*/) throws XPathException {
+ StringValue sv = (StringValue)arguments[0].head();
+ if(sv == null){
+ return StringValue.EMPTY_STRING;
+ }
+ String nf = (arguments.length == 1 ? "NFC" : Whitespace.trim(arguments[1].head().getStringValue()));
+ return normalize(sv, nf, context);
+ }
+
+ public StringValue normalize(StringValue sv, String form, XPathContext c) throws XPathException {
+ byte fb = Normalizer.C;
+ if (argument.length == 2) {
+ if (form.equalsIgnoreCase("NFC")) {
+ fb = Normalizer.C;
+ } else if (form.equalsIgnoreCase("NFD")) {
+ fb = Normalizer.D;
+ } else if (form.equalsIgnoreCase("NFKC")) {
+ fb = Normalizer.KC;
+ } else if (form.equalsIgnoreCase("NFKD")) {
+ fb = Normalizer.KD;
+ } else if (form.length() == 0) {
+ return sv;
+ } else {
+ String msg = "Normalization form " + form + " is not supported";
+ XPathException err = new XPathException(msg);
+ err.setErrorCode("FOCH0003");
+ err.setXPathContext(c);
+ err.setLocator(this);
+ throw err;
+ }
+ }
+
+ // fast path for ASCII strings: normalization is a no-op
+ boolean allASCII = true;
+ CharSequence chars = sv.getStringValueCS();
+ if (chars instanceof CompressedWhitespace) {
+ return sv;
+ }
+ for (int i=chars.length()-1; i>=0; i--) {
+ if (chars.charAt(i) > 127) {
+ allASCII = false;
+ break;
+ }
+ }
+ if (allASCII) {
+ return sv;
+ }
+
+
+ Normalizer norm = new Normalizer(fb, c.getConfiguration());
+ CharSequence result = norm.normalize(sv.getStringValueCS());
+ return StringValue.makeStringValue(result);
+ }
+
+}
+
diff --git a/sf/saxon/functions/NotFn.java b/sf/saxon/functions/NotFn.java
new file mode 100644
index 0000000..2b3dd9a
--- /dev/null
+++ b/sf/saxon/functions/NotFn.java
@@ -0,0 +1,144 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NotFnCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Negatable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+
+/**
+* This class supports the XPath functions boolean(), not(), true(), and false()
+*/
+
+
+public class NotFn extends SystemFunctionCall implements Negatable {
+
+ /**
+ * Static analysis: prevent sorting of the argument
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ XPathException err = TypeChecker.ebvError(argument[0], visitor.getConfiguration().getTypeHierarchy());
+ if (err != null) {
+ err.setLocator(this);
+ throw err;
+ }
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ argument[0] = ExpressionTool.unsortedIfHomogeneous(opt, argument[0]);
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e == this) {
+ Expression ebv = BooleanFn.rewriteEffectiveBooleanValue(argument[0], visitor, contextItemType);
+ if (ebv != null) {
+ argument[0] = ebv;
+ }
+ if (argument[0] instanceof Negatable && ((Negatable)argument[0]).isNegatable(visitor)) {
+ return ((Negatable)argument[0]).negate().optimize(visitor, contextItemType);
+ }
+ return this;
+ }
+ return e;
+ }
+
+ /**
+ * Check whether this specific instance of the expression is negatable
+ *
+ * @return true if it is
+ */
+
+ public boolean isNegatable(ExpressionVisitor visitor) {
+ return true;
+ }
+
+ /**
+ * Create an expression that returns the negation of this expression
+ * @return the negated expression
+ */
+
+ public Expression negate() {
+ return SystemFunctionCall.makeSystemFunction("boolean", getArguments());
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Evaluate the effective boolean value
+ */
+
+ public boolean effectiveBooleanValue(XPathContext c) throws XPathException {
+ try {
+ return !argument[0].effectiveBooleanValue(c);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(c);
+ throw e;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return BooleanValue.get(!ExpressionTool.effectiveBooleanValue(arguments[0].iterate()));
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NotFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NotFnCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/NumberFn.java b/sf/saxon/functions/NumberFn.java
new file mode 100644
index 0000000..7a65be9
--- /dev/null
+++ b/sf/saxon/functions/NumberFn.java
@@ -0,0 +1,196 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.NumberFnCompiler;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * Implements the XPath number() function.
+ */
+
+public class NumberFn extends SystemFunctionCall implements Callable {
+
+ /**
+ * Simplify and validate.
+ * This is a pure function so it can be simplified in advance if the arguments are known
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ argument[0].setFlattened(true);
+ return simplifyArguments(visitor);
+ }
+
+ /**
+ * Type-check the expression. This also calls preEvaluate() to evaluate the function
+ * if all the arguments are constant; functions that do not require this behavior
+ * can override the preEvaluate method.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e2 = super.typeCheck(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ if (argument[0] instanceof NumberFn) {
+ // happens through repeated rewriting
+ argument[0] = ((NumberFn)argument[0]).argument[0];
+ }
+ return this;
+ }
+
+ /**
+ * Add a representation of a doc() call or similar function to a PathMap.
+ * This is a convenience method called by the addToPathMap() methods for doc(), document(), collection()
+ * and similar functions. These all create a new root expression in the path map.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodes the node in the PathMap representing the focus at the point where this expression
+ * is called. Set to null if this expression appears at the top level.
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ /*@Nullable*/ public PathMap.PathMapNodeSet addDocToPathMap(/*@NotNull*/ PathMap pathMap, PathMap.PathMapNodeSet pathMapNodes) {
+ PathMap.PathMapNodeSet result = argument[0].addToPathMap(pathMap, pathMapNodes);
+ if (result != null) {
+ result.setAtomized();
+ }
+ return null;
+ }
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public DoubleValue evaluateItem(XPathContext context) throws XPathException {
+ Item arg0 = argument[0].evaluateItem(context);
+ if (arg0==null) {
+ return DoubleValue.NaN;
+ }
+ ConversionRules rules = context.getConfiguration().getConversionRules();
+ if (arg0 instanceof BooleanValue) {
+ return (DoubleValue)Converter.BOOLEAN_TO_DOUBLE.convert(((AtomicValue) arg0)).asAtomic();
+ } else if (arg0 instanceof NumericValue) {
+ return (DoubleValue)Converter.NUMERIC_TO_DOUBLE.convert(((AtomicValue) arg0)).asAtomic();
+ } else if (arg0 instanceof StringValue && !(arg0 instanceof AnyURIValue)) {
+ StringConverter sc = rules.getStringConverter(BuiltInAtomicType.DOUBLE);
+ ConversionResult cr = sc.convert((AtomicValue)arg0);
+ if (cr instanceof ValidationFailure) {
+ return DoubleValue.NaN;
+ } else {
+ return (DoubleValue)cr;
+ }
+ } else {
+ return DoubleValue.NaN;
+ }
+ }
+
+ /**
+ * Static method to perform the same conversion as the number() function. This is different from the
+ * convert(Type.DOUBLE) in that it produces NaN rather than an error for non-numeric operands.
+ * @param value the value to be converted
+ * @param config
+ * @return the result of the conversion
+ */
+
+ public static DoubleValue convert(AtomicValue value, Configuration config) {
+ try {
+ if (value==null) {
+ return DoubleValue.NaN;
+ }
+ if (value instanceof BooleanValue) {
+ return new DoubleValue(((BooleanValue)value).getBooleanValue() ? 1.0e0 : 0.0e0);
+ }
+ if (value instanceof DoubleValue) {
+ return ((DoubleValue)value);
+ }
+ if (value instanceof NumericValue) {
+ return new DoubleValue(((NumericValue)value).getDoubleValue());
+ }
+ if (value instanceof StringValue && !(value instanceof AnyURIValue)) {
+ double d = config.getConversionRules().getStringToDoubleConverter().stringToNumber(value.getStringValueCS());
+ return new DoubleValue(d);
+ }
+ return DoubleValue.NaN;
+ } catch (NumberFormatException e) {
+ return DoubleValue.NaN;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ AtomicValue in;
+ if (arguments.length == 0) {
+ Item c = context.getContextItem();
+ if (c == null) {
+ throw new XPathException("Context item for number() is absent");
+ }
+ if (c instanceof AtomicValue) {
+ in = (AtomicValue)c;
+ } else if (c instanceof NodeInfo) {
+ AtomicSequence as = ((NodeInfo)c).atomize();
+ if (as.getLength() == 0) {
+ return DoubleValue.NaN;
+ } else if (as.getLength() == 1) {
+ in = as.head();
+ } else {
+ throw new XPathException("Atomization of context item for number() is a sequence containing more than one item", "XPTY0004");
+ }
+ } else {
+ throw new XPathException("Context item for number() cannot be atomized", "XPTY0004");
+ }
+ } else {
+ in = (AtomicValue)arguments[0].head();
+ if (in == null) {
+ return EmptySequence.getInstance();
+ }
+ }
+ return convert(in, context.getConfiguration());
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the NumberFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new NumberFnCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/Position.java b/sf/saxon/functions/Position.java
new file mode 100644
index 0000000..66a9ba5
--- /dev/null
+++ b/sf/saxon/functions/Position.java
@@ -0,0 +1,128 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.PositionCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+
+
+public class Position extends SystemFunctionCall {
+
+ boolean contextPossiblyUndefined = true;
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return new IntegerValue[]{Int64Value.PLUS_ONE, MAX_SEQUENCE_LENGTH};
+ }
+
+ /*@NotNull*/
+ @Override
+ public Expression typeCheck(ExpressionVisitor visitor, /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (contextItemType == null || contextItemType.itemType == null) {
+ XPathException err = new XPathException("The context item is undefined at this point");
+ err.setErrorCode("XPDY0002");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ } else {
+ contextPossiblyUndefined = contextItemType.contextMaybeUndefined;
+ }
+ return super.typeCheck(visitor, contextItemType);
+ }
+
+ /**
+ * Promote this expression if possible
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Ask whether the context item may possibly be undefined
+ * @return true if it might be undefined
+ */
+
+ public boolean isContextPossiblyUndefined() {
+ return contextPossiblyUndefined;
+ }
+
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public Int64Value evaluateItem(XPathContext c) throws XPathException {
+ SequenceIterator currentIterator = c.getCurrentIterator();
+ if (currentIterator == null) {
+ XPathException e = new XPathException("The context item is absent, so position() is undefined");
+ e.setXPathContext(c);
+ e.setErrorCode("XPDY0002");
+ throw e;
+ }
+ return Int64Value.makeIntegerValue(c.getCurrentIterator().position());
+ }
+
+ /**
+ * Determine the intrinsic dependencies
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_POSITION;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return evaluateItem(context);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Position expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new PositionCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/Put.java b/sf/saxon/functions/Put.java
new file mode 100644
index 0000000..b9ea8c6
--- /dev/null
+++ b/sf/saxon/functions/Put.java
@@ -0,0 +1,128 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.InterpretedExpressionCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.PendingUpdateList;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Implements the fn:put() function in XQuery Update 1.0.
+ */
+public class Put extends SystemFunctionCall {
+
+ /*@Nullable*/ String expressionBaseURI = null;
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ if (expressionBaseURI == null) {
+ super.checkArguments(visitor);
+ expressionBaseURI = visitor.getStaticContext().getBaseURI();
+ if (expressionBaseURI == null && argument.length == 1) {
+ XPathException de = new XPathException("Base URI in static context of put() is unknown");
+ de.setErrorCode("FONS0005");
+ throw de;
+ }
+ }
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Put d = (Put)super.copy();
+ d.expressionBaseURI = expressionBaseURI;
+ return d;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof Put) &&
+ super.equals(o) &&
+ equalOrNull(expressionBaseURI, (((Put)o).expressionBaseURI));
+ }
+
+ /**
+ * Determine whether this is an updating expression as defined in the XQuery update specification
+ * @return true if this is an updating expression
+ */
+
+ public boolean isUpdatingExpression() {
+ return true;
+ }
+
+ /**
+ * Evaluate an updating expression, adding the results to a Pending Update List.
+ * The default implementation of this method, which is used for non-updating expressions,
+ * throws an UnsupportedOperationException
+ * @param context the XPath dynamic evaluation context
+ * @param pul the pending update list to which the results should be written
+ */
+
+ public void evaluatePendingUpdates(XPathContext context, PendingUpdateList pul) throws XPathException {
+ NodeInfo node = (NodeInfo)argument[0].evaluateItem(context);
+ int kind = node.getNodeKind();
+ if (kind != Type.ELEMENT && kind != Type.DOCUMENT) {
+ dynamicError("Node in put() must be a document or element node",
+ "FOUP0001", context);
+ }
+ String relative = argument[1].evaluateItem(context).getStringValue();
+ String abs;
+ try {
+ URI resolved = ResolveURI.makeAbsolute(relative, expressionBaseURI);
+ abs = resolved.toString();
+ } catch (URISyntaxException err) {
+ dynamicError("Base URI " + Err.wrap(expressionBaseURI) + " is invalid: " + err.getMessage(),
+ "FOUP0002", context);
+ abs = null;
+ }
+ pul.addPutAction(node, abs, this);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ throw new XPathException("Dynamic evaluation of fn:put() is not supported");
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Put expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new InterpretedExpressionCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/QNameFn.java b/sf/saxon/functions/QNameFn.java
new file mode 100644
index 0000000..7cc5d4e
--- /dev/null
+++ b/sf/saxon/functions/QNameFn.java
@@ -0,0 +1,143 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.QNameFnCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.QNameException;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.value.QNameValue;
+import net.sf.saxon.value.StringValue;
+
+
+/**
+* This class supports the fn:QName() function (previously named fn:expanded-QName())
+*/
+
+public class QNameFn extends SystemFunctionCall {
+
+ /**
+ * Pre-evaluate a function at compile time. Functions that do not allow
+ * pre-evaluation, or that need access to context information, can override this method.
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ try {
+ XPathContext early = visitor.getStaticContext().makeEarlyEvaluationContext();
+ final Item item1 = argument[1].evaluateItem(early);
+ final String lex = item1.getStringValue();
+ final Item item0 = argument[0].evaluateItem(early);
+ String uri;
+ if (item0 == null) {
+ uri = "";
+ } else {
+ uri = item0.getStringValue();
+ }
+ final NameChecker checker = visitor.getConfiguration().getNameChecker();
+ final String[] parts = checker.getQNameParts(lex);
+ // The QNameValue constructor does not check the prefix
+ if (parts[0].length() != 0 && !checker.isValidNCName(parts[0])) {
+ XPathException err = new XPathException("Malformed prefix in QName: '" + parts[0] + '\'');
+ err.setErrorCode("FOCA0002");
+ throw err;
+ }
+ return Literal.makeLiteral(
+ new QNameValue(parts[0], uri, parts[1], BuiltInAtomicType.QNAME, checker));
+ } catch (QNameException e) {
+ XPathException err = new XPathException(e.getMessage(), this);
+ err.setErrorCode("FOCA0002");
+ err.setLocator(this);
+ throw err;
+ } catch (XPathException err) {
+ if (err.getErrorCodeLocalPart().equals("FORG0001")) {
+ err.setErrorCode("FOCA0002");
+ }
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ /*@Nullable*/ public QNameValue evaluateItem(XPathContext context) throws XPathException {
+ StringValue arg0 = (StringValue)argument[0].evaluateItem(context);
+ StringValue arg1 = (StringValue)argument[1].evaluateItem(context);
+ return expandedQName(arg0, arg1, context.getConfiguration().getNameChecker());
+ }
+
+ public QNameValue expandedQName(StringValue namespace, StringValue lexical, NameChecker checker) throws XPathException {
+
+ String uri;
+ if (namespace == null) {
+ uri = null;
+ } else {
+ uri = namespace.getStringValue();
+ }
+
+ try {
+ final String lex = lexical.getStringValue();
+ final String[] parts = checker.getQNameParts(lex);
+ // The QNameValue constructor does not check the prefix
+ if (parts[0].length() != 0 && !checker.isValidNCName(parts[0])) {
+ XPathException err = new XPathException("Malformed prefix in QName: '" + parts[0] + '\'');
+ err.setErrorCode("FOCA0002");
+ throw err;
+ }
+ return new QNameValue(parts[0], uri, parts[1], BuiltInAtomicType.QNAME, checker);
+ } catch (QNameException e) {
+ throw new XPathException(e.getMessage(), "FOCA0002");
+ } catch (XPathException err) {
+ if (err.getErrorCodeLocalPart().equals("FORG0001")) {
+ err.setErrorCode("FOCA0002");
+ }
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return expandedQName(
+ (StringValue)arguments[0].head(),
+ (StringValue)arguments[1].head(),
+ context.getConfiguration().getNameChecker());
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the QNameFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new QNameFnCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/RegexGroup.java b/sf/saxon/functions/RegexGroup.java
new file mode 100644
index 0000000..b23395f
--- /dev/null
+++ b/sf/saxon/functions/RegexGroup.java
@@ -0,0 +1,83 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.RegexGroupCompiler;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.regex.RegexIterator;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.NumericValue;
+import net.sf.saxon.value.StringValue;
+
+
+public class RegexGroup extends SystemFunctionCall {
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ AtomicValue gp0 = (AtomicValue)argument[0].evaluateItem(c);
+ int gp = (int)((NumericValue)gp0).longValue();
+ return StringValue.makeStringValue(getGroup(gp, c));
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NumericValue gp0 = (NumericValue)arguments[0].head();
+ int gp = (int)(gp0.longValue());
+ return StringValue.makeStringValue(getGroup(gp, context));
+ }
+
+ /*@Nullable*/ public static String getGroup(int gp, XPathContext c) {
+ RegexIterator iter = c.getCurrentRegexIterator();
+ if (iter == null) {
+ return "";
+ }
+ String s = iter.getRegexGroup(gp);
+ if (s == null) {
+ return "";
+ }
+ return s;
+ }
+
+ /**
+ * Determine the dependencies
+ */
+
+ public int getIntrinsicDependencies() {
+ return StaticProperty.DEPENDS_ON_REGEX_GROUP;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the RegexGroup expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new RegexGroupCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/Remove.java b/sf/saxon/functions/Remove.java
new file mode 100644
index 0000000..9dc43e9
--- /dev/null
+++ b/sf/saxon/functions/Remove.java
@@ -0,0 +1,244 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.NumericValue;
+
+/**
+* The XPath 2.0 remove() function
+*/
+
+public class Remove extends SystemFunctionCall implements Callable {
+
+ /**
+ * Simplify. Recognize remove(seq, 1) as a TailExpression.
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ Expression exp = super.simplify(visitor);
+ if (exp instanceof Remove) {
+ return ((Remove)exp).simplifyAsTailExpression();
+ } else {
+ return exp;
+ }
+ }
+
+ /**
+ * Simplify. Recognize remove(seq, 1) as a TailExpression. This
+ * is worth doing because tail expressions used in a recursive call
+ * are handled specially.
+ */
+
+ private Expression simplifyAsTailExpression() {
+ if (Literal.isAtomic(argument[1])) {
+ try {
+ long value = ((IntegerValue)((Literal)argument[1]).getValue()).longValue();
+ if (value <= 0) {
+ return argument[0];
+ } else if (value == 1) {
+ TailExpression t = new TailExpression(argument[0], 2);
+ ExpressionTool.copyLocationInfo(this, t);
+ return t;
+ }
+ } catch (XPathException err) {
+ return this;
+ }
+ }
+ return this;
+ }
+
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e == this) {
+ return simplifyAsTailExpression();
+ }
+ return e;
+ }
+
+ /**
+ * Determine the data type of the items in the sequence
+ * @return the type of the input sequence
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return argument[0].getItemType(th);
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return argument[0].getIntegerBounds();
+ }
+
+ /**
+ * Evaluate the function to return an iteration of selected nodes.
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ SequenceIterator seq = argument[0].iterate(context);
+ NumericValue n = (NumericValue)argument[1].evaluateItem(context);
+ int pos = (int)n.longValue();
+ if (pos < 1) {
+ return seq;
+ }
+ return new RemoveIterator(seq, pos);
+ }
+
+ /**
+ * Evaluate the expression as a general function call
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NumericValue n = (NumericValue)arguments[1].head();
+ int pos = (int)n.longValue();
+ if (pos < 1) {
+ return arguments[0];
+ }
+ return SequenceTool.toLazySequence(new RemoveIterator(arguments[0].iterate(), pos));
+ }
+
+ /**
+ * An implementation of SequenceIterator that returns all items except the one
+ * at a specified position.
+ */
+
+ public static class RemoveIterator implements SequenceIterator, LastPositionFinder {
+
+ SequenceIterator base;
+ int removePosition;
+ int position = 0;
+ Item current = null;
+
+ public RemoveIterator(SequenceIterator base, int removePosition) {
+ this.base = base;
+ this.removePosition = removePosition;
+ }
+
+ public Item next() throws XPathException {
+ current = base.next();
+ if (current != null && base.position() == removePosition) {
+ current = base.next();
+ }
+ if (current == null) {
+ position = -1;
+ } else {
+ position++;
+ }
+ return current;
+ }
+
+ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Get the last position (that is, the number of items in the sequence). This method is
+ * non-destructive: it does not change the state of the iterator.
+ * The result is undefined if the next() method of the iterator has already returned null.
+ */
+
+ public int getLength() throws XPathException {
+ if (base instanceof LastPositionFinder) {
+ int x = ((LastPositionFinder)base).getLength();
+ if (removePosition >= 1 && removePosition <= x) {
+ return x - 1;
+ } else {
+ return x;
+ }
+ } else {
+ // This shouldn't happen, because this iterator only has the LAST_POSITION_FINDER property
+ // if the base iterator has the LAST_POSITION_FINDER property
+ throw new AssertionError("base of removeIterator is not a LastPositionFinder");
+ }
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new RemoveIterator( base.getAnother(),
+ removePosition);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link SequenceIterator#GROUNDED}, {@link SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return base.getProperties() & LAST_POSITION_FINDER;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/functions/Replace.java b/sf/saxon/functions/Replace.java
new file mode 100644
index 0000000..4aa950f
--- /dev/null
+++ b/sf/saxon/functions/Replace.java
@@ -0,0 +1,246 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.regex.RegularExpression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.StringValue;
+
+import java.util.regex.PatternSyntaxException;
+
+
+/**
+* This class implements the replace() function for replacing
+* substrings that match a regular expression
+*/
+
+public class Replace extends SystemFunctionCall implements Callable {
+
+ /*@Nullable*/ private RegularExpression regexp;
+ private boolean allow30features = false;
+ private boolean replacementChecked = false;
+
+ /**
+ * Simplify and validate.
+ * This is a pure function so it can be simplified in advance if the arguments are known
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ allow30features = DecimalValue.THREE.equals(visitor.getStaticContext().getXPathLanguageLevel());
+ Expression e = simplifyArguments(visitor);
+ if (e == this) {
+ maybePrecompile(visitor);
+ }
+ return e;
+ }
+
+ private void maybePrecompile(ExpressionVisitor visitor) throws XPathException {
+ // compile the regular expression once if possible
+ if (regexp == null) {
+ try {
+ regexp = Matches.tryToCompile(argument, 1, 3, visitor.getStaticContext());
+ } catch (XPathException err) {
+ err.setLocator(this);
+ throw err;
+ }
+
+ // check that it's not a pattern that matches ""
+ if (regexp != null && regexp.matches("")) {
+ XPathException err = new XPathException("The regular expression in replace() must not be one that matches a zero-length string");
+ err.setErrorCode("FORX0003");
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ }
+
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ // try once again to compile the regular expression once if possible
+ // (used when the regex has been identified as a constant as a result of earlier rewrites)
+ if (e == this) {
+ maybePrecompile(visitor);
+ }
+ if (argument[2] instanceof StringLiteral) {
+ // Do early checking of the replacement expression if known statically
+ String rep = ((StringLiteral)argument[2]).getStringValue();
+ String msg = checkReplacement(rep);
+ replacementChecked = true;
+ if (msg != null) {
+ XPathException ex = new XPathException(msg, "FORX0004");
+ ex.setLocator(this);
+ return new ErrorExpression(ex);
+ }
+ }
+ return e;
+ }
+
+ /**
+ * Get the compiled regular expression if available, otherwise return null
+ * @return the compiled regex, or null
+ */
+
+ public RegularExpression getCompiledRegularExpression() {
+ return regexp;
+ }
+
+ /**
+ * Evaluate the function in a string context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ return eval((StringValue)argument[0].evaluateItem(c),
+ (StringValue)argument[1].evaluateItem(c),
+ (StringValue)argument[2].evaluateItem(c),
+ (argument.length==3 ? null : (StringValue)argument[3].evaluateItem(c)), c);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return eval((StringValue)arguments[0].head(), (StringValue)arguments[1].head(), (StringValue)arguments[2].head(),
+ (arguments.length == 3 ? null : (StringValue)arguments[3].head()), context);
+ }
+
+ /**
+ * Internal method that does the work
+ * @param inputArg
+ * @param regexArg
+ * @param replaceArg
+ * @param flagsArg
+ * @param context
+ * @return
+ * @throws XPathException
+ */
+
+ private StringValue eval(StringValue inputArg, StringValue regexArg,
+ StringValue replaceArg, StringValue flagsArg, XPathContext context) throws XPathException {
+
+ if (inputArg==null) {
+ inputArg = StringValue.EMPTY_STRING;
+ }
+
+ CharSequence replacement = replaceArg.getStringValueCS();
+ if (!replacementChecked) {
+ // if it is a string literal, the check was done at compile time
+ String msg = checkReplacement(replacement);
+ if (msg != null) {
+ dynamicError(msg, "FORX0004", context);
+ }
+ }
+
+ RegularExpression re = regexp;
+ if (re == null) {
+
+ CharSequence flags;
+
+ if (flagsArg == null) {
+ flags = "";
+ } else {
+ flags = flagsArg.getStringValueCS();
+ }
+
+ try {
+ re = Configuration.getPlatform().compileRegularExpression(
+ regexArg.getStringValueCS(), flags.toString(), (allow30features ? "XP30" : "XP20"), null);
+
+ } catch (XPathException err) {
+ XPathException de = new XPathException(err);
+ de.setErrorCode("FORX0002");
+ de.setXPathContext(context);
+ de.setLocator(this);
+ throw de;
+ } catch (PatternSyntaxException err) {
+ XPathException de = new XPathException(err);
+ de.setErrorCode("FORX0002");
+ de.setXPathContext(context);
+ de.setLocator(this);
+ throw de;
+ }
+
+ // check that it's not a pattern that matches ""
+ if (re.matches("")) {
+ dynamicError(
+ "The regular expression in replace() must not be one that matches a zero-length string",
+ "FORX0003", context);
+ }
+ }
+ String input = inputArg.getStringValue();
+ CharSequence res = re.replace(input, replacement);
+ return StringValue.makeStringValue(res);
+ }
+
+ /**
+ * Check the contents of the replacement string
+ * @param rep the replacement string
+ * @return null if the string is OK, or an error message if not
+ */
+
+ public static String checkReplacement(CharSequence rep) {
+ for (int i=0; i<rep.length(); i++) {
+ char c = rep.charAt(i);
+ if (c == '$') {
+ if (i+1 < rep.length()) {
+ char next = rep.charAt(++i);
+ if (next < '0' || next > '9') {
+ return "Invalid replacement string in replace(): $ sign must be followed by digit 0-9";
+ }
+ } else {
+ return "Invalid replacement string in replace(): $ sign at end of string";
+ }
+ } else if (c == '\\') {
+ if (i+1 < rep.length()) {
+ char next = rep.charAt(++i);
+ if (next != '\\' && next != '$') {
+ return "Invalid replacement string in replace(): \\ character must be followed by \\ or $";
+ }
+ } else {
+ return "Invalid replacement string in replace(): \\ character at end of string";
+ }
+ }
+ }
+ return null;
+ }
+
+}
+
diff --git a/sf/saxon/functions/ResolveQName.java b/sf/saxon/functions/ResolveQName.java
new file mode 100644
index 0000000..7062257
--- /dev/null
+++ b/sf/saxon/functions/ResolveQName.java
@@ -0,0 +1,80 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.QNameValue;
+
+
+/**
+* This class supports the resolve-QName function in XPath 2.0
+*/
+
+public class ResolveQName extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate the expression
+ */
+
+ public QNameValue evaluateItem(XPathContext context) throws XPathException {
+ AtomicValue arg0 = (AtomicValue)argument[0].evaluateItem(context);
+ NodeInfo element = (NodeInfo)argument[1].evaluateItem(context);
+ return resolveQName(arg0, element, context);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return resolveQName((AtomicValue)arguments[0].head(), (NodeInfo)arguments[1].head(), context);
+ }
+
+ /**
+ * Internal method to do the real work
+ * @param qname the first argument
+ * @param element the second argument
+ * @param context the dynamic context
+ * @return the result
+ * @throws XPathException
+ */
+
+ /*@Nullable*/ private QNameValue resolveQName(AtomicValue qname, NodeInfo element, XPathContext context) throws XPathException {
+ if (qname == null) {
+ return null;
+ }
+ CharSequence lexicalQName = qname.getStringValueCS();
+ final NameChecker checker = context.getConfiguration().getNameChecker();
+
+ NamespaceResolver resolver = new InscopeNamespaceResolver(element);
+ StructuredQName qName;
+
+ try {
+ qName= StructuredQName.fromLexicalQName(lexicalQName, true, false, checker, resolver);
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ throw e;
+ }
+
+ return new QNameValue(qName, BuiltInAtomicType.QNAME);
+ }
+
+}
+
diff --git a/sf/saxon/functions/ResolveURI.java b/sf/saxon/functions/ResolveURI.java
new file mode 100644
index 0000000..6e3ab60
--- /dev/null
+++ b/sf/saxon/functions/ResolveURI.java
@@ -0,0 +1,322 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AnyURIValue;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.EmptySequence;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+/**
+* This class supports the resolve-uri() functions in XPath 2.0
+*/
+
+public class ResolveURI extends SystemFunctionCall implements Callable {
+
+ /*@Nullable*/ String expressionBaseURI = null;
+
+ @Override
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ expressionBaseURI = env.getBaseURI();
+ }
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ super.checkArguments(visitor);
+ if (expressionBaseURI == null) {
+ expressionBaseURI = visitor.getStaticContext().getBaseURI();
+ if (expressionBaseURI == null && argument.length == 1) {
+ XPathException de = new XPathException("Base URI in static context of resolve-uri() is unknown");
+ de.setErrorCode("FONS0005");
+ throw de;
+ }
+ }
+ }
+
+ /**
+ * Get the static base URI of the expression
+ * @return the base URI from the static context
+ */
+
+ /*@Nullable*/ public String getStaticBaseURI() {
+ return expressionBaseURI;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/ public Expression copy() {
+ ResolveURI d = (ResolveURI)super.copy();
+ d.expressionBaseURI = expressionBaseURI;
+ return d;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(/*@NotNull*/ Object o) {
+ return (o instanceof ResolveURI) &&
+ super.equals(o) &&
+ equalOrNull(expressionBaseURI, ((ResolveURI)o).expressionBaseURI);
+ }
+
+
+ /**
+ * Evaluate the function at run-time
+ */
+
+ /*@Nullable*/ public AnyURIValue evaluateItem(XPathContext context) throws XPathException {
+ AtomicValue arg0 = (AtomicValue)argument[0].evaluateItem(context);
+ if (arg0 == null) {
+ return null;
+ }
+ String relative = arg0.getStringValue();
+ String base;
+ if (argument.length == 2) {
+ base = argument[1].evaluateAsString(context).toString();
+ } else {
+ base = expressionBaseURI;
+ if (expressionBaseURI == null) {
+ dynamicError("Base URI in static context of resolve-uri() is unknown", "FONS0005", context);
+ return null;
+ }
+ }
+
+ return resolve(base, relative, context);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ /*@Nullable*/ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ AtomicValue arg0 = (AtomicValue)arguments[0].head();
+ if (arg0 == null) {
+ return EmptySequence.getInstance();
+ }
+ String relative = arg0.getStringValue();
+ String base;
+ if (argument.length == 2) {
+ //noinspection ConstantConditions
+ base = arguments[1].head().getStringValue();
+ } else {
+ base = expressionBaseURI;
+ if (expressionBaseURI == null) {
+ dynamicError("Base URI in static context of resolve-uri() is unknown", "FONS0005", context);
+ return null;
+ }
+ }
+
+ return resolve(base, relative, context);
+ }
+
+ /*@NotNull*/ private AnyURIValue resolve(String base, String relative, XPathContext context) throws XPathException {
+ try {
+ URI absoluteURI = new URI(base);
+ if (!absoluteURI.isAbsolute()) {
+ URI relativeURI = new URI(relative);
+ if (relativeURI.isAbsolute()) {
+ return new AnyURIValue(relative);
+ }
+ dynamicError("Base URI " + Err.wrap(base) + " is not an absolute URI", "FORG0002", context);
+ }
+ if (absoluteURI.isOpaque()) {
+ dynamicError("Base URI " + Err.wrap(base) + " is a non-hierarchic URI", "FORG0002", context);
+ }
+ if (absoluteURI.getRawFragment() != null) {
+ dynamicError("Base URI " + Err.wrap(base) + " contains a fragment identifier", "FORG0002", context);
+ }
+ URI resolved = makeAbsolute(relative, base);
+ return new AnyURIValue(resolved.toString());
+ } catch (URISyntaxException err) {
+ dynamicError("Base URI " + Err.wrap(base) + " is invalid: " + err.getMessage(),
+ "FORG0002", context);
+ return new AnyURIValue(""); //dummy return to satisfy the language rules
+ }
+ }
+
+ /**
+ * If a system ID can't be parsed as a URL, try to expand it as a relative
+ * URI using the current directory as the base URI.
+ * @param systemId the supplied systemId. Null is treated as equivalent to ""
+ * @return the systemId itself if it is a valid URL; otherwise the result of resolving
+ * the systemId as a relative file name in the current working directory; or if the
+ * current working directory is not available (e.g. in an applet) the supplied systemId
+ * unchanged (except that null is treated as "").
+ */
+
+ /*@NotNull*/
+ public static String tryToExpand(/*@Nullable*/ String systemId) {
+ if (systemId==null) {
+ systemId = "";
+ }
+ try {
+ new URL(systemId);
+ return systemId; // all is well
+ } catch (MalformedURLException err) {
+ String dir;
+ try {
+ dir = System.getProperty("user.dir");
+ } catch (Exception geterr) {
+ // this doesn't work when running an applet
+ return systemId;
+ }
+ if (!(dir.endsWith("/") || systemId.startsWith("/"))) {
+ dir = dir + '/';
+ }
+
+ try {
+ URI currentDirectoryURI = new File(dir).toURI();
+ URI baseURI = currentDirectoryURI.resolve(systemId);
+ return baseURI.toString();
+ } catch (Exception e) {
+ return systemId;
+ }
+
+ }
+ }
+
+ /**
+ * Construct an absolute URI from a relative URI and a base URI. The method uses the resolve
+ * method of the java.net.URI class, except where the base URI uses the (non-standard) "jar:" scheme,
+ * in which case the method used is <code>new URL(baseURL, relativeURL)</code>.
+ *
+ * <p>Spaces in either URI are converted to %20</p>
+ *
+ * <p>If no base URI is available, and the relative URI is not an absolute URI, then the current
+ * directory is used as a base URI.</p>
+ *
+ * @param relativeURI the relative URI. Null is permitted provided that the base URI is an absolute URI
+ * @param base the base URI. Null is permitted provided that relativeURI is an absolute URI
+ * @return the absolutized URI
+ * @throws java.net.URISyntaxException if either of the strings is not a valid URI or
+ * if the resolution fails
+ */
+
+ /*@NotNull*/
+ public static URI makeAbsolute(/*@Nullable*/ String relativeURI, /*@Nullable*/ String base) throws URISyntaxException {
+ URI absoluteURI;
+ // System.err.println("makeAbsolute " + relativeURI + " against base " + base);
+ if (relativeURI == null) {
+ if (base == null) {
+ throw new URISyntaxException("", "Relative and Base URI must not both be null");
+ }
+ absoluteURI = new URI(ResolveURI.escapeSpaces(base));
+ if (!absoluteURI.isAbsolute()) {
+ throw new URISyntaxException(base, "Relative URI not supplied, so base URI must be absolute");
+ } else {
+ return absoluteURI;
+ }
+ }
+ relativeURI = ResolveURI.escapeSpaces(relativeURI);
+ base = (base == null ? null : ResolveURI.escapeSpaces(base));
+ try {
+ if (base==null || base.length() == 0) {
+ absoluteURI = new URI(relativeURI);
+ if (!absoluteURI.isAbsolute()) {
+ String expandedBase = ResolveURI.tryToExpand(base);
+ if (!expandedBase.equals(base)) { // prevent infinite recursion
+ return makeAbsolute(relativeURI, expandedBase);
+ }
+ }
+ } else if (base != null && (base.startsWith("jar:") || base.startsWith("file:////"))) {
+
+ // jar: URIs can't be resolved by the java.net.URI class, because they don't actually
+ // conform with the RFC standards for hierarchic URI schemes (quite apart from not being
+ // a registered URI scheme). But they seem to be widely used.
+
+ // URIs starting file://// are accepted by the java.net.URI class, they are used to
+ // represent Windows UNC filenames. However, the java.net.URI algorithm for resolving
+ // a relative URI against such a base URI fails to produce a usable UNC filename (it's not
+ // clear whether Java is implementing RFC 3986 correctly here, it depends on interpretation).
+ // So we use the java.net.URL algorithm for this case too, because it works.
+
+ try {
+ URL baseURL = new URL(base);
+ URL absoluteURL = new URL(baseURL, relativeURI);
+ absoluteURI = absoluteURL.toURI();
+ // Note JDK1.5 dependency on URL.toURI()
+ } catch (MalformedURLException err) {
+ throw new URISyntaxException(base + " " + relativeURI, err.getMessage());
+ }
+ } else {
+ URI baseURI;
+ try {
+ baseURI = new URI(base);
+ } catch (URISyntaxException e) {
+ throw new URISyntaxException(base, "Invalid base URI: " + e.getMessage());
+ }
+ if (baseURI.getFragment() != null) {
+ int hash = base.indexOf('#');
+ if (hash >= 0) {
+ base = base.substring(0, hash);
+ }
+ try {
+ baseURI = new URI(base);
+ } catch (URISyntaxException e) {
+ throw new URISyntaxException(base, "Invalid base URI: " + e.getMessage());
+ }
+ }
+ try {
+ new URI(relativeURI); // for validation only
+ } catch (URISyntaxException e) {
+ throw new URISyntaxException(base, "Invalid relative URI: " + e.getMessage());
+ }
+ absoluteURI = (relativeURI.length() == 0 ? baseURI : baseURI.resolve(relativeURI));
+ }
+ } catch (IllegalArgumentException err0) {
+ // can be thrown by resolve() when given a bad URI
+ throw new URISyntaxException(relativeURI, "Cannot resolve URI against base " + Err.wrap(base));
+ }
+
+ return absoluteURI;
+ }
+
+
+ /**
+ * Replace spaces by %20
+ * @param s the input string
+ * @return the input string with each space replaced by %20
+ */
+
+ /*@NotNull*/ public static String escapeSpaces(/*@NotNull*/ String s) {
+ // It's not entirely clear why we have to escape spaces by hand, and not other special characters;
+ // it's just that tests with a variety of filenames show that this approach seems to work.
+ int i = s.indexOf(' ');
+ if (i < 0) {
+ return s;
+ }
+ return (i == 0 ? "" : s.substring(0, i))
+ + "%20"
+ + (i == s.length()-1 ? "" : escapeSpaces(s.substring(i+1)));
+ }
+
+}
+
diff --git a/sf/saxon/functions/Reverse.java b/sf/saxon/functions/Reverse.java
new file mode 100644
index 0000000..ae1d38d
--- /dev/null
+++ b/sf/saxon/functions/Reverse.java
@@ -0,0 +1,87 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ReversibleIterator;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceExtent;
+
+/**
+* Implement XPath function fn:reverse()
+*/
+
+public class Reverse extends SystemFunctionCall implements Callable {
+
+ /**
+ * Determine the item type of the value returned by the function
+ *
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return argument[0].getItemType(th); //AUTO
+ }
+
+ public int computeSpecialProperties() {
+ int baseProps = argument[0].getSpecialProperties();
+ if ((baseProps & StaticProperty.REVERSE_DOCUMENT_ORDER) != 0) {
+ return (baseProps &
+ (~StaticProperty.REVERSE_DOCUMENT_ORDER)) |
+ StaticProperty.ORDERED_NODESET;
+ } else if ((baseProps & StaticProperty.ORDERED_NODESET) != 0) {
+ return (baseProps &
+ (~StaticProperty.ORDERED_NODESET)) |
+ StaticProperty.REVERSE_DOCUMENT_ORDER;
+ } else {
+ return baseProps;
+ }
+ }
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ SequenceIterator forwards = argument[0].iterate(context);
+ return getReverseIterator(forwards);
+ }
+
+ public static SequenceIterator getReverseIterator(SequenceIterator forwards) throws XPathException {
+ if (forwards instanceof ReversibleIterator) {
+ return ((ReversibleIterator)forwards).getReverseIterator();
+ } else {
+ SequenceExtent extent = new SequenceExtent(forwards);
+ return extent.reverseIterate();
+ }
+ }
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ // EBV is independent of sequence order unless the sequence mixes atomic values and nodes
+ // Note, calls to get the EBV of reverse() should normally have been rewritten at compile time
+ ItemType type = argument[0].getItemType(context.getConfiguration().getTypeHierarchy());
+ if (type == AnyItemType.getInstance()) {
+ return super.effectiveBooleanValue(context);
+ } else {
+ return argument[0].effectiveBooleanValue(context);
+ }
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return SequenceTool.toLazySequence(getReverseIterator(arguments[0].iterate()));
+ }
+
+
+}
+
diff --git a/sf/saxon/functions/Root.java b/sf/saxon/functions/Root.java
new file mode 100644
index 0000000..9ff845f
--- /dev/null
+++ b/sf/saxon/functions/Root.java
@@ -0,0 +1,138 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.RootFunctionCompiler;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.*;
+
+import java.util.List;
+
+/**
+* Implement the XPath 2.0 root() function
+*/
+
+
+public class Root extends SystemFunctionCall {
+
+ /**
+ * Simplify and validate.
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ return simplifyArguments(visitor);
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-significant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ int prop = StaticProperty.ORDERED_NODESET |
+ StaticProperty.SINGLE_DOCUMENT_NODESET |
+ StaticProperty.NON_CREATIVE;
+ if ((getNumberOfArguments() == 0) ||
+ (argument[0].getSpecialProperties() & StaticProperty.CONTEXT_DOCUMENT_NODESET) != 0) {
+ prop |= StaticProperty.CONTEXT_DOCUMENT_NODESET;
+ }
+ return prop;
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ return pathMapNodeSet.createArc(AxisInfo.ANCESTOR_OR_SELF, NodeKindTest.DOCUMENT);
+ }
+
+
+ /**
+ * Evaluate in a general context
+ */
+
+ /*@Nullable*/ public NodeInfo evaluateItem(XPathContext c) throws XPathException {
+ NodeInfo start = (NodeInfo)argument[0].evaluateItem(c);
+ if (start==null) {
+ return null;
+ }
+ return start.getRoot();
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NodeInfo node = (arguments.length == 0 ? getContextNode(context) : (NodeInfo)arguments[0].head());
+ if (node == null) {
+ return EmptySequence.getInstance();
+ } else {
+ return node.getRoot();
+ }
+ }
+
+//#ifdefined BYTECODE
+ @Override
+ public int getStreamability(int syntacticContext, boolean allowExtensions, List<String> reasons) {
+ if (reasons != null) {
+ reasons.add("Calls root()");
+ }
+ return W3C_FREE_RANGING;
+ }
+
+ /**
+ * Return the compiler of the Root Function
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new RootFunctionCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/Round.java b/sf/saxon/functions/Round.java
new file mode 100644
index 0000000..bcb825c
--- /dev/null
+++ b/sf/saxon/functions/Round.java
@@ -0,0 +1,90 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.RoundingCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.NumericValue;
+
+/**
+ * This class supports the round() functions
+ */
+
+public final class Round extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate the function
+ */
+
+ /*@Nullable*/
+ public NumericValue evaluateItem(XPathContext context) throws XPathException {
+
+ NumericValue val0 = (NumericValue) argument[0].evaluateItem(context);
+ if (val0 == null) {
+ return null;
+ }
+
+ int scaleRnd = 0;
+ if (argument.length == 2) {
+ NumericValue scaleVal = (NumericValue) argument[1].evaluateItem(context);
+ scaleRnd = (int) scaleVal.longValue();
+ }
+ return val0.round(scaleRnd);
+ }
+
+ /**
+ * Determine the cardinality of the function.
+ */
+
+ public int computeCardinality() {
+ return argument[0].getCardinality();
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NumericValue val0 = (NumericValue) arguments[0].head();
+ if (val0 == null) {
+ return EmptySequence.getInstance();
+ }
+
+ int scaleRnd = 0;
+ if (arguments.length == 2) {
+ NumericValue scaleVal = (NumericValue) arguments[1].head();
+ scaleRnd = (int) scaleVal.longValue();
+ }
+ return val0.round(scaleRnd);
+
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Round expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new RoundingCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/RoundHalfToEven.java b/sf/saxon/functions/RoundHalfToEven.java
new file mode 100644
index 0000000..9a41f63
--- /dev/null
+++ b/sf/saxon/functions/RoundHalfToEven.java
@@ -0,0 +1,100 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.RoundingCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.NumericValue;
+
+/**
+* This class supports the round-to-half-even() function
+*/
+
+public final class RoundHalfToEven extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate the function
+ */
+
+ /*@Nullable*/ public NumericValue evaluateItem(XPathContext context) throws XPathException {
+
+ NumericValue val0 = (NumericValue)argument[0].evaluateItem(context);
+ if (val0 == null) {
+ return null;
+ }
+
+ int scale = 0;
+ if (argument.length==2) {
+ NumericValue scaleVal = (NumericValue)argument[1].evaluateItem(context);
+ if (scaleVal.compareTo(Integer.MAX_VALUE) > 0) {
+ return val0;
+ } else if (scaleVal.compareTo(Integer.MIN_VALUE) < 0) {
+ scale = Integer.MIN_VALUE;
+ } else {
+ scale = (int)scaleVal.longValue();
+ }
+ }
+ return val0.roundHalfToEven(scale);
+ }
+
+ /**
+ * Determine the cardinality of the function.
+ */
+
+ public int computeCardinality() {
+ return argument[0].getCardinality();
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ NumericValue val0 = (NumericValue)arguments[0].head();
+ if (val0 == null) {
+ return EmptySequence.getInstance();
+ }
+
+ int scale = 0;
+ if (arguments.length==2) {
+ NumericValue scaleVal = (NumericValue)arguments[1].head();
+ if (scaleVal.compareTo(Integer.MAX_VALUE) > 0) {
+ return val0;
+ } else if (scaleVal.compareTo(Integer.MIN_VALUE) < 0) {
+ scale = Integer.MIN_VALUE;
+ } else {
+ scale = (int)scaleVal.longValue();
+ }
+ }
+ return val0.roundHalfToEven(scale);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the RoundHalfToEven expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new RoundingCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/StandardFunction.java b/sf/saxon/functions/StandardFunction.java
new file mode 100644
index 0000000..0a12394
--- /dev/null
+++ b/sf/saxon/functions/StandardFunction.java
@@ -0,0 +1,895 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.util.HashMap;
+
+/**
+ * This class contains static data tables defining the properties of standard functions. "Standard functions"
+ * here means the XPath 2.0 functions, the XSLT 2.0 functions, and a few selected extension functions
+ * which need special recognition.
+ */
+
+public abstract class StandardFunction {
+
+ public static Sequence EMPTY = EmptySequence.getInstance();
+
+ /**
+ * Categories of functions, bit significant
+ */
+
+ public static final int CORE = 1;
+ public static final int XSLT = 2;
+ public static final int USE_WHEN = 4;
+ public static final int XQUPDATE = 8;
+ public static final int XPATH30 = 16;
+ public static final int INTERNAL = 32;
+ public static final int XSLT30 = 64;
+
+ /**
+ * Local names used for cardinality values
+ */
+
+ public static final int ONE = StaticProperty.ALLOWS_ONE;
+ public static final int OPT = StaticProperty.ALLOWS_ZERO_OR_ONE;
+ public static final int STAR = StaticProperty.ALLOWS_ZERO_OR_MORE;
+ public static final int PLUS = StaticProperty.ALLOWS_ONE_OR_MORE;
+
+ /**
+ * Classification of function arguments for serialization purposes; note that values must not conflict
+ * with bit settings used for cardinalities
+ */
+
+ public static final int LOOK = 1 << 24; // = syntactic context INSPECTION
+ public static final int TAKE = 1 << 25; // = syntactic context NODE-VALUE (implicit when type is atomic)
+ public static final int GIVE = 1 << 26; // = syntactic context INHERIT (node is included in function result)
+ public static final int WALK = 1 << 27; // = syntactic context NAVIGATE (function navigates from this node)
+
+ /**
+ * This class is never instantiated
+ */
+
+ private StandardFunction() {
+ }
+
+ /**
+ * Register a system function in the table of function details.
+ *
+ * @param name the function name
+ * @param implementationClass the class used to implement the function
+ * @param opcode identifies the function when a single class implements several functions
+ * @param minArguments the minimum number of arguments required
+ * @param maxArguments the maximum number of arguments allowed
+ * @param itemType the item type of the result of the function
+ * @param cardinality the cardinality of the result of the function
+ * @param applicability the host languages (and versions thereof) in which this function is available
+ * @param properties
+ * @return the entry describing the function. The entry is incomplete, it does not yet contain information
+ * about the function arguments.
+ */
+
+ /*@NotNull*/
+ public static Entry register(String name,
+ Class implementationClass,
+ int opcode,
+ int minArguments,
+ int maxArguments,
+ ItemType itemType,
+ int cardinality,
+ int applicability,
+ int properties) {
+ Entry e = makeEntry(name, implementationClass, opcode, minArguments, maxArguments,
+ itemType, cardinality, applicability, properties);
+ functionTable.put(name, e);
+ return e;
+ }
+
+ /**
+ * Make a table entry describing the signature of a function, with a reference to the implementation class.
+ *
+ * @param name the function name
+ * @param implementationClass the class used to implement the function
+ * @param opcode identifies the function when a single class implements several functions
+ * @param minArguments the minimum number of arguments required
+ * @param maxArguments the maximum number of arguments allowed
+ * @param itemType the item type of the result of the function
+ * @param cardinality the cardinality of the result of the function
+ * @param applicability the host languages (and versions of) in which this function is available
+ * @return the entry describing the function. The entry is incomplete, it does not yet contain information
+ * about the function arguments.
+ */
+ public static Entry makeEntry(String name, Class implementationClass, int opcode,
+ int minArguments, int maxArguments,
+ ItemType itemType, int cardinality, int applicability, int properties) {
+ Entry e = new Entry();
+ int hash = name.indexOf('#');
+ if (hash < 0) {
+ e.name = name;
+ } else {
+ e.name = name.substring(0, hash);
+ }
+ e.implementationClass = implementationClass;
+ e.opcode = opcode;
+ e.minArguments = minArguments;
+ e.maxArguments = maxArguments;
+ e.itemType = itemType;
+ e.cardinality = cardinality;
+ e.applicability = applicability;
+ e.properties = properties;
+ if (maxArguments > 100) {
+ // special case for concat()
+ e.argumentTypes = new SequenceType[1];
+ e.resultIfEmpty = new AtomicValue[1];
+ e.syntacticContext = new int[1];
+ } else {
+ e.argumentTypes = new SequenceType[maxArguments];
+ e.resultIfEmpty = new Sequence[maxArguments];
+ e.syntacticContext = new int[maxArguments];
+ }
+ return e;
+ }
+
+
+ private static HashMap<String, Entry> functionTable = new HashMap<String, Entry>(200);
+
+ public static final int AS_ARG0 = 1; // Result has same type as first argument
+ public static final int AS_PRIM_ARG0 = 2; // Result has same primitive type as first argument
+ public static final int FOCUS = 4; // Depends on focus
+ public static final int BASE = 8; // Depends on base URI
+ public static final int NS = 16; // Depends on namespace context
+ public static final int DCOLL = 32; // Depends on default collation
+
+ public static final int DEPENDS_ON_STATIC_CONTEXT = BASE | NS | DCOLL;
+
+
+ static {
+ Entry e;
+ register("abs", Abs.class, 0, 1, 1, BuiltInAtomicType.NUMERIC, OPT, CORE, AS_PRIM_ARG0)
+ .arg(0, BuiltInAtomicType.NUMERIC, OPT, EMPTY);
+
+ register("adjust-date-to-timezone", Adjust.class, 0, 1, 2, BuiltInAtomicType.DATE, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.DAY_TIME_DURATION, OPT, null);
+
+ register("adjust-dateTime-to-timezone", Adjust.class, 0, 1, 2, BuiltInAtomicType.DATE_TIME, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE_TIME, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.DAY_TIME_DURATION, OPT, null);
+
+ register("adjust-time-to-timezone", Adjust.class, 0, 1, 2, BuiltInAtomicType.TIME, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.TIME, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.DAY_TIME_DURATION, OPT, null);
+
+ register("avg", Average.class, 0, 1, 1, BuiltInAtomicType.ANY_ATOMIC, OPT, CORE, 0)
+ // can't say "same as first argument" because the avg of a set of integers is decimal
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY);
+
+ register("base-uri", BaseURI.class, 0, 0, 1, BuiltInAtomicType.ANY_URI, OPT, CORE, BASE)
+ .arg(0, Type.NODE_TYPE, OPT | LOOK, EMPTY);
+
+ register("boolean", BooleanFn.class, 0, 1, 1, BuiltInAtomicType.BOOLEAN, ONE, CORE, 0)
+ .arg(0, Type.ITEM_TYPE, STAR | LOOK, null);
+
+ register("ceiling", Ceiling.class, 0, 1, 1, BuiltInAtomicType.NUMERIC, OPT, CORE, AS_PRIM_ARG0)
+ .arg(0, BuiltInAtomicType.NUMERIC, OPT, EMPTY);
+
+ register("codepoint-equal", CodepointEqual.class, 0, 2, 2, BuiltInAtomicType.BOOLEAN, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.STRING, OPT, EMPTY);
+
+ register("codepoints-to-string", CodepointsToString.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.INTEGER, STAR, null);
+
+ register("collection", Collection.class, 0, 0, 1, Type.NODE_TYPE, STAR, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null);
+
+ register("compare#2", Compare.class, 0, 2, 2, BuiltInAtomicType.INTEGER, OPT, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.STRING, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.STRING, OPT, EMPTY);
+
+ register("compare#3", Compare.class, 0, 3, 3, BuiltInAtomicType.INTEGER, OPT, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.STRING, OPT, EMPTY)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("concat", Concat.class, 0, 2, Integer.MAX_VALUE, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, OPT, null);
+ // Note, this has a variable number of arguments so it is treated specially
+
+ register("contains#2", Contains.class, 0, 2, 2, BuiltInAtomicType.BOOLEAN, ONE, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, BooleanValue.TRUE);
+
+ register("contains#3", Contains.class, 0, 3, 3, BuiltInAtomicType.BOOLEAN, ONE, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, BooleanValue.TRUE)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("count", Count.class, 0, 1, 1, BuiltInAtomicType.INTEGER, ONE, CORE, 0)
+ .arg(0, Type.ITEM_TYPE, STAR | LOOK, Int64Value.ZERO);
+
+ register("current", Current.class, 0, 0, 0, Type.ITEM_TYPE, ONE, XSLT, 0);
+
+ register("current-date", CurrentDateTime.class, 0, 0, 0, BuiltInAtomicType.DATE, ONE, CORE, 0);
+
+ register("current-dateTime", CurrentDateTime.class, 0, 0, 0, BuiltInAtomicType.DATE_TIME, ONE, CORE, 0);
+
+ register("current-time", CurrentDateTime.class, 0, 0, 0, BuiltInAtomicType.TIME, ONE, CORE, 0);
+
+ register("current-group", CurrentGroup.class, 0, 0, 0, Type.ITEM_TYPE, STAR, XSLT, 0);
+
+ register("current-grouping-key", CurrentGroupingKey.class, 0, 0, 0, BuiltInAtomicType.ANY_ATOMIC, OPT, XSLT, 0);
+
+ register("data#0", Data.class, 0, 0, 0, BuiltInAtomicType.ANY_ATOMIC, STAR, XPATH30, FOCUS);
+
+ register("data#1", Data.class, 0, 1, 1, BuiltInAtomicType.ANY_ATOMIC, STAR, CORE, 0)
+ .arg(0, Type.ITEM_TYPE, STAR | TAKE, EMPTY);
+
+ register("dateTime", DateTimeConstructor.class, 0, 2, 2, BuiltInAtomicType.DATE_TIME, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.TIME, OPT, EMPTY);
+
+ register("day-from-date", Component.class, (Component.DAY << 16) + StandardNames.XS_DATE, 1, 1, BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE, OPT, EMPTY);
+
+ register("day-from-dateTime", Component.class, (Component.DAY << 16) + StandardNames.XS_DATE_TIME, 1, 1, BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE_TIME, OPT, EMPTY);
+
+ register("days-from-duration", Component.class, (Component.DAY << 16) + StandardNames.XS_DURATION, 1, 1, BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DURATION, OPT, EMPTY);
+
+ register("deep-equal#2", DeepEqual.class, 0, 2, 3, BuiltInAtomicType.BOOLEAN, ONE, CORE, DCOLL)
+ .arg(0, Type.ITEM_TYPE, STAR, null)
+ .arg(1, Type.ITEM_TYPE, STAR, null);
+
+ register("deep-equal#3", DeepEqual.class, 0, 2, 3, BuiltInAtomicType.BOOLEAN, ONE, CORE, BASE)
+ .arg(0, Type.ITEM_TYPE, STAR, null)
+ .arg(1, Type.ITEM_TYPE, STAR, null)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("default-collation", DefaultCollation.class, 0, 0, 0, BuiltInAtomicType.STRING, ONE, CORE, DCOLL);
+
+ register("distinct-values#1", DistinctValues.class, 0, 1, 2, BuiltInAtomicType.ANY_ATOMIC, STAR, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY);
+
+ register("distinct-values#2", DistinctValues.class, 0, 1, 2, BuiltInAtomicType.ANY_ATOMIC, STAR, CORE, BASE)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("doc", Doc.class, 0, 1, 1, NodeKindTest.DOCUMENT, OPT, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, EMPTY);
+
+ register("doc-available", DocAvailable.class, 0, 1, 1, BuiltInAtomicType.BOOLEAN, ONE, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, BooleanValue.FALSE);
+
+ register("document", DocumentFn.class, 0, 1, 2, Type.NODE_TYPE, STAR, XSLT, BASE)
+ .arg(0, Type.ITEM_TYPE, STAR, null)
+ .arg(1, Type.NODE_TYPE, ONE, null);
+
+ register("document-uri#0", DocumentUriFn.class, 0, 0, 0, BuiltInAtomicType.ANY_URI, OPT, XPATH30, FOCUS);
+
+ register("document-uri#1", DocumentUriFn.class, 0, 1, 1, BuiltInAtomicType.ANY_URI, OPT, CORE, 0)
+ .arg(0, Type.NODE_TYPE, OPT | LOOK, EMPTY);
+
+ register("empty", Empty.class, 0, 1, 1, BuiltInAtomicType.BOOLEAN, ONE, CORE, 0)
+ .arg(0, Type.ITEM_TYPE, STAR | LOOK, BooleanValue.TRUE);
+
+ register("ends-with#2", EndsWith.class, 0, 2, 2, BuiltInAtomicType.BOOLEAN, ONE, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, BooleanValue.TRUE);
+
+ register("ends-with#3", EndsWith.class, 0, 3, 3, BuiltInAtomicType.BOOLEAN, ONE, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, BooleanValue.TRUE)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("element-available", ElementAvailable.class, 0, 1, 1, BuiltInAtomicType.BOOLEAN, ONE, XSLT | USE_WHEN, NS)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null);
+
+ register("element-with-id#1", Id.class, Id.ELEMENT_WITH_ID, 1, 1, NodeKindTest.ELEMENT, STAR, CORE, FOCUS)
+ .arg(0, BuiltInAtomicType.STRING, STAR, EMPTY);
+
+ register("element-with-id#2", Id.class, Id.ELEMENT_WITH_ID, 2, 2, NodeKindTest.ELEMENT, STAR, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, STAR, EMPTY)
+ .arg(1, Type.NODE_TYPE, ONE, null);
+
+ register("encode-for-uri", EscapeURI.class, EscapeURI.ENCODE_FOR_URI, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING);
+
+ register("escape-html-uri", EscapeURI.class, EscapeURI.HTML_URI, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING);
+
+ register("error", Error.class, 0, 0, 3, Type.ITEM_TYPE, OPT, CORE, 0)
+ // The return type is chosen so that use of the error() function will never give a static type error,
+ // on the basis that item()? overlaps every other type, and it's almost impossible to make any
+ // unwarranted inferences from it, except perhaps count(error()) lt 2.
+ .arg(0, BuiltInAtomicType.QNAME, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null)
+ .arg(2, Type.ITEM_TYPE, STAR, null);
+
+ register("exactly-one", TreatFn.class, ONE, 1, 1, Type.ITEM_TYPE, ONE, CORE, AS_ARG0)
+ .arg(0, Type.ITEM_TYPE, ONE | GIVE, null);
+ // because we don't do draconian static type checking, we can do the work in the argument type checking code
+
+ register("false", False.class, 0, 0, 0, BuiltInAtomicType.BOOLEAN, ONE, CORE, 0);
+
+ register("exists", Exists.class, 0, 1, 1, BuiltInAtomicType.BOOLEAN, ONE, CORE, 0)
+ .arg(0, Type.ITEM_TYPE, STAR | LOOK, BooleanValue.FALSE);
+
+ register("floor", Floor.class, 0, 1, 1, BuiltInAtomicType.NUMERIC, OPT, CORE, AS_PRIM_ARG0)
+ .arg(0, BuiltInAtomicType.NUMERIC, OPT, EMPTY);
+
+ register("format-date", FormatDate.class, StandardNames.XS_DATE, 2, 5, BuiltInAtomicType.STRING,
+ OPT, XSLT | XPATH30, 0)
+ .arg(0, BuiltInAtomicType.DATE, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null)
+ .arg(2, BuiltInAtomicType.STRING, OPT, null)
+ .arg(3, BuiltInAtomicType.STRING, OPT, null)
+ .arg(4, BuiltInAtomicType.STRING, OPT, null);
+
+ register("format-dateTime", FormatDate.class, StandardNames.XS_DATE_TIME, 2, 5, BuiltInAtomicType.STRING,
+ OPT, XSLT | XPATH30, 0)
+ .arg(0, BuiltInAtomicType.DATE_TIME, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null)
+ .arg(2, BuiltInAtomicType.STRING, OPT, null)
+ .arg(3, BuiltInAtomicType.STRING, OPT, null)
+ .arg(4, BuiltInAtomicType.STRING, OPT, null);
+
+ register("format-number#2", FormatNumber.class, 0, 2, 2, BuiltInAtomicType.STRING, ONE, XSLT | XPATH30, 0)
+ .arg(0, BuiltInAtomicType.NUMERIC, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("format-number#3", FormatNumber.class, 0, 3, 3, BuiltInAtomicType.STRING, ONE, XSLT | XPATH30, NS)
+ .arg(0, BuiltInAtomicType.NUMERIC, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null)
+ .arg(2, BuiltInAtomicType.STRING, OPT, null);
+
+ register("format-time", FormatDate.class, StandardNames.XS_TIME, 2, 5, BuiltInAtomicType.STRING,
+ OPT, XSLT | XPATH30, 0)
+ .arg(0, BuiltInAtomicType.TIME, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null)
+ .arg(2, BuiltInAtomicType.STRING, OPT, null)
+ .arg(3, BuiltInAtomicType.STRING, OPT, null)
+ .arg(4, BuiltInAtomicType.STRING, OPT, null);
+
+ register("function-available", FunctionAvailable.class, 0, 1, 2, BuiltInAtomicType.BOOLEAN, ONE, XSLT | USE_WHEN, NS)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null)
+ .arg(1, BuiltInAtomicType.INTEGER, ONE, null);
+
+ register("generate-id#0", GenerateId.class, 0, 0, 0, BuiltInAtomicType.STRING, ONE, XSLT | XPATH30, FOCUS);
+
+ register("generate-id#1", GenerateId.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, XSLT | XPATH30, 0)
+ .arg(0, Type.NODE_TYPE, OPT | LOOK, StringValue.EMPTY_STRING);
+
+ register("hours-from-dateTime", Component.class, (Component.HOURS << 16) + StandardNames.XS_DATE_TIME, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE_TIME, OPT, EMPTY);
+
+ register("hours-from-duration", Component.class, (Component.HOURS << 16) + StandardNames.XS_DURATION, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DURATION, OPT, EMPTY);
+
+ register("hours-from-time", Component.class, (Component.HOURS << 16) + StandardNames.XS_TIME, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.TIME, OPT, EMPTY);
+
+ register("id#1", Id.class, Id.ID, 1, 1, NodeKindTest.ELEMENT, STAR, CORE, FOCUS)
+ .arg(0, BuiltInAtomicType.STRING, STAR, EMPTY);
+
+ register("id#2", Id.class, Id.ID, 2, 2, NodeKindTest.ELEMENT, STAR, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, STAR, EMPTY)
+ .arg(1, Type.NODE_TYPE, ONE, null);
+
+ register("idref#1", Idref.class, 0, 1, 1, Type.NODE_TYPE, STAR, CORE, FOCUS)
+ .arg(0, BuiltInAtomicType.STRING, STAR, EMPTY);
+
+ register("idref#2", Idref.class, 0, 2, 2, Type.NODE_TYPE, STAR, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, STAR, EMPTY)
+ .arg(1, Type.NODE_TYPE, ONE, null);
+
+ register("implicit-timezone", CurrentDateTime.class, 0, 0, 0, BuiltInAtomicType.DAY_TIME_DURATION, ONE, CORE, 0);
+
+ register("in-scope-prefixes", InScopePrefixes.class, 0, 1, 1, BuiltInAtomicType.STRING, STAR, CORE, 0)
+ .arg(0, NodeKindTest.ELEMENT, ONE | LOOK, null);
+
+ register("index-of#2", IndexOf.class, 0, 2, 2, BuiltInAtomicType.INTEGER, STAR, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY)
+ .arg(1, BuiltInAtomicType.ANY_ATOMIC, ONE, null);
+
+ register("index-of#3", IndexOf.class, 0, 3, 3, BuiltInAtomicType.INTEGER, STAR, CORE, BASE)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY)
+ .arg(1, BuiltInAtomicType.ANY_ATOMIC, ONE, null)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("insert-before", Insert.class, 0, 3, 3, Type.ITEM_TYPE, STAR, CORE, 0)
+ .arg(0, Type.ITEM_TYPE, STAR | GIVE, null)
+ .arg(1, BuiltInAtomicType.INTEGER, ONE, null)
+ .arg(2, Type.ITEM_TYPE, STAR | GIVE, null);
+
+ register("iri-to-uri", EscapeURI.class, EscapeURI.IRI_TO_URI, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING);
+
+ register("key#2", KeyFn.class, 0, 2, 2, Type.NODE_TYPE, STAR, XSLT, FOCUS | NS)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null)
+ .arg(1, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY);
+
+ register("key#3", KeyFn.class, 0, 3, 3, Type.NODE_TYPE, STAR, XSLT, NS)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null)
+ .arg(1, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY)
+ .arg(2, Type.NODE_TYPE, ONE, null);
+
+ register("lang#1", Lang.class, 0, 1, 1, BuiltInAtomicType.BOOLEAN, ONE, CORE, FOCUS)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null);
+
+ register("lang#2", Lang.class, 0, 2, 2, BuiltInAtomicType.BOOLEAN, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, Type.NODE_TYPE, ONE | LOOK, null);
+
+ register("last", Last.class, 0, 0, 0, BuiltInAtomicType.INTEGER, ONE, CORE, FOCUS);
+
+ register("local-name#0", LocalNameFn.class, 0, 0, 0, BuiltInAtomicType.STRING, ONE, CORE, FOCUS);
+
+ register("local-name#1", LocalNameFn.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, Type.NODE_TYPE, OPT | LOOK, StringValue.EMPTY_STRING);
+
+ register("local-name-from-QName", Component.class, (Component.LOCALNAME << 16) + StandardNames.XS_QNAME, 1, 1,
+ BuiltInAtomicType.NCNAME, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.QNAME, OPT, EMPTY);
+
+ register("lower-case", LowerCase.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING);
+
+ register("matches", Matches.class, 0, 2, 3, BuiltInAtomicType.BOOLEAN, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("max#1", Max.class, Minimax.MAX, 1, 1, BuiltInAtomicType.ANY_ATOMIC, OPT, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY);
+
+ register("max#2", Max.class, Minimax.MAX, 2, 2, BuiltInAtomicType.ANY_ATOMIC, OPT, CORE, BASE)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("min#1", Min.class, Minimax.MIN, 1, 1, BuiltInAtomicType.ANY_ATOMIC, OPT, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY);
+
+ register("min#2", Min.class, Minimax.MIN, 2, 2, BuiltInAtomicType.ANY_ATOMIC, OPT, CORE, BASE)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, EMPTY)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("minutes-from-dateTime", Component.class, (Component.MINUTES << 16) + StandardNames.XS_DATE_TIME, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE_TIME, OPT, EMPTY);
+
+ register("minutes-from-duration", Component.class, (Component.MINUTES << 16) + StandardNames.XS_DURATION, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DURATION, OPT, EMPTY);
+
+ register("minutes-from-time", Component.class, (Component.MINUTES << 16) + StandardNames.XS_TIME, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.TIME, OPT, EMPTY);
+
+ register("month-from-date", Component.class, (Component.MONTH << 16) + StandardNames.XS_DATE, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE, OPT, EMPTY);
+
+ register("month-from-dateTime", Component.class, (Component.MONTH << 16) + StandardNames.XS_DATE_TIME, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE_TIME, OPT, EMPTY);
+
+ register("months-from-duration", Component.class, (Component.MONTH << 16) + StandardNames.XS_DURATION, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DURATION, OPT, EMPTY);
+
+ register("name#0", NameFn.class, 0, 0, 0, BuiltInAtomicType.STRING, ONE, CORE, FOCUS);
+
+ register("name#1", NameFn.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, Type.NODE_TYPE, OPT | LOOK, StringValue.EMPTY_STRING);
+
+ register("namespace-uri#0", NamespaceUriFn.class, 0, 0, 0, BuiltInAtomicType.ANY_URI, ONE, CORE, FOCUS);
+
+ register("namespace-uri#1", NamespaceUriFn.class, 0, 1, 1, BuiltInAtomicType.ANY_URI, ONE, CORE, 0)
+ .arg(0, Type.NODE_TYPE, OPT | LOOK, StringValue.EMPTY_STRING);
+
+ register("namespace-uri-for-prefix", NamespaceForPrefix.class, 0, 2, 2, BuiltInAtomicType.ANY_URI, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, NodeKindTest.ELEMENT, ONE | LOOK, null);
+
+ register("namespace-uri-from-QName", Component.class, (Component.NAMESPACE << 16) + StandardNames.XS_QNAME, 1, 1, BuiltInAtomicType.ANY_URI, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.QNAME, OPT, EMPTY);
+
+ register("nilled#0", Nilled.class, 0, 0, 0, BuiltInAtomicType.BOOLEAN, OPT, XPATH30, FOCUS);
+
+ register("nilled#1", Nilled.class, 0, 1, 1, BuiltInAtomicType.BOOLEAN, OPT, CORE, 0)
+ .arg(0, Type.NODE_TYPE, OPT | LOOK, EMPTY);
+
+ register("node-name#0", NodeNameFn.class, 0, 0, 0, BuiltInAtomicType.QNAME, OPT, XPATH30, FOCUS);
+
+ register("node-name#1", NodeNameFn.class, 0, 1, 1, BuiltInAtomicType.QNAME, OPT, CORE, 0)
+ .arg(0, Type.NODE_TYPE, OPT | LOOK, EMPTY);
+
+ register("not", NotFn.class, 0, 1, 1, BuiltInAtomicType.BOOLEAN, ONE, CORE, 0)
+ .arg(0, Type.ITEM_TYPE, STAR | LOOK, BooleanValue.TRUE);
+
+ register("normalize-space#0", NormalizeSpace_0.class, 0, 0, 0, BuiltInAtomicType.STRING, ONE, CORE, FOCUS);
+
+ register("normalize-space#1", NormalizeSpace_1.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null);
+
+ register("normalize-unicode", NormalizeUnicode.class, 0, 1, 2, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("number#0", NumberFn.class, 0, 0, 0, BuiltInAtomicType.DOUBLE, ONE, CORE, FOCUS);
+
+ register("number#1", NumberFn.class, 0, 1, 1, BuiltInAtomicType.DOUBLE, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, OPT, DoubleValue.NaN);
+
+ register("one-or-more", TreatFn.class, PLUS, 1, 1, Type.ITEM_TYPE, PLUS, CORE, AS_ARG0)
+ .arg(0, Type.ITEM_TYPE, PLUS | GIVE, null);
+ // because we don't do draconian static type checking, we can do the work in the argument type checking code
+
+ register("position", Position.class, 0, 0, 0, BuiltInAtomicType.INTEGER, ONE, CORE, FOCUS);
+
+ register("prefix-from-QName", Component.class, (Component.PREFIX << 16) + StandardNames.XS_QNAME, 1, 1, BuiltInAtomicType.NCNAME, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.QNAME, OPT, EMPTY);
+
+ register("put", Put.class, 0, 2, 2, AnyItemType.getInstance(), OPT, XQUPDATE, 0)
+ .arg(0, Type.NODE_TYPE, ONE, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("QName", QNameFn.class, 0, 2, 2, BuiltInAtomicType.QNAME, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("regex-group", RegexGroup.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, XSLT, 0)
+ .arg(0, BuiltInAtomicType.INTEGER, ONE, null);
+
+ register("remove", Remove.class, 0, 2, 2, Type.ITEM_TYPE, STAR, CORE, AS_ARG0)
+ .arg(0, Type.ITEM_TYPE, STAR | GIVE, EMPTY)
+ .arg(1, BuiltInAtomicType.INTEGER, ONE, null);
+
+ register("replace", Replace.class, 0, 3, 4, BuiltInAtomicType.STRING,
+ ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null)
+ .arg(3, BuiltInAtomicType.STRING, ONE, null);
+
+ register("resolve-QName", ResolveQName.class, 0, 2, 2, BuiltInAtomicType.QNAME, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, EMPTY)
+ .arg(1, NodeKindTest.ELEMENT, ONE | LOOK, null);
+
+ register("resolve-uri#1", ResolveURI.class, 0, 1, 1, BuiltInAtomicType.ANY_URI, OPT, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null);
+
+ register("resolve-uri#2", ResolveURI.class, 0, 2, 2, BuiltInAtomicType.ANY_URI, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("reverse", Reverse.class, 0, 1, 1, Type.ITEM_TYPE, STAR, CORE, 0)
+ .arg(0, Type.ITEM_TYPE, STAR | GIVE, EMPTY);
+
+ register("root#0", Root.class, 0, 0, 0, Type.NODE_TYPE, OPT, CORE, FOCUS);
+
+ register("root#1", Root.class, 0, 1, 1, Type.NODE_TYPE, OPT, CORE, 0)
+ .arg(0, Type.NODE_TYPE, OPT | WALK, EMPTY);
+
+ register("round#1", Round.class, 0, 1, 1, BuiltInAtomicType.NUMERIC, OPT, CORE, AS_PRIM_ARG0)
+ .arg(0, BuiltInAtomicType.NUMERIC, OPT, EMPTY);
+
+ register("round#2", Round.class, 0, 2, 2, BuiltInAtomicType.NUMERIC, OPT, XPATH30, AS_PRIM_ARG0)
+ .arg(0, BuiltInAtomicType.NUMERIC, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.INTEGER, ONE, null);
+
+ register("round-half-to-even", RoundHalfToEven.class, 0, 1, 2, BuiltInAtomicType.NUMERIC, OPT, CORE, AS_PRIM_ARG0)
+ .arg(0, BuiltInAtomicType.NUMERIC, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.INTEGER, ONE, null);
+
+ register("seconds-from-dateTime", Component.class, (Component.SECONDS << 16) + StandardNames.XS_DATE_TIME, 1, 1, BuiltInAtomicType.DECIMAL, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE_TIME, OPT, EMPTY);
+
+ register("seconds-from-duration", Component.class, (Component.SECONDS << 16) + StandardNames.XS_DURATION, 1, 1, BuiltInAtomicType.DECIMAL, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DURATION, OPT, EMPTY);
+
+ register("seconds-from-time", Component.class, (Component.SECONDS << 16) + StandardNames.XS_TIME, 1, 1, BuiltInAtomicType.DECIMAL, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.TIME, OPT, EMPTY);
+
+ register("starts-with#2", StartsWith.class, 0, 2, 2, BuiltInAtomicType.BOOLEAN, ONE, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, BooleanValue.TRUE);
+
+ register("starts-with#3", StartsWith.class, 0, 3, 3, BuiltInAtomicType.BOOLEAN, ONE, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, BooleanValue.TRUE)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("static-base-uri", StaticBaseURI.class, 0, 0, 0, BuiltInAtomicType.ANY_URI, OPT, CORE, BASE);
+
+ register("string#0", StringFn.class, 0, 0, 0, BuiltInAtomicType.STRING, ONE, CORE, FOCUS);
+
+ register("string#1", StringFn.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, Type.ITEM_TYPE, OPT | TAKE, StringValue.EMPTY_STRING);
+
+ register("string-length#0", StringLength.class, 0, 0, 0, BuiltInAtomicType.INTEGER, ONE, CORE, FOCUS);
+
+ register("string-length#1", StringLength.class, 0, 1, 1, BuiltInAtomicType.INTEGER, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null);
+
+ register("string-join#1", StringJoin.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, XPATH30, 0)
+ .arg(0, BuiltInAtomicType.STRING, STAR, StringValue.EMPTY_STRING);
+
+ register("string-join#2", StringJoin.class, 0, 2, 2, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, STAR, StringValue.EMPTY_STRING)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("string-to-codepoints", StringToCodepoints.class, 0, 1, 1, BuiltInAtomicType.INTEGER, STAR, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, EMPTY);
+
+ register("subsequence", Subsequence.class, 0, 2, 3, Type.ITEM_TYPE, STAR, CORE, AS_ARG0)
+ .arg(0, Type.ITEM_TYPE, STAR | GIVE, EMPTY)
+ .arg(1, BuiltInAtomicType.NUMERIC, ONE, null)
+ .arg(2, BuiltInAtomicType.NUMERIC, ONE, null);
+
+ register("substring", Substring.class, 0, 2, 3, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING)
+ .arg(1, BuiltInAtomicType.NUMERIC, ONE, null)
+ .arg(2, BuiltInAtomicType.NUMERIC, ONE, null);
+
+ register("substring-after#2", SubstringAfter.class, 0, 2, 2, BuiltInAtomicType.STRING, ONE, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, null);
+
+ register("substring-after#3", SubstringAfter.class, 0, 3, 3, BuiltInAtomicType.STRING, ONE, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, null)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("substring-before#2", SubstringBefore.class, 0, 2, 2, BuiltInAtomicType.STRING, ONE, CORE, DCOLL)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING);
+
+ register("substring-before#3", SubstringBefore.class, 0, 3, 3, BuiltInAtomicType.STRING, ONE, CORE, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("sum", Sum.class, 0, 1, 2, BuiltInAtomicType.ANY_ATOMIC, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.ANY_ATOMIC, STAR, null)
+ .arg(1, BuiltInAtomicType.ANY_ATOMIC, OPT, null);
+
+ register("system-property", SystemProperty.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, XSLT | USE_WHEN, NS)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null);
+
+ register("timezone-from-date", Component.class, (Component.TIMEZONE << 16) + StandardNames.XS_DATE, 1, 1, BuiltInAtomicType.DAY_TIME_DURATION, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE, OPT, EMPTY);
+
+ register("timezone-from-dateTime", Component.class, (Component.TIMEZONE << 16) + StandardNames.XS_DATE_TIME, 1, 1,
+ BuiltInAtomicType.DAY_TIME_DURATION, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE_TIME, OPT, EMPTY);
+
+ register("timezone-from-time", Component.class, (Component.TIMEZONE << 16) + StandardNames.XS_TIME, 1, 1,
+ BuiltInAtomicType.DAY_TIME_DURATION, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.TIME, OPT, EMPTY);
+
+ register("trace", Trace.class, 0, 2, 2, Type.ITEM_TYPE, STAR, CORE, AS_ARG0)
+ .arg(0, Type.ITEM_TYPE, STAR, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+// register("true", BooleanFn.class, BooleanFn.TRUE, 0, 0, BuiltInAtomicType.BOOLEAN,
+// UNIT, CORE)
+
+ register("translate", Translate.class, 0, 3, 3, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("true", True.class, 0, 0, 0, BuiltInAtomicType.BOOLEAN, ONE, CORE, 0);
+
+ register("tokenize", Tokenize.class, 0, 2, 3, BuiltInAtomicType.STRING, STAR, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, EMPTY)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null)
+ .arg(2, BuiltInAtomicType.STRING, ONE, null);
+
+ register("type-available", TypeAvailable.class, 0, 1, 1, BuiltInAtomicType.BOOLEAN, ONE, XSLT | USE_WHEN, NS)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null);
+
+ register("unordered", Unordered.class, 0, 1, 1, Type.ITEM_TYPE, STAR, CORE, AS_ARG0)
+ .arg(0, Type.ITEM_TYPE, STAR | GIVE, EMPTY);
+
+ register("upper-case", UpperCase.class, 0, 1, 1, BuiltInAtomicType.STRING, ONE, CORE, 0)
+ .arg(0, BuiltInAtomicType.STRING, OPT, StringValue.EMPTY_STRING);
+
+ register("unparsed-entity-uri", UnparsedEntity.class, UnparsedEntity.URI, 1, 1, BuiltInAtomicType.ANY_URI, ONE, XSLT, FOCUS)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null);
+
+ // internal version of unparsed-entity-uri with second argument representing the current document
+ register("unparsed-entity-uri_9999_", UnparsedEntity.class, UnparsedEntity.URI, 2, 2,
+ BuiltInAtomicType.STRING, ONE, INTERNAL, 0)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null)
+ .arg(1, Type.NODE_TYPE, ONE, null);
+ // it must actually be a document node, but there's a non-standard error code
+
+ register("unparsed-entity-public-id", UnparsedEntity.class, UnparsedEntity.PUBLIC_ID, 1, 1, BuiltInAtomicType.STRING, ONE, XSLT, FOCUS)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null);
+
+ // internal version of unparsed-entity-public-id with second argument representing the current document
+ register("unparsed-entity-public-id_9999_", UnparsedEntity.class, UnparsedEntity.PUBLIC_ID, 2, 2,
+ BuiltInAtomicType.STRING, ONE, INTERNAL, 0)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null)
+ .arg(1, Type.NODE_TYPE, ONE, null);
+ // it must actually be a document node, but there's a non-standard error code
+
+ register("unparsed-text", UnparsedText.class, 0, 1, 2,
+ BuiltInAtomicType.STRING, OPT, XSLT | XPATH30, BASE)
+ .arg(0, BuiltInAtomicType.STRING, OPT, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("unparsed-text-available", UnparsedTextAvailable.class, 0, 1, 2,
+ BuiltInAtomicType.BOOLEAN, ONE, XSLT | XPATH30, BASE)
+ .arg(0, BuiltInAtomicType.STRING, ONE, null)
+ .arg(1, BuiltInAtomicType.STRING, ONE, null);
+
+ register("year-from-date", Component.class, (Component.YEAR << 16) + StandardNames.XS_DATE, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE, OPT, EMPTY);
+
+ register("year-from-dateTime", Component.class, (Component.YEAR << 16) + StandardNames.XS_DATE_TIME, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DATE_TIME, OPT, EMPTY);
+
+ register("years-from-duration", Component.class, (Component.YEAR << 16) + StandardNames.XS_DURATION, 1, 1,
+ BuiltInAtomicType.INTEGER, OPT, CORE, 0)
+ .arg(0, BuiltInAtomicType.DURATION, OPT, EMPTY);
+
+ register("zero-or-one", TreatFn.class, OPT, 1, 1, Type.ITEM_TYPE, OPT, CORE, AS_ARG0)
+ .arg(0, Type.ITEM_TYPE, OPT | GIVE, null);
+ // because we don't do draconian static type checking, we can do the work in the argument type checking code
+ }
+
+
+ /**
+ * Get the table entry for the function with a given name
+ *
+ * @param name the name of the function. This may be an unprefixed local-name for functions in the
+ * system namespace, or may use the conventional prefix "saxon:" in the case of Saxon extension functions
+ * that are specially recognized
+ * @param arity the number of arguments of the function, or -1 if any arity will do
+ * @return if the function name is known, an Entry containing information about the function. Otherwise,
+ * null
+ */
+
+ public static Entry getFunction(String name, int arity) {
+ if (arity == -1) {
+ for (int i = 0; i < 10; i++) {
+ Entry e = getFunction(name, i);
+ if (e != null) {
+ return e;
+ }
+ }
+ return null;
+ }
+ // try first for an entry of the form name#arity
+ Entry e = functionTable.get(name + '#' + arity);
+ if (e != null) {
+ return e;
+ }
+ // try for a generic entry
+ e = functionTable.get(name);
+ if (e != null && e.minArguments <= arity && e.maxArguments >= arity) {
+ return e;
+ }
+ return null;
+ }
+
+ /**
+ * An entry in the table describing the properties of a function
+ */
+ public static class Entry implements java.io.Serializable {
+ /**
+ * The name of the function: a local name in the case of functions in the standard library, or a
+ * name with the conventional prefix "saxon:" in the case of Saxon extension functions
+ */
+ public String name;
+ /**
+ * The class containing the implementation of this function (always a subclass of SystemFunction)
+ */
+ public Class implementationClass;
+ /**
+ * Some classes support more than one function. In these cases the particular function is defined
+ * by an integer opcode, whose meaning is local to the implementation class.
+ */
+ public int opcode;
+ /**
+ * The minimum number of arguments required
+ */
+ public int minArguments;
+ /**
+ * The maximum number of arguments permitted
+ */
+ public int maxArguments;
+ /**
+ * The item type of the result of the function
+ */
+ public ItemType itemType;
+ /**
+ * The cardinality of the result of the function
+ */
+ public int cardinality;
+ /**
+ * Flags indicating which host languages the function is applicable to
+ */
+ public int applicability;
+ /**
+ * The syntactic context of each argument for the purposes of streamability analysis
+ */
+ public int[] syntacticContext;
+ /**
+ * An array holding the types of the arguments to the function
+ */
+ public SequenceType[] argumentTypes;
+ /**
+ * An array holding, for each declared argument, the value that is to be returned if an empty sequence
+ * as the value of this argument allows the result to be determined irrespective of the values of the
+ * other arguments; null if there is no such calculation possible
+ */
+ public Sequence[] resultIfEmpty;
+ /**
+ * Any additional properties. Currently one bit is defined: SAME_AS_FIRST_ARGUMENT indicates that
+ * the result type is the same as the type of the first argument
+ */
+ public int properties;
+
+ /**
+ * Add information to a function entry about the argument types of the function
+ *
+ * @param a the position of the argument, counting from zero
+ * @param type the item type of the argument
+ * @param options the cardinality and usage of the argument
+ * @param resultIfEmpty the value returned by the function if an empty sequence appears as the value
+ * of this argument, in the case when this result is unaffected by any other arguments. Supply null
+ * if this does not apply.
+ * @return this entry (to allow chaining)
+ */
+
+ public Entry arg(int a, ItemType type, int options, /*@Nullable*/ Sequence resultIfEmpty) {
+ int cardinality = options & StaticProperty.CARDINALITY_MASK;
+ int syntacticContext = Expression.NAVIGATION_CONTEXT;
+ if ((options & TAKE) != 0) {
+ syntacticContext = Expression.NODE_VALUE_CONTEXT;
+ } else if ((options & GIVE) != 0) {
+ syntacticContext = Expression.INHERITED_CONTEXT;
+ } else if ((options & LOOK) != 0) {
+ syntacticContext = Expression.INSPECTION_CONTEXT;
+ } else if (type instanceof AtomicType) {
+ syntacticContext = Expression.NODE_VALUE_CONTEXT;
+ }
+ try {
+ this.argumentTypes[a] = SequenceType.makeSequenceType(type, cardinality);
+ this.resultIfEmpty[a] = resultIfEmpty;
+ this.syntacticContext[a] = syntacticContext;
+ } catch (ArrayIndexOutOfBoundsException err) {
+ System.err.println("Internal Saxon error: Can't set argument " + a + " of " + name);
+ }
+ return this;
+ }
+
+ }
+
+
+}
+
diff --git a/sf/saxon/functions/StartsWith.java b/sf/saxon/functions/StartsWith.java
new file mode 100644
index 0000000..61da01d
--- /dev/null
+++ b/sf/saxon/functions/StartsWith.java
@@ -0,0 +1,128 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.StartsWithCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.RuleBasedSubstringMatcher;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.lib.SubstringMatcher;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.StringValue;
+
+import java.text.RuleBasedCollator;
+
+
+/**
+ * Implements the fn:starts-with() function
+ */
+public class StartsWith extends CollatingFunction implements Callable {
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 2;
+ }
+
+ /**
+ * Get the effective boolean value of the expression. This returns false if the value
+ * is the empty sequence, a zero-length string, a number equal to zero, or the boolean
+ * false. Otherwise it returns true.
+ * @param context The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ try {
+ StringValue s0 = (StringValue)argument[0].evaluateItem(context);
+ StringValue s1 = (StringValue)argument[1].evaluateItem(context);
+ return startsWith(s0, s1, getCollator(context));
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+
+ public static boolean startsWith(StringValue arg0, StringValue arg1, StringCollator collator) throws XPathException {
+ if (arg1==null || arg1.isZeroLength()) {
+ return true;
+ }
+
+ if (arg0==null || arg0.isZeroLength()) {
+ return false;
+ }
+
+ String s0 = arg0.getStringValue();
+ String s1 = arg1.getStringValue();
+
+ if (collator instanceof CodepointCollator) {
+ // fast path for this common case
+ return s0.startsWith(s1, 0);
+ } else {
+ if (collator instanceof SimpleCollation &&
+ ((SimpleCollation)collator).getCollation() instanceof RuleBasedCollator) {
+ collator = new RuleBasedSubstringMatcher((RuleBasedCollator)((SimpleCollation)collator).getCollation());
+ }
+
+ if (collator instanceof SubstringMatcher) {
+ return ((SubstringMatcher)collator).startsWith(s0, s1);
+ } else {
+ throw new XPathException("The collation requested for starts-with() does not support substring matching", "FOCH0004");
+ }
+ }
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public BooleanValue evaluateItem(/*@NotNull*/ XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ try {
+ StringValue s0 = (StringValue)arguments[0].head();
+ StringValue s1 = (StringValue)arguments[1].head();
+ StringCollator collator = getCollatorFromLastArgument(arguments, 2, context);
+ return BooleanValue.get( startsWith(s0, s1, collator));
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ e.maybeSetContext(context);
+ throw e;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the StartsWith expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new StartsWithCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/StaticBaseURI.java b/sf/saxon/functions/StaticBaseURI.java
new file mode 100644
index 0000000..75383ae
--- /dev/null
+++ b/sf/saxon/functions/StaticBaseURI.java
@@ -0,0 +1,72 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AnyURIValue;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+ * This class supports the static-base-uri() function in XPath 2.0. The expression
+ * is always evaluated at compile time.
+*/
+
+public class StaticBaseURI extends CompileTimeFunction {
+
+ String staticBaseURI;
+
+ /**
+ * Bind aspects of the static context on which the particular function depends
+ *
+ * @param env the static context of the function call
+ * @throws net.sf.saxon.trans.XPathException
+ * if execution with this static context will inevitably fail
+ */
+ @Override
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ if (staticBaseURI == null) {
+ staticBaseURI = env.getBaseURI();
+ }
+ }
+
+ /**
+ * Compile time evaluation
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ if (staticBaseURI != null) {
+ return Literal.makeLiteral(new AnyURIValue(staticBaseURI));
+ }
+ String baseURI = visitor.getStaticContext().getBaseURI();
+ if (baseURI == null) {
+ return Literal.makeEmptySequence();
+ }
+ return Literal.makeLiteral(new AnyURIValue(baseURI));
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ if (staticBaseURI == null) {
+ return EmptySequence.getInstance();
+ }
+ return new AnyURIValue(staticBaseURI);
+ }
+}
+
diff --git a/sf/saxon/functions/StaticContextForSystemFunctions.java b/sf/saxon/functions/StaticContextForSystemFunctions.java
new file mode 100644
index 0000000..964ebc5
--- /dev/null
+++ b/sf/saxon/functions/StaticContextForSystemFunctions.java
@@ -0,0 +1,281 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.CollationMap;
+import net.sf.saxon.expr.EarlyEvaluationContext;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.LocationMap;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.trans.DecimalFormatManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.DecimalValue;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+/**
+ * An implementation of the StaticContext interface, holding those parts of the
+ * static context that can be used by system functions. This is especially relevant
+ * when system functions are executed dynamically via function-lookup().
+*/
+
+public class StaticContextForSystemFunctions {
+
+ private String baseURI = null;
+ private Executable executable;
+ private String defaultElementNamespace = NamespaceConstant.NULL;
+ private DecimalFormatManager decimalFormatManager = null;
+
+ public StaticContextForSystemFunctions(Executable exec) {
+ this.executable = exec;
+ }
+
+ /**
+ * Get the system configuration
+ */
+
+ public Configuration getConfiguration() {
+ return executable.getConfiguration();
+ }
+
+ /**
+ * Get the collation map in use
+ * @return the collation map
+ */
+
+ public CollationMap getCollationMap() {
+ return executable.getCollationTable();
+ }
+
+ /**
+ * Ask whether this static context is schema-aware
+ * @return true if this context is schema-aware
+ */
+
+ public boolean isSchemaAware() {
+ return executable.isSchemaAware();
+ }
+
+ /**
+ * Construct a dynamic context for early evaluation of constant subexpressions
+ */
+
+ public XPathContext makeEarlyEvaluationContext() {
+ return new EarlyEvaluationContext(getConfiguration(), getCollationMap());
+ }
+
+
+ public LocationMap getLocationMap() {
+ return null;
+ }
+
+ /**
+ * Set the base URI in the static context
+ * @param baseURI the base URI of the expression
+ */
+
+ public void setBaseURI(String baseURI) {
+ this.baseURI = baseURI;
+ }
+
+ /**
+ * Get the Base URI, for resolving any relative URI's used
+ * in the expression. Used by the document() function, resolve-uri(), etc.
+ * @return "" if no base URI has been set
+ */
+
+ public String getBaseURI() {
+ return baseURI==null ? "" : baseURI;
+ }
+
+ /**
+ * Get the function library containing all the in-scope functions available in this static
+ * context. This method is called by the XPath parser when binding a function call in the
+ * XPath expression to an implementation of the function.
+ */
+
+ public FunctionLibrary getFunctionLibrary() {
+ return null;
+ }
+
+ /**
+ * Get a named collation.
+ * @return the collation identified by the given name, as set previously using declareCollation.
+ * Return null if no collation with this name is found.
+ */
+
+ public StringCollator getCollation(String name) {
+ return getCollationMap().getNamedCollation(name);
+ }
+
+ /**
+ * Get the name of the default collation.
+ * @return the name of the default collation; or the name of the codepoint collation
+ * if no default collation has been defined
+ */
+
+ public String getDefaultCollationName() {
+ return getCollationMap().getDefaultCollationName();
+ }
+
+ /**
+ * Get the NamePool used for compiling expressions
+ */
+
+ public NamePool getNamePool() {
+ return getConfiguration().getNamePool();
+ }
+
+ /**
+ * Issue a compile-time warning. This method is used during XPath expression compilation to
+ * output warning conditions. The default implementation writes the message to the
+ * error listener registered with the Configuration.
+ */
+
+ public void issueWarning(String s, SourceLocator locator) {
+ try {
+ getConfiguration().getErrorListener().warning(new XPathException(s));
+ } catch (TransformerException e) {
+ getConfiguration().getStandardErrorOutput().println("Warning: " + s);
+ }
+ }
+
+ /**
+ * Get the system ID of the container of the expression. Used to construct error messages.
+ * @return "" always
+ */
+
+ public String getSystemId() {
+ return "";
+ }
+
+
+ /**
+ * Get the line number of the expression within that container.
+ * Used to construct error messages.
+ * @return -1 always
+ */
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+
+ /**
+ * Get the default namespace URI for elements and types
+ * Return NamespaceConstant.NULL (that is, the zero-length string) for the non-namespace
+ * @return the default namespace for elements and type
+ */
+
+ public String getDefaultElementNamespace() {
+ return defaultElementNamespace;
+ }
+
+ /**
+ * Set the default namespace for elements and types
+ * @param uri the namespace to be used for unprefixed element and type names.
+ * The value "" (or NamespaceConstant.NULL) represents the non-namespace
+ */
+
+ public void setDefaultElementNamespace(String uri) {
+ defaultElementNamespace = uri;
+ }
+
+ /**
+ * Get the default function namespace.
+ * The value "" (or NamespaceConstant.NULL) represents the non-namespace
+ * @return the default namesapce for functions
+ */
+
+ public String getDefaultFunctionNamespace() {
+ return null;
+ }
+
+
+
+ /**
+ * Get the XPath language level supported, as a string.
+ * The current levels supported are "2.0", and "3.0". The default is "2.0".
+ * If running XQuery 1.0, the value is "2.0"; if running XQuery 3.0, it is "3.0".
+ * @return the XPath language level
+ * @since 9.3
+ */
+
+ public DecimalValue getXPathLanguageLevel() {
+ return DecimalValue.TWO;
+ }
+
+ /**
+ * Determine whether Backwards Compatible Mode is used
+ * @return true if XPath 1.0 compatibility mode is to be set to true;
+ * otherwise false
+ */
+
+ public boolean isInBackwardsCompatibleMode() {
+ return false;
+ }
+
+ /**
+ * Determine whether a built-in type is available in this context. This method caters for differences
+ * between host languages as to which set of types are built in.
+ *
+ * @param type the supposedly built-in type. This will always be a type in the XS namespace.
+ * @return true if this type can be used in this static context
+ */
+
+ public boolean isAllowedBuiltInType(BuiltInAtomicType type) {
+ return true;
+ }
+
+ /**
+ * Set the DecimalFormatManager used to resolve the names of decimal formats used in calls
+ * to the format-number() function.
+ * @param manager the decimal format manager for this static context, or null if no named decimal
+ * formats are available in this environment.
+ */
+
+ public void setDecimalFormatManager(DecimalFormatManager manager) {
+ this.decimalFormatManager = manager;
+ }
+
+ /**
+ * Get the required type of the context item. If no type has been explicitly declared for the context
+ * item, an instance of AnyItemType (representing the type item()) is returned.
+ * @return the required type of the context item
+ * @since 9.3
+ */
+
+ public ItemType getRequiredContextItemType() {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Get a DecimalFormatManager to resolve the names of decimal formats used in calls
+ * to the format-number() function.
+ * @return the decimal format manager for this static context; a newly created empty
+ * DecimalFormatManager if none has been supplied
+ * @since 9.2
+ */
+
+ public DecimalFormatManager getDecimalFormatManager() {
+ if (decimalFormatManager == null) {
+ decimalFormatManager = new DecimalFormatManager();
+ }
+ return decimalFormatManager;
+ }
+
+}
+
diff --git a/sf/saxon/functions/StringFn.java b/sf/saxon/functions/StringFn.java
new file mode 100644
index 0000000..349c66e
--- /dev/null
+++ b/sf/saxon/functions/StringFn.java
@@ -0,0 +1,200 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.StringFnCompiler;
+import com.saxonica.stream.adjunct.StringFnAdjunct;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SimpleNodeConstructor;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.StringValue;
+
+
+/**
+* Implement XPath function string()
+*/
+
+public class StringFn extends SystemFunctionCall implements Callable {
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided directly. The other methods will always be available
+ * indirectly, using an implementation that relies on one of the other methods.
+ * @return the implementation method, for example {@link #ITERATE_METHOD} or {@link #EVALUATE_METHOD} or
+ * {@link #PROCESS_METHOD}
+ */
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | WATCH_METHOD;
+ }
+
+ @Override
+ public int getIntrinsicDependencies() {
+ if (getNumberOfArguments() == 0) {
+ return StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ } else {
+ return super.getIntrinsicDependencies();
+ }
+ }
+
+ /**
+ * Simplify and validate.
+ * This is a pure function so it can be simplified in advance if the arguments are known
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ useContextItemAsDefault(visitor);
+ argument[0].setFlattened(true);
+ return simplifyArguments(visitor);
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (th.isSubType(argument[0].getItemType(th), BuiltInAtomicType.STRING) &&
+ argument[0].getCardinality() == StaticProperty.EXACTLY_ONE) {
+ return argument[0];
+ }
+ if (argument[0] instanceof SimpleNodeConstructor) {
+ return ((SimpleNodeConstructor)argument[0]).getContentExpression();
+ }
+ return this;
+ }
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet the PathMapNodeSet to which the paths embodied in this expression should be added
+ * @return the pathMapNodeSet representing the points in the source document that are both reachable by this
+ * expression, and that represent possible results of this expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ /*@Nullable*/ public PathMap.PathMapNodeSet addToPathMap(PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ PathMap.PathMapNodeSet result = argument[0].addToPathMap(pathMap, pathMapNodeSet);
+ if (result != null && (result != pathMapNodeSet || argument[0] instanceof ContextItemExpression)) {
+ TypeHierarchy th = getExecutable().getConfiguration().getTypeHierarchy();
+ ItemType operandItemType = argument[0].getItemType(th);
+ if (th.relationship(NodeKindTest.ELEMENT, operandItemType) != TypeHierarchy.DISJOINT ||
+ th.relationship(NodeKindTest.DOCUMENT, operandItemType) != TypeHierarchy.DISJOINT) {
+ result.setAtomized();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ try {
+ Item arg = argument[0].evaluateItem(c);
+ if (arg==null) {
+ return StringValue.EMPTY_STRING;
+ } else if (arg instanceof StringValue && ((StringValue) arg).getItemType() == BuiltInAtomicType.STRING) {
+ return (StringValue)arg;
+ } else {
+ return StringValue.makeStringValue(arg.getStringValueCS());
+ }
+ } catch (UnsupportedOperationException e) {
+ // Cannot obtain the string value of a function item
+ XPathException err = new XPathException(e.getMessage(), "FOTY0014");
+ err.setLocator(this);
+ err.setXPathContext(c);
+ throw err;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ if (arguments.length == 0) {
+ return new StringValue(context.getContextItem().getStringValueCS());
+ } else {
+ return new StringValue(SequenceTool.getStringValue(arguments[0]));
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the StringFn expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new StringFnCompiler();
+ }
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public StringFnAdjunct getStreamingAdjunct() {
+ return new StringFnAdjunct();
+ }
+
+ //#endif
+
+
+}
+
diff --git a/sf/saxon/functions/StringJoin.java b/sf/saxon/functions/StringJoin.java
new file mode 100644
index 0000000..7c68ff9
--- /dev/null
+++ b/sf/saxon/functions/StringJoin.java
@@ -0,0 +1,310 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.StringJoinCompiler;
+import com.saxonica.stream.adjunct.StreamingAdjunct;
+import com.saxonica.stream.adjunct.StringJoinAdjunct;
+import net.sf.saxon.event.ComplexContentOutputter;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * xf:string-join(string* $sequence, string $separator)
+ */
+
+public class StringJoin extends SystemFunctionCall implements Callable {
+
+ private boolean returnEmptyIfEmpty;
+
+ /**
+ * Indicate that when the input sequence (first argument) is empty, the function should return
+ * an empty sequence rather than an empty string
+ *
+ * @param option true if an empty sequence should be returned when the input is an empty sequence.
+ */
+
+ public void setReturnEmptyIfEmpty(boolean option) {
+ returnEmptyIfEmpty = option;
+ }
+
+ public boolean isReturnEmptyIfEmpty() {
+ return returnEmptyIfEmpty;
+ }
+
+ /**
+ * Determine the cardinality of the function.
+ */
+ @Override
+ public int computeCardinality() {
+ if (returnEmptyIfEmpty) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ } else {
+ return StaticProperty.EXACTLY_ONE;
+ }
+ }
+
+ /*@NotNull*/
+ @Override
+ public Expression copy() {
+ StringJoin sj = (StringJoin) super.copy();
+ sj.returnEmptyIfEmpty = returnEmptyIfEmpty;
+ return sj;
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof StringJoin) &&
+ super.equals(o) &&
+ returnEmptyIfEmpty == ((StringJoin)o).returnEmptyIfEmpty;
+ }
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | ITEM_FEED_METHOD;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType)
+ throws XPathException {
+ Expression exp = super.optimize(visitor, contextItemType);
+ if (exp instanceof StringJoin) {
+ Expression exp2 = ((StringJoin) exp).simplifySingleton();
+ if (exp != exp2) {
+ return visitor.optimize(exp2, contextItemType);
+ }
+ }
+ return exp;
+ }
+
+ private Expression simplifySingleton() {
+ int card = argument[0].getCardinality();
+ if (!Cardinality.allowsMany(card)) {
+ if (Cardinality.allowsZero(card)) {
+ return SystemFunctionCall.makeSystemFunction("string", new Expression[]{argument[0]});
+ } else {
+ return argument[0];
+ }
+ }
+ return this;
+ }
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+
+ // This rather tortuous code is designed to ensure that we don't evaluate the
+ // separator argument unless there are at least two items in the sequence.
+
+ SequenceIterator iter = argument[0].iterate(c);
+ Item it = iter.next();
+ if (it == null) {
+ return (returnEmptyIfEmpty ? null : StringValue.EMPTY_STRING);
+ }
+
+ CharSequence first = it.getStringValueCS();
+
+ it = iter.next();
+ if (it == null) {
+ return StringValue.makeStringValue(first);
+ }
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ sb.append(first);
+
+ // Type checking ensures that the separator is not an empty sequence
+ if (argument.length == 1) {
+ sb.append(it.getStringValueCS());
+ while (true) {
+ it = iter.next();
+ if (it == null) {
+ return StringValue.makeStringValue(sb.condense());
+ }
+ sb.append(it.getStringValueCS());
+ }
+
+ } else {
+ Item sepItem = argument[1].evaluateItem(c);
+ assert sepItem != null;
+ CharSequence sep = sepItem.getStringValueCS();
+ sb.append(sep);
+ sb.append(it.getStringValueCS());
+
+ while (true) {
+ it = iter.next();
+ if (it == null) {
+ return StringValue.makeStringValue(sb.condense());
+ }
+ sb.append(sep);
+ sb.append(it.getStringValueCS());
+ }
+ }
+ }
+
+ /**
+ * Process the instruction in push mode. This avoids constructing the concatenated string
+ * in memory, instead its parts can be sent straight to the serializer.
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ // This rather tortuous code is designed to ensure that we don't evaluate the
+ // separator argument unless there are at least two items in the sequence.
+
+ SequenceReceiver out = context.getReceiver();
+ if (out instanceof ComplexContentOutputter) {
+ // Optimization is only safe if evaluated as part of a complex content constructor
+ // Start and end with an empty string to force space separation from any previous or following outputs
+ out.append(StringValue.EMPTY_STRING, 0, 0);
+
+ SequenceIterator iter = argument[0].iterate(context);
+ Item it = iter.next();
+ if (it == null) {
+ return;
+ }
+
+ CharSequence first = it.getStringValueCS();
+ out.characters(first, 0, 0);
+
+ it = iter.next();
+ if (it == null) {
+ out.append(StringValue.EMPTY_STRING, 0, 0);
+ return;
+ }
+
+ // Type checking ensures that the separator is not an empty sequence
+ if (argument.length == 1) {
+ out.characters(it.getStringValueCS(), 0, 0);
+
+ while (true) {
+ it = iter.next();
+ if (it == null) {
+ break;
+ }
+ out.characters(it.getStringValueCS(), 0, 0);
+ }
+ } else {
+ Item sepItem = argument[1].evaluateItem(context);
+ assert sepItem != null;
+ CharSequence sep = sepItem.getStringValueCS();
+ out.characters(sep, 0, 0);
+ out.characters(it.getStringValueCS(), 0, 0);
+
+ while (true) {
+ it = iter.next();
+ if (it == null) {
+ break;
+ }
+ out.characters(sep, 0, 0);
+ out.characters(it.getStringValueCS(), 0, 0);
+ }
+
+ }
+ out.append(StringValue.EMPTY_STRING, 0, 0);
+ } else {
+ out.append(evaluateItem(context), 0, 0);
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ SequenceIterator iter = arguments[0].iterate();
+ Item it = iter.next();
+ if (it == null) {
+ return (returnEmptyIfEmpty ? EmptySequence.getInstance() : StringValue.EMPTY_STRING);
+ }
+
+ CharSequence first = it.getStringValueCS();
+
+ it = iter.next();
+ if (it == null) {
+ return StringValue.makeStringValue(first);
+ }
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ sb.append(first);
+
+ // Type checking ensures that the separator is not an empty sequence
+ if (arguments.length == 1) {
+ sb.append(it.getStringValueCS());
+ while (true) {
+ it = iter.next();
+ if (it == null) {
+ return StringValue.makeStringValue(sb.condense());
+ }
+ sb.append(it.getStringValueCS());
+ }
+
+ } else {
+ Item sepItem = arguments[1].head();
+ assert sepItem != null;
+ CharSequence sep = sepItem.getStringValueCS();
+ sb.append(sep);
+ sb.append(it.getStringValueCS());
+
+ while (true) {
+ it = iter.next();
+ if (it == null) {
+ return StringValue.makeStringValue(sb.condense());
+ }
+ sb.append(sep);
+ sb.append(it.getStringValueCS());
+ }
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the StringJoin expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new StringJoinCompiler();
+ }
+//#endif
+//#ifdefined STREAM
+
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public StreamingAdjunct getStreamingAdjunct() {
+ return new StringJoinAdjunct();
+ }
+
+ //#endif
+
+}
+
diff --git a/sf/saxon/functions/StringLength.java b/sf/saxon/functions/StringLength.java
new file mode 100644
index 0000000..eee70b5
--- /dev/null
+++ b/sf/saxon/functions/StringLength.java
@@ -0,0 +1,192 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.StringLengthCompiler;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * Implement the XPath string-length() function
+ */
+
+public class StringLength extends SystemFunctionCall implements Callable {
+
+ boolean contextPossiblyUndefined = true;
+
+ /**
+ * Simplify and validate.
+ * This is a pure function so it can be simplified in advance if the arguments are known
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ //useContextItemAsDefault();
+ return simplifyArguments(visitor);
+ }
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return new IntegerValue[]{Int64Value.ZERO, MAX_STRING_LENGTH};
+ }
+
+ /**
+ * Determine the intrinsic dependencies of an expression, that is, those which are not derived
+ * from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
+ * on the context position, while (position()+1) does not. The default implementation
+ * of the method returns 0, indicating "no dependencies".
+ *
+ * @return a set of bit-significant flags identifying the "intrinsic"
+ * dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
+ */
+
+ public int getIntrinsicDependencies() {
+ int d = super.getIntrinsicDependencies();
+ if (argument.length == 0) {
+ d |= StaticProperty.DEPENDS_ON_CONTEXT_ITEM;
+ }
+ return d;
+ }
+
+ /**
+ * Pre-evaluate a function at compile time. Functions that do not allow
+ * pre-evaluation, or that need access to context information, can override this method.
+ * @param visitor an expression visitor
+ * @return the expression, either unchanged, or pre-evaluated
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ if (argument.length == 0) {
+ return this;
+ } else {
+ return Literal.makeLiteral(
+ (GroundedValue)evaluateItem(visitor.getStaticContext().makeEarlyEvaluationContext()));
+ }
+ }
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, /*@Nullable*/ ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (argument.length == 0 && contextItemType == null) {
+ XPathException err = new XPathException("The context item for string-length() is absent");
+ err.setErrorCode("XPDY0002");
+ err.setIsTypeError(true);
+ err.setLocator(this);
+ throw err;
+ } else if (contextItemType != null) {
+ contextPossiblyUndefined = contextItemType.contextMaybeUndefined;
+ }
+ return super.typeCheck(visitor, contextItemType);
+ }
+
+ /**
+ * Ask whether the context item may possibly be undefined
+ * @return true if it might be undefined
+ */
+
+ public boolean isContextPossiblyUndefined() {
+ return contextPossiblyUndefined;
+ }
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public Int64Value evaluateItem(XPathContext c) throws XPathException {
+ AtomicValue sv;
+ if (argument.length == 0) {
+ final Item contextItem = c.getContextItem();
+ if (contextItem == null) {
+ dynamicError("The context item for string-length() is not set", "XPDY0002", c);
+ return null;
+ }
+ sv = StringValue.makeStringValue(contextItem.getStringValueCS());
+ } else {
+ sv = (AtomicValue)argument[0].evaluateItem(c);
+ }
+ if (sv==null) {
+ return Int64Value.ZERO;
+ }
+
+ if (sv instanceof StringValue) {
+ return Int64Value.makeIntegerValue(((StringValue)sv).getStringLength());
+ } else {
+ CharSequence s = sv.getStringValueCS();
+ return Int64Value.makeIntegerValue(StringValue.getStringLength(s));
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ AtomicValue sv;
+ if (argument.length == 0) {
+ final Item contextItem = context.getContextItem();
+ if (contextItem == null) {
+ dynamicError("The context item for string-length() is not set", "XPDY0002", context);
+ return null;
+ }
+ sv = StringValue.makeStringValue(contextItem.getStringValueCS());
+ } else {
+ sv = (AtomicValue)arguments[0].head();
+ }
+ if (sv==null) {
+ return Int64Value.ZERO;
+ }
+
+ if (sv instanceof StringValue) {
+ return Int64Value.makeIntegerValue(((StringValue)sv).getStringLength());
+ } else {
+ CharSequence s = sv.getStringValueCS();
+ return Int64Value.makeIntegerValue(StringValue.getStringLength(s));
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the StringLength expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new StringLengthCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/StringToCodepoints.java b/sf/saxon/functions/StringToCodepoints.java
new file mode 100644
index 0000000..aa430e5
--- /dev/null
+++ b/sf/saxon/functions/StringToCodepoints.java
@@ -0,0 +1,64 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * This class supports the function string-to-codepoints()
+ */
+
+public class StringToCodepoints extends SystemFunctionCall implements Callable {
+
+ /**
+ * For an expression that returns an integer or a sequence of integers, get
+ * a lower and upper bound on the values of the integers that may be returned, from
+ * static analysis. The default implementation returns null, meaning "unknown" or
+ * "not applicable". Other implementations return an array of two IntegerValue objects,
+ * representing the lower and upper bounds respectively. The values
+ * UNBOUNDED_LOWER and UNBOUNDED_UPPER are used by convention to indicate that
+ * the value may be arbitrarily large. The values MAX_STRING_LENGTH and MAX_SEQUENCE_LENGTH
+ * are used to indicate values limited by the size of a string or the size of a sequence.
+ *
+ * @return the lower and upper bounds of integer values in the result, or null to indicate
+ * unknown or not applicable.
+ */
+ @Override
+ public IntegerValue[] getIntegerBounds() {
+ return new IntegerValue[]{Int64Value.PLUS_ONE, Int64Value.makeIntegerValue(1114111)};
+ }
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext c) throws XPathException {
+ Item item = argument[0].evaluateItem(c);
+ if (item==null) {
+ return EmptyIterator.getInstance();
+ }
+ return ((StringValue)item).iterateCharacters();
+ }
+
+ public Sequence call(/*@NotNull*/ XPathContext context, Sequence[] arguments) throws XPathException {
+ if(arguments.length==0){
+ return EmptySequence.getInstance();
+ }
+ return SequenceTool.toLazySequence(((StringValue) arguments[0].head()).iterateCharacters());
+ }
+
+}
+
diff --git a/sf/saxon/functions/Subsequence.java b/sf/saxon/functions/Subsequence.java
new file mode 100644
index 0000000..c96b751
--- /dev/null
+++ b/sf/saxon/functions/Subsequence.java
@@ -0,0 +1,282 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.pattern.SimplePositionalPattern;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.NumericValue;
+
+import java.util.List;
+
+/**
+* Implements the XPath 2.0 subsequence() function
+*/
+
+
+public class Subsequence extends SystemFunctionCall implements Callable {
+
+ /**
+ * Determine the data type of the items in the sequence
+ * @return the type of the argument
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return argument[0].getItemType(th);
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-significant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ return argument[0].getSpecialProperties();
+ }
+
+
+ /**
+ * Determine the cardinality of the function.
+ */
+
+ public int computeCardinality() {
+ if (getNumberOfArguments() == 3 && Literal.isConstantOne(argument[2])) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+ return argument[0].getCardinality() | StaticProperty.ALLOWS_ZERO;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ if (e != this) {
+ return e;
+ }
+ if (getNumberOfArguments() == 2 && Literal.isAtomic(argument[1]) && !(argument[0] instanceof ErrorExpression)) {
+ NumericValue start = (NumericValue)((Literal)argument[1]).getValue();
+ start = start.round(0); // TODO: shouldn't we always round up?
+ long intstart = start.longValue();
+ if (intstart > Integer.MAX_VALUE) {
+ return Literal.makeEmptySequence();
+ }
+ if (intstart <= 0) {
+ return argument[0];
+ }
+ return new TailExpression(argument[0], (int)intstart);
+ }
+ return this;
+ }
+
+//#ifdefined STREAM
+
+ /**
+ * Convert this expression to a streaming pattern (a pattern used internally to match nodes during
+ * push processing of an event stream)
+ *
+ * @param config the Saxon configuration
+ * @param reasonForFailure a list which will be populated with messages giving reasons why the
+ * expression cannot be converted
+ * @return the equivalent pattern if conversion succeeds; otherwise null
+ */
+ @Override
+ public Pattern toStreamingPattern(Configuration config, List<String> reasonForFailure) {
+ if (getNumberOfArguments() == 3 && Literal.isConstantOne(getArguments()[1])) {
+ // child::x[position() <= N]
+ TypeHierarchy th = config.getTypeHierarchy();
+ Expression base = getArguments()[0];
+ if (base instanceof AxisExpression &&
+ ((AxisExpression)base).getAxis() == AxisInfo.CHILD &&
+ base.getItemType(th).getPrimitiveType() == Type.ELEMENT) {
+ return new SimplePositionalPattern(
+ (NodeTest)base.getItemType(th),
+ getArguments()[2],
+ Token.FLE);
+ }
+ }
+ return super.toStreamingPattern(config, reasonForFailure);
+ }
+
+//#endif
+
+ /**
+ * Evaluate the function to return an iteration of selected nodes.
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<? extends Item> iterate(XPathContext context) throws XPathException {
+ SequenceIterator seq = argument[0].iterate(context);
+ AtomicValue startVal0 = (AtomicValue)argument[1].evaluateItem(context);
+ NumericValue startVal = (NumericValue)startVal0;
+
+ if (argument.length == 2) {
+
+ return subSequence(seq, startVal, null, context);
+
+ } else {
+
+ // There are three arguments
+
+ AtomicValue lengthVal0 = (AtomicValue)argument[2].evaluateItem(context);
+ NumericValue lengthVal = (NumericValue)lengthVal0;
+
+ return subSequence(seq, startVal, lengthVal, context);
+ }
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException{
+
+ return SequenceTool.toLazySequence(subSequence(
+ arguments[0].iterate(),
+ (NumericValue) arguments[1].head(),
+ (arguments.length == 2 ? null : (NumericValue) arguments[2].head()),
+ context));
+
+ }
+
+
+ public static SequenceIterator subSequence(SequenceIterator seq, NumericValue startVal, NumericValue lengthVal, XPathContext context)
+ throws XPathException{
+
+
+ if (lengthVal == (null)) {
+ long lstart;
+ if (startVal instanceof Int64Value) {
+ lstart = startVal.longValue();
+ if (lstart <= 1) {
+ return seq;
+ }
+ } else if (startVal.isNaN()) {
+ return EmptyIterator.getInstance();
+ } else {
+ startVal = startVal.round(0);
+ if (startVal.compareTo(Int64Value.PLUS_ONE) <= 0) {
+ return seq;
+ } else if (startVal.compareTo(Int64Value.MAX_LONG) > 0) {
+ return EmptyIterator.getInstance();
+ } else {
+ lstart = startVal.longValue();
+ }
+ }
+
+ if (lstart > Integer.MAX_VALUE) {
+ // we don't allow sequences longer than an this
+ return EmptyIterator.getInstance();
+ }
+
+ return TailIterator.make(seq, (int)lstart);
+
+ } else {
+
+ // There are three arguments
+
+
+ if (startVal instanceof Int64Value && lengthVal instanceof Int64Value) {
+ long lstart = startVal.longValue();
+ if (lstart > Integer.MAX_VALUE) {
+ return EmptyIterator.getInstance();
+ }
+ long llength = lengthVal.longValue();
+ if (llength > Integer.MAX_VALUE) {
+ llength = Integer.MAX_VALUE;
+ }
+ if (llength < 1) {
+ return EmptyIterator.getInstance();
+ }
+ long lend = lstart + llength - 1;
+ if (lend < 1) {
+ return EmptyIterator.getInstance();
+ }
+ int start = (lstart < 1 ? 1 : (int)lstart);
+ return SubsequenceIterator.make(seq, start, (int)lend);
+ } else {
+ if (startVal.isNaN()) {
+ return EmptyIterator.getInstance();
+ }
+ if (startVal.compareTo(Int64Value.MAX_LONG) > 0) {
+ return EmptyIterator.getInstance();
+ }
+ startVal = startVal.round(0);
+
+ if (lengthVal.isNaN()) {
+ return EmptyIterator.getInstance();
+ }
+ lengthVal = lengthVal.round(0);
+
+ if (lengthVal.compareTo(Int64Value.ZERO) <= 0) {
+ return EmptyIterator.getInstance();
+ }
+ NumericValue rend = (NumericValue)ArithmeticExpression.compute(
+ startVal, Calculator.PLUS, lengthVal, context);
+ if (rend.isNaN()) {
+ // Can happen when start = -INF, length = +INF
+ return EmptyIterator.getInstance();
+ }
+ rend = (NumericValue)ArithmeticExpression.compute(
+ rend, Calculator.MINUS, Int64Value.PLUS_ONE, context);
+ if (rend.compareTo(Int64Value.ZERO) <= 0) {
+ return EmptyIterator.getInstance();
+ }
+
+ long lstart;
+ if (startVal.compareTo(Int64Value.PLUS_ONE) <= 0) {
+ lstart = 1;
+ } else {
+ lstart = startVal.longValue();
+ }
+ if (lstart > Integer.MAX_VALUE) {
+ return EmptyIterator.getInstance();
+ }
+
+ long lend;
+ if (rend.compareTo(Int64Value.MAX_LONG) >= 0) {
+ lend = Integer.MAX_VALUE;
+ } else {
+ lend = rend.longValue();
+ }
+ return SubsequenceIterator.make(seq, (int)lstart, (int)lend);
+
+ }
+ }
+
+ }
+
+
+}
+
diff --git a/sf/saxon/functions/Substring.java b/sf/saxon/functions/Substring.java
new file mode 100644
index 0000000..53dc403
--- /dev/null
+++ b/sf/saxon/functions/Substring.java
@@ -0,0 +1,266 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SubstringCompiler;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Int64Value;
+import net.sf.saxon.value.NumericValue;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * This class implements the XPath substring() function
+ */
+
+public class Substring extends SystemFunctionCall implements Callable {
+
+ /**
+ * Type-check the expression. This also calls preEvaluate() to evaluate the function
+ * if all the arguments are constant; functions that do not require this behavior
+ * can override the preEvaluate method.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e2 = super.typeCheck(visitor, contextItemType);
+ if (e2 != this) {
+ return e2;
+ }
+ TypeHierarchy th = visitor.getConfiguration().getTypeHierarchy();
+ if (argument[1] instanceof NumberFn) {
+ Expression a1 = ((NumberFn)argument[1]).getArguments()[0];
+ if (th.isSubType(a1.getItemType(th), BuiltInAtomicType.INTEGER)) {
+ argument[1] = a1;
+ }
+ }
+ if (argument.length > 2 && argument[2] instanceof NumberFn) {
+ Expression a2 = ((NumberFn)argument[2]).getArguments()[0];
+ if (th.isSubType(a2.getItemType(th), BuiltInAtomicType.INTEGER)) {
+ argument[2] = a2;
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public StringValue evaluateItem(XPathContext context) throws XPathException {
+
+ AtomicValue av = (AtomicValue)argument[0].evaluateItem(context);
+ if (av==null) {
+ return StringValue.EMPTY_STRING;
+ }
+ StringValue sv = (StringValue)av;
+ if (sv.isZeroLength()) {
+ return StringValue.EMPTY_STRING;
+ }
+
+ AtomicValue a1 = (AtomicValue)argument[1].evaluateItem(context);
+ NumericValue a = (NumericValue)a1;
+
+ if (argument.length==2) {
+ StringValue result = StringValue.makeStringValue(substring(sv, a));
+ if (sv.isKnownToContainNoSurrogates()) {
+ result.setContainsNoSurrogates();
+ }
+ return result;
+ } else {
+ AtomicValue b2 = (AtomicValue)argument[2].evaluateItem(context);
+ NumericValue b = (NumericValue)b2;
+ StringValue result = StringValue.makeStringValue(substring(sv, a, b, context));
+ if (sv.isKnownToContainNoSurrogates()) {
+ result.setContainsNoSurrogates();
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Implement the substring function with two arguments.
+ * @param sv the string value
+ * @param start the numeric offset (1-based) of the first character to be included in the result
+ * (if not an integer, the XPath rules apply)
+ * @return the substring starting at this position.
+ */
+
+ public static UnicodeString substring(StringValue sv, NumericValue start) {
+ UnicodeString s = sv.getUnicodeString();
+ int slength = s.length();
+
+ long lstart;
+ if (start instanceof Int64Value) {
+ //noinspection RedundantCast
+ lstart = ((Int64Value)start).longValue();
+ if (lstart > slength) {
+ return UnicodeString.EMPTY_STRING;
+ } else if (lstart <= 0) {
+ lstart = 1;
+ }
+ } else {
+ //NumericValue rstart = start.round();
+ // We need to be careful to handle cases such as plus/minus infinity
+ if (start.isNaN()) {
+ return UnicodeString.EMPTY_STRING;
+ } else if (start.signum() <= 0) {
+ return s;
+ } else if (start.compareTo(slength) > 0) {
+ // this works even where the string contains surrogate pairs,
+ // because the Java length is always >= the XPath length
+ return UnicodeString.EMPTY_STRING;
+ } else {
+ lstart = Math.round(start.getDoubleValue());
+ }
+ }
+
+ if (lstart > s.length()) {
+ return UnicodeString.EMPTY_STRING;
+ }
+ return s.substring((int)lstart-1, s.length());
+ }
+
+ /**
+ * Implement the substring function with three arguments.
+ * @param sv the string value
+ * @param start the numeric offset (1-based) of the first character to be included in the result
+ * (if not an integer, the XPath rules apply)
+ * @param len the length of the required substring (again, XPath rules apply)
+ * @param context the XPath dynamic context. Provided because some arithmetic computations require it
+ * @return the substring starting at this position.
+ */
+
+ public static UnicodeString substring(StringValue sv, NumericValue start, /*@NotNull*/ NumericValue len, XPathContext context) {
+
+ CharSequence s = sv.getStringValueCS();
+ int slength = s.length();
+
+ long lstart;
+ if (start instanceof Int64Value) {
+ //noinspection RedundantCast
+ lstart = ((Int64Value)start).longValue();
+ if (lstart > slength) {
+ return UnicodeString.EMPTY_STRING;
+ }
+ } else {
+ start = start.round(0);
+ // TODO: convert directly to a long using Math.round, as in the 2-argument case
+ // We need to be careful to handle cases such as plus/minus infinity and NaN
+ if (start.isNaN()) {
+ return UnicodeString.EMPTY_STRING;
+ } else if (start.signum() <= 0) {
+ lstart = 0;
+ } else if (start.compareTo(slength) > 0) {
+ // this works even where the string contains surrogate pairs,
+ // because the Java length is always >= the XPath length
+ return UnicodeString.EMPTY_STRING;
+ } else {
+ try {
+ lstart = start.longValue();
+ } catch (XPathException err) {
+ // this shouldn't happen unless the string length exceeds the bounds
+ // of a long
+ throw new AssertionError("string length out of permissible range");
+ }
+ }
+ }
+
+ NumericValue end;
+ try {
+ //end = start.arithmetic(Token.PLUS, len.round(), context);
+ end = (NumericValue)ArithmeticExpression.compute(start, Calculator.PLUS, len.round(0), context);
+ } catch (XPathException e) {
+ throw new AssertionError("Unexpected arithmetic failure in substring");
+ }
+ long lend;
+ if (end instanceof Int64Value) {
+ //noinspection RedundantCast
+ lend = ((Int64Value)end).longValue();
+ } else {
+ // We need to be careful to handle cases such as plus/minus infinity and NaN
+ if (end.isNaN()) {
+ return UnicodeString.EMPTY_STRING;
+ } else if (end.signum() <= 0) {
+ return UnicodeString.EMPTY_STRING;
+ } else if (end.compareTo(slength) > 0) {
+ // this works even where the string contains surrogate pairs,
+ // because the Java length is always >= the XPath length
+ lend = slength+1;
+ } else {
+ try {
+ lend = end.ceiling().longValue();
+ } catch (XPathException err) {
+ // this shouldn't happen unless the string length exceeds the bounds
+ // of a long
+ throw new AssertionError("string length out of permissible range");
+ }
+ }
+ }
+
+ if (lend < lstart) {
+ return UnicodeString.EMPTY_STRING;
+ }
+
+ UnicodeString us = sv.getUnicodeString();
+ int clength = us.length();
+ int a1 = (int)lstart - 1;
+ if (a1 >= clength) {
+ return UnicodeString.EMPTY_STRING;
+ }
+ int a2 = Math.min(clength, (int)lend - 1);
+ if (a1 < 0) {
+ if (a2 < 0) {
+ return UnicodeString.EMPTY_STRING;
+ } else {
+ a1 = 0;
+ }
+ }
+ return us.substring(a1, a2);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue arg0 = (StringValue)arguments[0].head();
+ NumericValue arg1 = (NumericValue)arguments[1].head();
+ if (arguments.length == 2) {
+ return StringValue.makeStringValue(substring(arg0, arg1));
+ } else {
+ NumericValue arg2 = (NumericValue)arguments[2].head();
+ return StringValue.makeStringValue(substring(arg0, arg1, arg2, context));
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Substring expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SubstringCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/SubstringAfter.java b/sf/saxon/functions/SubstringAfter.java
new file mode 100644
index 0000000..cc7af84
--- /dev/null
+++ b/sf/saxon/functions/SubstringAfter.java
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SubstringAfterCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.RuleBasedSubstringMatcher;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.lib.SubstringMatcher;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+import java.text.RuleBasedCollator;
+
+/**
+ * Implements the fn:substring-after() function
+ */
+public class SubstringAfter extends CollatingFunction implements Callable {
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 2;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ /*@Nullable*/ public StringValue evaluateItem(XPathContext context) throws XPathException {
+ StringValue arg1 = (StringValue)argument[0].evaluateItem(context);
+ StringValue arg2 = (StringValue)argument[1].evaluateItem(context);
+ StringCollator collator = getCollator(context);
+ return substringAfter(context, arg1, arg2, collator);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue arg1 = (StringValue)arguments[0].head();
+ StringValue arg2 = (StringValue)arguments[1].head();
+ StringCollator collator = getCollatorFromLastArgument(arguments, 2, context);
+ return substringAfter(context, arg1, arg2, collator);
+ }
+
+ private StringValue substringAfter(XPathContext context, StringValue arg1, StringValue arg2, StringCollator collator) throws XPathException {
+ if (arg1 == null) {
+ arg1 = StringValue.EMPTY_STRING;
+ }
+ if (arg2 == null) {
+ arg2 = StringValue.EMPTY_STRING;
+ }
+ if (arg2.isZeroLength()) {
+ return arg1;
+ }
+ if (arg1.isZeroLength()) {
+ return StringValue.EMPTY_STRING;
+ }
+
+ String s1 = arg1.getStringValue();
+ String s2 = arg2.getStringValue();
+
+ String result = null;
+ if (collator instanceof CodepointCollator) {
+ // fast path for this common case
+ int i = s1.indexOf(s2);
+ if (i < 0) {
+ result = "";
+ } else {
+ result = s1.substring(i + s2.length());
+ }
+
+ } else {
+ if (collator instanceof SimpleCollation &&
+ ((SimpleCollation)collator).getCollation() instanceof RuleBasedCollator) {
+ collator = new RuleBasedSubstringMatcher((RuleBasedCollator)((SimpleCollation)collator).getCollation());
+ }
+
+ if (collator instanceof SubstringMatcher) {
+ result = ((SubstringMatcher)collator).substringAfter(s1, s2);
+ } else {
+ dynamicError("The collation requested for " + getDisplayName() +
+ " does not support substring matching", "FOCH0004", context);
+ }
+ }
+ StringValue s = StringValue.makeStringValue(result);
+ if (arg1.isKnownToContainNoSurrogates()) {
+ s.setContainsNoSurrogates();
+ }
+ return s;
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the SubstringAfter expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SubstringAfterCompiler();
+ }
+//#endif
+
+}
\ No newline at end of file
diff --git a/sf/saxon/functions/SubstringBefore.java b/sf/saxon/functions/SubstringBefore.java
new file mode 100644
index 0000000..ed1747e
--- /dev/null
+++ b/sf/saxon/functions/SubstringBefore.java
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SubstringBeforeCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.RuleBasedSubstringMatcher;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.lib.SubstringMatcher;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+import java.text.RuleBasedCollator;
+
+
+/**
+ * Implements the fn:substring-before() function
+ */
+public class SubstringBefore extends CollatingFunction implements Callable {
+
+ /**
+ * Get the argument position (0-based) containing the collation name
+ * @return the position of the argument containing the collation URI
+ */
+ @Override
+ protected int getCollationArgument() {
+ return 2;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public Item evaluateItem(/*@NotNull*/ XPathContext context) throws XPathException {
+ StringValue arg1 = (StringValue)argument[1].evaluateItem(context);
+ if (arg1==null || arg1.isZeroLength()) {
+ return StringValue.EMPTY_STRING;
+ }
+
+ StringValue arg0 = (StringValue)argument[0].evaluateItem(context);
+ if (arg0==null || arg0.isZeroLength()) {
+ return StringValue.EMPTY_STRING;
+ }
+ StringCollator collator = getCollator(context);
+ return substringBefore(context, arg0, arg1, collator);
+ }
+
+ private StringValue substringBefore(XPathContext context, StringValue arg0, StringValue arg1, StringCollator collator) throws XPathException {
+ String s0 = arg0.getStringValue();
+ String s1 = arg1.getStringValue();
+ StringValue result;
+ if (collator instanceof CodepointCollator) {
+ // fast path for this common case
+ int j = s0.indexOf(s1);
+ if (j<0) {
+ result = StringValue.EMPTY_STRING;
+ } else {
+ result = new StringValue(s0.substring(0, j));
+ }
+
+ } else {
+ if (collator instanceof SimpleCollation &&
+ ((SimpleCollation)collator).getCollation() instanceof RuleBasedCollator) {
+ collator = new RuleBasedSubstringMatcher((RuleBasedCollator)((SimpleCollation)collator).getCollation());
+ }
+
+ if (collator instanceof SubstringMatcher) {
+ result = new StringValue(((SubstringMatcher)collator).substringBefore(s0, s1));
+ } else {
+ dynamicError("The collation requested for " + getDisplayName() +
+ " does not support substring matching", "FOCH0004", context);
+ return null;
+ }
+ }
+
+ if (arg0.isKnownToContainNoSurrogates()) {
+ result.setContainsNoSurrogates();
+ }
+ return result;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue arg1 = (StringValue)arguments[1].head();
+ if (arg1==null || arg1.isZeroLength()) {
+ return StringValue.EMPTY_STRING;
+ }
+
+ StringValue arg0 = (StringValue)arguments[0].head();
+ if (arg0==null || arg0.isZeroLength()) {
+ return StringValue.EMPTY_STRING;
+ }
+
+ StringCollator collator = getCollatorFromLastArgument(arguments, 2, context);
+ return substringBefore(context, arg0, arg1, collator);
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the SubstringBefore expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SubstringBeforeCompiler();
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/functions/Sum.java b/sf/saxon/functions/Sum.java
new file mode 100644
index 0000000..c29bfb4
--- /dev/null
+++ b/sf/saxon/functions/Sum.java
@@ -0,0 +1,208 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.SumCompiler;
+import com.saxonica.stream.adjunct.StreamingAdjunct;
+import com.saxonica.stream.adjunct.SumAdjunct;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+import javax.xml.transform.SourceLocator;
+
+
+/**
+ * Implementation of the fn:sum function
+ */
+public class Sum extends Aggregate {
+
+ /**
+ * Get implementation method
+ * @return a value that indicates this function is capable of being streamed
+ */
+
+ public int getImplementationMethod() {
+ return super.getImplementationMethod() | ITEM_FEED_METHOD;
+ }
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ ItemType base = Atomizer.getAtomizedItemType(argument[0], false, th);
+ if (base.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ base = BuiltInAtomicType.DOUBLE;
+ }
+ if (Cardinality.allowsZero(argument[0].getCardinality())) {
+ if (argument.length == 1) {
+ return Type.getCommonSuperType(base, BuiltInAtomicType.INTEGER, th);
+ } else {
+ return Type.getCommonSuperType(base, argument[1].getItemType(th), th);
+ }
+ } else {
+ return base.getPrimitiveItemType();
+ }
+ }
+
+ public int computeCardinality(){
+ if(argument.length == 1 || argument[1].getCardinality() == 1){
+ return StaticProperty.EXACTLY_ONE;
+ }else{
+ return super.computeCardinality();
+ }
+
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ /*@Nullable*/ public AtomicValue evaluateItem(XPathContext context) throws XPathException {
+ AtomicValue sum = total(argument[0].iterate(context), context, this);
+ if (sum != null) {
+ return sum;
+ } else {
+ // the sequence was empty
+ if (argument.length == 2) {
+ return (AtomicValue)argument[1].evaluateItem(context);
+ } else {
+ return Int64Value.ZERO;
+ }
+ }
+ }
+
+ /**
+ * Calculate the total of a sequence.
+ * @param iter iterator over the items to be totalled
+ * @param context the XPath dynamic context
+ * @param location location of the expression in the source for diagnostics
+ * @return the total, according to the rules of the XPath sum() function, but returning null
+ * if the sequence is empty. (It's then up to the caller to decide what the correct result is
+ * for an empty sequence.
+ */
+
+ public static AtomicValue total(SequenceIterator iter, XPathContext context, SourceLocator location)
+ throws XPathException {
+ ConversionRules rules = context.getConfiguration().getConversionRules();
+ StringConverter toDouble = rules.getStringConverter(BuiltInAtomicType.DOUBLE);
+ AtomicValue sum = (AtomicValue)iter.next();
+ if (sum == null) {
+ // the sequence is empty
+ return null;
+ }
+ if (sum instanceof UntypedAtomicValue) {
+ try {
+ sum = toDouble.convert(sum).asAtomic();
+ } catch (XPathException e) {
+ e.maybeSetLocation(location);
+ throw e;
+ }
+ }
+ if (sum instanceof NumericValue) {
+ while (true) {
+ AtomicValue next = (AtomicValue)iter.next();
+ if (next == null) {
+ return sum;
+ }
+ if (next instanceof UntypedAtomicValue) {
+ next = toDouble.convert(next).asAtomic();
+ } else if (!(next instanceof NumericValue)) {
+ XPathException err = new XPathException("Input to sum() contains a mix of numeric and non-numeric values");
+ err.setXPathContext(context);
+ err.setErrorCode("FORG0006");
+ err.setLocator(location);
+ throw err;
+ }
+ //sum = ((NumericValue)sum).arithmetic(Token.PLUS, (NumericValue)next, context);
+ sum = ArithmeticExpression.compute(sum, Calculator.PLUS, next, context);
+ if (sum.isNaN() && sum instanceof DoubleValue) {
+ // take an early bath, once we've got a double NaN it's not going to change
+ return sum;
+ }
+ }
+ } else if (sum instanceof DurationValue) {
+ if (!((sum instanceof DayTimeDurationValue) || (sum instanceof YearMonthDurationValue))) {
+ XPathException err = new XPathException("Input to sum() contains a duration that is neither a dayTimeDuration nor a yearMonthDuration");
+ err.setXPathContext(context);
+ err.setErrorCode("FORG0006");
+ err.setLocator(location);
+ throw err;
+ }
+ while (true) {
+ AtomicValue next = (AtomicValue)iter.next();
+ if (next == null) {
+ return sum;
+ }
+ if (!(next instanceof DurationValue)) {
+ XPathException err = new XPathException("Input to sum() contains a mix of duration and non-duration values");
+ err.setXPathContext(context);
+ err.setErrorCode("FORG0006");
+ err.setLocator(location);
+ throw err;
+ }
+ sum = ((DurationValue)sum).add((DurationValue)next);
+ }
+ } else {
+ XPathException err = new XPathException(
+ "Input to sum() contains a value of type " +
+ sum.getPrimitiveType().getDisplayName() +
+ " which is neither numeric, nor a duration");
+ err.setXPathContext(context);
+ err.setErrorCode("FORG0006");
+ err.setLocator(location);
+ throw err;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ AtomicValue total = total(arguments[0].iterate(), context, this);
+ if (total == null) {
+ return (arguments.length == 2 ? arguments[1].head() : IntegerValue.ZERO);
+ } else {
+ return total;
+ }
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Sum expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new SumCompiler();
+ }
+//#endif
+//#ifdefined STREAM
+ /**
+ * Get a class that supports streamed evaluation of this expression
+ *
+ * @return the relevant StreamingAdjunct, or null if none is available
+ */
+ @Override
+ public StreamingAdjunct getStreamingAdjunct() {
+ return new SumAdjunct();
+ }
+
+ //#endif
+}
+
diff --git a/sf/saxon/functions/SystemFunctionCall.java b/sf/saxon/functions/SystemFunctionCall.java
new file mode 100644
index 0000000..8457eff
--- /dev/null
+++ b/sf/saxon/functions/SystemFunctionCall.java
@@ -0,0 +1,472 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.functions.hof.SpecificFunctionType;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.FunctionItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+* Abstract superclass for calls to functions in the standard function library
+*/
+
+public abstract class SystemFunctionCall extends FunctionCall implements Callable {
+
+ public Callable getConvertingCallable() {
+ final Callable raw = this;
+ return new Callable() {
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ Sequence[] convertedArgs = new Sequence[arguments.length];
+ for (int i=0; i<arguments.length; i++) {
+ RoleLocator role = new RoleLocator(RoleLocator.FUNCTION, getFunctionName(), i);
+ SequenceType requiredType = getRequiredType(i);
+ TypeHierarchy th = context.getConfiguration().getTypeHierarchy();
+ convertedArgs[i] = th.applyFunctionConversionRules(
+ arguments[i], requiredType, role, SystemFunctionCall.this);
+ }
+ return raw.call(context, convertedArgs);
+ }
+ };
+ }
+
+ /**
+ * Make a system function call (one in the standard function namespace).
+ * @param name The local name of the function.
+ * @param arguments the arguments to the function call
+ * @return a FunctionCall that implements this function, if it
+ * exists, or null if the function is unknown.
+ */
+
+ /*@Nullable*/ public static FunctionCall makeSystemFunction(String name, /*@NotNull*/ Expression[] arguments) {
+ StandardFunction.Entry entry = StandardFunction.getFunction(name, arguments.length);
+ if (entry==null) {
+ return null;
+ }
+ Class functionClass = entry.implementationClass;
+ try {
+ SystemFunctionCall f = (SystemFunctionCall)functionClass.newInstance();
+ f.setDetails(entry);
+ f.setFunctionName(new StructuredQName("", NamespaceConstant.FN, name));
+ f.setArguments(arguments);
+ return f;
+ } catch (IllegalAccessException err) {
+ return null;
+ } catch (InstantiationException err) {
+ return null;
+ }
+ }
+
+
+ private StandardFunction.Entry details;
+ protected int operation;
+
+ /**
+ * Set the details of this type of function
+ * @param entry information giving details of the function signature
+ */
+
+ public void setDetails(StandardFunction.Entry entry) {
+ details = entry;
+ operation = details.opcode;
+ }
+
+ /**
+ * Get the details of the function signature
+ * @return information about the function signature
+ */
+
+ public StandardFunction.Entry getDetails() {
+ return details;
+ }
+
+ public int getOperation(){
+ return operation;
+ }
+
+//#ifdefined HOF
+ /**
+ * Get the item type of the function item
+ *
+ * @param th the type hierarchy cache
+ * @return the function item's type
+ */
+ public FunctionItemType getFunctionItemType(TypeHierarchy th) {
+ SequenceType[] argTypes = getDetails().argumentTypes;
+ if (argTypes.length > argument.length) {
+ argTypes = new SequenceType[argument.length];
+ System.arraycopy(getDetails().argumentTypes, 0, argTypes, 0, argument.length);
+ }
+ return new SpecificFunctionType(argTypes,
+ SequenceType.makeSequenceType(getDetails().itemType, getDetails().cardinality));
+ }
+//#endif
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided directly. The other methods will always be available
+ * indirectly, using an implementation that relies on one of the other methods.
+ * @return the implementation method, for example {@link #ITERATE_METHOD} or {@link #EVALUATE_METHOD} or
+ * {@link #PROCESS_METHOD}
+ */
+
+ public int getImplementationMethod() {
+ boolean feedable = getNumberOfArguments() > 0 &&
+ !Cardinality.allowsMany(getRequiredType(0).getCardinality());
+ int methods = super.getImplementationMethod();
+ if (feedable) {
+ methods |= Expression.ITEM_FEED_METHOD;
+ }
+ return methods;
+ }
+
+ public NodeInfo getDefaultArgumentNode(XPathContext context, Sequence[] arguments, String funcName) throws XPathException{
+ if (arguments.length == 0) {
+ Item item = context.getContextItem();
+ if (item == null) {
+ throw new XPathException("Context item for "+funcName+" is absent", "XPDY0002");
+ } else if (!(item instanceof NodeInfo)) {
+ throw new XPathException("Context item for "+funcName+" must be a node", "XPTY0004");
+ } else {
+ return (NodeInfo)item;
+ }
+ } else {
+ return (NodeInfo)arguments[0].head();
+ }
+ }
+
+ /**
+ * Bind aspects of the static context on which the particular function depends
+ * @param env the static context of the function call
+ * @throws XPathException if execution with this static context will inevitably fail
+ */
+
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ // default: no action
+ }
+
+ /**
+ * Method called during static type checking
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ checkArgumentCount(details.minArguments, details.maxArguments);
+ for (int i=0; i<argument.length; i++) {
+ checkArgument(i, visitor);
+ }
+ }
+
+ /**
+ * Perform static type checking on an argument to a function call, and add
+ * type conversion logic where necessary.
+ * @param arg argument number, zero-based
+ * @param visitor an expression visitor
+ * @throws XPathException if the argument is statically invalid
+ */
+
+ private void checkArgument(int arg, /*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ RoleLocator role = new RoleLocator(RoleLocator.FUNCTION,
+ getFunctionName(), arg);
+ //role.setSourceLocator(this);
+ role.setErrorCode(getErrorCodeForTypeErrors());
+ argument[arg] = TypeChecker.staticTypeCheck(
+ argument[arg],
+ getRequiredType(arg),
+ visitor.getStaticContext().isInBackwardsCompatibleMode(),
+ role, visitor);
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression sf = super.optimize(visitor, contextItemType);
+ if (sf == this && argument.length <= details.resultIfEmpty.length) {
+ // the condition eliminates concat, which is a special case.
+ for (int i=0; i<argument.length; i++) {
+ if (Literal.isEmptySequence(argument[i]) && details.resultIfEmpty[i] != null) {
+ return Literal.makeLiteral(SequenceTool.toGroundedValue(details.resultIfEmpty[i]));
+ }
+ }
+ }
+ return sf;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ Expression[] a2 = new Expression[argument.length];
+ for (int i=0; i<argument.length; i++) {
+ a2[i] = argument[i].copy();
+ }
+ Expression e2 = SystemFunctionCall.makeSystemFunction(details.name, a2);
+ if (e2 == null) {
+ throw new UnsupportedOperationException("SystemFunction.copy()");
+ }
+ ExpressionTool.copyLocationInfo(this, e2);
+ return e2;
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+ @Override
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ List<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>(argument.length);
+ for (int i=0; i<argument.length; i++) {
+ list.add(new SubExpressionInfo(argument[i], true, false, details.syntacticContext[i]));
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Determine whether two expressions are equivalent
+ */
+ @Override
+ public boolean equals(Object o) {
+ return (o != null) && (o instanceof SystemFunctionCall) && super.equals(o) && operation == ((SystemFunctionCall)o).operation;
+ }
+
+ /**
+ * Return true if two objects are equal or if both are null
+ * @param x the first object
+ * @param y the second object
+ * @return true if both x and y are null or if x.equals(y)
+ */
+
+ public static boolean equalOrNull(Object x, Object y) {
+ if (x == null) {
+ return (y == null);
+ } else {
+ return (y != null) && x.equals(y);
+ }
+ }
+
+
+ /**
+ * Return the error code to be used for type errors. This is overridden for functions
+ * such as exactly-one(), one-or-more(), ...
+ * @return the error code to be used for type errors in the function call. Normally XPTY0004,
+ * but different codes are used for functions such as exactly-one()
+ */
+
+ public String getErrorCodeForTypeErrors() {
+ return "XPTY0004";
+ }
+
+ /**
+ * Get the required type of the nth argument
+ * @param arg the number of the argument whose type is requested, zero-based
+ * @return the required type of the argument as defined in the function signature
+ */
+
+ protected SequenceType getRequiredType(int arg) {
+ if (details == null) {
+ return SequenceType.ANY_SEQUENCE;
+ }
+ return details.argumentTypes[arg];
+ // this is overridden for concat()
+ }
+
+ /**
+ * Determine the item type of the value returned by the function
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ if (details == null) {
+ // probably an unresolved function call
+ return AnyItemType.getInstance();
+ }
+ ItemType type = details.itemType;
+ if ((details.properties & StandardFunction.AS_ARG0) != 0) {
+ if (argument.length > 0) {
+ return argument[0].getItemType(th);
+ } else {
+ return AnyItemType.getInstance();
+ // if there is no first argument, an error will be reported
+ }
+ } else if ((details.properties & StandardFunction.AS_PRIM_ARG0) != 0) {
+ if (argument.length > 0) {
+ return argument[0].getItemType(th).getPrimitiveItemType();
+ } else {
+ return AnyItemType.getInstance();
+ // if there is no first argument, an error will be reported
+ }
+ } else {
+ return type;
+ }
+ }
+
+ /**
+ * Determine the cardinality of the function.
+ */
+
+ public int computeCardinality() {
+ if (details==null) {
+ //System.err.println("**** No details for " + getClass() + " at " + this);
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+ return details.cardinality;
+ }
+
+ /**
+ * Determine the special properties of this expression. The general rule
+ * is that a system function call is non-creative if its return type is
+ * atomic, or if all its arguments are non-creative. This is overridden
+ * for the generate-id() function, which is considered creative if
+ * its operand is creative (because the result depends on the
+ * identity of the operand)
+ */
+
+ public int computeSpecialProperties() {
+ int p = super.computeSpecialProperties();
+ if (details == null) {
+ return p;
+ }
+ if (details.itemType.isPlainType() ||
+ (details.properties & StandardFunction.AS_ARG0) != 0 || (details.properties & StandardFunction.AS_PRIM_ARG0) != 0) {
+ return p | StaticProperty.NON_CREATIVE;
+ }
+ for (Expression anArgument : argument) {
+ if ((anArgument.getSpecialProperties() & StaticProperty.NON_CREATIVE) == 0) {
+ // the argument is creative
+ return p;
+ }
+ }
+ return p | StaticProperty.NON_CREATIVE;
+ }
+
+ /**
+ * Set "." as the default value for the first and only argument. Called from subclasses.
+ * @param visitor the expression visitor
+ */
+
+ protected final void useContextItemAsDefault(/*@NotNull*/ ExpressionVisitor visitor) {
+ if (argument.length==0) {
+ argument = new Expression[1];
+ argument[0] = new ContextItemExpression();
+ details = StandardFunction.getFunction(getFunctionName().getLocalPart(), 1);
+ ExpressionTool.copyLocationInfo(this, argument[0]);
+ visitor.resetStaticProperties();
+ }
+ // Note that the extra argument is added before type-checking takes place. The
+ // type-checking will add any necessary checks to ensure that the context item
+ // is a node, in cases where this is required.
+ }
+
+ /**
+ * Add an implicit argument referring to the context document. Called by functions such as
+ * id() and key() that take the context document as an implicit argument
+ * @param pos the position of the argument whose default value is ".", zero-based
+ * @param augmentedName the name to be used for the function call with its extra argument.
+ * There are some cases where user function calls cannot supply the argument directly (notably
+ * unparsed-entity-uri() and unparsed-entity-public-id()) and in these cases a synthesized
+ * function name is used for the new function call.
+ * @throws net.sf.saxon.trans.XPathException if a static error is found
+ */
+
+ protected final void addContextDocumentArgument(int pos, String augmentedName)
+ throws XPathException {
+ if (argument.length > pos) {
+ return;
+ // this can happen during optimization, if the extra argument is already present
+ }
+ if (argument.length != pos) {
+ throw new XPathException("Too few arguments in call to " + augmentedName + "() function");
+ }
+ Expression[] newArgs = new Expression[pos+1];
+ System.arraycopy(argument, 0, newArgs, 0, argument.length);
+ RootExpression rootExpression = new RootExpression();
+ ExpressionTool.copyLocationInfo(this, rootExpression);
+ newArgs[pos] = rootExpression;
+ argument = newArgs;
+ setDetails(StandardFunction.getFunction(augmentedName, newArgs.length));
+ }
+
+ /**
+ * Add a representation of a doc() call or similar function to a PathMap.
+ * This is a convenience method called by the addToPathMap() methods for doc(), document(), collection()
+ * and similar functions. These all create a new root expression in the path map.
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodes the node in the PathMap representing the focus at the point where this expression
+ * is called. Set to null if this expression appears at the top level.
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression
+ */
+
+ /*@Nullable*/ public PathMap.PathMapNodeSet addDocToPathMap(/*@NotNull*/ PathMap pathMap, PathMap.PathMapNodeSet pathMapNodes) {
+ argument[0].addToPathMap(pathMap, pathMapNodes);
+ return new PathMap.PathMapNodeSet(pathMap.makeNewRoot(this));
+ }
+
+ /**
+ * Helper method for subclasses: get the context item if it is a node, throwing appropriate errors
+ * if not
+ * @param context the XPath dynamic context
+ * @return the context item if it exists and is a node
+ * @throws XPathException if there is no context item or if the context item is not a node
+ */
+
+ protected NodeInfo getContextNode(XPathContext context) throws XPathException {
+ Item item = context.getContextItem();
+ if (item == null) {
+ XPathException err = new XPathException("Context item for " + getFunctionName() + "() is absent", "XPDY0002");
+ err.maybeSetContext(context);
+ err.setLocator(this);
+ throw err;
+ } else if (!(item instanceof NodeInfo)) {
+ XPathException err = new XPathException("Context item for " + getFunctionName() +"() is not a node", "XPTY0004");
+ err.maybeSetContext(context);
+ err.setLocator(this);
+ throw err;
+ } else {
+ return (NodeInfo)item;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/functions/SystemFunctionLibrary.java b/sf/saxon/functions/SystemFunctionLibrary.java
new file mode 100644
index 0000000..e7e32cf
--- /dev/null
+++ b/sf/saxon/functions/SystemFunctionLibrary.java
@@ -0,0 +1,239 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.functions.hof.CallableFunctionItem;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.SuppliedParameterReference;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.FunctionItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.z.IntHashMap;
+
+/**
+ * The SystemFunctionLibrary represents the collection of functions in the fn: namespace. That is, the
+ * functions defined in the "Functions and Operators" specification, optionally augmented by the additional
+ * functions defined in XSLT.
+ */
+
+public class SystemFunctionLibrary implements FunctionLibrary {
+
+ private int functionSet;
+
+ private static IntHashMap<SystemFunctionLibrary> THE_INSTANCES =
+ new IntHashMap<SystemFunctionLibrary>(3);
+
+ /**
+ * Factory method to create or get a SystemFunctionLibrary
+ * @param functionSet determines the set of functions allowed. One or more of the bit settings
+ * {@link StandardFunction#CORE}, {@link StandardFunction#XSLT}, {@link StandardFunction#XQUPDATE}, etc
+ * @return the appropriate SystemFunctionLibrary
+ */
+
+ public static synchronized SystemFunctionLibrary getSystemFunctionLibrary(int functionSet) {
+ if (THE_INSTANCES.get(functionSet) == null) {
+ THE_INSTANCES.put(functionSet, new SystemFunctionLibrary(functionSet));
+ }
+ return THE_INSTANCES.get(functionSet);
+ }
+
+ /**
+ * Create a SystemFunctionLibrary
+ * @param functionSet determines the set of functions allowed. One or more of the bit settings
+ * {@link StandardFunction#CORE}, {@link StandardFunction#XSLT}, {@link StandardFunction#XQUPDATE}, etc
+ */
+
+ private SystemFunctionLibrary(int functionSet) {
+ this.functionSet = functionSet;
+ }
+
+ /**
+ * Bind an extension function, given the URI and local parts of the function name,
+ * and the list of expressions supplied as arguments. This method is called at compile
+ * time.
+ *
+ *
+ * @param functionName the name of the function to be bound
+ * @param arity
+ * @param staticArgs The expressions supplied statically in the function call. The intention is
+ * that the static type of the arguments (obtainable via getItemType() and getCardinality() may
+ * be used as part of the binding algorithm.
+ * @param env
+ * @param container
+ * @return An object representing the extension function to be called, if one is found;
+ * null if no extension function was found matching the required name and arity.
+ * @throws net.sf.saxon.trans.XPathException if a function is found with the required name and arity, but
+ * the implementation of the function cannot be loaded or used; or if an error occurs
+ * while searching for the function; or if this function library "owns" the namespace containing
+ * the function call, but no function was found.
+ */
+
+ public Expression bind(StructuredQName functionName, int arity, Expression[] staticArgs, StaticContext env, Container container)
+ throws XPathException {
+ String uri = functionName.getURI();
+ if (uri.equals(NamespaceConstant.FN)) {
+ String local = functionName.getLocalPart();
+ StandardFunction.Entry entry = StandardFunction.getFunction(local, arity);
+ if (entry == null) {
+ if (StandardFunction.getFunction(local, -1) == null) {
+ XPathException err = new XPathException("Unknown system function " + local + "()");
+ err.setErrorCode("XPST0017");
+ err.setIsStaticError(true);
+ throw err;
+ } else {
+ XPathException err = new XPathException("System function " + local + "() cannot be called with "
+ + pluralArguments(arity));
+ err.setErrorCode("XPST0017");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ }
+ if ((functionSet & entry.applicability) == 0) {
+ XPathException err = new XPathException(
+ "System function " + local + "#"+staticArgs.length +" is not available with this host language/version");
+ err.setErrorCode("XPST0017");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ Class functionClass = entry.implementationClass;
+ SystemFunctionCall f;
+ try {
+ f = (SystemFunctionCall)functionClass.newInstance();
+ } catch (Exception err) {
+ throw new AssertionError("Failed to load system function: " + err.getMessage());
+ }
+ f.setDetails(entry);
+ f.setFunctionName(functionName);
+ checkArgumentCount(arity, entry.minArguments, entry.maxArguments, local);
+ if (staticArgs != null) {
+ f.setArguments(staticArgs);
+ }
+ f.setContainer(container);
+ f.bindStaticContext(env);
+ return f;
+ } else {
+ return null;
+ }
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ * <p/>
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ * @throws net.sf.saxon.trans.XPathException
+ * in the event of certain errors, for example attempting to get a function
+ * that is private
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext) throws XPathException {
+ SuppliedParameterReference[] args = new SuppliedParameterReference[arity];
+ Expression call = bind(functionName, arity, args, staticContext, null);
+ if (call == null) {
+ return null;
+ } else if (call instanceof SystemFunctionCall) {
+ TypeHierarchy th = staticContext.getConfiguration().getTypeHierarchy();
+ FunctionItemType type = ((SystemFunctionCall)call).getFunctionItemType(th);
+ return new CallableFunctionItem(functionName, arity, ((SystemFunctionCall) call).getConvertingCallable(), type);
+ } else {
+ throw new UnsupportedOperationException("Cannot create function item for function " + functionName);
+ }
+ }
+//#endif
+
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ String uri = functionName.getURI();
+ if (uri.equals(NamespaceConstant.FN)) {
+ String local = functionName.getLocalPart();
+ StandardFunction.Entry entry = StandardFunction.getFunction(local, arity);
+ return entry != null && (functionSet & entry.applicability) != 0 &&
+ entry.minArguments <= arity && entry.maxArguments >= arity;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Check number of arguments. <BR>
+ * A convenience routine for use in subclasses.
+ * @param numArgs the actual number of arguments (arity)
+ * @param min the minimum number of arguments allowed
+ * @param max the maximum number of arguments allowed
+ * @param local the local name of the function (for diagnostics)
+ * @return the actual number of arguments
+ * @throws net.sf.saxon.trans.XPathException if the number of arguments is out of range
+ */
+
+ private int checkArgumentCount(int numArgs, int min, int max, String local) throws XPathException {
+ if (min==max && numArgs != min) {
+ throw new XPathException("Function " + Err.wrap(local, Err.FUNCTION) + " must have "
+ + pluralArguments(min), "XPST0017");
+ }
+ if (numArgs < min) {
+ throw new XPathException("Function " + Err.wrap(local, Err.FUNCTION) + " must have at least "
+ + pluralArguments(min), "XPST0017");
+ }
+ if (numArgs > max) {
+ throw new XPathException("Function " + Err.wrap(local, Err.FUNCTION) + " must have no more than "
+ + pluralArguments(max), "XPST0017");
+ }
+ return numArgs;
+ }
+
+ /**
+ * Utility routine used in constructing error messages
+ * @param num the number of arguments
+ * @return the string " argument" or "arguments" depending whether num is plural
+ */
+
+ private static String pluralArguments(int num) {
+ if (num == 0) {
+ return "zero arguments";
+ }
+ if (num == 1) {
+ return "one argument";
+ }
+ return num + " arguments";
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ *
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ public FunctionLibrary copy() {
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/functions/SystemProperty.java b/sf/saxon/functions/SystemProperty.java
new file mode 100644
index 0000000..05398b0
--- /dev/null
+++ b/sf/saxon/functions/SystemProperty.java
@@ -0,0 +1,189 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Version;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.style.UseWhenStaticContext;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * Implementation of the XSLT system-property() function
+ */
+
+public class SystemProperty extends SystemFunctionCall implements Callable {
+
+ private NamespaceResolver nsContext;
+ private StructuredQName propertyName;
+ private transient boolean checked = false;
+ private boolean isSchemaAware = false;
+ // the second time checkArguments is called, it's a global check so the static context is inaccurate
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ if (checked) return;
+ checked = true;
+ super.checkArguments(visitor);
+ if (argument[0] instanceof StringLiteral) {
+ try {
+ boolean is30 = visitor.getExecutable().isAllowXPath30();
+ propertyName = StructuredQName.fromLexicalQName(
+ ((StringLiteral)argument[0]).getStringValue(),
+ false, is30, visitor.getConfiguration().getNameChecker(),
+ visitor.getStaticContext().getNamespaceResolver());
+ } catch (XPathException e) {
+ String code = e.getErrorCodeLocalPart();
+ if (code==null || code.equals("FOCA0002") || code.equals("FONS0004")) {
+ e.setErrorCode("XTDE1390");
+ throw e;
+ }
+ }
+ // Don't actually read the system property yet, it might be different at run-time
+ } else {
+ // we need to save the namespace context
+ nsContext = visitor.getStaticContext().getNamespaceResolver();
+ isSchemaAware = visitor.getStaticContext().isSchemaAware();
+ }
+ }
+
+ /**
+ * preEvaluate: this method performs compile-time evaluation for properties in the XSLT namespace only
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ if (propertyName != null && NamespaceConstant.XSLT.equals(propertyName.getURI())) {
+ if (propertyName.getLocalPart().equals("is-schema-aware")) {
+ StaticContext env = visitor.getStaticContext();
+ boolean aware;
+ if (env instanceof UseWhenStaticContext) {
+ Configuration config = env.getConfiguration();
+ aware = "EE".equals(config.getEditionCode()) &&
+ config.isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XSLT);
+ } else {
+ aware = env.isSchemaAware();
+ }
+ return new StringLiteral(aware ? "yes" : "no");
+ }
+ return new StringLiteral(
+ getProperty(NamespaceConstant.XSLT, propertyName.getLocalPart(), visitor.getExecutable()));
+ } else {
+ return this;
+ }
+ }
+
+ /**
+ * Evaluate the function at run-time
+ */
+
+ /*@Nullable*/ public StringValue evaluateItem(XPathContext context) throws XPathException {
+
+ StructuredQName qName = propertyName;
+ if (qName == null) {
+ CharSequence name = argument[0].evaluateItem(context).getStringValueCS();
+ try {
+ boolean is30 = context.getController().getExecutable().isAllowXPath30();
+ qName = StructuredQName.fromLexicalQName(name,
+ false, is30, context.getConfiguration().getNameChecker(),
+ nsContext);
+ if (NamespaceConstant.XSLT.equals(qName.getURI()) &&
+ "is-schema-aware".equals(qName.getLocalPart())) {
+ return new StringValue(isSchemaAware ? "yes" : "no");
+ }
+ } catch (XPathException err) {
+ dynamicError("Invalid system property name. " + err.getMessage(), "XTDE1390", context);
+ return null;
+ }
+ }
+ return new StringValue(getProperty(
+ qName.getURI(), qName.getLocalPart(), context.getController().getExecutable()));
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ if (propertyName != null) {
+ return evaluateItem(context);
+ } else {
+ StringValue name = (StringValue)arguments[0].head();
+ try {
+ boolean is30 = context.getController().getExecutable().isAllowXPath30();
+ StructuredQName qName = StructuredQName.fromLexicalQName(name.getStringValue(),
+ false, is30, context.getConfiguration().getNameChecker(),
+ nsContext);
+ if (NamespaceConstant.XSLT.equals(qName.getURI()) &&
+ "is-schema-aware".equals(qName.getLocalPart())) {
+ return new StringValue(isSchemaAware ? "yes" : "no");
+ } else {
+ return new StringValue(getProperty(
+ qName.getURI(), qName.getLocalPart(), context.getController().getExecutable()));
+ }
+ } catch (XPathException err) {
+ dynamicError("Invalid system property name. " + err.getMessage(), "XTDE1390", context);
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Here's the real code:
+ * @param uri the namespace URI of the system property name
+ * @param local the local part of the system property name
+ * @param exec the Saxon executable
+ * @return the value of the corresponding system property
+ */
+
+ public static String getProperty(String uri, String local, Executable exec) {
+ if (uri.equals(NamespaceConstant.XSLT)) {
+ if (local.equals("version")) {
+ return exec.isAllowXPath30() ? "3.0" : "2.0";
+ //return Version.getXSLVersionString();
+ } else if (local.equals("vendor")) {
+ return Version.getProductVendor();
+ } else if (local.equals("vendor-url")) {
+ return Version.getWebSiteAddress();
+ } else if (local.equals("product-name")) {
+ return Version.getProductName();
+ } else if (local.equals("product-version")) {
+ return Version.getProductVariantAndVersion(exec.getConfiguration());
+ } else if (local.equals("supports-serialization")) {
+ return "yes";
+ } else if (local.equals("supports-backwards-compatibility")) {
+ return "yes";
+ } else if (local.equals("supports-namespace-axis")) { // Erratum E14
+ return "yes";
+ }
+ return "";
+
+ } else if (uri.length() == 0 && exec.getConfiguration().getBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
+ String val = System.getProperty(local);
+ return val==null ? "" : val;
+ } else {
+ return "";
+ }
+ }
+
+}
+
diff --git a/sf/saxon/functions/Tokenize.java b/sf/saxon/functions/Tokenize.java
new file mode 100644
index 0000000..1916b2b
--- /dev/null
+++ b/sf/saxon/functions/Tokenize.java
@@ -0,0 +1,208 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.regex.RegularExpression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.EmptySequence;
+
+
+/**
+* This class implements the tokenize() function for regular expression matching. This returns a
+* sequence of strings representing the unmatched substrings: the separators which match the
+* regular expression are not returned.
+*/
+
+public class Tokenize extends SystemFunctionCall implements Callable {
+
+ /*@Nullable*/ private RegularExpression regexp;
+ private boolean allow30features = false;
+
+ /**
+ * Simplify and validate.
+ * This is a pure function so it can be simplified in advance if the arguments are known
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ allow30features = DecimalValue.THREE.equals(visitor.getStaticContext().getXPathLanguageLevel());
+ Expression e = simplifyArguments(visitor);
+ if (e == this) {
+ maybePrecompile(visitor);
+ }
+ return e;
+ }
+
+ private void maybePrecompile(ExpressionVisitor visitor) throws XPathException {
+ // compile the regular expression once if possible
+ if (regexp == null) {
+ try {
+ regexp = Matches.tryToCompile(argument, 1, 2, visitor.getStaticContext());
+ } catch (XPathException err) {
+ err.setLocator(this);
+ throw err;
+ }
+ // check that it's not a pattern that matches ""
+ if (regexp != null && regexp.matches("")) {
+ XPathException err = new XPathException("The regular expression in tokenize() must not be one that matches a zero-length string");
+ err.setErrorCode("FORX0003");
+ err.setLocator(this);
+ throw err;
+ }
+ }
+ }
+
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.optimize(visitor, contextItemType);
+ // try once again to compile the regular expression once if possible
+ // (used when the regex has been identified as a constant as a result of earlier rewrites)
+ if (e == this) {
+ maybePrecompile(visitor);
+ }
+ return e;
+ }
+
+ /**
+ * Get the compiled regular expression if available, otherwise return null
+ * @return the compiled regular expression, or null
+ */
+
+ public RegularExpression getCompiledRegularExpression() {
+ return regexp;
+ }
+
+ /**
+ * Iterate over the results of the function
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext c) throws XPathException {
+ AtomicValue sv = (AtomicValue)argument[0].evaluateItem(c);
+ if (sv==null) {
+ return EmptyIterator.getInstance();
+ }
+ CharSequence input = sv.getStringValueCS();
+ if (input.length() == 0) {
+ return EmptyIterator.getInstance();
+ }
+
+ RegularExpression re = regexp;
+ if (re == null) {
+
+ sv = (AtomicValue)argument[1].evaluateItem(c);
+ CharSequence pattern = sv.getStringValueCS();
+
+ CharSequence flags;
+ if (argument.length==2) {
+ flags = "";
+ } else {
+ sv = (AtomicValue)argument[2].evaluateItem(c);
+ flags = sv.getStringValueCS();
+ }
+
+ re = buildRegex(c, pattern, flags);
+
+ }
+ return re.tokenize(input);
+ }
+
+ private RegularExpression buildRegex(XPathContext c, CharSequence pattern, CharSequence flags) throws XPathException {
+ RegularExpression re;
+ try {
+ re = Configuration.getPlatform().compileRegularExpression(
+ pattern, flags.toString(), (allow30features ? "XP30" : "XP20"), null);
+
+ } catch (XPathException err) {
+ XPathException de = new XPathException(err);
+ de.setErrorCode("FORX0002");
+ de.setXPathContext(c);
+ de.setLocator(this);
+ throw de;
+ }
+ // check that it's not a pattern that matches ""
+ if (re.matches("")) {
+ XPathException err = new XPathException("The regular expression in tokenize() must not be one that matches a zero-length string");
+ err.setErrorCode("FORX0003");
+ err.setLocator(this);
+ throw err;
+ }
+ return re;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ AtomicValue sv = (AtomicValue)arguments[0].head();
+ if (sv==null) {
+ return EmptySequence.getInstance();
+ }
+ CharSequence input = sv.getStringValueCS();
+ if (input.length() == 0) {
+ return EmptySequence.getInstance();
+ }
+
+ RegularExpression re = regexp;
+ if (re == null) {
+ sv = (AtomicValue)arguments[1].head();
+ CharSequence pattern = sv.getStringValueCS();
+
+ CharSequence flags;
+ if (argument.length==2) {
+ flags = "";
+ } else {
+ sv = (AtomicValue)arguments[2].head();
+ flags = sv.getStringValueCS();
+ }
+
+ re = buildRegex(context, pattern, flags);
+
+ }
+ return SequenceTool.toLazySequence(re.tokenize(input));
+ }
+}
+
diff --git a/sf/saxon/functions/Trace.java b/sf/saxon/functions/Trace.java
new file mode 100644
index 0000000..42f11e9
--- /dev/null
+++ b/sf/saxon/functions/Trace.java
@@ -0,0 +1,223 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.InstructionDetails;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.io.PrintStream;
+
+/**
+* This class supports the XPath 2.0 function trace().
+ * The value is traced to the registered output stream (defaulting to System.err),
+ * unless a TraceListener is in use, in which case the information is sent to the TraceListener
+*/
+
+
+public class Trace extends SystemFunctionCall implements Callable {
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Get the static properties of this expression (other than its type). The result is
+ * bit-significant. These properties are used for optimizations. In general, if
+ * property bit is set, it is true, but if it is unset, the value is unknown.
+ */
+
+ public int computeSpecialProperties() {
+ return argument[0].getSpecialProperties();
+ }
+
+ /**
+ * Get the static cardinality
+ */
+
+ public int computeCardinality() {
+ return argument[0].getCardinality();
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ Item val = argument[0].evaluateItem(context);
+ String label = argument[1].evaluateAsString(context).toString();
+ Controller controller = context.getController();
+ if (controller.isTracing()) {
+ notifyListener(label, val, context);
+ } else {
+ PrintStream out = controller.getTraceFunctionDestination();
+ if (out != null) {
+ traceItem(val, label, out);
+ }
+ }
+ return val;
+ }
+
+ private void notifyListener(String label, Sequence val, XPathContext context) {
+ InstructionDetails info = new InstructionDetails();
+ info.setConstructType(Location.TRACE_CALL);
+ info.setLineNumber(getLineNumber());
+ info.setSystemId(getSystemId());
+ info.setProperty("label", label);
+ info.setProperty("value", val);
+ TraceListener listener = context.getController().getTraceListener();
+ listener.enter(info, context);
+ listener.leave(info);
+ }
+
+ public static void traceItem(/*@Nullable*/ Item val, String label, PrintStream out) {
+ if (val==null) {
+ out.println(label + ": empty sequence");
+ } else {
+ if (val instanceof NodeInfo) {
+ out.println(label + ": " + Type.displayTypeName(val) + ": "
+ + Navigator.getPath((NodeInfo)val));
+ } else {
+ out.println(label + ": " + Type.displayTypeName(val) + ": "
+ + val.getStringValue());
+ }
+ }
+ }
+
+ /**
+ * Iterate over the results of the function
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ Controller controller = context.getController();
+ if (controller.isTracing()) {
+ String label = argument[1].evaluateAsString(context).toString();
+ Sequence value = ExpressionTool.eagerEvaluate(argument[0], context);
+ notifyListener(label, value, context);
+ return value.iterate();
+ } else {
+ PrintStream out = controller.getTraceFunctionDestination();
+ if (out == null) {
+ return argument[0].iterate(context);
+ } else {
+ return new TracingIterator(argument[0].iterate(context),
+ argument[1].evaluateAsString(context).toString(),
+ out);
+ }
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ Controller controller = context.getController();
+ if (controller.isTracing()) {
+ String label = arguments[1].head().getStringValue();
+ Sequence value = SequenceExtent.makeSequenceExtent(arguments[0].iterate());
+ notifyListener(label, value, context);
+ return value;
+ } else {
+ PrintStream out = controller.getTraceFunctionDestination();
+ if (out == null) {
+ return arguments[0];
+ } else {
+ return SequenceTool.toLazySequence(new TracingIterator(arguments[0].iterate(),
+ arguments[1].head().toString(),
+ out));
+ }
+ }
+ }
+
+ /**
+ * Tracing Iterator class
+ */
+
+ private class TracingIterator implements SequenceIterator {
+
+ SequenceIterator base;
+ String label;
+ PrintStream out;
+ boolean empty = true;
+
+
+ public TracingIterator(SequenceIterator base, String label, PrintStream out) {
+ this.base = base;
+ this.label = label;
+ this.out = out;
+ }
+
+ public Item next() throws XPathException {
+ Item n = base.next();
+ if (n==null) {
+ if (empty) {
+ traceItem(null, label, out);
+ }
+ } else {
+ traceItem(n, label + " [" + position() + ']', out);
+ empty = false;
+ }
+ return n;
+ }
+
+ public Item current() {
+ return base.current();
+ }
+
+ public int position() {
+ return base.position();
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new TracingIterator(base.getAnother(), label, out);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/functions/Translate.java b/sf/saxon/functions/Translate.java
new file mode 100644
index 0000000..578858a
--- /dev/null
+++ b/sf/saxon/functions/Translate.java
@@ -0,0 +1,192 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.TranslateCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.z.IntToIntHashMap;
+import net.sf.saxon.z.IntToIntMap;
+
+/**
+ * Implement the XPath translate() function
+ */
+
+public class Translate extends SystemFunctionCall implements Callable {
+
+ /*@Nullable*/ private IntToIntMap staticMap = null;
+ // if the second and third arguments are known statically, we build a hash table for fast
+ // lookup at run-time.
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression e = super.typeCheck(visitor, contextItemType);
+ if (e == this && argument[1] instanceof StringLiteral && argument[2] instanceof StringLiteral) {
+ // second and third arguments known statically: build an index
+ staticMap = buildMap((StringValue)((StringLiteral)argument[1]).getValue(),
+ (StringValue)((StringLiteral)argument[2]).getValue());
+ }
+ return e;
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ public StringValue evaluateItem(XPathContext context) throws XPathException {
+
+ StringValue sv1 = (StringValue)argument[0].evaluateItem(context);
+ if (sv1==null) {
+ return StringValue.EMPTY_STRING;
+ }
+
+ if (staticMap != null) {
+ CharSequence in = sv1.getStringValueCS();
+ CharSequence sb = translateUsingMap(in, staticMap);
+ return new StringValue(sb);
+ }
+
+ StringValue sv2 = (StringValue)argument[1].evaluateItem(context);
+
+ StringValue sv3 = (StringValue)argument[2].evaluateItem(context);
+
+ return StringValue.makeStringValue(translate(sv1, sv2, sv3));
+ }
+
+ /**
+ * Get the translation map built at compile time if there is one
+ */
+
+ public IntToIntMap getStaticMap() {
+ return staticMap;
+ }
+
+ /**
+ * Perform the translate function
+ */
+
+ public static CharSequence translate(StringValue sv0, StringValue sv1, StringValue sv2) {
+
+ // if any string contains surrogate pairs, expand everything to 32-bit characters
+ if (sv0.containsSurrogatePairs() || sv1.containsSurrogatePairs() || sv2.containsSurrogatePairs()) {
+ return translateUsingMap(sv0.getStringValueCS(), buildMap(sv1, sv2));
+ }
+
+ // if the size of the strings is above some threshold, use a hash map to avoid O(n*m) performance
+ if (sv0.getStringLength() * sv1.getStringLength() > 1000) {
+ // Cut-off point for building the map based on some simple measurements
+ return translateUsingMap(sv0.getStringValueCS(), buildMap(sv1, sv2));
+ }
+
+ CharSequence cs0 = sv0.getStringValueCS();
+ CharSequence cs1 = sv1.getStringValueCS();
+ CharSequence cs2 = sv2.getStringValueCS();
+
+ String st1 = cs1.toString();
+ FastStringBuffer sb = new FastStringBuffer(cs0.length());
+ int s2len = cs2.length();
+ int s0len = cs0.length();
+ for (int i=0; i<s0len; i++) {
+ char c = cs0.charAt(i);
+ int j = st1.indexOf(c);
+ if (j<s2len) {
+ sb.append(( j<0 ? c : cs2.charAt(j) ));
+ }
+ }
+ return sb;
+ }
+
+ /**
+ * Build an index
+ * @param arg1 a string containing characters to be replaced
+ * @param arg2 a string containing the corresponding replacement characters
+ * @return a map that maps characters to their replacements (or to -1 if the character is to be removed)
+ */
+
+ private static IntToIntMap buildMap(StringValue arg1, StringValue arg2) {
+ UnicodeString a1 = UnicodeString.makeUnicodeString(arg1.getStringValueCS());
+ UnicodeString a2 = UnicodeString.makeUnicodeString(arg2.getStringValueCS());
+ IntToIntMap map = new IntToIntHashMap(a1.length(), 0.5);
+ // allow plenty of free space, it's better for lookups (though worse for iteration)
+ for (int i=0; i<a1.length(); i++) {
+ if (map.find(a1.charAt(i))) {
+ // no action: duplicate
+ } else {
+ map.put(a1.charAt(i), (i>a2.length()-1 ? -1 : a2.charAt(i)));
+ }
+ }
+ return map;
+ }
+
+ /**
+ * Implement the translate() function using an index built at compile time
+ * @param in the string to be translated
+ * @param map index built at compile time, mapping input characters to output characters. The map returns
+ * -1 for a character that is to be deleted from the input string, Integer.MAX_VALUE for a character that is
+ * to remain intact
+ * @return the translated character string
+ */
+
+ public static CharSequence translateUsingMap(CharSequence in, IntToIntMap map) {
+ UnicodeString us = UnicodeString.makeUnicodeString(in);
+ int len = us.length();
+ FastStringBuffer sb = new FastStringBuffer(len);
+ for (int i=0; i<len; i++) {
+ int c = us.charAt(i);
+ int newchar = map.get(c);
+ if (newchar == Integer.MAX_VALUE) {
+ // character not in map, so is not to be translated
+ newchar = c;
+ }
+ if (newchar == -1) {
+ // no action, delete the character
+ } else {
+ sb.appendWideChar(newchar);
+ }
+ }
+ return sb;
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue sv0 = (StringValue)arguments[0].head();
+ StringValue sv1 = (StringValue)arguments[1].head();
+ StringValue sv2 = (StringValue)arguments[2].head();
+ return new StringValue(translate(sv0, sv1, sv2));
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the Translate expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new TranslateCompiler();
+ }
+//#endif
+}
+
diff --git a/sf/saxon/functions/TreatFn.java b/sf/saxon/functions/TreatFn.java
new file mode 100644
index 0000000..a9aa94f
--- /dev/null
+++ b/sf/saxon/functions/TreatFn.java
@@ -0,0 +1,65 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* This class supports the XPath 2.0 functions exactly-one(), one-or-more(), zero-or-one().
+* Because Saxon doesn't do strict static type checking, these are essentially identity
+* functions; the run-time type checking is done as part of the function call mechanism
+*/
+
+public class TreatFn extends SystemFunctionCall implements Callable {
+
+ /**
+ * Return the error code to be used for type errors
+ */
+
+ public String getErrorCodeForTypeErrors() {
+ switch (operation) {
+ case StaticProperty.ALLOWS_ZERO_OR_ONE:
+ return "FORG0003";
+ case StaticProperty.ALLOWS_ONE_OR_MORE:
+ return "FORG0004";
+ case StaticProperty.EXACTLY_ONE:
+ return "FORG0005";
+ default:
+ return "XPTY0004";
+ }
+ }
+
+ /**
+ * Evaluate the function
+ */
+
+ /*@Nullable*/ public Item evaluateItem(XPathContext context) throws XPathException {
+ return argument[0].evaluateItem(context);
+ }
+
+ /**
+ * Iterate over the results of the function
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ return argument[0].iterate(context);
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return arguments[0];
+ }
+
+}
+
diff --git a/sf/saxon/functions/True.java b/sf/saxon/functions/True.java
new file mode 100644
index 0000000..8dfb70a
--- /dev/null
+++ b/sf/saxon/functions/True.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+
+/**
+ * Implement the XPath function fn:true()
+ */
+
+public class True extends SystemFunctionCall implements Callable {
+
+ @Override
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ return Literal.makeLiteral(BooleanValue.TRUE);
+ }
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.TRUE;
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return BooleanValue.TRUE;
+ }
+}
+
diff --git a/sf/saxon/functions/TypeAvailable.java b/sf/saxon/functions/TypeAvailable.java
new file mode 100644
index 0000000..653a306
--- /dev/null
+++ b/sf/saxon/functions/TypeAvailable.java
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.BuiltInListType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.StringValue;
+
+import java.util.Set;
+
+/**
+* This class supports the XSLT element-available and function-available functions.
+*/
+
+public class TypeAvailable extends Available implements Callable {
+
+ private Set<String> importedSchemaNamespaces;
+ private boolean isXSLT20basic;
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ StaticContext env = visitor.getStaticContext();
+ isXSLT20basic = !env.isSchemaAware() && !env.getXPathLanguageLevel().equals(DecimalValue.THREE);
+ super.checkArguments(visitor);
+ if (!(argument[0] instanceof Literal &&
+ (argument.length==1 || argument[1] instanceof Literal))) {
+ importedSchemaNamespaces = visitor.getStaticContext().getImportedSchemaNamespaces();
+ }
+ }
+
+ /**
+ * preEvaluate: this method uses the static context to do early evaluation of the function
+ * if the argument is known (which is the normal case)
+ * @param visitor the expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ nsContext = visitor.getStaticContext().getNamespaceResolver();
+ String lexicalQName = ((Literal)argument[0]).getValue().getStringValue();
+ Configuration config = visitor.getConfiguration();
+ boolean b = typeAvailable(lexicalQName, config, visitor.getStaticContext().getImportedSchemaNamespaces());
+ return Literal.makeLiteral(BooleanValue.get(b));
+ }
+
+ /**
+ * Run-time evaluation.
+ */
+
+ public BooleanValue evaluateItem(XPathContext context) throws XPathException {
+ AtomicValue av1 = (AtomicValue)argument[0].evaluateItem(context);
+ StringValue nameValue = (StringValue)av1;
+ boolean b = typeAvailable(nameValue.getStringValue(), context.getConfiguration(), importedSchemaNamespaces);
+ return BooleanValue.get(b);
+ }
+
+ private boolean typeAvailable(String lexicalName, Configuration config, Set<String> importedSchemaNamespaces) throws XPathException {
+ StructuredQName qName;
+ try {
+ // TODO: accept EQName syntax.
+ if (lexicalName.indexOf(':') < 0) {
+ String uri = nsContext.getURIForPrefix("", true);
+ qName = new StructuredQName("", uri, lexicalName);
+ } else {
+ qName = StructuredQName.fromLexicalQName(lexicalName,
+ false, false, config.getNameChecker(),
+ nsContext);
+ }
+ } catch (XPathException e) {
+ e.setErrorCode("XTDE1428");
+ e.setLocator(this);
+ throw e;
+ }
+
+ String uri = qName.getURI();
+ if (uri.equals(NamespaceConstant.JAVA_TYPE)) {
+ try {
+ config.getClass(qName.getLocalPart(), false, null);
+ return(true);
+ } catch (XPathException err) {
+ return(false);
+ }
+ } else if (uri.equals(NamespaceConstant.SCHEMA) || importedSchemaNamespaces.contains(uri)) {
+ final int fp = config.getNamePool().allocate(
+ qName.getPrefix(), uri, qName.getLocalPart()) & 0xfffff;
+ SchemaType type = config.getSchemaType(fp);
+ return type != null &&
+ !(isXSLT20basic && (type instanceof BuiltInListType ||
+ (type instanceof BuiltInAtomicType && !((BuiltInAtomicType) type).isAllowedInBasicXSLT())));
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments /*@NotNull*/) throws XPathException {
+ String lexicalQName = arguments[0].head().getStringValue();
+ return BooleanValue.get(typeAvailable(lexicalQName, context.getConfiguration(), importedSchemaNamespaces));
+ }
+}
+
diff --git a/sf/saxon/functions/URIQueryParameters.java b/sf/saxon/functions/URIQueryParameters.java
new file mode 100644
index 0000000..54b3a97
--- /dev/null
+++ b/sf/saxon/functions/URIQueryParameters.java
@@ -0,0 +1,239 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.XMLReader;
+
+import javax.xml.transform.TransformerException;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+/**
+ * A set of query parameters on a URI passed to the collection() or document() function
+ */
+
+public class URIQueryParameters {
+
+ /*@Nullable*/ FilenameFilter filter = null;
+ Boolean recurse = null;
+ Integer validation = null;
+ int strip = Whitespace.UNSPECIFIED;
+ Integer onError = null;
+ XMLReader parser = null;
+ Boolean xinclude = null;
+ boolean unparsed;
+
+ public static final int ON_ERROR_FAIL = 1;
+ public static final int ON_ERROR_WARNING = 2;
+ public static final int ON_ERROR_IGNORE = 3;
+
+ /**
+ * Create an object representing the query part of a URI
+ * @param query the part of the URI after the "?" symbol
+ * @param config the Saxon configuration
+ */
+
+ public URIQueryParameters(String query, Configuration config) {
+ if (query != null) {
+ StringTokenizer t = new StringTokenizer(query, ";&");
+ while (t.hasMoreTokens()) {
+ String tok = t.nextToken();
+ int eq = tok.indexOf('=');
+ if (eq > 0 && eq < (tok.length()-1)) {
+ String keyword = tok.substring(0, eq);
+ String value = tok.substring(eq+1);
+
+ if (keyword.equals("select")) {
+ filter = makeGlobFilter(value);
+ } else if (keyword.equals("recurse")) {
+ recurse = Boolean.valueOf("yes".equals(value));
+ } else if (keyword.equals("validation")) {
+ int v = Validation.getCode(value);
+ if (v != Validation.INVALID) {
+ validation = Integer.valueOf(v);
+ }
+ } else if (keyword.equals("strip-space")) {
+ if (value.equals("yes")) {
+ strip = Whitespace.ALL;
+ } else if (value.equals("ignorable")) {
+ strip = Whitespace.IGNORABLE;
+ } else if (value.equals("no")) {
+ strip = Whitespace.NONE;
+ }
+ } else if (keyword.equals("xinclude")) {
+ if (value.equals("yes")) {
+ xinclude = Boolean.TRUE;
+ } else if (value.equals("no")) {
+ xinclude = Boolean.FALSE;
+ }
+ } else if (keyword.equals("unparsed")) {
+ if (value.equals("yes")) {
+ unparsed = true;
+ } else if (value.equals("no")) {
+ unparsed = false;
+ }
+ } else if (keyword.equals("on-error")) {
+ if (value.equals("warning")) {
+ onError = Integer.valueOf(ON_ERROR_WARNING);
+ } else if (value.equals("ignore")) {
+ onError = Integer.valueOf(ON_ERROR_IGNORE);
+ } else if (value.equals("fail")) {
+ onError = Integer.valueOf(ON_ERROR_FAIL);
+ }
+ } else if (keyword.equals("parser")) {
+ try {
+ if (config == null) {
+ config = new Configuration();
+ }
+ parser = (XMLReader)config.getInstance(value, null);
+ } catch (XPathException err) {
+ try {
+ config.getErrorListener().warning(err);
+ } catch (TransformerException e) {
+ //
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ public static RegexFilter makeGlobFilter(String value) {
+ FastStringBuffer sb = new FastStringBuffer(value.length() + 6);
+ sb.append('^');
+ for (int i=0; i<value.length(); i++) {
+ char c = value.charAt(i);
+ if (c == '.') {
+ // replace "." with "\."
+ sb.append("\\.");
+ } else if (c == '*') {
+ // replace "*" with ".*"
+ sb.append(".*");
+ } else if (c == '?') {
+ // replace "*" with ".?"
+ sb.append(".?");
+ } else {
+ sb.append(c);
+ }
+ }
+ sb.append('$');
+ String s = sb.toString();
+ Pattern pattern = Pattern.compile(s);
+ return new RegexFilter(pattern);
+ }
+
+ /**
+ * Get the value of the strip-space=yes|no parameter.
+ * @return one of the values
+ * {@link Whitespace#ALL}, {@link net.sf.saxon.value.Whitespace#IGNORABLE}, {@link net.sf.saxon.value.Whitespace#NONE},
+ * {@link Whitespace#UNSPECIFIED}
+ */
+
+ public int getStripSpace() {
+ return strip;
+ }
+
+ /**
+ * Get the value of the validation=strict|lax|preserve|strip parameter, or null if unspecified
+ */
+
+ public Integer getValidationMode() {
+ return validation;
+ }
+
+ /**
+ * Get the file name filter (select=pattern), or null if unspecified
+ */
+
+ public FilenameFilter getFilenameFilter() {
+ return filter;
+ }
+
+ /**
+ * Get the value of the recurse=yes|no parameter, or null if unspecified
+ */
+
+ public Boolean getRecurse() {
+ return recurse;
+ }
+
+ /**
+ * Get the value of the on-error=fail|warning|ignore parameter, or null if unspecified
+ */
+
+ public Integer getOnError() {
+ return onError;
+ }
+
+ /**
+ * Get the value of xinclude=yes|no, or null if unspecified
+ */
+
+ public Boolean getXInclude() {
+ return xinclude;
+ }
+
+ /**
+ * Get the value of unparsed=yes|no, or false if unspecified
+ */
+
+ public boolean isUnparsed() {
+ return unparsed;
+ }
+
+ /**
+ * Get the selected XML parser, or null if unspecified
+ */
+
+ public XMLReader getXMLReader() {
+ return parser;
+ }
+
+ /**
+ * A FilenameFilter that tests file names against a regular expression
+ */
+
+ public static class RegexFilter implements FilenameFilter {
+
+ private Pattern pattern;
+
+
+ public RegexFilter(Pattern regex) {
+ this.pattern = regex;
+ }
+
+ /**
+ * Tests if a specified file should be included in a file list.
+ *
+ * @param dir the directory in which the file was found.
+ * @param name the name of the file.
+ * @return <code>true</code> if and only if the name should be
+ * included in the file list; <code>false</code> otherwise.
+ */
+
+ public boolean accept(File dir, String name) {
+ return new File(dir, name).isDirectory() || pattern.matcher(name).matches();
+ //return pattern.matcher(name).matches();
+ }
+
+ public boolean matches(String name) {
+ return pattern.matcher(name).matches();
+ }
+
+ }
+}
+
diff --git a/sf/saxon/functions/Unordered.java b/sf/saxon/functions/Unordered.java
new file mode 100644
index 0000000..258d67e
--- /dev/null
+++ b/sf/saxon/functions/Unordered.java
@@ -0,0 +1,66 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* XPath 2.0 unordered() function
+*/
+
+public class Unordered extends CompileTimeFunction {
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression exp = super.typeCheck(visitor, contextItemType);
+ if (exp instanceof Unordered) {
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ return ExpressionTool.unsorted(opt, ((Unordered)exp).argument[0], false);
+ }
+ return exp;
+ }
+
+ /*@NotNull*/
+ public Expression optimize(/*@NotNull*/ ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ Expression exp = super.optimize(visitor, contextItemType);
+ if (exp instanceof Unordered) {
+ return ExpressionTool.unsorted(visitor.getConfiguration().obtainOptimizer(),
+ ((Unordered) exp).argument[0], false);
+ }
+ return exp;
+ }
+
+ /**
+ * preEvaluate: called if the argument is constant
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) throws XPathException {
+ return argument[0];
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ return arguments[0];
+ }
+}
+
diff --git a/sf/saxon/functions/UnparsedEntity.java b/sf/saxon/functions/UnparsedEntity.java
new file mode 100644
index 0000000..e7be6d3
--- /dev/null
+++ b/sf/saxon/functions/UnparsedEntity.java
@@ -0,0 +1,156 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.StringValue;
+
+/**
+* Implements the unparsed-entity-uri() function defined in XSLT 1.0
+* and the unparsed-entity-public-id() function defined in XSLT 2.0
+*/
+
+
+public class UnparsedEntity extends SystemFunctionCall implements Callable {
+
+ public static int URI = 0;
+ public static int PUBLIC_ID = 1;
+
+ /**
+ * Simplify: add a second implicit argument, the context document
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ UnparsedEntity f = (UnparsedEntity)super.simplify(visitor);
+ f.addContextDocumentArgument(1, (operation==URI ? "unparsed-entity-uri_9999_": "unparsed-entity-public-id_9999_"));
+ return f;
+ }
+
+ /**
+ * Type-check the expression. This also calls preEvaluate() to evaluate the function
+ * if all the arguments are constant; functions that do not require this behavior
+ * can override the preEvaluate method.
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ try {
+ return super.typeCheck(visitor, contextItemType);
+ } catch (XPathException err) {
+ if ("XPDY0002".equals(err.getErrorCodeLocalPart())) {
+ if (operation == URI) {
+ XPathException e = new XPathException("Cannot call the unparsed-entity-uri()" +
+ " function when there is no context node");
+ e.setErrorCode("XTDE1370");
+ throw e;
+ } else {
+ XPathException e = new XPathException("Cannot call the unparsed-entity-public-id()" +
+ " function when there is no context node");
+ e.setErrorCode("XTDE1380");
+ e.setLocator(this);
+ throw e;
+ }
+ }
+ throw err;
+ }
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Evaluate the expression
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ String arg0 = argument[0].evaluateItem(context).getStringValue();
+ NodeInfo doc = null;
+ try {
+ doc = (NodeInfo)argument[1].evaluateItem(context);
+ } catch (XPathException err) {
+ String code = err.getErrorCodeLocalPart();
+ if ("XPDY0002".equals(code)) {
+ if (operation == URI) {
+ XPathException e = new XPathException("Cannot call the unparsed-entity-uri()" +
+ " function when there is no context node");
+ e.setErrorCode("XTDE1370");
+ throw e;
+ } else {
+ XPathException e = new XPathException("Cannot call the unparsed-entity-public-id()" +
+ " function when there is no context node");
+ e.setErrorCode("XTDE1380");
+ e.setLocator(this);
+ throw e;
+ }
+ } else if ("XPDY0050".equals(code)) {
+ if (operation == URI) {
+ XPathException e = new XPathException("Can only call the unparsed-entity-uri()" +
+ " function when the context node is in a tree rooted at a document node");
+ e.setErrorCode("XTDE1370");
+ e.setLocator(this);
+ throw e;
+ } else {
+ XPathException e = new XPathException("Can only call the unparsed-entity-public-id()" +
+ " function when the context node is in a tree rooted at a document node");
+ e.setErrorCode("XTDE1380");
+ e.setLocator(this);
+ throw e;
+ }
+ }
+ }
+ if (doc.getNodeKind() != Type.DOCUMENT) {
+ String code = (operation==URI ? "XTDE1370" : "XTDE1380");
+ dynamicError("In function " + getDisplayName() +
+ ", the context node must be in a tree whose root is a document node", code, context);
+ }
+ String[] ids = ((DocumentInfo)doc).getUnparsedEntity(arg0);
+ if (ids==null) return StringValue.EMPTY_STRING;
+ return new StringValue(ids[operation]);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ String arg0 = arguments[0].head().getStringValue();
+ NodeInfo doc = (NodeInfo)arguments[1].head();
+ if (doc.getNodeKind() != Type.DOCUMENT) {
+ String code = (operation==URI ? "XTDE1370" : "XTDE1380");
+ dynamicError("In function " + getDisplayName() +
+ ", the context node must be in a tree whose root is a document node", code, context);
+ }
+ String[] ids = ((DocumentInfo)doc).getUnparsedEntity(arg0);
+ return (ids==null ? StringValue.EMPTY_STRING : new StringValue(ids[operation]));
+ }
+}
+
diff --git a/sf/saxon/functions/UnparsedText.java b/sf/saxon/functions/UnparsedText.java
new file mode 100644
index 0000000..0f4dc7f
--- /dev/null
+++ b/sf/saxon/functions/UnparsedText.java
@@ -0,0 +1,306 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.StringValue;
+
+import java.io.*;
+import java.net.URI;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.MalformedInputException;
+import java.nio.charset.UnmappableCharacterException;
+
+
+public class UnparsedText extends SystemFunctionCall implements Callable {
+
+ // TODO: There is now a requirement that the results should be stable
+
+ // TODO: Consider supporting a query parameter ?substitute-character=xFFDE
+
+ /*@Nullable*/ String expressionBaseURI = null;
+
+
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ if (expressionBaseURI == null) {
+ super.checkArguments(visitor);
+ expressionBaseURI = visitor.getStaticContext().getBaseURI();
+ }
+ }
+
+ /**
+ * getExpressionBaseURI: this method returns the expression base URI
+ */
+ public String getExpressionBaseURI(){
+ return expressionBaseURI;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ // in principle we could pre-evaluate any call of unparsed-text() with
+ // constant arguments. But we don't, because the file contents might
+ // change before the stylesheet executes.
+ }
+
+ public int computeSpecialProperties() {
+ return super.computeSpecialProperties() &~ StaticProperty.NON_CREATIVE;
+ // Pretend the function is creative to prevent the result going into a global variable,
+ // which takes excessive memory. (TODO: But it does ensure stability...)
+ }
+
+ /**
+ * This method handles evaluation of the function:
+ * it returns a StringValue in the case of unparsed-text(), or a BooleanValue
+ * in the case of unparsed-text-available(). In the case of unparsed-text-lines()
+ * this shouldn't be called, but we deal with it anyway.
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ StringValue hrefVal = (StringValue)argument[0].evaluateItem(context);
+ String encoding = (getNumberOfArguments() == 2 ? argument[1].evaluateItem(context).getStringValue() : null);
+ return evalUnparsedText(hrefVal, encoding, context);
+ }
+
+ /**
+ * Get the prefix of the error code for dynamic errors: "XTDE" for XSLT 2.0, "FOUT" for XPath 3.0
+ */
+
+ private static String getErrorCodePrefix(XPathContext context) {
+ try {
+ if (context.getController().getExecutable().isAllowXPath30()) {
+ return "FOUT";
+ } else {
+ return "XTDE";
+ }
+ } catch (Exception e) {
+ return "XTDE";
+ }
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue hrefVal = (StringValue)arguments[0].head();
+ String encoding = (getNumberOfArguments() == 2 ? arguments[1].head().getStringValue() : null);
+ return evalUnparsedText(hrefVal, encoding, context);
+ }
+
+ public Item evalUnparsedText(StringValue hrefVal, String encoding, XPathContext context) throws XPathException {
+ CharSequence content;
+ StringValue result;
+ try {
+ if (hrefVal == null) {
+ return null;
+ }
+ String href = hrefVal.getStringValue();
+
+ content = readFile(href, expressionBaseURI, encoding, context);
+ result = new StringValue(content);
+ } catch (XPathException err) {
+ err.maybeSetErrorCode(getErrorCodePrefix(context) + "1170");
+ throw err;
+ }
+ return result;
+ }
+
+
+ /**
+ * Supporting routine to load one external file given a URI (href) and a baseURI
+ */
+
+ public CharSequence readFile(String href, String baseURI, String encoding, XPathContext context)
+ throws XPathException {
+
+ final Configuration config = context.getConfiguration();
+ NameChecker checker = config.getNameChecker();
+
+ // Use the URI machinery to validate and resolve the URIs
+
+ URI absoluteURI = getAbsoluteURI(href, baseURI, context);
+
+ Reader reader;
+ try {
+ reader = context.getController().getUnparsedTextURIResolver().resolve(absoluteURI, encoding, config);
+ } catch (XPathException err) {
+ err.maybeSetErrorCode(getErrorCodePrefix(context) + "1170");
+ err.maybeSetLocation(this);
+ throw err;
+ }
+ try {
+ return readFile(checker, reader, context);
+ } catch (java.io.UnsupportedEncodingException encErr) {
+ XPathException e = new XPathException("Unknown encoding " + Err.wrap(encoding), encErr);
+ e.setErrorCode(getErrorCodePrefix(context) + "1190");
+ throw e;
+ } catch (java.io.IOException ioErr) {
+// System.err.println("ProxyHost: " + System.getProperty("http.proxyHost"));
+// System.err.println("ProxyPort: " + System.getProperty("http.proxyPort"));
+ XPathException e = handleIOError(absoluteURI, ioErr, context);
+ e.setLocator(this);
+ throw e;
+ }
+ }
+
+ public static URI getAbsoluteURI(String href, String baseURI, XPathContext context) throws XPathException {
+ URI absoluteURI;
+ try {
+ absoluteURI = ResolveURI.makeAbsolute(href, baseURI);
+ } catch (java.net.URISyntaxException err) {
+ XPathException e = new XPathException(err.getReason() + ": " + err.getInput(), err);
+ e.setErrorCode(getErrorCodePrefix(context) + "1170");
+ throw e;
+ }
+
+ if (absoluteURI.getFragment() != null) {
+ XPathException e = new XPathException("URI for unparsed-text() must not contain a fragment identifier");
+ e.setErrorCode(getErrorCodePrefix(context) + "1170");
+ throw e;
+ }
+
+ // The URL dereferencing classes throw all kinds of strange exceptions if given
+ // ill-formed sequences of %hh escape characters. So we do a sanity check that the
+ // escaping is well-formed according to UTF-8 rules
+
+ EscapeURI.checkPercentEncoding(absoluteURI.toString());
+ return absoluteURI;
+ }
+
+ public static XPathException handleIOError(URI absoluteURI, IOException ioErr, XPathContext context) {
+ String message = "Failed to read input file";
+ if (absoluteURI != null && !ioErr.getMessage().equals(absoluteURI.toString())) {
+ message += ' ' + absoluteURI.toString();
+ }
+ message += " (" + ioErr.getClass().getName() + ')';
+ XPathException e = new XPathException(message, ioErr);
+ String errorCode = "FOUT1200";
+ if (context != null) {
+ if (ioErr instanceof MalformedInputException) {
+ errorCode = getErrorCodePrefix(context) + "1200";
+ } else if (ioErr instanceof CharacterCodingException) {
+ errorCode = getErrorCodePrefix(context) + "1200";
+ } else if (ioErr instanceof UnmappableCharacterException) {
+ errorCode = getErrorCodePrefix(context) + "1190";
+ } else {
+ errorCode = getErrorCodePrefix(context) + "1170";
+ }
+ }
+ e.setErrorCode(errorCode);
+ return e;
+ }
+
+ /**
+ * Read the contents of an unparsed text file
+ * @param checker NameChecker for checking whether characters are valid XML characters
+ * @param reader Reader to be used for reading the file
+ * @return a CharSequence representing the contents of the file
+ * @throws IOException if a failure occurs reading the file
+ * @throws XPathException if the file contains illegal characters
+ */
+
+ public static CharSequence readFile(NameChecker checker, Reader reader, XPathContext context) throws IOException, XPathException {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.LARGE);
+ char[] buffer = new char[2048];
+ boolean first = true;
+ int actual;
+ int line = 1;
+ int column = 1;
+ while (true) {
+ actual = reader.read(buffer, 0, 2048);
+ if (actual < 0) {
+ break;
+ }
+ for (int c=0; c<actual;) {
+ int ch32 = buffer[c++];
+ if (ch32 == '\n') {
+ line++;
+ column = 0;
+ }
+ column++;
+ if (UTF16CharacterSet.isHighSurrogate(ch32)) {
+ if (c==actual) {
+ actual = reader.read(buffer, 0, 2048);
+ c = 0;
+ }
+ char low = buffer[c++];
+ ch32 = UTF16CharacterSet.combinePair((char)ch32, low);
+ }
+ if (!checker.isValidChar(ch32)) {
+ XPathException err = new XPathException("The unparsed-text file contains a character that is illegal in XML (line=" +
+ line + " column=" + column + " value=hex " + Integer.toHexString(ch32) + ')');
+ err.setErrorCode(getErrorCodePrefix(context) + "1190");
+ throw err;
+ }
+ }
+ if (first) {
+ first = false;
+ if (buffer[0]=='\ufeff') {
+ // don't include the BOM in the result
+ sb.append(buffer, 1, actual-1);
+ } else {
+ sb.append(buffer, 0, actual);
+ }
+ } else {
+ sb.append(buffer, 0, actual);
+ }
+ }
+ reader.close();
+ return sb.condense();
+ }
+
+ // diagnostic method to output the octets of a file
+
+ public static void main(String[] args) throws Exception {
+ FastStringBuffer sb1 = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ FastStringBuffer sb2 = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ File file = new File(args[0]);
+ InputStream is = new FileInputStream(file);
+ while (true) {
+ int b = is.read();
+ if (b<0) {
+ System.out.println(sb1.toString());
+ System.out.println(sb2.toString()); break;
+ }
+ sb1.append(Integer.toHexString(b)+" ");
+ sb2.append((char)b + " ");
+ if (sb1.length() > 80) {
+ System.out.println(sb1.toString());
+ System.out.println(sb2.toString());
+ sb1 = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ sb2 = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ }
+ }
+ is.close();
+ }
+
+}
+
diff --git a/sf/saxon/functions/UnparsedTextAvailable.java b/sf/saxon/functions/UnparsedTextAvailable.java
new file mode 100644
index 0000000..5fe99d7
--- /dev/null
+++ b/sf/saxon/functions/UnparsedTextAvailable.java
@@ -0,0 +1,70 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.StringValue;
+
+
+public class UnparsedTextAvailable extends UnparsedText implements Callable {
+
+ // TODO: There is now a requirement that the results should be stable
+
+ /**
+ * This method handles evaluation of the function:
+ * it returns a StringValue in the case of unparsed-text(), or a BooleanValue
+ * in the case of unparsed-text-available(). In the case of unparsed-text-lines()
+ * this shouldn't be called, but we deal with it anyway.
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ StringValue hrefVal = (StringValue)argument[0].evaluateItem(context);
+ String encoding = (getNumberOfArguments() == 2 ? argument[1].evaluateItem(context).getStringValue() : null);
+ return BooleanValue.get(evalUnparsedTextAvailable(hrefVal, encoding, context));
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue hrefVal = (StringValue)arguments[0].head();
+ String encoding = (getNumberOfArguments() == 2 ? arguments[1].head().getStringValue() : null);
+ return BooleanValue.get(
+ evalUnparsedTextAvailable(hrefVal, encoding, context));
+ }
+
+ public boolean evalUnparsedTextAvailable(/*@Nullable*/ StringValue hrefVal, String encoding, XPathContext context) throws XPathException {
+ try {
+ if (hrefVal == null) {
+ return false;
+ }
+ String href = hrefVal.getStringValue();
+
+ readFile(href, expressionBaseURI, encoding, context);
+ } catch (XPathException err) {
+ return false;
+ }
+ return true;
+ }
+
+
+}
+
diff --git a/sf/saxon/functions/UpperCase.java b/sf/saxon/functions/UpperCase.java
new file mode 100644
index 0000000..8fc3112
--- /dev/null
+++ b/sf/saxon/functions/UpperCase.java
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import com.saxonica.bytecode.ExpressionCompiler;
+import com.saxonica.bytecode.ForceCaseCompiler;
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+
+/**
+* This class implements the fn:upper-case() function
+*/
+
+public class UpperCase extends SystemFunctionCall implements Callable {
+
+ /**
+ * Evaluate in a general context
+ */
+
+ public StringValue evaluateItem(XPathContext c) throws XPathException {
+ StringValue sv = (StringValue)argument[0].evaluateItem(c);
+ if (sv==null) {
+ return StringValue.EMPTY_STRING;
+ } else {
+ return StringValue.makeStringValue(sv.getStringValue().toUpperCase());
+ }
+ }
+
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ StringValue sv = (StringValue)arguments[0].head();
+ return (sv == null ? StringValue.EMPTY_STRING : StringValue.makeStringValue(sv.getStringValue().toUpperCase()));
+ }
+
+//#ifdefined BYTECODE
+ /**
+ * Return the compiler of the UpperCase expression
+ *
+ * @return the relevant ExpressionCompiler
+ */
+ @Override
+ public ExpressionCompiler getExpressionCompiler() {
+ return new ForceCaseCompiler();
+ }
+//#endif
+
+
+}
+
diff --git a/sf/saxon/functions/UriCollection.java b/sf/saxon/functions/UriCollection.java
new file mode 100644
index 0000000..c26a281
--- /dev/null
+++ b/sf/saxon/functions/UriCollection.java
@@ -0,0 +1,150 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.CollectionURIResolver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.AnyURIValue;
+
+import javax.xml.transform.Source;
+
+/**
+ * Implement the fn:uri-collection() function (new in XQuery 3.0/XSLT 3.0). This is responsible for calling the
+ * registered {@link net.sf.saxon.lib.CollectionURIResolver}. For the effect of the default
+ * system-supplied CollectionURIResolver, see {@link net.sf.saxon.lib.StandardCollectionURIResolver}
+ */
+
+public class UriCollection extends SystemFunctionCall implements Callable {
+
+ // TODO: change the standard collection URI resolver so that query parameters such as onerror=warning become
+ // part of the returned document URI, to be actioned only when the document URI is dereferenced
+
+ /*@Nullable*/ private String expressionBaseURI = null;
+
+ /**
+ * Bind aspects of the static context on which the particular function depends
+ *
+ * @param env the static context of the function call
+ * @throws net.sf.saxon.trans.XPathException
+ * if execution with this static context will inevitably fail
+ */
+ @Override
+ public void bindStaticContext(StaticContext env) throws XPathException {
+ expressionBaseURI = env.getBaseURI();
+ }
+
+ public String getStaticBaseURI() {
+ return expressionBaseURI;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * @param visitor an expression visitor
+ */
+
+ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+ /**
+ * Iterate over the contents of the collection
+ * @param context the dynamic context
+ * @return an iterator, whose items will always be nodes (typically but not necessarily document nodes)
+ * @throws net.sf.saxon.trans.XPathException
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(final XPathContext context) throws XPathException {
+
+ String href;
+
+ if (getNumberOfArguments() == 0) {
+ // No arguments supplied: this gets the default collection
+ href = context.getConfiguration().getDefaultCollection();
+ } else {
+ Item item = argument[0].evaluateItem(context);
+ href = (item == null ? context.getConfiguration().getDefaultCollection() : item.getStringValue());
+ }
+
+ CollectionURIResolver resolver = context.getConfiguration().getCollectionURIResolver();
+ SequenceIterator iter;
+ try {
+ iter = resolver.resolve(href, expressionBaseURI, context);
+ } catch (XPathException e) {
+ e.setLocator(this);
+ throw e;
+ }
+
+ return getResolverResults(iter, context);
+ }
+
+ /**
+ * Evaluate the expression
+ *
+ *
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as SequenceIterators
+ * @return the result of the evaluation, in the form of a SequenceIterator
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ String href;
+
+ if (arguments.length == 0) {
+ // No arguments supplied: this gets the default collection
+ href = context.getConfiguration().getDefaultCollection();
+ } else {
+ Item arg = arguments[0].head();
+ href = (arg == null ? context.getConfiguration().getDefaultCollection() : arg.getStringValue());
+ }
+
+ CollectionURIResolver resolver = context.getConfiguration().getCollectionURIResolver();
+ SequenceIterator iter;
+ try {
+ iter = resolver.resolve(href, expressionBaseURI, context);
+ } catch (XPathException e) {
+ e.setLocator(this);
+ throw e;
+ }
+
+ return SequenceTool.toLazySequence(getResolverResults(iter, context));
+ }
+
+ private static SequenceIterator getResolverResults(
+ SequenceIterator iter, final XPathContext context) {
+ if (iter == null) {
+ return EmptyIterator.getInstance();
+ } else {
+ ItemMappingFunction imf = new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ if (item instanceof NodeInfo) {
+ return DocumentUriFn.getDocumentURI(((NodeInfo)item), context);
+ } else if (item instanceof Source) {
+ return new AnyURIValue(((Source)item).getSystemId());
+ } else if (item instanceof AnyURIValue) {
+ return item;
+ } else {
+ throw new XPathException("Value returned by CollectionURIResolver must be a Source or an anyURI");
+ }
+ }
+ };
+ return new ItemMappingIterator(iter, imf);
+ }
+ }
+
+
+
+
+}
+
diff --git a/sf/saxon/functions/VendorFunctionLibrary.java b/sf/saxon/functions/VendorFunctionLibrary.java
new file mode 100644
index 0000000..cd21a00
--- /dev/null
+++ b/sf/saxon/functions/VendorFunctionLibrary.java
@@ -0,0 +1,58 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.functions;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * The VendorFunctionLibrary represents specially-recognized functions in the Saxon namespace.
+ */
+
+public class VendorFunctionLibrary extends IntegratedFunctionLibrary {
+
+ /**
+ * Create the Vendor Function Library for Saxon
+ */
+
+ public VendorFunctionLibrary() {
+ init();
+ }
+
+ protected void init() {
+ registerFunction(new IsWholeNumber());
+ }
+
+ @Override
+ public FunctionLibrary copy() {
+ return new VendorFunctionLibrary();
+ }
+
+ /**
+ * Make a Saxon function with a given name
+ * @param localName the local name of the function
+ * @param arguments the arguments of the function
+ * @param env the static context
+ * @param container the container for the new expression
+ * @return an exprssion representing a call on the given function
+ */
+
+ /*@Nullable*/ public Expression makeSaxonFunction(String localName, Expression[] arguments, StaticContext env, Container container)
+ throws XPathException {
+ String uri = NamespaceConstant.SAXON;
+ StructuredQName functionName = new StructuredQName("saxon", uri, localName);
+ return bind(functionName, arguments.length, arguments, env, container);
+ }
+
+
+}
+
diff --git a/sf/saxon/functions/package.html b/sf/saxon/functions/package.html
new file mode 100644
index 0000000..4235d97
--- /dev/null
+++ b/sf/saxon/functions/package.html
@@ -0,0 +1,50 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.functions</title>
+</head>
+
+<body>
+
+<p>This package provides implementations of all the core functions available for use
+in XPath expressions. This includes all the functions defined in the XPath 2.0
+<i>Functions and Operators</i> specification, as well as the additional functions
+defined for use in XSLT. The package also includes Saxon extension functions. Most
+of these are in a single class <code>Extensions</code>, but some of the more
+complex functions are in their own classes, for example <code>Evaluate</code> implements
+<code>saxon:evaluate()</code>.</p>
+
+<p>There is one class for group of closely-related functions. These all inherit from the class
+net.sf.saxon.expr.Function. The class <code>StandardFunction</code> is used to map a function
+name to its implementation; it contains tables of information describing the signature of each
+function, so that the type-checking code is completely generic.</p>
+
+<p>The package also contains machinery for defining user extension functions. A collection
+of functions is represented by a <code>FunctionLibrary</code> object. There are several
+standard function libraries available, covering core functions, Saxon extension functions
+constructor functions, and user extension functions: each category is covered by a subclass
+of <code>FunctionLibrary</code>, and there is also a <code>FunctionLibraryList</code> that
+represents the total collection of functions in these individual libraries. The
+<code>JavaExtensionLibrary</code> contains the logic for binding Java extension functions
+given their name and arity and the types of their arguments. The class <code>ExtensionFunctionCall</code>
+contains the run-time logic for converting XPath values to the required Java types, and for converting
+the result back to an XPath value.</p>
+
+<p>These classes, although public, will not normally be used directly by user-written
+ Java applications.</p>
+
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/java/JavaCollationFactory.java b/sf/saxon/java/JavaCollationFactory.java
new file mode 100644
index 0000000..7b0c073
--- /dev/null
+++ b/sf/saxon/java/JavaCollationFactory.java
@@ -0,0 +1,216 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.java;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.sort.AlphanumericCollator;
+import net.sf.saxon.expr.sort.CaseFirstCollator;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.XPathException;
+
+import java.text.Collator;
+import java.text.ParseException;
+import java.text.RuleBasedCollator;
+import java.util.Comparator;
+import java.util.Locale;
+import java.util.Properties;
+
+/**
+ * A JavaCollationFactory allows a Collation to be created given
+ * a set of properties that the collation should have. This class creates a collator using
+ * the facilities of the Java platform; there is a corresponding class that uses the .NET
+ * platform.
+*/
+
+public abstract class JavaCollationFactory {
+
+ /**
+ * The class is a never instantiated
+ */
+ private JavaCollationFactory() {
+ }
+
+ /**
+ * Make a collator with given properties
+ * @param config the Configuration
+ * @param props the desired properties of the collation
+ * @return a collation with these properties
+ */
+
+ /*@Nullable*/ public static StringCollator makeCollation(Configuration config, String uri, Properties props)
+ throws XPathException {
+
+ Collator collator = null;
+ StringCollator stringCollator = null;
+
+ // If a specific collation class is requested, this overrides everything else
+
+ String classAtt = props.getProperty("class");
+ if (classAtt != null) {
+ Object comparator = config.getInstance(classAtt, null);
+ if (comparator instanceof Collator) {
+ collator = (Collator)comparator;
+ } else if (comparator instanceof StringCollator) {
+ stringCollator = (StringCollator)comparator;
+ } else if (comparator instanceof Comparator) {
+ stringCollator = new SimpleCollation((Comparator)comparator);
+ } else {
+ throw new XPathException("Requested collation class " + classAtt + " is not a Comparator");
+ }
+ }
+
+ // If rules are specified, create a RuleBasedCollator
+
+ if (collator == null && stringCollator == null) {
+ String rulesAtt = props.getProperty("rules");
+ if (rulesAtt != null) {
+ try {
+ collator = new RuleBasedCollator(rulesAtt);
+ } catch (ParseException e) {
+ throw new XPathException("Invalid collation rules: " + e.getMessage());
+ }
+ }
+
+ // Start with the lang attribute
+
+ if (collator == null) {
+ String langAtt = props.getProperty("lang");
+ if (langAtt!=null) {
+ collator = Collator.getInstance(getLocale(langAtt));
+ } else {
+ collator = Collator.getInstance(); // use default locale
+ }
+ }
+ }
+
+ if (collator != null) {
+ // See if there is a strength attribute
+ String strengthAtt = props.getProperty("strength");
+ if (strengthAtt!=null) {
+ if (strengthAtt.equals("primary")) {
+ collator.setStrength(Collator.PRIMARY);
+ } else if (strengthAtt.equals("secondary")) {
+ collator.setStrength(Collator.SECONDARY);
+ } else if (strengthAtt.equals("tertiary")) {
+ collator.setStrength(Collator.TERTIARY);
+ } else if (strengthAtt.equals("identical")) {
+ collator.setStrength(Collator.IDENTICAL);
+ } else {
+ throw new XPathException("strength must be primary, secondary, tertiary, or identical");
+ }
+ }
+
+ // Look for the properties ignore-case, ignore-modifiers, ignore-width
+
+ String ignore = props.getProperty("ignore-width");
+ if (ignore != null) {
+ if (ignore.equals("yes") && strengthAtt == null) {
+ collator.setStrength(Collator.TERTIARY);
+ } else if (ignore.equals("no")) {
+ // no-op
+ } else {
+ throw new XPathException("ignore-width must be yes or no");
+ }
+ }
+
+ ignore = props.getProperty("ignore-case");
+ if (ignore != null && strengthAtt == null) {
+ if (ignore.equals("yes")) {
+ collator.setStrength(Collator.SECONDARY);
+ } else if (ignore.equals("no")) {
+ // no-op
+ } else {
+ throw new XPathException("ignore-case must be yes or no");
+ }
+ }
+
+ ignore = props.getProperty("ignore-modifiers");
+ if (ignore != null) {
+ if (ignore.equals("yes") && strengthAtt == null) {
+ collator.setStrength(Collator.PRIMARY);
+ } else if (ignore.equals("no")) {
+ // no-op
+ } else {
+ throw new XPathException("ignore-modifiers must be yes or no");
+ }
+ }
+
+ // The ignore-symbols property is ignored
+
+ // See if there is a decomposition attribute
+ String decompositionAtt = props.getProperty("decomposition");
+ if (decompositionAtt!=null) {
+ if (decompositionAtt.equals("none")) {
+ collator.setDecomposition(Collator.NO_DECOMPOSITION);
+ } else if (decompositionAtt.equals("standard")) {
+ collator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
+ } else if (decompositionAtt.equals("full")) {
+ collator.setDecomposition(Collator.FULL_DECOMPOSITION);
+ } else {
+ throw new XPathException("decomposition must be none, standard, or full");
+ }
+ }
+ }
+
+ if (stringCollator == null) {
+ stringCollator = new SimpleCollation(collator);
+ }
+
+ // See if there is a case-order property
+ String caseOrder = props.getProperty("case-order");
+ if (caseOrder != null && !"#default".equals(caseOrder)) {
+ // force base collator to ignore case differences
+ if (collator != null) {
+ collator.setStrength(Collator.SECONDARY);
+ }
+ if (caseOrder.equals("lower-first")) {
+ stringCollator = new CaseFirstCollator(stringCollator, false);
+ } else if (caseOrder.equals("upper-first")) {
+ stringCollator = new CaseFirstCollator(stringCollator, true);
+ } else {
+ throw new XPathException("case-order must be lower-first, upper-first, or #default");
+ }
+ }
+
+ // See if there is an alphanumeric property
+ String alphanumeric = props.getProperty("alphanumeric");
+ if (alphanumeric != null && !"no".equals(alphanumeric)) {
+ if (alphanumeric.equals("yes")) {
+ stringCollator = new AlphanumericCollator(stringCollator);
+ } else if (alphanumeric.equals("codepoint")) {
+ stringCollator = new AlphanumericCollator(CodepointCollator.getInstance());
+ } else {
+ throw new XPathException("alphanumeric must be yes, no, or codepoint");
+ }
+ }
+
+ return stringCollator;
+ }
+
+ /**
+ * Get a locale given a language code in XML format
+ */
+
+ private static Locale getLocale(String lang) {
+ int hyphen = lang.indexOf("-");
+ String language, country;
+ if (hyphen < 1) {
+ language = lang;
+ country = "";
+ } else {
+ language = lang.substring(0, hyphen);
+ country = lang.substring(hyphen+1);
+ }
+ return new Locale(language, country);
+ }
+
+
+}
+
diff --git a/sf/saxon/java/JavaPlatform.java b/sf/saxon/java/JavaPlatform.java
new file mode 100644
index 0000000..7d32b20
--- /dev/null
+++ b/sf/saxon/java/JavaPlatform.java
@@ -0,0 +1,265 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.java;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Platform;
+import net.sf.saxon.dom.DOMEnvelope;
+import net.sf.saxon.dom.DOMObjectModel;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.functions.FunctionLibraryList;
+import net.sf.saxon.lib.StandardCollectionURIResolver;
+import net.sf.saxon.regex.RegularExpression;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.regex.ARegularExpression;
+import net.sf.saxon.regex.JavaRegularExpression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.stream.StreamSource;
+import java.text.Collator;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Implementation of the Platform class containing methods specific to the Java platform
+ * (as distinct from .NET). This is a singleton class with no instance data.
+ */
+public class JavaPlatform implements Platform {
+
+ /**
+ * The constructor is called during the static initialization of the Configuration
+ */
+
+ public JavaPlatform() {}
+
+ /**
+ * Perform platform-specific initialization of the configuration
+ */
+
+ public void initialize(Configuration config) {
+ config.registerExternalObjectModel(DOMEnvelope.getInstance());
+ config.registerExternalObjectModel(DOMObjectModel.getInstance());
+ config.setCollectionURIResolver(new StandardCollectionURIResolver());
+ }
+
+ /**
+ * Return true if this is the Java platform
+ */
+
+ public boolean isJava() {
+ return true;
+ }
+
+ /**
+ * Return true if this is the .NET platform
+ */
+
+ public boolean isDotNet() {
+ return false;
+ }
+
+ /**
+ * Get the platform version
+ */
+
+ public String getPlatformVersion() {
+ return "Java version " + System.getProperty("java.version");
+ }
+
+ /**
+ * Get a suffix letter to add to the Saxon version number to identify the platform
+ */
+
+ public String getPlatformSuffix() {
+ return "J";
+ }
+
+ /**
+ * Get a parser by instantiating the SAXParserFactory
+ *
+ * @return the parser (XMLReader)
+ */
+
+ public XMLReader loadParser() {
+ XMLReader parser;
+ try {
+ parser = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
+ } catch (ParserConfigurationException err) {
+ throw new TransformerFactoryConfigurationError(err);
+ } catch (SAXException err) {
+ throw new TransformerFactoryConfigurationError(err);
+ }
+ return parser;
+ }
+
+ /**
+ * Convert a StreamSource to either a SAXSource or a PullSource, depending on the native
+ * parser of the selected platform
+ *
+ * @param pipe the pipeline configuration
+ * @param input the supplied StreamSource
+ * @param validation indicates whether schema validation is required
+ * @param dtdValidation indicates whether DTD validation is required
+ * @param stripspace indicates whether whitespace text nodes should be stripped
+ * @return the PullSource or SAXSource, initialized with a suitable parser, or the original
+ * input Source, if now special handling is required or possible. This implementation
+ * always returns the original input unchanged.
+ */
+
+ public Source getParserSource(PipelineConfiguration pipe, StreamSource input, int validation,
+ boolean dtdValidation, int stripspace) {
+ return input;
+ }
+
+ /**
+ * Obtain a collation with a given set of properties. The set of properties is extensible
+ * and variable across platforms. Common properties with example values include lang=ed-GB,
+ * strength=primary, case-order=upper-first, ignore-modifiers=yes, alphanumeric=yes.
+ * Properties that are not supported are generally ignored; however some errors, such as
+ * failing to load a requested class, are fatal.
+ * @param config the configuration object
+ * @param props the desired properties of the collation
+ * @param uri the collation URI
+ * @return a collation with these properties
+ * @throws XPathException if a fatal error occurs
+ */
+
+ /*@Nullable*/ public StringCollator makeCollation(Configuration config, Properties props, String uri) throws XPathException {
+ return JavaCollationFactory.makeCollation(config, uri, props);
+ }
+
+ /**
+ * Given a collation, determine whether it is capable of returning collation keys.
+ * The essential property of collation keys
+ * is that if two values are equal under the collation, then the collation keys are
+ * equal under the equals() method.
+ *
+ * @param collation the collation, provided as a Comparator
+ * @return true if this collation can supply collation keys
+ */
+
+ public boolean canReturnCollationKeys(StringCollator collation) {
+ return (collation instanceof CodepointCollator) ||
+ ((collation instanceof SimpleCollation) &&
+ (((SimpleCollation)collation).getCollation() instanceof Collator));
+ }
+
+ /**
+ * Given a collation, get a collation key. The essential property of collation keys
+ * is that if two values are equal under the collation, then the collation keys are
+ * compare correctly under the equals() method.
+ *
+ * @throws ClassCastException if the collation is not one that is capable of supplying
+ * collation keys (this should have been checked in advance)
+ */
+
+ public Object getCollationKey(SimpleCollation namedCollation, String value) {
+ return ((Collator)namedCollation.getCollation()).getCollationKey(value);
+ }
+
+ /**
+ * Compile a regular expression
+ *
+ *
+ * @param regex the regular expression as a string
+ * @param flags the value of the flags attribute
+ * @param hostLanguage one of "XSD10", "XSD11", "XP20" or "XP30"
+ * @param warnings
+ * @return the compiled regular expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if the regular expression or the flags are invalid
+ */
+ public RegularExpression compileRegularExpression(CharSequence regex, String flags, String hostLanguage, List<String> warnings) throws XPathException {
+ // recognize "!" as the last flag to mean: use native Java regex syntax
+ if (flags.endsWith("!")) {
+ return new JavaRegularExpression(regex, flags.substring(0, flags.length()-1));
+ } else {
+ return new ARegularExpression(regex, flags, hostLanguage, warnings);
+ }
+ }
+
+ /**
+ * Add the platform-specific function libraries to a function library list. This version
+ * of the method does nothing
+ * @param list the function library list that is to be extended
+ * @param config the Configuration
+ * @param hostLanguage the host language, for example Configuration.XQUERY
+ */
+
+ public void addFunctionLibraries(FunctionLibraryList list, Configuration config, int hostLanguage) {
+ // do nothing
+ }
+
+ public SchemaType getExternalObjectType(Configuration config, String uri, String localName) {
+ throw new UnsupportedOperationException("getExternalObjectType for Java");
+ }
+
+ /**
+ * Return the name of the directory in which the software is installed (if available)
+ * @return the name of the directory in which Saxon is installed, if available, or null otherwise
+ * @param edition
+ */
+
+ public String getInstallationDirectory(String edition, Configuration config) {
+ return System.getenv("SAXON_HOME");
+ }
+
+ /**
+ * Register all the external object models that are provided as standard
+ * with the relevant edition of Saxon for this Configuration
+ *
+ * @since 9.3
+ */
+
+ public void registerAllBuiltInObjectModels(Configuration config) {
+ // No action for Saxon-HE
+ }
+
+ /**
+ * Set the default XML parser to be loaded by the SAXParserFactory on this platform.
+ * Needed because the Apache catalog resolver uses the SAXParserFactory to instantiate
+ * a parser, and if not customized this causes a failure on the .NET platform.
+ *
+ * @since 9.4
+ */
+
+ public void setDefaultSAXParserFactory(){
+ // No action for Saxon on Java
+ }
+
+ public ClassLoader getClassLoaderForGeneratedClass(final String definedClassName, final byte[] classFile, Configuration config, Class thisClass) {
+ ClassLoader parentClassLoader = config.getDynamicLoader().getClassLoader();
+ if (parentClassLoader == null) {
+ parentClassLoader = Thread.currentThread().getContextClassLoader();
+ }
+ if (parentClassLoader == null) {
+ parentClassLoader = thisClass.getClassLoader();
+ }
+ return new ClassLoader(parentClassLoader) {
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals(definedClassName)) {
+ return defineClass(name, classFile, 0, classFile.length);
+ } else {
+ return super.findClass(name);
+ }
+ }
+ };
+ }
+
+}
+
diff --git a/sf/saxon/java/package.html b/sf/saxon/java/package.html
new file mode 100644
index 0000000..65604f1
--- /dev/null
+++ b/sf/saxon/java/package.html
@@ -0,0 +1,26 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.java</title>
+
+</head>
+ <body>
+ <p>This package contains Saxon code that is specific to the Java platform, as distinct from .NET</p>
+
+ <p>The areas where Saxon has different implementations for the two platforms are primarily
+ URI handling, interfaces to XML parsers, regular expression handling, and use of collations.</p>
+
+ <p>Access to these classes is generally via the <code>Platform</code> object, of which the
+ implementation for the Java platform is named <code>JavaPlatform</code>. This is obtained in turn
+ via the static method <code>Configuration.getPlatform()</code></p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/lib/AugmentedSource.java b/sf/saxon/lib/AugmentedSource.java
new file mode 100644
index 0000000..05b795e
--- /dev/null
+++ b/sf/saxon/lib/AugmentedSource.java
@@ -0,0 +1,523 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.FilterFactory;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.TreeModel;
+import net.sf.saxon.type.SchemaType;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.XMLReader;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.sax.SAXSource;
+import java.util.List;
+
+/**
+ * This class is an extension of the JAXP Source interface. The class can be used
+ * wherever a JAXP Source object can be used, and it provides additional information
+ * about the way that the Source is to be processed: for example, it indicates
+ * whether or not it should be validated against a schema. Other options that can
+ * be set include the SAX XMLReader to be used, and the choice of whether a source
+ * in the form of an existing tree should be copied or wrapped.
+ *
+ * <p>Internally, an AugmentedSource combines an underlying Source object with a
+ * {@link ParseOptions} object holding the selected options. Many Saxon interfaces allow
+ * the ParseOptions to be supplied directly, making this class unnecessary; but it is useful
+ * when passing a Source to a JAXP interface that does not allow further options to be
+ * supplied.</p>
+ * @since 8.8
+ */
+
+public class AugmentedSource implements Source {
+
+ private Source source;
+ private ParseOptions options = new ParseOptions();
+
+ /**
+ * Create an AugmentedSource that wraps a given Source object (which must not itself be an
+ * AugmentedSource)
+ * @param source the Source object to be wrapped. This must be an implementation of Source
+ * that Saxon recognizes, or an implementation for which a {@link net.sf.saxon.lib.SourceResolver} has been
+ * registered with the {@link net.sf.saxon.Configuration}. The source must not itself be an
+ * AugmentedSource.
+ * <p>As an alternative to this constructor, consider using the factory method
+ * {@link #makeAugmentedSource}, which does accept any kind of Source including an
+ * AugmentedSource as input.</p>
+ * @throws IllegalArgumentException if the wrapped source is an AugmentedSource
+ * @since 8.8
+ */
+
+ private AugmentedSource(Source source) {
+ if (source instanceof AugmentedSource) {
+ throw new IllegalArgumentException("Contained source must not be an AugmentedSource");
+ }
+ this.source = source;
+ }
+
+ /**
+ * Create an AugmentedSource that wraps a given Source object. If this is already
+ * an AugmentedSource, the original AugmentedSource is returned. Note that this means that
+ * setting any properties on the returned AugmentedSource will also affect the original.
+ * @param source the Source object to be wrapped
+ * @return an AugmentedSource
+ * @since 8.8
+ */
+
+ public static AugmentedSource makeAugmentedSource(Source source) {
+ if (source instanceof AugmentedSource) {
+ return (AugmentedSource)source;
+ }
+ return new AugmentedSource(source);
+ }
+
+ /**
+ * Add a filter to the list of filters to be applied to the raw input
+ * @param filter a factory for the filter to be added
+ */
+
+ public void addFilter(FilterFactory filter) {
+ options.addFilter(filter);
+ }
+
+ /**
+ * Get the list of filters to be applied to the input. Returns null if there are no filters.
+ * @return the list of filters, if there are any
+ */
+
+ public List<FilterFactory> getFilters() {
+ return options.getFilters();
+ }
+
+ /**
+ * Get the Source object wrapped by this AugmentedSource
+ * @return the contained Source object
+ * @since 8.8
+ */
+
+ public Source getContainedSource() {
+ return source;
+ }
+
+ /**
+ * Get the ParseOptions defined in this AugmentedSource
+ * @return the ParseOptions, a bundle of options equivalent to obtaining all the
+ * properties individually. Changes to this object will be live, that is, they will
+ * affect the AugmentedSource from which they came.
+ */
+
+ public ParseOptions getParseOptions() {
+ return options;
+ }
+
+ /**
+ * Set the space-stripping action to be applied to the source document
+ * @param stripAction one of {@link net.sf.saxon.value.Whitespace#IGNORABLE},
+ * {@link net.sf.saxon.value.Whitespace#ALL}, or {@link net.sf.saxon.value.Whitespace#NONE}
+ * @since 8.8
+ */
+
+ public void setStripSpace(int stripAction) {
+ options.setStripSpace(stripAction);
+ }
+
+ /**
+ * Get the space-stripping action to be applied to the source document
+ * @return one of {@link net.sf.saxon.value.Whitespace#IGNORABLE},
+ * {@link net.sf.saxon.value.Whitespace#ALL}, or {@link net.sf.saxon.value.Whitespace#NONE}
+ * @since 8.8
+ */
+
+ public int getStripSpace() {
+ return options.getStripSpace();
+ }
+
+ /**
+ * Set the tree model to use. Default is the tiny tree
+ * @param model one of {@link Builder#TINY_TREE}, {@link Builder#TINY_TREE_CONDENSED} or
+ * {@link Builder#LINKED_TREE}
+ * @since 8.9 (Condensed tree added in 9.2)
+ * @deprecated since 9.2: use {@link #setModel}
+ */
+
+ public void setTreeModel(int model) {
+ options.setTreeModel(model);
+ }
+
+ /**
+ * Get the tree model that will be used.
+ * @return one of {@link Builder#TINY_TREE}, {@link Builder#TINY_TREE_CONDENSED},
+ * {@link Builder#LINKED_TREE}, or {link Builder#UNSPECIFIED_TREE_MODEL}
+ * if no call on setTreeModel() has been made
+ * @since 8.9 (Condensed tree added in 9.2)
+ * @deprecated since 9.2: use {@link #getModel}
+ */
+
+ public int getTreeModel() {
+ return options.getTreeModel();
+ }
+
+ /**
+ * Set the tree model to use. Default is the tiny tree
+ * @param model typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link net.sf.saxon.om.TreeModel#TINY_TREE_CONDENSED}, or {@link net.sf.saxon.om.TreeModel#LINKED_TREE}. However, in principle
+ * a user-defined tree model can be used.
+ * @since 9.2
+ */
+
+ public void setModel(TreeModel model) {
+ options.setModel(model);
+ }
+
+ /**
+ * Get the tree model that will be used.
+ * @return typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link TreeModel#TINY_TREE_CONDENSED}, or {@link TreeModel#LINKED_TREE}. However, in principle
+ * a user-defined tree model can be used.
+ * @since 9.2
+ */
+
+ public TreeModel getModel() {
+ return options.getModel();
+ }
+
+
+ /**
+ * Set whether or not schema validation of this source is required
+ * @param option one of {@link Validation#STRICT},
+ * {@link Validation#LAX}, {@link Validation#STRIP},
+ * {@link Validation#PRESERVE}, {@link Validation#DEFAULT}
+ * @since 8.8
+ *
+ */
+
+ public void setSchemaValidationMode(int option) {
+ options.setSchemaValidationMode(option);
+ }
+
+ /**
+ * Get whether or not schema validation of this source is required
+ * @return the validation mode requested, or {@link Validation#DEFAULT}
+ * to use the default validation mode from the Configuration.
+ * @since 8.8
+ */
+
+ public int getSchemaValidation() {
+ return options.getSchemaValidationMode();
+ }
+
+ /**
+ * Set the name of the top-level element for validation.
+ * If a top-level element is set then the document
+ * being validated must have this as its outermost element
+ * @param elementName the QName of the required top-level element, or null to unset the value
+ * @since 9.0. Type of argument changed in 9.5.
+ */
+
+ public void setTopLevelElement(NodeName elementName) {
+ options.setTopLevelElement(elementName);
+ }
+
+ /**
+ * Get the name of the top-level element for validation.
+ * If a top-level element is set then the document
+ * being validated must have this as its outermost element
+ * @return the QName of the required top-level element, or null if no value is set
+ * @since 9.0. Type of result changed in 9.5.
+ */
+
+ public NodeName getTopLevelElement() {
+ return options.getTopLevelElement();
+ }
+
+ /**
+ * Set the type of the top-level element for validation.
+ * If this is set then the document element is validated against this type
+ * @param type the schema type required for the document element, or null to unset the value
+ * @since 9.0
+ */
+
+ public void setTopLevelType(SchemaType type) {
+ options.setTopLevelType(type);
+ }
+
+ /**
+ * Get the type of the document element for validation.
+ * If this is set then the document element of the document
+ * being validated must have this type
+ * @return the type of the required top-level element, or null if no value is set
+ * @since 9.0
+ */
+
+ public SchemaType getTopLevelType() {
+ return options.getTopLevelType();
+ }
+
+ /**
+ * Set whether or not DTD validation of this source is required
+ * @param option one of {@link Validation#STRICT},
+ * {@link Validation#STRIP}, {@link Validation#DEFAULT}
+ * @since 8.8
+ */
+
+ public void setDTDValidationMode(int option) {
+ options.setDTDValidationMode(option);
+ }
+
+ /**
+ * Get whether or not DTD validation of this source is required
+ * @return the validation mode requested, or {@link Validation#DEFAULT}
+ * to use the default validation mode from the Configuration.
+ * @since 8.8
+ */
+
+ public int getDTDValidation() {
+ return options.getDTDValidationMode();
+ }
+
+
+ /**
+ * Set whether line numbers are to be maintained in the constructed document
+ * @param lineNumbering true if line numbers are to be maintained
+ * @since 8.8
+ */
+
+ public void setLineNumbering(boolean lineNumbering) {
+ options.setLineNumbering(lineNumbering);
+ }
+
+ /**
+ * Get whether line numbers are to be maintained in the constructed document
+ * @return true if line numbers are maintained
+ * @since 8.8
+ */
+
+ public boolean isLineNumbering() {
+ return options.isLineNumbering();
+ }
+
+ /**
+ * Determine whether setLineNumbering() has been called
+ * @return true if setLineNumbering() has been called
+ * @since 8.9
+ */
+
+ public boolean isLineNumberingSet() {
+ return options.isLineNumberingSet();
+ }
+
+ /**
+ * Set the SAX parser (XMLReader) to be used
+ * @param parser the SAX parser
+ * @since 8.8
+ */
+
+ public void setXMLReader(XMLReader parser) {
+ options.setXMLReader(parser);
+ if (source instanceof SAXSource) {
+ ((SAXSource)source).setXMLReader(parser);
+ }
+ }
+
+ /**
+ * Get the SAX parser (XMLReader) to be used
+ * @return the parser
+ * @since 8.8
+ */
+
+ /*@Nullable*/ public XMLReader getXMLReader() {
+ XMLReader parser = options.getXMLReader();
+ if (parser != null) {
+ return parser;
+ } else if (source instanceof SAXSource) {
+ return ((SAXSource)source).getXMLReader();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Assuming that the contained Source is a node in a tree, indicate whether a tree should be created
+ * as a view of this supplied tree, or as a copy.
+ * <p>This option is used only when the Source is supplied to an interface such as the JAXP
+ * Transformer.transform() method where there is no other way of indicating whether a supplied
+ * external document should be wrapped or copied. It is not used when the Source is supplied to
+ * a Saxon-defined interface.</p>
+ * @param wrap if true, the node in the supplied Source is wrapped, to create a view. If false, the node
+ * and its contained subtree is copied. If null, the system default is chosen.
+ * @since 8.8
+ */
+
+ public void setWrapDocument(Boolean wrap) {
+ options.setWrapDocument(wrap);
+ }
+
+ /**
+ Assuming that the contained Source is a node in a tree, determine whether a tree will be created
+ * as a view of this supplied tree, or as a copy.
+ * <p>This option is used only when the Source is supplied to an interface such as the JAXP
+ * Transformer.transform() method where there is no other way of indicating whether a supplied
+ * external document should be wrapped or copied. It is not used when the Source is supplied to
+ * a Saxon-defined interface.</p>
+ * @return if true, the node in the supplied Source is wrapped, to create a view. If false, the node
+ * and its contained subtree is copied. If null, the system default is chosen.
+ * @since 8.8
+ */
+
+ public Boolean getWrapDocument() {
+ return options.getWrapDocument();
+ }
+
+ /**
+ * Set the System ID. This sets the System Id on the underlying Source object.
+ * @param id the System ID. This provides a base URI for the document, and also the result
+ * of the document-uri() function
+ * @since 8.8
+ */
+
+ public void setSystemId(String id) {
+ source.setSystemId(id);
+ }
+
+ /**
+ * Get the System ID. This gets the System Id on the underlying Source object.
+ * @return the System ID: effectively the base URI.
+ * @since 8.8
+ */
+
+ public String getSystemId() {
+ return source.getSystemId();
+ }
+
+ /**
+ * <p>Set state of XInclude processing.</p>
+ * <p/>
+ * <p>If XInclude markup is found in the document instance, should it be
+ * processed as specified in <a href="http://www.w3.org/TR/xinclude/">
+ * XML Inclusions (XInclude) Version 1.0</a>.</p>
+ * <p/>
+ * <p>XInclude processing defaults to <code>false</code>.</p>
+ *
+ * @param state Set XInclude processing to <code>true</code> or
+ * <code>false</code>
+ * @since 8.9
+ */
+ public void setXIncludeAware(boolean state) {
+ options.setXIncludeAware(state);
+ }
+
+ /**
+ * <p>Determine whether setXIncludeAware() has been called.</p>
+ *
+ * @return true if setXIncludeAware() has been called
+ * @since 8.9
+ */
+
+ public boolean isXIncludeAwareSet() {
+ return options.isXIncludeAwareSet();
+ }
+
+ /**
+ * <p>Get state of XInclude processing.</p>
+ *
+ * @return current state of XInclude processing. Default value is false.
+ * @since 8.9
+ */
+
+ public boolean isXIncludeAware() {
+ return options.isXIncludeAware();
+ }
+
+ /**
+ * Set an EntityResolver to be used when parsing. Note this is used only
+ * when a system-allocated parser is used; when a user-supplied parser
+ * is used (for example in a SAXSource), the EntityResolver should be preinitialized.
+ * @param resolver the EntityResolver to be used
+ * @since 8.9. The method had no useful effect in releases prior to 9.2.
+ */
+
+ public void setEntityResolver(EntityResolver resolver) {
+ options.setEntityResolver(resolver);
+ }
+
+ /**
+ * Get the EntityResolver that will be used when parsing
+ * @return the EntityResolver, if one has been set using {@link #setEntityResolver},
+ * otherwise null.
+ * @since 8.9 The method had no useful effect in releases prior to 9.2.
+ */
+
+ public EntityResolver getEntityResolver() {
+ return options.getEntityResolver();
+ }
+
+ /**
+ * Set an ErrorListener to be used when parsing
+ * @param listener the ErrorListener to be used
+ * @since 8.9
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ options.setErrorListener(listener);
+ }
+
+ /**
+ * Get the ErrorListener that will be used when parsing
+ * @return the ErrorListener, if one has been set using {@link #setErrorListener},
+ * otherwise null.
+ * @since 8.9
+ */
+
+ public ErrorListener getErrorListener() {
+ return options.getErrorListener();
+ }
+
+
+
+ /**
+ * Set whether or not the user of this Source is encouraged to close it as soon as reading is finished.
+ * Normally the expectation is that any Stream in a StreamSource will be closed by the component that
+ * created the Stream. However, in the case of a Source returned by a URIResolver, there is no suitable
+ * interface (the URIResolver has no opportunity to close the stream). Also, in some cases such as reading
+ * of stylesheet modules, it is possible to close the stream long before control is returned to the caller
+ * who supplied it. This tends to make a difference on .NET, where a file often can't be opened if there
+ * is a stream attached to it.
+ * @param close true if the source should be closed as soon as it has been consumed
+ * @since 8.8
+ */
+
+ public void setPleaseCloseAfterUse(boolean close) {
+ options.setPleaseCloseAfterUse(close);
+ }
+
+ /**
+ * Determine whether or not the user of this Source is encouraged to close it as soon as reading is
+ * finished.
+ * @return true if the source should be closed as soon as it has been consumed
+ * @since 8.8
+ */
+
+ public boolean isPleaseCloseAfterUse() {
+ return options.isPleaseCloseAfterUse();
+ }
+
+ /**
+ * Close any resources held by this Source. This only works if the underlying Source is one that is
+ * recognized as holding closable resources.
+ * @since 8.8
+ */
+
+ public void close() {
+ ParseOptions.close(source);
+ }
+
+
+}
+
diff --git a/sf/saxon/lib/CollationURIResolver.java b/sf/saxon/lib/CollationURIResolver.java
new file mode 100644
index 0000000..aa87427
--- /dev/null
+++ b/sf/saxon/lib/CollationURIResolver.java
@@ -0,0 +1,48 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+
+import java.io.Serializable;
+
+/**
+ * A CollationURIResolver accepts a collation name as input, and returns
+ * a collation (represented by a {@link StringCollator} as output. A CollationURIResolver
+ * can be registered with the Configuration (or with a TransformerFactory)
+ * to resolve all collation URIs used in a stylesheet or query.
+ */
+public interface CollationURIResolver extends Serializable {
+
+ /**
+ * Resolve a collation URI (expressed as a string) and return
+ * the corresponding collation.
+ * @param relativeURI the collation URI as written in the query or stylesheet
+ * @param baseURI The base URI of the static context where the collation URI
+ * appears. The base URI is available only in cases where the collation URI is resolved
+ * at compile time; in cases where the collation URI is not resolved until execution
+ * time (typically because it is supplied as an expression rather than as a string literal)
+ * this parameter is currently set to null.
+ * @param config The configuration. Provided in case the collation URI resolver
+ * needs it.
+ * @return a StringCollator, representing the collation to be used. Note that although
+ * any StringCollator may be returned, functions such as contains() that need to break
+ * a string into its collation units will work only if the returned StringCollator
+ * is a {@link net.sf.saxon.lib.SubstringMatcher}.
+ * <p>If the Collation URI cannot be resolved, return null.
+ * Note that unlike the JAXP URIResolver, returning null does not cause the default
+ * CollationURIResolver to be invoked; if this is required, the user-written CollationURIResolver
+ * should explicitly instantiate and invoke the {@link StandardCollationURIResolver} before
+ * returning null.</p>
+ * @since 8.5/8.9 (this interface was introduced provisionally in 8.5, and modified in 8.9 to return
+ * a StringCollator rather than a Comparator).
+ */
+
+ public StringCollator resolve(String relativeURI, String baseURI, Configuration config);
+}
+
diff --git a/sf/saxon/lib/CollectionURIResolver.java b/sf/saxon/lib/CollectionURIResolver.java
new file mode 100644
index 0000000..fd0745d
--- /dev/null
+++ b/sf/saxon/lib/CollectionURIResolver.java
@@ -0,0 +1,61 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Serializable;
+
+
+/**
+ * This interface defines a CollectionURIResolver. This is a counterpart to the JAXP
+ * URIResolver, but is used to map the URI of collection into a sequence of documents.
+ * It is used to support the fn:collection() and fn:uri-collection() functions.
+ */
+
+public interface CollectionURIResolver extends Serializable {
+
+ /**
+ * Resolve a URI.
+ *
+ * @param href The relative URI of the collection. This corresponds to the
+ * argument supplied to the collection() function. If the collection() function
+ * was called with no arguments (to get the "default collection") this argument
+ * will be null.
+ * @param base The base URI that should be used. This is the base URI of the
+ * static context in which the call to collection() was made, typically the URI
+ * of the stylesheet or query module
+ * @param context The dynamic execution context
+ * @return an Iterator over the documents in the collection. The items returned
+ * by this iterator must be instances either of xs:anyURI, or of node() (specifically,
+ * {@link net.sf.saxon.om.NodeInfo}).
+ * <p>When the fn:uri-collection() function is called: the result will consist of
+ * (a) any items that are xs:anyURI values, and (b) the document URIs of any items that are
+ * document nodes with a document URI.</p>
+ * <p>When the fn:collection() function is called: if xs:anyURI values are returned, the corresponding
+ * document will be retrieved as if by a call to the fn:doc() function: this means that
+ * the system first checks to see if the document is already loaded, and if not, calls
+ * the registered URIResolver to dereference the URI. This is the recommended approach
+ * to ensure that the resulting collection is stable: however, it has the consequence
+ * that the documents will by default remain in memory for the duration of the query
+ * or transformation.</p>
+ * <p>
+ * If the collection URI is not recognized, the method may either return an empty iterator,
+ * in which case no error is reported, or it may throw an exception, in which case
+ * the query or transformation fails. Returning null has the same effect as returning
+ * an empty iterator.</p>
+ * @throws net.sf.saxon.trans.XPathException
+ * if any failure occurs
+ */
+
+ public SequenceIterator resolve(String href, String base, XPathContext context) throws XPathException;
+
+}
+
diff --git a/sf/saxon/lib/ConversionRules.java b/sf/saxon/lib/ConversionRules.java
new file mode 100644
index 0000000..f358618
--- /dev/null
+++ b/sf/saxon/lib/ConversionRules.java
@@ -0,0 +1,642 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.expr.sort.LRUCache;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.NotationSet;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.type.*;
+
+import java.io.Serializable;
+
+/**
+ * This class defines a set of rules for converting between different atomic types. It handles the variations
+ * that arise between different versions of the W3C specifications, for example the changes in Name syntax
+ * between XML 1.0 and XML 1.1, the introduction of "+INF" as a permitted xs:double value in XSD 1.1, and so on.
+ *
+ * <p>It is possible to nominate a customized <code>ConversionRules</code> object at the level of the
+ * {@link net.sf.saxon.Configuration}, either by instantiating this class and changing the properties, or
+ * by subclassing.</p>
+ *
+ * @since 9.3
+ *
+ * @see net.sf.saxon.Configuration#setConversionRules(ConversionRules)
+ */
+
+public class ConversionRules implements Serializable {
+
+ private NameChecker nameChecker;
+ private StringToDouble stringToDouble;
+ private NotationSet notationSet; // may be null
+ private URIChecker uriChecker;
+ private boolean allowYearZero;
+
+ // These two tables need to be synchronised to make the caching thread-safe
+ private LRUCache<Integer, Converter> converterCache =
+ new LRUCache<Integer, Converter>(100, true);
+ private LRUCache<Integer, StringConverter> stringConverterCache =
+ new LRUCache<Integer, StringConverter>(100, true);
+
+
+ public ConversionRules() {
+
+ }
+
+ /**
+ * Create a copy of these conversion rules.
+ * @return a copy of the rules. The cache of converters is NOT copied (because changes to the conversion rules would
+ * invalidate the cache)
+ */
+
+ public ConversionRules copy() {
+ ConversionRules cr = new ConversionRules();
+ copyTo(cr);
+ return cr;
+ }
+
+ /**
+ * Create a copy of these conversion rules.
+ * @param cr a ConversionRules object which will be updated to hold a copy of the rules.
+ * The cache of converters is NOT copied (because changes to the conversion rules would
+ * invalidate the cache)
+ */
+
+ public void copyTo(ConversionRules cr) {
+ cr.nameChecker = nameChecker;
+ cr.stringToDouble = stringToDouble;
+ cr.notationSet = notationSet;
+ cr.uriChecker = uriChecker;
+ cr.allowYearZero = allowYearZero;
+ cr.converterCache.clear();
+ cr.stringConverterCache.clear();
+ }
+
+ /**
+ * Set the class that will be used to check whether XML names are valid.
+ * @param checker the class to be used for checking names. There are variants of this
+ * class depending on which version/edition of the XML specification is in use.
+ */
+
+ public void setNameChecker(NameChecker checker) {
+ this.nameChecker = checker;
+ }
+
+ /**
+ * Get the class that will be used to check whether XML names are valid.
+ * @return the class to be used for checking names. There are variants of this
+ * class depending on which version/edition of the XML specification is in use.
+ */
+
+ public NameChecker getNameChecker() {
+ return nameChecker;
+ }
+
+
+
+ /**
+ * Set the converter that will be used for converting strings to doubles and floats.
+ * @param converter the converter to be used. There are two converters in regular use:
+ * they differ only in whether the lexical value "+INF" is recognized as a representation of
+ * positive infinity.
+ */
+
+ public void setStringToDoubleConverter(StringToDouble converter) {
+ this.stringToDouble = converter;
+ }
+
+ /**
+ * Get the converter that will be used for converting strings to doubles and floats.
+ * @return the converter to be used. There are two converters in regular use:
+ * they differ only in whether the lexical value "+INF" is recognized as a representation of
+ * positive infinity.
+ */
+
+ public StringToDouble getStringToDoubleConverter() {
+ return stringToDouble;
+ }
+
+ /**
+ * Specify the set of notations that are accepted by xs:NOTATION and its subclasses. This is to
+ * support the rule that for a notation to be valid, it must be declared in an xs:notation declaration
+ * in the schema
+ * @param notations the set of notations that are recognized; or null, to indicate that all notation
+ * names are accepted
+ */
+
+ public void setNotationSet(/*@Nullable*/ NotationSet notations) {
+ this.notationSet = notations;
+ }
+
+ /**
+ * Ask whether a given notation is accepted by xs:NOTATION and its subclasses. This is to
+ * support the rule that for a notation to be valid, it must be declared in an xs:notation declaration
+ * in the schema
+ * @param uri the namespace URI of the notation
+ * @param local the local part of the name of the notation
+ * @return true if the notation is in the set of recognized notation names
+ */
+
+
+ public boolean isDeclaredNotation(String uri, String local) {
+ //noinspection SimplifiableIfStatement
+ if (notationSet == null) {
+ return true; // in the absence of a known configuration, treat all notations as valid
+ } else {
+ return notationSet.isDeclaredNotation(uri, local);
+ }
+ }
+
+ /**
+ * Set the class to be used for checking URI values. By default, no checking takes place.
+ * @param checker an object to be used for checking URIs; or null if any string is accepted as an anyURI value
+ */
+
+ public void setURIChecker(URIChecker checker) {
+ this.uriChecker = checker;
+ }
+
+ /**
+ * Ask whether a string is a valid instance of xs:anyURI according to the rules
+ * defined by the current URIChecker
+ * @param string the string to be checked against the rules for URIs
+ * @return true if the string represents a valid xs:anyURI value
+ */
+
+ public boolean isValidURI(CharSequence string) {
+ return uriChecker == null || uriChecker.isValidURI(string);
+ }
+
+ /**
+ * Say whether year zero is permitted in dates. By default it is not permitted when XSD 1.0 is in use,
+ * but it is permitted when XSD 1.1 is used.
+ * @param allowed true if year zero is permitted
+ */
+
+ public void setAllowYearZero(boolean allowed) {
+ allowYearZero = allowed;
+ }
+
+ /**
+ * Ask whether year zero is permitted in dates. By default it is not permitted when XSD 1.0 is in use,
+ * but it is permitted when XSD 1.1 is used.
+ * @return true if year zero is permitted
+ */
+
+ public boolean isAllowYearZero() {
+ return allowYearZero;
+ }
+
+ /**
+ * Get a Converter for a given pair of atomic types. These can be primitive types,
+ * derived types, or user-defined types. The converter implements the casting rules.
+ * @param source the source type
+ * @param target the target type
+ * @return a Converter if conversion between the two types is possible; or null otherwise
+ */
+
+ /*@Nullable*/ public Converter getConverter(AtomicType source, AtomicType target) {
+ // For a lookup key, use the primitive type of the source type (always 10 bits) and the
+ // fingerprint of the target type (20 bits)
+ int key = (source.getPrimitiveType() << 20) | target.getFingerprint();
+ Converter converter = converterCache.get(key);
+ if (converter == null) {
+ converter = makeConverter(source, target);
+ if (converter != null) {
+ converterCache.put(key, converter);
+ } else {
+ return null;
+ }
+ }
+ return converter;
+ }
+
+ /**
+ * Create a converter that handles conversion from one primitive type to another.
+ * <p/>
+ * <p>This method is intended for internal use only. The approved way to get a converter is using the
+ * factory method {@link net.sf.saxon.lib.ConversionRules#getConverter(net.sf.saxon.type.AtomicType, net.sf.saxon.type.AtomicType)}}</p>
+ *
+ * @param sourceType the type of the value to be converted
+ * @param targetType the type of the result of the conversion
+ * @return the converter if one is available; or null if no conversion is possible
+ */
+
+ /*@Nullable*/ private Converter makeConverter(/*@NotNull*/ AtomicType sourceType, /*@NotNull*/ AtomicType targetType) {
+ if (sourceType == targetType) {
+ return StringConverter.IdentityConverter.THE_INSTANCE;
+ }
+
+ int tt = targetType.getFingerprint();
+ int tp = targetType.getPrimitiveType();
+ int st = sourceType.getPrimitiveType();
+
+ if ((st == StandardNames.XS_STRING || st == StandardNames.XS_UNTYPED_ATOMIC) &&
+ (tp == StandardNames.XS_STRING || tp == StandardNames.XS_UNTYPED_ATOMIC)) {
+ return makeStringConverter(targetType);
+ }
+
+ if (!(targetType.isPrimitiveType())) {
+ AtomicType primTarget = (AtomicType) targetType.getPrimitiveItemType();
+ if (sourceType == primTarget) {
+ return new Converter.DownCastingConverter(targetType, this);
+ } else if (st == StandardNames.XS_STRING || st == StandardNames.XS_UNTYPED_ATOMIC) {
+ return makeStringConverter(targetType);
+ } else {
+ Converter stageOne = makeConverter(sourceType, primTarget);
+ if (stageOne == null) {
+ return null;
+ }
+ Converter stageTwo = new Converter.DownCastingConverter(targetType, this);
+ return new Converter.TwoPhaseConverter(stageOne, stageTwo);
+ }
+ }
+
+
+ if (st == tt) {
+ // we are casting between subtypes of the same primitive type.
+ // TODO: (optimization) check whether the targetType is a supertype of the source type, in which case
+ // it's a simple upcast. (Unfortunately, we don't have the TypeHierarchy available)
+ Converter upcast = new Converter.UpCastingConverter((AtomicType) sourceType.getPrimitiveItemType());
+ Converter downcast = new Converter.DownCastingConverter(targetType, this);
+ return new Converter.TwoPhaseConverter(upcast, downcast);
+ }
+
+ switch (tt) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ return Converter.TO_UNTYPED_ATOMIC;
+ case StandardNames.XS_STRING:
+ return Converter.TO_STRING;
+ case StandardNames.XS_FLOAT:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToFloat(this);
+ case StandardNames.XS_DOUBLE:
+ case StandardNames.XS_DECIMAL:
+ //case StandardNames.XS_PRECISION_DECIMAL:
+ case StandardNames.XS_INTEGER:
+ case StandardNames.XS_NUMERIC:
+ return Converter.NUMERIC_TO_FLOAT;
+ case StandardNames.XS_BOOLEAN:
+ return Converter.BOOLEAN_TO_FLOAT;
+ default:
+ return null;
+ }
+ case StandardNames.XS_DOUBLE:
+ case StandardNames.XS_NUMERIC:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToDouble(this);
+ case StandardNames.XS_FLOAT:
+ case StandardNames.XS_DECIMAL:
+ //case StandardNames.XS_PRECISION_DECIMAL:
+ case StandardNames.XS_INTEGER:
+ case StandardNames.XS_NUMERIC:
+ return Converter.NUMERIC_TO_DOUBLE;
+ case StandardNames.XS_BOOLEAN:
+ return Converter.BOOLEAN_TO_DOUBLE;
+ default:
+ return null;
+ }
+ case StandardNames.XS_DECIMAL:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_DECIMAL;
+ case StandardNames.XS_FLOAT:
+ return Converter.FLOAT_TO_DECIMAL;
+ case StandardNames.XS_DOUBLE:
+ return Converter.DOUBLE_TO_DECIMAL;
+// case StandardNames.XS_PRECISION_DECIMAL:
+// return PRECISION_DECIMAL_TO_DECIMAL;
+ case StandardNames.XS_INTEGER:
+ return Converter.INTEGER_TO_DECIMAL;
+ case StandardNames.XS_NUMERIC:
+ return Converter.NUMERIC_TO_DECIMAL;
+ case StandardNames.XS_BOOLEAN:
+ return Converter.BOOLEAN_TO_DECIMAL;
+ default:
+ return null;
+ }
+
+ case StandardNames.XS_INTEGER:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_INTEGER;
+ case StandardNames.XS_FLOAT:
+ return Converter.FLOAT_TO_INTEGER;
+ case StandardNames.XS_DOUBLE:
+ return Converter.DOUBLE_TO_INTEGER;
+ case StandardNames.XS_DECIMAL:
+ //case StandardNames.XS_PRECISION_DECIMAL:
+ return Converter.DECIMAL_TO_INTEGER;
+ case StandardNames.XS_NUMERIC:
+ return Converter.NUMERIC_TO_INTEGER;
+ case StandardNames.XS_BOOLEAN:
+ return Converter.BOOLEAN_TO_INTEGER;
+ default:
+ return null;
+ }
+ case StandardNames.XS_DURATION:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_DURATION;
+ case StandardNames.XS_DAY_TIME_DURATION:
+ case StandardNames.XS_YEAR_MONTH_DURATION:
+ return new Converter.UpCastingConverter(BuiltInAtomicType.DURATION);
+ default:
+ return null;
+ }
+ case StandardNames.XS_YEAR_MONTH_DURATION:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_YEAR_MONTH_DURATION;
+ case StandardNames.XS_DURATION:
+ case StandardNames.XS_DAY_TIME_DURATION:
+ return Converter.DURATION_TO_YEAR_MONTH_DURATION;
+ default:
+ return null;
+ }
+ case StandardNames.XS_DAY_TIME_DURATION:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_DAY_TIME_DURATION;
+ case StandardNames.XS_DURATION:
+ case StandardNames.XS_YEAR_MONTH_DURATION:
+ return Converter.DURATION_TO_DAY_TIME_DURATION;
+ default:
+ return null;
+ }
+ case StandardNames.XS_DATE_TIME:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToDateTime(this);
+ case StandardNames.XS_DATE:
+ return Converter.DATE_TO_DATE_TIME;
+ default:
+ return null;
+ }
+ case StandardNames.XS_TIME:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_TIME;
+ case StandardNames.XS_DATE_TIME:
+ return Converter.DATE_TIME_TO_TIME;
+ default:
+ return null;
+ }
+ case StandardNames.XS_DATE:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToDate(this);
+ case StandardNames.XS_DATE_TIME:
+ return Converter.DATE_TIME_TO_DATE;
+ default:
+ return null;
+ }
+ case StandardNames.XS_G_YEAR_MONTH:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToGYearMonth(this);
+ case StandardNames.XS_DATE:
+ return Converter.TwoPhaseConverter.makeTwoPhaseConverter(
+ BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME, BuiltInAtomicType.G_YEAR_MONTH, this);
+ case StandardNames.XS_DATE_TIME:
+ return Converter.DATE_TIME_TO_G_YEAR_MONTH;
+ default:
+ return null;
+ }
+ case StandardNames.XS_G_YEAR:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToGYear(this);
+ case StandardNames.XS_DATE:
+ return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME,
+ BuiltInAtomicType.G_YEAR, this);
+ case StandardNames.XS_DATE_TIME:
+ return Converter.DATE_TIME_TO_G_YEAR;
+ default:
+ return null;
+ }
+ case StandardNames.XS_G_MONTH_DAY:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToGMonthDay(this);
+ case StandardNames.XS_DATE:
+ return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME,
+ BuiltInAtomicType.G_MONTH_DAY, this);
+ case StandardNames.XS_DATE_TIME:
+ return Converter.DATE_TIME_TO_G_MONTH_DAY;
+ default:
+ return null;
+ }
+ case StandardNames.XS_G_DAY:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToGDayConverter(this);
+ case StandardNames.XS_DATE:
+ return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME,
+ BuiltInAtomicType.G_DAY, this);
+ case StandardNames.XS_DATE_TIME:
+ return Converter.DATE_TIME_TO_G_DAY;
+ default:
+ return null;
+ }
+ case StandardNames.XS_G_MONTH:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToGMonth(this);
+ case StandardNames.XS_DATE:
+ return Converter.TwoPhaseConverter.makeTwoPhaseConverter(BuiltInAtomicType.DATE, BuiltInAtomicType.DATE_TIME,
+ BuiltInAtomicType.G_MONTH, this);
+ case StandardNames.XS_DATE_TIME:
+ return Converter.DATE_TIME_TO_G_MONTH;
+ default:
+ return null;
+ }
+ case StandardNames.XS_BOOLEAN:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_BOOLEAN;
+ case StandardNames.XS_FLOAT:
+ case StandardNames.XS_DOUBLE:
+ case StandardNames.XS_DECIMAL:
+ // case StandardNames.XS_PRECISION_DECIMAL:
+ case StandardNames.XS_INTEGER:
+ case StandardNames.XS_NUMERIC:
+ return Converter.NUMERIC_TO_BOOLEAN;
+ default:
+ return null;
+ }
+ case StandardNames.XS_BASE64_BINARY:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_BASE64_BINARY;
+ case StandardNames.XS_HEX_BINARY:
+ return Converter.HEX_BINARY_TO_BASE64_BINARY;
+ default:
+ return null;
+ }
+ case StandardNames.XS_HEX_BINARY:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_HEX_BINARY;
+ case StandardNames.XS_BASE64_BINARY:
+ return Converter.BASE64_BINARY_TO_HEX_BINARY;
+ default:
+ return null;
+ }
+ case StandardNames.XS_ANY_URI:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToAnyURI(this);
+ default:
+ return null;
+ }
+ case StandardNames.XS_QNAME:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToQName(this);
+ case StandardNames.XS_NOTATION:
+ return Converter.NOTATION_TO_QNAME;
+ default:
+ return null;
+ }
+ case StandardNames.XS_NOTATION:
+ switch (st) {
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_STRING:
+ return new StringConverter.StringToNotation(this);
+ case StandardNames.XS_QNAME:
+ return Converter.QNAME_TO_NOTATION;
+ default:
+ return null;
+ }
+
+ case StandardNames.XS_ANY_ATOMIC_TYPE:
+ return Converter.IDENTITY_CONVERTER;
+
+ default:
+ throw new IllegalArgumentException("Unknown primitive type " + tt);
+ }
+ }
+
+ /**
+ * Get a Converter that converts from strings to a given atomic type. These can be primitive types,
+ * derived types, or user-defined types. The converter implements the casting rules.
+ * @param target the target type
+ * @return a Converter if conversion between the two types is possible; or null otherwise
+ */
+
+ public StringConverter getStringConverter(AtomicType target) {
+ int key = target.getFingerprint();
+ StringConverter converter = stringConverterCache.get(key);
+ if (converter == null) {
+ converter = makeStringConverter(target);
+ stringConverterCache.put(key, converter);
+ }
+ return converter;
+ }
+
+ /**
+ * Static factory method to get a StringConverter for a specific target type
+ * @param targetType the target type of the conversion
+ * @return a StringConverter that can be used to convert strings to the target type, or to
+ * validate strings against the target type
+ */
+
+ /*@NotNull*/ private StringConverter makeStringConverter(/*@NotNull*/ final AtomicType targetType) {
+
+ int tt = targetType.getPrimitiveType();
+ if (targetType.isBuiltInType()) {
+ if (tt == StandardNames.XS_STRING) {
+ switch (targetType.getFingerprint()) {
+ case StandardNames.XS_STRING:
+ return StringConverter.STRING_TO_STRING;
+ case StandardNames.XS_NORMALIZED_STRING:
+ return StringConverter.STRING_TO_NORMALIZED_STRING;
+ case StandardNames.XS_TOKEN:
+ return StringConverter.STRING_TO_TOKEN;
+ case StandardNames.XS_LANGUAGE:
+ return StringConverter.STRING_TO_LANGUAGE;
+ case StandardNames.XS_NAME:
+ return new StringConverter.StringToName(this);
+ case StandardNames.XS_NCNAME:
+ case StandardNames.XS_ID:
+ case StandardNames.XS_IDREF:
+ case StandardNames.XS_ENTITY:
+ return new StringConverter.StringToNCName(this, targetType);
+ case StandardNames.XS_NMTOKEN:
+ return new StringConverter.StringToNMTOKEN(this);
+ default:
+ throw new AssertionError("Unknown built-in subtype of xs:string");
+
+ }
+ } else if (tt == StandardNames.XS_UNTYPED_ATOMIC) {
+ return StringConverter.STRING_TO_UNTYPED_ATOMIC;
+ } else if (targetType.isPrimitiveType()) {
+ // converter to built-in types unrelated to xs:string
+ Converter converter = getConverter(BuiltInAtomicType.STRING, targetType);
+ assert converter != null;
+ return (StringConverter)converter;
+ } else if (tt == StandardNames.XS_INTEGER) {
+ return new StringConverter.StringToIntegerSubtype((BuiltInAtomicType)targetType);
+ } else {
+ switch (targetType.getFingerprint()) {
+ case StandardNames.XS_DAY_TIME_DURATION:
+ return StringConverter.STRING_TO_DAY_TIME_DURATION;
+ case StandardNames.XS_YEAR_MONTH_DURATION:
+ return StringConverter.STRING_TO_YEAR_MONTH_DURATION;
+ case StandardNames.XS_DATE_TIME_STAMP:
+ StringConverter first = new StringConverter.StringToDateTime(this);
+ Converter.DownCastingConverter second = new Converter.DownCastingConverter(targetType, this);
+ return new StringConverter.StringToNonStringDerivedType(first, second);
+ default:
+ throw new AssertionError("Unknown built in type " + targetType.toString());
+ }
+ }
+ } else {
+ if (tt == StandardNames.XS_STRING) {
+ if (targetType.getBuiltInBaseType() == BuiltInAtomicType.STRING) {
+ // converter to user-defined subtypes of xs:string
+ return new StringConverter.StringToStringSubtype(this, targetType);
+ } else {
+ // converter to user-defined subtypes of built-in subtypes of xs:string
+ return new StringConverter.StringToDerivedStringSubtype(this, targetType);
+ }
+ } else {
+ // converter to user-defined types derived from types other than xs:string
+ StringConverter first = getStringConverter((AtomicType) targetType.getPrimitiveItemType());
+ Converter.DownCastingConverter second = new Converter.DownCastingConverter(targetType, this);
+ return new StringConverter.StringToNonStringDerivedType(first, second);
+ }
+ }
+
+ }
+
+}
+
diff --git a/sf/saxon/lib/EnvironmentVariableResolver.java b/sf/saxon/lib/EnvironmentVariableResolver.java
new file mode 100644
index 0000000..2c3a93b
--- /dev/null
+++ b/sf/saxon/lib/EnvironmentVariableResolver.java
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import java.util.Set;
+
+/**
+ * This interface defines a Saxon plug-in used to resolve calls on the XPath 3.0
+ * functions available-environment-variables() and environment-variable(). The standard
+ * implementation reads environment variables using the Java method {@link System#getenv()};
+ * this can be overridden by a user-provided implementation that resolves environment variables
+ * any way it likes.
+ */
+public interface EnvironmentVariableResolver {
+
+ /**
+ * Get the list of available environment variables.
+ * @return a set of strings; each such string should be an acceptable argument to the
+ * method {@link #getEnvironmentVariable(String)}
+ */
+
+ public Set<String> getAvailableEnvironmentVariables();
+
+ /**
+ * Get the value of a specific environment variable
+ * @param name the name of the required environment variable
+ * @return the value of the named environment variable, or null if the variable is
+ * not defined. The method must not return null if the name is in the list of variables
+ * returned by the method {@link #getAvailableEnvironmentVariables()}
+ */
+
+ public String getEnvironmentVariable(String name);
+}
+
diff --git a/sf/saxon/lib/ExtensionFunctionCall.java b/sf/saxon/lib/ExtensionFunctionCall.java
new file mode 100644
index 0000000..5548ab9
--- /dev/null
+++ b/sf/saxon/lib/ExtensionFunctionCall.java
@@ -0,0 +1,225 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Serializable;
+
+/**
+ * This abstract class is provided to allow user-written extension functions to be implemented
+ * with the full capabilities of functions that are an intrinsic part of the Saxon product.
+ * In particular, the class has the opportunity to save data from the static context and
+ * to optimize itself at compile time.
+ *
+ * <p>Instances of this class are created by calling the method makeCallExpression() on the
+ * {@link ExtensionFunctionDefinition} object that represents the definition of the function.</p>
+ *
+ * <p>The compiler will create one instance of this class for each function call appearing in the
+ * expression being compiled. The class must therefore have a public zero-argument constructor.</p>
+ *
+ * <p>The compiler will ensure that the supplied arguments in the extension function call are converted
+ * if necessary to the declared argument types, by applying the standard conversion rules. The result
+ * returned by the function is checked against the declared return type, but no conversion takes place:
+ * the returned value must strictly conform to the declared type.</p>
+ *
+ * <p>Note that an IntegratedFunction is trusted; calls are allowed even if the configuration option
+ * {@link net.sf.saxon.lib.FeatureKeys#ALLOW_EXTERNAL_FUNCTIONS} is false. In cases where an IntegratedFunction
+ * is used to load and execute untrusted code, it should check this configuration option before doing so.</p>
+ *
+ * @since 9.2
+ */
+public abstract class ExtensionFunctionCall implements Serializable, Callable {
+
+ ExtensionFunctionDefinition definition;
+ Container container;
+
+ /**
+ * This method is called by the system to provide information about the extension function call.
+ * It should not be called by the implementor of the extension function.
+ * @param definition the extension function definition
+ * @param container the container of the extension function call, providing access to location information
+ */
+
+ public final void setDefinition(ExtensionFunctionDefinition definition, Container container) {
+ this.definition = definition;
+ this.container = container;
+ }
+
+ /**
+ * Get the definition of this extension function
+ * @return the function definition from which this ExtensionFunctionCall was created
+ */
+
+ public final ExtensionFunctionDefinition getDefinition() {
+ return definition;
+ }
+
+ /**
+ * Get the Executable in which this extension function call occurs
+ * @return the containing executable
+ * @since 9.3
+ */
+
+ /*@Nullable*/ public final Executable getExecutable() {
+ return container.getExecutable();
+ }
+
+ /**
+ * Get the container of the expression. This provides information about the location
+ * of the expression (potentially useful for diagnostics). Any new expression that is created
+ * while rewriting this expression should have the same container assigned.
+ * @return the container of the extension function call expression
+ * @since 9.3
+ */
+
+ public final Container getContainer() {
+ return container;
+ }
+
+ /**
+ * Supply static context information.
+ * <p>This method is called during compilation to provide information about the static context in which
+ * the function call appears. If the implementation of the function needs information from the static context,
+ * then it should save it now, as it will not be available later at run-time.</p>
+ * <p>The implementation also has the opportunity to examine the expressions that appear in the
+ * arguments to the function call at this stage. These might already have been modified from the original
+ * expressions as written by the user. The implementation must not modify any of these expressions.</p>
+ * <p>The default implementation of this method does nothing.</p>
+ * @param context The static context in which the function call appears. The method must not modify
+ * the static context.
+ * @param locationId An integer code representing the location of the call to the extension function
+ * in the stylesheet; can be used in conjunction with the locationMap held in the static context for diagnostics
+ * @param arguments The XPath expressions supplied in the call to this function. The method must not
+ * modify this array, or any of the expressions contained in the array.
+ * @throws XPathException if the implementation is able to detect a static error in the way the
+ * function is being called (for example it might require that the types of the arguments are
+ * consistent with each other).
+ */
+
+ public void supplyStaticContext(StaticContext context, int locationId, Expression[] arguments) throws XPathException {
+ // default implementation does nothing
+ }
+
+ /**
+ * Rewrite the function call. This method is called at compile time. It gives the implementation
+ * an opportunity to replace itself with an optimized implementation that returns the same result.
+ * This includes the ability to pre-evaluate the function and return its result as a literal value.
+ *
+ * <p>There is also a further opportunity to perform static checking at this stage and to throw an error
+ * if the arguments are invalid.</p>
+ *
+ * @param context The static context in which the function call appears. The method must not modify
+ * the static context.
+ * @param arguments The XPath expressions supplied in the call to this function. This method is called
+ * after type-checking, so these expressions may have been modified by adding atomization operators
+ * or type-checking operations, for example.
+ * @return an expression to be evaluated at run-time in place of this function call. Return null
+ * if no rewriting is required and the function call should be used unchanged. Return a
+ * {@link net.sf.saxon.expr.Literal} representing the result of the function call if the function call
+ * can be precomputed at compile time
+ * @throws XPathException if the implementation is able to detect a static error in the way the
+ * function is being called (for example it might require that the types of the arguments are
+ * consistent with each other).
+ */
+
+ /*@Nullable*/ public Expression rewrite(StaticContext context, Expression[] arguments) throws XPathException {
+ // default implementation does nothing
+ return null;
+ }
+
+ /**
+ * Copy local data from one copy of the function to another. This method must be implemented
+ * in any implementation that maintains local data retained from the static context; the job of the
+ * method is to copy this local data to the supplied destination function.
+ *
+ * <p>This method is called if a call to the extension function needs to be copied during
+ * the process of optimization. For example, this occurs if the function containing the call
+ * to the extension function is inlined.</p>
+ *
+ * <p>If any objects held as local data for the function call are mutable then deep copies must
+ * be made.</p>
+ *
+ * @param destination the function to which the local data must be copied. This will always
+ * be an instance of the same function class as the source function.
+ */
+
+ public void copyLocalData(ExtensionFunctionCall destination) {
+ // default implementation does nothing
+ }
+
+ /**
+ * Evaluate this function call at run-time
+ *
+ *
+ *
+ * @param context The XPath dynamic evaluation context
+ *
+ * @param arguments The values of the arguments to the function call. Each argument value (which is in general
+ * a sequence) is supplied in the form of an iterator over the items in the sequence. Any required conversions
+ * to the declared types of the arguments will already have been performed.
+ *
+ * <p>If required, the supplied sequence can be materialized by calling, for example,
+ * <code>new SequenceExtent(arguments[i])</code>.
+ * If the argument is always a singleton, then the single item may be obtained by calling
+ * <code>arguments[i].next()</code>.</p>
+ *
+ * <p>The implementation is not obliged to read all the items in each <code>SequenceIterator</code>
+ * if they are not required to compute the result; but if any <code>SequenceIterator</code> is not read
+ * to completion, it is good practice to call its <code>close()</code> method.</p>
+ *
+ * @return an iterator over the results of the function.
+ *
+ * <p>The implementation is responsible for ensuring that the result is a valid instance of the declared
+ * result type. Saxon will check that this is the case if the {@link net.sf.saxon.lib.ExtensionFunctionDefinition#trustResultType()}
+ * method returns false, but it will never convert the supplied result value to the declared result type.</p>
+ *
+ * <p>If the result is a single item, it can be returned in the form of a {@link net.sf.saxon.tree.iter.SingletonIterator}.
+ * If the result is an empty sequence, the method should return {@link net.sf.saxon.tree.iter.EmptyIterator#getInstance()}</p>
+ *
+ * @throws XPathException if a dynamic error occurs during evaluation of the function. The Saxon run-time
+ * code will add information about the error location.
+ */
+
+ public abstract Sequence call(XPathContext context, Sequence[] arguments) throws XPathException;
+
+ /**
+ * Compute the effective boolean value of the result.
+ *
+ * <p>Implementations can override this method but are not required to do so. If it is overridden,
+ * the result must be consistent with the rules for calculating effective boolean value. The method
+ * should be implemented in cases where computing the effective boolean value is significantly cheaper
+ * than computing the full result.</p>
+ *
+ * @param context The XPath dynamic evaluation context
+ * @param arguments The values of the arguments to the function call. Each argument value (which is in general
+ * a sequence) is supplied in the form of an iterator over the items in the sequence. If required, the
+ * supplied sequence can be materialized by calling, for example, <code>new SequenceExtent(arguments[i])</code>.
+ * If the argument is always a singleton, then the single item may be obtained by calling
+ * <code>arguments[i].next()</code>. The implementation is not obliged to read all the items in each
+ * <code>SequenceIterator</code> if they are not required to compute the result; but if any SequenceIterator is not read
+ * to completion, it is good practice to call its close() method.
+ * @return the effective boolean value of the result
+ * @throws XPathException if a dynamic error occurs during evaluation of the function. The Saxon run-time
+ * code will add information about the error location.
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context, Sequence[] arguments) throws XPathException {
+ return ExpressionTool.effectiveBooleanValue(call(context, arguments).iterate());
+ }
+
+// public boolean effectiveBooleanValue(SequenceIterator[] arguments, XPathContext context) throws XPathException {
+// return ExpressionTool.effectiveBooleanValue(call(context, arguments).iterate());
+// }
+
+}
+
diff --git a/sf/saxon/lib/ExtensionFunctionDefinition.java b/sf/saxon/lib/ExtensionFunctionDefinition.java
new file mode 100644
index 0000000..12ee276
--- /dev/null
+++ b/sf/saxon/lib/ExtensionFunctionDefinition.java
@@ -0,0 +1,138 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.value.SequenceType;
+
+import java.io.Serializable;
+
+/**
+ * This abstract class is provided to allow user-written extension functions to be implemented
+ * with the full capabilities of functions that are an intrinsic part of the Saxon product.
+ * In particular, the class has the opportunity to save data from the static context and
+ * to optimize itself at compile time.
+ *
+ * <p>There should be one class implementing this interface for each function name; if there
+ * are several functions with the same name but different arity, the same class should implement
+ * them all.</p>
+ *
+ * <p>Note that an IntegratedFunction is trusted; calls are allowed even if the configuration option
+ * {@link net.sf.saxon.lib.FeatureKeys#ALLOW_EXTERNAL_FUNCTIONS} is false. In cases where an IntegratedFunction
+ * is used to load and execute untrusted code, it should check this configuration option before doing so.</p>
+ *
+ * @since 9.2
+ */
+public abstract class ExtensionFunctionDefinition implements Serializable {
+
+ /**
+ * Get the name of the function, as a QName.
+ * <p>This method must be implemented in all subclasses</p>
+ * @return the function name
+ */
+
+ public abstract StructuredQName getFunctionQName();
+
+ /**
+ * Get the minimum number of arguments required by the function
+ * <p>The default implementation returns the number of items in the result of calling
+ * {@link #getArgumentTypes}</p>
+ * @return the minimum number of arguments that must be supplied in a call to this function
+ */
+
+ public int getMinimumNumberOfArguments() {
+ return getArgumentTypes().length;
+ }
+
+ /**
+ * Get the maximum number of arguments allowed by the function.
+ * <p>The default implementation returns the value of {@link #getMinimumNumberOfArguments}
+ * @return the maximum number of arguments that may be supplied in a call to this function
+ */
+
+ public int getMaximumNumberOfArguments() {
+ return getMinimumNumberOfArguments();
+ }
+
+ /**
+ * Get the required types for the arguments of this function.
+ * <p>This method must be implemented in all subtypes.</p>
+ * @return the required types of the arguments, as defined by the function signature. Normally
+ * this should be an array of size {@link #getMaximumNumberOfArguments()}; however for functions
+ * that allow a variable number of arguments, the array can be smaller than this, with the last
+ * entry in the array providing the required type for all the remaining arguments.
+ */
+
+ public abstract SequenceType[] getArgumentTypes();
+
+ /**
+ * Get the type of the result of the function
+ * <p>This method must be implemented in all subtypes.</p>
+ * @return the return type of the function, as defined by its function signature
+ * @param suppliedArgumentTypes the static types of the supplied arguments to the function.
+ * This is provided so that a more precise result type can be returned in the common
+ * case where the type of the result depends on the types of the arguments.
+ */
+
+ public abstract SequenceType getResultType(SequenceType[] suppliedArgumentTypes);
+
+ /**
+ * Ask whether the result actually returned by the function can be trusted,
+ * or whether it should be checked against the declared type.
+ * @return true if the function implementation warrants that the value it returns will
+ * be an instance of the declared result type. The default value is false, in which case
+ * the result will be checked at run-time to ensure that it conforms to the declared type.
+ * If the value true is returned, but the function returns a value of the wrong type, the
+ * consequences are unpredictable.
+ */
+
+ public boolean trustResultType() {
+ return false;
+ }
+
+ /**
+ * Ask whether the result of the function depends on the focus, or on other variable parts
+ * of the context.
+ * @return true if the result of the function depends on the context item, position, or size.
+ * Despite the method name, the method should also return true if the function depends on other
+ * parts of the context that vary from one part of the query/stylesheet to another, for example
+ * the XPath default namespace.
+ * <p>The default implementation returns false.</p>
+ * <p>The method must return true if the function
+ * makes use of any of these values from the dynamic context. Returning true inhibits certain
+ * optimizations, such as moving the function call out of the body of an xsl:for-each loop,
+ * or extracting it into a global variable.</p>
+ */
+
+ public boolean dependsOnFocus() {
+ return false;
+ }
+
+ /**
+ * Ask whether the function has side-effects. If the function does have side-effects, the optimizer
+ * will be less aggressive in moving or removing calls to the function. However, calls on functions
+ * with side-effects can never be guaranteed.
+ * @return true if the function has side-effects (including creation of new nodes, if the
+ * identity of those nodes is significant). The default implementation returns false.
+ */
+
+ public boolean hasSideEffects() {
+ return false;
+ }
+
+ /**
+ * Create a call on this function. This method is called by the compiler when it identifies
+ * a function call that calls this function.
+ * @return an expression representing a call of this extension function
+ */
+
+ /*@Nullable*/ public abstract ExtensionFunctionCall makeCallExpression();
+
+
+}
+
diff --git a/sf/saxon/lib/ExternalObjectModel.java b/sf/saxon/lib/ExternalObjectModel.java
new file mode 100644
index 0000000..7d3563e
--- /dev/null
+++ b/sf/saxon/lib/ExternalObjectModel.java
@@ -0,0 +1,117 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.JPConverter;
+import net.sf.saxon.expr.PJConverter;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import java.io.Serializable;
+
+/**
+ * This interface must be implemented by any third-party object model that can
+ * be wrapped with a wrapper that implements the Saxon Object Model (the NodeInfo interface).
+ *
+ * <p>This interface is designed to enable advanced applications to implement and register
+ * new object model implementations that Saxon can then use without change. Although it is intended
+ * for external use, it cannot at this stage be considered part of the stable Saxon Public API.
+ * In particular, it is likely that the interface will grow by the addition of new methods.</p>
+ *
+ * <p>For maximum integration, an object may extend {@link net.sf.saxon.om.TreeModel} as well as implementing
+ * this interface. To implement <code>TreeModel</code>, it must supply a Builder; in effect this
+ * means that it will be possible to use the external object model for output as well as for
+ * input.</p>
+ */
+
+public interface ExternalObjectModel extends Serializable {
+
+ /**
+ * Get the URI of the external object model as used in the JAXP factory interfaces for obtaining
+ * an XPath implementation
+ * @return the URI used to identify this object model in the JAXP XPath factory mechanism.
+ */
+
+ public String getIdentifyingURI();
+
+ /**
+ * Get a converter from XPath values to values in the external object model
+ * @param targetClass the required class of the result of the conversion. If this class represents
+ * a node or list of nodes in the external object model, the method should return a converter that takes
+ * a native node or sequence of nodes as input and returns a node or sequence of nodes in the
+ * external object model representation. Otherwise, it should return null.
+ * @return a converter, if the targetClass is recognized as belonging to this object model;
+ * otherwise null
+ */
+
+ public PJConverter getPJConverter(Class targetClass);
+
+ /**
+ * Get a converter from values in the external object model to XPath values.
+ *
+ * @param sourceClass the class (static or dynamic) of values to be converted
+ * @param config
+ * @return a converter, if the sourceClass is recognized as belonging to this object model;
+ * otherwise null
+ */
+
+ public JPConverter getJPConverter(Class sourceClass, Configuration config);
+
+ /**
+ * Get a converter that converts a sequence of XPath nodes to this model's representation
+ * of a node list.
+ * <p><i>This method is primarily for the benefit of DOM, which uses its own NodeList
+ * class to represent collections of nodes. Most other object models use standard
+ * Java collection objects such as java.util.List</i></p>
+ * @param node an example of the kind of node used in this model
+ * @return if the model does not recognize this node as one of its own, return null. Otherwise
+ * return a PJConverter that takes a list of XPath nodes (represented as NodeInfo objects) and
+ * returns a collection of nodes in this object model
+ */
+
+ public PJConverter getNodeListCreator(Object node);
+
+ /**
+ * Test whether this object model recognizes a particular kind of JAXP Result object,
+ * and if it does, return a Receiver that builds an instance of this data model from
+ * a sequence of events. If the Result is not recognised, return null.
+ * @param result a JAXP result object
+ * @return a Receiver that writes to that result, if available; or null otherwise
+ * @throws net.sf.saxon.trans.XPathException if any failure occurs
+ */
+
+ public Receiver getDocumentBuilder(Result result) throws XPathException;
+
+ /**
+ * Test whether this object model recognizes a particular kind of JAXP Source object,
+ * and if it does, send the contents of the document to a supplied Receiver, and return true.
+ * Otherwise, return false.
+ * @param source a JAXP Source object
+ * @param receiver the Receiver that is to receive the data from the Source
+ * @return true if the data from the Source has been sent to the Receiver, false otherwise
+ * @throws net.sf.saxon.trans.XPathException if any failure occurs
+ */
+
+ public boolean sendSource(Source source, Receiver receiver) throws XPathException;
+
+ /**
+ * Wrap or unwrap a node using this object model to return the corresponding Saxon node. If the supplied
+ * source does not belong to this object model, return null
+ * @param source a JAXP Source object
+ * @param config the Saxon configuration
+ * @return a NodeInfo corresponding to the Source, if this can be constructed; otherwise null
+ */
+
+ public NodeInfo unravel(Source source, Configuration config);
+
+}
+
diff --git a/sf/saxon/lib/FeatureKeys.java b/sf/saxon/lib/FeatureKeys.java
new file mode 100644
index 0000000..cb95c14
--- /dev/null
+++ b/sf/saxon/lib/FeatureKeys.java
@@ -0,0 +1,1570 @@
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+ * FeatureKeys defines a set of constants, representing the names of Saxon configuration
+ * options which can be supplied to the Saxon implementations of the JAXP
+ * interfaces TransformerFactory, SchemaFactory, Validator, and ValidationHandler,
+ * and to other interfaces such as the s9api {@link net.sf.saxon.s9api.Processor}
+ *
+ * @author Michael H. Kay
+ */
+
+ public abstract class FeatureKeys {
+
+
+/**
+* <p>ALLOW_EXTERNAL_FUNCTIONS determines whether calls to reflexive external functions are
+* allowed. More specifically, if set to <b>false</b> it disallows all of the
+* following:</p>
+*
+* <ul>
+* <li>Calls to reflexive Java extension functions</li>
+* <li>Use of the XSLT system-property() function to access Java system properties</li>
+* <li>Use of a relative URI in the <code>xsl:result-document</code> instruction</li>
+* <li>Calls to XSLT extension instructions</li>
+* </ul>
+*
+* <p>The default value is <b>true</b>. The setting <b>false</b> is recommended in an
+* environment where untrusted stylesheets may be executed.</p>
+*
+* <p>This option does not disable use of the <code>doc()</code> function or similar
+* functions to access the filestore of the machine where the transformation or query
+* is running. That should be done using a user-supplied <code>URIResolver</code></p>
+*
+* <p>Note that integrated extension functions are trusted; calls to such functions are
+* allowed even if this configuration option is false. In cases where an integrated
+* extension function is used to load and execute untrusted code, it should check this
+* configuration option before doing so.</p>
+*
+**/
+
+public final static String ALLOW_EXTERNAL_FUNCTIONS =
+"http://saxon.sf.net/feature/allow-external-functions";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>ALLOW_MULTITHREADING determines whether multi-threading is allowed.</p>
+* <p>If true (the default), the presence of the attribute <code>saxon:threads="N"</code>
+* on an <code>xsl:for-each</code> instruction, when running under Saxon-EE, causes
+* multi-threaded execution. If set to false, the value of the
+* <code>saxon:threads</code> argument is ignored.</p>
+* <p>Setting the value to false also disables asynchronous processing of
+* <code>xsl:result-document</code> instructions.</p>
+* <p>The default value is true if Saxon-EE is in use, false otherwise.</p>
+*
+**/
+
+public final static String ALLOW_MULTITHREADING =
+"http://saxon.sf.net/feature/allow-multithreading";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>ALLOW_OLD_JAVA_URI_FORMAT determines whether extension function calls to
+* dynamically-loaded Java extension functions may use the URI format supported in
+* older Saxon releases.</p>
+* <p>If the value is false (the default), the only URI format accepted is (for example)
+* "java:java.util.Date" - that is, a URI consisting of the string "java:" followed by
+* the Java qualified class name of the class containing the implementation of the
+* extension function.</p>
+* <p>If the value is true, then in addition to this format, the following are accepted:
+* (a) the Java class name on its own ("java.util.Date"), and (b) any URI in which the
+* Java class name follows the last "/" character (for example
+* "http://my.com/extensions/java.util.Date"). This last format was originally provided
+* for compatibility with xt and xalan, but it causes problems because it leads to
+* unnecessary attempts to load spurious classes when the user did not intend the URI
+* to represent a dynamically-loaded Java class.</p>
+*
+**/
+
+public final static String ALLOW_OLD_JAVA_URI_FORMAT =
+"http://saxon.sf.net/feature/allow-old-java-uri-format";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>ALLOW_STREAMABILITY_EXTENSIONS determines whether Saxon allows streaming of constructs
+* that are not guaranteed streamable according to the W3C streamability rules in the XSLT 3.0
+* specification.</p>
+* <p>When this is set to false, Saxon tries to match the W3C streamability rules as closely as
+* possible (ensuring that a stylesheet that is streamable under Saxon is also streamable under any
+* other streamable XSLT 3.0 processor). However, it is likely that there will still be some differences.</p>
+*
+*
+**/
+
+public final static String ALLOW_STREAMABILITY_EXTENSIONS =
+"http://saxon.sf.net/feature/allow-streamability-extensions";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>ASSERTIONS_CAN_SEE_COMMENTS determines whether comment and processing instructions
+* in a document being validated are visible to assertions in an XSD 1.1 schema.</p>
+* <p>If the value is false (the default), comments and processing instructions are
+* stripped from the view of the document that is made visible to the XPath expression
+* that implements the assertion. If this creates adjacent text nodes, they are collapsed
+* into a single text node.</p>
+* <p>If the value is true, then comments and processing instructions are
+* visible to the XPath assertion.</p>
+*
+**/
+
+public final static String ASSERTIONS_CAN_SEE_COMMENTS =
+"http://saxon.sf.net/feature/assertionsCanSeeComments";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The supplied <code>CollationURIResolver</code> is used to process any collation URIs
+* found in the query or stylesheet, returning an object of class
+* <link>StringCollator</link> that implements the requested collation.</p>
+*
+**/
+
+public final static String COLLATION_URI_RESOLVER =
+"http://saxon.sf.net/feature/collation-uri-resolver";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The supplied class is instantiated and the resulting instance is used as the value of
+* the <link>FeatureKeys#COLLATION_URI_RESOLVER</link> property.</p>
+*
+**/
+
+public final static String COLLATION_URI_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/collation-uri-resolver-class";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The supplied <code>CollectionURIResolver</code> is used to process any URIs used in
+* calls to the <code>collection()</code> function. The
+* <code>CollectionURIResolver</code> may either return a sequence of URIs (which
+* are then resolved in the same way as URIs passed to the <code>doc()</code>
+* function), or it may return a sequence of (typically document) nodes.</p>
+*
+**/
+
+public final static String COLLECTION_URI_RESOLVER =
+"http://saxon.sf.net/feature/collection-uri-resolver";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The supplied class is instantiated and the resulting instance is used as the value of
+* the <link>FeatureKeys#COLLECTION_URI_RESOLVER</link> property.</p>
+*
+**/
+
+public final static String COLLECTION_URI_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/collection-uri-resolver-class";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**<p>If run-time tracing of stylesheet or query execution is required, then the code must
+* be compiled with tracing enabled. Default is false. This option causes code to be
+* compiled that makes calls to a <link>net.sf.saxon.lib.TraceListener</link>, but this has
+* no effect unless a <code>TraceListener</code> is registered at execution time.</p>
+**/
+
+public final static String COMPILE_WITH_TRACING =
+"http://saxon.sf.net/feature/compile-with-tracing";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This attribute cannot be set on the <link>net.sf.saxon.Configuration</link> itself,
+* but it can be set on various JAXP factory objects such as a <code>TransformerFactory</code> or
+* <code>DocumentBuilderFactory</code>, to ensure that several such factories use the same
+* <code>Configuration</code>. Note that other configuration options are held in the <code>Configuration</code>
+* object, so setting this attribute will cancel all others that have been set. Also,
+* if two factories share the same <code>Configuration</code>, then setting an attribute on one
+* affects all the others.</p>
+*
+**/
+
+public final static String CONFIGURATION =
+"http://saxon.sf.net/feature/configuration";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**<p>Defines a configuration file to be applied to the configuration. This attribute cannot
+* be set on the <link>net.sf.saxon.Configuration</link> itself, but it can be set on
+* various JAXP factory objects such as a <code>TransformerFactory</code> or
+* <code>DocumentBuilderFactory</code>. It is particularly useful when running
+* transformations via the Ant xslt task, where it can be set as follows to define all
+* configuration settings: </p><p>
+* <factory name="net.sf.saxon.TransformerFactoryImpl">
+* <attribute name="http://saxon.sf.net/feature/configuration-file"
+* value="c:/saxon/config.xml"/>
+* </factory>
+* </p>
+*
+**/
+
+public final static String CONFIGURATION_FILE =
+"http://saxon.sf.net/feature/configuration-file";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option is set to indicate that bytecode generation should be run in debugging mode; it
+* injects diagnostic tracing calls into the generated bytecode. This
+* should be used only if requested by Saxonica support to diagnose a problem related to bytecode generation.</p>
+*
+**/
+
+public final static String DEBUG_BYTE_CODE =
+"http://saxon.sf.net/feature/debugByteCode";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option is relevant only if <code>DISPLAY_BYTE_CODE</code> is set to true. It defines a directory
+* to which files containing bytecode in human-readable form will be written. There will be one such
+* file for each generated class, with a filename designed to give some kind of clue as to its
+* relationship to the source code.</p>
+* <p>The default value is the the directory <code>saxonByteCode</code> within the current
+* working directory.</p>
+* <p>The named directory is created if it does not already exist.</p>
+* <p>Because the generation of class names involves random numbers, files will tend to accumulate
+* in the supplied directory, even when the same source code is compiled repeatedly.</p>
+*
+**/
+
+public final static String DEBUG_BYTE_CODE_DIR =
+"http://saxon.sf.net/feature/debugByteCodeDir";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option determines the collation that is used for comparing strings when no
+* explicit collation is requested. It is not necessary for this collation to exist (or
+* to have been registered) when setting this option; it only needs to exist by the
+* time it is used.</p>
+* <p>In XSLT it is possible to override this setting using the
+* <code>[xsl:]default-collation</code> attribute on any stylesheet element. In
+* XQuery, it can be overridden using the <code>declare default collation</code>
+* declaration in the query prolog, or via a setter method in class
+* <link>net.sf.saxon.query.StaticQueryContext</link>.</p>
+* <p>If no value is specified, the Unicode codepoint collation is used.</p>
+*
+**/
+
+public final static String DEFAULT_COLLATION =
+"http://saxon.sf.net/feature/defaultCollation";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p> This determines the collection that is used when the <code>fn:collection()</code>
+* function is called with no arguments; the effect is the same as if it were called
+* passing the URI that is the value of this configuration property.</p>
+*
+**/
+
+public final static String DEFAULT_COLLECTION =
+"http://saxon.sf.net/feature/defaultCollection";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p> This determines the country that is used by <code>format-date()</code> and similar
+* functions if no country code is supplied explicitly. If no value is given for this
+* property, the default is taken from the Java Locale, which in turn typically depends
+* on settings for the current user in the operating system.</p>
+*
+**/
+
+public final static String DEFAULT_COUNTRY =
+"http://saxon.sf.net/feature/defaultCountry";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option determines the language that is used by <code>format-date()</code>,
+* <code>xsl:number</code> and similar constructs if no language code is supplied
+* explicitly. If no value is given for this property, the default is taken from the
+* Java Locale, which in turn typically depends on settings for the current user in the
+* operating system.</p>
+*
+**/
+
+public final static String DEFAULT_LANGUAGE =
+"http://saxon.sf.net/feature/defaultLanguage";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option is set to indicate that bytecode generation should be run in display mode. The effect
+* is to output files (one per class) containing a human-readable print of the generated bytecode.
+* The files are placed in the directory identified by the <code>DEBUG_BYTE_CODE_DIR</code> option.</p>
+*
+**/
+
+public final static String DISPLAY_BYTE_CODE =
+"http://saxon.sf.net/feature/displayByteCode";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>If true, the XML parser is requested to perform validation of source documents
+* against their DTD. Default is false.</p>
+* <p>This option establishes a default for use whenever source documents (not stylesheets
+* or schema documents) are parsed. The option can be overridden for individual
+* documents by setting the <link>net.sf.saxon.lib.ParseOptions</link> for that
+* individual document, for example from a <code>URIResolver</code>.</p>
+*
+**/
+
+public final static String DTD_VALIDATION =
+"http://saxon.sf.net/feature/validation";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option determines whether DTD validation failures should be treated as
+* recoverable. If the option is set, a validation failure is reported as a warning
+* rather than an error. The default is false.</p>
+* <p>This option establishes a default for use whenever source documents (not stylesheets
+* or schema documents) are parsed. The option can be overridden for individual
+* documents by setting the <link>net.sf.saxon.lib.ParseOptions</link> for that
+* individual document, for example from a <code>URIResolver</code>.</p>
+*
+**/
+
+public final static String DTD_VALIDATION_RECOVERABLE =
+"http://saxon.sf.net/feature/dtd-validation-recoverable";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The supplied class is instantiated and the resulting <code>EntityResolver</code>
+* is used whenever Saxon itself creates an XMLReader
+* for parsing source documents. It is not used with a user-supplied
+* XMLReader.</p>
+* <p>The default value is <link>net.sf.saxon.lib.EntityResolver</link>.
+* This is an entity resolver that recognizes the names of many standard W3C DTDs
+* and external entity files, and resolves them against local copies
+* issued with the Saxon software, to avoid the need to fetch them from the web.</p>
+* <p>The property can be set to a zero-length string, in which case no EntityResolver
+* will be used.</p>
+*
+*
+**/
+
+public final static String ENTITY_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/entityResolverClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**<p>Setting the ENVIRONMENT_VARIABLE_RESOLVER causes the supplied object to be
+* registered as the environment variable resolver for the
+* <code>Configuration</code>. The setting is global: it affects all queries and
+* transformations using this configuration.</p>
+* <p>The environment variable resolver is used when the XPath functions
+* <code>available-environment-variables</code> or <code>environment-variable</code>
+* are called. Saxon essentially delegates the evaluation of the function to the external
+* resolver.</p>
+*
+**/
+
+public final static String ENVIRONMENT_VARIABLE_RESOLVER =
+"http://saxon.sf.net/feature/environmentVariableResolver";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**<p>Setting ENVIRONMENT_VARIABLE_RESOLVER_CLASS causes an instance of the supplied
+* class to be created, and registered as the environment variable resolver for the
+* <code>Configuration</code>. The setting is global: it affects all queries and
+* transformations using this configuration.</p>
+* <p>The environment variable resolver is used when the XPath functions
+* <code>available-environment-variables</code> or <code>environment-variable</code>
+* are called. Saxon essentially delegates the evaluation of the function to the external
+* resolver.</p>
+*
+**/
+
+public final static String ENVIRONMENT_VARIABLE_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/environmentVariableResolverClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>ERROR_LISTENER_CLASS is the name of the class used to implement the JAXP
+* <code>ErrorListener</code>. This is used both at compile time and at run-time.
+* Currently if this option is specified, the class is instantiated, and the same
+* instance is used for all processes running under this configuration. This may change
+* in future so that a new instance is created for each compilation or evaluation.</p>
+* <p>Finer control can be obtained by setting the <code>ErrorListener</code> for a
+* specific XSLT or XQuery compilation or evaluation.</p>
+*
+**/
+
+public final static String ERROR_LISTENER_CLASS =
+"http://saxon.sf.net/feature/errorListenerClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>EXPAND_ATTRIBUTE_DEFAULTS determines whether fixed and default values defined in a
+* schema or DTD will be expanded (both on input and on output documents, if validation
+* is requested). By default (and for conformance with the specification) validation
+* against a DTD or schema will cause default values defined in the schema or DTD to be
+* inserted into the document. Setting this feature to false suppresses this behavior.
+* In the case of DTD-defined defaults this only works if the XML parser reports
+* whether each attribute was specified in the source or generated by expanding a
+* default value. Not all XML parsers report this information.</p>
+*
+**/
+
+public final static String EXPAND_ATTRIBUTE_DEFAULTS =
+"http://saxon.sf.net/feature/expandAttributeDefaults";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>By default, Saxon-EE attempts to generate Java bytecode for evaluation of
+* parts of a query or stylesheet that are amenable to such treatment. Setting this option
+* to false disables this.</p>
+*
+**/
+
+public final static String GENERATE_BYTE_CODE =
+"http://saxon.sf.net/feature/generateByteCode";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>If this option is set to true, then when a <code>SAXSource</code> is supplied as the input to an XSLT
+* transformation, Saxon will ignore the <code>XMLReader</code> supplied in the <code>SAXSource</code> (in
+* fact, it will modify the supplied <code>SAXSource</code> setting the <code>XMLReader</code> to null) and
+* use an <code>XMLReader</code> created using the value of the <code>SOURCE_PARSER_CLASS</code> option in
+* preference.</p>
+* <p>The defining use case for this feature is when calling a Saxon transformation from Ant. Ant always supplies
+* the source document as a <code>SAXSource</code> using a parser instantiated using the default JAXP mechanism.
+* A different parser set using the property <code>http://saxon.sf.net/feature/sourceParserClass</code> will
+* normally be ignored in this case; setting the <code>ignoreSAXSourceParser</code> option overrides this. This
+* is especially useful if the input format is not in fact XML, but some other format converted to a SAX event stream
+* by means of a custom parser.</p>
+*
+**/
+
+public final static String IGNORE_SAX_SOURCE_PARSER =
+"http://saxon.sf.net/feature/ignoreSAXSourceParser";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>LAZY_CONSTRUCTION_MODE determines whether temporary trees are constructed * lazily.
+* The default setting is false; there are a few situations (but not many) where
+* setting this * to true can give a performance benefit (especially a memory
+* saving).</p>
+* <p>The option is most likely to be effective when executing XQuery in "pull" mode, that
+* is, when the client calls the query processor to deliver the result as a stream of
+* nodes, rather than running the query and piping the results into a serializer.</p>
+*
+**/
+
+public final static String LAZY_CONSTRUCTION_MODE =
+"http://saxon.sf.net/feature/lazyConstructionMode";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>LICENSE_FILE_LOCATION holds the filename in which the Saxon license file is held.
+* This is the full file name, for example <code>c:/saxon/license/license.lic</code>. Setting this
+* property causes Saxon to immediately read the specified file and register the
+* license data, assuming it can be found at this location. The property is not
+* recognized for reading, and it is not recognized for writing except in Saxon-PE and
+* Saxon-EE.</p>
+*
+**/
+
+public final static String LICENSE_FILE_LOCATION =
+"http://saxon.sf.net/feature/licenseFileLocation";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Default is false. If true, line and column number information is retained for all
+* source documents. This information is accessible using the
+* <code>saxon:line-number()</code> and <code>saxon:column-number()</code>
+* extension functions.</p>
+* <p>Note that the information is only as good as the XML parser supplies. SAX parsers
+* generally report the position of an element node using the line and column number of
+* the ">" character that forms the last character of the start tag.</p>
+*
+**/
+
+public final static String LINE_NUMBERING =
+"http://saxon.sf.net/feature/linenumbering";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Use the specified Receiver to process the output from <code>xsl:message</code>. The
+* class must implement the <code>net.sf.saxon.event.Receiver</code> interface. This
+* interface is similar to a SAX <code>ContentHandler</code>, in that it takes a stream
+* of events to generate output.</p>
+* <p> By default the standard XML emitter is used, configured to write to the standard
+* error stream, and to include no XML declaration. </p>
+* <p>In general the content of a message is an XML fragment. Each message is output as a
+* new document. The sequence of calls to this Receiver is as follows: there is a
+* single <code>open()</code> call at the start of the transformation, and a single
+* <code>close()</code> call at the end; and each evaluation of an
+* <code>xsl:message</code> instruction starts with a <code>startDocument()</code>
+* call and ends with <code>endDocument()</code>. </p>
+* <p>The <code>startDocument()</code> event has a <code>properties</code> argument
+* indicating whether <code>terminate="yes"</code> was specified, and the
+* <code>locationId</code> on calls such as <code>startElement()</code> and
+* <code>characters()</code> can be used to identify the location in the stylesheet
+* where the message data originated (this is achieved by passing the supplied
+* <code>locationId</code> in a call to
+* <code>getPipelineConfiguration().getLocator().getSystemId(locationId)</code>, or
+* to <code>getLineNumber()</code> on the same object). </p>
+* <p>Select the class <code>net.sf.saxon.event.MessageWarner</code> to have
+* <code>xsl:message</code> output notified to the JAXP <code>ErrorListener</code>,
+* as described in the JAXP documentation.</p>
+*
+**/
+
+public final static String MESSAGE_EMITTER_CLASS =
+"http://saxon.sf.net/feature/messageEmitterClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Affects XQuery only. An instance of a user-written class implementing Saxon's
+* <link>net.sf.saxon.lib.ModuleURIResolver</link> interface. This is used to
+* process any URIs used in <code>import module</code> directives in XQuery.</p>
+*
+**/
+
+public final static String MODULE_URI_RESOLVER =
+"http://saxon.sf.net/feature/moduleURIResolver";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Affects XQuery only. The name of a user-written class implementing Saxon's
+* <link>net.sf.saxon.lib.ModuleURIResolver</link> interface. This is used to
+* process any URIs used in <code>import module</code> directives in XQuery.</p>
+*
+**/
+
+public final static String MODULE_URI_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/moduleURIResolverClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Affects schema construction (whether for standalone validation, or in the context of XSLT or XQuery).
+* If set to true, the schema processor attempts to fetch a schema document appearing
+* in an <code>xs:import</code> directive, whether or not a schema for that namespace has already
+* been loaded, unless the absolute URI formed from the <code>schemaLocation</code> is the same as the absolute
+* URI that was used to load that namespace. If set to false, the schema processor ignores the
+* <code>schemaLocation</code> on an <code>xs:import</code> declaration if schema components for the requested
+* namespace are already available. Note that setting the value to true might cause spurious errors
+* due to like-named schema components being loaded more than once. On the other hand, setting the
+* value to false might result in validation failing because schema components are missing.</p>
+*
+* <p>Note: Both settings are conformant with the W3C recommendation, which
+* leaves the details implementation-defined. It is possible (and advisable) to write schemas in
+* such a way that this setting makes no difference, by ensuring that all imports for a particular
+* namespace go via a "home" schema document for that namespace, where the home schema document
+* contains <code>xs:include</code> declarations for all the schema documents defining components
+* in that namespace.</p>
+*
+*
+**/
+
+public final static String MULTIPLE_SCHEMA_IMPORTS =
+"http://saxon.sf.net/feature/multipleSchemaImports";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Indicates that the supplied <code>NamePool</code> should be used as the target
+* (run-time) NamePool by all stylesheets compiled (using <code>newTemplates()</code>)
+* after this call on <code>setAttribute</code>. Normally a single system-allocated
+* <code>NamePool</code> is used for all stylesheets compiled while the Java VM
+* remains loaded; this attribute allows user control over the allocation of name
+* pools. Note that source trees used as input to a transformation must be built using
+* the same <code>NamePool</code> that is used when the stylesheet is compiled: this
+* will happen automatically if the input to a transformation is supplied as a
+* <code>SAXSource</code> or <code>StreamSource</code> but it is under user control
+* if you build the source tree yourself.</p>
+* <p>This option can be used to make two <code>Configuration</code> objects share a <code>NamePool</code> even though they
+* differ in other respects.</p>
+*
+**/
+
+public final static String NAME_POOL =
+"http://saxon.sf.net/feature/namePool";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>OCCURRENCE_LIMITS determines the largest values of <code>minOccurs</code> and
+* <code>maxOccurs</code> that can be accommodated when compiling an "expanded"
+* finite state machine to represent an XSD content model grammar. These limits do not
+* apply in the common cases where the grammar can be implemented using a counting
+* finite-state-machine, but in those cases where this is not possible, any
+* <code>minOccurs</code> value greater than the first integer is reduced to the
+* value of the first integer, and any <code>maxOccurs</code> value greater than the
+* second integer is treated as "unbounded".</p>
+*
+* <p>Setting these values too high may cause an OutOfMemoryException since the size of
+* the finite state machine constructed by Saxon increases linearly with the values of
+* <code>minOccurs</code> and <code>maxOccurs</code>.</p>
+*
+**/
+
+public final static String OCCURRENCE_LIMITS =
+"http://saxon.sf.net/feature/occurrenceLimits";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>A string whose value is an integer in the range 0 (no optimization) to 10 (full
+* optimization); currently all values other than 0 result in full optimization but
+* this is likely to change in future. The default is full optimization; this feature
+* allows optimization to be suppressed in cases where reducing compile time is
+* important, or where optimization gets in the way of debugging, or causes extension
+* functions with side-effects to behave unpredictably. (Note however, that even with
+* no optimization, lazy evaluation may still cause the evaluation order to be not as
+* expected.) </p>
+*
+**/
+
+public final static String OPTIMIZATION_LEVEL =
+"http://saxon.sf.net/feature/optimizationLevel";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The supplied <code>OutputURIResolver</code> will be used to resolve URIs of secondary
+* result documents selected in the <code>href</code> attribute of the
+* XSLT <code>xsl:result-document</code> instruction</p>
+*
+**/
+
+public final static String OUTPUT_URI_RESOLVER =
+"http://saxon.sf.net/feature/outputURIResolver";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The supplied class will be instantiated, and the resulting
+* <code>OutputURIResolver</code> will be used to resolve URIs of secondary result
+* documents selected in the <code>href</code> attribute of the
+* XSLT <code>xsl:result-document</code> instruction</p>
+*
+**/
+
+public final static String OUTPUT_URI_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/outputURIResolverClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>If true, calls on the <code>doc()</code> and <code>document()</code> functions, if
+* their arguments are known at compile time, will be evaluated at compile time, and
+* the resulting document will be stored as part of the Saxon
+* <link>net.sf.saxon.Configuration</link> and shared by all queries and
+* transformations running within that <code>Configuration</code>. This is useful for
+* reference documents that have stable content and are used by many different queries
+* and transformations. The default is false, which means each query or transformation
+* will reload the document from disk.</p>
+* <p>In XSLT 3.0 a better way of having external documents pre-loaded at stylesheet compile
+* time is to use the new facility of static global variables.</p>
+*
+**/
+
+public final static String PRE_EVALUATE_DOC_FUNCTION =
+"http://saxon.sf.net/feature/preEvaluateDocFunction";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option has no effect on the Java platform. The default is <b>true</b>. When
+* running on the .NET platform, if the option is true it causes the Apache Xerces
+* parser (cross-compiled using IKVMC) to be used in preference to the .NET XML parser.
+* If false the .NET XML parser (<code>System.Xml.XmlTextReader</code>) is used. One
+* reason for providing this option is that the .NET XML parser does not report ID
+* attributes, which means that the <code>id()</code> function does not work.</p>
+*
+**/
+
+public final static String PREFER_JAXP_PARSER =
+"http://saxon.sf.net/feature/preferJaxpParser";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>True if the the standard URI resolver is to recognize query parameters included in
+* the URI (for example, <code>?val=strict</code>). Such parameters can then be used in
+* URIs passed to the <code>doc()</code> or <code>document()</code> functions. For
+* details of the query parameters available, see <xref>Source
+* Documents</xref>. The default is false.</p>
+* <p>This option has no effect if a user-supplied <code>URIResolver</code> is in use,
+* unless the user-supplied <code>URIResolver</code> chooses to inherit this
+* functionality from the standard <code>URIResolver</code>.</p>
+* <p>Allowed parameters include <code>validation=strict|lax|strip</code> to perform schema
+* validation, <code>strip-space=yes|ignorable|no</code> to control whitespace
+* stripping, and <code>xinclude=yes|no</code> to control whether XInclude processing
+* takes place (assuming the XML parser supports it).</p>
+*
+**/
+
+public final static String RECOGNIZE_URI_QUERY_PARAMETERS =
+"http://saxon.sf.net/feature/recognize-uri-query-parameters";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>An integer, one of <link>net.sf.saxon.Configuration#RECOVER_SILENTLY</link>,
+* <link>net.sf.saxon.Configuration#RECOVER_WITH_WARNINGS</link>, or
+* <link>net.sf.saxon.Configuration#DO_NOT_RECOVER</link>. Indicates the policy for
+* handling dynamic errors that the XSLT specification defines as recoverable. 0 means
+* recover silently; 1 means recover after signalling a warning to the
+* <code>ErrorListener</code>; 2 means treat the error as fatal. An example of a
+* recoverable error is when two template rules match the same node.</p>
+* <p>Note that XSLT 3.0 has eliminated all "recoverable errors" from the specification.</p>
+*
+**/
+
+public final static String RECOVERY_POLICY =
+"http://saxon.sf.net/feature/recoveryPolicy";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Indicates the policy for handling dynamic errors that the XSLT specification defines
+* as recoverable. "recoverSilently" means recover silently; "recoverWithWarnings"
+* means recover after signalling a warning to the <code>ErrorListener</code>;
+* "doNotRecover" means treat the error as fatal. An example of a recoverable error is
+* when two template rules match the same node. </p>
+* <p>Note that XSLT 3.0 has eliminated all "recoverable errors" from the specification.</p>
+*
+**/
+
+public final static String RECOVERY_POLICY_NAME =
+"http://saxon.sf.net/feature/recoveryPolicyName";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Indicates the maximum number of threads to be used for processing <code>xsl:result-document</code>
+* instructions in parallel. If the maximum number of threads are already in use, the <code>xsl:result-document</code>
+* instruction will be executed synchronously within its parent thread.</p>
+* <p>The default value is initialized to <code>Runtime.getRuntime().availableProcessors()</code> which is intended to
+* represent the number of "processors" (under some definition) available from the hardware.</p>
+* <p>The limit applies per <code>Configuration</code>. If multiple workloads are running on the same server under separate
+* Saxon <code>Configuration</code> objects, then it may be desirable to lower the limit.</p>
+* <p>Setting a value of zero or one suppresses multithreading entirely. This can also be achieved (for a specific
+* <code>xsl:result-document</code> instruction) by setting the attribute <code>saxon:asynchronous="no"</code>
+* in the stylesheet. Suppressing multithreading may be desirable in a stylesheet that calls extension functions
+* with side-effects.</p>
+*
+**/
+
+public final static String RESULT_DOCUMENT_THREADS =
+"http://saxon.sf.net/feature/resultDocumentThreads";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>If set to true, indicates that when input is obtained from a SAX parser, the DTD-based
+* attribute type notified by the XML parser should be used to set the type annotation of the
+* resulting node: for example a DTD type of NMTOKENS results in a type annotation of
+* xs:NMTOKENS. </p>
+* <p>This option is retained for backwards compatibility (at some time in the past, it was the
+* default), but is deprecated.</p>
+*
+**/
+
+public final static String RETAIN_DTD_ATTRIBUTE_TYPES =
+"http://saxon.sf.net/feature/retain-dtd-attribute-types";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The supplied <code>SchemaURIResolver</code> will be used to resolve URIs of schema
+* documents referenced in <code>xsl:import-schema</code> declarations in XSLT,
+* <code>import schema</code> in XQuery, references from one schema document to
+* another using <code>xs:include</code> or <code>xs:import</code>, and references from
+* an instance document to a schema using <code>xsi:schemaLocation</code>.</p>
+*
+**/
+
+public final static String SCHEMA_URI_RESOLVER =
+"http://saxon.sf.net/feature/schemaURIResolver";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The name of a class that implements the interface <code>SchemaURIResolver</code>;
+* this class will be instantiated and the resulting instance will be used as the value
+* of the <link>FeatureKeys#SCHEMA_URI_RESOLVER</link> property.</p>
+*
+**/
+
+public final static String SCHEMA_URI_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/schemaURIResolverClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Indicates whether and how schema validation should be applied to source
+* documents.</p>
+*
+**/
+
+public final static String SCHEMA_VALIDATION =
+"http://saxon.sf.net/feature/schema-validation";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Indicates whether and how schema validation should be applied to source
+* documents.</p>
+*
+**/
+
+public final static String SCHEMA_VALIDATION_MODE =
+"http://saxon.sf.net/feature/schema-validation-mode";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The class will be instantiated and the resulting <code>SerializerFactory</code> will
+* be used to create the serialization pipeline for XSLT and XQuery results. By
+* subclassing the standard <code>SerializerFactory</code> it is possible to customize
+* many aspects of the output produced by the Serializer, or to introduce new
+* serialization methods and parameters.</p>
+*
+**/
+
+public final static String SERIALIZER_FACTORY_CLASS =
+"http://saxon.sf.net/feature/serializerFactoryClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The class will be instantiated and the resulting <code>XMLReader</code> will be used
+* to parse source documents (that is, the principal source document plus any secondary
+* source documents read using the <code>doc()</code>, <code>document()</code>, or
+* <code>collection()</code> function)</p>
+* <p>Note that the selected parser is used only when the input is supplied in the form
+* of a <code>StreamSource</code>; it is ignored when a <code>SAXSource</code> with a pre-initialized
+* <code>XMLReeader</code> is supplied. A consequence is that this configuration option has no effect
+* when running transformations from an ant script, since the ant <code>xslt</code> task always supplies
+* the input in the form of a <code>SAXSource</code>.</p>
+*
+**/
+
+public final static String SOURCE_PARSER_CLASS =
+"http://saxon.sf.net/feature/sourceParserClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>On interfaces that allow a <code>org.xml.sax.Source</code> to be supplied, if a kind
+* of <code>Source</code> is provided that Saxon does not recognize, it will be passed
+* to the user-supplied <code>SourceResolver</code>, which has the opportunity to
+* convert it to a kind of <code>Source</code> that Saxon does recognize. This allows
+* new kinds of input to be supplied as input to Saxon's query, transformation, and
+* validation engines.</p>
+*
+**/
+
+public final static String SOURCE_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/sourceResolverClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>STANDARD_ERROR_OUTPUT_FILE is the name of a file to which Saxon will redirect output
+* that would otherwise go to the operating system standard error stream (System.err).
+* This is the fallback destination for various tracing and diagnostic output. In some
+* cases a more specific mechanism exists to select the destination for particular
+* kinds of output.</p>
+* <p>Note that if the Configuration is used in more than one processing thread, the messages
+* from different threads will be interleaved in the output file. A more selective approach
+* is to use a different <code>ErrorListener</code> in different processing threads, and arrange
+* for each <code>ErrorListener</code> to write to its own logging destination.</p>
+*
+**/
+
+public final static String STANDARD_ERROR_OUTPUT_FILE =
+"http://saxon.sf.net/feature/standardErrorOutputFile";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Indicates whether all whitespace, no whitespace, or whitespace in elements defined in
+* a DTD or schema as having element-only content should be stripped from source
+* documents. The default is "ignorable". This whitespace stripping is additional to
+* any stripping done as a result of the <code>xsl:strip-space</code> declaration in an
+* XSLT stylesheet.</p>
+*
+**/
+
+public final static String STRIP_WHITESPACE =
+"http://saxon.sf.net/feature/strip-whitespace";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**<p>The class will be instantiated, and the resulting <code>XMLReader</code> will be used
+* to parse stylesheet documents (that is, the principal stylesheet module plus any
+* secondary source documents read using xsl:include or xsl:import) and also schema
+* documents.</p>
+**/
+
+public final static String STYLE_PARSER_CLASS =
+"http://saxon.sf.net/feature/sourceParserClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This is set to true to suppress the warning otherwise issued by command-line interfaces
+* indicating that an evaluation license is in use and is due to expire in a set number of days.</p>
+*
+**/
+
+public final static String SUPPRESS_EVALUATION_EXPIRY_WARNING =
+"http://saxon.sf.net/feature/suppressEvaluationExpiryWarning";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This is set to true to cause basic timing and tracing information is to be output to
+* the standard error output stream. The name of the feature is poorly chosen, since
+* much of the information that is output has nothing to do with timing, for example
+* the names of output files for <code>xsl:result-document</code> are traced, as
+* are the names of schema documents loaded.</p>
+*
+**/
+
+public final static String TIMING =
+"http://saxon.sf.net/feature/timing";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>If this option is set, Saxon will output (to the standard error output) progress
+* information about its attempts to locate and disambiguate references to reflexive
+* Java extension functions. This is useful for diagnostics if the XQuery or XSLT
+* compiler is failing to locate user-written extension functions.</p>
+*
+**/
+
+public final static String TRACE_EXTERNAL_FUNCTIONS =
+"http://saxon.sf.net/feature/trace-external-functions";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>If this option is set, Saxon will output (to the standard error output) detailed
+* information about the rewrites to the expression tree made by the optimizer. This
+* information is mainly useful for internal system debugging, but it is also possible
+* to digest it to analyze the ways in which the expression has been optimized for the
+* purpose of performance analysis and tuning.</p>
+*
+**/
+
+public final static String TRACE_OPTIMIZER_DECISIONS =
+"http://saxon.sf.net/feature/trace-optimizer-decisions";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The <code>TraceListener</code> will be notified of significant events occurring
+* during a query or transformation, for tracing or debugging purposes.</p>
+* <p>Setting a <code>TraceListener</code> automatically sets the
+* <link>FeatureKeys#COMPILE_WITH_TRACING</link> option.</p>
+* <p>Avoid this option if more than one transformation or query is running concurrently:
+* use the feature <link>FeatureKeys#TRACE_LISTENER_CLASS</link> instead.
+* Alternatively, it is possible to set a <code>TraceListener</code> for an individual
+* query or transformation.</p>
+*
+**/
+
+public final static String TRACE_LISTENER =
+"http://saxon.sf.net/feature/traceListener";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The class will be instantiated once for each query or transformation, and the
+* resulting <code>TraceListener</code> will be notified of significant events
+* occurring during that query or transformation, for tracing or debugging
+* purposes.</p>
+* <p>Setting a <code>TraceListener</code> automatically sets the
+* <link>FeatureKeys#COMPILE_WITH_TRACING</link> option.</p>
+*
+**/
+
+public final static String TRACE_LISTENER_CLASS =
+"http://saxon.sf.net/feature/traceListenerClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Selects an implementation of the Saxon tree model. The default is
+* <code>TINY_TREE</code>.</p>
+* <p>For running XQuery Update, use the linked tree, because it is the only implementation
+* that is updateable.</p>
+*
+**/
+
+public final static String TREE_MODEL =
+"http://saxon.sf.net/feature/treeModel";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Selects an implementation of the Saxon tree model. The default is
+* <code>tinyTree</code>.</p>
+* <p>For running XQuery Update, use the linked tree, because it is the only implementation
+* that is updateable.</p>
+*
+**/
+
+public final static String TREE_MODEL_NAME =
+"http://saxon.sf.net/feature/treeModelName";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>An instance of the specified <code>URIResolver</code> class will be created, and used
+* to resolve (dereference) all URIs specifed in calls to the <code>doc()</code> and
+* <code>document()</code> functions, as well as URIs used in
+* <code>xsl:include</code> and <code>xsl:import</code> and location hints for
+* XQuery modules and XSD schema documents.</p>
+*
+**/
+
+public final static String URI_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/uriResolverClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option determines whether a <code>TransformerHandler</code> created with this
+* <code>TransformerFactory</code> or <code>Configuration</code> recognizes the
+* JAXP-defined processing instructions <code>Result.PI_DISABLE_OUTPUT_ESCAPING</code>
+* and <code>Result.PI_ENABLE_OUTPUT_ESCAPING</code> in the input stream as
+* instructions to disable or to re-enable output escaping. The default value is
+* <b>false</b>.</p>
+*
+**/
+
+public final static String USE_PI_DISABLE_OUTPUT_ESCAPING =
+"http://saxon.sf.net/feature/use-pi-disable-output-escaping";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option is relevant only when the TinyTree is used; it determines whether (for a
+* validated document) a cache will be maintained containing the typed values of nodes.
+* Typed values are held in the cache only for elements and attributes whose type is
+* other than string, untypedAtomic, or anyURI. The default value is true. Setting this
+* value to false can reduce memory requirements at the cost of requiring recomputation
+* of typed values on each access.</p>
+*
+**/
+
+public final static String USE_TYPED_VALUE_CACHE =
+"http://saxon.sf.net/feature/use-typed-value-cache";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option determines whether or not to use the <code>xsi:schemaLocation</code>
+* and<code> xsi:noNamespaceSchemaLocation</code> attributes in an instance
+* document to locate a schema for validation.</p>
+* <p>Note, these attribute are only consulted if validation is requested; the presence of
+* one of these attributes never by itself triggers validation.</p>
+*
+**/
+
+public final static String USE_XSI_SCHEMA_LOCATION =
+"http://saxon.sf.net/feature/useXsiSchemaLocation";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option determines whether non-fatal validation errors in XQuery or XSLT result
+* documents should result in comments being inserted into the result tree. The
+* command-line flag <code>-outval:recover</code> sets both this option and the
+* <link>FeatureKeys#VALIDATION_WARNINGS</link> option.</p>
+*
+**/
+
+public final static String VALIDATION_COMMENTS =
+"http://saxon.sf.net/feature/validation-comments";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option indicates (if true) that errors occuring while validating a final result
+* tree are not to be treated as fatal.</p>
+* <p>Regardless of the setting of this option, all validation errors are reported to
+* the error() method of the ErrorListener, and validation is terminated if the
+* error() method throws an exception, or if the error limit set in the ParseOptions
+* object is reached.</p>
+* <p>This option primarily controls what happens at the end of a validation episode.
+* If the validation episode detected one or more validation errors, then when this option
+* is off, an exception is thrown, which will normally result in any query or stylesheet
+* failing with a dynamic error, and no output file being written. If the option is on,
+* no exception is thrown, and the output is written as if validation had been successful.
+* Note in this case that any type annotations present in a result document are unreliable.</p>
+*
+* <p>If this option is set when running XSLT or XQuery, it is ignored as far as
+* input files are concerned: validation errors in input files are still fatal.
+* However, if the option is set and a validation error occurs in a final output file,
+* the output file is still written and the process terminates as if successful.</p>
+*
+* <p>The detailed interpretation of this option changed in Saxon 9.5.</p>
+*
+*
+**/
+
+public final static String VALIDATION_WARNINGS =
+"http://saxon.sf.net/feature/validation-warnings";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Indicates whether a warning message should be notified (to the
+* <code>ErrorListener</code>) if running Saxon against an XSLT stylesheet that
+* specifies <code>version="1.0"</code>. The warning that an XSLT 1.0 stylesheet is
+* being processed using an XSLT 2.0 processor is output by default (because the W3C
+* specification requires it), but it may be suppressed using this option.</p>
+*
+**/
+
+public final static String VERSION_WARNING =
+"http://saxon.sf.net/feature/version-warning";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Indicates whether source documents should have any XInclude directives expanded. The
+* default is false. The option applies to all input XML documents, including
+* stylesheets and schema documents. It can be overridden for individual documents
+* using the <link>net.sf.saxon.lib.ParseOptions</link> class.</p>
+* <p>This option relies on support in the underlying XML parser. If the XML parser does
+* not support XInclude processing, the option is ignored.</p>
+*
+**/
+
+public final static String XINCLUDE =
+"http://saxon.sf.net/feature/xinclude-aware";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This determines the XML version used by the Configuration.</p>
+* <p>Note that source documents specifying xml version="1.0" or "1.1" are accepted
+* regardless of this setting. The effect of this switch is to change the validation
+* rules for types such as <code>xs:Name</code> and <code>xs:NCName</code>, to change
+* the characters allowed in names within XPath expressions (etc.), to change the
+* meaning of <code>\i</code> and <code>\c</code> in regular expressions, and to
+* determine whether the serializer allows XML 1.1 documents to be constructed. </p>
+* <p>The default is currently 1.0, but may change.</p>
+*
+**/
+
+public final static String XML_VERSION =
+"http://saxon.sf.net/feature/xml-version";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>Determines whether XQuery Update syntax is accepted. If true, update syntax is
+* accepted, if false, it is not accepted. Setting the value to true does not mean that
+* the query has to use update syntax, only that it may do so.</p>
+* <p>Note that XQuery Update syntax and XQuery 3.0 syntax cannot currently be mixed.</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+* <p>On the command line, this option is combined with the option "discard" which
+* indicates that updates are allowed, but the updates are not written back to
+* filestore. This does not correspond to any option in the Java API, where writing an
+* updated document back to filestore only happens if explicitly requested.</p>
+*
+**/
+
+public final static String XQUERY_ALLOW_UPDATE =
+"http://saxon.sf.net/feature/xqueryAllowUpdate";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This option defines the default value of the construction mode in the XQuery static
+* context (overridable in the query prolog)</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+*
+**/
+
+public final static String XQUERY_CONSTRUCTION_MODE =
+"http://saxon.sf.net/feature/xqueryConstructionMode";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property defines the default namespace for elements and types that are not
+* qualified by a namespace prefix.</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+*
+**/
+
+public final static String XQUERY_DEFAULT_ELEMENT_NAMESPACE =
+"http://saxon.sf.net/feature/xqueryDefaultElementNamespace";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**<p>This property defines the default namespace for function names that are not
+* qualified by a namespace prefix.</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+**/
+
+public final static String XQUERY_DEFAULT_FUNCTION_NAMESPACE =
+"http://saxon.sf.net/feature/xqueryDefaultFunctionNamespace";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property defines how the empty sequence is handled in XQuery sorting (the "order
+* by" clause). If true, () comes at the start of the sorted sequence; if false, it
+* comes last.</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+*
+**/
+
+public final static String XQUERY_EMPTY_LEAST =
+"http://saxon.sf.net/feature/xqueryEmptyLeast";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property defines the default value of the inherit-namespaces property in the
+* XQuery static context.</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+*
+**/
+
+public final static String XQUERY_INHERIT_NAMESPACES =
+"http://saxon.sf.net/feature/xqueryInheritNamespaces";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property defines whether "boundary space" (insignificant space in direct element
+* constructors) should be retained or not</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+*
+**/
+
+public final static String XQUERY_PRESERVE_BOUNDARY_SPACE =
+"http://saxon.sf.net/feature/xqueryPreserveBoundarySpace";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property defines whether unused namespace declarations are retained by XQuery
+* element copy operations</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+*
+**/
+
+public final static String XQUERY_PRESERVE_NAMESPACES =
+"http://saxon.sf.net/feature/xqueryPreserveNamespaces";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property defines the default expected context item type for a query.</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+*
+**/
+
+public final static String XQUERY_REQUIRED_CONTEXT_ITEM_TYPE =
+"http://saxon.sf.net/feature/xqueryRequiredContextItemType";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>A query will automatically be schema-aware if it contains an <code>import
+* schema</code> declaration. This property allows a query to be marked as
+* schema-aware even if it contains no <code>import schema</code> declaration. It is
+* necessary for a query to be compiled as schema-aware if it is to handle typed
+* (validated) input documents in which nodes have type annotations based on their
+* schema-defined type.</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+*
+**/
+
+public final static String XQUERY_SCHEMA_AWARE =
+"http://saxon.sf.net/feature/xquerySchemaAware";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The specified class is instantiated to create an <code>ErrorListener</code>,
+* and all reports of static errors in a query will go to this
+* <code>ErrorListener</code>.</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+* <p>In the absence of this property, the global <code>ErrorListener</code>
+* specified as the value of the <link>#ERROR_LISTENER_CLASS</link> property is used.</p>
+*
+**/
+
+public final static String XQUERY_STATIC_ERROR_LISTENER_CLASS =
+"http://saxon.sf.net/feature/xqueryStaticErrorListenerClass";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property determines the version of XQuery used by the Configuration. In order
+* to use XQuery 3.0, it is necessary both to set the XQuery compiler to process XQuery
+* 3.0, and to specify XQuery 3.0 in the query prolog of each module that uses XQuery
+* 3.0 features.</p>
+* <p>This option can be set for a particular XQuery compilation. When the option is set at
+* the <code>Configuration</code> level, it acts as a default.</p>
+* <p>Note that XQuery 3.0 features cannot be used with XQuery Update.</p>
+* <p>XQuery 3.0 is supported only in Saxon EE.</p>
+*
+**/
+
+public final static String XQUERY_VERSION =
+"http://saxon.sf.net/feature/xqueryVersion";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property determines the version of XML Schema used by the Configuration. The
+* default is XSD 1.0. If XSD 1.0 is selected, XSD 1.1 features will be rejected, with
+* the exception of the version control attributes that allow sections of the schema to
+* be marked as requiring XSD 1.0 or XSD 1.1.</p>
+*
+**/
+
+public final static String XSD_VERSION =
+"http://saxon.sf.net/feature/xsd-version";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property indicates the name of a mode within a stylesheet in which execution
+* (using template rules) should begin</p>
+* <p>This option can be set for a particular XSLT transformation. When the option is set at
+* the <code>Configuration</code> (or on a <code>TransformerFactory</code>), it acts as a default.</p>
+*
+**/
+
+public final static String XSLT_INITIAL_MODE =
+"http://saxon.sf.net/feature/initialMode";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property indicates the name of a named template within a stylesheet where
+* execution should begin</p>
+* <p>This option can be set for a particular XSLT transformation. When the option is set at
+* the <code>Configuration</code> level (or on a <code>TransformerFactory</code>), it acts as a default.</p>
+*
+**/
+
+public final static String XSLT_INITIAL_TEMPLATE =
+"http://saxon.sf.net/feature/initialTemplate";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property indicates whether stylesheets should be compiled with the ability to
+* handle schema-typed input documents. By default a stylesheet is compiled to handle
+* such input if it contains an <code>xsl:import-schema</code> instruction, and not
+* otherwise. It is
+* necessary for a stylesheet to be compiled as schema-aware if it is to handle typed
+* (validated) input documents in which nodes have type annotations based on their
+* schema-defined type.</p>
+* <p>This option can be set for a particular XSLT compilation. When the option is set at
+* the <code>Configuration</code> level (or on a <code>TransformerFactory</code>), it acts as a default.</p>
+*
+**/
+
+public final static String XSLT_SCHEMA_AWARE =
+"http://saxon.sf.net/feature/xsltSchemaAware";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>The specified class is instantiated to create an <code>ErrorListener</code>,
+* and all reports of static errors in a stylesheet will go to this
+* <code>ErrorListener</code>.</p>
+* <p>This option can be set for a particular XSLT compilation. When the option is set at
+* the <code>Configuration</code> level (or on a <code>TransformerFactory</code>), it acts as a default.</p>
+* <p>In the absence of this property, the global <code>ErrorListener</code>
+* specified as the value of the <link>#ERROR_LISTENER_CLASS</link> property is used.</p>
+*
+**/
+
+public final static String XSLT_STATIC_ERROR_LISTENER_CLASS =
+"http://saxon.sf.net/feature/stylesheetErrorListener";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property defines a <code>URIResolver</code> used when dereferencing the URIs
+* that appear in the <code>href</code> attributes of the <code>xsl:include</code> and
+* <code>xsl:import</code> declarations. Note that this defaults to the setting of
+* the global <code>URI_RESOLVER</code> property.</p>
+* <p>This option can be set for a particular XSLT compilation. When the option is set at
+* the <code>Configuration</code> level (or on a <code>TransformerFactory</code>), it acts as a default.</p>
+* <p>In the absence of this property, the global <code>URIResolver</code>
+* specified as the value of the <link>#URI_RESOLVER_CLASS</link> property is used.</p>
+*
+**/
+
+public final static String XSLT_STATIC_URI_RESOLVER_CLASS =
+"http://saxon.sf.net/feature/stylesheetURIResolver";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+/**
+* <p>This property determines the version of XSLT to be supported by default</p>
+* <p>XSLT 3.0 is supported only in Saxon EE. If no value is specified for the property,
+* an XSLT 2.0 or XSLT 3.0 processor is used depending on the value of the <code>version</code>
+* attribute of the <code>xsl:stylesheet</code> element.</p>
+*
+**/
+
+public final static String XSLT_VERSION =
+"http://saxon.sf.net/feature/xsltVersion";
+
+// AUTO-GENERATED FROM FeatureKeys.xml - DO NOT EDIT THIS FILE
+
+
+}
+
\ No newline at end of file
diff --git a/sf/saxon/lib/Initializer.java b/sf/saxon/lib/Initializer.java
new file mode 100644
index 0000000..15d3b33
--- /dev/null
+++ b/sf/saxon/lib/Initializer.java
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+
+import javax.xml.transform.TransformerException;
+
+/**
+ * This interface can be implemented by users (there are no implementations in Saxon itself). It is
+ * used only when Saxon is invoked from the command line, and the -init:class option is used on the command
+ * line to nominate an implementation of this class. The initialize() method of the supplied class will
+ * then be called to perform any user-defined initialization of the Configuration.
+ *
+ * The initializer is invoked after all other options on the command line have been processed; the initializer
+ * can therefore examine the Configuration to see what options have been set, and it can modify them accordingly.
+ *
+ * @since 9.3
+ */
+public interface Initializer {
+
+ /**
+ * Initialize the Configuration
+ * @param config the Configuration to be initialized
+ * @throws TransformerException if the initializer chooses to abort processing for any reason
+ */
+
+ public void initialize(Configuration config) throws TransformerException;
+}
+
diff --git a/sf/saxon/lib/LocalizerFactory.java b/sf/saxon/lib/LocalizerFactory.java
new file mode 100644
index 0000000..c4bbb0e
--- /dev/null
+++ b/sf/saxon/lib/LocalizerFactory.java
@@ -0,0 +1,56 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import java.io.Serializable;
+import java.util.Properties;
+
+/**
+ * Interface allowing localization modules for different languages to be dynamically loaded
+ */
+public abstract class LocalizerFactory implements Serializable {
+
+
+ /**
+ * Set properties for a particular language. The properties available are specific to the
+ * LocalizerFactory in use. Default implementation does nothing.
+ * @param lang the language
+ * @param properties properties of this language
+ * @since 9.2
+ */
+
+ public void setLanguageProperties(String lang, Properties properties) {
+ // no action
+ }
+
+
+ /**
+ * Get the numberer for a given language
+ * @param language the language code (for example "de" or "en-GB"). The value may be null,
+ * in which case a default language should be assumed.
+ * @param country the country, as used in format-date(). This is not the country associated
+ * with the language, but the one associated with the date to be formatted. It is primarily
+ * used to determine a civil time zone name. The value may be null, in which case a default
+ * country should be assumed.
+ * @return the appropriate numberer, or null if none is available (in which case the English
+ * numberer will be used)
+ */
+
+ public abstract Numberer getNumberer(String language, String country);
+
+ /**
+ * Copy the state of this factory to create a new LocalizerFactory. The default implementation
+ * returns this LocalizerFactory unchanged (which should only be done if it is immutable).
+ * @return a copy of this LocalizerFactory
+ */
+
+ public LocalizerFactory copy() {
+ return this;
+ }
+}
+
diff --git a/sf/saxon/lib/ModuleURIResolver.java b/sf/saxon/lib/ModuleURIResolver.java
new file mode 100644
index 0000000..84a742b
--- /dev/null
+++ b/sf/saxon/lib/ModuleURIResolver.java
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.stream.StreamSource;
+import java.io.Serializable;
+
+
+/**
+ * A ModuleURIResolver is used when resolving references to
+ * query modules. It takes as input a URI that identifies the module to be loaded, and a set of
+ * location hints, and returns one or more StreamSource obects containing the queries
+ * to be imported.
+* @author Michael H. Kay
+*/
+
+public interface ModuleURIResolver extends Serializable {
+
+ /**
+ * Locate a query module, or a set of query modules, given the identifying URI and
+ * a set of associated location hints.
+ *
+ * <p>The module URI resolver is generally invoked when locating library modules. When a
+ * module import declaration is encountered in the query source, Saxon first expands any
+ * relative URIs appearing in the location hints to absolute URIs by resolving against
+ * the base URI of the module. It then discards any absolutized location hints that have
+ * previously been used in processing other module import declarations. If there are no
+ * remaining location hints, and if a module with the requested module URI has already
+ * been loaded, then the module URI resolver is not called (it is assumed this is a duplicate
+ * request). In all other cases, the module URI resolver is called, passing the requested
+ * module namespace URI and the list of absolutized location hints excluding any that have
+ * been used in a previous request.</p>
+ *
+ *
+ * <p>The module URI resolver is also
+ * invoked when loading the main query module in cases where this is provided in the form
+ * of a URI: specifically, when running a query from the command line specifying the -u
+ * option, or with a supplied query file name that starts with "http:" or "file:". When
+ * locating the main query module, the moduleURI and baseURI parameters will be null, and
+ * the resolver must either return null (which delegates to the standard ModuleURIResolver),
+ * or must return a singleton StreamSource, that is, an array of length 1.</p>
+ *
+ * @param moduleURI the module URI of the module to be imported; or null when
+ * loading a non-library module.
+ * @param baseURI The base URI of the module containing the "import module" declaration;
+ * null if no base URI is known
+ * @param locations The set of URIs specified in the "at" clause of "import module",
+ * which serve as location hints for the module. The values supplied are absolute URIs
+ * formed by resolving the relative URI appearing in the query against the base URI of the query.
+ *
+ * @return an array of {@link StreamSource} objects each identifying the contents of a query module to be
+ * imported. Each StreamSource must contain a
+ * non-null absolute System ID which will be used as the base URI of the imported module,
+ * and either an {@link java.io.InputStream} or an {@link java.io.Reader} representing the text of the module.
+ * <p>The contained InputStream or Reader must be positioned at the start of the
+ * content to be read; it will be consumed by the system and will be closed after use.</p>
+ * <p>The method may alternatively return null, in which case the system attempts to resolve the URI using the
+ * standard module URI resolver. The standard module URI resolver attempts to dereference each one of the
+ * location hints to locate a query module. If there are no location hints, or if any location hint
+ * cannot be dereferenced, it reports a fatal error.</p>
+ *
+ * @throws XPathException if the module cannot be located, and if delegation to the default
+ * module resolver is not required.
+ */
+
+ public StreamSource[] resolve(String moduleURI, String baseURI, String[] locations) throws XPathException;
+
+}
+
diff --git a/sf/saxon/lib/NamespaceConstant.java b/sf/saxon/lib/NamespaceConstant.java
new file mode 100644
index 0000000..c21d60b
--- /dev/null
+++ b/sf/saxon/lib/NamespaceConstant.java
@@ -0,0 +1,401 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+/**
+ * This class is not instantiated, it exists to hold a set of constants representing known
+ * namespaces. For each of these, there is a constant for the namespace URI and for many of
+ * them, there is a numeric constant used as the code for this namespace in the name pool.
+ * <p/>
+ * <p>This class also defines constant URIs for some objects other than namespaces -
+ * for example, URIs that identify the various object models used in the JAXP XPath API,
+ * and the Unicode codepoint collation URI.</p>
+ *
+ * @author Michael H. Kay
+ */
+
+public class NamespaceConstant {
+
+ /**
+ * A URI representing the null namespace (actually, an empty string)
+ */
+
+ public static final String NULL = "";
+ /**
+ * The numeric URI code representing the null namespace (actually, zero)
+ */
+ public static final short NULL_CODE = 0;
+
+ /**
+ * Fixed namespace name for XML: "http://www.w3.org/XML/1998/namespace".
+ */
+ public static final String XML = "http://www.w3.org/XML/1998/namespace";
+ /**
+ * Numeric code representing the XML namespace
+ */
+ public static final short XML_CODE = 1;
+
+
+ /**
+ * Fixed namespace name for XSLT: "http://www.w3.org/1999/XSL/Transform"
+ */
+ public static final String XSLT = "http://www.w3.org/1999/XSL/Transform";
+ /**
+ * Numeric code representing the XSLT namespace
+ */
+ public static final short XSLT_CODE = 2;
+
+ /**
+ * Fixed namespace name for SAXON: "http://saxon.sf.net/"
+ */
+ public static final String SAXON = "http://saxon.sf.net/";
+ /**
+ * Numeric code representing the SAXON namespace
+ */
+ public static final short SAXON_CODE = 3;
+
+ /**
+ * Namespace name for XML Schema: "http://www.w3.org/2001/XMLSchema"
+ */
+ public static final String SCHEMA = "http://www.w3.org/2001/XMLSchema";
+ /**
+ * Numeric code representing the schema namespace
+ */
+ public static final short SCHEMA_CODE = 4;
+
+ /**
+ * XML-schema-defined namespace for use in instance documents ("xsi")
+ */
+ public static final String SCHEMA_INSTANCE = "http://www.w3.org/2001/XMLSchema-instance";
+
+ public static final short XSI_CODE = 5;
+
+ /**
+ * Namespace defined in XSD 1.1 for schema versioning
+ */
+ public static final String SCHEMA_VERSIONING = "http://www.w3.org/2007/XMLSchema-versioning";
+
+ /**
+ * Fixed namespace name for SAXON SQL extension: "http://saxon.sf.net/sql"
+ */
+ public static final String SQL = "http://saxon.sf.net/sql";
+
+ /**
+ * Fixed namespace name for EXSLT/Common: "http://exslt.org/common"
+ */
+ public static final String EXSLT_COMMON = "http://exslt.org/common";
+
+ /**
+ * Fixed namespace name for EXSLT/math: "http://exslt.org/math"
+ */
+ public static final String EXSLT_MATH = "http://exslt.org/math";
+
+ /**
+ * Fixed namespace name for EXSLT/sets: "http://exslt.org/sets"
+ */
+ public static final String EXSLT_SETS = "http://exslt.org/sets";
+
+ /**
+ * Fixed namespace name for EXSLT/date: "http://exslt.org/dates-and-times"
+ */
+ public static final String EXSLT_DATES_AND_TIMES = "http://exslt.org/dates-and-times";
+
+ /**
+ * Fixed namespace name for EXSLT/random: "http://exslt.org/random"
+ */
+ public static final String EXSLT_RANDOM = "http://exslt.org/random";
+
+ /**
+ * The standard namespace for functions and operators
+ */
+ public static final String FN = "http://www.w3.org/2005/xpath-functions";
+
+ /**
+ * The standard namespace for XQuery output declarations
+ */
+ public static final String OUTPUT = "http://www.w3.org/2010/xslt-xquery-serialization";
+
+
+ /**
+ * The standard namespace for system error codes
+ */
+ public static final String ERR = "http://www.w3.org/2005/xqt-errors";
+
+
+ /**
+ * Predefined XQuery namespace for local functions
+ */
+ public static final String LOCAL = "http://www.w3.org/2005/xquery-local-functions";
+
+ /**
+ * Math namespace for the XPath 3.0 math functions
+ */
+
+ public static final String MATH = "http://www.w3.org/2005/xpath-functions/math";
+
+ /**
+ * Namespace URI for XPath 3.0 functions associated with maps
+ */
+ public final static String MAP_FUNCTIONS = "http://www.w3.org/2005/xpath-functions/map";
+
+ /**
+ * Namespace URI for XPath 3.0 functions associated with maps as defined in the 2013 XSLT specification
+ */
+ public final static String MAP_FUNCTIONS_2011 = "http://www.w3.org/2011/xpath-functions/map";
+
+ /**
+ * Recognize the Microsoft namespace so we can give a suitably sarcastic error message
+ */
+
+ public static final String MICROSOFT_XSL = "http://www.w3.org/TR/WD-xsl";
+
+ /**
+ * The XHTML namespace http://www.w3.org/1999/xhtml
+ */
+
+ public static final String XHTML = "http://www.w3.org/1999/xhtml";
+
+ /**
+ * The SVG namespace
+ */
+
+ public static final String SVG = "http://www.w3.org/2000/svg";
+
+ /**
+ * The MathML namespace
+ */
+
+ public static final String MATHML = "http://www.w3.org/1998/Math/MathML";
+
+ /**
+ * The XMLNS namespace (used in DOM)
+ */
+
+ public static final String XMLNS = "http://www.w3.org/2000/xmlns/";
+
+ /**
+ * The XLink namespace
+ */
+
+ public static final String XLINK = "http://www.w3.org/1999/xlink";
+
+ /**
+ * The xquery-option namespace for the XQuery 3.0 feature names
+ */
+
+ public static final String XQUERY_OPTIONS = "http://www.w3.org/2011/xquery-options";
+
+ /**
+ * The xquery namespace for the XQuery 3.0 declare option
+ */
+
+ public static final String XQUERY = "http://www.w3.org/2012/xquery";
+
+ /**
+ * Namespace for types representing external Java objects
+ */
+
+ public static final String JAVA_TYPE = "http://saxon.sf.net/java-type";
+
+ /**
+ * Namespace for types representing external .NET objects
+ */
+
+ public static final String DOT_NET_TYPE = "http://saxon.sf.net/clitype";
+
+ /**
+ * Namespace for names allocated to anonymous types. This exists so that
+ * a name fingerprint can be allocated for use as a type annotation.
+ */
+
+ public static final String ANONYMOUS = "http://ns.saxonica.com/anonymous-type";
+
+ /**
+ * Namespace for the Saxon serialization of the schema component model
+ */
+
+ public static final String SCM = "http://ns.saxonica.com/schema-component-model";
+
+ /**
+ * URI identifying the Saxon object model for use in the JAXP 1.3 XPath API
+ */
+
+ public static final String OBJECT_MODEL_SAXON = "http://saxon.sf.net/jaxp/xpath/om";
+
+
+ /**
+ * URI identifying the XOM object model for use in the JAXP 1.3 XPath API
+ */
+
+ public static final String OBJECT_MODEL_XOM = "http://www.xom.nu/jaxp/xpath/xom";
+
+ /**
+ * URI identifying the JDOM object model for use in the JAXP 1.3 XPath API
+ */
+
+ public static final String OBJECT_MODEL_JDOM = "http://jdom.org/jaxp/xpath/jdom";
+
+ /**
+ * URI identifying the AXIOM object model for use in the JAXP 1.3 XPath API
+ */
+
+ // TODO: get an official URI approved
+ public static final String OBJECT_MODEL_AXIOM = "http://ws.apache.org/jaxp/xpath/axiom";
+
+ /**
+ * URI identifying the DOM4J object model for use in the JAXP 1.3 XPath API
+ */
+
+ public static final String OBJECT_MODEL_DOM4J = "http://www.dom4j.org/jaxp/xpath/dom4j";
+
+ /**
+ * URI identifying the .NET DOM object model (not used, but needed for consistency)
+ */
+
+ public static final String OBJECT_MODEL_DOT_NET_DOM = "http://saxon.sf.net/object-model/dotnet/dom";
+
+ /**
+ * URI identifying the Unicode codepoint collation
+ */
+
+ public static final String CODEPOINT_COLLATION_URI = "http://www.w3.org/2005/xpath-functions/collation/codepoint";
+
+ /**
+ * URI for the names of generated global variables
+ */
+
+ public static final String SAXON_GENERATED_GLOBAL = SAXON + "generated-global-variable";
+
+ /**
+ * URI for the Saxon configuration file
+ */
+
+ public static final String SAXON_CONFIGURATION = "http://saxon.sf.net/ns/configuration";
+
+ /**
+ * URI for the EXPath zip module
+ */
+
+ public static final String EXPATH_ZIP = "http://expath.org/ns/zip";
+
+
+ /**
+ * Private constructor: class is never instantiated
+ */
+
+ private NamespaceConstant() {
+ }
+
+ /**
+ * Determine whether a namespace is a reserved namespace
+ *
+ * @param uri the namespace URI to be tested
+ * @return true if this namespace URI is a reserved namespace
+ */
+
+ public static boolean isReserved(/*@Nullable*/ String uri) {
+ return uri != null &&
+ (uri.equals(XSLT) || uri.equals(FN) || uri.equals(XML) || uri.equals(SCHEMA) || uri.equals(SCHEMA_INSTANCE));
+ }
+
+ /**
+ * Determine whether a namespace is a reserved namespace
+ *
+ * @param uri the namespace URI to be tested
+ * @return true if this namespace URI is reserved in XQuery
+ */
+
+ public static boolean isReservedInQuery(String uri) {
+ return uri.equals(FN) ||
+ uri.equals(XML) ||
+ uri.equals(SCHEMA) ||
+ uri.equals(SCHEMA_INSTANCE);
+ }
+
+ /**
+ * Determine whether a namespace is a reserved namespace
+ *
+ * @param uri the namespace URI to be tested
+ * @return true if this namespace URI is reserved in XQuery
+ */
+
+ public static boolean isReservedInQuery30(String uri) {
+ return uri.equals(FN) ||
+ uri.equals(XML) ||
+ uri.equals(SCHEMA) ||
+ uri.equals(SCHEMA_INSTANCE) ||
+ uri.equals(MATH) ||
+ uri.equals(XQUERY) ||
+ uri.equals(XQUERY_OPTIONS);
+ }
+
+ /**
+ * Find a similar namespace to one that is a possible mis-spelling
+ *
+ * @param candidate the possibly mis-spelt namespace
+ * @return the correct spelling of the namespace
+ */
+
+ public static String findSimilarNamespace(String candidate) {
+ if (isSimilar(candidate, XML)) {
+ return XML;
+ } else if (isSimilar(candidate, SCHEMA)) {
+ return SCHEMA;
+ } else if (isSimilar(candidate, XSLT)) {
+ return XSLT;
+ } else if (isSimilar(candidate, SCHEMA_INSTANCE)) {
+ return SCHEMA_INSTANCE;
+ } else if (isSimilar(candidate, FN)) {
+ return FN;
+ } else if (isSimilar(candidate, SAXON)) {
+ return SAXON;
+ } else if (isSimilar(candidate, EXSLT_COMMON)) {
+ return EXSLT_COMMON;
+ } else if (isSimilar(candidate, EXSLT_MATH)) {
+ return EXSLT_MATH;
+ } else if (isSimilar(candidate, EXSLT_DATES_AND_TIMES)) {
+ return EXSLT_DATES_AND_TIMES;
+ } else if (isSimilar(candidate, EXSLT_RANDOM)) {
+ return EXSLT_RANDOM;
+ } else if (isSimilar(candidate, XHTML)) {
+ return XHTML;
+ } else if (isSimilar(candidate, ERR)) {
+ return ERR;
+ } else if (isSimilar(candidate, JAVA_TYPE)) {
+ return JAVA_TYPE;
+ } else if (isSimilar(candidate, DOT_NET_TYPE)) {
+ return DOT_NET_TYPE;
+ } else {
+ return null;
+ }
+ }
+
+ private static boolean isSimilar(String s1, String s2) {
+ if (s1.equalsIgnoreCase(s2)) {
+ return true;
+ } else if (s1.startsWith(s2) && s1.length() - s2.length() < 3) {
+ return true;
+ } else if (s2.startsWith(s1) && s2.length() - s1.length() < 3) {
+ return true;
+ } else if (s1.length() > 8 && Math.abs(s2.length() - s1.length()) < 3) {
+ int diff = 0;
+ for (int i = 0; i < s1.length(); i++) {
+ char c1 = s1.charAt(i);
+ if (!((i < s2.length() && c1 == s2.charAt(i))
+ || (i > 0 && i < s2.length() - 1 && c1 == s2.charAt(i - 1))
+ || (i + 1 < s2.length() && c1 == s2.charAt(i + 1)))) {
+ diff++;
+ }
+ }
+ return diff < 3;
+ } else {
+ return false;
+ }
+ }
+}
+
diff --git a/sf/saxon/lib/Numberer.java b/sf/saxon/lib/Numberer.java
new file mode 100644
index 0000000..0847727
--- /dev/null
+++ b/sf/saxon/lib/Numberer.java
@@ -0,0 +1,146 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.expr.number.NumericGroupFormatter;
+import net.sf.saxon.regex.UnicodeString;
+
+import java.io.Serializable;
+
+/**
+ * Interface Numberer supports number formatting. There is a separate
+ * implementation for each language, e.g. Numberer_en for English.
+ * This supports the xsl:number element
+ * @author Michael H. Kay
+ */
+
+public interface Numberer extends Serializable {
+
+ /**
+ * Set the country used by this numberer (currently used only for names of timezones).
+ * <p>Note: this method is called by the system when allocating a numberer for
+ * a specific language and country. Since numberers are normally shared across threads,
+ * it should not be changed after the initial creation of the Numberer.</p>
+ * @param country The ISO two-letter country code.
+ */
+
+ public void setCountry(String country);
+
+ /**
+ * Get the country used by this numberer
+ * @return The ISO code of the country
+ */
+
+ public String getCountry();
+
+ /**
+ * Format a number into a string
+ * @param number The number to be formatted
+ * @param picture The format token. This is a single component of the format attribute
+ * of xsl:number, e.g. "1", "01", "i", or "a"
+ * @param groupSize number of digits per group (0 implies no grouping)
+ * @param groupSeparator string to appear between groups of digits
+ * @param letterValue The letter-value specified to xsl:number: "alphabetic" or
+ * "traditional". Can also be an empty string or null.
+ * @param ordinal The value of the ordinal attribute specified to xsl:number
+ * The value "yes" indicates that ordinal numbers should be used; "" or null indicates
+ * that cardinal numbers
+ * @return the formatted number. Note that no errors are reported; if the request
+ * is invalid, the number is formatted as if the string() function were used.
+ */
+
+ public String format(long number,
+ UnicodeString picture,
+ int groupSize,
+ String groupSeparator,
+ String letterValue,
+ String ordinal);
+
+ /**
+ * Format a number into a string
+ *
+ * @param number The number to be formatted
+ * @param picture The format token. This is a single component of the format attribute
+ * of xsl:number, e.g. "1", "01", "i", or "a"
+ * @param numGrpFormatter an object that handles insertion of grouping separators into the formatted number
+ * @param letterValue The letter-value specified to xsl:number: "alphabetic" or
+ * "traditional". Can also be an empty string or null.
+ * @param ordinal The value of the ordinal attribute specified to xsl:number
+ * The value "yes" indicates that ordinal numbers should be used; "" or null indicates
+ * that cardinal numbers
+ * @return the formatted number. Note that no errors are reported; if the request
+ * is invalid, the number is formatted as if the string() function were used.
+ */
+
+ public String format(long number,
+ UnicodeString picture,
+ NumericGroupFormatter numGrpFormatter,
+ String letterValue,
+ String ordinal);
+
+ /**
+ * Get a month name or abbreviation
+ * @param month The month number (1=January, 12=December)
+ * @param minWidth The minimum number of characters
+ * @param maxWidth The maximum number of characters
+ * @return the month name or abbreviation as a string (for example, "September" or "Sep")
+ */
+
+ public String monthName(int month, int minWidth, int maxWidth);
+
+ /**
+ * Get a day name or abbreviation
+ * @param day The month number (1=Monday, 7=Sunday)
+ * @param minWidth The minimum number of characters
+ * @param maxWidth The maximum number of characters
+ * @return the day name or abbreviation as a string (for example, "Monday" or "Mon")
+ */
+
+ public String dayName(int day, int minWidth, int maxWidth);
+
+ /**
+ * Get an am/pm indicator
+ * @param minutes the minutes within the day
+ * @param minWidth minimum width of output
+ * @param maxWidth maximum width of output
+ * @return the AM or PM indicator
+ */
+
+ public String halfDayName(int minutes,
+ int minWidth, int maxWidth);
+
+ /**
+ * Get an ordinal suffix for a particular component of a date/time.
+ * @param component the component specifier from a format-dateTime picture, for
+ * example "M" for the month or "D" for the day.
+ * @return a string that is acceptable in the ordinal attribute of xsl:number
+ * to achieve the required ordinal representation. For example, "-e" for the day component
+ * in German, to have the day represented as "dritte August".
+ */
+
+ public String getOrdinalSuffixForDateTime(String component);
+
+ /**
+ * Get the name for an era (e.g. "BC" or "AD")
+ * @param year the proleptic gregorian year, using "0" for the year before 1AD
+ * @return the name of the era, for example "AD"
+ */
+
+ public String getEraName(int year);
+
+ /**
+ * Get the name of a calendar
+ * @param code The code representing the calendar as in the XSLT 2.0 spec, e.g. AD for the Gregorian calendar
+ * @return the name of the calendar, for example "AD"
+ */
+
+ public String getCalendarName(String code);
+
+
+}
+
diff --git a/sf/saxon/lib/OutputURIResolver.java b/sf/saxon/lib/OutputURIResolver.java
new file mode 100644
index 0000000..e4f1d73
--- /dev/null
+++ b/sf/saxon/lib/OutputURIResolver.java
@@ -0,0 +1,73 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+import javax.xml.transform.Result;
+import javax.xml.transform.TransformerException;
+
+
+/**
+* This interface defines an OutputURIResolver. This is a counterpart to the JAXP
+* URIResolver, but is used to map the URI of a secondary result document to a Result object
+* which acts as the destination for the new document.
+* @author Michael H. Kay
+*/
+
+public interface OutputURIResolver {
+
+ /**
+ * Get an instance of this OutputURIResolver class.
+ * <p>This method is called every time an xsl:result-document instruction
+ * is evaluated (with an href attribute). The resolve() and close() methods
+ * will be called on the returned instance.</p>
+ * <p>If the OutputURIResolver is stateless (that is, it retains no information
+ * between resolve() and close()), then the same instance can safely be returned
+ * each time. For a stateful OutputURIResolver, it must either take care to be
+ * thread-safe (handling multiple invocations of xsl:result-document concurrently),
+ * or it must return a fresh instance of itself for each call.</p>
+ */
+
+ public OutputURIResolver newInstance();
+
+ /**
+ * Resolve an output URI.
+ * @param href The relative URI of the output document. This corresponds to the
+ * href attribute of the xsl:result-document instruction.
+ * @param base The base URI that should be used. This is the Base Output URI, typically
+ * the URI of the principal output document
+ * @return a Result object representing the destination for the XML document. The
+ * method can also return null, in which case the standard output URI resolver
+ * will be used to create a Result object.
+ * <p>The systemId property of the returned Result object should normally be the
+ * result of resolving href (as a relative URI) against the value of base.
+ * This systemId is used to enforce the error
+ * conditions in the XSLT specification that disallow writing two result trees to the
+ * same destination, or reading from a destination that is written to in the same
+ * transformation. Setting the systemId to null, or to a deceptive value, will defeat
+ * these error checks (which can sometimes be useful). Equally, setting the systemId
+ * to the same value on repeated calls when different href/base arguments are supplied
+ * will cause a spurious error.</p>
+ * @throws javax.xml.transform.TransformerException if any error occurs
+ */
+
+ public Result resolve(String href, String base) throws TransformerException;
+
+ /**
+ * Signal completion of the result document. This method is called by the system
+ * when the result document has been successfully written. It allows the resolver
+ * to perform tidy-up actions such as closing output streams, or firing off
+ * processes that take this result tree as input. Note that the OutputURIResolver
+ * is stateless, so the the original Result object is supplied to identify the document
+ * that has been completed.
+ * @param result The result object returned by the previous call of resolve()
+ * @throws javax.xml.transform.TransformerException if any error occurs
+ */
+
+ public void close(Result result) throws TransformerException;
+
+}
+
diff --git a/sf/saxon/lib/ParseOptions.java b/sf/saxon/lib/ParseOptions.java
new file mode 100644
index 0000000..84bbd43
--- /dev/null
+++ b/sf/saxon/lib/ParseOptions.java
@@ -0,0 +1,800 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.FilterFactory;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.TreeModel;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.ValidationParams;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * This class defines options for parsing and/or validating a source document. Some of the options
+ * are relevant only when parsing, some only when validating, but they are combined into a single
+ * class because the two operations are often performed together.
+ */
+
+public class ParseOptions implements Serializable {
+
+ private int schemaValidation = Validation.DEFAULT;
+ private int dtdValidation = Validation.DEFAULT;
+ private NodeName topLevelElement;
+ private SchemaType topLevelType;
+ /*@Nullable*/ private transient XMLReader parser = null;
+ /*@Nullable*/ private Boolean wrapDocument = null;
+ /*@Nullable*/ private TreeModel treeModel = null;
+ private int stripSpace = Whitespace.UNSPECIFIED;
+ /*@Nullable*/ private Boolean lineNumbering = null;
+ /*@Nullable*/ private Boolean xIncludeAware = null;
+ private boolean pleaseClose = false;
+ /*@Nullable*/ private transient ErrorListener errorListener = null;
+ /*@Nullable*/ private transient EntityResolver entityResolver = null;
+ /*@Nullable*/ private transient ErrorHandler errorHandler = null;
+ /*@Nullable*/ private List<FilterFactory> filters = null;
+ private boolean sourceIsXQJ = false;
+ private boolean continueAfterValidationErrors = false;
+ private boolean addCommentsAfterValidationErrors = false;
+ private boolean expandAttributeDefaults = true;
+ private boolean useXsiSchemaLocation = true;
+ private boolean checkEntityReferences = false;
+ private int validationErrorLimit = Integer.MAX_VALUE;
+ /*@Nullable*/ private ValidationParams validationParams = null;
+ /*@Nullable*/ private ValidationStatisticsRecipient validationStatisticsRecipient = null;
+
+ /**
+ * Create a ParseOptions object with default options set
+ */
+
+ public ParseOptions() {
+ //entityResolver = new StandardEntityResolver();
+ }
+
+ /**
+ * Create a ParseOptions object as a copy of another ParseOptions
+ * @param p the ParseOptions to be copied
+ */
+
+ public ParseOptions(/*@NotNull*/ ParseOptions p) {
+ schemaValidation = p.schemaValidation;
+ validationParams = p.validationParams;
+ dtdValidation = p.dtdValidation;
+ topLevelElement = p.topLevelElement;
+ topLevelType = p.topLevelType;
+ parser = p.parser;
+ wrapDocument = p.wrapDocument;
+ treeModel = p.treeModel;
+ stripSpace = p.stripSpace;
+ lineNumbering = p.lineNumbering;
+ xIncludeAware = p.xIncludeAware;
+ pleaseClose = p.pleaseClose;
+ errorListener = p.errorListener;
+ entityResolver = p.entityResolver;
+ if (p.filters != null) {
+ filters = new ArrayList<FilterFactory>(p.filters);
+ }
+ sourceIsXQJ = p.sourceIsXQJ;
+ expandAttributeDefaults = p.expandAttributeDefaults;
+ useXsiSchemaLocation = p.useXsiSchemaLocation;
+ validationErrorLimit = p.validationErrorLimit;
+ continueAfterValidationErrors = p.continueAfterValidationErrors;
+ addCommentsAfterValidationErrors = p.addCommentsAfterValidationErrors;
+ }
+
+ /**
+ * Merge another set of parseOptions into these parseOptions
+ * @param options the other parseOptions. If both are present,
+ * the other parseOptions take precedence
+ */
+
+ public void merge(/*@NotNull*/ ParseOptions options) {
+ if (options.dtdValidation != Validation.DEFAULT) {
+ dtdValidation = options.dtdValidation;
+ }
+ if (options.schemaValidation != Validation.DEFAULT) {
+ schemaValidation = options.schemaValidation;
+ }
+ if (options.topLevelElement != null) {
+ topLevelElement = options.topLevelElement;
+ }
+ if (options.topLevelType != null) {
+ topLevelType = options.topLevelType;
+ }
+ if (options.parser != null) {
+ parser = options.parser;
+ }
+ if (options.wrapDocument != null) {
+ wrapDocument = options.wrapDocument;
+ }
+ if (options.treeModel != null) {
+ treeModel = options.treeModel;
+ }
+ if (options.stripSpace != Whitespace.UNSPECIFIED) {
+ stripSpace = options.stripSpace;
+ }
+ if (options.lineNumbering != null) {
+ lineNumbering = options.lineNumbering;
+ }
+ if (options.xIncludeAware != null) {
+ xIncludeAware = options.xIncludeAware;
+ }
+ if (options.pleaseClose) {
+ pleaseClose = true;
+ }
+ if (options.errorListener != null) {
+ errorListener = options.errorListener;
+ }
+ if (options.entityResolver != null) {
+ entityResolver = options.entityResolver;
+ }
+ if (options.filters != null) {
+ if (filters == null) {
+ filters = new ArrayList<FilterFactory>();
+ }
+ filters.addAll(options.filters);
+ }
+ if (options.sourceIsXQJ) {
+ sourceIsXQJ = true;
+ }
+ if (!options.expandAttributeDefaults) {
+ // expand defaults unless the other options says don't
+ expandAttributeDefaults = false;
+ }
+ if (!options.useXsiSchemaLocation) {
+ // expand defaults unless the other options says don't
+ useXsiSchemaLocation = false;
+ }
+ if (options.addCommentsAfterValidationErrors) {
+ // add comments if either set of options requests it
+ addCommentsAfterValidationErrors = true;
+ }
+ validationErrorLimit = java.lang.Math.min(validationErrorLimit, options.validationErrorLimit);
+ }
+
+ /**
+ * Merge settings from the Configuration object into these parseOptions
+ * @param config the Configuration. Settings from the Configuration are
+ * used only where no setting is present in this ParseOptions object
+ */
+
+ public void applyDefaults(/*@NotNull*/ Configuration config) {
+ if (dtdValidation == Validation.DEFAULT) {
+ dtdValidation = config.isValidation() ? Validation.STRICT : Validation.SKIP;
+ }
+ if (schemaValidation == Validation.DEFAULT) {
+ schemaValidation = config.getSchemaValidationMode();
+ }
+ if (treeModel == null) {
+ treeModel = TreeModel.getTreeModel(config.getTreeModel());
+ }
+ if (stripSpace == Whitespace.UNSPECIFIED) {
+ stripSpace = config.getStripsWhiteSpace();
+ }
+ if (lineNumbering == null) {
+ lineNumbering = config.isLineNumbering();
+ }
+ if (xIncludeAware == null) {
+ xIncludeAware = config.isXIncludeAware();
+ }
+ if (errorListener == null) {
+ errorListener = config.getErrorListener();
+ }
+
+ }
+
+ /**
+ * Add a filter to the list of filters to be applied to the raw input
+ * @param filterFactory the filterFactory to be added
+ */
+
+ public void addFilter(FilterFactory filterFactory) {
+ if (filters == null) {
+ filters = new ArrayList<FilterFactory>(5);
+ }
+ filters.add(filterFactory);
+ }
+
+ /**
+ * Get the list of filters to be applied to the input. Returns null if there are no filters.
+ * @return the list of filters, if there are any
+ */
+
+ /*@Nullable*/ public List<FilterFactory> getFilters() {
+ return filters;
+ }
+
+ /**
+ * Set the space-stripping action to be applied to the source document
+ * @param stripAction one of {@link net.sf.saxon.value.Whitespace#IGNORABLE},
+ * {@link net.sf.saxon.value.Whitespace#ALL}, or {@link net.sf.saxon.value.Whitespace#NONE}
+ */
+
+ public void setStripSpace(int stripAction) {
+ stripSpace = stripAction;
+ }
+
+ /**
+ * Get the space-stripping action to be applied to the source document
+ * @return one of {@link net.sf.saxon.value.Whitespace#IGNORABLE},
+ * {@link net.sf.saxon.value.Whitespace#ALL}, or {@link net.sf.saxon.value.Whitespace#NONE}
+ */
+
+ public int getStripSpace() {
+ return (stripSpace == Whitespace.UNSPECIFIED ? Whitespace.IGNORABLE : stripSpace);
+ }
+
+ /**
+ * Set the tree model to use. Default is the tiny tree
+ * @param model one of {@link net.sf.saxon.event.Builder#TINY_TREE},
+ * {@link net.sf.saxon.event.Builder#LINKED_TREE} or {@link net.sf.saxon.event.Builder#TINY_TREE_CONDENSED}
+ */
+
+ public void setTreeModel(int model) {
+ treeModel = TreeModel.getTreeModel(model);
+ }
+
+ /**
+ * Get the tree model that will be used.
+ * @return one of {@link net.sf.saxon.event.Builder#TINY_TREE}, {@link net.sf.saxon.event.Builder#LINKED_TREE},
+ * or {@link net.sf.saxon.event.Builder#TINY_TREE_CONDENSED},
+ * or {link Builder#UNSPECIFIED_TREE_MODEL} if no call on setTreeModel() has been made
+ */
+
+ public int getTreeModel() {
+ if (treeModel == null) {
+ return Builder.UNSPECIFIED_TREE_MODEL;
+ }
+ return treeModel.getSymbolicValue();
+ }
+
+ /**
+ * Set the tree model to use. Default is the tiny tree
+ * @param model typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link TreeModel#TINY_TREE_CONDENSED}, or {@link TreeModel#LINKED_TREE}. However, in principle
+ * a user-defined tree model can be used.
+ * @since 9.2
+ */
+
+ public void setModel(TreeModel model) {
+ treeModel = model;
+ }
+
+ /**
+ * Get the tree model that will be used.
+ * @return typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link TreeModel#TINY_TREE_CONDENSED}, or {@link TreeModel#LINKED_TREE}. However, in principle
+ * a user-defined tree model can be used.
+ */
+
+ public TreeModel getModel() {
+ return treeModel==null ? TreeModel.TINY_TREE : treeModel;
+ }
+
+
+
+ /**
+ * Set whether or not schema validation of this source is required
+ * @param option one of {@link Validation#STRICT},
+ * {@link Validation#LAX}, {@link Validation#STRIP},
+ * {@link Validation#PRESERVE}, {@link Validation#DEFAULT}
+ */
+
+ public void setSchemaValidationMode(int option) {
+ schemaValidation = option;
+ }
+
+ /**
+ * Get whether or not schema validation of this source is required
+ * @return the validation mode requested, or {@link Validation#DEFAULT}
+ * to use the default validation mode from the Configuration.
+
+ */
+
+ public int getSchemaValidationMode() {
+ return schemaValidation;
+ }
+
+ /**
+ * Set whether to expand default attributes defined in a DTD or schema.
+ * By default, default attribute values are expanded
+ * @param expand true if missing attribute values are to take the default value
+ * supplied in a DTD or schema, false if they are to be left as absent
+ */
+
+ public void setExpandAttributeDefaults(boolean expand) {
+ this.expandAttributeDefaults = expand;
+ }
+
+ /**
+ * Ask whether to expand default attributes defined in a DTD or schema.
+ * By default, default attribute values are expanded
+ * @return true if missing attribute values are to take the default value
+ * supplied in a DTD or schema, false if they are to be left as absent
+ */
+
+ public boolean isExpandAttributeDefaults() {
+ return expandAttributeDefaults;
+ }
+
+ /**
+ * Set the name of the top-level element for validation.
+ * If a top-level element is set then the document
+ * being validated must have this as its outermost element
+ * @param elementName the QName of the required top-level element, or null to unset the value
+ */
+
+ public void setTopLevelElement(NodeName elementName) {
+ topLevelElement = elementName;
+ }
+
+ /**
+ * Get the name of the top-level element for validation.
+ * If a top-level element is set then the document
+ * being validated must have this as its outermost element
+ * @return the QName of the required top-level element, or null if no value is set
+ * @since 9.0
+ */
+
+ public NodeName getTopLevelElement() {
+ return topLevelElement;
+ }
+
+ /**
+ * Set the type of the top-level element for validation.
+ * If this is set then the document element is validated against this type
+ * @param type the schema type required for the document element, or null to unset the value
+ */
+
+ public void setTopLevelType(SchemaType type) {
+ topLevelType = type;
+ }
+
+ /**
+ * Get the type of the document element for validation.
+ * If this is set then the document element of the document
+ * being validated must have this type
+ * @return the type of the required top-level element, or null if no value is set
+ */
+
+ public SchemaType getTopLevelType() {
+ return topLevelType;
+ }
+
+ /**
+ * Set whether or not to use the xsi:schemaLocation and xsi:noNamespaceSchemaLocation attributes
+ * in an instance document to locate a schema for validation. Note, these attribute are only used
+ * if validation is requested.
+ * @param use true if these attributes are to be used, false if they are to be ignored
+ */
+
+ public void setUseXsiSchemaLocation(boolean use) {
+ useXsiSchemaLocation = use;
+ }
+
+ /**
+ * Ask whether or not to use the xsi:schemaLocation and xsi:noNamespaceSchemaLocation attributes
+ * in an instance document to locate a schema for validation. Note, these attribute are only used
+ * if validation is requested.
+ * @return true (the default) if these attributes are to be used, false if they are to be ignored
+ */
+
+ public boolean isUseXsiSchemaLocation() {
+ return useXsiSchemaLocation;
+ }
+
+ /**
+ * Get the limit on the number of errors reported before schema validation is abandoned. Default
+ * is unlimited (Integer.MAX_VALUE)
+ * @return the limit on the number of errors
+ */
+
+ public int getValidationErrorLimit() {
+ return validationErrorLimit;
+ }
+
+ /**
+ * Set a limit on the number of errors reported before schema validation is abandoned. Default
+ * is unlimited (Integer.MAX_VALUE). If set to one, validation is terminated as soon as a single
+ * validation error is detected.
+ * @param validationErrorLimit the limit on the number of errors
+ */
+
+ public void setValidationErrorLimit(int validationErrorLimit) {
+ this.validationErrorLimit = validationErrorLimit;
+ }
+
+ /**
+ * Set whether or not DTD validation of this source is required
+ * @param option one of {@link Validation#STRICT}, {@link Validation#LAX},
+ * {@link Validation#STRIP}, {@link Validation#DEFAULT}.
+ *
+ * <p>The value {@link Validation#LAX} indicates that DTD validation is
+ * requested, but validation failures are treated as warnings only.</p>
+ */
+
+ public void setDTDValidationMode(int option) {
+ dtdValidation = option;
+ }
+
+ /**
+ * Get whether or not DTD validation of this source is required
+ * @return the validation mode requested, or {@link Validation#DEFAULT}
+ * to use the default validation mode from the Configuration.
+ *
+ * <p>The value {@link Validation#LAX} indicates that DTD validation is
+ * requested, but validation failures are treated as warnings only.</p>
+ */
+
+ public int getDTDValidationMode() {
+ return dtdValidation;
+ }
+
+ /**
+ * Say that statistics of component usage are maintained during schema validation, and indicate where
+ * they should be sent
+ * @param recipient the agent to be notified of the validation statistics on completion of the
+ * validation episode, May be set to null if no agent is to be notified.
+ */
+
+ public void setValidationStatisticsRecipient(/*@Nullable*/ ValidationStatisticsRecipient recipient) {
+ validationStatisticsRecipient = recipient;
+ }
+
+ /**
+ * Ask whether statistics of component usage are maintained during schema validation,
+ * and where they will be sent
+ * @return the agent to be notified of the validation statistics on completion of the
+ * validation episode, or null if none has been nominated
+ */
+
+ /*@Nullable*/ public ValidationStatisticsRecipient getValidationStatisticsRecipient() {
+ return validationStatisticsRecipient;
+ }
+
+ /**
+ * Set whether line numbers are to be maintained in the constructed document
+ * @param lineNumbering true if line numbers are to be maintained
+ */
+
+ public void setLineNumbering(boolean lineNumbering) {
+ this.lineNumbering = lineNumbering;
+ }
+
+ /**
+ * Get whether line numbers are to be maintained in the constructed document
+ * @return true if line numbers are maintained
+ */
+
+ public boolean isLineNumbering() {
+ return lineNumbering != null && lineNumbering;
+ }
+
+ /**
+ * Determine whether setLineNumbering() has been called
+ * @return true if setLineNumbering() has been called
+ */
+
+ public boolean isLineNumberingSet() {
+ return lineNumbering != null;
+ }
+
+ /**
+ * Set the SAX parser (XMLReader) to be used
+ * @param parser the SAX parser
+ */
+
+ public void setXMLReader(XMLReader parser) {
+ this.parser = parser;
+ }
+
+ /**
+ * Get the SAX parser (XMLReader) to be used
+ * @return the parser
+ */
+
+ /*@Nullable*/ public XMLReader getXMLReader() {
+ return parser;
+ }
+
+ /**
+ * Set an EntityResolver to be used when parsing. Note that this will not be used if an XMLReader
+ * has been supplied (in that case, the XMLReader should be initialized with the EntityResolver
+ * already set.)
+ * @param resolver the EntityResolver to be used. May be null, in which case any existing
+ * EntityResolver is removed from the options
+ */
+
+ public void setEntityResolver(/*@Nullable*/ EntityResolver resolver) {
+ entityResolver = resolver;
+ }
+
+ /**
+ * Get the EntityResolver that will be used when parsing
+ * @return the EntityResolver, if one has been set using {@link #setEntityResolver},
+ * otherwise null.
+ */
+
+ /*@Nullable*/ public EntityResolver getEntityResolver() {
+ return entityResolver;
+ }
+
+ /**
+ * Set an ErrorHandler to be used when parsing. Note that this will not be used if an XMLReader
+ * has been supplied (in that case, the XMLReader should be initialized with the ErrorHandler
+ * already set.)
+ * @param handler the ErrorHandler to be used, or null to indicate that no ErrorHandler is to be used.
+ */
+
+ public void setErrorHandler(/*@Nullable*/ ErrorHandler handler) {
+ errorHandler = handler;
+ }
+
+ /**
+ * Get the ErrorHandler that will be used when parsing
+ * @return the ErrorHandler, if one has been set using {@link #setErrorHandler},
+ * otherwise null.
+ */
+
+ /*@Nullable*/ public ErrorHandler getErrorHandler() {
+ return errorHandler;
+ }
+
+
+ /**
+ * Assuming that the contained Source is a node in a tree, indicate whether a tree should be created
+ * as a view of this supplied tree, or as a copy.
+ * @param wrap if true, the node in the supplied Source is wrapped, to create a view. If false, the node
+ * and its contained subtree is copied. If null, the system default is chosen.
+ */
+
+ public void setWrapDocument(/*@Nullable*/ Boolean wrap) {
+ wrapDocument = wrap;
+ }
+
+ /**
+ Assuming that the contained Source is a node in a tree, determine whether a tree will be created
+ * as a view of this supplied tree, or as a copy.
+ * @return if true, the node in the supplied Source is wrapped, to create a view. If false, the node
+ * and its contained subtree is copied. If null, the system default is chosen.
+ * @since 8.8
+ */
+
+ /*@Nullable*/ public Boolean getWrapDocument() {
+ return wrapDocument;
+ }
+
+ /**
+ * <p>Set state of XInclude processing.</p>
+ * <p/>
+ * <p>If XInclude markup is found in the document instance, should it be
+ * processed as specified in <a href="http://www.w3.org/TR/xinclude/">
+ * XML Inclusions (XInclude) Version 1.0</a>.</p>
+ * <p/>
+ * <p>XInclude processing defaults to <code>false</code>.</p>
+ *
+ * @param state Set XInclude processing to <code>true</code> or
+ * <code>false</code>
+ * @since 8.9
+ */
+ public void setXIncludeAware(boolean state) {
+ xIncludeAware = state;
+ }
+
+ /**
+ * <p>Determine whether setXIncludeAware() has been called.</p>
+ *
+ * @return true if setXIncludeAware() has been called
+ */
+
+ public boolean isXIncludeAwareSet() {
+ return (xIncludeAware != null);
+ }
+
+ /**
+ * <p>Get state of XInclude processing.</p>
+ *
+ * @return current state of XInclude processing. Default value is false.
+ */
+
+ public boolean isXIncludeAware() {
+ return xIncludeAware != null && xIncludeAware;
+ }
+
+ /**
+ * Set an ErrorListener to be used when parsing
+ * @param listener the ErrorListener to be used; or null, to indicate that the standard ErrorListener is to be used
+ */
+
+ public void setErrorListener(/*@Nullable*/ ErrorListener listener) {
+ errorListener = listener;
+ }
+
+ /**
+ * Get the ErrorListener that will be used when parsing
+ * @return the ErrorListener, if one has been set using {@link #setErrorListener},
+ * otherwise null.
+ */
+
+ /*@Nullable*/ public ErrorListener getErrorListener() {
+ return errorListener;
+ }
+
+ /**
+ * Say that processing should continue after a validation error. Note that all validation
+ * errors are reported to the error() method of the ErrorListener, and processing always
+ * continues except when this method chooses to throw an exception. At the end of the document,
+ * a fatal error is thrown if (a) there have been any validation errors, and (b) this option
+ * is not set.
+ * @param keepGoing true if processing should continue
+ */
+
+ public void setContinueAfterValidationErrors(boolean keepGoing) {
+ continueAfterValidationErrors = keepGoing;
+ }
+
+ /**
+ * Ask whether processing should continue after a validation error. Note that all validation
+ * errors are reported to the error() method of the ErrorListener, and processing always
+ * continues except when this method chooses to throw an exception. At the end of the document,
+ * a fatal error is thrown if (a) there have been any validation errors, and (b) this option
+ * is not set.
+ * @return true if processing should continue
+ */
+
+ public boolean isContinueAfterValidationErrors() {
+ return continueAfterValidationErrors;
+ }
+
+ /**
+ * Say that on validation errors, messages explaining the error should (where possible)
+ * be written as comments in the validated source document. This option is only relevant when
+ * processing continues after a validation error
+ * @param keepGoing true if comments should be added
+ * @since 9.3. Default is now false; in previous releases this option was always on.
+ */
+
+ public void setAddCommentsAfterValidationErrors(boolean keepGoing) {
+ addCommentsAfterValidationErrors = keepGoing;
+ }
+
+ /**
+ * Ask whether on validation errors, messages explaining the error should (where possible)
+ * be written as comments in the validated source document. This option is only relevant when
+ * processing continues after a validation error
+ * @return true if comments should be added
+ * @since 9.3
+ */
+
+ public boolean isAddCommentsAfterValidationErrors() {
+ return addCommentsAfterValidationErrors;
+ }
+
+ /**
+ * Set the validation parameters. These are the values of variables declared in the schema
+ * using the saxon:param extension, and referenced in XSD assertions (or CTA expressions)
+ * associated with user-defined types
+ * @param params the validation parameters
+ */
+
+ public void setValidationParams(ValidationParams params) {
+ validationParams = params;
+ }
+
+ /**
+ * Get the validation parameters. These are the values of variables declared in the schema
+ * using the saxon:param extension, and referenced in XSD assertions (or CTA expressions)
+ * associated with user-defined types
+ * @return the validation parameters
+ */
+
+ public ValidationParams getValidationParams() {
+ return validationParams;
+ }
+
+
+ /**
+ * Say whether to check elements and attributes of type xs:ENTITY (or xs:ENTITIES)
+ * against the unparsed entities declared in the document's DTD. This is normally
+ * true when performing standalone schema validation, false when invoking validation
+ * from XSLT or XQuery.
+ * @param check true if entities are to be checked, false otherwise
+ */
+
+ public void setCheckEntityReferences(boolean check) {
+ this.checkEntityReferences = check;
+ }
+
+ /**
+ * Ask whether to check elements and attributes of type xs:ENTITY (or xs:ENTITIES)
+ * against the unparsed entities declared in the document's DTD. This is normally
+ * true when performing standalone schema validation, false when invoking validation
+ * from XSLT or XQuery.
+ * @return true if entities are to be checked, false otherwise
+ */
+
+ public boolean isCheckEntityReferences() {
+ return this.checkEntityReferences;
+ }
+
+
+ /**
+ * Set whether or not the user of this Source is encouraged to close it as soon as reading is finished.
+ * Normally the expectation is that any Stream in a StreamSource will be closed by the component that
+ * created the Stream. However, in the case of a Source returned by a URIResolver, there is no suitable
+ * interface (the URIResolver has no opportunity to close the stream). Also, in some cases such as reading
+ * of stylesheet modules, it is possible to close the stream long before control is returned to the caller
+ * who supplied it. This tends to make a difference on .NET, where a file often can't be opened if there
+ * is a stream attached to it.
+ * @param close true if the source should be closed as soon as it has been consumed
+ */
+
+ public void setPleaseCloseAfterUse(boolean close) {
+ pleaseClose = close;
+ }
+
+ /**
+ * Determine whether or not the user of this Source is encouraged to close it as soon as reading is
+ * finished.
+ * @return true if the source should be closed as soon as it has been consumed
+ */
+
+ public boolean isPleaseCloseAfterUse() {
+ return pleaseClose;
+ }
+
+ /**
+ * Close any resources held by a given Source. This only works if the underlying Source is one that is
+ * recognized as holding closable resources.
+ * @param source the source to be closed
+ * @since 9.2
+ */
+
+ public static void close(/*@NotNull*/ Source source) {
+ try {
+ if (source instanceof StreamSource) {
+ StreamSource ss = (StreamSource)source;
+ if (ss.getInputStream() != null) {
+ ss.getInputStream().close();
+ }
+ if (ss.getReader() != null) {
+ ss.getReader().close();
+ }
+ } else if (source instanceof SAXSource) {
+ InputSource is = ((SAXSource)source).getInputSource();
+ if (is != null) {
+ if (is.getByteStream() != null) {
+ is.getByteStream().close();
+ }
+ if (is.getCharacterStream() != null) {
+ is.getCharacterStream().close();
+ }
+ }
+ }
+ } catch (IOException err) {
+ // no action
+ }
+ }
+
+}
+
diff --git a/sf/saxon/lib/RelativeURIResolver.java b/sf/saxon/lib/RelativeURIResolver.java
new file mode 100644
index 0000000..565a324
--- /dev/null
+++ b/sf/saxon/lib/RelativeURIResolver.java
@@ -0,0 +1,98 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+
+/**
+ * The standard JAXP URIResolver is given a relative URI and a base URI and returns the resource
+ * identified by this combination. However, to support a stable implementation of the doc() function,
+ * Saxon needs to know what the absolute URI is before the resource is fetched, so it can determine whether
+ * a document with that absolute URI actually exists.
+ * <p>
+ * This extended interface defines a URIResolver that separates the two functions of resolving a relative URI
+ * against a base URI, and fetching a resource with that absolute URI. If the URI resolver supplied to Saxon
+ * implements this interface, the absolute URI associated with a loaded document will be the URI returned by
+ * this resolver.
+ * <p>
+ * The particular motivation for providing this interface is to allow a URIResolver to wrap a .NET XmlResolver,
+ * which has additional capability not present in the JAXP interface.
+ */
+
+public interface RelativeURIResolver extends URIResolver {
+
+ /**
+ * Create an absolute URI from a relative URI and a base URI. This method performs the
+ * process which is correctly called "URI resolution": this is purely a syntactic operation
+ * on the URI strings, and does not retrieve any resources.
+ * @param href A relative or absolute URI, to be resolved against the specified base URI
+ * @param base The base URI against which the first argument will be made
+ * absolute if the absolute URI is required.
+ * @return A string containing the absolute URI that results from URI resolution. If the resource
+ * needs to be fetched, this absolute URI will be supplied as the href parameter in a subsequent
+ * call to the <code>resolve</code> method.
+ * @throws javax.xml.transform.TransformerException if any failure occurs
+ */
+
+ public String makeAbsolute(String href, String base)
+ throws TransformerException;
+
+ /**
+ * Called by the processor when it encounters
+ * an xsl:include, xsl:import, or document() function.
+ *
+ * @param uri The absolute URI to be dereferenced
+ *
+ * @return A Source object, or null if the href cannot be dereferenced,
+ * and the processor should try to resolve the URI itself.
+ *
+ * @throws javax.xml.transform.TransformerException if an error occurs when trying to
+ * resolve the URI.
+ */
+ public Source dereference(String uri)
+ throws TransformerException;
+
+ /**
+ * Called by the processor when it encounters
+ * an xsl:include, xsl:import, or document() function.
+ *
+ * <p>Despite the name, the main purpose of this method is to dereference the URI, not merely
+ * to resolve it.</p>
+ *
+ * <p>This method is provided because it is required by the interface. When using a RelativeURIResolver,
+ * the single-argument dereference() method is preferred. The result of calling this method should be the
+ * same as the result of calling <code>dereference(makeAbsolute(href, base))</code></p>
+ *
+ * @param href An href attribute, which may be relative or absolute.
+ * @param base The base URI against which the first argument will be made
+ * absolute if the absolute URI is required.
+ *
+ * @return A Source object, or null if the href cannot be resolved,
+ * and the processor should try to resolve the URI itself.
+ *
+ * @throws javax.xml.transform.TransformerException if an error occurs when trying to
+ * resolve the URI.
+ */
+ public Source resolve(String href, String base)
+ throws TransformerException;
+
+ /**
+ * Specify the media type of the resource that is expected to be delivered. This information is
+ * supplied by the processor primarily to indicate whether the URIResolver is allowed to return
+ * an XML tree already parsed. If the value is "text/plain" then the Source returned by the
+ * resolve() method should be a StreamSource.
+ * @param mediaType the expected media type
+ */
+
+ public void setExpectedMediaType(String mediaType);
+ // TODO: method is not currently used
+
+}
+
diff --git a/sf/saxon/lib/SaxonOutputKeys.java b/sf/saxon/lib/SaxonOutputKeys.java
new file mode 100644
index 0000000..c822c2f
--- /dev/null
+++ b/sf/saxon/lib/SaxonOutputKeys.java
@@ -0,0 +1,543 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.Name11Checker;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.QNameException;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ValidationException;
+import net.sf.saxon.value.DecimalValue;
+
+import javax.xml.transform.OutputKeys;
+import java.math.BigDecimal;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+/**
+ * Provides string constants that can be used to set
+ * output properties for a Transformer, or to retrieve
+ * output properties from a Transformer or Templates object.
+ * <p/>
+ * These keys are private Saxon keys that supplement the standard keys
+ * defined in javax.xml.transform.OutputKeys. As well as Saxon extension
+ * attributes, the list includes new attributes defined in XSLT 2.0 which
+ * are not yet supported in JAXP
+ */
+
+public class SaxonOutputKeys {
+
+ /**
+ * This class is not instantiated
+ */
+
+ private SaxonOutputKeys() {
+ }
+
+ /**
+ * String constant representing the saxon:xquery output method name
+ */
+
+ /*@NotNull*/ public static final String SAXON_XQUERY_METHOD = "{http://saxon.sf.net/}xquery";
+
+ /**
+ * String constant representing the saxon:base64Binary output method name
+ */
+
+ /*@NotNull*/ public static final String SAXON_BASE64_BINARY_METHOD = "{http://saxon.sf.net/}base64Binary";
+
+ /**
+ * String constant representing the saxon:hexBinary output method name
+ */
+
+ /*@NotNull*/ public static final String SAXON_HEX_BINARY_METHOD = "{http://saxon.sf.net/}hexBinary";
+
+ /**
+ * String constant representing the saxon:ptree output method name
+ */
+
+ /*@NotNull*/ public static final String SAXON_PTREE_METHOD = "{http://saxon.sf.net/}ptree";
+
+ /**
+ * saxon:indent-spaces = integer.
+ * <p/>
+ * <p>Defines the number of spaces used for indentation of output</p>
+ */
+
+ /*@NotNull*/ public static final String INDENT_SPACES = "{http://saxon.sf.net/}indent-spaces";
+
+ /**
+ * saxon:line-length = integer.
+ * <p/>
+ * <p>Defines the desired maximum line length used when indenting output</p>
+ */
+
+ /*@NotNull*/ public static final String LINE_LENGTH = "{http://saxon.sf.net/}line-length";
+
+ /**
+ * suppress-indentation = list of element names
+ * <p/>
+ * <p>Defines elements within which no indentation will occur</p>
+ */
+
+ /*@NotNull*/ public static final String SUPPRESS_INDENTATION = "suppress-indentation";
+
+ /**
+ * html-version = decimal
+ * <p>Defines the version of HTML. For the XHTML output method this allows separate
+ * specification of the XHTML version and the XML version. This is a new serialization
+ * parameter in the draft 3.0 specification.</p>
+ */
+
+ public static final String HTML_VERSION = "html-version";
+
+ /**
+ * saxon:attribute-order = list of attribute names
+ * <p/>
+ * <p>Defines an ordering for attributes in the serialized output. Any attribute present in the list
+ * will appear correctly ordered according to the list; other attributes will be ordered first by namespace,
+ * then by local name.</p>
+ */
+
+ /*@NotNull*/ public static final String ATTRIBUTE_ORDER = "{http://saxon.sf.net/}attribute-order";
+
+ /**
+ * saxon:double-space = list of element names
+ * <p/>
+ * <p>Defines elements that will have an extra blank line added before the start tag, in addition
+ * to normal indentation</p>
+ */
+
+ /*@NotNull*/ public static final String DOUBLE_SPACE = "{http://saxon.sf.net/}double-space";
+
+ /**
+ * stylesheet-version. This serialization parameter is set automatically by the XSLT processor
+ * to the value of the version attribute on the principal stylesheet module.
+ */
+
+ /*@NotNull*/ public static final String STYLESHEET_VERSION = "{http://saxon.sf.net/}stylesheet-version";
+
+ /**
+ * use-character-map = list-of-qnames.
+ * <p/>
+ * <p>Defines the character maps used in this output definition. The QNames
+ * are represented in Clark notation as {uri}local-name.</p>
+ */
+
+ /*@NotNull*/ public static final String USE_CHARACTER_MAPS = "use-character-maps";
+
+
+ /**
+ * include-content-type = "yes" | "no". This attribute is defined in XSLT 2.0
+ * <p/>
+ * <p>Indicates whether the META tag is to be added to HTML output</p>
+ */
+
+ /*@NotNull*/ public static final String INCLUDE_CONTENT_TYPE = "include-content-type";
+
+ /**
+ * undeclare-prefixes = "yes" | "no". This attribute is defined in XSLT 2.0
+ * <p/>
+ * <p>Indicates XML 1.1 namespace undeclarations are to be output when required</p>
+ */
+
+ /*@NotNull*/ public static final String UNDECLARE_PREFIXES = "undeclare-prefixes";
+
+ /**
+ * escape-uri-attributes = "yes" | "no". This attribute is defined in XSLT 2.0
+ * <p/>
+ * <p>Indicates whether HTML attributes of type URI are to be URI-escaped</p>
+ */
+
+ /*@NotNull*/ public static final String ESCAPE_URI_ATTRIBUTES = "escape-uri-attributes";
+
+ /**
+ * representation = rep1[;rep2].
+ * <p/>
+ * <p>Indicates the preferred way of representing non-ASCII characters in HTML
+ * and XML output. rep1 is for characters in the range 128-256, rep2 for those
+ * above 256.</p>
+ */
+ /*@NotNull*/ public static final String CHARACTER_REPRESENTATION = "{http://saxon.sf.net/}character-representation";
+
+ /**
+ * saxon:next-in-chain = URI.
+ * <p/>
+ * <p>Indicates that the output is to be piped into another XSLT stylesheet
+ * to perform another transformation. The auxiliary property NEXT_IN_CHAIN_BASE_URI
+ * records the base URI of the stylesheet element where this attribute was found.</p>
+ */
+ /*@NotNull*/ public static final String NEXT_IN_CHAIN = "{http://saxon.sf.net/}next-in-chain";
+ /*@NotNull*/ public static final String NEXT_IN_CHAIN_BASE_URI = "{http://saxon.sf.net/}next-in-chain-base-uri";
+
+ /**
+ * byte-order-mark = yes|no.
+ * <p/>
+ * <p>Indicates whether UTF-8/UTF-16 output is to start with a byte order mark. Values are "yes" or "no",
+ * default is "no"
+ */
+
+ /*@NotNull*/ public static final String BYTE_ORDER_MARK = "byte-order-mark";
+
+ /**
+ * normalization-form = NFC|NFD|NFKC|NFKD|non.
+ * <p/>
+ * <p>Indicates that a given Unicode normalization form (or no normalization) is required.
+ */
+
+ /*@NotNull*/ public static final String NORMALIZATION_FORM = "normalization-form";
+
+ /**
+ * recognize-binary = yes|no.
+ * <p/>
+ * <p>If set to "yes", and the output is being written using output method "text", Saxon will recognize
+ * two processing instructions <?hex XXXX?> and <b64 XXXX?> containing binary data encoded
+ * as a hexBinary or base64 string respectively. The corresponding strings will be decoded as characters
+ * in the encoding being used for the output file, and will be written out to the output without checking
+ * that they represent valid XML strings.</p>
+ */
+
+ /*@NotNull*/ public static final String RECOGNIZE_BINARY = "{http://saxon.sf.net/}recognize-binary";
+
+ /**
+ * saxon:require-well-formed = yes|no.
+ * <p/>
+ * <p>Indicates whether a user-supplied ContentHandler requires the stream of SAX events to be
+ * well-formed (that is, to have a single element node and no text nodes as children of the root).
+ * The default is "no".</p>
+ */
+
+ /*@NotNull*/ public static final String REQUIRE_WELL_FORMED = "{http://saxon.sf.net/}require-well-formed";
+
+ /**
+ * supply-source-locator = yes|no.
+ * <p/>
+ * <p>If set to "yes", and the output is being sent to a SAXResult (or to a user-supplied content handler),
+ * indicates that the SAX Locator made available to the ContentHandler will contain information about the
+ * location of the context node in the source document as well as the location in the stylesheet or query.</p>
+ */
+
+ /*@NotNull*/ public static final String SUPPLY_SOURCE_LOCATOR = "{http://saxon.sf.net/}supply-source-locator";
+
+ /**
+ * wrap="yes"|"no".
+ * <p/>
+ * This property is only available in the XQuery API. The value "yes" indicates that the result
+ * sequence produced by the query is to be wrapped, that is, each item in the result is represented
+ * as a separate element. This format allows any sequence to be represented as an XML document,
+ * including for example sequences consisting of parentless attribute nodes.
+ */
+
+ /*@NotNull*/ public static final String WRAP = "{http://saxon.sf.net/}wrap-result-sequence";
+
+ /**
+ * Property used internally to identify the XSLT implicit result document
+ */
+
+ /*@NotNull*/ public static final String IMPLICIT_RESULT_DOCUMENT = "{http://saxon.sf.net/}implicit-result-document";
+
+ /**
+ * Check that a supplied output property is valid.
+ *
+ * @param key the name of the property
+ * @param value the value of the property. This may be set to null, in which case no validation takes place.
+ * The value must be in JAXP format, that is, with lexical QNames expanded to Clark names
+ * @param config the Saxon Configuration. May be null, in which case validation may be incomplete
+ * @throws XPathException if the property name or value is invalid
+ */
+
+ public static void checkOutputProperty(/*@NotNull*/ String key, /*@Nullable*/ String value, /*@Nullable*/ Configuration config) throws XPathException {
+ NameChecker checker = (config == null ? new Name11Checker() : config.getNameChecker());
+ if (!key.startsWith("{") || key.startsWith("{http://saxon.sf.net/}")) {
+ if (key.equals(BYTE_ORDER_MARK)) {
+ if (value != null) {
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(OutputKeys.CDATA_SECTION_ELEMENTS)) {
+ if (value != null) {
+ checkListOfClarkNames(key, value, checker);
+ }
+ } else if (key.equals(OutputKeys.DOCTYPE_PUBLIC)) {
+ if (value != null) {
+ checkPublicIdentifier(value);
+ }
+ } else if (key.equals(OutputKeys.DOCTYPE_SYSTEM)) {
+ if (value != null) {
+ checkSystemIdentifier(value);
+ }
+ } else if (key.equals(OutputKeys.ENCODING)) {
+ // no constraints
+ } else if (key.equals(ESCAPE_URI_ATTRIBUTES) || key.equals("escape-uri-attibutes")) {
+ // constant was misspelled in 9.0 and earlier releases
+ if (value != null) {
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(INCLUDE_CONTENT_TYPE)) {
+ if (value != null) {
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(OutputKeys.INDENT)) {
+ if (value != null) {
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(OutputKeys.MEDIA_TYPE)) {
+ // no constraints
+ } else if (key.equals(OutputKeys.METHOD)) {
+ if (value != null) {
+ checkMethod(value, config, checker);
+ }
+ } else if (key.equals(NORMALIZATION_FORM)) {
+ if (value != null) {
+ checkNormalizationForm(value, checker);
+ }
+ } else if (key.equals(OutputKeys.OMIT_XML_DECLARATION)) {
+ if (value != null) {
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(OutputKeys.STANDALONE)) {
+ if (value != null && !value.equals("omit")) {
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(UNDECLARE_PREFIXES)) {
+ if (value != null) {
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(USE_CHARACTER_MAPS)) {
+ if (value != null) {
+ checkListOfClarkNames(key, value, checker);
+ }
+ } else if (key.equals(OutputKeys.VERSION)) {
+ // no constraints
+ } else if (key.equals(STYLESHEET_VERSION)) {
+ // no constraints
+ } else if (key.equals(INDENT_SPACES)) {
+ if (value != null) {
+ checkExtensions(key, config);
+ checkNonNegativeInteger(key, value);
+ }
+ } else if (key.equals(LINE_LENGTH)) {
+ if (value != null) {
+ checkExtensions(key, config);
+ checkNonNegativeInteger(key, value);
+ }
+ } else if (key.equals(CHARACTER_REPRESENTATION)) {
+ checkExtensions(key, config);
+ } else if (key.equals(NEXT_IN_CHAIN)) {
+ checkExtensions(key, config);
+ } else if (key.equals(NEXT_IN_CHAIN_BASE_URI)) {
+ // no validation performed
+ } else if (key.equals(REQUIRE_WELL_FORMED)) {
+ if (value != null) {
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(RECOGNIZE_BINARY)) {
+ if (value != null) {
+ checkExtensions(key, config);
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(SUPPRESS_INDENTATION)) {
+ if (value != null) {
+ checkExtensions(key, config);
+ checkListOfClarkNames(key, value, checker);
+ }
+
+ } else if (key.equals(DOUBLE_SPACE)) {
+ if (value != null) {
+ checkExtensions(key, config);
+ checkListOfClarkNames(key, value, checker);
+ }
+
+ } else if (key.equals(WRAP)) {
+ if (value != null) {
+ checkExtensions(key, config);
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(SUPPLY_SOURCE_LOCATOR)) {
+ if (value != null) {
+ checkYesOrNo(key, value);
+ }
+ } else if (key.equals(HTML_VERSION)) {
+ if (value != null) {
+ checkDecimal(key, value);
+ }
+ } else {
+ throw new XPathException("Unknown serialization parameter " + Err.wrap(key), "XQST0109");
+ }
+ } else {
+ //return;
+ }
+ }
+
+ private static void checkExtensions(String key, /*@Nullable*/ Configuration config) throws XPathException {
+ if (config != null) {
+ config.checkLicensedFeature(Configuration.LicenseFeature.PROFESSIONAL_EDITION, "custom serialization " + key);
+ }
+ }
+
+ private static void checkYesOrNo(String key, String value) throws XPathException {
+ if ("yes".equals(value) || "no".equals(value)) {
+ // OK
+ } else {
+ throw new XPathException("Serialization parameter " + Err.wrap(key) + " must have the value yes or no", "SEPM0016");
+ }
+ }
+
+ private static void checkMethod(/*@NotNull*/ String value, Configuration config, /*@NotNull*/ NameChecker checker) throws XPathException {
+ if ("xml".equals(value)) return;
+ if ("html".equals(value)) return;
+ if ("xhtml".equals(value)) return;
+ if ("text".equals(value)) return;
+ if (isValidClarkName(value, checker)) {
+ checkExtensions(value, config);
+ } else {
+ throw new XPathException("Invalid value for serialization method: " +
+ "must be xml, html, xhtml, text, or a QName in '{uri}local' form", "SEPM0016");
+ }
+
+ }
+
+ private static void checkNormalizationForm(String value, /*@NotNull*/ NameChecker checker) throws XPathException {
+ if (!checker.isValidNmtoken(value)) {
+ throw new XPathException("Invalid value for normalization-form: " +
+ "must be NFC, NFD, NFKC, NFKD, fully-normalized, or none", "SEPM0016");
+ }
+// if ("NFC".equals(value)) return;
+// if ("NFD".equals(value)) return;
+// if ("NFKC".equals(value)) return;
+// if ("NFKD".equals(value)) return;
+// if ("fully-normalized".equals(value)) return;
+// if ("none".equals(value)) return;
+// throw new XPathException("Invalid value for normalization-form: " +
+// "must be NFC, NFD, NFKC, NFKD, fully-normalized, or none");
+
+ }
+
+ private static boolean isValidClarkName(/*@NotNull*/ String value, /*@NotNull*/ NameChecker checker) {
+ if (value.charAt(0) != '{') {
+ return false;
+ }
+ int closer = value.indexOf('}');
+ return closer >= 2 &&
+ closer != value.length() - 1 &&
+ checker.isValidNCName(value.substring(closer + 1));
+ }
+
+ private static void checkNonNegativeInteger(String key, String value) throws XPathException {
+ try {
+ int n = Integer.parseInt(value);
+ if (n < 0) {
+ throw new XPathException("Value of " + Err.wrap(key) + " must be a non-negative integer", "SEPM0016");
+ }
+ } catch (NumberFormatException err) {
+ throw new XPathException("Value of " + Err.wrap(key) + " must be a non-negative integer", "SEPM0016");
+ }
+ }
+
+ private static void checkDecimal(String key, String value) throws XPathException {
+ if (!DecimalValue.castableAsDecimal(value)) {
+ throw new XPathException("Value of " + Err.wrap(key) +
+ " must be a decimal number", "SEPM0016");
+ }
+ }
+
+ private static void checkListOfClarkNames(String key, String value, /*@NotNull*/ NameChecker checker) throws XPathException {
+ StringTokenizer tok = new StringTokenizer(value, " \t\n\r", false);
+ while (tok.hasMoreTokens()) {
+ String s = tok.nextToken();
+ if (isValidClarkName(s, checker) || checker.isValidNCName(s)) {
+ // ok
+ } else {
+ throw new XPathException("Value of " + Err.wrap(key) +
+ " must be a list of QNames in '{uri}local' notation", "SEPM0016");
+ }
+ }
+ }
+
+ private static Pattern publicIdPattern = Pattern.compile("^[\\s\\r\\na-zA-Z0-9\\-'()+,./:=?;!*#@$_%]*$");
+
+ private static void checkPublicIdentifier(String value) throws XPathException {
+ if (!publicIdPattern.matcher(value).matches()) {
+ throw new XPathException("Invalid character in doctype-public parameter", "SEPM0016");
+ }
+ }
+
+ private static void checkSystemIdentifier(/*@NotNull*/ String value) throws XPathException {
+ if (value.contains("'") && value.contains("\"")) {
+ throw new XPathException("The doctype-system parameter must not contain both an apostrophe and a quotation mark", "SEPM0016");
+ }
+ }
+
+ /**
+ * Process a serialization property whose value is a list of element names, for example cdata-section-elements
+ *
+ *
+ * @param value The value of the property as written
+ * @param nsResolver The namespace resolver to use; may be null if prevalidated is set or if names are supplied
+ * in Clark format
+ * @param useDefaultNS
+ * @param prevalidated true if the property has already been validated
+ * @param checker The name checker to use for name syntax (XML 1.0 or XML 1.1)
+ * @param errorCode The error code to return in the event of problems
+ * @return The list of element names with lexical QNames replaced by Clark names, starting with a single space
+ * @throws XPathException if any error is found in the list of element names, for example, an undeclared namespace prefix
+ */
+
+ /*@NotNull*/
+ public static String parseListOfNodeNames(
+ String value, /*@Nullable*/ NamespaceResolver nsResolver, boolean useDefaultNS, boolean prevalidated, /*@NotNull*/ NameChecker checker, String errorCode)
+ throws XPathException {
+ String s = "";
+ StringTokenizer st = new StringTokenizer(value, " \t\n\r", false);
+ while (st.hasMoreTokens()) {
+ String displayname = st.nextToken();
+ if (prevalidated || (nsResolver == null)) {
+ s += ' ' + displayname;
+ } else {
+ try {
+ String[] parts = checker.getQNameParts(displayname);
+ String muri = nsResolver.getURIForPrefix(parts[0], useDefaultNS);
+ if (muri == null) {
+ XPathException err = new XPathException("Namespace prefix '" + parts[0] + "' has not been declared");
+ err.setErrorCode(errorCode);
+ throw err;
+ }
+ s += " {" + muri + '}' + parts[1];
+ } catch (QNameException err) {
+ XPathException e = new XPathException("Invalid element name. " + err.getMessage());
+ e.setErrorCode(errorCode);
+ throw e;
+ }
+ }
+ }
+ return s;
+ }
+
+ /**
+ * Examine the already-validated properties to see whether the html-version property is present
+ * with the decimal value 5.0
+ * @param properties the properties to be examined
+ * @return true if the properties include html-version="5.0". The property is a decimal value, so
+ * it can also be written, for example, "5" or "+5.00".
+ */
+ public static boolean isHtmlVersion5(Properties properties) {
+ String htmlVersion = properties.getProperty(SaxonOutputKeys.HTML_VERSION);
+ boolean is5;
+ try {
+ return htmlVersion != null &&
+ ((DecimalValue)DecimalValue.makeDecimalValue(htmlVersion, false).asAtomic())
+ .getDecimalValue().equals(BigDecimal.valueOf(5));
+ } catch (ValidationException e) {
+ return false;
+ }
+ }
+}
+
diff --git a/sf/saxon/lib/SchemaURIResolver.java b/sf/saxon/lib/SchemaURIResolver.java
new file mode 100644
index 0000000..697d0b5
--- /dev/null
+++ b/sf/saxon/lib/SchemaURIResolver.java
@@ -0,0 +1,59 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.Source;
+import java.io.Serializable;
+
+
+/**
+ * A SchemaURIResolver is used when resolving references to
+ * schema documents. It takes as input the target namespace of the schema to be loaded, and a set of
+ * location hints as input, and returns one or more Source obects containing the schema documents
+ * to be imported.
+* @author Michael H. Kay
+*/
+
+public interface SchemaURIResolver extends Serializable {
+
+ /**
+ * Set the configuration information for use by the resolver
+ * @param config the Saxon Configuration (which will always be an {@link com.saxonica.config.EnterpriseConfiguration})
+ */
+
+ public void setConfiguration(Configuration config);
+
+ /**
+ * Resolve a URI identifying a schema document, given the target namespace URI and
+ * a set of associated location hints.
+ * @param targetNamespace the target namespaces of the schema to be imported. The "null namesapce"
+ * is identified by a zero-length string. In the case of an xsd:include directive, where no
+ * target namespace is specified, the parameter is null.
+ * @param baseURI The base URI of the module containing the "import schema" declaration;
+ * null if no base URI is known
+ * @param locations The set of URIs identified as schema location hints. In most cases (xs:include, xs:import,
+ * xsi:schemaLocation, xsl:import-schema) there is only one URI in this list. With an XQuery "import module"
+ * declaration, however, a list of URIs may be specified.
+ * @return an array of Source objects each identifying a schema document to be loaded.
+ * These need not necessarily correspond one-to-one with the location hints provided.
+ * Returning a zero-length array indictates that no schema documents should be loaded (perhaps because
+ * the relevant schema components are already present in the schema cache).
+ * The method may also return null to indicate that resolution should be delegated to the standard
+ * (Saxon-supplied) SchemaURIResolver.
+ * @throws net.sf.saxon.trans.XPathException if the module cannot be located, and if delegation to the default
+ * module resolver is not required.
+ */
+
+ public Source[] resolve(String targetNamespace, String baseURI, String[] locations) throws XPathException;
+
+
+}
+
diff --git a/sf/saxon/lib/SerializerFactory.java b/sf/saxon/lib/SerializerFactory.java
new file mode 100644
index 0000000..08d33dc
--- /dev/null
+++ b/sf/saxon/lib/SerializerFactory.java
@@ -0,0 +1,753 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.*;
+import net.sf.saxon.query.SequenceWrapper;
+import net.sf.saxon.serialize.*;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamResult;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Properties;
+
+/**
+* Helper class to construct a serialization pipeline for a given result destination
+* and a given set of output properties. The pipeline is represented by a Receiver object
+* to which result tree events are sent.
+ *
+ * Since Saxon 8.8 is is possible to write a subclass of SerializerFactory and register it
+ * with the Configuration, allowing customisation of the Serializer pipeline.
+ *
+ * The class includes methods for instantiating each of the components used on the Serialization
+ * pipeline. This allows a customized SerializerFactory to replace any or all of these components
+ * by subclasses that refine the behaviour.
+ *
+*/
+
+public class SerializerFactory implements Serializable {
+
+ Configuration config;
+
+ private static Class staxResultClass;
+
+ static {
+ try {
+ staxResultClass = Class.forName("javax.xml.transform.stax.StAXResult");
+ } catch (Exception err) {
+ // no action; if StAXSource isn't available then we don't use it.
+ }
+ }
+
+ /**
+ * Create a SerializerFactory
+ * @param config the Saxon Configuration
+ */
+
+ public SerializerFactory(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Create a serializer with given output properties, and return
+ * an XMLStreamWriter that can be used to feed events to the serializer.
+ * @param result the destination of the serialized output (wraps a Writer, an OutputStream, or a File)
+ * @param properties the serialization properties to be used
+ * @return a serializer in the form of an XMLStreamWriter
+ * @throws net.sf.saxon.trans.XPathException if any error occurs
+ */
+
+ public StreamWriterToReceiver getXMLStreamWriter(
+ StreamResult result,
+ Properties properties) throws XPathException {
+ Receiver r = getReceiver(result, config.makePipelineConfiguration(), properties);
+ return new StreamWriterToReceiver(r);
+ }
+
+ /**
+ * Get a Receiver that wraps a given Result object. Saxon calls this method to construct
+ * a serialization pipeline. The method can be overridden in a subclass; alternatively, the
+ * subclass can override the various methods used to instantiate components of the serialization
+ * pipeline.
+ *
+ * <p>Note that this method ignores the {@link SaxonOutputKeys#WRAP} output property. If
+ * wrapped output is required, the user must create a {@link net.sf.saxon.query.SequenceWrapper} directly.</p>
+ *
+ * @param result The final destination of the serialized output. Usually a StreamResult,
+ * but other kinds of Result are possible.
+ * @param pipe The PipelineConfiguration.
+ * @param props The serialization properties. If this includes the property {@link SaxonOutputKeys#USE_CHARACTER_MAPS}
+ * then the PipelineConfiguration must contain a non-null Controller, and the Executable associated with this Controller
+ * must have a CharacterMapIndex which is used to resolve the names of the character maps appearing in this property.
+ * @return the newly constructed Receiver that performs the required serialization
+ * @throws net.sf.saxon.trans.XPathException if any failure occurs
+ */
+
+ public Receiver getReceiver(Result result,
+ PipelineConfiguration pipe,
+ Properties props)
+ throws XPathException {
+ if (pipe.getController() != null) {
+ return getReceiver(result, pipe, props, pipe.getController().getExecutable().getCharacterMapIndex());
+ } else {
+ return getReceiver(result, pipe, props, null);
+ }
+ }
+
+ /**
+ * Get a Receiver that wraps a given Result object. Saxon calls this method to construct
+ * a serialization pipeline. The method can be overridden in a subclass; alternatively, the
+ * subclass can override the various methods used to instantiate components of the serialization
+ * pipeline.
+ *
+ * <p>Note that this method ignores the {@link SaxonOutputKeys#WRAP} output property. If
+ * wrapped output is required, the user must create a {@link net.sf.saxon.query.SequenceWrapper} directly.</p>
+ *
+ * @param result The final destination of the serialized output. Usually a StreamResult,
+ * but other kinds of Result are possible.
+ * @param pipe The PipelineConfiguration.
+ * @param props The serialization properties
+ * @param charMapIndex The index of character maps. Required if any of the serialization properties
+ * is {@link SaxonOutputKeys#USE_CHARACTER_MAPS}, in which case the named character maps listed in that
+ * property must be present in the index of character maps.
+ * @return the newly constructed Receiver that performs the required serialization
+ */
+
+ public Receiver getReceiver(Result result,
+ PipelineConfiguration pipe,
+ Properties props,
+ /*@Nullable*/ CharacterMapIndex charMapIndex)
+ throws XPathException {
+ if (result instanceof Emitter) {
+ if (((Emitter)result).getOutputProperties() == null) {
+ ((Emitter)result).setOutputProperties(props);
+ }
+ return (Emitter)result;
+ } else if (result instanceof Receiver) {
+ Receiver receiver = (Receiver)result;
+ receiver.setSystemId(result.getSystemId());
+ receiver.setPipelineConfiguration(pipe);
+ return receiver;
+ } else if (result instanceof SAXResult) {
+ ContentHandlerProxy proxy = newContentHandlerProxy();
+ proxy.setUnderlyingContentHandler(((SAXResult)result).getHandler());
+ proxy.setPipelineConfiguration(pipe);
+ proxy.setOutputProperties(props);
+ if ("yes".equals(props.getProperty(SaxonOutputKeys.SUPPLY_SOURCE_LOCATOR))) {
+ if (pipe.getConfiguration().isCompileWithTracing()) {
+ pipe.getController().addTraceListener(proxy.getTraceListener());
+ } else {
+ XPathException de = new XPathException("Cannot use saxon:supply-source-locator unless tracing was enabled at compile time");
+ de.setErrorCode(SaxonErrorCode.SXSE0002);
+ throw de;
+ }
+ }
+ //proxy.open();
+ return proxy;
+ } else if (result instanceof StreamResult) {
+
+ // The "target" is the start of the output pipeline, the Receiver that
+ // instructions will actually write to (except that other things like a
+ // NamespaceReducer may get added in front of it). The "emitter" is the
+ // last thing in the output pipeline, the Receiver that actually generates
+ // characters or bytes that are written to the StreamResult.
+
+ Receiver target;
+ String method = props.getProperty(OutputKeys.METHOD);
+ if (method==null) {
+ return newUncommittedSerializer(result, new Sink(pipe), props);
+ }
+
+ Emitter emitter = null;
+
+ CharacterMapExpander characterMapExpander = null;
+ String useMaps = props.getProperty(SaxonOutputKeys.USE_CHARACTER_MAPS);
+ if (useMaps != null) {
+ if (charMapIndex == null) {
+ XPathException de = new XPathException("Cannot use character maps in an environment with no Controller");
+ de.setErrorCode(SaxonErrorCode.SXSE0001);
+ throw de;
+ }
+ characterMapExpander = charMapIndex.makeCharacterMapExpander(useMaps, new Sink(pipe), this);
+ }
+
+ ProxyReceiver normalizer = null;
+ String normForm = props.getProperty(SaxonOutputKeys.NORMALIZATION_FORM);
+ if (normForm != null && !normForm.equals("none")) {
+ normalizer = newUnicodeNormalizer(new Sink(pipe), props);
+ }
+
+ if ("html".equals(method)) {
+ emitter = newHTMLEmitter(props);
+ emitter.setPipelineConfiguration(pipe);
+ target = createHTMLSerializer(emitter, props, pipe, characterMapExpander, normalizer);
+
+ } else if ("xml".equals(method)) {
+ emitter = newXMLEmitter(props);
+ emitter.setPipelineConfiguration(pipe);
+ target = createXMLSerializer((XMLEmitter)emitter, props, pipe, characterMapExpander, normalizer);
+
+ } else if ("xhtml".equals(method)) {
+ emitter = newXHTMLEmitter(props);
+ emitter.setPipelineConfiguration(pipe);
+ target = createXHTMLSerializer(emitter, props, pipe, characterMapExpander, normalizer);
+
+ } else if ("text".equals(method)) {
+ emitter = newTEXTEmitter();
+ emitter.setPipelineConfiguration(pipe);
+ target = createTextSerializer(emitter, props, characterMapExpander, normalizer);
+
+ } else if (method.startsWith("{" + NamespaceConstant.SAXON + "}")) {
+ target = createSaxonSerializationMethod(method, props, pipe, characterMapExpander, normalizer);
+ if (target instanceof Emitter) {
+ emitter = (Emitter)target;
+ }
+
+ } else {
+ Receiver userReceiver;
+ if (pipe == null) {
+ throw new XPathException("Unsupported serialization method " + method);
+ } else {
+ userReceiver = createUserDefinedOutputMethod(method, props, pipe);
+ target = userReceiver;
+ if (userReceiver instanceof Emitter) {
+ emitter = (Emitter)userReceiver;
+ } else {
+ return userReceiver;
+ }
+ }
+ }
+ if (emitter != null) {
+ emitter.setOutputProperties(props);
+ StreamResult sr = (StreamResult)result;
+ emitter.setStreamResult(sr);
+ }
+ return target;
+
+ } else if(staxResultClass != null && staxResultClass.isAssignableFrom(result.getClass())) {
+ StAXResultHandler handler = (StAXResultHandler) config.getDynamicLoader().getInstance("net.sf.saxon.lib.StAXHandler", getClass().getClassLoader());
+ return handler.getReceiver(result, props);
+ }else {
+ if (pipe != null) {
+ // try to find an external object model that knows this kind of Result
+ List externalObjectModels = pipe.getConfiguration().getExternalObjectModels();
+ for (Object externalObjectModel : externalObjectModels) {
+ ExternalObjectModel model = (ExternalObjectModel) externalObjectModel;
+ Receiver builder = model.getDocumentBuilder(result);
+ if (builder != null) {
+ builder.setSystemId(result.getSystemId());
+ builder.setPipelineConfiguration(pipe);
+ return builder;
+ }
+ }
+ }
+ }
+
+ throw new IllegalArgumentException("Unknown type of result: " + result.getClass());
+ }
+
+ /**
+ * Create a serialization pipeline to implement the HTML output method. This method is protected
+ * so that it can be customized in a user-written SerializerFactory
+ * @param emitter the emitter at the end of the pipeline (created using the method {@link #newHTMLEmitter}
+ * @param props the serialization properties
+ * @param pipe the pipeline configuration information
+ * @param characterMapExpander the filter to be used for expanding character maps defined in the stylesheet
+ * @param normalizer the filter used for Unicode normalization
+ * @return a Receiver acting as the entry point to the serialization pipeline
+ * @throws XPathException if a failure occurs
+ */
+
+ protected Receiver createHTMLSerializer(
+ Emitter emitter, Properties props, PipelineConfiguration pipe,
+ CharacterMapExpander characterMapExpander, ProxyReceiver normalizer) throws XPathException {
+ Receiver target;
+ target = emitter;
+ if (!"no".equals(props.getProperty(OutputKeys.INDENT))) {
+ target = newHTMLIndenter(target, props);
+ }
+ if (normalizer != null) {
+ normalizer.setUnderlyingReceiver(target);
+ target = normalizer;
+ }
+ if (characterMapExpander != null) {
+ characterMapExpander.setUnderlyingReceiver(target);
+ target = characterMapExpander;
+ }
+ String cdataElements = props.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
+ if (cdataElements!=null && cdataElements.length()>0) {
+ target = newCDATAFilter(target, props);
+ }
+ if (!"no".equals(props.getProperty(SaxonOutputKeys.ESCAPE_URI_ATTRIBUTES))) {
+ target = newHTMLURIEscaper(target, props);
+ }
+ if (!"no".equals(props.getProperty(SaxonOutputKeys.INCLUDE_CONTENT_TYPE))) {
+ target = newHTMLMetaTagAdjuster(target, props);
+ }
+ String attributeOrder = props.getProperty(SaxonOutputKeys.ATTRIBUTE_ORDER);
+ if (attributeOrder != null && attributeOrder.length() > 0) {
+ target = newAttributeSorter(target, props);
+ }
+ return target;
+ }
+
+ /**
+ * Create a serialization pipeline to implement the text output method. This method is protected
+ * so that it can be customized in a user-written SerializerFactory
+ *
+ * @param emitter the emitter at the end of the pipeline (created using the method {@link #newTEXTEmitter}
+ * @param props the serialization properties
+ * @param characterMapExpander the filter to be used for expanding character maps defined in the stylesheet
+ * @param normalizer the filter used for Unicode normalization
+ * @return a Receiver acting as the entry point to the serialization pipeline
+ * @throws XPathException if a failure occurs
+ */
+
+ protected Receiver createTextSerializer(
+ Emitter emitter, Properties props,
+ CharacterMapExpander characterMapExpander, ProxyReceiver normalizer) throws XPathException {
+ Receiver target;
+ target = emitter;
+ if (characterMapExpander != null) {
+ characterMapExpander.setUnderlyingReceiver(target);
+ characterMapExpander.setUseNullMarkers(false);
+ target = characterMapExpander;
+ }
+ if (normalizer != null) {
+ normalizer.setUnderlyingReceiver(target);
+ target = normalizer;
+ }
+ target = addTextOutputFilter(target, props);
+ return target;
+ }
+
+ /**
+ * Create a serialization pipeline to implement the XHTML output method. This method is protected
+ * so that it can be customized in a user-written SerializerFactory
+ * @param emitter the emitter at the end of the pipeline (created using the method {@link #newXHTMLEmitter}
+ * @param props the serialization properties
+ * @param pipe the pipeline configuration information
+ * @param characterMapExpander the filter to be used for expanding character maps defined in the stylesheet
+ * @param normalizer the filter used for Unicode normalization
+ * @return a Receiver acting as the entry point to the serialization pipeline
+ * @throws XPathException if a failure occurs
+ */
+
+ protected Receiver createXHTMLSerializer(
+ Emitter emitter, Properties props, PipelineConfiguration pipe,
+ CharacterMapExpander characterMapExpander, ProxyReceiver normalizer) throws XPathException {
+ // Ensure that the XHTML namespace is registered in the NamePool. Without this, the meta-tag insertion can fail
+ pipe.getConfiguration().getNamePool().allocateCodeForURI(NamespaceConstant.XHTML);
+ Receiver target;
+ target = emitter;
+ if (!"no".equals(props.getProperty(OutputKeys.INDENT))) {
+ target = newXHTMLIndenter(target, props);
+ }
+ if (normalizer != null) {
+ normalizer.setUnderlyingReceiver(target);
+ target = normalizer;
+ }
+ if (characterMapExpander != null) {
+ characterMapExpander.setUnderlyingReceiver(target);
+ characterMapExpander.setPipelineConfiguration(pipe);
+ target = characterMapExpander;
+ }
+ String cdataElements = props.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
+ if (cdataElements!=null && cdataElements.length()>0) {
+ target = newCDATAFilter(target, props);
+ }
+
+ if (SaxonOutputKeys.isHtmlVersion5(props)) {
+ target = addHtml5Component(target, props);
+ }
+ if (!"no".equals(props.getProperty(SaxonOutputKeys.ESCAPE_URI_ATTRIBUTES))) {
+ target = newXHTMLURIEscaper(target, props);
+ }
+ if (!"no".equals(props.getProperty(SaxonOutputKeys.INCLUDE_CONTENT_TYPE))) {
+ target = newXHTMLMetaTagAdjuster(target, props);
+ }
+ String attributeOrder = props.getProperty(SaxonOutputKeys.ATTRIBUTE_ORDER);
+ if (attributeOrder != null && attributeOrder.length() > 0) {
+ target = newAttributeSorter(target, props);
+ }
+ return target;
+ }
+
+ public Receiver addHtml5Component(Receiver next, Properties outputProperties){
+ return next;
+ }
+
+ /**
+ * Create a serialization pipeline to implement the XML output method. This method is protected
+ * so that it can be customized in a user-written SerializerFactory
+ * @param emitter the emitter at the end of the pipeline (created using the method {@link #newXHTMLEmitter}
+ * @param props the serialization properties
+ * @param pipe the pipeline configuration information
+ * @param characterMapExpander the filter to be used for expanding character maps defined in the stylesheet
+ * @param normalizer the filter used for Unicode normalization
+ * @return a Receiver acting as the entry point to the serialization pipeline
+ * @throws XPathException if a failure occurs
+ */
+
+ protected Receiver createXMLSerializer(
+ XMLEmitter emitter, Properties props, PipelineConfiguration pipe,
+ CharacterMapExpander characterMapExpander, ProxyReceiver normalizer) throws XPathException {
+ Receiver target;
+
+ if ("yes".equals(props.getProperty(OutputKeys.INDENT))) {
+ target = newXMLIndenter(emitter, props);
+ } else {
+ target = emitter;
+ }
+ if ("1.0".equals(props.getProperty(OutputKeys.VERSION)) &&
+ pipe.getConfiguration().getXMLVersion() == Configuration.XML11) {
+ // Check result meets XML 1.0 constraints if configuration allows XML 1.1 input but
+ // this result document must conform to 1.0
+ target = newXML10ContentChecker(target, props);
+ }
+ if (normalizer != null) {
+ normalizer.setUnderlyingReceiver(target);
+ target = normalizer;
+ }
+ if (characterMapExpander != null) {
+ characterMapExpander.setUnderlyingReceiver(target);
+ target = characterMapExpander;
+ }
+ String cdataElements = props.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
+ if (cdataElements!=null && cdataElements.length()>0) {
+ target = newCDATAFilter(target, props);
+ }
+ String attributeOrder = props.getProperty(SaxonOutputKeys.ATTRIBUTE_ORDER);
+ if (attributeOrder != null && attributeOrder.length() > 0) {
+ target = newAttributeSorter(target, props);
+ }
+ // Adding a NamespaceReducer in 9.4 because otherwise analyze-string-001 outputs a redundant xmlns="" undeclaration,
+ // as a consequence of the change to TreeReceiver introduced in response to bug 5857
+ //target = new NamespaceReducer(target);
+ return target;
+ }
+
+ protected Receiver createSaxonSerializationMethod(
+ String method, Properties props,
+ PipelineConfiguration pipe, CharacterMapExpander characterMapExpander,
+ ProxyReceiver normalizer) throws XPathException {
+ throw new XPathException("Saxon serialization methods require Saxon-PE to be enabled");
+ }
+
+ /**
+ * Create a serialization pipeline to implement a user-defined output method. This method is protected
+ * so that it can be customized in a user-written SerializerFactory
+ * @param method the name of the user-defined output method, as a QName in Clark format
+ * (that is "{uri}local").
+ * @param props the serialization properties
+ * @param pipe the pipeline configuration information
+ * @return a Receiver acting as the entry point to the serialization pipeline
+ * @throws XPathException if a failure occurs
+ */
+
+ protected Receiver createUserDefinedOutputMethod(String method, Properties props, PipelineConfiguration pipe) throws XPathException {
+ Receiver userReceiver;// See if this output method is recognized by the Configuration
+ userReceiver = pipe.getConfiguration().makeEmitter(method, props);
+ userReceiver.setPipelineConfiguration(pipe);
+ if (userReceiver instanceof ContentHandlerProxy &&
+ "yes".equals(props.getProperty(SaxonOutputKeys.SUPPLY_SOURCE_LOCATOR))) {
+ if (pipe.getConfiguration().isCompileWithTracing()) {
+ pipe.getController().addTraceListener(
+ ((ContentHandlerProxy)userReceiver).getTraceListener());
+ } else {
+ XPathException de = new XPathException(
+ "Cannot use saxon:supply-source-locator unless tracing was enabled at compile time");
+ de.setErrorCode(SaxonErrorCode.SXSE0002);
+ throw de;
+ }
+ }
+ return userReceiver;
+ }
+
+
+ /**
+ * Create a ContentHandlerProxy. This method exists so that it can be overridden in a subclass.
+ * @return the newly created ContentHandlerProxy.
+ */
+
+ protected ContentHandlerProxy newContentHandlerProxy() {
+ return new ContentHandlerProxy();
+ }
+
+ /**
+ * Create an UncommittedSerializer. This method exists so that it can be overridden in a subclass.
+ * @param result the result destination
+ * @param next the next receiver in the pipeline
+ * @param properties the serialization properties
+ * @return the newly created UncommittedSerializer.
+ */
+
+ protected UncommittedSerializer newUncommittedSerializer(Result result, Receiver next, Properties properties) {
+ return new UncommittedSerializer(result, next, properties);
+ }
+
+ /**
+ * Create a new XML Emitter. This method exists so that it can be overridden in a subclass.
+ * @param properties the output properties
+ * @return the newly created XML emitter.
+ */
+
+ protected Emitter newXMLEmitter(Properties properties) {
+ return new XMLEmitter();
+ }
+
+ /**
+ * Create a new HTML Emitter. This method exists so that it can be overridden in a subclass.
+ * @param properties the output properties
+ * @return the newly created HTML emitter.
+ */
+
+ protected Emitter newHTMLEmitter(Properties properties) {
+ HTMLEmitter emitter;
+ String versionProperty = properties.getProperty(SaxonOutputKeys.HTML_VERSION);
+ // Note, we recognize html-version even when running XSLT 2.0.
+ if (versionProperty == null) {
+ versionProperty = properties.getProperty(OutputKeys.VERSION);
+ }
+ if (versionProperty != null && versionProperty.equals("5.0")) {
+ emitter = new HTML50Emitter();
+ } else {
+ emitter = new HTML40Emitter();
+ }
+ return emitter;
+ }
+
+ /**
+ * Create a new XHTML Emitter. This method exists so that it can be overridden in a subclass.
+ * @param properties the output properties
+ * @return the newly created XHTML emitter.
+ */
+
+ protected Emitter newXHTMLEmitter(Properties properties) {
+ return new XHTML1Emitter();
+ }
+
+ /**
+ * Add a filter to the text output method pipeline. This does nothing unless overridden
+ * in a superclass
+ * @param next the next receiver (typically the TextEmitter)
+ * @param properties the output properties
+ * @return the receiver to be used in place of the "next" receiver
+ */
+
+ public Receiver addTextOutputFilter(Receiver next, Properties properties) throws XPathException {
+ return next;
+ }
+
+ /**
+ * Create a new Text Emitter. This method exists so that it can be overridden in a subclass.
+ * @return the newly created text emitter.
+ */
+
+ protected Emitter newTEXTEmitter() {
+ return new TEXTEmitter();
+ }
+
+
+ /**
+ * Create a new XML Indenter. This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created XML indenter.
+ */
+
+ protected ProxyReceiver newXMLIndenter(XMLEmitter next, Properties outputProperties) {
+ XMLIndenter r = new XMLIndenter(next);
+ r.setOutputProperties(outputProperties);
+ return r;
+ }
+
+ /**
+ * Create a new HTML Indenter. This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created HTML indenter.
+ */
+
+ protected ProxyReceiver newHTMLIndenter(Receiver next, Properties outputProperties) {
+ return new HTMLIndenter(next, "html");
+ }
+
+ /**
+ * Create a new XHTML Indenter. This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created XHTML indenter.
+ */
+
+ protected ProxyReceiver newXHTMLIndenter(Receiver next, Properties outputProperties) {
+ return new HTMLIndenter(next, "xhtml");
+ }
+
+ /**
+ * Create a new XHTML MetaTagAdjuster, responsible for insertion, removal, or replacement of meta
+ * elements. This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created XHTML MetaTagAdjuster.
+ */
+
+ protected MetaTagAdjuster newXHTMLMetaTagAdjuster(Receiver next, Properties outputProperties) {
+ MetaTagAdjuster r = new MetaTagAdjuster(next);
+ r.setOutputProperties(outputProperties);
+ r.setIsXHTML(true);
+ return r;
+ }
+
+ /**
+ * Create a new XHTML MetaTagAdjuster, responsible for insertion, removal, or replacement of meta
+ * elements. This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created HTML MetaTagAdjuster.
+ */
+
+ protected MetaTagAdjuster newHTMLMetaTagAdjuster(Receiver next, Properties outputProperties) {
+ MetaTagAdjuster r = new MetaTagAdjuster(next);
+ r.setOutputProperties(outputProperties);
+ r.setIsXHTML(false);
+ return r;
+ }
+
+ /**
+ * Create a new HTML URI Escaper, responsible for percent-encoding of URIs in
+ * HTML output documents. This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created HTML URI escaper.
+ */
+
+ protected ProxyReceiver newHTMLURIEscaper(Receiver next, Properties outputProperties) {
+ return new HTMLURIEscaper(next);
+ }
+
+ /**
+ * Create a new XHTML URI Escaper, responsible for percent-encoding of URIs in
+ * HTML output documents. This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created HTML URI escaper.
+ */
+
+ protected ProxyReceiver newXHTMLURIEscaper(Receiver next, Properties outputProperties) {
+ return new XHTMLURIEscaper(next);
+ }
+
+
+ /**
+ * Create a new CDATA Filter, responsible for insertion of CDATA sections where required.
+ * This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created CDATA filter.
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ protected ProxyReceiver newCDATAFilter(Receiver next, Properties outputProperties) throws XPathException {
+ CDATAFilter r = new CDATAFilter(next);
+ r.setOutputProperties(outputProperties);
+ return r;
+ }
+
+ /**
+ * Create a new AttributeSorter, responsible for sorting of attributes into a specified order.
+ * This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created CDATA filter.
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ protected ProxyReceiver newAttributeSorter(Receiver next, Properties outputProperties) throws XPathException {
+ AttributeSorter r = new AttributeSorter(next);
+ r.setOutputProperties(outputProperties);
+ return r;
+ }
+
+
+ /**
+ * Create a new XML 1.0 content checker, responsible for checking that the output conforms to
+ * XML 1.0 rules (this is used only if the Configuration supports XML 1.1 but the specific output
+ * file requires XML 1.0). This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created XML 1.0 content checker.
+ */
+
+ protected ProxyReceiver newXML10ContentChecker(Receiver next, Properties outputProperties) {
+ return new XML10ContentChecker(next);
+ }
+
+ /**
+ * Create a Unicode Normalizer. This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization parameters
+ * @return the newly created Unicode normalizer.
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ protected ProxyReceiver newUnicodeNormalizer(Receiver next, Properties outputProperties) throws XPathException {
+ String normForm = outputProperties.getProperty(SaxonOutputKeys.NORMALIZATION_FORM);
+ return new UnicodeNormalizer(normForm, next);
+ }
+
+ /**
+ * Create a new CharacterMapExpander. This method exists so that it can be overridden in a subclass.
+ * @param next the next receiver in the pipeline
+ * @return the newly created CharacterMapExpander.
+ */
+
+ public CharacterMapExpander newCharacterMapExpander(Receiver next) {
+ return new CharacterMapExpander(next);
+ }
+
+ /**
+ * Prepare another stylesheet to handle the output of this one.
+ * <p>
+ * This method is intended for internal use, to support the
+ * <code>saxon:next-in-chain</code> extension.
+ *
+ * @param controller the current transformation
+ * @param href URI of the next stylesheet to be applied
+ * @param baseURI base URI for resolving href if it's a relative
+ * URI
+ * @param result the output destination of the current stylesheet
+ * @return a replacement destination for the current stylesheet
+ * @throws XPathException if any dynamic error occurs
+ */
+
+ public Result prepareNextStylesheet(Controller controller, String href, String baseURI, Result result)
+ throws TransformerException {
+ controller.getConfiguration().checkLicensedFeature(Configuration.LicenseFeature.PROFESSIONAL_EDITION, "saxon:next-in-chain");
+ return null;
+ }
+
+ /**
+ * Get a SequenceWrapper, a class that serializes an XDM sequence with full annotation of item types, node kinds,
+ * etc. There are variants for Saxon-HE and Saxon-PE
+ */
+
+ public SequenceWrapper newSequenceWrapper(Receiver destination) {
+ return new SequenceWrapper(destination);
+ }
+
+}
+
diff --git a/sf/saxon/lib/SourceResolver.java b/sf/saxon/lib/SourceResolver.java
new file mode 100644
index 0000000..165ccf3
--- /dev/null
+++ b/sf/saxon/lib/SourceResolver.java
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.Source;
+
+
+/**
+ * This interface defines a SourceResolver. A SourceResolver can be registered as
+ * part of the Configuration, and enables new kinds of Source to be recognized
+ * beyond those that are natively recognized by Saxon.
+ * <p>
+ * The task of the SourceResolver is to take any Source as input, and to return
+ * a Source that has native support in Saxon: that is, one of the classes
+ * StreamSource, SAXSource, DOMSource, {@link net.sf.saxon.om.NodeInfo},
+ * or {@link net.sf.saxon.pull.PullSource}
+ * @author Michael H. Kay
+*/
+
+public interface SourceResolver {
+
+ /**
+ * Resolve a Source.
+ * @param source A source object, typically the source supplied as the first
+ * argument to {@link javax.xml.transform.Transformer#transform(javax.xml.transform.Source, javax.xml.transform.Result)}
+ * or similar methods.
+ * @param config The Configuration. This provides the SourceResolver with access to
+ * configuration information; it also allows the SourceResolver to invoke the
+ * resolveSource() method on the Configuration object as a fallback implementation.
+ * @return a source object that Saxon knows how to process. This must be an instance of one
+ * of the classes StreamSource, SAXSource, DOMSource, {@link AugmentedSource},
+ * {@link net.sf.saxon.om.NodeInfo},
+ * or {@link net.sf.saxon.pull.PullSource}. Return null if the Source object is not
+ * recognized
+ * @throws XPathException if the Source object is recognized but cannot be processed
+ */
+
+ /*@Nullable*/ public Source resolveSource(Source source, Configuration config) throws XPathException;
+
+}
+
diff --git a/sf/saxon/lib/StAXResultHandler.java b/sf/saxon/lib/StAXResultHandler.java
new file mode 100644
index 0000000..b81c120
--- /dev/null
+++ b/sf/saxon/lib/StAXResultHandler.java
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.event.Receiver;
+
+import javax.xml.transform.Result;
+import java.util.Properties;
+
+/**
+ * StAxResultHandler is a helper class
+ */
+public interface StAXResultHandler {
+
+ Receiver getReceiver(Result result, Properties properties);
+}
+
diff --git a/sf/saxon/lib/StandardCollationURIResolver.java b/sf/saxon/lib/StandardCollationURIResolver.java
new file mode 100644
index 0000000..98d0c20
--- /dev/null
+++ b/sf/saxon/lib/StandardCollationURIResolver.java
@@ -0,0 +1,91 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.AnyURIValue;
+
+import javax.xml.transform.TransformerException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * StandardCollationURIResolver allows a Collation to be created given
+ * a URI starting with "http://saxon.sf.net/collation" followed by a set of query parameters.
+*/
+
+public class StandardCollationURIResolver implements CollationURIResolver {
+
+ private static final StandardCollationURIResolver theInstance = new StandardCollationURIResolver();
+
+ /**
+ * The class is normally used as a singleton, but the constructor is public to allow the class to be named
+ * as a value of the configuration property COLLATION_URI_RESOLVER
+ */
+ public StandardCollationURIResolver() {
+ }
+
+ /**
+ * Return the singleton instance of this class
+ * @return the singleton instance
+ */
+
+ public static StandardCollationURIResolver getInstance() {
+ return theInstance;
+ }
+
+
+ /**
+ * Create a collator from a parameterized URI
+ * @return null if the collation URI is not recognized. If the collation URI is recognized but contains
+ * errors, the method returns null after sending a warning to the ErrorListener.
+ */
+
+ /*@Nullable*/ public StringCollator resolve(String uri, String base, Configuration config) {
+ try {
+ if (uri.equals("http://saxon.sf.net/collation")) {
+ return Configuration.getPlatform().makeCollation(config, new Properties(), uri);
+ } else if (uri.startsWith("http://saxon.sf.net/collation?")) {
+ URI uuri;
+ try {
+ uuri = new URI(uri);
+ } catch (URISyntaxException err) {
+ throw new XPathException(err);
+ }
+ Properties props = new Properties();
+ String query = uuri.getRawQuery();
+ StringTokenizer queryTokenizer = new StringTokenizer(query, ";&");
+ while (queryTokenizer.hasMoreElements()) {
+ String param = queryTokenizer.nextToken();
+ int eq = param.indexOf('=');
+ if (eq > 0 && eq < param.length()-1) {
+ String kw = param.substring(0, eq);
+ String val = AnyURIValue.decode(param.substring(eq + 1));
+ props.setProperty(kw, val);
+ }
+ }
+ return Configuration.getPlatform().makeCollation(config, props, uri);
+ } else {
+ return null;
+ }
+ } catch (XPathException e) {
+ try {
+ config.getErrorListener().warning(e);
+ } catch (TransformerException e1) {
+ //
+ }
+ return null;
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/lib/StandardCollectionURIResolver.java b/sf/saxon/lib/StandardCollectionURIResolver.java
new file mode 100644
index 0000000..43d742b
--- /dev/null
+++ b/sf/saxon/lib/StandardCollectionURIResolver.java
@@ -0,0 +1,506 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.Sender;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.functions.DocumentFn;
+import net.sf.saxon.functions.ResolveURI;
+import net.sf.saxon.functions.URIQueryParameters;
+import net.sf.saxon.functions.UnparsedText;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ArrayIterator;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.value.AnyURIValue;
+import net.sf.saxon.value.ObjectValue;
+import net.sf.saxon.value.TextFragmentValue;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.XMLReader;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.stream.StreamSource;
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * This class implements the default collection URI Resolver.
+ * <p>
+ * This supports two implementations of collections. If the URI supplied uses the "file:/" scheme, and the
+ * file that is referenced is a directory, then the collection is the set of files in that directory. Query parameters
+ * may be included in the URI:
+ * <ul>
+ * <li><p>recurse=yes|no controls whether the directory is scanned recursively; </p></li>
+ * <li><p>strip-space=yes|no determines whether whitespace text nodes are stripped from the selected documents; </p></li>
+ * <li><p>validation=strict|lax|preserve|strip determines whether schema validation is applied;</p></li>
+ * <li><p>select=pattern determines which files in the directory are selected.</p></li>
+ * <li><p>on-error=fail|warn|ignore determines the action taken if processing of a file fails</p></li>
+ * <li><p>parser=qualified.class.name selects the parser (XMLReader) to be used to read the files</p></li>
+ * </ul>
+ * <p>
+ * Otherwise, the resolver attempts to dereference the URI to obtain a catalog file. This is an XML file
+ * containing a list of documents, in the format: </p>
+ * <pre>
+ * <collection>
+ * <doc href="doc1.xml"/>
+ * <doc href="doc2.xml"/>
+ * </collection>
+ * </pre>
+ */
+
+public class StandardCollectionURIResolver implements CollectionURIResolver {
+
+ /**
+ * Resolve a URI.
+ *
+ * @param href The relative URI of the collection. This corresponds to the
+ * argument supplied to the collection() function. If the collection() function
+ * was called with no arguments (to get the "default collection") this argument
+ * will be null.
+ * @param base The base URI that should be used. This is the base URI of the
+ * static context in which the call to collection() was made, typically the URI
+ * of the stylesheet or query module
+ * @return an Iterator over the documents in the collection. The items returned
+ * by this iterator must be instances either of xs:anyURI, or of node() (specifically,
+ * {@link net.sf.saxon.om.NodeInfo}.). If xs:anyURI values are returned, the corresponding
+ * document will be retrieved as if by a call to the doc() function: this means that
+ * the system first checks to see if the document is already loaded, and if not, calls
+ * the registered URIResolver to dereference the URI. This is the recommended approach
+ * to ensure that the resulting collection is stable: however, it has the consequence
+ * that the documents will by default remain in memory for the duration of the query
+ * or transformation.
+ */
+
+ public SequenceIterator resolve(/*@Nullable*/ String href, String base, XPathContext context) throws XPathException {
+
+ if (href == null) {
+ XPathException err = new XPathException("No default collection has been defined");
+ err.setErrorCode("FODC0002");
+ err.setXPathContext(context);
+ throw err;
+ }
+
+ URIQueryParameters params = null;
+ URI relativeURI;
+ try {
+ relativeURI = new URI(ResolveURI.escapeSpaces(href));
+ String query = relativeURI.getQuery();
+ if (query != null) {
+ params = new URIQueryParameters(query, context.getConfiguration());
+ int q = href.indexOf('?');
+ href = ResolveURI.escapeSpaces(href.substring(0, q));
+ relativeURI = new URI(href);
+ }
+
+ } catch (URISyntaxException e) {
+ XPathException err = new XPathException("Invalid relative URI " + Err.wrap(href, Err.VALUE) + " passed to collection() function");
+ err.setErrorCode("FODC0004");
+ err.setXPathContext(context);
+ throw err;
+ }
+
+ URI resolvedURI = makeAbsoluteURI(href, base, context, relativeURI);
+
+ if ("file".equals(resolvedURI.getScheme())) {
+ File file = new File(resolvedURI);
+ if (!file.exists()) {
+ XPathException err = new XPathException("The file or directory " + resolvedURI + " does not exist");
+ err.setErrorCode("FODC0002");
+ err.setXPathContext(context);
+ throw err;
+ }
+ if (file.isDirectory()) {
+ return directoryContents(file, params, context);
+ }
+ }
+ return catalogContents(href, base, resolvedURI.toString(), context);
+
+ }
+
+ protected URI makeAbsoluteURI(String href, String base, XPathContext context, URI relativeURI) throws XPathException {
+ URI resolvedURI;
+ if (!relativeURI.isAbsolute()) {
+ if (base == null) {
+ base = ResolveURI.tryToExpand(base);
+ }
+ try {
+ resolvedURI = ResolveURI.makeAbsolute(href, base);
+ } catch (URISyntaxException e) {
+ XPathException err = new XPathException("Cannot resolve relative URI: " + e.getMessage());
+ err.setErrorCode("FODC0004");
+ err.setXPathContext(context);
+ throw err;
+ }
+ } else {
+ resolvedURI = relativeURI;
+ }
+ return resolvedURI;
+ }
+
+ /**
+ * Return the contents of a collection that maps to a directory in filestore
+ * @param directory the directory to be processed
+ * @param params parameters indicating whether to process recursively, what to do on
+ * errors, and which files to select
+ * @param context the dynamic XPath evaluation context
+ * @return an iterator over the documents in the collection
+ */
+
+ protected SequenceIterator<DocumentInfo> directoryContents(File directory, URIQueryParameters params, XPathContext context) {
+
+ FilenameFilter filter = null;
+
+ if (params != null) {
+ FilenameFilter f = params.getFilenameFilter();
+ if (f != null) {
+ filter = f;
+ }
+ }
+
+ File[] files;
+ if (filter == null) {
+ files = directory.listFiles();
+ } else {
+ files = directory.listFiles(filter);
+ }
+
+ ObjectValue[] fileValues = new ObjectValue[files.length];
+ for (int f=0; f<files.length; f++) {
+ fileValues[f] = new ObjectValue(files[f]);
+ }
+
+ // If the URI requested suppression of errors, or that errors should be treated
+ // as warnings, we set up a special ErrorListener to achieve this
+
+ int onError = URIQueryParameters.ON_ERROR_FAIL;
+ if (params != null && params.getOnError() != null) {
+ onError = params.getOnError();
+ }
+ final Controller controller = context.getController();
+ final PipelineConfiguration oldPipe = context.getConfiguration().makePipelineConfiguration();
+ oldPipe.setController(context.getController());
+ final PipelineConfiguration newPipe = new PipelineConfiguration(oldPipe);
+ final ErrorListener oldErrorListener = (controller==null ? new StandardErrorListener() : controller.getErrorListener());
+ if (onError == URIQueryParameters.ON_ERROR_IGNORE) {
+ newPipe.setErrorListener(new ErrorListener() {
+ public void warning(TransformerException exception) {}
+ public void error(TransformerException exception) {}
+ public void fatalError(TransformerException exception) {}
+ });
+ } else if (onError == URIQueryParameters.ON_ERROR_WARNING) {
+ newPipe.setErrorListener(new ErrorListener() {
+ public void warning(TransformerException exception) throws TransformerException {
+ oldErrorListener.warning(exception);
+ }
+ public void error(TransformerException exception) throws TransformerException {
+ oldErrorListener.warning(exception);
+ XPathException supp = new XPathException("The document will be excluded from the collection");
+ supp.setLocator(exception.getLocator());
+ oldErrorListener.warning(supp);
+ }
+ public void fatalError(TransformerException exception) throws TransformerException {
+ error(exception);
+ }
+ });
+ }
+ FileExpander expander = new FileExpander(params, newPipe);
+ SequenceIterator<ObjectValue> base = new ArrayIterator<ObjectValue>(fileValues);
+ return new MappingIterator<ObjectValue, DocumentInfo>(base, expander);
+ }
+
+ /**
+ * Return a collection defined as a list of URIs in a catalog file
+ * @param href the relative URI as supplied
+ * @param baseURI the base URI
+ * @param absURI the absolute URI of the catalog file
+ * @param context the dynamic evaluation context
+ * @return an iterator over the documents in the collection
+ * @throws XPathException if any failures occur
+ */
+
+ protected SequenceIterator<? extends NodeInfo> catalogContents(String href, String baseURI, String absURI, final XPathContext context)
+ throws XPathException {
+
+ boolean stable = true;
+ Source source = DocumentFn.resolveURI(href, baseURI, null, context.getController());
+ ParseOptions options = new ParseOptions();
+ options.setSchemaValidationMode(Validation.SKIP);
+ DocumentInfo catalog = context.getConfiguration().buildDocument(source, options);
+ if (catalog==null) {
+ // we failed to read the catalogue
+ XPathException err = new XPathException("Failed to load collection catalog " + absURI);
+ err.setErrorCode("FODC0004");
+ err.setXPathContext(context);
+ throw err;
+ }
+
+ // Now return an iterator over the documents that it refers to
+
+ AxisIterator iter =
+ catalog.iterateAxis(AxisInfo.CHILD, NodeKindTest.ELEMENT);
+ NodeInfo top = iter.next();
+ if (top == null || !("collection".equals(top.getLocalPart()) && top.getURI().length() == 0)) {
+ String message;
+ if (top == null) {
+ message = "No outermost element found in collection catalog";
+ } else if (top.getURI().length() != 0) {
+ message = "Collection catalog should not use a namespace";
+ } else {
+ message = "Collection catalog outermost element should be <catalog> (found " + top.getLocalPart() + ">)";
+ }
+ XPathException err = new XPathException(message);
+ err.setErrorCode("FODC0004");
+ err.setXPathContext(context);
+ throw err;
+ }
+ iter.close();
+
+ String stableAtt = top.getAttributeValue("", "stable");
+ if (stableAtt != null) {
+ if ("true".equals(stableAtt)) {
+ stable = true;
+ } else if ("false".equals(stableAtt)) {
+ stable = false;
+ } else {
+ XPathException err = new XPathException(
+ "The 'stable' attribute of element <collection> must be true or false");
+ err.setErrorCode("FODC0004");
+ err.setXPathContext(context);
+ throw err;
+ }
+ }
+
+ final boolean finalStable = stable;
+ AxisIterator documents =
+ top.iterateAxis(AxisInfo.CHILD, NodeKindTest.ELEMENT);
+
+ ItemMappingFunction<NodeInfo, Item> catalogueMapper = new ItemMappingFunction<NodeInfo, Item>() {
+ public Item mapItem(NodeInfo item) throws XPathException {
+ if (!("doc".equals(item.getLocalPart()) &&
+ item.getURI().length() == 0)) {
+ XPathException err = new XPathException("children of <collection> element must be <doc> elements");
+ err.setErrorCode("FODC0004");
+ err.setXPathContext(context);
+ throw err;
+ }
+ String href = Navigator.getAttributeValue(item, "", "href");
+ if (href==null) {
+ XPathException err = new XPathException("\"<doc> element in catalog has no @href attribute\"");
+ err.setErrorCode("FODC0004");
+ err.setXPathContext(context);
+ throw err;
+ }
+ String uri;
+ try {
+ uri = new URI(item.getBaseURI()).resolve(href).toString();
+ } catch (URISyntaxException e) {
+ XPathException err = new XPathException("Invalid base URI or href URI in collection catalog: ("
+ + item.getBaseURI() + ", " + href + ")");
+ err.setErrorCode("FODC0004");
+ err.setXPathContext(context);
+ throw err;
+ }
+ if (finalStable) {
+ return new AnyURIValue(uri);
+ } else {
+ // stability not required, bypass the document pool and URI resolver
+ return context.getConfiguration().buildDocument(new StreamSource(uri));
+ }
+ }
+ };
+
+ return new ItemMappingIterator(documents, catalogueMapper);
+ }
+
+ /**
+ * Mapping function to process the files in a directory. This maps a sequence of external
+ * objects representing files to a sequence of DocumentInfo nodes representing the parsed
+ * contents of those files.
+ */
+
+
+ protected static class FileExpander implements MappingFunction<ObjectValue, DocumentInfo> {
+
+ private URIQueryParameters params;
+ boolean recurse = false;
+ int strip = Whitespace.XSLT;
+ int validation = Validation.STRIP;
+ Boolean xinclude = null;
+ boolean unparsed;
+ XMLReader parser = null;
+ int onError = URIQueryParameters.ON_ERROR_FAIL;
+ FilenameFilter filter = null;
+ PipelineConfiguration pipe;
+
+ public FileExpander(URIQueryParameters params, PipelineConfiguration pipe) {
+ this.params = params;
+ this.pipe = pipe;
+ if (params != null) {
+ FilenameFilter f = params.getFilenameFilter();
+ if (f != null) {
+ filter = f;
+ }
+ Boolean r = params.getRecurse();
+ if (r != null) {
+ recurse = r;
+ }
+ Integer v = params.getValidationMode();
+ if (v != null) {
+ validation = v;
+ }
+ xinclude = params.getXInclude();
+ strip = params.getStripSpace();
+ if (strip == Whitespace.UNSPECIFIED) {
+ strip = Whitespace.XSLT;
+ }
+ unparsed = params.isUnparsed();
+ Integer e = params.getOnError();
+ if (e != null) {
+ onError = e;
+ }
+ XMLReader p = params.getXMLReader();
+ if (p != null) {
+ parser = p;
+ }
+ }
+
+ }
+
+ /**
+ * Map one item to a sequence.
+ *
+ * @param item The item to be mapped.
+ * If context is supplied, this must be the same as context.currentItem().
+ * @return either (a) a SequenceIterator over the sequence of items that the supplied input
+ * item maps to, or (b) an Item if it maps to a single item, or (c) null if it maps to an empty
+ * sequence.
+ */
+
+ public SequenceIterator<DocumentInfo> map(ObjectValue item) throws XPathException {
+ File file = (File) item.getObject();
+ if (file.isDirectory()) {
+ if (recurse) {
+ File[] files;
+ if (filter == null) {
+ files = file.listFiles();
+ } else {
+ files = file.listFiles(filter);
+ }
+
+ ObjectValue[] fileValues = new ObjectValue[files.length];
+ for (int f=0; f<files.length; f++) {
+ fileValues[f] = new ObjectValue(files[f]);
+ }
+
+ FileExpander expander = new FileExpander(params, pipe);
+ return new MappingIterator<ObjectValue, DocumentInfo>(new ArrayIterator<ObjectValue>(fileValues), expander);
+ } else {
+ return null;
+ }
+ } else if (unparsed) {
+ try {
+ Reader reader = new FileReader(file);
+ NameChecker checker = pipe.getConfiguration().getNameChecker();
+ CharSequence content = UnparsedText.readFile(checker, reader, null);
+ String uri = file.toURI().toString();
+ TextFragmentValue doc = new TextFragmentValue(content, uri);
+ doc.setSystemId(file.toURI().toString());
+ doc.setConfiguration(pipe.getConfiguration());
+ return SingletonIterator.makeIterator((DocumentInfo)doc);
+ } catch (IOException err) {
+ if (onError == URIQueryParameters.ON_ERROR_IGNORE) {
+ return null;
+ } else if (onError == URIQueryParameters.ON_ERROR_WARNING) {
+ try {
+ XPathException warn = new XPathException("Failed to read " + file.getPath(), err);
+ pipe.getErrorListener().warning(warn);
+ XPathException supp = new XPathException("The document will be excluded from the collection");
+ pipe.getErrorListener().warning(supp);
+ } catch (TransformerException err2) {
+ //
+ }
+ return null;
+ } else {
+ throw new XPathException("Failed to read " + file.getPath(), err);
+ }
+ }
+ } else {
+ try {
+ Source source = new StreamSource(file.toURI().toString());
+ ParseOptions options = new ParseOptions();
+ if (validation != Validation.STRIP && validation != Validation.PRESERVE) {
+ options.setSchemaValidationMode(validation);
+ }
+ if (xinclude != null) {
+ options.setXIncludeAware(xinclude);
+ }
+ if (parser != null) {
+ options.setXMLReader(parser);
+ }
+
+ if (params != null) {
+ int stripSpace = params.getStripSpace();
+ if (stripSpace == Whitespace.UNSPECIFIED) {
+ stripSpace = Whitespace.XSLT;
+ }
+ options.setStripSpace(stripSpace);
+ }
+ if (pipe.getController() != null) {
+ Builder b = pipe.getController().makeBuilder();
+ Receiver s = b;
+ if (pipe.getController().getExecutable().stripsInputTypeAnnotations()) {
+ s = pipe.getConfiguration().getAnnotationStripper(s);
+ }
+ s.setPipelineConfiguration(pipe);
+ Sender.send(source, s, options);
+ DocumentInfo node = (DocumentInfo)b.getCurrentRoot();
+ b.reset();
+ return SingletonIterator.makeIterator(node);
+ } else {
+ DocumentInfo doc = pipe.getConfiguration().buildDocument(source, options);
+ return SingletonIterator.makeIterator(doc);
+ }
+ } catch (XPathException err) {
+ if (onError == URIQueryParameters.ON_ERROR_IGNORE) {
+ return null;
+ } else if (onError == URIQueryParameters.ON_ERROR_WARNING) {
+ try {
+ if (!err.hasBeenReported()) {
+ pipe.getErrorListener().warning(err);
+ XPathException supp = new XPathException("The document will be excluded from the collection");
+ supp.setLocator(err.getLocator());
+ pipe.getErrorListener().warning(supp);
+ }
+ } catch (TransformerException err2) {
+ //
+ }
+ return null;
+ } else {
+ throw err;
+ }
+ }
+ }
+ }
+ }
+
+
+
+
+}
+
diff --git a/sf/saxon/lib/StandardEntityResolver.java b/sf/saxon/lib/StandardEntityResolver.java
new file mode 100644
index 0000000..3374775
--- /dev/null
+++ b/sf/saxon/lib/StandardEntityResolver.java
@@ -0,0 +1,640 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * This class is an EntityResolver used to resolve references to common
+ * DTDs and entity files, using local copies provided with the Saxon product.
+ * It has become necessary to do this because W3C is no longer serving
+ * these files from its server. Ideally the job of caching these files
+ * would belong to the XML parser, but because many of the parsers were
+ * issued years ago, they cannot be relied on to do it.
+ */
+public class StandardEntityResolver implements EntityResolver {
+
+ private static StandardEntityResolver THE_INSTANCE = new StandardEntityResolver();
+
+ /**
+ * Get a general-purpose instance of this class. This should be treated as immutable,
+ * since it is shared with other users. That is, it should not be associated with any
+ * configuration.
+ * @return the singleton, general-purpose instance of this class
+ */
+
+ public static StandardEntityResolver getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private static HashMap<String, String> publicIds = new HashMap<String, String>(30);
+ private static HashMap<String, String> systemIds = new HashMap<String, String>(30);
+
+ public Configuration config = null;
+
+ /**
+ * Register a DTD or other entity to be resolved by this
+ * entity resolver
+ *
+ * @param publicId the public identifier of the DTD or entity
+ * @param systemId the system identifier of the DTD or entity
+ * @param fileName the fileName of the Saxon local copy of the
+ * resource, relative to the data directory in the JAR file
+ */
+
+ public static void register(
+ /*@Nullable*/ String publicId,
+ String systemId,
+ String fileName) {
+ if (publicId != null) {
+ publicIds.put(publicId, fileName);
+ }
+ if (systemId != null) {
+ systemIds.put(systemId, fileName);
+ }
+ }
+
+ static {
+
+ register("-//W3C//ENTITIES Latin 1 for XHTML//EN",
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent",
+ "w3c/xhtml-lat1.ent");
+
+ register("-//W3C//ENTITIES Symbols for XHTML//EN",
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent",
+ "w3c/xhtml-symbol.ent");
+
+ register("-//W3C//ENTITIES Special for XHTML//EN",
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent",
+ "w3c/xhtml-special.ent");
+
+ register("-//W3C//DTD XHTML 1.0 Transitional//EN",
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd",
+ "w3c/xhtml10/xhtml1-transitional.dtd");
+
+ register("-//W3C//DTD XHTML 1.0 Strict//EN",
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd",
+ "w3c/xhtml10/xhtml1-strict.dtd");
+
+ register("-//W3C//DTD XHTML 1.0 Frameset//EN",
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd",
+ "w3c/xhtml10/xhtml1-frameset.dtd");
+
+ register("-//W3C//DTD XHTML Basic 1.0//EN",
+ "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd",
+ "w3c/xhtml10/xhtml-basic10.dtd");
+
+ register("-//W3C//DTD XHTML 1.1//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml11.dtd",
+ "w3c/xhtml11/xhtml11.dtd");
+
+ register("-//W3C//DTD XHTML Basic 1.1//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-basic11.dtd",
+ "w3c/xhtml11/xhtml-basic11.dtd");
+
+ register("-//W3C//ELEMENTS XHTML Access Element 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-access-1.mod",
+ "w3c/xhtml11/xhtml-access-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Access Attribute Qnames 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-access-qname-1.mod",
+ "w3c/xhtml11/xhtml-access-qname-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Java Applets 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-applet-1.mod",
+ "w3c/xhtml11/xhtml-applet-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Base Architecture 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-arch-1.mod",
+ "w3c/xhtml11/xhtml-arch-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Common Attributes 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-attribs-1.mod",
+ "w3c/xhtml11/xhtml-attribs-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Base Element 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-base-1.mod",
+ "w3c/xhtml11/xhtml-base-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Basic Forms 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-basic-form-1.mod",
+ "w3c/xhtml11/xhtml-basic-form-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Basic Tables 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-basic-table-1.mod",
+ "w3c/xhtml11/xhtml-basic-table-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Basic 1.0 Document Model 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-basic10-model-1.mod",
+ "w3c/xhtml11/xhtml-basic10-model-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Basic 1.1 Document Model 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-basic11-model-1.mod",
+ "w3c/xhtml11/xhtml-basic11-model-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML BDO Element 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-bdo-1.mod",
+ "w3c/xhtml11/xhtml-bdo-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Block Phrasal 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-blkphras-1.mod",
+ "w3c/xhtml11/xhtml-blkphras-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Block Presentation 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-blkpres-1.mod",
+ "w3c/xhtml11/xhtml-blkpres-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Block Structural 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-blkstruct-1.mod",
+ "w3c/xhtml11/xhtml-blkstruct-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Character Entities 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-charent-1.mod",
+ "w3c/xhtml11/xhtml-charent-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Client-side Image Maps 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-csismap-1.mod",
+ "w3c/xhtml11/xhtml-csismap-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Datatypes 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-datatypes-1.mod",
+ "w3c/xhtml11/xhtml-datatypes-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Editing Markup 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-edit-1.mod",
+ "w3c/xhtml11/xhtml-edit-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Intrinsic Events 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-events-1.mod",
+ "w3c/xhtml11/xhtml-events-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Forms 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-form-1.mod",
+ "w3c/xhtml11/xhtml-form-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Frames 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-frames-1.mod",
+ "w3c/xhtml11/xhtml-frames-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Modular Framework 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-framework-1.mod",
+ "w3c/xhtml11/xhtml-framework-1.mod");
+
+ register("-//W3C//ENTITIES XHTML HyperAttributes 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-hyperAttributes-1.mod",
+ "w3c/xhtml11/xhtml-hyperAttributes-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Hypertext 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-hypertext-1.mod",
+ "w3c/xhtml11/xhtml-hypertext-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Inline Frame Element 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-iframe-1.mod",
+ "w3c/xhtml11/xhtml-iframe-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Images 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-image-1.mod",
+ "w3c/xhtml11/xhtml-image-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Inline Phrasal 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-inlphras-1.mod",
+ "w3c/xhtml11/xhtml-inlphras-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Inline Presentation 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-inlpres-1.mod",
+ "w3c/xhtml11/xhtml-inlpres-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Inline Structural 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-inlstruct-1.mod",
+ "w3c/xhtml11/xhtml-inlstruct-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Inline Style 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-inlstyle-1.mod",
+ "w3c/xhtml11/xhtml-inlstyle-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Inputmode 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-inputmode-1.mod",
+ "w3c/xhtml11/xhtml-inputmode-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Legacy Markup 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-legacy-1.mod",
+ "w3c/xhtml11/xhtml-legacy-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Legacy Redeclarations 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-legacy-redecl-1.mod",
+ "w3c/xhtml11/xhtml-legacy-redecl-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Link Element 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-link-1.mod",
+ "w3c/xhtml11/xhtml-link-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Lists 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-list-1.mod",
+ "w3c/xhtml11/xhtml-list-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Metainformation 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-meta-1.mod",
+ "w3c/xhtml11/xhtml-meta-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Metainformation 2.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-meta-2.mod",
+ "w3c/xhtml11/xhtml-meta-2.mod");
+
+ register("-//W3C//ENTITIES XHTML MetaAttributes 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-metaAttributes-1.mod",
+ "w3c/xhtml11/xhtml-metaAttributes-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Name Identifier 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-nameident-1.mod",
+ "w3c/xhtml11/xhtml-nameident-1.mod");
+
+ register("-//W3C//NOTATIONS XHTML Notations 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-notations-1.mod",
+ "w3c/xhtml11/xhtml-notations-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Embedded Object 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-object-1.mod",
+ "w3c/xhtml11/xhtml-object-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Param Element 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-param-1.mod",
+ "w3c/xhtml11/xhtml-param-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Presentation 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-pres-1.mod",
+ "w3c/xhtml11/xhtml-pres-1.mod");
+
+ register("-//W3C//ENTITIES XHTML-Print 1.0 Document Model 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-print10-model-1.mod",
+ "w3c/xhtml11/xhtml-print10-model-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Qualified Names 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-qname-1.mod",
+ "w3c/xhtml11/xhtml-qname-1.mod");
+
+ register("-//W3C//ENTITIES XHTML+RDFa Document Model 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-model-1.mod",
+ "w3c/xhtml11/xhtml-rdfa-model-1.mod");
+
+ register("-//W3C//ENTITIES XHTML RDFa Attribute Qnames 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-qname-1.mod",
+ "w3c/xhtml11/xhtml-rdfa-qname-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Role Attribute 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-role-1.mod",
+ "w3c/xhtml11/xhtml-role-1.mod");
+
+ register("-//W3C//ENTITIES XHTML Role Attribute Qnames 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-role-qname-1.mod",
+ "w3c/xhtml11/xhtml-role-qname-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Ruby 1.0//EN",
+ "http://www.w3.org/TR/ruby/xhtml-ruby-1.mod",
+ "w3c/xhtml11/xhtml-ruby-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Scripting 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-script-1.mod",
+ "w3c/xhtml11/xhtml-script-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Server-side Image Maps 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-ssismap-1.mod",
+ "w3c/xhtml11/xhtml-ssismap-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Document Structure 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-struct-1.mod",
+ "w3c/xhtml11/xhtml-struct-1.mod");
+
+ register("-//W3C//DTD XHTML Style Sheets 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-style-1.mod",
+ "w3c/xhtml11/xhtml-style-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Tables 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-table-1.mod",
+ "w3c/xhtml11/xhtml-table-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Target 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-target-1.mod",
+ "w3c/xhtml11/xhtml-target-1.mod");
+
+ register("-//W3C//ELEMENTS XHTML Text 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml-text-1.mod",
+ "w3c/xhtml11/xhtml-text-1.mod");
+
+ register("-//W3C//ENTITIES XHTML 1.1 Document Model 1.0//EN",
+ "http://www.w3.org/MarkUp/DTD/xhtml11-model-1.mod",
+ "w3c/xhtml11/xhtml11-model-1.mod");
+
+ register("-//W3C//MathML 1.0//EN",
+ "http://www.w3.org/Math/DTD/mathml1/mathml.dtd",
+ "w3c/mathml/mathml1/mathml.dtd");
+
+ register("-//W3C//DTD MathML 2.0//EN",
+ "http://www.w3.org/Math/DTD/mathml2/mathml2.dtd",
+ "w3c/mathml/mathml2/mathml2.dtd");
+
+ register("-//W3C//DTD MathML 3.0//EN",
+ "http://www.w3.org/Math/DTD/mathml3/mathml3.dtd",
+ "w3c/mathml/mathml3/mathml3.dtd");
+
+ register("-//W3C//DTD SVG 1.0//EN",
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd",
+ "w3c/svg10/svg10.dtd");
+
+ register("-//W3C//DTD SVG 1.1//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd",
+ "w3c/svg11/svg11.dtd");
+
+ register("-//W3C//DTD SVG 1.1 Tiny//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd",
+ "w3c/svg11/svg11-tiny.dtd");
+
+ register("-//W3C//DTD SVG 1.1 Basic//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd",
+ "w3c/svg11/svg11-basic.dtd");
+
+ register("-//W3C//ENTITIES SVG 1.1 Document Model//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-model.mod",
+ "w3c/svg11/svg11-model.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Attribute Collection//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-attribs.mod",
+ "w3c/svg11/svg11-attribs.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Modular Framework//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-framework.mod",
+ "w3c/svg11/svg-framework.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Datatypes//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-datatypes.mod",
+ "w3c/svg11/svg-datatypes.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Qualified Name//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-qname.mod",
+ "w3c/svg11/svg-qname.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Core Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-core-attrib.mod",
+ "w3c/svg11/svg-core-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Container Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-container-attrib.mod",
+ "w3c/svg11/svg-container-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Viewport Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-viewport-attrib.mod",
+ "w3c/svg11/svg-viewport-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Paint Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-paint-attrib.mod",
+ "w3c/svg11/svg-paint-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Paint Opacity Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-opacity-attrib.mod",
+ "w3c/svg11/svg-opacity-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Graphics Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-graphics-attrib.mod",
+ "w3c/svg11/svg-graphics-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Document Events Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-docevents-attrib.mod",
+ "w3c/svg11/svg-docevents-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Graphical Element Events Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-graphevents-attrib.mod",
+ "w3c/svg11/svg-graphevents-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 Animation Events Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-animevents-attrib.mod",
+ "w3c/svg11/svg-animevents-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 XLink Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-xlink-attrib.mod",
+ "w3c/svg11/svg-xlink-attrib.mod");
+
+ register("-//W3C//ENTITIES SVG 1.1 External Resources Attribute//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-extresources-attrib.mod",
+ "w3c/svg11/svg-extresources-attrib.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Structure//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-structure.mod",
+ "w3c/svg11/svg-structure.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Conditional Processing//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-conditional.mod",
+ "w3c/svg11/svg-conditional.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Image//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-image.mod",
+ "w3c/svg11/svg-image.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Style//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-style.mod",
+ "w3c/svg11/svg-style.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Shape//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-shape.mod",
+ "w3c/svg11/svg-shape.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Text//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-text.mod",
+ "w3c/svg11/svg-text.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Marker//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-marker.mod",
+ "w3c/svg11/svg-marker.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Color Profile//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-profile.mod",
+ "w3c/svg11/svg-profile.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Gradient//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-gradient.mod",
+ "w3c/svg11/svg-gradient.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Pattern//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-pattern.mod",
+ "w3c/svg11/svg-pattern.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Clip//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-clip.mod",
+ "w3c/svg11/svg-clip.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Mask//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-mask.mod",
+ "w3c/svg11/svg-mask.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Filter//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-filter.mod",
+ "w3c/svg11/svg-filter.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Cursor//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-cursor.mod",
+ "w3c/svg11/svg-cursor.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Hyperlinking//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-hyperlink.mod",
+ "w3c/svg11/svg-hyperlink.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 View//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-view.mod",
+ "w3c/svg11/svg-view.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Scripting//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-script.mod",
+ "w3c/svg11/svg-script.mod");
+
+
+ register("-//W3C//ELEMENTS SVG 1.1 Animation//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-animation.mod",
+ "w3c/svg11/svg-animation.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Font//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-font.mod",
+ "w3c/svg11/svg-font.mod");
+
+ register("-//W3C//ELEMENTS SVG 1.1 Extensibility//EN",
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg-extensibility.mod",
+ "w3c/svg11/svg-extensibility.mod");
+
+
+ register("-//XML-DEV//ENTITIES RDDL Document Model 1.0//EN",
+ "http://www.rddl.org/xhtml-rddl-model-1.mod",
+ "w3c/rddl/xhtml-rddl-model-1.mod");
+
+ register("-//XML-DEV//DTD XHTML RDDL 1.0//EN",
+ "http://www.rddl.org/rddl-xhtml.dtd",
+ "w3c/rddl/rddl-xhtml.dtd");
+
+ register("-//XML-DEV//ENTITIES RDDL QName Module 1.0//EN",
+ "http://www.rddl.org/rddl-qname-1.mod",
+ "w3c/rddl/rddl-qname-1.mod");
+
+ register("-//XML-DEV//ENTITIES RDDL Resource Module 1.0//EN",
+ "http://www.rddl.org/rddl-resource-1.mod",
+ "w3c/rddl/rddl-resource-1.mod");
+
+ register("-//W3C//DTD Specification V2.10//EN",
+ "http://www.w3.org/2002/xmlspec/dtd/2.10/xmlspec.dtd",
+ "w3c/xmlspec/xmlspec.dtd");
+
+ register("-//W3C//DTD XMLSCHEMA 200102//EN",
+ "http://www.w3.org/2001/XMLSchema.dtd",
+ "w3c/xmlschema/XMLSchema.dtd");
+
+ register("datatypes",
+ "http://www.w3.org/2001/datatypes.dtd",
+ "w3c/xmlschema/datatypes.dtd");
+ }
+
+ /**
+ * Set configuration details. This is used to control tracing of accesses to files
+ *
+ * @param config the Saxon configuration
+ */
+
+ public void setConfiguration(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Allow the application to resolve external entities.
+ * <p/>
+ * <p>The parser will call this method before opening any external
+ * entity except the top-level document entity. Such entities include
+ * the external DTD subset and external parameter entities referenced
+ * within the DTD (in either case, only if the parser reads external
+ * parameter entities), and external general entities referenced
+ * within the document element (if the parser reads external general
+ * entities). The application may request that the parser locate
+ * the entity itself, that it use an alternative URI, or that it
+ * use data provided by the application (as a character or byte
+ * input stream).</p>
+ * <p/>
+ * <p>Application writers can use this method to redirect external
+ * system identifiers to secure and/or local URIs, to look up
+ * public identifiers in a catalogue, or to read an entity from a
+ * database or other input source (including, for example, a dialog
+ * box). Neither XML nor SAX specifies a preferred policy for using
+ * public or system IDs to resolve resources. However, SAX specifies
+ * how to interpret any InputSource returned by this method, and that
+ * if none is returned, then the system ID will be dereferenced as
+ * a URL. </p>
+ * <p/>
+ * <p>If the system identifier is a URL, the SAX parser must
+ * resolve it fully before reporting it to the application.</p>
+ *
+ * @param publicId The public identifier of the external entity
+ * being referenced, or null if none was supplied.
+ * @param systemId The system identifier of the external entity
+ * being referenced.
+ * @return An InputSource object describing the new input source,
+ * or null to request that the parser open a regular
+ * URI connection to the system identifier.
+ * @throws org.xml.sax.SAXException Any SAX exception, possibly
+ * wrapping another exception.
+ * @throws java.io.IOException A Java-specific IO exception,
+ * possibly the result of creating a new InputStream
+ * or Reader for the InputSource.
+ * @see org.xml.sax.InputSource
+ */
+ public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
+ // See if it's a known public ID
+ String fileName = publicIds.get(publicId);
+ if (fileName != null) {
+ return fetch(fileName, config);
+ }
+
+ // See if it's a known system ID
+ fileName = systemIds.get(systemId);
+ if (fileName != null) {
+ return fetch(fileName, config);
+ }
+
+ // Otherwise, leave the parser to resolve the URI in the normal way
+ return null;
+ }
+
+ public static InputSource fetch(String filename, Configuration config) {
+ boolean tracing = false;
+ PrintStream traceDestination = null;
+ if (config != null) {
+ tracing = config.isTiming();
+ traceDestination = config.getStandardErrorOutput();
+ }
+ if (tracing) {
+ if (traceDestination == null) {
+ traceDestination = System.err;
+ }
+ traceDestination.println("Fetching Saxon copy of " + filename);
+ }
+ List<String> messages = new ArrayList<String>();
+ List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
+ InputStream in = Configuration.locateResource(filename, messages, classLoaders);
+ if (tracing) {
+ for (String s : messages) {
+ traceDestination.println(s);
+ }
+ }
+ if (in == null) {
+ return null;
+ }
+ return new InputSource(in);
+ }
+}
+
diff --git a/sf/saxon/lib/StandardEnvironmentVariableResolver.java b/sf/saxon/lib/StandardEnvironmentVariableResolver.java
new file mode 100644
index 0000000..7d2b1a2
--- /dev/null
+++ b/sf/saxon/lib/StandardEnvironmentVariableResolver.java
@@ -0,0 +1,44 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Default implementation of the {@link EnvironmentVariableResolver}. This implementation
+ * maps "environment variables" as defined in the XPath context to environment variables
+ * delivered by Java using the {@link System#getenv()} method.
+ */
+
+public class StandardEnvironmentVariableResolver implements EnvironmentVariableResolver {
+
+ /**
+ * Get the list of available environment variables.
+ *
+ * @return a set of strings; each such string should be an acceptable argument to the
+ * method {@link #getEnvironmentVariable(String)}
+ */
+ public Set<String> getAvailableEnvironmentVariables() {
+ Map<String, String> vars = System.getenv();
+ return vars.keySet();
+ }
+
+ /**
+ * Get the value of a specific environment variable
+ *
+ * @param name the name of the required environment variable
+ * @return the value of the named environment variable, or null if the variable is
+ * not defined. The method must not return null if the name is in the list of variables
+ * returned by the method {@link #getAvailableEnvironmentVariables()}
+ */
+ public String getEnvironmentVariable(String name) {
+ return System.getenv(name);
+ }
+}
+
diff --git a/sf/saxon/lib/StandardErrorHandler.java b/sf/saxon/lib/StandardErrorHandler.java
new file mode 100644
index 0000000..efee83d
--- /dev/null
+++ b/sf/saxon/lib/StandardErrorHandler.java
@@ -0,0 +1,157 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * A default implementation of the SAX ErrorHandler interface. Used by Saxon to catch XML parsing errors
+ * if no error handler is supplied by the application.
+ */
+
+public class StandardErrorHandler implements org.xml.sax.ErrorHandler {
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Implement the org.xml.sax.ErrorHandler interface.
+ ////////////////////////////////////////////////////////////////////////////
+
+ private ErrorListener errorListener;
+ private Writer errorOutput;
+ private int warningCount = 0;
+ private int errorCount = 0;
+ private int fatalErrorCount = 0;
+
+ public StandardErrorHandler(ErrorListener listener) {
+ errorListener = listener;
+ }
+
+ /**
+ * Set output for error messages produced by the default error handler.
+ * The default error handler does not throw an exception
+ * for parse errors or input I/O errors, rather it returns a result code and
+ * writes diagnostics to a user-specified output writer, which defaults to
+ * System.err<BR>
+ * This call has no effect if setErrorHandler() has been called to supply a
+ * user-defined error handler
+ * @param writer The Writer to use for error messages
+ */
+
+ public void setErrorOutput(Writer writer) {
+ errorOutput = writer;
+ }
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void warning (SAXParseException e) {
+ if (errorListener != null) {
+ try {
+ warningCount++;
+ errorListener.warning(new TransformerException(e));
+ } catch (Exception ignored) {}
+ }
+ }
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void error (SAXParseException e) throws SAXException {
+ //System.err.println("ErrorHandler.error " + e.getMessage());
+ errorCount++;
+ reportError(e, false);
+ }
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void fatalError (/*@NotNull*/ SAXParseException e) throws SAXException {
+ //System.err.println("ErrorHandler.fatalError " + e.getMessage());
+ fatalErrorCount++;
+ reportError(e, true);
+ throw e;
+ }
+
+ /**
+ * Common routine for SAX errors and fatal errors
+ * @param e the exception being handled
+ * @param isFatal true if the error is classified as fatal
+ */
+
+ protected void reportError (SAXParseException e, boolean isFatal) {
+ if (errorListener != null) {
+ try {
+ ExpressionLocation loc =
+ new ExpressionLocation(e.getSystemId(), e.getLineNumber(), e.getColumnNumber());
+ XPathException err = new XPathException("Error reported by XML parser", loc, e);
+ err.setErrorCode(SaxonErrorCode.SXXP0003);
+ if (isFatal) {
+ errorListener.fatalError(err);
+ } else {
+ errorListener.error(err);
+ }
+ } catch (Exception ignored) {}
+ } else {
+
+ try {
+ if (errorOutput == null) {
+ errorOutput = new PrintWriter(System.err);
+ }
+ String errcat = (isFatal ? "Fatal error" : "Error");
+ errorOutput.write(errcat + " reported by XML parser: " + e.getMessage() + '\n');
+ errorOutput.write(" URL: " + e.getSystemId() + '\n');
+ errorOutput.write(" Line: " + e.getLineNumber() + '\n');
+ errorOutput.write(" Column: " + e.getColumnNumber() + '\n');
+ errorOutput.flush();
+ } catch (Exception e2) {
+ System.err.println(e);
+ System.err.println(e2);
+ e2.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Return the number of warnings (including warnings) reported
+ * @return the number of warnings
+ */
+
+ public int getWarningCount() {
+ return warningCount;
+ }
+
+ /**
+ * Return the number of errors reported
+ * @return the number of non-fatal errors
+ */
+
+ public int getErrorCount() {
+ return errorCount;
+ }
+
+ /**
+ * Return the number of fatal errors reported
+ * @return the number of fatal errors
+ */
+
+ public int getFatalErrorCount() {
+ return fatalErrorCount;
+ }
+}
+
diff --git a/sf/saxon/lib/StandardErrorListener.java b/sf/saxon/lib/StandardErrorListener.java
new file mode 100644
index 0000000..4c73a00
--- /dev/null
+++ b/sf/saxon/lib/StandardErrorListener.java
@@ -0,0 +1,651 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ContextStackFrame;
+import net.sf.saxon.trace.ContextStackIterator;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.KeyDefinition;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.ValidationException;
+import org.xml.sax.SAXException;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.dom.DOMLocator;
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.util.Iterator;
+
+/**
+ * <B>StandardErrorListener</B> is the standard error handler for XSLT and XQuery processing
+ * errors, used if no other ErrorListener is nominated.
+ *
+ * @author Michael H. Kay
+ */
+
+public class StandardErrorListener implements ErrorListener, Serializable {
+
+ private int recoveryPolicy = Configuration.RECOVER_WITH_WARNINGS;
+ private int warningCount = 0;
+ private int maximumNumberOfWarnings = 25;
+ protected transient PrintStream errorOutput = System.err;
+
+ /**
+ * Create a Standard Error Listener
+ */
+
+ public StandardErrorListener() {
+ }
+
+ /**
+ * Make a clean copy of this ErrorListener. This is necessary because the
+ * standard error listener is stateful (it remembers how many errors there have been)
+ *
+ * @param hostLanguage the host language (not used by this implementation)
+ * @return a copy of this error listener
+ */
+
+ public StandardErrorListener makeAnother(int hostLanguage) {
+ StandardErrorListener sel;
+ try {
+ sel = this.getClass().newInstance();
+ } catch (InstantiationException e) {
+ sel = new StandardErrorListener();
+ } catch (IllegalAccessException e) {
+ sel = new StandardErrorListener();
+ }
+ sel.errorOutput = errorOutput;
+ return sel;
+ }
+
+ // Note, when the standard error listener is used, a new
+ // one is created for each transformation, because it holds
+ // the recovery policy and the warning count.
+
+ /**
+ * Set output destination for error messages (default is System.err)
+ *
+ * @param writer The PrintStream to use for error messages
+ */
+
+ public void setErrorOutput(PrintStream writer) {
+ errorOutput = writer;
+ }
+
+ /**
+ * Get the error output stream
+ *
+ * @return the error output stream
+ */
+
+ public PrintStream getErrorOutput() {
+ return errorOutput;
+ }
+
+ /**
+ * Set the recovery policy
+ *
+ * @param policy the recovery policy for XSLT recoverable errors. One of
+ * {@link Configuration#RECOVER_SILENTLY},
+ * {@link Configuration#RECOVER_WITH_WARNINGS},
+ * {@link Configuration#DO_NOT_RECOVER}.
+ */
+
+ public void setRecoveryPolicy(int policy) {
+ recoveryPolicy = policy;
+ }
+
+ /**
+ * Get the recovery policy
+ *
+ * @return the recovery policy for XSLT recoverable errors. One of
+ * {@link Configuration#RECOVER_SILENTLY},
+ * {@link Configuration#RECOVER_WITH_WARNINGS},
+ * {@link Configuration#DO_NOT_RECOVER}.
+ */
+
+ public int getRecoveryPolicy() {
+ return recoveryPolicy;
+ }
+
+ /**
+ * Set the maximum number of warnings that are reported; further warnings after this limit
+ * are silently ignored
+ * @param max the maximum number of warnings output
+ */
+
+ public void setMaximumNumberOfWarnings(int max) {
+ this.maximumNumberOfWarnings = max;
+ }
+
+ /**
+ * Get the maximum number of warnings that are reported; further warnings after this limit
+ * are silently ignored
+ * @return the maximum number of warnings output
+ */
+
+ public int getMaximumNumberOfWarnings() {
+ return this.maximumNumberOfWarnings;
+ }
+
+ /**
+ * Receive notification of a warning.
+ * <p/>
+ * <p>Transformers can use this method to report conditions that
+ * are not errors or fatal errors. The default behaviour is to
+ * take no action.</p>
+ * <p/>
+ * <p>After invoking this method, the Transformer must continue with
+ * the transformation. It should still be possible for the
+ * application to process the document through to the end.</p>
+ *
+ * @param exception The warning information encapsulated in a
+ * transformer exception.
+ * @throws javax.xml.transform.TransformerException
+ * if the application
+ * chooses to discontinue the transformation.
+ * @see javax.xml.transform.TransformerException
+ */
+
+ public void warning(TransformerException exception)
+ throws TransformerException {
+
+ if (recoveryPolicy == Configuration.RECOVER_SILENTLY) {
+ // do nothing
+ return;
+ }
+
+ if (errorOutput == null) {
+ // can happen after deserialization
+ errorOutput = System.err;
+ }
+ String message = "";
+ if (exception.getLocator() != null) {
+ message = getLocationMessage(exception) + "\n ";
+ }
+ message += wordWrap(getExpandedMessage(exception));
+
+ if (exception instanceof ValidationException) {
+ errorOutput.println("Validation error " + message);
+
+ } else {
+ errorOutput.println("Warning: " + message);
+ warningCount++;
+ if (warningCount > getMaximumNumberOfWarnings()) {
+ errorOutput.println("No more warnings will be displayed");
+ recoveryPolicy = Configuration.RECOVER_SILENTLY;
+ warningCount = 0;
+ }
+ }
+ }
+
+ /**
+ * Receive notification of a recoverable error.
+ * <p/>
+ * <p>The transformer must continue to provide normal parsing events
+ * after invoking this method. It should still be possible for the
+ * application to process the document through to the end.</p>
+ * <p/>
+ * <p>The action of the standard error listener depends on the
+ * recovery policy that has been set, which may be one of RECOVER_SILENTLY,
+ * RECOVER_WITH_WARNING, or DO_NOT_RECOVER
+ *
+ * @param exception The error information encapsulated in a
+ * transformer exception.
+ * @throws TransformerException if the application
+ * chooses to discontinue the transformation.
+ * @see TransformerException
+ */
+
+ public void error(TransformerException exception) throws TransformerException {
+ if (recoveryPolicy == Configuration.RECOVER_SILENTLY && !(exception instanceof ValidationException)) {
+ // do nothing
+ return;
+ }
+ if (errorOutput == null) {
+ // can happen after deserialization
+ errorOutput = System.err;
+ }
+ String message;
+ if (exception instanceof ValidationException) {
+ String explanation = getExpandedMessage(exception);
+ String constraintReference = ((ValidationException)exception).getConstraintReferenceMessage();
+ String validationLocation = ((ValidationException)exception).getValidationLocationText();
+ String contextLocation = ((ValidationException)exception).getContextLocationText();
+ message = "Validation error " +
+ getLocationMessage(exception) +
+ "\n " +
+ wordWrap(explanation) +
+ wordWrap(constraintReference != null ? "" : ("\n " + constraintReference)) +
+ wordWrap(validationLocation.length()==0 ? "" : ("\n " + validationLocation)) +
+ wordWrap(contextLocation.length()==0 ? "" : ("\n " + contextLocation));
+ } else {
+ String prefix = (recoveryPolicy == Configuration.RECOVER_WITH_WARNINGS ?
+ "Recoverable error " : "Error ");
+ message = prefix + getLocationMessage(exception) +
+ "\n " +
+ wordWrap(getExpandedMessage(exception));
+ }
+
+ if (exception instanceof ValidationException) {
+ errorOutput.println(message);
+
+ } else if (recoveryPolicy == Configuration.RECOVER_WITH_WARNINGS) {
+ errorOutput.println(message);
+ warningCount++;
+ if (warningCount > getMaximumNumberOfWarnings()) {
+ errorOutput.println("No more warnings will be displayed");
+ recoveryPolicy = Configuration.RECOVER_SILENTLY;
+ warningCount = 0;
+ }
+ } else {
+ errorOutput.println(message);
+ errorOutput.println("Processing terminated because error recovery is disabled");
+ throw XPathException.makeXPathException(exception);
+ }
+ }
+
+ /**
+ * Receive notification of a non-recoverable error.
+ * <p/>
+ * <p>The application must assume that the transformation cannot
+ * continue after the Transformer has invoked this method,
+ * and should continue (if at all) only to collect
+ * addition error messages. In fact, Transformers are free
+ * to stop reporting events once this method has been invoked.</p>
+ *
+ * @param exception The error information encapsulated in a
+ * transformer exception.
+ * @throws TransformerException if the application
+ * chooses to discontinue the transformation.
+ * @see TransformerException
+ */
+
+ public void fatalError(TransformerException exception) throws TransformerException {
+ if (exception instanceof XPathException && ((XPathException)exception).hasBeenReported()) {
+ // don't report the same error twice
+ return;
+ }
+ if (errorOutput == null) {
+ // can happen after deserialization
+ errorOutput = System.err;
+ }
+ String message;
+ if (exception instanceof ValidationException) {
+ String explanation = getExpandedMessage(exception);
+ String constraintReference = ((ValidationException)exception).getConstraintReferenceMessage();
+ if (constraintReference != null) {
+ explanation += " (" + constraintReference + ')';
+ }
+ message = "Validation error " +
+ getLocationMessage(exception) +
+ "\n " +
+ wordWrap(explanation);
+ } else {
+ message = "Error " +
+ getLocationMessage(exception) +
+ "\n " +
+ wordWrap(getExpandedMessage(exception));
+
+ }
+
+ errorOutput.println(message);
+ if (exception instanceof XPathException) {
+ ((XPathException)exception).setHasBeenReported(true);
+ // probably redundant. It's the caller's job to set this flag, because there might be
+ // a non-standard error listener in use.
+ }
+
+ if (exception instanceof XPathException) {
+ XPathContext context = ((XPathException)exception).getXPathContext();
+ if (context != null && getRecoveryPolicy() != Configuration.RECOVER_SILENTLY) {
+ outputStackTrace(errorOutput, context);
+ }
+ }
+ }
+
+ /**
+ * Generate a stack trace. This method is protected so it can be overridden in a subclass.
+ * @param out the destination for the stack trace
+ * @param context the context (which holds the information to be output)
+ */
+
+ protected void outputStackTrace(PrintStream out, XPathContext context) {
+ printStackTrace(out, context);
+ }
+
+ /**
+ * Get a string identifying the location of an error.
+ *
+ * @param err the exception containing the location information
+ * @return a message string describing the location
+ */
+
+ public String getLocationMessage(TransformerException err) {
+ SourceLocator loc = err.getLocator();
+ while (loc == null) {
+ if (err.getException() instanceof TransformerException) {
+ err = (TransformerException)err.getException();
+ loc = err.getLocator();
+ } else if (err.getCause() instanceof TransformerException) {
+ err = (TransformerException)err.getCause();
+ loc = err.getLocator();
+ } else {
+ return "";
+ }
+ }
+ return getLocationMessageText(loc);
+ }
+
+ private static String getLocationMessageText(SourceLocator loc) {
+ String locMessage = "";
+ String systemId = null;
+ NodeInfo node = null;
+ String path = null;
+ String nodeMessage = null;
+ int lineNumber = -1;
+ if (loc instanceof DOMLocator) {
+ nodeMessage = "at " + ((DOMLocator)loc).getOriginatingNode().getNodeName() + ' ';
+ } else if (loc instanceof NodeInfo) {
+ node = (NodeInfo)loc;
+ nodeMessage = "at " + node.getDisplayName() + ' ';
+ } else if (loc instanceof ValidationException && (node = ((ValidationException)loc).getNode()) != null) {
+ nodeMessage = "at " + node.getDisplayName() + ' ';
+ } else if (loc instanceof ValidationException && loc.getLineNumber() == -1 && (path = ((ValidationException)loc).getPath()) != null) {
+ nodeMessage = "at " + path + ' ';
+ } else if (loc instanceof Instruction) {
+ String instructionName = getInstructionName(((Instruction)loc));
+ if (!"".equals(instructionName)) {
+ nodeMessage = "at " + instructionName + ' ';
+ }
+ systemId = loc.getSystemId();
+ lineNumber = loc.getLineNumber();
+ } else if (loc instanceof Procedure) {
+ String kind = "procedure";
+ if (loc instanceof UserFunction) {
+ kind = "function";
+ } else if (loc instanceof Template) {
+ kind = "template";
+ } else if (loc instanceof AttributeSet) {
+ kind = "attribute-set";
+ } else if (loc instanceof KeyDefinition) {
+ kind = "key";
+ }
+ systemId = loc.getSystemId();
+ lineNumber = loc.getLineNumber();
+ nodeMessage = "at " + kind + " ";
+ StructuredQName name = ((InstructionInfo)loc).getObjectName();
+ if (name != null) {
+ nodeMessage += name.toString();
+ nodeMessage += " ";
+ }
+ }
+ if (lineNumber == -1) {
+ lineNumber = loc.getLineNumber();
+ }
+ boolean containsLineNumber = lineNumber != -1;
+ if (node != null && !containsLineNumber) {
+ nodeMessage = "at " + Navigator.getPath(node) + ' ';
+ }
+ if (nodeMessage != null) {
+ locMessage += nodeMessage;
+ }
+ if (containsLineNumber) {
+ locMessage += "on line " + lineNumber + ' ';
+ if (loc.getColumnNumber() != -1) {
+ locMessage += "column " + loc.getColumnNumber() + ' ';
+ }
+ }
+
+ if (systemId != null && systemId.length() == 0) {
+ systemId = null;
+ }
+ if (systemId == null) {
+ systemId = loc.getSystemId();
+ }
+ if (systemId != null && systemId.length() != 0) {
+ locMessage += (containsLineNumber ? "of " : "in ") + abbreviatePath(systemId) + ':';
+ }
+ return locMessage;
+ }
+
+ /**
+ * Abbreviate a URI (if requested)
+ * @param uri the URI to be abbreviated
+ * @return the abbreviated URI, unless full path names were requested, in which case
+ * the URI as supplied
+ */
+
+ /*@Nullable*/ public static String abbreviatePath(String uri) {
+ if (uri == null) {
+ return null;
+ }
+ int slash = uri.lastIndexOf('/');
+ if (slash >= 0 && slash < uri.length()-1) {
+ return uri.substring(slash+1);
+ } else {
+ return uri;
+ }
+ }
+
+ /**
+ * Get a string containing the message for this exception and all contained exceptions
+ *
+ * @param err the exception containing the required information
+ * @return a message that concatenates the message of this exception with its contained exceptions,
+ * also including information about the error code and location.
+ */
+
+ public String getExpandedMessage(TransformerException err) {
+
+ StructuredQName qCode = null;
+ String additionalLocationText = null;
+ if (err instanceof XPathException) {
+ qCode = ((XPathException)err).getErrorCodeQName();
+ additionalLocationText = ((XPathException)err).getAdditionalLocationText();
+ }
+ if (qCode == null && err.getException() instanceof XPathException) {
+ qCode = ((XPathException)err.getException()).getErrorCodeQName();
+ }
+ String message = "";
+ if (qCode != null) {
+ if (qCode.getURI().equals(NamespaceConstant.ERR)) {
+ message = qCode.getLocalPart();
+ } else {
+ message = qCode.getDisplayName();
+ }
+ }
+
+ if (additionalLocationText != null) {
+ message += " " + additionalLocationText;
+ }
+
+ if (err instanceof XPathException) {
+ Sequence errorObject = ((XPathException)err).getErrorObject();
+ if (errorObject != null) {
+ String errorObjectDesc = getErrorObjectString(errorObject);
+ if (errorObjectDesc != null) {
+ message += " " + errorObjectDesc;
+ }
+ }
+ }
+
+ Throwable e = err;
+ while (true) {
+ if (e == null) {
+ break;
+ }
+ String next = e.getMessage();
+ if (next == null) {
+ next = "";
+ }
+ if (next.startsWith("net.sf.saxon.trans.XPathException: ")) {
+ next = next.substring(next.indexOf(": ") + 2);
+ }
+ if (!("TRaX Transform Exception".equals(next) || message.endsWith(next))) {
+ if (!"".equals(message) && !message.trim().endsWith(":")) {
+ message += ": ";
+ }
+ message += next;
+ }
+ if (e instanceof TransformerException) {
+ e = ((TransformerException)e).getException();
+ } else if (e instanceof SAXException) {
+ e = ((SAXException)e).getException();
+ } else {
+ // e.printStackTrace();
+ break;
+ }
+ }
+
+ return message;
+ }
+
+ /**
+ * Get a string representation of the error object associated with the exception (this represents
+ * the final argument to fn:error, in the case of error triggered by calls on the fn:error function).
+ * The standard implementation returns null, meaning that the error object is not displayed; but
+ * the method can be overridden in a subclass to create a custom display of the error object, which
+ * is then appended to the message text.
+ * @param errorObject the error object passed as the last argument to fn:error. Note: this method is
+ * not called if the error object is absent/null
+ * @return a string representation of the error object to be appended to the message, or null if no
+ * output of the error object is required
+ */
+
+ public String getErrorObjectString(Sequence errorObject) {
+ return null;
+ }
+
+ /**
+ * Extract a name identifying the instruction at which an error occurred
+ *
+ * @param inst the provider of information
+ * @return the name of the containing instruction or expression, in user-meaningful terms
+ */
+
+ public static String getInstructionName(Instruction inst) {
+ try {
+ //InstructionInfo info = inst.getInstructionInfo();
+ int construct = inst.getInstructionNameCode();
+ if (construct < 0) {
+ return "";
+ }
+ if (construct < 1024 &&
+ construct != StandardNames.XSL_FUNCTION &&
+ construct != StandardNames.XSL_TEMPLATE) {
+ // it's a standard name
+ if (inst.getExecutable().getHostLanguage() == Configuration.XSLT) {
+ return StandardNames.getDisplayName(construct);
+ } else {
+ String s = StandardNames.getDisplayName(construct);
+ int colon = s.indexOf(':');
+ if (colon > 0) {
+ String local = s.substring(colon + 1);
+ if (local.equals("document")) {
+ return "document node constructor";
+ } else if (local.equals("text") || s.equals("value-of")) {
+ return "text node constructor";
+ } else if (local.equals("element")) {
+ return "computed element constructor";
+ } else if (local.equals("attribute")) {
+ return "computed attribute constructor";
+ } else if (local.equals("variable")) {
+ return "variable declaration";
+ } else if (local.equals("param")) {
+ return "external variable declaration";
+ } else if (local.equals("comment")) {
+ return "comment constructor";
+ } else if (local.equals("processing-instruction")) {
+ return "processing-instruction constructor";
+ } else if (local.equals("namespace")) {
+ return "namespace node constructor";
+ }
+ }
+ return s;
+ }
+ }
+ switch (construct) {
+ case Location.LITERAL_RESULT_ELEMENT: {
+ StructuredQName qName = inst.getObjectName();
+ return "element constructor <" + qName.getDisplayName() + '>';
+ }
+ case Location.LITERAL_RESULT_ATTRIBUTE: {
+ StructuredQName qName = inst.getObjectName();
+ return "attribute constructor " + qName.getDisplayName() + "=\"{...}\"";
+ }
+
+ default:
+ return "";
+ }
+
+ } catch (Exception err) {
+ return "";
+ }
+ }
+
+ /**
+ * Wordwrap an error message into lines of 72 characters or less (if possible)
+ *
+ * @param message the message to be word-wrapped
+ * @return the message after applying word-wrapping
+ */
+
+ private static String wordWrap(String message) {
+ if(message.length()>1000){
+ message= message.substring(0, 1000);
+ }
+ int nl = message.indexOf('\n');
+ if (nl < 0) {
+ nl = message.length();
+ }
+ if (nl > 100) {
+ int i = 90;
+ while (message.charAt(i) != ' ' && i > 0) {
+ i--;
+ }
+ if (i > 10) {
+ return message.substring(0, i) + "\n " + wordWrap(message.substring(i + 1));
+ } else {
+ return message;
+ }
+ } else if (nl < message.length()) {
+ return message.substring(0, nl) + '\n' + wordWrap(message.substring(nl + 1));
+ } else {
+ return message;
+ }
+ }
+
+ /**
+ * Print a stack trace to a specified output destination
+ * @param out the print stream to which the stack trace will be output
+ * @param context the XPath dynamic execution context (which holds the head of a linked
+ * list of context objects, representing the execution stack)
+ */
+
+ public static void printStackTrace(PrintStream out, XPathContext context) {
+ Iterator<ContextStackFrame> iterator = new ContextStackIterator(context);
+ while (iterator.hasNext()) {
+ ContextStackFrame frame = iterator.next();
+ frame.print(out);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/lib/StandardModuleURIResolver.java b/sf/saxon/lib/StandardModuleURIResolver.java
new file mode 100644
index 0000000..0d3d52d
--- /dev/null
+++ b/sf/saxon/lib/StandardModuleURIResolver.java
@@ -0,0 +1,174 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.functions.ResolveURI;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.stream.StreamSource;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+
+
+/**
+ * This class is the standard ModuleURIResolver used to implement the "import module" declaration
+ * in a Query Prolog. It is used when no user-defined ModuleURIResolver has been specified, or when
+ * the user-defined ModuleURIResolver decides to delegate to the standard ModuleURIResolver.
+ *
+ * @author Michael H. Kay
+*/
+
+public class StandardModuleURIResolver implements ModuleURIResolver {
+
+ private static StandardModuleURIResolver THE_INSTANCE = new StandardModuleURIResolver();
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular instance of this class
+ */
+
+ public static StandardModuleURIResolver getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * Create a StandardModuleURIResolver. Although the class is generally used as a singleton,
+ * a public constructor is provided so that the class can be named in configuration files and
+ * instantiated in the same way as user-written module URI resolvers.
+ */
+
+ public StandardModuleURIResolver() {
+
+ }
+
+ /**
+ * Resolve a module URI and associated location hints.
+ * @param moduleURI The module namespace URI of the module to be imported; or null when
+ * loading a non-library module.
+ * @param baseURI The base URI of the module containing the "import module" declaration;
+ * null if no base URI is known
+ * @param locations The set of URIs specified in the "at" clause of "import module",
+ * which serve as location hints for the module
+ * @return an array of StreamSource objects each identifying the contents of a module to be
+ * imported. Each StreamSource must contain a
+ * non-null absolute System ID which will be used as the base URI of the imported module,
+ * and either an InputSource or a Reader representing the text of the module.
+ * @throws XPathException (error XQST0059) if the module cannot be located
+ */
+
+ public StreamSource[] resolve(String moduleURI, String baseURI, String[] locations) throws XPathException {
+ if (locations.length == 0) {
+ XPathException err = new XPathException("Cannot locate module for namespace " + moduleURI);
+ err.setErrorCode("XQST0059");
+ err.setIsStaticError(true);
+ throw err;
+ } else {
+ // One or more locations given: import modules from all these locations
+ StreamSource[] sources = new StreamSource[locations.length];
+ for (int m=0; m<locations.length; m++) {
+ String href = locations[m];
+ URI absoluteURI;
+ try {
+ absoluteURI = ResolveURI.makeAbsolute(href, baseURI);
+ } catch (URISyntaxException err) {
+ XPathException se = new XPathException("Cannot resolve relative URI " + href, err);
+ se.setErrorCode("XQST0059");
+ se.setIsStaticError(true);
+ throw se;
+ }
+ sources[m] = getQuerySource(absoluteURI);
+ }
+ return sources;
+ }
+ }
+
+ /**
+ * Get a StreamSource object representing the source of a query, given its URI.
+ * This method attempts to discover the encoding by reading any HTTP headers.
+ * If the encoding can be determined, it returns a StreamSource containing a Reader that
+ * performs the required decoding. Otherwise, it returns a StreamSource containing an
+ * InputSource, leaving the caller to sort out encoding problems.
+ * @param absoluteURI the absolute URI of the source query
+ * @return a StreamSource containing a Reader or InputSource, as well as a systemID representing
+ * the base URI of the query.
+ * @throws XPathException if the URIs are invalid or cannot be resolved or dereferenced, or
+ * if any I/O error occurs
+ */
+
+ /*@NotNull*/ protected StreamSource getQuerySource(URI absoluteURI)
+ throws XPathException {
+
+ try {
+ InputStream is;
+ URL absoluteURL = absoluteURI.toURL();
+ URLConnection connection = absoluteURL.openConnection();
+ connection.connect();
+ is = connection.getInputStream();
+
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is);
+ }
+
+ // Get any external (HTTP) encoding label.
+ String contentType;
+ String encoding = null;
+
+ // The file:// URL scheme gives no useful information...
+ if (!"file".equals(connection.getURL().getProtocol())) {
+
+ // Use the contentType from the HTTP header if available
+ contentType = connection.getContentType();
+
+ if (contentType != null) {
+ int pos = contentType.indexOf("charset");
+ if (pos>=0) {
+ pos = contentType.indexOf('=', pos + 7);
+ if (pos>=0) {
+ contentType = contentType.substring(pos + 1);
+ }
+ if ((pos = contentType.indexOf(';')) > 0) {
+ contentType = contentType.substring(0, pos);
+ }
+
+ // attributes can have comment fields (RFC 822)
+ if ((pos = contentType.indexOf('(')) > 0) {
+ contentType = contentType.substring(0, pos);
+ }
+ // ... and values may be quoted
+ if ((pos = contentType.indexOf('"')) > 0) {
+ contentType = contentType.substring(pos + 1,
+ contentType.indexOf('"', pos + 2));
+ }
+ encoding = contentType.trim();
+ }
+ }
+ }
+ StreamSource ss = new StreamSource();
+ if (encoding == null) {
+ ss.setInputStream(is);
+ } else {
+ ss.setReader(new InputStreamReader(is, encoding));
+ }
+ ss.setSystemId(absoluteURL.toString());
+ return ss;
+ } catch (IOException err) {
+ XPathException se = new XPathException(err);
+ se.setErrorCode("XQST0059");
+ se.setIsStaticError(true);
+ throw se;
+ }
+
+ }
+}
+
diff --git a/sf/saxon/lib/StandardOutputResolver.java b/sf/saxon/lib/StandardOutputResolver.java
new file mode 100644
index 0000000..afa0937
--- /dev/null
+++ b/sf/saxon/lib/StandardOutputResolver.java
@@ -0,0 +1,166 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.stream.StreamResult;
+import java.io.*;
+import java.net.*;
+
+
+/**
+* This class defines the default OutputURIResolver. This is a counterpart to the JAXP
+* URIResolver, but is used to map the URI of a secondary result document to a Result object
+* which acts as the destination for the new document.
+* @author Michael H. Kay
+*/
+
+public class StandardOutputResolver implements OutputURIResolver, Serializable {
+
+ private static StandardOutputResolver theInstance = new StandardOutputResolver();
+
+ /**
+ * Get a singular instance
+ * @return the singleton instance of the class
+ */
+
+ public static StandardOutputResolver getInstance() {
+ return theInstance;
+ }
+
+ /**
+ * Get an instance of this OutputURIResolver class.
+ * <p>This method is called every time an xsl:result-document instruction
+ * is evaluated (with an href attribute). The resolve() and close() methods
+ * will be called on the returned instance.</p>
+ * <p>This OutputURIResolver is stateless (that is, it retains no information
+ * between resolve() and close()), so the same instance can safely be returned
+ * each time. For a stateful OutputURIResolver, it must either take care to be
+ * thread-safe (handling multiple invocations of xsl:result-document concurrently),
+ * or it must return a fresh instance of itself for each call.</p>
+ */
+
+ public StandardOutputResolver newInstance() {
+ return this;
+ }
+
+ /**
+ * Resolve an output URI
+ * @param href The relative URI of the output document. This corresponds to the
+ * href attribute of the xsl:result-document instruction.
+ * @param base The base URI that should be used. This is the base output URI,
+ * normally the URI of the principal output file.
+ * @return a Result object representing the destination for the XML document
+ */
+
+ public Result resolve(String href, /*@Nullable*/ String base) throws XPathException {
+
+ // System.err.println("Output URI Resolver (href='" + href + "', base='" + base + "')");
+
+ String which = "base";
+ try {
+ URI absoluteURI;
+ if (href.length() == 0) {
+ if (base==null) {
+ throw new XPathException("The system identifier of the principal output file is unknown");
+ }
+ absoluteURI= new URI(base);
+ } else {
+ which = "relative";
+ absoluteURI= new URI(href);
+ }
+ if (!absoluteURI.isAbsolute()) {
+ if (base==null) {
+ throw new XPathException("The system identifier of the principal output file is unknown");
+ }
+ which = "base";
+ URI baseURI = new URI(base);
+ which = "relative";
+ absoluteURI = baseURI.resolve(href);
+ }
+
+ if ("file".equals(absoluteURI.getScheme())) {
+ return makeOutputFile(absoluteURI);
+
+ } else {
+
+ // See if the Java VM can conjure up a writable URL connection for us.
+ // This is optimistic: I have yet to discover a URL scheme that it can handle "out of the box".
+ // But it can apparently be achieved using custom-written protocol handlers.
+
+ URLConnection connection = absoluteURI.toURL().openConnection();
+ connection.setDoInput(false);
+ connection.setDoOutput(true);
+ connection.connect();
+ OutputStream stream = connection.getOutputStream();
+ StreamResult result = new StreamResult(stream);
+ result.setSystemId(absoluteURI.toASCIIString());
+ return result;
+ }
+ } catch (URISyntaxException err) {
+ throw new XPathException("Invalid syntax for " + which + " URI", err);
+ } catch (IllegalArgumentException err2) {
+ throw new XPathException("Invalid " + which + " URI syntax", err2);
+ } catch (MalformedURLException err3) {
+ throw new XPathException("Resolved URL is malformed", err3);
+ } catch (UnknownServiceException err5) {
+ throw new XPathException("Specified protocol does not allow output", err5);
+ } catch (IOException err4) {
+ throw new XPathException("Cannot open connection to specified URL", err4);
+ }
+ }
+
+ /**
+ * Create an output file (unless it already exists) and return a reference to it as a Result object
+ * @param absoluteURI the URI of the output file (which should use the "file" scheme
+ * @return a Result object referencing this output file
+ * @throws XPathException
+ */
+
+ public static synchronized Result makeOutputFile(URI absoluteURI) throws XPathException {
+ try {
+ return new StreamResult(new File(absoluteURI));
+ } catch (IllegalArgumentException err) {
+ throw new XPathException("Cannot write to URI " + absoluteURI + " (" + err.getMessage() + ")");
+ }
+ }
+
+ /**
+ * Signal completion of the result document. This method is called by the system
+ * when the result document has been successfully written. It allows the resolver
+ * to perform tidy-up actions such as closing output streams, or firing off
+ * processes that take this result tree as input. Note that the OutputURIResolver
+ * is stateless, so the original href is supplied to identify the document
+ * that has been completed.
+ */
+
+ public void close(Result result) throws XPathException {
+ if (result instanceof StreamResult) {
+ OutputStream stream = ((StreamResult)result).getOutputStream();
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failed while closing output file", err);
+ }
+ }
+ Writer writer = ((StreamResult)result).getWriter(); // Path not used, but there for safety
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failed while closing output file", err);
+ }
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/lib/StandardURIChecker.java b/sf/saxon/lib/StandardURIChecker.java
new file mode 100644
index 0000000..ae01b09
--- /dev/null
+++ b/sf/saxon/lib/StandardURIChecker.java
@@ -0,0 +1,94 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.expr.sort.LRUCache;
+import net.sf.saxon.functions.EscapeURI;
+import net.sf.saxon.value.Whitespace;
+
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * This class checks whether a string is a valid URI. Different checking rules can be chosen by including
+ * a different URIChecker in the {@link ConversionRules} used when the value is checked.
+ */
+public class StandardURIChecker implements URIChecker, Serializable {
+
+ private static StandardURIChecker THE_INSTANCE = new StandardURIChecker();
+
+ public static StandardURIChecker getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * To prevent repeated validation of commonly used URIs (especially namespaces)
+ * we keep a small cache. This is especially useful in the case of URIs that are
+ * valid only after escaping, as otherwise an exception occurs during the validation process
+ */
+
+ /*@NotNull*/ private static ThreadLocal<LRUCache<CharSequence, Boolean>> caches =
+ new ThreadLocal<LRUCache<CharSequence, Boolean>>();
+
+ /**
+ * Protected constructor to allow subclassing
+ */
+
+ protected StandardURIChecker() {}
+
+ /**
+ * Validate a string to determine whether it is a valid URI
+ * @param value the string to be checked
+ * @return true if the string is considered to be a valid URI
+ */
+
+ public boolean isValidURI(CharSequence value) {
+ LRUCache<CharSequence, Boolean> cache = caches.get();
+ if (cache == null) {
+ cache = new LRUCache<CharSequence, Boolean>(50);
+ caches.set(cache);
+ }
+
+ if (cache.get(value) != null) {
+ return true;
+ }
+
+ String sv = Whitespace.trim(value);
+
+ // Allow zero-length strings (RFC2396 is ambivalent on this point)
+ if (sv.length() == 0) {
+ return true;
+ }
+
+ // Allow a string if the java.net.URI class accepts it
+ try {
+ new URI(sv);
+ cache.put(value, Boolean.TRUE);
+ return true;
+ } catch (URISyntaxException e) {
+ // keep trying
+ // Note: it's expensive to throw exceptions on a success path, so we keep a cache.
+ }
+
+ // Allow a string if it can be escaped into a form that java.net.URI accepts
+ sv = EscapeURI.iriToUri(sv).toString();
+ try {
+ new URI(sv);
+ cache.put(value, Boolean.TRUE);
+ return true;
+ } catch (URISyntaxException e) {
+ return false;
+ }
+ }
+
+ public static void main(String[] args) {
+ System.err.println(args[0] + " is valid? - " + getInstance().isValidURI(args[0]));
+ }
+}
+
diff --git a/sf/saxon/lib/StandardURIResolver.java b/sf/saxon/lib/StandardURIResolver.java
new file mode 100644
index 0000000..73af4a6
--- /dev/null
+++ b/sf/saxon/lib/StandardURIResolver.java
@@ -0,0 +1,278 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Platform;
+import net.sf.saxon.event.FilterFactory;
+import net.sf.saxon.event.IDFilter;
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.functions.EscapeURI;
+import net.sf.saxon.functions.ResolveURI;
+import net.sf.saxon.functions.URIQueryParameters;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.NonDelegatingURIResolver;
+import net.sf.saxon.trans.XPathException;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.sax.SAXSource;
+import java.io.Serializable;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+
+/**
+* This class provides the service of converting a URI into an InputSource.
+* It is used to get stylesheet modules referenced by xsl:import and xsl:include,
+* and source documents referenced by the document() function. The standard version
+* handles anything that the java URL class will handle.
+* You can write a subclass to handle other kinds of URI, e.g. references to things in
+* a database.
+* @author Michael H. Kay
+*/
+
+public class StandardURIResolver implements NonDelegatingURIResolver, Serializable {
+
+ // TODO: IDEA: support the data: URI scheme. (Requires unescaping of the URI, then parsing the content as XML)
+
+ /*@Nullable*/ private Configuration config = null;
+ protected boolean recognizeQueryParameters = false;
+
+ /**
+ * Create a StandardURIResolver, with no reference to a Configuration.
+ * Note: it is preferable but not essential to supply a Configuration, either in the constructor
+ * or in a subsequent call of <code>setConfiguration()</code>
+ */
+
+ public StandardURIResolver() {
+ this(null);
+ }
+
+ /**
+ * Create a StandardURIResolver, with a reference to a Configuration
+ * @param config The Configuration object. May be null.
+ * This is used (if available) to get a reusable SAX Parser for a source XML document
+ */
+
+ public StandardURIResolver(/*@Nullable*/ Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Indicate that query parameters (such as validation=strict) are to be recognized
+ * @param recognize Set to true if query parameters in the URI are to be recognized and acted upon.
+ * The default (for compatibility and interoperability reasons) is false.
+ */
+
+ public void setRecognizeQueryParameters(boolean recognize) {
+ recognizeQueryParameters = recognize;
+ }
+
+ /**
+ * Determine whether query parameters (such as validation=strict) are to be recognized
+ * @return true if query parameters are recognized and interpreted by Saxon.
+ */
+
+ public boolean queryParametersAreRecognized() {
+ return recognizeQueryParameters;
+ }
+
+ /**
+ * Get the relevant platform
+ * @return the platform
+ */
+
+ protected Platform getPlatform() {
+ return Configuration.getPlatform();
+ }
+
+ /**
+ * Set the configuration
+ * @param config the configuration
+ */
+
+ public void setConfiguration(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Get the configuration if available
+ * @return the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Resolve a URI
+ * @param href The relative or absolute URI. May be an empty string. May contain
+ * a fragment identifier starting with "#", which must be the value of an ID attribute
+ * in the referenced XML document.
+ * @param base The base URI that should be used. May be null if uri is absolute.
+ * @return a Source object representing an XML document
+ */
+
+ public Source resolve(String href, String base)
+ throws XPathException {
+
+ if (config != null && config.isTiming()) {
+ assert config != null;
+ config.getStandardErrorOutput().println("URIResolver.resolve href=\"" + href + "\" base=\"" + base + "\"");
+ }
+ // System.err.println("StandardURIResolver, href=" + href + ", base=" + base);
+
+ String relativeURI = href;
+ String id = null;
+
+ // Extract any fragment identifier. Note, this code is no longer used to
+ // resolve fragment identifiers in URI references passed to the document()
+ // function: the code of the document() function handles these itself.
+
+ int hash = href.indexOf('#');
+ if (hash>=0) {
+ relativeURI = href.substring(0, hash);
+ id = href.substring(hash+1);
+ // System.err.println("StandardURIResolver, href=" + href + ", id=" + id);
+ }
+
+ URIQueryParameters params = null;
+ URI uri;
+ URI relative;
+ try {
+ relativeURI = ResolveURI.escapeSpaces(relativeURI);
+ relative = new URI(relativeURI);
+ } catch (URISyntaxException err) {
+ throw new XPathException("Invalid relative URI " + Err.wrap(relativeURI), err);
+ }
+
+ String query = relative.getQuery();
+ if (query != null && recognizeQueryParameters) {
+ params = new URIQueryParameters(query, config);
+ int q = relativeURI.indexOf('?');
+ relativeURI = relativeURI.substring(0, q);
+ }
+
+ Source source = null;
+ if (recognizeQueryParameters && relativeURI.endsWith(".ptree")) {
+ source = getPTreeSource(relativeURI, base);
+ }
+
+ if (source == null) {
+ try {
+ uri = ResolveURI.makeAbsolute(relativeURI, base);
+ } catch (URISyntaxException err) {
+ // System.err.println("Recovering from " + err);
+ // last resort: if the base URI is null, or is itself a relative URI, we
+ // try to expand it relative to the current working directory
+ String expandedBase = ResolveURI.tryToExpand(base);
+ if (!expandedBase.equals(base)) { // prevent infinite recursion
+ return resolve(href, expandedBase);
+ }
+ //err.printStackTrace();
+ throw new XPathException("Invalid URI " + Err.wrap(relativeURI) + " - base " + Err.wrap(base), err);
+ }
+
+ // Check that any "%" sign in the URI is part of a well-formed percent-encoded UTF-8 character.
+ // Without this check, dereferencing the resulting URL can fail with arbitrary unchecked exceptions
+
+ final String uriString = uri.toString();
+ EscapeURI.checkPercentEncoding(uriString);
+
+ source = new SAXSource();
+ setSAXInputSource((SAXSource)source, uriString);
+
+
+ if (params != null) {
+ XMLReader parser = params.getXMLReader();
+ if (parser != null) {
+ ((SAXSource)source).setXMLReader(parser);
+ }
+ }
+
+ if (((SAXSource)source).getXMLReader() == null) {
+ if (config==null) {
+ try {
+ ((SAXSource)source).setXMLReader(Configuration.getPlatform().loadParser());
+ } catch (Exception err) {
+ throw new XPathException(err);
+ }
+ } else {
+ //((SAXSource)source).setXMLReader(config.getSourceParser());
+ // Leave the Sender to allocate an XMLReader, so that it can be returned to the pool after use
+ }
+ }
+ }
+
+ if (params != null) {
+ int stripSpace = params.getStripSpace();
+ source = AugmentedSource.makeAugmentedSource(source);
+ ((AugmentedSource)source).setStripSpace(stripSpace);
+ }
+
+ if (id != null) {
+ final String idFinal = id;
+ FilterFactory factory = new FilterFactory() {
+ public ProxyReceiver makeFilter(Receiver next) {
+ return new IDFilter(next, idFinal);
+ }
+ };
+ source = AugmentedSource.makeAugmentedSource(source);
+ ((AugmentedSource)source).addFilter(factory);
+ }
+
+ if (params != null) {
+ Integer validation = params.getValidationMode();
+ if (validation != null) {
+ source = AugmentedSource.makeAugmentedSource(source);
+ ((AugmentedSource)source).setSchemaValidationMode(validation);
+ }
+ }
+
+ if (params != null) {
+ Boolean xinclude = params.getXInclude();
+ if (xinclude != null) {
+ source = AugmentedSource.makeAugmentedSource(source);
+ ((AugmentedSource)source).setXIncludeAware(xinclude.booleanValue());
+ }
+ }
+
+ return source;
+ }
+
+ /**
+ * Handle a PTree source file (Saxon-EE only)
+ * @param href the relative URI
+ * @param base the base URI
+ * @return the new Source object
+ */
+
+ protected Source getPTreeSource(String href, String base) throws XPathException {
+ throw new XPathException("PTree files can only be read using a Saxon-EE configuration");
+ }
+
+ /**
+ * Set the InputSource part of the returned SAXSource. This is done in a separate
+ * method to allow subclassing. The default implementation simply places the URI in the
+ * InputSource, allowing the XML parser to take responsibility for the dereferencing.
+ * A subclass may choose to dereference the URI at this point an place an InputStream
+ * in the SAXSource.
+ * @param source the SAXSource being initialized
+ * @param uriString the absolute (resolved) URI to be used
+ */
+
+ protected void setSAXInputSource(SAXSource source, String uriString) {
+ source.setInputSource(new InputSource(uriString));
+ source.setSystemId(uriString);
+ }
+
+}
+
diff --git a/sf/saxon/lib/StandardUnparsedTextResolver.java b/sf/saxon/lib/StandardUnparsedTextResolver.java
new file mode 100644
index 0000000..242f487
--- /dev/null
+++ b/sf/saxon/lib/StandardUnparsedTextResolver.java
@@ -0,0 +1,278 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.charset.*;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * Default implementation of the UnparsedTextURIResolver, used if no other implementation
+ * is nominated to the Configuration.
+ */
+
+public class StandardUnparsedTextResolver implements UnparsedTextURIResolver {
+
+ private boolean debug = false;
+
+ /**
+ * Set debugging on or off. In debugging mode, information is written to System.err
+ * to trace the process of deducing an encoding.
+ *
+ * @param debug set to true to enable debugging
+ */
+
+ public void setDebugging(boolean debug) {
+ this.debug = debug;
+ }
+
+ /**
+ * Resolve the URI passed to the XSLT unparsed-text() function, after resolving
+ * against the base URI.
+ *
+ * @param absoluteURI the absolute URI obtained by resolving the supplied
+ * URI against the base URI
+ * @param encoding the encoding requested in the call of unparsed-text(), if any. Otherwise null.
+ * @param config The configuration. Provided in case the URI resolver
+ * needs it.
+ * @return a Reader, which Saxon will use to read the unparsed text. After the text has been read,
+ * the close() method of the Reader will be called.
+ * @throws net.sf.saxon.trans.XPathException if any failure occurs
+ * @since 8.9
+ */
+
+ public Reader resolve(URI absoluteURI, String encoding, Configuration config) throws XPathException {
+ URL absoluteURL;
+ PrintStream err = config.getStandardErrorOutput();
+ if (debug) {
+ err.println("unparsed-text(): processing " + absoluteURI);
+ err.println("unparsed-text(): requested encoding = " + encoding);
+ }
+ if (!absoluteURI.isAbsolute()) {
+ throw new XPathException("Resolved URI supplied to unparsed-text() is not absolute: " + absoluteURI.toString(),
+ "FOUT1170");
+ }
+ try {
+ absoluteURL = absoluteURI.toURL();
+ } catch (MalformedURLException mue) {
+ XPathException e = new XPathException("Cannot convert absolute URI to URL", mue);
+ e.setErrorCode("FOUT1170");
+ throw e;
+ }
+ try {
+ InputStream is;
+ URLConnection connection = absoluteURL.openConnection();
+ connection.setRequestProperty("Accept-Encoding","gzip");
+ try {
+ connection.connect();
+ } catch (IOException ioe) {
+ if (debug) {
+ err.println("unparsed-text(): connection failure. " + ioe.getMessage());
+ }
+ XPathException xpe = new XPathException("Failed to read input file", ioe);
+ xpe.setErrorCode("FOUT1170");
+ throw xpe;
+ }
+
+ is = connection.getInputStream();
+ String contentEncoding = connection.getContentEncoding();
+
+ if ("gzip".equals(contentEncoding)) {
+ is = new GZIPInputStream(is);
+ }
+ if (debug) {
+ err.println("unparsed-text(): established connection " +
+ ("gzip".equals(contentEncoding) ? " (zipped)" : ""));
+ }
+ try {
+
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is);
+ }
+
+ // Get any external (HTTP) encoding label.
+ boolean isXmlMediaType = false;
+
+ // The file:// URL scheme gives no useful information...
+ if (!"file".equals(connection.getURL().getProtocol())) {
+
+ // Use the contentType from the HTTP header if available
+ String contentType = connection.getContentType();
+ if (debug) {
+ err.println("unparsed-text(): content type = " + contentType);
+ }
+ if (contentType != null) {
+ String mediaType;
+ int pos = contentType.indexOf(';');
+ if (pos >= 0) {
+ mediaType = contentType.substring(0, pos);
+ } else {
+ mediaType = contentType;
+ }
+ mediaType = mediaType.trim();
+ if (debug) {
+ err.println("unparsed-text(): media type = " + mediaType);
+ }
+ isXmlMediaType = (mediaType.startsWith("application/") || mediaType.startsWith("text/")) &&
+ (mediaType.endsWith("/xml") || mediaType.endsWith("+xml"));
+
+ String charset = "";
+ pos = contentType.toLowerCase().indexOf("charset");
+ if (pos >= 0) {
+ pos = contentType.indexOf('=', pos + 7);
+ if (pos >= 0) {
+ charset = contentType.substring(pos + 1);
+ }
+ if ((pos = charset.indexOf(';')) > 0) {
+ charset = charset.substring(0, pos);
+ }
+
+ // attributes can have comment fields (RFC 822)
+ if ((pos = charset.indexOf('(')) > 0) {
+ charset = charset.substring(0, pos);
+ }
+ // ... and values may be quoted
+ if ((pos = charset.indexOf('"')) > 0) {
+ charset = charset.substring(pos + 1,
+ charset.indexOf('"', pos + 2));
+ }
+ if (debug) {
+ err.println("unparsed-text(): charset = " + charset.trim());
+ }
+ encoding = charset.trim();
+ }
+ }
+ }
+
+ if (encoding == null || isXmlMediaType) {
+ // Try to detect the encoding from the start of the content
+ is.mark(100);
+ byte[] start = new byte[100];
+ int read = is.read(start, 0, 100);
+ is.reset();
+ encoding = inferEncoding(start, read, err);
+ if (debug) {
+ err.println("unparsed-text(): inferred encoding = " + encoding);
+ }
+ }
+
+ } catch (IOException e) {
+ encoding = "UTF-8";
+ }
+
+ //}
+
+ // The following is necessary to ensure that encoding errors are not recovered.
+ Charset charset = Charset.forName(encoding);
+ CharsetDecoder decoder = charset.newDecoder();
+ decoder = decoder.onMalformedInput(CodingErrorAction.REPORT);
+ decoder = decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
+ return new BufferedReader(new InputStreamReader(is, decoder));
+
+
+ } catch (IOException ioe) {
+ throw new XPathException(ioe.getMessage(), "FOUT1170");
+ } catch (IllegalCharsetNameException icne) {
+ throw new XPathException("Invalid encoding name: " + encoding, "FOUT1190");
+ } catch(UnsupportedCharsetException uce){
+ throw new XPathException("Invalid encoding name: " + encoding, "FOUT1190");
+ }
+
+ }
+
+ /**
+ * Infer the encoding of a file by reading the first few bytes of the file
+ *
+ * @param start the first few bytes of the file
+ * @param read the number of bytes that have been read
+ * @return the inferred encoding
+ */
+
+ private String inferEncoding(byte[] start, int read, /*@NotNull*/ PrintStream err) {
+ if (read >= 2) {
+ if (ch(start[0]) == 0xFE && ch(start[1]) == 0xFF) {
+ if (debug) {
+ err.println("unparsed-text(): found UTF-16 byte order mark");
+ }
+ return "UTF-16";
+ } else if (ch(start[0]) == 0xFF && ch(start[1]) == 0xFE) {
+ if (debug) {
+ err.println("unparsed-text(): found UTF-16LE byte order mark");
+ }
+ return "UTF-16LE";
+ }
+ }
+ if (read >= 3) {
+ if (ch(start[0]) == 0xEF && ch(start[1]) == 0xBB && ch(start[2]) == 0xBF) {
+ if (debug) {
+ err.println("unparsed-text(): found UTF-8 byte order mark");
+ }
+ return "UTF-8";
+ }
+ }
+ if (read >= 4) {
+ if (ch(start[0]) == '<' && ch(start[1]) == '?' &&
+ ch(start[2]) == 'x' && ch(start[3]) == 'm' && ch(start[4]) == 'l') {
+ if (debug) {
+ err.println("unparsed-text(): found XML declaration");
+ }
+ FastStringBuffer sb = new FastStringBuffer(read);
+ for (int b = 0; b < read; b++) {
+ sb.append((char)start[b]);
+ }
+ String p = sb.toString();
+ int v = p.indexOf("encoding");
+ if (v >= 0) {
+ v += 8;
+ while (v < p.length() && " \n\r\t=\"'".indexOf(p.charAt(v)) >= 0) {
+ v++;
+ }
+ sb.setLength(0);
+ while (v < p.length() && p.charAt(v) != '"' && p.charAt(v) != '\'') {
+ sb.append(p.charAt(v++));
+ }
+ if (debug) {
+ err.println("unparsed-text(): encoding in XML declaration = " + sb.toString());
+ }
+ return sb.toString();
+ }
+ if (debug) {
+ err.println("unparsed-text(): no encoding found in XML declaration");
+ }
+ }
+ } else if (read > 0 && start[0] == 0 && start[2] == 0 && start[4] == 0 && start[6] == 0) {
+ if (debug) {
+ err.println("unparsed-text(): even-numbered bytes are zero, inferring UTF-16");
+ }
+ return "UTF-16";
+ } else if (read > 1 && start[1] == 0 && start[3] == 0 && start[5] == 0 && start[7] == 0) {
+ if (debug) {
+ err.println("unparsed-text(): odd-numbered bytes are zero, inferring UTF-16LE");
+ }
+ return "UTF-16LE";
+ }
+ // If all else fails, assume UTF-8
+ if (debug) {
+ err.println("unparsed-text(): assuming fallback encoding (UTF-8)");
+ }
+ return "UTF-8";
+ }
+
+ private int ch(byte b) {
+ return ((int)b) & 0xff;
+ }
+}
+
diff --git a/sf/saxon/lib/StringCollator.java b/sf/saxon/lib/StringCollator.java
new file mode 100644
index 0000000..84907ba
--- /dev/null
+++ b/sf/saxon/lib/StringCollator.java
@@ -0,0 +1,51 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import java.io.Serializable;
+
+/**
+ * This interface represents a "collation" as defined in XPath, that is, a set of rules for comparing strings
+ * <p>Note: an implementation of this interface that wraps a Java {@link java.text.RuleBasedCollator} is
+ * available: see {@link net.sf.saxon.expr.sort.RuleBasedSubstringMatcher}.</p>
+ */
+public interface StringCollator extends Serializable {
+
+ /**
+ * Compare two strings
+ * @param o1 the first string
+ * @param o2 the second string
+ * @return 0 if the strings are considered equal, a negative integer if the first string is less than the second,
+ * a positive integer if the first string is greater than the second
+ */
+
+ int compareStrings(String o1, String o2);
+
+ /**
+ * Compare two strings for equality. This may be more efficient than using compareStrings and
+ * testing whether the result is zero, but it must give the same result
+ * @param s1 the first string
+ * @param s2 the second string
+ * @return true if and only if the strings are considered equal,
+ */
+
+ boolean comparesEqual(String s1, String s2);
+
+ /**
+ * Get a collation key for a String. The essential property of collation keys
+ * is that if (and only if) two strings are equal under the collation, then
+ * comparing the collation keys using the equals() method must return true.
+ * @param s the string whose collation key is required
+ * @return the collation key
+ */
+
+ Object getCollationKey(String s);
+
+
+}
+
diff --git a/sf/saxon/lib/SubstringMatcher.java b/sf/saxon/lib/SubstringMatcher.java
new file mode 100644
index 0000000..da9d013
--- /dev/null
+++ b/sf/saxon/lib/SubstringMatcher.java
@@ -0,0 +1,70 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+/**
+ * This interface is implemented by a collation that is capable of supporting
+ * the XPath functions that require matching of a substring: namely contains(),
+ * starts-with, ends-with, substring-before, and substring-after. For sorting
+ * and comparing strings, a collation needs to implement only the {@link StringCollator}
+ * interface; for matching of substrings, it must also implement this interface.
+ */
+public interface SubstringMatcher extends StringCollator {
+
+ /**
+ * Test whether one string contains another, according to the rules
+ * of the XPath contains() function
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return true iff s1 contains s2
+ */
+
+ public boolean contains(String s1, String s2);
+
+ /**
+ * Test whether one string starts with another, according to the rules
+ * of the XPath starts-with() function
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return true iff s1 starts with s2
+ */
+
+ public boolean startsWith(String s1, String s2);
+
+ /**
+ * Test whether one string ends with another, according to the rules
+ * of the XPath ends-with() function
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return true iff s1 ends with s2
+ */
+
+ public boolean endsWith(String s1, String s2);
+
+ /**
+ * Return the part of a string before a given substring, according to the rules
+ * of the XPath substring-before() function
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return the part of s1 that precedes the first occurrence of s2
+ */
+
+ public String substringBefore(String s1, String s2);
+
+ /**
+ * Return the part of a string after a given substring, according to the rules
+ * of the XPath substring-after() function
+ * @param s1 the containing string
+ * @param s2 the contained string
+ * @return the part of s1 that follows the first occurrence of s2
+ */
+
+ public String substringAfter(String s1, String s2);
+
+}
+
diff --git a/sf/saxon/lib/TraceListener.java b/sf/saxon/lib/TraceListener.java
new file mode 100644
index 0000000..8f1c3b9
--- /dev/null
+++ b/sf/saxon/lib/TraceListener.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trace.InstructionInfo;
+
+import java.io.PrintStream;
+import java.util.EventListener;
+
+/**
+ * This interface defines methods that are called by Saxon during the execution of
+ * a stylesheet, if tracing is switched on. Tracing can be switched on by nominating
+ * an implementation of this class using the TRACE_LISTENER feature of the TransformerFactory,
+ * or using the addTraceListener() method of the Controller, which is Saxon's implementation
+ * of tyhe JAXP javax.xml.transform.Transformer interface.
+ */
+
+public interface TraceListener extends EventListener {
+
+ /**
+ * Method called to supply the destination for output
+ * @param stream a PrintStream to which any output produced by the TraceListener should be written
+ */
+
+ public void setOutputDestination(PrintStream stream);
+
+ /**
+ * Method called at the start of execution, that is, when the run-time transformation starts
+ * @param controller identifies the transformation controller, and provides the listener with
+ * access to context and configuration information
+ */
+
+ public void open(Controller controller);
+
+ /**
+ * Method called at the end of execution, that is, when the run-time execution ends
+ */
+
+ public void close();
+
+ /**
+ * Method that is called when an instruction in the stylesheet gets processed.
+ *
+ * @param instruction gives information about the instruction being
+ * executed, and about the context in which it is executed. This object is mutable,
+ * so if information from the InstructionInfo is to be retained, it must be copied.
+ */
+
+ public void enter(InstructionInfo instruction, XPathContext context);
+
+ /**
+ * Method that is called after processing an instruction of the stylesheet,
+ * that is, after any child instructions have been processed.
+ *
+ * @param instruction gives the same information that was supplied to the
+ * enter method, though it is not necessarily the same object. Note that the
+ * line number of the instruction is that of the start tag in the source stylesheet,
+ * not the line number of the end tag.
+ */
+
+ public void leave(InstructionInfo instruction);
+
+ /**
+ * Method that is called by an instruction that changes the current item
+ * in the source document: that is, xsl:for-each, xsl:apply-templates, xsl:for-each-group.
+ * The method is called after the enter method for the relevant instruction, and is called
+ * once for each item processed.
+ *
+ * @param currentItem the new current item. Item objects are not mutable; it is safe to retain
+ * a reference to the Item for later use.
+ */
+
+ public void startCurrentItem(Item currentItem);
+
+ /**
+ * Method that is called when an instruction has finished processing a new current item
+ * and is ready to select a new current item or revert to the previous current item.
+ * The method will be called before the leave() method for the instruction that made this
+ * item current.
+ *
+ * @param currentItem the item that was current, whose processing is now complete. This will represent
+ * the same underlying item as the corresponding startCurrentItem() call, though it will
+ * not necessarily be the same actual object.
+ */
+
+ public void endCurrentItem(Item currentItem);
+
+}
+
diff --git a/sf/saxon/lib/URIChecker.java b/sf/saxon/lib/URIChecker.java
new file mode 100644
index 0000000..04b0a5b
--- /dev/null
+++ b/sf/saxon/lib/URIChecker.java
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+/**
+ * This interface defines a method for checking whether a string is considered to be a valid URI.
+ *
+ * <p>A user-supplied implementation of this class can be set in a customized instance of
+ * {@link ConversionRules}, which can be set in the configuration using
+ * {@link net.sf.saxon.Configuration#setConversionRules(ConversionRules)}</p>
+ *
+ * <p>A user-supplied implementation can be written either from scratch, or by reference to the
+ * system-supplied implementation {@link StandardURIChecker}.
+ */
+public interface URIChecker {
+
+ /**
+ * Check whether a given string is considered valid according to the rules of the xs:anyURI type.
+ * <p>This method is called during schema validation, and when casting string to xs:anyURI. It is not
+ * used when the xs:anyURI type is used as a return value from methods such as namespace-uri() or
+ * namespace-uri-from-QName() - in such cases no checking is applied to the name.</p>
+ * @param value the string to be checked
+ * @return true if the string is considered to represent a valid URI
+ */
+
+ boolean isValidURI(CharSequence value);
+}
diff --git a/sf/saxon/lib/UnparsedTextURIResolver.java b/sf/saxon/lib/UnparsedTextURIResolver.java
new file mode 100644
index 0000000..d29a9e4
--- /dev/null
+++ b/sf/saxon/lib/UnparsedTextURIResolver.java
@@ -0,0 +1,45 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Reader;
+import java.io.Serializable;
+import java.net.URI;
+
+/**
+ * An UnparsedTextURIResolver accepts an absolute URI and optionally an encoding name as input,
+ * and returns a Reader as its result.
+ */
+
+public interface UnparsedTextURIResolver extends Serializable {
+
+ /**
+ * Resolve the URI passed to the XSLT unparsed-text() function, after resolving
+ * against the base URI.
+ *
+ * <p>Note that a user-written resolver is responsible for enforcing some of the rules in the
+ * XSLT specification, such as the rules for inferring an encoding when none is supplied. Saxon
+ * will not report any error if the resolver does this in a non-conformant way.</p>
+ *
+ * @param absoluteURI the absolute URI obtained by resolving the supplied
+ * URI against the base URI
+ * @param encoding the encoding requested in the call of unparsed-text(), if any. Otherwise null.
+ * @param config The Saxon configuration. Provided in case the URI resolver
+ * needs it.
+ * @return a Reader, which Saxon will use to read the unparsed text. After the text has been read,
+ * the close() method of the Reader will be called.
+ * @throws net.sf.saxon.trans.XPathException if any failure occurs
+ * @since 8.9
+ */
+
+ /*@NotNull*/ public Reader resolve(URI absoluteURI, String encoding, Configuration config) throws XPathException;
+}
+
diff --git a/sf/saxon/lib/Validation.java b/sf/saxon/lib/Validation.java
new file mode 100644
index 0000000..23092fb
--- /dev/null
+++ b/sf/saxon/lib/Validation.java
@@ -0,0 +1,112 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+
+/**
+* This class contains constants and static methods to manipulate the validation
+* property of a type.
+*/
+
+public final class Validation {
+
+ /**
+ * Code indicating that the value of a validation request was invalid
+ */
+
+ public static final int INVALID = -1;
+
+ /**
+ * Code for strict validation
+ */
+
+ public static final int STRICT = 1;
+
+ /**
+ * Code for lax validation
+ */
+
+ public static final int LAX = 2;
+
+ /**
+ * Code corresponding to the XSLT option validation=preserve, which indicates
+ * that existing type annotations are to be preserved but no new validation is performed.
+ */
+
+ public static final int PRESERVE = 3;
+
+ /**
+ * Code corresponding to the XSLT option validation=strip, which indicates
+ * that existing type annotations are to be removed and no new validation is performed.
+ */
+
+ public static final int STRIP = 4;
+
+ /**
+ * Synonym for {@link #STRIP}, corresponding to XQuery usage
+ */
+
+ public static final int SKIP = 4; // synonym provided for the XQuery API
+
+ /**
+ * Code indicating that no specific validation options were requested
+ */
+
+ public static final int DEFAULT = 0;
+
+ /**
+ * Code indicating that validation against a named type was requested
+ */
+
+ public static final int BY_TYPE = 8;
+
+ /**
+ * This class is never instantiated
+ */
+
+ private Validation() {
+ }
+
+ /**
+ * Get the integer validation code corresponding to a given string
+ * @param value one of "strict", "lax", "preserve", or "strip"
+ * @return the corresponding code {@link #STRICT}, {@link #LAX},
+ * {@link #PRESERVE}, or {@link #STRIP}
+ */
+
+ public static int getCode(String value) {
+ if (value.equals("strict")) {
+ return STRICT;
+ } else if (value.equals("lax")) {
+ return LAX;
+ } else if (value.equals("preserve")) {
+ return PRESERVE;
+ } else if (value.equals("strip")) {
+ return STRIP;
+ } else {
+ return INVALID;
+ }
+ }
+
+ /**
+ * Get a string representation of a validation code
+ * @param value one of the validation codes defined in this class
+ * @return one of the strings "strict", "lax", "preserve", "skip" (sic), or "invalid"
+ */
+
+ /*@NotNull*/ public static String toString(int value) {
+ switch(value) {
+ case STRICT: return "strict";
+ case LAX: return "lax";
+ case PRESERVE: return "preserve";
+ case STRIP: return "skip"; // for XQuery
+ case BY_TYPE: return "by type";
+ default: return "invalid";
+ }
+ }
+}
diff --git a/sf/saxon/lib/ValidationStatisticsRecipient.java b/sf/saxon/lib/ValidationStatisticsRecipient.java
new file mode 100644
index 0000000..a3dbdf3
--- /dev/null
+++ b/sf/saxon/lib/ValidationStatisticsRecipient.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.lib;
+
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaComponent;
+
+import java.util.Map;
+
+/**
+ * Defines a class that is notified of validation statistics at the end of a validation episode
+ */
+public interface ValidationStatisticsRecipient {
+
+ /**
+ * Notify the validation statistics
+ * @param statistics the statistics, in the form of a map from schema components (currently,
+ * element declarations and schema types) to a count of how often the component
+ * was used during the validation episode
+ */
+
+ public void notifyValidationStatistics(Map<SchemaComponent, Integer> statistics) throws XPathException;
+}
+
diff --git a/sf/saxon/lib/package.html b/sf/saxon/lib/package.html
new file mode 100644
index 0000000..7ebb4d7
--- /dev/null
+++ b/sf/saxon/lib/package.html
@@ -0,0 +1,31 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.lib</title>
+</head>
+
+<body>
+
+<p>This package collects together interfaces intended for user applications to implement
+ in order to customize Saxon's behavior, default implementations of these interfaces,
+ and classes containing constants that are used in public Saxon interfaces.</p>
+
+<p>These interfaces were brought together into this package in Saxon 9.3, having previously
+been dispersed around the package hierarchy and mixed with classes and interfaces intended
+only for internal use.</p>
+
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+20 July 2010</i></p>
+</body>
+</html>
diff --git a/sf/saxon/om/AbsolutePath.java b/sf/saxon/om/AbsolutePath.java
new file mode 100644
index 0000000..641ee66
--- /dev/null
+++ b/sf/saxon/om/AbsolutePath.java
@@ -0,0 +1,236 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.Type;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents the path from the root of a tree to the node, as a sequence of (name, position) pairs
+ */
+
+public class AbsolutePath {
+
+ private List<PathElement> path;
+ private String systemId;
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+
+ /**
+ * Inner class representing one step in the path
+ */
+
+ public static class PathElement {
+ int nodeKind;
+ NodeName name;
+ int index;
+
+ /**
+ * Create a path element
+ * @param nodeKind the kind of node
+ * @param name the name of the node
+ * @param index the position of the node relative to siblings of the same node kind and name
+ */
+ public PathElement(int nodeKind, NodeName name, int index) {
+ this.nodeKind = nodeKind;
+ this.name = name;
+ this.index = index;
+ }
+
+ /**
+ * Get the node kind
+ * @return the node kind, as a constant from {@link net.sf.saxon.type.Type}
+ */
+ public int getNodeKind() {
+ return nodeKind;
+ }
+
+ /**
+ * Get the name of the node
+ * @return the node name
+ */
+
+ public NodeName getName() {
+ return name;
+ }
+
+ /**
+ * Get the position of the node
+ * @return relative to siblings of the same node kind and name
+ */
+
+ public int getIndex() {
+ return index;
+ }
+
+ /**
+ * Get a string representation of the path
+ * @param fsb buffer into which the string representation will be written
+ * @param option for representing namespaces:
+ * 'p': use namepace prefix. 'u': use full URI. 's': use abbreviated URI
+ */
+
+ public void toString(FastStringBuffer fsb, char option) {
+ switch (nodeKind) {
+ case Type.DOCUMENT:
+ fsb.append("(/)");
+ break;
+ case Type.ATTRIBUTE:
+ fsb.append('@');
+ if (!name.getURI().isEmpty()) {
+ if (option=='u') {
+ fsb.append("Q{");
+ fsb.append(name.getURI());
+ fsb.append("}");
+ } else if (option=='p'){
+ String prefix = name.getPrefix();
+ if (prefix.length()!=0) {
+ fsb.append(prefix);
+ fsb.append(':');
+ }
+ } else if (option=='s') {
+ fsb.append("Q{");
+ fsb.append(Err.abbreviateURI(name.getURI()));
+ fsb.append("}");
+ }
+ }
+ fsb.append(getName().getLocalPart());
+ break;
+ case Type.ELEMENT:
+ if (option=='u') {
+ fsb.append("Q{");
+ fsb.append(name.getURI());
+ fsb.append("}");
+ } else if (option=='p') {
+ String prefix = name.getPrefix();
+ if (prefix.length()!=0) {
+ fsb.append(prefix);
+ fsb.append(':');
+ }
+ } else if (option=='s') {
+ if (name.getURI().length() != 0) {
+ fsb.append("Q{");
+ fsb.append(Err.abbreviateURI(name.getURI()));
+ fsb.append("}");
+ }
+ }
+ fsb.append(name.getLocalPart());
+ fsb.append('[');
+ fsb.append(getIndex()+"");
+ fsb.append(']');
+ break;
+ case Type.TEXT:
+ fsb.append("text()");
+ break;
+ case Type.COMMENT:
+ fsb.append("comment()");
+ fsb.append('[');
+ fsb.append(getIndex()+"");
+ fsb.append(']');
+ break;
+ case Type.PROCESSING_INSTRUCTION:
+ fsb.append("processing-instruction(");
+ fsb.append(name.getLocalPart());
+ fsb.append(")[");
+ fsb.append(getIndex()+"");
+ fsb.append(']');
+ break;
+ case Type.NAMESPACE:
+ fsb.append("namespace::");
+ if (name.getLocalPart().length()==0) {
+ fsb.append("*[Q{" + NamespaceConstant.FN + "}local-name()=\"\"]");
+ } else {
+ fsb.append(name.getLocalPart());
+ }
+ break;
+ default:
+ }
+ }
+ }
+
+
+ /**
+ * Create an absolute path
+ * @param path the list of path elements, starting from the root. It is not necessary to include a path element
+ * for the document node.
+ */
+
+ public AbsolutePath(List<PathElement> path) {
+ this.path = new ArrayList<PathElement>(path);
+ }
+
+ /**
+ * Get a string representing the path using namespace prefixes to represent QNames
+ * @return the path in the form <code>/prefix:local[n]/prefix:local[m]/...</code>
+ */
+
+ public String getPathUsingPrefixes() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ for (AbsolutePath.PathElement pe : path) {
+ fsb.append('/');
+ pe.toString(fsb, 'p');
+ }
+ return fsb.toString();
+ }
+
+ /**
+ * Get a string representing the path using namespace URIs to represent QNames
+ * @return the path in the form <code>/Q{uri}local[n]/Q{uri}local[m]/...</code>
+ */
+
+ public String getPathUsingUris() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ for (AbsolutePath.PathElement pe : path) {
+ fsb.append('/');
+ pe.toString(fsb, 'u');
+ }
+ return fsb.toString();
+ }
+
+ /**
+ * Get a string representing the path using abbreviated namespace URIs to represent QNames
+ * @return the path in the form <code>/Q{uri}local[n]/Q{uri}local[m]/...</code>, with the URIs shortened
+ */
+
+ public String getPathUsingAbbreviatedUris() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ for (AbsolutePath.PathElement pe : path) {
+ fsb.append('/');
+ pe.toString(fsb, 's');
+ }
+ return fsb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return getPathUsingUris();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof AbsolutePath && obj.toString().equals(toString());
+ }
+
+ @Override
+ public int hashCode() {
+ return toString().hashCode();
+ }
+}
+
diff --git a/sf/saxon/om/AbstractItem.java b/sf/saxon/om/AbstractItem.java
new file mode 100644
index 0000000..2f1c91a
--- /dev/null
+++ b/sf/saxon/om/AbstractItem.java
@@ -0,0 +1,91 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+ * Abstract superclass for items
+ */
+public abstract class AbstractItem implements Item, GroundedValue {
+
+ /**
+ * Get the n'th item in the value, counting from 0
+ *
+ * @param n the index of the required item, with 0 representing the first item in the sequence
+ * @return the n'th item if it exists, or null otherwise
+ */
+ public Item itemAt(int n) {
+ return (n==0 ? this : null);
+ }
+
+ /**
+ * Get a subsequence of the value
+ *
+ * @param start the index of the first item to be included in the result, counting from zero.
+ * A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
+ * sequence is returned
+ * @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
+ * get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
+ * is returned. If the value goes off the end of the sequence, the result returns items up to the end
+ * of the sequence
+ * @return the required subsequence. If min is
+ */
+ public final GroundedValue subsequence(int start, int length) {
+ return ((start <= 0 && (start+length) > 0) ? this : EmptySequence.getInstance());
+ }
+
+ /**
+ * Get the size of the value (the number of items)
+ *
+ * @return the number of items in the sequence
+ */
+ public final int getLength() {
+ return 1;
+ }
+
+ /**
+ * Get the first item in the sequence.
+ *
+ * @return the first item in the sequence if there is one, or null if the sequence
+ * is empty
+ * @throws net.sf.saxon.trans.XPathException
+ * in the situation where the sequence is evaluated lazily, and
+ * evaluation of the first item causes a dynamic error.
+ */
+ public Item head() throws XPathException {
+ return this;
+ }
+
+ /**
+ * Get an iterator over all the items in the sequence
+ *
+ * @return an iterator over all the items
+ * @throws net.sf.saxon.trans.XPathException
+ * in the situation where the sequence is evaluated lazily, and
+ * constructing an iterator over the items causes a dynamic error.
+ */
+ public SequenceIterator<? extends Item> iterate() throws XPathException {
+ return SingletonIterator.makeIterator((Item)this);
+ }
+
+ /**
+ * Reduce the sequence to its simplest form. If the value is an empty sequence, the result will be
+ * EmptySequence.getInstance(). If the value is a single atomic value, the result will be an instance
+ * of AtomicValue. If the value is a single item of any other kind, the result will be an instance
+ * of SingletonItem. Otherwise, the result will typically be unchanged.
+ *
+ * @return the simplified sequence
+ */
+ public GroundedValue reduce() {
+ return this;
+ }
+}
+
diff --git a/sf/saxon/om/AllElementsSpaceStrippingRule.java b/sf/saxon/om/AllElementsSpaceStrippingRule.java
new file mode 100644
index 0000000..dc6ee0c
--- /dev/null
+++ b/sf/saxon/om/AllElementsSpaceStrippingRule.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+
+import net.sf.saxon.event.Stripper;
+
+/**
+ * A whitespace stripping rule that strips all elements unless xml:space indicates that whitespace
+ * should be preserved.
+ */
+
+public class AllElementsSpaceStrippingRule implements SpaceStrippingRule {
+
+ private final static AllElementsSpaceStrippingRule THE_INSTANCE = new AllElementsSpaceStrippingRule();
+
+ public static AllElementsSpaceStrippingRule getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * Decide whether an element is in the set of white-space preserving element types
+ *
+ * @param fingerprint identifies the element being tested
+ * @return STRIP_DEFAULT: strip spaces unless xml:space tells you not to.
+ */
+
+ public byte isSpacePreserving(NodeName fingerprint) {
+ return Stripper.STRIP_DEFAULT;
+ }
+
+}
+
diff --git a/sf/saxon/om/AtomicArray.java b/sf/saxon/om/AtomicArray.java
new file mode 100644
index 0000000..344c9ea
--- /dev/null
+++ b/sf/saxon/om/AtomicArray.java
@@ -0,0 +1,257 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ArrayIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.EmptySequence;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A sequence of atomic values, implemented using an underlying array.
+ *
+ * Often used for representing the typed value of a list-valued node.
+ *
+ * @since 9.5
+ */
+public class AtomicArray implements AtomicSequence {
+
+ private AtomicValue[] content;
+
+ /**
+ * Create an AtomicArray over a supplied array of atomic values
+ * @param content the supplied array
+ */
+
+ public AtomicArray(AtomicValue[] content) {
+ this.content = content;
+ }
+
+ /**
+ * Create an AtomicArray supplying the contents as an iterator
+ * @param iter the iterator that supplies the values (which must be position
+ * at the start of the sequence, and which will be consumed by the method).
+ * @throws XPathException if evaluation of the SequenceIterator fails, or if
+ * any of the items returned by the SequenceIterator is not atomic
+ */
+
+ public AtomicArray(SequenceIterator iter) throws XPathException {
+ List<AtomicValue> list = new ArrayList<AtomicValue>(10);
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (!(item instanceof AtomicValue)) {
+ throw new IllegalArgumentException();
+ }
+ list.add((AtomicValue)item);
+ }
+ AtomicValue[] av = new AtomicValue[list.size()];
+ content = list.toArray(av);
+ }
+
+ public AtomicValue head() {
+ return (content.length > 0 ? content[0] : null);
+ }
+
+ public SequenceIterator<AtomicValue> iterate() {
+ return new ArrayIterator<AtomicValue>(content);
+ }
+
+ /**
+ * Get the n'th item in the sequence (base-zero addressing)
+ * @param n the index of the required item, the first item being zero
+ * @return the n'th item if n is in range, or null otherwise
+ */
+
+ public AtomicValue itemAt(int n) {
+ if (n >= 0 && n < content.length) {
+ return content[n];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the length of the sequence
+ * @return the number of items in the sequence
+ */
+
+ public int getLength() {
+ return content.length;
+ }
+
+ /**
+ * Get a subsequence of this sequence
+ * @param start the index of the first item to be included in the result, counting from zero.
+ * A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
+ * sequence is returned
+ * @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
+ * get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
+ * is returned. If the value goes off the end of the sequence, the result returns items up to the end
+ * of the sequence
+ * @return the required subsequence
+ */
+
+ public AtomicArray subsequence(int start, int length) {
+ if (start < 0) {
+ start = 0;
+ }
+ if (start + length > content.length) {
+ length = content.length - start;
+ }
+ AtomicValue[] av = new AtomicValue[length];
+ System.arraycopy(content, start, av, 0, length);
+ return new AtomicArray(av);
+ }
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules.
+ * @return the canonical lexical representation if defined in XML Schema; otherwise, the result
+ * of casting to string according to the XPath 2.0 rules
+ */
+
+ public CharSequence getCanonicalLexicalRepresentation() {
+ return getStringValueCS();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ boolean first = true;
+ for (AtomicValue av : content) {
+ if (!first) {
+ fsb.append(' ');
+ } else {
+ first = false;
+ }
+ fsb.append(av.getStringValueCS());
+ }
+ return fsb.condense();
+ }
+
+ public String getStringValue() {
+ return getStringValueCS().toString();
+ }
+
+ public boolean effectiveBooleanValue() throws XPathException {
+ return ExpressionTool.effectiveBooleanValue(iterate());
+ }
+
+ /**
+ * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
+ * The default implementation is written to compare sequences of atomic values.
+ * This method is overridden for AtomicValue and its subclasses.
+ *
+ * <p>In the case of data types that are partially ordered, the returned Comparable extends the standard
+ * semantics of the compareTo() method by returning the value {@link SequenceTool#INDETERMINATE_ORDERING} when there
+ * is no defined order relationship between two given values.</p>
+ *
+ * <p>For comparing key/keyref values, XSD 1.1 defines that a singleton list is equal to its only member. To
+ * achieve this, this method returns the schema comparable of the singleton member if the list has length one.
+ * This won't give the correct ordering semantics, but we rely on lists never taking part in ordering comparisons.</p>
+ *
+ * @return a Comparable that follows XML Schema comparison rules
+ */
+
+ public Comparable getSchemaComparable() {
+ if (content.length == 1) {
+ return content[0].getSchemaComparable();
+ } else {
+ return new ValueSchemaComparable();
+ }
+ }
+
+ private class ValueSchemaComparable implements Comparable<ValueSchemaComparable> {
+ public AtomicArray getValue() {
+ return AtomicArray.this;
+ }
+ public int compareTo(ValueSchemaComparable obj) {
+ try {
+ //if (obj instanceof ValueSchemaComparable) {
+ SequenceIterator<AtomicValue> iter1 = getValue().iterate();
+ SequenceIterator<AtomicValue> iter2 = obj.getValue().iterate();
+ while (true) {
+ AtomicValue item1 = iter1.next();
+ AtomicValue item2 = iter2.next();
+ if (item1 == null && item2 == null) {
+ return 0;
+ }
+ if (item1 == null) {
+ return -1;
+ } else if (item2 == null) {
+ return +1;
+ }
+ int c = item1.getSchemaComparable().compareTo(item2.getSchemaComparable());
+ if (c != 0) {
+ return c;
+ }
+ }
+// } else {
+// return INDETERMINATE_ORDERING;
+// }
+ } catch (XPathException e) {
+ throw new AssertionError("Failure comparing schema values: " + e.getMessage());
+ }
+ }
+
+ public boolean equals(/*@NotNull*/ Object obj) {
+ return ValueSchemaComparable.class.isAssignableFrom(obj.getClass())
+ && compareTo((ValueSchemaComparable)obj) == 0;
+ }
+
+ public int hashCode() {
+ try {
+ int hash = 0x06639662; // arbitrary seed
+ SequenceIterator iter = getValue().iterate();
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ return hash;
+ }
+ if(item instanceof AtomicValue){
+ hash ^= ((AtomicValue)item).getSchemaComparable().hashCode();
+ }
+ }
+ } catch (XPathException e) {
+ return 0;
+ }
+ }
+ }
+
+ /**
+ * Reduce the sequence to its simplest form. If the value is an empty sequence, the result will be
+ * EmptySequence.getInstance(). If the value is a single atomic value, the result will be an instance
+ * of AtomicValue. If the value is a single item of any other kind, the result will be an instance
+ * of SingletonItem. Otherwise, the result will typically be unchanged.
+ *
+ * @return the simplified sequence
+ */
+ public GroundedValue reduce() {
+ int len = getLength();
+ if (len == 0) {
+ return EmptySequence.getInstance();
+ } else if (len == 1) {
+ return itemAt(0);
+ } else {
+ return this;
+ }
+ }
+}
+
diff --git a/sf/saxon/om/AtomicSequence.java b/sf/saxon/om/AtomicSequence.java
new file mode 100644
index 0000000..2ae5475
--- /dev/null
+++ b/sf/saxon/om/AtomicSequence.java
@@ -0,0 +1,58 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * Interface representing a sequence of atomic values. This is often used to represent the
+ * typed value of a node. In most cases the typed value of a node is a single atomic value,
+ * so the class AtomicValue implements this interface.
+ *
+ * <p>An AtomicSequence is always represented as a GroundedValue: that is, the entire sequence
+ * is in memory, making operations such as {@link #itemAt(int)} and {@link #getLength()} possible.</p>
+ */
+
+public interface AtomicSequence extends Sequence, GroundedValue {
+
+ public AtomicValue head();
+
+ public SequenceIterator<? extends AtomicValue> iterate();
+
+ public AtomicValue itemAt(int n);
+
+ public int getLength();
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules.
+ * @return the canonical lexical representation if defined in XML Schema; otherwise, the result
+ * of casting to string according to the XPath 2.0 rules
+ */
+
+ public CharSequence getCanonicalLexicalRepresentation();
+
+ /**
+ * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
+ * The default implementation is written to compare sequences of atomic values.
+ * This method is overridden for AtomicValue and its subclasses.
+ *
+ * <p>In the case of data types that are partially ordered, the returned Comparable extends the standard
+ * semantics of the compareTo() method by returning the value {@link SequenceTool#INDETERMINATE_ORDERING} when there
+ * is no defined order relationship between two given values.</p>
+ *
+ * @return a Comparable that follows XML Schema comparison rules
+ */
+
+ public Comparable getSchemaComparable();
+
+ public CharSequence getStringValueCS();
+
+ public String getStringValue();
+}
+
diff --git a/sf/saxon/om/AttributeCollection.java b/sf/saxon/om/AttributeCollection.java
new file mode 100644
index 0000000..6b2ac93
--- /dev/null
+++ b/sf/saxon/om/AttributeCollection.java
@@ -0,0 +1,201 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+
+import net.sf.saxon.type.SimpleType;
+
+/**
+ * AttributeCollection represents the collection of attributes available on a particular element
+ * node. It is modelled on the SAX2 Attributes interface, but is extended firstly to work with
+ * Saxon NamePools, and secondly to provide type information as required by the XPath 2.0 data model.
+ */
+
+public interface AttributeCollection {
+
+ /**
+ * Return the number of attributes in the list.
+ *
+ * @return The number of attributes in the list.
+ */
+
+ int getLength();
+
+ /**
+ * Get the node name of an attribute (by position)
+ * @param index The position of the attribute in the list
+ * @return The node name of the attribute, or null if there is no attribute in that position
+ */
+
+ NodeName getNodeName(int index);
+
+ /**
+ * Get the namecode of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The name code of the attribute, or -1 if there is no attribute at that position.
+ */
+
+ int getNameCode(int index);
+
+
+ /**
+ * Get the type annotation of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The type annotation
+ */
+
+ SimpleType getTypeAnnotation(int index);
+
+ /**
+ * Get the locationID of an attribute (by position)
+ * @param index The position of the attribute in the list.
+ * @return The location identifier of the attribute. This can be supplied
+ * to a {@link net.sf.saxon.event.LocationProvider} in order to obtain the
+ * actual system identifier and line number of the relevant location
+ */
+
+ int getLocationId(int index);
+
+ /**
+ * Get the systemId part of the location of an attribute, at a given index.
+ *
+ * <p>Attribute location information is not available from a SAX parser, so this method
+ * is not useful for getting the location of an attribute in a source document. However,
+ * in a Saxon result document, the location information represents the location in the
+ * stylesheet of the instruction used to generate this attribute, which is useful for
+ * debugging.</p>
+ * @param index the required attribute
+ * @return the systemId of the location of the attribute
+ */
+
+ String getSystemId(int index);
+
+ /**
+ * Get the line number part of the location of an attribute, at a given index.
+ *
+ * <p>Attribute location information is not available from a SAX parser, so this method
+ * is not useful for getting the location of an attribute in a source document. However,
+ * in a Saxon result document, the location information represents the location in the
+ * stylesheet of the instruction used to generate this attribute, which is useful for
+ * debugging.</p>
+ * @param index the required attribute
+ * @return the line number of the location of the attribute
+ */
+
+ int getLineNumber(int index);
+
+ /**
+ * Get the properties of an attribute (by position)
+ * @param index The position of the attribute in the list.
+ * @return The properties of the attribute. This is a set
+ * of bit-settings defined in class {@link net.sf.saxon.event.ReceiverOptions}. The
+ * most interesting of these is {{@link net.sf.saxon.event.ReceiverOptions#DEFAULTED_ATTRIBUTE},
+ * which indicates an attribute that was added to an element as a result of schema validation.
+ */
+
+ int getProperties(int index);
+
+ /**
+ * Get the prefix of the name of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The prefix of the attribute name as a string, or null if there
+ * is no attribute at that position. Returns "" for an attribute that
+ * has no prefix.
+ */
+
+ String getPrefix(int index);
+
+ /**
+ * Get the lexical QName of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The lexical QName of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ String getQName(int index);
+
+ /**
+ * Get the local name of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The local name of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ String getLocalName(int index);
+
+ /**
+ * Get the namespace URI of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The namespace URI of the attribute as a string
+ * (empty string for an attribute in no namespace), or null if there
+ * is no attribute at that position.
+ */
+
+ String getURI(int index);
+
+ /**
+ * Get the index of an attribute (by name).
+ *
+ * @param uri The namespace uri of the attribute.
+ * @param localname The local name of the attribute.
+ * @return The index position of the attribute
+ */
+
+ int getIndex(String uri, String localname);
+
+ /**
+ * Get the index, given the fingerprint
+ */
+
+ int getIndexByFingerprint(int fingerprint);
+
+ /**
+ * Get the attribute value using its fingerprint
+ */
+
+ String getValueByFingerprint(int fingerprint);
+
+ /**
+ * Get the value of an attribute (by name).
+ *
+ * @param uri The namespace uri of the attribute.
+ * @param localname The local name of the attribute.
+ * @return The value of the attribute
+ */
+
+ public String getValue(String uri, String localname);
+
+ /**
+ * Get the value of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The attribute value as a string, or null if
+ * there is no attribute at that position.
+ */
+
+ String getValue(int index);
+
+ /**
+ * Determine whether a given attribute has the is-ID property set
+ */
+
+ boolean isId(int index);
+
+ /**
+ * Determine whether a given attribute has the is-idref property set
+ */
+
+ boolean isIdref(int index);
+}
+
diff --git a/sf/saxon/om/AxisInfo.java b/sf/saxon/om/AxisInfo.java
new file mode 100644
index 0000000..2c896c6
--- /dev/null
+++ b/sf/saxon/om/AxisInfo.java
@@ -0,0 +1,348 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+
+/**
+ * An axis, that is a direction of navigation in the document structure.
+ */
+
+public final class AxisInfo {
+
+ /**
+ * Constant representing the ancestor axis
+ */
+
+ public static final byte ANCESTOR = 0;
+ /** Constant representing the ancestor-or-self axis
+ */
+ public static final byte ANCESTOR_OR_SELF = 1;
+ /** Constant representing the attribute axis
+ */
+ public static final byte ATTRIBUTE = 2;
+ /** Constant representing the child axis
+ */
+ public static final byte CHILD = 3;
+ /** Constant representing the descendant axis
+ */
+ public static final byte DESCENDANT = 4;
+ /** Constant representing the descendant-or-self axis
+ */
+ public static final byte DESCENDANT_OR_SELF = 5;
+ /** Constant representing the following axis
+ */
+ public static final byte FOLLOWING = 6;
+ /** Constant representing the following-sibling axis
+ */
+ public static final byte FOLLOWING_SIBLING = 7;
+ /** Constant representing the namespace axis
+ */
+ public static final byte NAMESPACE = 8;
+ /** Constant representing the parent axis
+ */
+ public static final byte PARENT = 9;
+ /** Constant representing the preceding axis
+ */
+ public static final byte PRECEDING = 10;
+ /** Constant representing the preceding-sibling axis
+ */
+ public static final byte PRECEDING_SIBLING = 11;
+ /** Constant representing the self axis
+ */
+ public static final byte SELF = 12;
+
+ // preceding-or-ancestor axis gives all preceding nodes including ancestors,
+ // in reverse document order
+
+ /** Constant representing the preceding-or-ancestor axis. This axis is used internally by the xsl:number implementation, it returns the union of the preceding axis and the ancestor axis.
+ */
+ public static final byte PRECEDING_OR_ANCESTOR = 13;
+
+ /**
+ * Table indicating the principal node type of each axis
+ */
+
+ public static final short[] principalNodeType =
+ {
+ Type.ELEMENT, // ANCESTOR
+ Type.ELEMENT, // ANCESTOR_OR_SELF;
+ Type.ATTRIBUTE, // ATTRIBUTE;
+ Type.ELEMENT, // CHILD;
+ Type.ELEMENT, // DESCENDANT;
+ Type.ELEMENT, // DESCENDANT_OR_SELF;
+ Type.ELEMENT, // FOLLOWING;
+ Type.ELEMENT, // FOLLOWING_SIBLING;
+ Type.NAMESPACE, // NAMESPACE;
+ Type.ELEMENT, // PARENT;
+ Type.ELEMENT, // PRECEDING;
+ Type.ELEMENT, // PRECEDING_SIBLING;
+ Type.ELEMENT, // SELF;
+ Type.ELEMENT, // PRECEDING_OR_ANCESTOR;
+ };
+
+ /**
+ * Table indicating for each axis whether it is in forwards document order
+ */
+
+ public static final boolean[] isForwards =
+ {
+ false, // ANCESTOR
+ false, // ANCESTOR_OR_SELF;
+ true, // ATTRIBUTE;
+ true, // CHILD;
+ true, // DESCENDANT;
+ true, // DESCENDANT_OR_SELF;
+ true, // FOLLOWING;
+ true, // FOLLOWING_SIBLING;
+ true, // NAMESPACE;
+ true, // PARENT;
+ false, // PRECEDING;
+ false, // PRECEDING_SIBLING;
+ true, // SELF;
+ false, // PRECEDING_OR_ANCESTOR;
+ };
+
+ /**
+ * Table indicating for each axis whether it is in reverse document order
+ */
+
+// public static final boolean[] isReverse =
+// {
+// true, // ANCESTOR
+// true, // ANCESTOR_OR_SELF;
+// false, // ATTRIBUTE;
+// false, // CHILD;
+// false, // DESCENDANT;
+// false, // DESCENDANT_OR_SELF;
+// false, // FOLLOWING;
+// false, // FOLLOWING_SIBLING;
+// false, // NAMESPACE;
+// true, // PARENT;
+// true, // PRECEDING;
+// true, // PRECEDING_SIBLING;
+// true, // SELF;
+// true, // PRECEDING_OR_ANCESTOR;
+// };
+
+ /**
+ * Table indicating for each axis whether it is a peer axis. An axis is a peer
+ * axis if no node on the axis is an ancestor of another node on the axis.
+ */
+
+ public static final boolean[] isPeerAxis =
+ {
+ false, // ANCESTOR
+ false, // ANCESTOR_OR_SELF;
+ true, // ATTRIBUTE;
+ true, // CHILD;
+ false, // DESCENDANT;
+ false, // DESCENDANT_OR_SELF;
+ false, // FOLLOWING;
+ true, // FOLLOWING_SIBLING;
+ true, // NAMESPACE;
+ true, // PARENT;
+ false, // PRECEDING;
+ true, // PRECEDING_SIBLING;
+ true, // SELF;
+ false, // PRECEDING_OR_ANCESTOR;
+ };
+
+ /**
+ * Table indicating for each axis whether it is contained within the subtree
+ * rooted at the origin node.
+ */
+
+ public static final boolean[] isSubtreeAxis =
+ {
+ false, // ANCESTOR
+ false, // ANCESTOR_OR_SELF;
+ true, // ATTRIBUTE;
+ true, // CHILD;
+ true, // DESCENDANT;
+ true, // DESCENDANT_OR_SELF;
+ false, // FOLLOWING;
+ false, // FOLLOWING_SIBLING;
+ true, // NAMESPACE;
+ false, // PARENT;
+ false, // PRECEDING;
+ false, // PRECEDING_SIBLING;
+ true, // SELF;
+ false, // PRECEDING_OR_ANCESTOR;
+ };
+
+ /**
+ * Table giving the name of each axis as used in XPath, for example "ancestor-or-self"
+ */
+
+ public static final String[] axisName =
+ {
+ "ancestor", // ANCESTOR
+ "ancestor-or-self", // ANCESTOR_OR_SELF;
+ "attribute", // ATTRIBUTE;
+ "child", // CHILD;
+ "descendant", // DESCENDANT;
+ "descendant-or-self", // DESCENDANT_OR_SELF;
+ "following", // FOLLOWING;
+ "following-sibling", // FOLLOWING_SIBLING;
+ "namespace", // NAMESPACE;
+ "parent", // PARENT;
+ "preceding", // PRECEDING;
+ "preceding-sibling", // PRECEDING_SIBLING;
+ "self", // SELF;
+ "preceding-or-ancestor",// PRECEDING_OR_ANCESTOR;
+ };
+
+ /**
+ * The class is never instantiated
+ */
+
+ private AxisInfo() {
+ }
+
+ /**
+ * Resolve an axis name into a symbolic constant representing the axis
+ *
+ * @param name
+ * @throws XPathException
+ * @return integer value representing the named axis
+ */
+
+ public static byte getAxisNumber(String name) throws XPathException {
+ if (name.equals("ancestor")) return ANCESTOR;
+ if (name.equals("ancestor-or-self")) return ANCESTOR_OR_SELF;
+ if (name.equals("attribute")) return ATTRIBUTE;
+ if (name.equals("child")) return CHILD;
+ if (name.equals("descendant")) return DESCENDANT;
+ if (name.equals("descendant-or-self")) return DESCENDANT_OR_SELF;
+ if (name.equals("following")) return FOLLOWING;
+ if (name.equals("following-sibling")) return FOLLOWING_SIBLING;
+ if (name.equals("namespace")) return NAMESPACE;
+ if (name.equals("parent")) return PARENT;
+ if (name.equals("preceding")) return PRECEDING;
+ if (name.equals("preceding-sibling")) return PRECEDING_SIBLING;
+ if (name.equals("self")) return SELF;
+ // preceding-or-ancestor cannot be used in an XPath expression
+ throw new XPathException("Unknown axis name: " + name);
+ }
+
+ /**
+ * The following table indicates the combinations of axis and node-kind that always
+ * return an empty result.
+ */
+
+ private static final int DOC = 1<<Type.DOCUMENT;
+ private static final int ELE = 1<<Type.ELEMENT;
+ private static final int ATT = 1<<Type.ATTRIBUTE;
+ private static final int TEX = 1<<Type.TEXT;
+ private static final int PIN = 1<<Type.PROCESSING_INSTRUCTION;
+ private static final int COM = 1<<Type.COMMENT;
+ private static final int NAM = 1<<Type.NAMESPACE;
+
+ private static int[] voidAxisTable = {
+ DOC, // ANCESTOR
+ 0, // ANCESTOR_OR_SELF;
+ DOC|ATT|TEX|PIN|COM|NAM, // ATTRIBUTE;
+ ATT|TEX|PIN|COM|NAM, // CHILD;
+ ATT|TEX|PIN|COM|NAM, // DESCENDANT;
+ 0, // DESCENDANT_OR_SELF;
+ DOC, // FOLLOWING;
+ DOC|ATT|NAM, // FOLLOWING_SIBLING;
+ DOC|ATT|TEX|PIN|COM|NAM, // NAMESPACE;
+ DOC, // PARENT;
+ DOC, // PRECEDING;
+ DOC|ATT|NAM, // PRECEDING_SIBLING;
+ 0, // SELF;
+ };
+
+ /**
+ * Ask whether a given axis can contain any nodes when starting at the specified node kind.
+ * For example, the attribute axis when starting at an attribute node will always be empty
+ * @param axis the axis, for example {@link AxisInfo#ATTRIBUTE}
+ * @param nodeKind the node kind of the origin node, for example {@link Type#ATTRIBUTE}
+ * @return true if no nodes will ever appear on the specified axis when starting at the specified
+ * node kind.
+ */
+
+ public static boolean isAlwaysEmpty(int axis, int nodeKind) {
+ return (voidAxisTable[axis] & (1<<nodeKind)) != 0;
+ }
+
+ /**
+ * The following table indicates the kinds of node found on each axis
+ */
+
+ private static int[] nodeKindTable = {
+ DOC|ELE, // ANCESTOR
+ DOC|ELE|ATT|TEX|PIN|COM|NAM, // ANCESTOR_OR_SELF;
+ ATT, // ATTRIBUTE;
+ ELE|TEX|PIN|COM, // CHILD;
+ ELE|TEX|PIN|COM, // DESCENDANT;
+ DOC|ELE|ATT|TEX|PIN|COM|NAM, // DESCENDANT_OR_SELF;
+ ELE|TEX|PIN|COM, // FOLLOWING;
+ ELE|TEX|PIN|COM, // FOLLOWING_SIBLING;
+ NAM, // NAMESPACE;
+ DOC|ELE, // PARENT;
+ DOC|ELE|TEX|PIN|COM, // PRECEDING;
+ ELE|TEX|PIN|COM, // PRECEDING_SIBLING;
+ DOC|ELE|ATT|TEX|PIN|COM|NAM, // SELF;
+ };
+
+ /**
+ * Determine whether a given kind of node can be found on a given axis. For example,
+ * the attribute axis will never contain any element nodes.
+ * @param axis the axis, for example {@link AxisInfo#ATTRIBUTE}
+ * @param nodeKind the node kind of the origin node, for example {@link Type#ELEMENT}
+ * @return true if the given kind of node can appear on the specified axis
+ */
+
+ public static boolean containsNodeKind(int axis, int nodeKind) {
+ return (nodeKindTable[axis] & (1<<nodeKind)) != 0;
+ }
+
+ /**
+ * For each axis, determine the inverse axis, in the sense that if A is on axis X starting at B,
+ * the B is on the axis inverseAxis[X] starting at A. This doesn't quite work for the PARENT axis,
+ * which has no simple inverse: this table gives the inverse as CHILD
+ */
+
+ /*@NotNull*/ public static byte[] inverseAxis = {
+ DESCENDANT, // ANCESTOR
+ DESCENDANT_OR_SELF, // ANCESTOR_OR_SELF;
+ PARENT, // ATTRIBUTE;
+ PARENT, // CHILD;
+ ANCESTOR, // DESCENDANT;
+ ANCESTOR_OR_SELF, // DESCENDANT_OR_SELF;
+ PRECEDING, // FOLLOWING;
+ PRECEDING_SIBLING, // FOLLOWING_SIBLING;
+ PARENT, // NAMESPACE;
+ CHILD, // PARENT;
+ FOLLOWING, // PRECEDING;
+ FOLLOWING_SIBLING, // PRECEDING_SIBLING;
+ SELF // SELF;
+ };
+
+}
+
+/*
+ // a list for any future cut-and-pasting...
+ ANCESTOR
+ ANCESTOR_OR_SELF;
+ ATTRIBUTE;
+ CHILD;
+ DESCENDANT;
+ DESCENDANT_OR_SELF;
+ FOLLOWING;
+ FOLLOWING_SIBLING;
+ NAMESPACE;
+ PARENT;
+ PRECEDING;
+ PRECEDING_SIBLING;
+ SELF;
+*/
\ No newline at end of file
diff --git a/sf/saxon/om/Chain.java b/sf/saxon/om/Chain.java
new file mode 100644
index 0000000..88cfff0
--- /dev/null
+++ b/sf/saxon/om/Chain.java
@@ -0,0 +1,395 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.SequenceExtent;
+import net.sf.saxon.value.SingletonItem;
+
+import java.util.*;
+
+/**
+ * A chain is an implementation of Sequence that represents the concatenation of
+ * a number of subsequences.
+ *
+ * <p>The most common use case is a recursive function that appends one item to a
+ * sequence each time it is called. Each call of this function will create a Chain
+ * with two subsequences, the first being a Chain and the second an individual item.
+ * The design of the class is constrained by the need to handle this extreme case.</p>
+ *
+ * <p>Firstly, the iterator for the class cannot use simple recursion to navigate the
+ * tree because it will often be too deep, causing StackOverflow. So it maintains its
+ * own Stack (on the Java heap).</p>
+ *
+ * <p>Secondly, even using the heap will run out of space at about a million entries.
+ * To prevent this, any Chains of size less than thirty items are amalgamated into
+ * chunks of 30. Building larger chunks than this would cause insertion operations
+ * to have linear performance (and thus the total cost of sequence construction
+ * would be quadratic. The figure of 30 was chosen because elapsed time is almost
+ * as good as with smaller chunks, and memory use during navigation is substantially
+ * reduced.</p>
+ */
+public class Chain implements GroundedValue {
+
+ private List<GroundedValue> children = new ArrayList<GroundedValue>();
+
+ public Chain(List<GroundedValue> children) {
+ this.children = children;
+
+ int size = 0;
+ boolean copy = false;
+ for (GroundedValue gv : children) {
+ if (gv instanceof Chain) {
+ if (((Chain)gv).children.size() < 30) {
+ size += ((Chain)gv).children.size();
+ copy = true;
+ } else {
+ size++;
+ }
+ } else {
+ size++;
+ }
+ }
+ if (copy) {
+ this.children = new ArrayList<GroundedValue>(size);
+ for (GroundedValue gv : children) {
+ if (gv instanceof Chain) {
+ if (((Chain)gv).children.size() < 30) {
+ this.children.addAll(((Chain)gv).children);
+ } else {
+ this.children.add(gv);
+ }
+ } else {
+ this.children.add(gv);
+ }
+ }
+ } else {
+ this.children = children;
+ }
+ }
+
+ public Item head() throws XPathException {
+ for (Sequence seq : children) {
+ Item head = seq.head();
+ if (head != null) {
+ return head;
+ }
+ }
+ return null;
+ }
+
+ public SequenceIterator<Item> iterate() {
+ return new ChainIterator();
+ }
+
+ /**
+ * Add more items to the end of this sequence. This method must only be called while the value
+ * is being constructed, since the sequence thereafter is immutable.
+ * @param sequence the items to be added
+ */
+
+ public void append(GroundedValue sequence) {
+ children.add(sequence);
+ }
+
+ /**
+ * Add a single item to the end of this sequence. This method must only be called while the value
+ * is being constructed, since the sequence thereafter is immutable.
+ * @param item the item to be added
+ */
+
+
+ public void append(Item item) {
+ if (item instanceof GroundedValue) {
+ children.add((GroundedValue) item);
+ } else {
+ children.add(new SingletonItem(item));
+ }
+ }
+
+ /**
+ * Consolidate the sequence. This reduces it to a form in which the chain wraps a single sequenceExtent,
+ * making it easy to perform operations such as subsequence() and itemAt() efficiently.
+ */
+
+ private void consolidate() {
+ try {
+ List<Item> extent = new ArrayList<Item>();
+ SequenceIterator<Item> iter = iterate();
+ Item item;
+ while ((item = iter.next()) != null) {
+ extent.add(item);
+ }
+ GroundedValue replacement = new SequenceExtent<Item>(extent);
+ children = new ArrayList<GroundedValue>(1);
+ this.children.add(replacement);
+ } catch (XPathException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * Get the n'th item in the value, counting from 0
+ *
+ * @param n the index of the required item, with 0 representing the first item in the sequence
+ * @return the n'th item if it exists, or null otherwise
+ */
+ public Item itemAt(int n) {
+ if (n < 0 || children.size() == 0) {
+ return null;
+ }
+ if (children.size() > 1) {
+ consolidate();
+ }
+ return children.get(0).itemAt(n);
+ }
+
+ /**
+ * Get a subsequence of the value
+ *
+ * @param start the index of the first item to be included in the result, counting from zero.
+ * A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
+ * sequence is returned
+ * @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
+ * get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
+ * is returned. If the value goes off the end of the sequence, the result returns items up to the end
+ * of the sequence
+ * @return the required subsequence.
+ */
+ public GroundedValue subsequence(int start, int length) {
+ if (children.size() == 0) {
+ return EmptySequence.getInstance();
+ }
+ if (children.size() > 1) {
+ consolidate();
+ }
+ return children.get(0).subsequence(start, length);
+ }
+
+ /**
+ * Get the size of the value (the number of items)
+ *
+ * @return the number of items in the sequence
+ */
+ public int getLength() {
+ int n = 0;
+ for (GroundedValue v : children) {
+ n += v.getLength();
+ }
+ return n;
+ }
+
+ /**
+ * Get the effective boolean value of this sequence
+ *
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if the sequence has no effective boolean value (for example a sequence of two integers)
+ */
+ public boolean effectiveBooleanValue() throws XPathException {
+ return ExpressionTool.effectiveBooleanValue(iterate());
+ }
+
+ /**
+ * Get the string value of this sequence. The string value of an item is the result of applying the string()
+ * function. The string value of a sequence is the space-separated result of applying the string-join() function
+ * using a single space as the separator
+ *
+ * @return the string value of the sequence.
+ * @throws net.sf.saxon.trans.XPathException
+ * if the sequence contains items that have no string value (for example, function items)
+ */
+ public String getStringValue() throws XPathException {
+ return SequenceTool.getStringValue(this);
+ }
+
+ /**
+ * Get the string value of this sequence. The string value of an item is the result of applying the string()
+ * function. The string value of a sequence is the space-separated result of applying the string-join() function
+ * using a single space as the separator
+ *
+ * @return the string value of the sequence.
+ * @throws net.sf.saxon.trans.XPathException
+ * if the sequence contains items that have no string value (for example, function items)
+ */
+ public CharSequence getStringValueCS() throws XPathException {
+ return SequenceTool.getStringValue(this);
+ }
+
+ /**
+ * Reduce the sequence to its simplest form. If the value is an empty sequence, the result will be
+ * EmptySequence.getInstance(). If the value is a single atomic value, the result will be an instance
+ * of AtomicValue. If the value is a single item of any other kind, the result will be an instance
+ * of SingletonItem. Otherwise, the result will typically be unchanged.
+ *
+ * @return the simplified sequence
+ */
+ public GroundedValue reduce() {
+ if (children.size() != 1) {
+ consolidate();
+ }
+ return children.get(0);
+ }
+
+ private class ChainIterator implements SequenceIterator<Item>, GroundedIterator<Item> {
+
+ private class ChainPosition {
+ Chain chain;
+ int offset;
+ public ChainPosition(Chain chain, int offset) {
+ this.chain = chain;
+ this.offset = offset;
+ }
+ }
+
+ private Queue<SequenceIterator> queue = new LinkedList<SequenceIterator>();
+
+ private Stack<ChainPosition> stack;
+ private Item current;
+ private int position = 0;
+
+ public ChainIterator() {
+ stack = new Stack<ChainPosition>();
+ stack.push(new ChainPosition(Chain.this, 0));
+ }
+
+
+ /**
+ * Get the next item in the sequence.
+ * <p>The coding of this method is designed to avoid recursion, since it is not uncommon for the tree
+ * of Chain objects to be as deep as the length of the sequence it represents, and this inevitably
+ * leads to stack overflow. So the method maintains its own stack (on the Java heap).</p>
+ *
+ * @return the next item, or null if there are no more items.
+ */
+
+ public Item next() throws XPathException {
+
+ // If there are iterators on the queue waiting to be processed, then take the first
+ // item from the first iterator on the queue.
+ while (!queue.isEmpty()) {
+ SequenceIterator ui = queue.peek();
+ while (ui != null) {
+ current = ui.next();
+ if (current != null) {
+ position++;
+ return current;
+ } else {
+ queue.remove();
+ ui = queue.peek();
+ }
+ }
+ }
+
+ // Otherwise, or after attempting to process the iterators on the queue, look at the
+ // stack of Chain objects and get the next item from the top-most Chain. If this is itself
+ // a Chain, then add it to the stack and repeat. If this Chain is exhausted, then pop it off the
+ // stack and repeat.
+
+ while (!stack.isEmpty()) {
+ ChainPosition cp = stack.peek();
+ if (cp != null) {
+ GroundedValue gv = cp.chain.children.get(cp.offset);
+ if (++cp.offset >= cp.chain.children.size()) {
+ stack.pop();
+ }
+ if (gv instanceof Chain) {
+ stack.push(new ChainPosition((Chain)gv, 0));
+ } else {
+ try {
+ queue.offer(gv.iterate());
+ } catch (XPathException e) {
+ throw new AssertionError(e);
+ }
+ return next();
+ }
+ }
+ }
+
+ // If we get here, there is no more data available
+
+ current = null;
+ position = -1;
+ return null;
+ }
+
+ /**
+ * Get the current value in the sequence (the one returned by the
+ * most recent call on next()). This will be null before the first
+ * call of next().
+ *
+ * @return the current item, the one most recently returned by a call on
+ * next(); or null, if next() has not been called, or if the end
+ * of the sequence has been reached.
+ */
+
+ public Item current() {
+ return current;
+ }
+
+ /**
+ * Get the current position. This will be zero before the first call
+ * on next(), otherwise it will be the number of times that next() has
+ * been called.
+ *
+ * @return the current position, the position of the item returned by the
+ * most recent call of next()
+ */
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+
+ }
+
+ /**
+ * Get another SequenceIterator that iterates over the same items as the original,
+ * but which is repositioned at the start of the sequence.
+ *
+ * @return a SequenceIterator that iterates over the same items,
+ * positioned before the first item
+ */
+
+ /*@NotNull*/
+ public ChainIterator getAnother() {
+ return new ChainIterator();
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link SequenceIterator#GROUNDED}, {@link SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return SequenceIterator.GROUNDED;
+ }
+
+ /**
+ * Return a GroundedValue containing all the items in the sequence returned by this
+ * SequenceIterator. This should be an "in-memory" value, not a Closure.
+ *
+ * @return the corresponding Value
+ */
+ public GroundedValue materialize() throws XPathException {
+ return Chain.this;
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/om/CodedName.java b/sf/saxon/om/CodedName.java
new file mode 100644
index 0000000..be33e7f
--- /dev/null
+++ b/sf/saxon/om/CodedName.java
@@ -0,0 +1,173 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * An implementation of NodeName that encapsulates an integer namecode and a reference to the NamePool from which
+ * it was allocated.
+ */
+public class CodedName implements NodeName {
+
+ private int nameCode;
+ private NamePool pool;
+
+ public CodedName(int nameCode, NamePool pool) {
+ this.nameCode = nameCode;
+ this.pool = pool;
+ }
+
+ /**
+ * Get the prefix of the QName.
+ *
+ * @return the prefix. Returns the empty string if the name is unprefixed.
+ */
+ public String getPrefix() {
+ return pool.getPrefix(nameCode);
+ }
+
+ /**
+ * Get the namespace URI of the QName.
+ *
+ * @return the URI. Returns the empty string to represent the no-namespace
+ */
+ public String getURI() {
+ return pool.getURI(nameCode);
+ }
+
+ /**
+ * Get the local part of the QName
+ *
+ * @return the local part of the QName
+ */
+ public String getLocalPart() {
+ return pool.getLocalName(nameCode);
+ }
+
+ /**
+ * Get the display name, that is the lexical QName in the form [prefix:]local-part
+ *
+ * @return the lexical QName
+ */
+ public String getDisplayName() {
+ return pool.getDisplayName(nameCode);
+ }
+
+ /**
+ * Get the name in the form of a StructuredQName
+ *
+ * @return the name in the form of a StructuredQName
+ */
+ public StructuredQName getStructuredQName() {
+ return pool.getStructuredQName(nameCode);
+ }
+
+ /**
+ * Test whether this name is in the same namespace as another name
+ *
+ * @return true if the two names are in the same namespace
+ */
+ public boolean isInSameNamespace(/*@NotNull*/ NodeName other) {
+ return getURI().equals(other.getURI());
+ }
+
+ /**
+ * Test whether this name is in a given namespace
+ *
+ * @param ns the namespace to be tested against
+ * @return true if the name is in the specified namespace
+ */
+ public boolean isInNamespace(String ns) {
+ return getURI().equals(ns);
+ }
+
+ /**
+ * Get a {@link net.sf.saxon.om.NamespaceBinding} whose (prefix, uri) pair are the prefix and URI of this
+ * node name
+ *
+ * @return the corresponding NamespaceBinding
+ */
+
+ public NamespaceBinding getNamespaceBinding() {
+ return pool.getNamespaceBinding(nameCode);
+ }
+
+
+ /**
+ * Ask whether this node name representation has a known namecode and fingerprint
+ *
+ * @return true if the methods getFingerprint() and getNameCode() will
+ * return a result other than -1
+ */
+ public boolean hasFingerprint() {
+ return true;
+ }
+
+ /**
+ * Get the fingerprint of this name if known. This method should not to any work to allocate
+ * a fingerprint if none is already available
+ *
+ * @return the fingerprint if known; otherwise -1
+ */
+ public int getFingerprint() {
+ return nameCode & NamePool.FP_MASK;
+ }
+
+ /**
+ * Get the nameCode of this name if known. This method should not to any work to allocate
+ * a nameCode if none is already available
+ *
+ * @return the nameCode if known; otherwise -1
+ */
+ public int getNameCode() {
+ return nameCode;
+ }
+
+ /**
+ * Get the nameCode of this name, allocating a new code from the namepool if necessary
+ *
+ * @param namePool the NamePool used to allocate the name
+ * @return a nameCode for this name, newly allocated if necessary
+ */
+ public int allocateNameCode(NamePool namePool) {
+ return nameCode;
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+ @Override
+ public int hashCode() {
+ return StructuredQName.computeHashCode(getURI(), getLocalPart());
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof NodeName) {
+ NodeName n = (NodeName)obj;
+ if (n.hasFingerprint()) {
+ return getFingerprint() == n.getFingerprint();
+ } else {
+ return n.getLocalPart().equals(getLocalPart()) && n.isInNamespace(getURI());
+ }
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isIdentical(IdentityComparable other) {
+ if(other instanceof NodeName) {
+ return this.equals(other) && this.getPrefix().equals(((NodeName)other).getPrefix());
+ } else {
+ return false;
+ }
+ }
+}
+
diff --git a/sf/saxon/om/CopyOptions.java b/sf/saxon/om/CopyOptions.java
new file mode 100644
index 0000000..4b742ea
--- /dev/null
+++ b/sf/saxon/om/CopyOptions.java
@@ -0,0 +1,41 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.event.ReceiverOptions;
+
+/**
+ * Non-instantiable class to define options for the {@link NodeInfo#copy} method
+ */
+public abstract class CopyOptions {
+
+ public static final int LOCAL_NAMESPACES = 1;
+
+ public static final int ALL_NAMESPACES = 2;
+
+ public static final int SOME_NAMESPACES = LOCAL_NAMESPACES | ALL_NAMESPACES;
+
+ public static final int TYPE_ANNOTATIONS = 4;
+
+ public static final int FOR_UPDATE = 8;
+
+ public static final int IS_XQUERY = 16; // either XSLT or XQUERY: only affects error codes
+
+ public static boolean includes(int options, int option) {
+ return (options & option) == option;
+ }
+
+ public static int getStartDocumentProperties(int copyOptions) {
+ int properties = 0;
+ if (CopyOptions.includes(copyOptions, CopyOptions.FOR_UPDATE)) {
+ properties |= ReceiverOptions.MUTABLE_TREE;
+ }
+ return properties;
+ }
+}
+
diff --git a/sf/saxon/om/DocumentInfo.java b/sf/saxon/om/DocumentInfo.java
new file mode 100644
index 0000000..9a4853d
--- /dev/null
+++ b/sf/saxon/om/DocumentInfo.java
@@ -0,0 +1,104 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import java.util.Iterator;
+
+/**
+ * This interface represents a document node as defined in the XPath 2.0 data model.
+ * It extends NodeInfo, which is used to represent any node. Every document node must
+ * be an instance of DocumentInfo.
+ * <p>
+ * The interface supports two methods in addition to those for NodeInfo: one to find
+ * elements given their ID value, and one to locate unparsed entities. In addition,
+ * document nodes have an important property that is not true of nodes in general:
+ * two distinct Java DocumentInfo objects never represent the same document node.
+ * So the Java "==" operator returns the same result as the {@link NodeInfo#isSameNodeInfo}
+ * method.
+ * <p>
+ * This interface is part of the Saxon public API, and as such (from Saxon8.4 onwards)
+ * those methods that form part of the stable public API are labelled with a JavaDoc "since" tag
+ * to indicate when they were added to the product.
+ *
+ * @author Michael H. Kay
+ * @since 8.4
+ */
+
+public interface DocumentInfo extends NodeInfo {
+
+ /**
+ * Ask whether the document contains any nodes whose type annotation is anything other than
+ * UNTYPED
+ * @return true if the document contains elements whose type is other than UNTYPED
+ */
+
+ public boolean isTyped();
+
+ /**
+ * Get the element with a given ID, if any
+ *
+ * @param id the required ID value
+ * @param getParent true if running the element-with-id() function rather than the id()
+ * function; the difference is that in the case of an element of type xs:ID, the parent of
+ * the element should be returned, not the element itself.
+ * @return the element with the given ID, or null if there is no such ID
+ * present (or if the parser has not notified attributes as being of
+ * type ID)
+ * @since 8.4. Second argument added in 9.2.
+ */
+
+ public NodeInfo selectID(String id, boolean getParent);
+
+ /**
+ * Get the list of unparsed entities defined in this document
+ * @return an Iterator, whose items are of type String, containing the names of all
+ * unparsed entities defined in this document. If there are no unparsed entities or if the
+ * information is not available then an empty iterator is returned
+ * @since 9.1
+ */
+
+ public Iterator<String> getUnparsedEntityNames();
+
+ /**
+ * Get the unparsed entity with a given name
+ *
+ * @param name the name of the entity
+ * @return if the entity exists, return an array of two Strings, the first
+ * holding the system ID of the entity (as an absolute URI if possible),
+ * the second holding the public ID if there is one, or null if not.
+ * If the entity does not exist, the method returns null.
+ * Applications should be written on the assumption that this array may
+ * be extended in the future to provide additional information.
+ * @since 8.4
+ */
+
+ public String[] getUnparsedEntity(String name);
+
+ /**
+ * Set user data on the document node. The user data can be retrieved subsequently
+ * using {@link #getUserData}
+ * @param key A string giving the name of the property to be set. Clients are responsible
+ * for choosing a key that is likely to be unique. Must not be null. Keys used internally
+ * by Saxon are prefixed "saxon:".
+ * @param value The value to be set for the property. May be null, which effectively
+ * removes the existing value for the property.
+ */
+
+ public void setUserData(String key, Object value);
+
+ /**
+ * Get user data held in the document node. This retrieves properties previously set using
+ * {@link #setUserData}
+ * @param key A string giving the name of the property to be retrieved.
+ * @return the value of the property, or null if the property has not been defined.
+ */
+
+ public Object getUserData(String key);
+
+}
+
diff --git a/sf/saxon/om/DocumentPool.java b/sf/saxon/om/DocumentPool.java
new file mode 100644
index 0000000..53057dd
--- /dev/null
+++ b/sf/saxon/om/DocumentPool.java
@@ -0,0 +1,172 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.trans.KeyManager;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * An object representing the collection of documents handled during
+ * a single transformation.
+ *
+ * <p>The function of allocating document numbers is performed
+ * by the DocumentNumberAllocator in the Configuration, not by the DocumentPool. This has a
+ * number of effects: in particular it allows operations involving multiple
+ * documents (such as generateId() and document()) to occur in a free-standing
+ * XPath environment.</p>
+ */
+
+public final class DocumentPool implements Serializable {
+
+ // The document pool ensures that the document()
+ // function, when called twice with the same URI, returns the same document
+ // each time. For this purpose we use a hashtable from
+ // URI to DocumentInfo object.
+
+ /*@NotNull*/ private Map<DocumentURI, DocumentInfo> documentNameMap = new HashMap<DocumentURI, DocumentInfo>(10);
+
+
+ // The set of documents known to be unavailable. These documents must remain
+ // unavailable for the duration of a transformation or query!
+
+ /*@NotNull*/ private Set<DocumentURI> unavailableDocuments = new HashSet<DocumentURI>(10);
+
+ /**
+ * Add a document to the pool
+ * @param doc The DocumentInfo for the document in question
+ * @param uri The document-uri property of the document.
+ */
+
+ public synchronized void add(DocumentInfo doc, /*@Nullable*/ String uri) {
+ if (uri!=null) {
+ documentNameMap.put(new DocumentURI(uri), doc);
+ }
+ }
+
+ /**
+ * Add a document to the pool
+ * @param doc The DocumentInfo for the document in question
+ * @param uri The document-uri property of the document.
+ */
+
+ public synchronized void add(DocumentInfo doc, /*@Nullable*/ DocumentURI uri) {
+ if (uri!=null) {
+ documentNameMap.put(uri, doc);
+ }
+ }
+
+ /**
+ * Get the document with a given document-uri
+ * @param uri The document-uri property of the document.
+ * @return the DocumentInfo with the given document-uri property if it exists,
+ * or null if it is not found.
+ */
+
+ public synchronized DocumentInfo find(String uri) {
+ return documentNameMap.get(new DocumentURI(uri));
+ }
+
+ /**
+ * Get the document with a given document-uri
+ * @param uri The document-uri property of the document.
+ * @return the DocumentInfo with the given document-uri property if it exists,
+ * or null if it is not found.
+ */
+
+ public synchronized DocumentInfo find(DocumentURI uri) {
+ return documentNameMap.get(uri);
+ }
+
+
+ /**
+ * Get the URI for a given document node, if it is present in the pool. This supports the
+ * document-uri() function.
+ * @param doc The document node
+ * @return The uri of the document node, if present in the pool, or the systemId of the document node otherwise
+ */
+
+ /*@Nullable*/ public synchronized String getDocumentURI(NodeInfo doc) {
+ for (DocumentURI uri : documentNameMap.keySet()) {
+ DocumentInfo found = find(uri);
+ if (found == null) {
+ continue; // can happen when discard-document() is used concurrently
+ }
+ if (found.isSameNodeInfo(doc)) {
+ return uri.toString();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Determine whether a given document is present in the pool
+ * @param doc the document being sought
+ * @return true if the document is present, false otherwise
+ */
+
+ public synchronized boolean contains(DocumentInfo doc) {
+ // relies on "equals" for nodes comparing node identity
+ return documentNameMap.values().contains(doc);
+ }
+
+ /**
+ * Release a document from the document pool. This means that if the same document is
+ * loaded again later, the source will need to be re-parsed, and nodes will get new identities.
+ * @param doc the document to be discarded from the pool
+ * @return the document supplied in the doc parameter
+ */
+
+ public synchronized DocumentInfo discard(DocumentInfo doc) {
+ for (Map.Entry<DocumentURI, DocumentInfo> e : documentNameMap.entrySet()) {
+ DocumentURI name = e.getKey();
+ DocumentInfo entry = e.getValue();
+ if (entry.isSameNodeInfo(doc)) {
+ documentNameMap.remove(name);
+ return doc;
+ }
+ }
+ return doc;
+ }
+
+ /**
+ * Release all indexs for documents in this pool held by the KeyManager
+ * @param keyManager the keymanager from which indexes are to be released
+ */
+
+ public void discardIndexes(/*@NotNull*/ KeyManager keyManager) {
+ for (DocumentInfo doc : documentNameMap.values()) {
+ keyManager.clearDocumentIndexes(doc);
+ }
+ }
+
+ /**
+ * Add a document URI to the set of URIs known to be unavailable (because doc-available() has returned
+ * false
+ * @param uri the URI of the unavailable document
+ */
+
+ public void markUnavailable(DocumentURI uri) {
+ unavailableDocuments.add(uri);
+ }
+
+ /**
+ * Ask whether a document URI is in the set of URIs known to be unavailable, because doc-available()
+ * has been previously called and has returned false
+ */
+
+ public boolean isMarkedUnavailable(DocumentURI uri) {
+ return unavailableDocuments.contains(uri);
+ }
+
+}
+
diff --git a/sf/saxon/om/DocumentURI.java b/sf/saxon/om/DocumentURI.java
new file mode 100644
index 0000000..f086845
--- /dev/null
+++ b/sf/saxon/om/DocumentURI.java
@@ -0,0 +1,81 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import java.io.File;
+
+/**
+ * This class encapsulates a string used as the value of the document-uri() property of a document,
+ * together with a normalized representation of the string used for equality comparisons. The idea
+ * is that on Windows systems, document URIs are compared using case-blind comparison, but the original
+ * case is retained for display purposes.
+ */
+public class DocumentURI {
+
+ public final static boolean CASE_BLIND_FILES = new File("a").equals(new File("A"));
+
+ private String displayValue;
+ private String normalizedValue;
+
+ /**
+ * Create a DocumentURI object that wraps a given URI
+ * @param uri the URI to be wrapped. Must not be null
+ * @throws NullPointerException if uri is null
+ */
+
+ public DocumentURI(/*@Nullable*/ String uri) {
+ if (uri == null) {
+ throw new NullPointerException("uri");
+ }
+ this.displayValue = uri;
+ this.normalizedValue = normalizeURI(uri);
+ }
+
+ @Override
+ public String toString() {
+ return displayValue;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof DocumentURI && normalizedValue.equals(((DocumentURI)obj).normalizedValue);
+ }
+
+ @Override
+ public int hashCode() {
+ return normalizedValue.hashCode();
+ }
+
+ /**
+ * Normalize the representation of file: URIs to give better equality matching than straight
+ * string comparison. The main purpose is (a) to eliminate the distinction between "file:/" and
+ * "file:///", and (b) to normalize case in the case of Windows filenames: especially the distinction
+ * between "file:/C:" and "file:/c:".
+ * @param uri the URI to be normalized
+ * @return the normalized URI.
+ */
+
+ public static String normalizeURI(String uri) {
+ if (uri == null) {
+ return null;
+ }
+ if (uri.startsWith("FILE:")) {
+ uri = "file:" + uri.substring(5);
+ }
+ if (uri.startsWith("file:")) {
+ if (uri.startsWith("file:///")) {
+ uri = "file:/" + uri.substring(8);
+ }
+ if (CASE_BLIND_FILES) {
+ uri = uri.toLowerCase();
+ }
+ }
+ return uri;
+ }
+}
+
diff --git a/sf/saxon/om/EmptyAtomicSequence.java b/sf/saxon/om/EmptyAtomicSequence.java
new file mode 100644
index 0000000..c4cb728
--- /dev/null
+++ b/sf/saxon/om/EmptyAtomicSequence.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+ * An implementation of AtomicSequence that contains no items.
+ */
+
+public class EmptyAtomicSequence implements AtomicSequence {
+
+ private static EmptyAtomicSequence THE_INSTANCE = new EmptyAtomicSequence();
+
+ public static EmptyAtomicSequence getInstance() {
+ return THE_INSTANCE;
+ }
+
+ public AtomicValue head() {
+ return null;
+ }
+
+ public SequenceIterator<? extends AtomicValue> iterate() {
+ return EmptyIterator.getInstance();
+ }
+
+ public AtomicValue itemAt(int n) {
+ return null;
+ }
+
+ public int getLength() {
+ return 0;
+ }
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules.
+ *
+ * @return the canonical lexical representation if defined in XML Schema; otherwise, the result
+ * of casting to string according to the XPath 2.0 rules
+ */
+ public CharSequence getCanonicalLexicalRepresentation() {
+ return "";
+ }
+
+ /**
+ * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
+ * The default implementation is written to compare sequences of atomic values.
+ * This method is overridden for AtomicValue and its subclasses.
+ * <p/>
+ * <p>In the case of data types that are partially ordered, the returned Comparable extends the standard
+ * semantics of the compareTo() method by returning the value {@link net.sf.saxon.om.SequenceTool#INDETERMINATE_ORDERING} when there
+ * is no defined order relationship between two given values.</p>
+ *
+ * @return a Comparable that follows XML Schema comparison rules
+ */
+ public Comparable getSchemaComparable() {
+ return null;
+ }
+
+ public CharSequence getStringValueCS() {
+ return "";
+ }
+
+ public String getStringValue() {
+ return "";
+ }
+
+ /**
+ * Get a subsequence of the value
+ *
+ * @param start the index of the first item to be included in the result, counting from zero.
+ * A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
+ * sequence is returned
+ * @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
+ * get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
+ * is returned. If the value goes off the end of the sequence, the result returns items up to the end
+ * of the sequence
+ * @return the required subsequence. If min is
+ */
+ public GroundedValue subsequence(int start, int length) {
+ return this;
+ }
+
+ public boolean effectiveBooleanValue() throws XPathException {
+ return false;
+ }
+
+ /**
+ * Reduce the sequence to its simplest form. If the value is an empty sequence, the result will be
+ * EmptySequence.getInstance(). If the value is a single atomic value, the result will be an instance
+ * of AtomicValue. If the value is a single item of any other kind, the result will be an instance
+ * of SingletonItem. Otherwise, the result will typically be unchanged.
+ *
+ * @return the simplified sequence
+ */
+ public GroundedValue reduce() {
+ return EmptySequence.getInstance();
+ }
+}
+
diff --git a/sf/saxon/om/FingerprintedNode.java b/sf/saxon/om/FingerprintedNode.java
new file mode 100644
index 0000000..eb4496a
--- /dev/null
+++ b/sf/saxon/om/FingerprintedNode.java
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * This is a marker interface used to identify nodes that contain a namepool fingerprint. Although all nodes
+ * are capable of returning a fingerprint, some (notably DOM, XOM, and JDOM nodes) need to calculate it on demand.
+ * A node that implements this interface indicates that obtaining the fingerprint for use in name comparisons
+ * is more efficient than using the URI and local name.
+ */
+
+public interface FingerprintedNode {
+ /**
+ * Get the value of the attribute with a given fingerprint.
+ * @param fp the fingerprint of the required attribute
+ * @return the string value of the attribute if present, or null if absent
+ */
+ public String getAttributeValue(int fp);
+}
+
diff --git a/sf/saxon/om/FingerprintedQName.java b/sf/saxon/om/FingerprintedQName.java
new file mode 100644
index 0000000..0ec4204
--- /dev/null
+++ b/sf/saxon/om/FingerprintedQName.java
@@ -0,0 +1,135 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * A QName triple (prefix, URI, local) with the additional ability to hold an integer fingerprint.
+ * The integer fingerprint provides a fast way of checking equality. A FingerprintedQName makes sense
+ * only in the context of a known NamePool, and instances must be compared only if they relate to the
+ * same NamePool. The fingerprint is optional, and is used only if present.
+ */
+public class FingerprintedQName extends StructuredQName implements NodeName {
+
+ private int nameCode = -1;
+
+ public FingerprintedQName(String prefix, String uri, String localName) {
+ super(prefix, uri, localName);
+ }
+
+ public FingerprintedQName(String prefix, String uri, String localName, int nameCode) {
+ super(prefix, uri, localName);
+ this.nameCode = nameCode;
+ }
+
+ /**
+ * Make a structuredQName from a Clark name
+ * @param expandedName the name in Clark notation "{uri}local" if in a namespace, or "local" otherwise.
+ * The format "{}local" is also accepted for a name in no namespace.
+ * @return the constructed StructuredQName
+ * @throws IllegalArgumentException if the Clark name is malformed
+ */
+
+ public static FingerprintedQName fromClarkName(String expandedName) {
+ String namespace;
+ String localName;
+ if (expandedName.charAt(0) == '{') {
+ int closeBrace = expandedName.indexOf('}');
+ if (closeBrace < 0) {
+ throw new IllegalArgumentException("No closing '}' in Clark name");
+ }
+ namespace = expandedName.substring(1, closeBrace);
+ if (closeBrace == expandedName.length()) {
+ throw new IllegalArgumentException("Missing local part in Clark name");
+ }
+ localName = expandedName.substring(closeBrace + 1);
+ } else {
+ namespace = "";
+ localName = expandedName;
+ }
+ return new FingerprintedQName("", namespace, localName);
+ }
+
+ /**
+ * Ask whether this node name representation has a known namecode and fingerprint
+ *
+ * @return true if the methods getFingerprint() and getNameCode() will
+ * return a result other than -1
+ */
+ public boolean hasFingerprint() {
+ return nameCode != -1;
+ }
+
+ /**
+ * Get the fingerprint of this name if known. This method should not to any work to allocate
+ * a fingerprint if none is already available
+ *
+ * @return the fingerprint if known; otherwise -1
+ */
+ public int getFingerprint() {
+ return (nameCode == -1 ? -1 : nameCode & NamePool.FP_MASK);
+ }
+
+ /**
+ * Get the nameCode of this name if known. This method should not to any work to allocate
+ * a nameCode if none is already available
+ *
+ * @return the fingerprint if known; otherwise -1
+ */
+ public int getNameCode() {
+ return nameCode;
+ }
+
+ /**
+ * Set the nameCode for this QName. Note that this modifies the FingerprintedQName object
+ * and makes it unusable with a different NamePool.
+ * @param nameCode the nameCode associated with this QName by the NamePool
+ */
+
+ public void setNameCode(int nameCode) {
+ this.nameCode = nameCode;
+ }
+
+ /**
+ * Allocate a nameCode from the NamePool (if none has already been allocated).
+ * Note that this modifies the FingerprintedQName object and makes it unusable with a different NamePool.
+ * @param pool the namePool
+ * @return the allocated name code (or the existing namecode if there already was one)
+ */
+
+ public int allocateNameCode(NamePool pool) {
+ if (nameCode == -1) {
+ nameCode = pool.allocate(getPrefix(), getURI(), getLocalPart());
+ }
+ return nameCode;
+ }
+
+ /*
+ * Compare two names for equality
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ if (other instanceof NodeName) {
+ if (nameCode != -1 && ((NodeName)other).hasFingerprint()) {
+ return getFingerprint() == ((NodeName)other).getFingerprint();
+ } else {
+ return getLocalPart().equals(((NodeName)other).getLocalPart()) &&
+ isInSameNamespace((NodeName)other);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isIdentical(IdentityComparable other) {
+ if(other instanceof NodeName) {
+ return this.equals(other) && this.getPrefix().equals(((NodeName)other).getPrefix());
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/sf/saxon/om/FunctionItem.java b/sf/saxon/om/FunctionItem.java
new file mode 100644
index 0000000..b2df6a5
--- /dev/null
+++ b/sf/saxon/om/FunctionItem.java
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.expr.Callable;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.FunctionItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+/**
+ * XDM 3.0 introduces a third kind of item, beyond nodes and atomic values: the function item. Function
+ * items implement this marker interface. The actual implementation class is in Saxon-PE and Saxon-EE only.
+ */
+
+public interface FunctionItem extends Item, Callable, GroundedValue {
+
+ /**
+ * Get the item type of the function item
+ * @param th the type hierarchy cache
+ * @return the function item's type
+ */
+
+ public FunctionItemType getFunctionItemType(TypeHierarchy th);
+
+ /**
+ * Get the name of the function, or null if it is anonymous
+ * @return the function name, or null for an anonymous inline function
+ */
+
+ public StructuredQName getFunctionName();
+
+ /**
+ * Get the arity of the function
+ * @return the number of arguments in the function signature
+ */
+
+ public int getArity();
+
+ /**
+ * Invoke the function
+ * @param context the XPath dynamic evaluation context
+ * @param args the actual arguments to be supplied
+ * @return the result of invoking the function
+ * @throws XPathException if a dynamic error occurs within the function
+ */
+
+ public Sequence call(XPathContext context, Sequence[] args) throws XPathException;
+
+ /**
+ * Test whether this FunctionItem is deep-equal to another function item,
+ * under the rules of the deep-equal function
+ *
+ * @param other the other function item
+ * @param context the dynamic evaluation context
+ * @param comparer the object to perform the comparison
+ * @param flags options for how the comparison is performed
+ * @return true if the two function items are deep-equal
+ * @throws XPathException if the comparison cannot be performed
+ */
+
+ public boolean deepEquals(FunctionItem other, XPathContext context, AtomicComparer comparer, int flags) throws XPathException;
+
+}
+
diff --git a/sf/saxon/om/GroundedValue.java b/sf/saxon/om/GroundedValue.java
new file mode 100644
index 0000000..8caf628
--- /dev/null
+++ b/sf/saxon/om/GroundedValue.java
@@ -0,0 +1,89 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A value that exists in memory and that can be directly addressed
+ */
+public interface GroundedValue extends Sequence {
+
+ /**
+ * Get the n'th item in the value, counting from 0
+ * @param n the index of the required item, with 0 representing the first item in the sequence
+ * @return the n'th item if it exists, or null otherwise
+ */
+
+ public Item itemAt(int n);
+
+ /**
+ * Get a subsequence of the value
+ *
+ * @param start the index of the first item to be included in the result, counting from zero.
+ * A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
+ * sequence is returned
+ * @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
+ * get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
+ * is returned. If the value goes off the end of the sequence, the result returns items up to the end
+ * of the sequence
+ * @return the required subsequence.
+ */
+
+ public GroundedValue subsequence(int start, int length);
+
+ /**
+ * Get the size of the value (the number of items)
+ * @return the number of items in the sequence
+ */
+
+ public int getLength();
+
+ /**
+ * Get the effective boolean value of this sequence
+ * @return the effective boolean value
+ * @throws XPathException if the sequence has no effective boolean value (for example a sequence of two integers)
+ */
+
+ public boolean effectiveBooleanValue() throws XPathException;
+
+ /**
+ * Get the string value of this sequence. The string value of an item is the result of applying the string()
+ * function. The string value of a sequence is the space-separated result of applying the string-join() function
+ * using a single space as the separator
+ * @return the string value of the sequence.
+ * @throws XPathException if the sequence contains items that have no string value (for example, function items)
+ */
+
+ public String getStringValue() throws XPathException;
+
+ /**
+ * Get the string value of this sequence. The string value of an item is the result of applying the string()
+ * function. The string value of a sequence is the space-separated result of applying the string-join() function
+ * using a single space as the separator
+ * @return the string value of the sequence.
+ * @throws XPathException if the sequence contains items that have no string value (for example, function items)
+ */
+
+ public CharSequence getStringValueCS() throws XPathException;
+
+ /**
+ * Reduce the sequence to its simplest form. If the value is an empty sequence, the result will be
+ * EmptySequence.getInstance(). If the value is a single atomic value, the result will be an instance
+ * of AtomicValue. If the value is a single item of any other kind, the result will be an instance
+ * of SingletonItem. Otherwise, the result will typically be unchanged.
+ * @return the simplified sequence
+ */
+
+ public GroundedValue reduce();
+
+
+
+
+}
+
diff --git a/sf/saxon/om/IdentityComparable.java b/sf/saxon/om/IdentityComparable.java
new file mode 100644
index 0000000..a1c2235
--- /dev/null
+++ b/sf/saxon/om/IdentityComparable.java
@@ -0,0 +1,18 @@
+package net.sf.saxon.om;
+
+/**
+ * The IdentityComparable class provides a way to compare class objects for equality
+ *
+ *
+ */
+public interface IdentityComparable {
+
+ /**
+ * Determine whether two IdentityComparable objects are identical. This is a stronger
+ * test than equality (even schema-equality); for example two dateTime values are not identical unless
+ * they are in the same timezone.
+ * @param other
+ * @return true if the two values are indentical, false otherwise
+ */
+ public boolean isIdentical(IdentityComparable other);
+}
diff --git a/sf/saxon/om/InscopeNamespaceResolver.java b/sf/saxon/om/InscopeNamespaceResolver.java
new file mode 100644
index 0000000..00ba6c2
--- /dev/null
+++ b/sf/saxon/om/InscopeNamespaceResolver.java
@@ -0,0 +1,119 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.util.NamespaceIterator;
+import net.sf.saxon.type.Type;
+
+import java.util.Iterator;
+
+/**
+ * A NamespaceResolver that resolves namespace prefixes by reference to a node in a document for which
+ * those namespaces are in-scope.
+ */
+public class InscopeNamespaceResolver implements NamespaceResolver {
+
+ private NodeInfo node;
+
+ /**
+ * Create a NamespaceResolver that resolves according to the in-scope namespaces
+ * of a given node
+ * @param node the given node. Must not be null.
+ * If this is an element node, the resolver uses the in-scope namespaces
+ * of that element. If it is a document node, the only in-scope namespace is the XML namespace. If it
+ * is any other kind of node, then if the node has a parent element, the resolver uses the in-scope
+ * namespaces of the parent element, otherwise ithe only in-scope namespace is the XML namespace.
+ */
+
+ public InscopeNamespaceResolver(NodeInfo node) {
+ int kind = node.getNodeKind();
+ if (kind == Type.ELEMENT || kind == Type.DOCUMENT) {
+ this.node = node;
+ } else {
+ NodeInfo parent = node.getParent();
+ this.node = (parent == null ? node : parent);
+ }
+ }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ * @param prefix the namespace prefix. May be the zero-length string, indicating
+ * that there is no prefix. This indicates either the default namespace or the
+ * null namespace, depending on the value of useDefault.
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is "". If false, the method returns "" when the prefix is "".
+ * @return the uri for the namespace, or null if the prefix is not in scope.
+ * The "null namespace" is represented by the pseudo-URI "".
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(String prefix, boolean useDefault) {
+ if (prefix.length()==0 && !useDefault) {
+ return NamespaceConstant.NULL;
+ }
+ AxisIterator iter = node.iterateAxis(AxisInfo.NAMESPACE);
+ while (true) {
+ NodeInfo node = iter.next();
+ if (node == null) {
+ break;
+ }
+ if (node.getLocalPart().equals(prefix)) {
+ return node.getStringValue();
+ }
+ }
+ return (prefix.length()==0 ? NamespaceConstant.NULL : null);
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ return new Iterator<String>() {
+ int phase = 0;
+ Iterator<NamespaceBinding> iter = NamespaceIterator.iterateNamespaces(node);
+
+ public boolean hasNext() {
+ if (iter.hasNext()) {
+ return true;
+ } else if (phase == 0) {
+ phase = 1;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public String next() {
+ if (phase == 1) {
+ phase = 2;
+ return "xml";
+ } else {
+ return iter.next().getPrefix();
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+ };
+ }
+
+ /**
+ * Get the node on which this namespace resolver is based
+ * @return the node on which this namespace resolver is based
+ */
+
+ public NodeInfo getNode() {
+ return node;
+ }
+}
+
diff --git a/sf/saxon/om/Item.java b/sf/saxon/om/Item.java
new file mode 100644
index 0000000..7d79714
--- /dev/null
+++ b/sf/saxon/om/Item.java
@@ -0,0 +1,74 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.evpull.PullEvent;
+
+/**
+ * An Item is an object that can occur as a member of a sequence.
+ * It corresponds directly to the concept of an item in the XPath 2.0 data model.
+ * There are two kinds of Item: atomic values, and nodes.
+ * <p>
+ * This interface is part of the public Saxon API. As such (starting from Saxon 8.4),
+ * methods that form part of the stable API are labelled with a JavaDoc "since" tag
+ * to identify the Saxon release at which they were introduced.
+ * <p>
+ * Note: there is no method getItemType(). This is to avoid having to implement it
+ * on every implementation of NodeInfo. Instead, use the static method Type.getItemType(Item).
+ *
+ * @author Michael H. Kay
+ * @since 8.4
+ */
+
+public interface Item extends Sequence, PullEvent {
+
+ /**
+ * Get the value of the item as a string. For nodes, this is the string value of the
+ * node as defined in the XPath 2.0 data model, except that all nodes are treated as being
+ * untyped: it is not an error to get the string value of a node with a complex type.
+ * For atomic values, the method returns the result of casting the atomic value to a string.
+ * <p>
+ * If the calling code can handle any CharSequence, the method {@link #getStringValueCS} should
+ * be used. If the caller requires a string, this method is preferred.
+ *
+ * @return the string value of the item
+ * @throws UnsupportedOperationException if the item is a function item (an unchecked exception
+ * is used here to avoid introducing exception handling to a large number of paths where it is not
+ * needed)
+ * @see #getStringValueCS
+ * @since 8.4
+ */
+
+ public String getStringValue();
+
+ /**
+ * Get the string value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String. The method satisfies the rule that
+ * <code>X.getStringValueCS().toString()</code> returns a string that is equal to
+ * <code>X.getStringValue()</code>.
+ * <p>
+ * Note that two CharSequence values of different types should not be compared using equals(), and
+ * for the same reason they should not be used as a key in a hash table.
+ * <p>
+ * If the calling code can handle any CharSequence, this method should
+ * be used. If the caller requires a string, the {@link #getStringValue} method is preferred.
+ *
+ * @return the string value of the item
+ * @throws UnsupportedOperationException if the item is a function item (an unchecked exception
+ * is used here to avoid introducing exception handling to a large number of paths where it is not
+ * needed)
+ * @see #getStringValue
+ * @since 8.4
+ */
+
+ public CharSequence getStringValueCS();
+
+
+}
+
+
diff --git a/sf/saxon/om/LazySequence.java b/sf/saxon/om/LazySequence.java
new file mode 100644
index 0000000..bcb7935
--- /dev/null
+++ b/sf/saxon/om/LazySequence.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A sequence that wraps an iterator, without being materialized. If used more than once, the input
+ * iterator is re-evaluated (which can cause problem if the client is dependent on node identity).
+ */
+public class LazySequence implements Sequence {
+
+ SequenceIterator iterator;
+ boolean used = false;
+
+ public LazySequence(SequenceIterator iterator) {
+ this.iterator = iterator;
+ }
+
+ public Item head() throws XPathException {
+ return iterate().next();
+ }
+
+ public synchronized SequenceIterator iterate() throws XPathException {
+ if (used) {
+ return iterator.getAnother();
+ } else {
+ used = true;
+ return iterator;
+ }
+ }
+}
+
diff --git a/sf/saxon/om/MemoSequence.java b/sf/saxon/om/MemoSequence.java
new file mode 100644
index 0000000..7c14344
--- /dev/null
+++ b/sf/saxon/om/MemoSequence.java
@@ -0,0 +1,262 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ArrayIterator;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.SequenceExtent;
+
+/**
+ * A Sequence implementation that represents a lazy evaluation of a supplied iterator
+ */
+
+public class MemoSequence implements Sequence {
+
+ SequenceIterator inputIterator;
+ //boolean used = false;
+
+ /*@Nullable*/ private Item[] reservoir = null;
+ private int used;
+ protected int state;
+
+ // State in which no items have yet been read
+ private static final int UNREAD = 0;
+
+ // State in which zero or more items are in the reservoir and it is not known
+ // whether more items exist
+ private static final int MAYBE_MORE = 1;
+
+ // State in which all the items are in the reservoir
+ private static final int ALL_READ = 3;
+
+ // State in which we are getting the base iterator. If the closure is called in this state,
+ // it indicates a recursive entry, which is only possible on an error path
+ private static final int BUSY = 4;
+
+ // State in which we know that the value is an empty sequence
+ protected static final int EMPTY = 5;
+
+
+ public MemoSequence(SequenceIterator iterator) {
+ this.inputIterator = iterator;
+ }
+
+ public Item head() throws XPathException {
+ return iterate().next();
+ }
+
+
+ public synchronized SequenceIterator iterate() throws XPathException {
+
+ switch (state) {
+ case UNREAD:
+ state = BUSY;
+ if (inputIterator instanceof EmptyIterator) {
+ state = EMPTY;
+ return inputIterator;
+ }
+ reservoir = new Item[50];
+ used = 0;
+ state = MAYBE_MORE;
+ return new ProgressiveIterator();
+
+ case MAYBE_MORE:
+ return new ProgressiveIterator();
+
+ case ALL_READ:
+ switch (used) {
+ case 0:
+ state = EMPTY;
+ return EmptyIterator.emptyIterator();
+ case 1:
+ assert reservoir != null;
+ return SingletonIterator.makeIterator(reservoir[0]);
+ default:
+ return new ArrayIterator<Item>(reservoir, 0, used);
+ }
+
+ case BUSY:
+ // recursive entry: can happen if there is a circularity involving variable and function definitions
+ // Can also happen if variable evaluation is attempted in a debugger, hence the cautious message
+ XPathException de = new XPathException("Attempt to access a variable while it is being evaluated");
+ de.setErrorCode("XTDE0640");
+ //de.setXPathContext(context);
+ throw de;
+
+ case EMPTY:
+ return EmptyIterator.emptyIterator();
+
+ default:
+ throw new IllegalStateException("Unknown iterator state");
+
+ }
+ }
+
+ /**
+ * Append an item to the reservoir
+ * @param item the item to be added
+ */
+
+ private void append(Item item) {
+ assert reservoir != null;
+ if (used >= reservoir.length) {
+ Item[] r2 = new Item[used*2];
+ System.arraycopy(reservoir, 0, r2, 0, used);
+ reservoir = r2;
+ }
+ reservoir[used++] = item;
+ }
+
+ /**
+ * Release unused space in the reservoir (provided the amount of unused space is worth reclaiming)
+ */
+
+ private void condense() {
+ if (reservoir != null && reservoir.length - used > 30) {
+ Item[] r2 = new Item[used];
+ System.arraycopy(reservoir, 0, r2, 0, used);
+ reservoir = r2;
+ }
+ }
+
+
+ /**
+ * A ProgressiveIterator starts by reading any items already held in the reservoir;
+ * when the reservoir is exhausted, it reads further items from the inputIterator,
+ * copying them into the reservoir as they are read.
+ */
+
+ public final class ProgressiveIterator implements SequenceIterator, LastPositionFinder, GroundedIterator {
+
+ int position = -1; // zero-based position in the reservoir of the
+ // item most recently read
+
+ /**
+ * Create a ProgressiveIterator
+ */
+
+ public ProgressiveIterator() {
+ }
+
+ /*@Nullable*/ public Item next() throws XPathException {
+ synchronized (MemoSequence.this) {
+ // synchronized for the case where a multi-threaded xsl:for-each is reading the variable
+ if (position == -2) { // means we've already returned null once, keep doing so if called again.
+ return null;
+ }
+ if (++position < used) {
+ assert reservoir != null;
+ return reservoir[position];
+ } else if (state == ALL_READ) {
+ // someone else has read the input to completion in the meantime
+ position = -2;
+ return null;
+ } else {
+ assert inputIterator != null;
+ Item i = inputIterator.next();
+ if (i == null) {
+ state = ALL_READ;
+ condense();
+ position = -2;
+ return null;
+ }
+ position = used;
+ append(i);
+ state = MAYBE_MORE;
+ return i;
+ }
+ }
+ }
+
+ /*@Nullable*/ public Item current() {
+ if (position < 0) {
+ return null;
+ }
+ assert reservoir != null;
+ return reservoir[position];
+ }
+
+ public int position() {
+ return position + 1; // return one-based position
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/ public ProgressiveIterator getAnother() {
+ return new ProgressiveIterator();
+ }
+
+ /**
+ * Get the last position (that is, the number of items in the sequence)
+ */
+
+ public int getLength() throws XPathException {
+ if (state == ALL_READ) {
+ return used;
+ } else if (state == EMPTY) {
+ return 0;
+ } else {
+ // save the current position
+ int savePos = position;
+ // fill the reservoir
+ while (true) {
+ Item item = next();
+ if (item == null) {
+ break;
+ }
+ }
+ // reset the current position
+ position = savePos;
+ // return the total number of items
+ return used;
+ }
+ }
+
+ /**
+ * Return a value containing all the items in the sequence returned by this
+ * SequenceIterator
+ *
+ * @return the corresponding value
+ */
+
+ /*@Nullable*/ public GroundedValue materialize() {
+ if (state == ALL_READ) {
+ assert reservoir != null;
+ return new SequenceExtent<Item>(reservoir);
+ } else if (state == EMPTY) {
+ return EmptySequence.getInstance();
+ } else {
+ throw new IllegalStateException("Progressive iterator is not grounded until all items are read");
+ }
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED} and {@link #LAST_POSITION_FINDER}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ */
+
+ public int getProperties() {
+ if (state == EMPTY || state == ALL_READ) {
+ return GROUNDED | LAST_POSITION_FINDER;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/om/MutableDocumentInfo.java b/sf/saxon/om/MutableDocumentInfo.java
new file mode 100644
index 0000000..0063451
--- /dev/null
+++ b/sf/saxon/om/MutableDocumentInfo.java
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * Interface representing the document node of a mutable document
+ */
+public interface MutableDocumentInfo {
+
+ /**
+ * This method is called before performing a batch of updates; it allows all cached data that
+ * might be invalidated by such updates to be cleared
+ */
+
+ public void resetIndexes();
+}
+
diff --git a/sf/saxon/om/MutableNodeInfo.java b/sf/saxon/om/MutableNodeInfo.java
new file mode 100644
index 0000000..f214b63
--- /dev/null
+++ b/sf/saxon/om/MutableNodeInfo.java
@@ -0,0 +1,244 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.type.SimpleType;
+
+/**
+ * An extension of the NodeInfo interface defining a node that can be updated. The updating methods are
+ * closely modelled on the updating primitives defined in the XQuery Update specification, however, their
+ * semantics should not be assumed to be identical to those primitives.
+ *
+ * <p>These primitives may leave the tree in a state where the type annotations of nodes do not correctly
+ * describe the structure of the nodes. It is the caller's responsibility to maintain the integrity of
+ * type annotations.</p>
+ *
+ * <p>This interface was introduced in Saxon 9.1 and modified in Saxon 9.2. It is not yet considered
+ * 100% stable. Some aspects of the semantics are not clearly specified, for example the effect of each
+ * operations on namespace bindings, and such details may change in the future. The primary purpose
+ * of this interface is as an internal interface for use by XQuery Update; applications use it (or
+ * implement it) at their own risk.</p>
+ */
+
+public interface MutableNodeInfo extends NodeInfo {
+
+ /**
+ * Set the type annotation on a node. This must only be called when the caller has verified (by validation)
+ * that the node is a valid instance of the specified type. The call is ignored if the node is not an element
+ * or attribute node.
+ * @param typeCode the type annotation (possibly including high bits set to indicate the isID, isIDREF, and
+ * isNilled properties)
+ */
+
+ public void setTypeAnnotation(int typeCode);
+
+ /**
+ * Insert a sequence of nodes as children of this node.
+ *
+ * <p>This method takes no action unless the target node is a document node or element node. It also
+ * takes no action in respect of any supplied nodes that are not elements, text nodes, comments, or
+ * processing instructions.</p>
+ *
+ * <p>The supplied nodes will form the new children. Adjacent text nodes will be merged, and
+ * zero-length text nodes removed. The supplied nodes may be modified in situ, for example to change their
+ * parent property and to add namespace bindings, or they may be copied, at the discretion of
+ * the implementation.</p>
+ *
+ * @param source the nodes to be inserted. The implementation determines what implementation classes
+ * of node it will accept; all implementations must accept nodes constructed using the Builder supplied
+ * by the {@link #newBuilder} method on this object. The supplied nodes may be modified in situ, for example
+ * to change their parent property and to add namespace bindings, but this depends on the implementation.
+ * The argument array may be modified as a result of the call.
+ * @param atStart true if the new nodes are to be inserted before existing children; false if they are
+ * to be inserted after existing children
+ * @param inherit true if the inserted nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ * @throws IllegalArgumentException if the supplied nodes use a node implementation that this
+ * implementation does not accept.
+ */
+
+ public void insertChildren(NodeInfo[] source, boolean atStart, boolean inherit);
+
+ /**
+ * Insert a sequence of nodes as siblings of this node.
+ *
+ * <p>This method takes no action unless the target node is an element, text node, comment, or
+ * processing instruction, and one that has a parent node. It also
+ * takes no action in respect of any supplied nodes that are not elements, text nodes, comments, or
+ * processing instructions.</p>
+ *
+ * <p>The supplied nodes will form new children of this node. Adjacent text nodes will be merged, and
+ * zero-length text nodes removed. The supplied nodes may be modified in situ, for example to change their
+ * parent property and to add namespace bindings, or they may be copied, at the discretion of
+ * the implementation.</p>
+ *
+ * @param source the nodes to be inserted. The implementation determines what implementation classes
+ * of node it will accept; all implementations must accept nodes constructed using the Builder supplied
+ * by the {@link #newBuilder} method on this object. The supplied nodes may be modified in situ, for example
+ * to change their parent property and to add namespace bindings, but this depends on the implementation.
+ * The argument array may be modified as a result of the call.
+ * @param before true if the new nodes are to be inserted before the target node; false if they are
+ * to be inserted after the target node
+ * @param inherit true if the inserted nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ * @throws IllegalArgumentException if any supplied node is not a text node, element, comment, or
+ * processing instruction or if any of the supplied nodes uses a node implementation that this
+ * implementation does not accept.
+ */
+
+ public void insertSiblings(NodeInfo[] source, boolean before, boolean inherit);
+
+ /**
+ * Remove an attribute from this element node
+ *
+ * <p>If the target node is not an element, or if the supplied node is not an attribute
+ * of this element, this method takes no action.</p>
+ *
+ * <p>The attribute object itself becomes unusable; any attempt to use this attribute object,
+ * or any other object representing the same node, is likely to result in an exception.
+ * Calling the {@link #isDeleted} method on the attribute object will return true.</p>
+ *
+ * @param attribute the attribute node to be removed
+ */
+
+ public void removeAttribute(NodeInfo attribute);
+
+ /**
+ * Add an attribute to this element node.
+ *
+ * <p>If this node is not an element, the method takes no action.
+ * If the element already has an attribute with this name, the method
+ * throws an exception.</p>
+ *
+ * <p>This method does not perform any namespace fixup. It is the caller's responsibility
+ * to ensure that any namespace prefix used in the name of the attribute (or in its value
+ * if it has a namespace-sensitive type) is declared on this element.</p>
+ *
+ *
+ *
+ * @param name the name of the new attribute
+ * @param attType the type annotation of the new attribute
+ * @param value the string value of the new attribute
+ * @param properties properties including IS_ID and IS_IDREF properties
+ * @throws IllegalStateException if the element already has an attribute with the given name.
+ */
+
+ public void addAttribute(NodeName name, SimpleType attType, CharSequence value, int properties);
+
+ /**
+ * Delete this node (that is, detach it from its parent).
+ *
+ * <p>Following this call, calling {@link #isDeleted} on the target node or on any of its
+ * descendants (or their attributes) will return true, and the node will in all other respects
+ * be unusable. The effect of other operations on the node is undefined.</p>
+ *
+ * <p>If the deleted node has preceding and following siblings that are both text nodes,
+ * the two text nodes will be joined into a single text node (the identity of this node
+ * with respect to the original text nodes is undefined).</p>
+ */
+
+ public void delete();
+
+ /**
+ * Test whether this MutableNodeInfo object represents a node that has been deleted.
+ * Generally, such a node is unusable, and any attempt to use it will result in an exception
+ * being thrown.
+ *
+ * @return true if this node has been deleted
+ */
+
+ public boolean isDeleted();
+
+ /**
+ * Replace this node with a given sequence of nodes. This node is effectively deleted, and the replacement
+ * nodes are attached to the parent of this node in its place.
+ *
+ * <p>The supplied nodes will become children of this node's parent. Adjacent text nodes will be merged, and
+ * zero-length text nodes removed. The supplied nodes may be modified in situ, for example to change their
+ * parent property and to add namespace bindings, or they may be copied, at the discretion of
+ * the implementation.</p>
+ *
+ * @param replacement the replacement nodes. If this node is an attribute, the replacements
+ * must also be attributes; if this node is not an attribute, the replacements must not be attributes.
+ * The implementation determines what implementation classes
+ * of node it will accept; all implementations must accept nodes constructed using the Builder supplied
+ * by the {@link #newBuilder} method on this object. The supplied nodes may be modified in situ, for example
+ * to change their parent property and to add namespace bindings, but this depends on the implementation.
+ * The argument array may be modified as a result of the call.
+ * @param inherit true if the replacement nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ * @throws IllegalArgumentException if any of the replacement nodes is of the wrong kind. When replacing
+ * a child node, the replacement nodes must all be elements, text, comment, or PI nodes; when replacing
+ * an attribute, the replacement nodes must all be attributes.
+ * @throws IllegalStateException if this node is deleted or if it has no parent node.
+ * @throws IllegalStateException if two replacement attributes have the same name.
+ */
+
+ public void replace(NodeInfo[] replacement, boolean inherit);
+
+ /**
+ * Replace the string-value of this node. If applied to an element or document node, this
+ * causes all existing children to be deleted, and replaced with a new text node
+ * whose string value is the value supplied. The caller is responsible for checking
+ * that the value is valid, for example that comments do not contain a double hyphen; the
+ * implementation is not required to check for such conditions.
+ * @param stringValue the new string value
+ */
+
+ public void replaceStringValue(CharSequence stringValue);
+
+ /**
+ * Rename this node.
+ * <p>This call has no effect if applied to a nameless node, such as a text node or comment.</p>
+ * <p>If necessary, a new namespace binding will be added to the target element, or to the element
+ * parent of the target attribute</p>
+ *
+ * @param newName the new name for the node
+ * @throws IllegalArgumentException if the new name code is not present in the name pool, or if
+ * it has a (prefix, uri) pair in which the
+ * prefix is the same as that of an existing in-scope namespace binding and the uri is different from that
+ * namespace binding.
+ */
+
+ public void rename(NodeName newName);
+
+ /**
+ * Add a namespace binding (that is, a namespace node) to this element. This call has no effect if applied
+ * to a node other than an element.
+ * @param nscode The namespace code representing the (prefix, uri) pair of the namespace binding to be
+ * added. If the target element already has a namespace binding with this (prefix, uri) pair, the call has
+ * no effect. If the target element currently has a namespace binding with this prefix and a different URI, an
+ * exception is raised.
+ * @param inherit If true, the new namespace binding will be inherited by any children of the target element
+ * that do not already have a namespace binding for the specified prefix, recursively.
+ * If false, the new namespace binding will not be inherited.
+ * @throws IllegalArgumentException if the namespace code is not present in the namepool, or if the target
+ * element already has a namespace binding for this prefix
+ */
+
+ public void addNamespace(NamespaceBinding nscode, boolean inherit);
+
+ /**
+ * Remove type information from this node (and its ancestors, recursively).
+ * This method implements the upd:removeType() primitive defined in the XQuery Update specification.
+ * (Note: the caller is responsible for updating the set of nodes marked for revalidation)
+ */
+
+ public void removeTypeAnnotation();
+
+ /**
+ * Get a Builder suitable for building nodes that can be attached to this document.
+ * @return a new Builder that constructs nodes using the same object model implementation
+ * as this one, suitable for attachment to this tree
+ */
+
+ public Builder newBuilder();
+
+}
+
diff --git a/sf/saxon/om/Name10Checker.java b/sf/saxon/om/Name10Checker.java
new file mode 100644
index 0000000..36576ab
--- /dev/null
+++ b/sf/saxon/om/Name10Checker.java
@@ -0,0 +1,92 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.serialize.charcode.XMLCharacterData;
+
+/**
+ * The class checks names and characters
+ * against the rules of the XML 1.0 and XML Namespaces 1.0 specification
+ */
+
+public final class Name10Checker extends NameChecker {
+
+ public static final Name10Checker theInstance = new Name10Checker();
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular instance of this class
+ */
+
+ public static Name10Checker getInstance() {
+ return theInstance;
+ }
+
+ /**
+ * Validate whether a given string constitutes a valid NCName, as defined in XML Namespaces.
+ *
+ * @param name the name to be tested
+ * @return true if the name is a lexically-valid NCName
+ */
+
+// public boolean isValidNCName(CharSequence name) {
+// return XMLChar.isValidNCName(name);
+// }
+
+ /**
+ * Test whether a character is a valid XML character
+ *
+ * @param ch the character to be tested
+ * @return true if this is a valid character in the selected version of XML
+ */
+
+ public boolean isValidChar(int ch) {
+ //return XMLChar.isValid(ch);
+ return XMLCharacterData.isValid10(ch);
+ }
+
+
+ /**
+ * Test whether a character can appear in an NCName
+ *
+ * @param ch the character to be tested
+ * @return true if this is a valid character in an NCName. The rules for XML 1.0 fifth
+ * edition are the same as the XML 1.1 rules, and these are the rules that we use.
+ */
+
+ public boolean isNCNameChar(int ch) {
+ return XMLCharacterData.isNCName11(ch);
+ }
+
+ /**
+ * Test whether a character can appear at the start of an NCName
+ *
+ * @param ch the character to be tested
+ * @return true if this is a valid character at the start of an NCName. The rules for XML 1.0 fifth
+ * edition are the same as the XML 1.1 rules, and these are the rules that we use.
+ */
+
+ public boolean isNCNameStartChar(int ch) {
+ return XMLCharacterData.isNCNameStart11(ch);
+ }
+
+ /**
+ * Return the XML version supported by this NameChecker
+ *
+ * @return "1.0" as a string
+ */
+
+ /*@NotNull*/ public String getXMLVersion() {
+ return "1.0";
+ }
+
+ public static void main(String[] args) {
+ System.err.println(new Name10Checker().isValidNCName("a:b"));
+ }
+}
+
diff --git a/sf/saxon/om/Name11Checker.java b/sf/saxon/om/Name11Checker.java
new file mode 100644
index 0000000..f9f2730
--- /dev/null
+++ b/sf/saxon/om/Name11Checker.java
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.serialize.charcode.XMLCharacterData;
+
+/**
+ * The class checks names against the rules of the XML 1.1 and XML Namespaces 1.1 specification
+ */
+
+public final class Name11Checker extends NameChecker {
+
+ public static final Name11Checker theInstance = new Name11Checker();
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular instance of this class
+ */
+
+ public static Name11Checker getInstance() {
+ return theInstance;
+ }
+
+ /**
+ * Test whether a character is a valid XML character
+ *
+ * @param ch the character to be tested
+ * @return true if this is a valid character in the selected version of XML
+ */
+
+ public boolean isValidChar(int ch) {
+ //return XMLChar.isValid(ch);
+ return XMLCharacterData.isValid11(ch);
+ }
+
+
+ /**
+ * Test whether a character can appear in an NCName
+ *
+ * @param ch the character to be tested
+ * @return true if this is a valid character in an NCName the selected version of XML
+ */
+
+ public boolean isNCNameChar(int ch) {
+ return XMLCharacterData.isNCName11(ch);
+ }
+
+ /**
+ * Test whether a character can appear at the start of an NCName
+ *
+ * @param ch the character to be tested
+ * @return true if this is a valid character at the start of an NCName the selected version of XML
+ */
+
+ public boolean isNCNameStartChar(int ch) {
+ return XMLCharacterData.isNCNameStart11(ch);
+ }
+
+
+ /**
+ * Return the XML version supported by this NameChecker
+ *
+ * @return "1.1" as a string
+ */
+
+ /*@NotNull*/ public String getXMLVersion() {
+ return "1.1";
+ }
+}
+
diff --git a/sf/saxon/om/NameChecker.java b/sf/saxon/om/NameChecker.java
new file mode 100644
index 0000000..48989bf
--- /dev/null
+++ b/sf/saxon/om/NameChecker.java
@@ -0,0 +1,258 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Serializable;
+
+/**
+ * A NameChecker performs validation and analysis of XML names. There are two implementations
+ * of this interface, one for XML 1.0 names and one for XML 1.1 names. The class also handles
+ * validation of characters against the XML 1.0 or XML 1.1 rules.
+ */
+
+public abstract class NameChecker implements Serializable {
+
+ /**
+ * Validate whether a given string constitutes a valid QName, as defined in XML Namespaces.
+ * Note that this does not test whether the prefix is actually declared.
+ *
+ * @param name the name to be tested
+ * @return true if the name is a lexically-valid QName
+ */
+
+ public final boolean isQName(String name) {
+ int colon = name.indexOf(':');
+ if (colon < 0) {
+ return isValidNCName(name);
+ }
+ return colon != 0 &&
+ colon != name.length() - 1 &&
+ isValidNCName(name.substring(0, colon)) &&
+ isValidNCName(name.substring(colon + 1));
+ }
+
+ /**
+ * Extract the prefix from a QName. Note, the QName is assumed to be valid.
+ *
+ * @param qname The lexical QName whose prefix is required
+ * @return the prefix, that is the part before the colon. Returns an empty
+ * string if there is no prefix
+ */
+
+ public static String getPrefix(String qname) {
+ int colon = qname.indexOf(':');
+ if (colon < 0) {
+ return "";
+ }
+ return qname.substring(0, colon);
+ }
+
+ /**
+ * Validate a QName, and return the prefix and local name. The local name is checked
+ * to ensure it is a valid NCName. The prefix is not checked, on the theory that the caller
+ * will look up the prefix to find a URI, and if the prefix is invalid, then no URI will
+ * be found.
+ *
+ * @param qname the lexical QName whose parts are required. Note that leading and trailing
+ * whitespace is not permitted
+ * @return an array of two strings, the prefix and the local name. The first
+ * item is a zero-length string if there is no prefix.
+ * @throws QNameException if not a valid QName.
+ */
+
+ public final String[] getQNameParts(CharSequence qname) throws QNameException {
+ String[] parts = new String[2];
+ int colon = -1;
+ int len = qname.length();
+ for (int i = 0; i < len; i++) {
+ if (qname.charAt(i) == ':') {
+ colon = i;
+ break;
+ }
+ }
+ if (colon < 0) {
+ parts[0] = "";
+ parts[1] = qname.toString();
+ if (!isValidNCName(parts[1])) {
+ throw new QNameException("Invalid QName " + Err.wrap(qname));
+ }
+ } else {
+ if (colon == 0) {
+ throw new QNameException("QName cannot start with colon: " + Err.wrap(qname));
+ }
+ if (colon == len - 1) {
+ throw new QNameException("QName cannot end with colon: " + Err.wrap(qname));
+ }
+ parts[0] = qname.subSequence(0, colon).toString();
+ parts[1] = qname.subSequence(colon + 1, len).toString();
+
+ if (!isValidNCName(parts[1])) {
+ if (!isValidNCName(parts[0])) {
+ throw new QNameException("Both the prefix " + Err.wrap(parts[0]) +
+ " and the local part " + Err.wrap(parts[1]) + " are invalid");
+ }
+ throw new QNameException("Invalid QName local part " + Err.wrap(parts[1]));
+ }
+ }
+ return parts;
+ }
+
+ /**
+ * Validate a QName, and return the prefix and local name. Both parts are checked
+ * to ensure they are valid NCNames.
+ * <p/>
+ * <p><i>Used from compiled code</i></p>
+ *
+ * @param qname the lexical QName whose parts are required. Note that leading and trailing
+ * whitespace is not permitted
+ * @return an array of two strings, the prefix and the local name. The first
+ * item is a zero-length string if there is no prefix.
+ * @throws XPathException if not a valid QName.
+ */
+
+ /*@NotNull*/ public final String[] checkQNameParts(CharSequence qname) throws XPathException {
+ try {
+ String[] parts = getQNameParts(qname);
+ if (parts[0].length() > 0 && !isValidNCName(parts[0])) {
+ throw new XPathException("Invalid QName prefix " + Err.wrap(parts[0]));
+ }
+ return parts;
+ } catch (QNameException e) {
+ XPathException err = new XPathException(e.getMessage());
+ err.setErrorCode("FORG0001");
+ throw err;
+ }
+ }
+
+ /**
+ * Validate whether a given string constitutes a valid NCName, as defined in XML Namespaces.
+ *
+ * @param ncName the name to be tested. Any whitespace trimming must have already been applied.
+ * @return true if the name is a lexically-valid QName
+ */
+
+ public final boolean isValidNCName(CharSequence ncName) {
+ if (ncName.length() == 0) {
+ return false;
+ }
+ int s = 1;
+ char ch = ncName.charAt(0);
+ if (UTF16CharacterSet.isHighSurrogate(ch)) {
+ if (!isNCNameStartChar(UTF16CharacterSet.combinePair(ch, ncName.charAt(1)))) {
+ return false;
+ }
+ s = 2;
+ } else {
+ if (!isNCNameStartChar(ch)) {
+ return false;
+ }
+ }
+ for (int i = s; i < ncName.length(); i++) {
+ ch = ncName.charAt(i);
+ if (UTF16CharacterSet.isHighSurrogate(ch)) {
+ if (!isNCNameChar(UTF16CharacterSet.combinePair(ch, ncName.charAt(++i)))) {
+ return false;
+ }
+ } else {
+ if (!isNCNameChar(ch)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check to see if a string is a valid Nmtoken according to [7]
+ * in the XML 1.0 Recommendation
+ *
+ * @param nmtoken the string to be tested. Any whitespace trimming must have already been applied.
+ * @return true if nmtoken is a valid Nmtoken
+ */
+
+ public final boolean isValidNmtoken(CharSequence nmtoken) {
+ if (nmtoken.length() == 0) {
+ return false;
+ }
+ for (int i = 0; i < nmtoken.length(); i++) {
+ char ch = nmtoken.charAt(i);
+ if (UTF16CharacterSet.isHighSurrogate(ch)) {
+ if (!isNCNameChar(UTF16CharacterSet.combinePair(ch, nmtoken.charAt(++i)))) {
+ return false;
+ }
+ } else {
+ if (ch != ':' && !isNCNameChar(ch)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+
+ /**
+ * Test whether a character is a valid XML character
+ *
+ * @param ch the character to be tested
+ * @return true if this is a valid character in the selected version of XML
+ */
+
+ public abstract boolean isValidChar(int ch);
+
+ /**
+ * Test whether all the characters in a CharSequence are valid XML characters
+ * @param chars the character sequence to be tested
+ * @return the codepoint of the first invalid character in the character sequence (according to the selected version of XML);
+ * or -1 if all characters in the character sequence are valid
+ */
+
+ public int firstInvalidChar(CharSequence chars) {
+ for (int c=0; c < chars.length(); c++) {
+ int ch32 = chars.charAt(c);
+ if (UTF16CharacterSet.isHighSurrogate(ch32)) {
+ char low = chars.charAt(c++);
+ ch32 = UTF16CharacterSet.combinePair((char)ch32, low);
+ }
+ if (!isValidChar(ch32)) {
+ return ch32;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Test whether a character can appear in an NCName
+ *
+ * @param ch the character to be tested
+ * @return true if this is a valid character in an NCName the selected version of XML
+ */
+
+ public abstract boolean isNCNameChar(int ch);
+
+ /**
+ * Test whether a character can appear at the start of an NCName
+ *
+ * @param ch the character to be tested
+ * @return true if this is a valid character at the start of an NCName the selected version of XML
+ */
+
+ public abstract boolean isNCNameStartChar(int ch);
+
+ /**
+ * Return the XML version supported by this NameChecker
+ *
+ * @return "1.0" or "1.1" as a string
+ */
+
+ public abstract String getXMLVersion();
+}
+
diff --git a/sf/saxon/om/NameOfNode.java b/sf/saxon/om/NameOfNode.java
new file mode 100644
index 0000000..bb855fb
--- /dev/null
+++ b/sf/saxon/om/NameOfNode.java
@@ -0,0 +1,176 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * An implementation of NodeName that gets the name of an existing NodeInfo object. Useful when nodes are copied.
+ */
+public class NameOfNode implements NodeName {
+
+ private NodeInfo node;
+
+ public NameOfNode(NodeInfo node) {
+ this.node = node;
+ }
+
+ /**
+ * Get the prefix of the QName.
+ *
+ * @return the prefix. Returns the empty string if the name is unprefixed.
+ */
+ public String getPrefix() {
+ return node.getPrefix();
+ }
+
+ /**
+ * Get the namespace URI of the QName.
+ *
+ * @return the URI. Returns the empty string to represent the no-namespace
+ */
+ public String getURI() {
+ return node.getURI();
+ }
+
+ /**
+ * Get the local part of the QName
+ *
+ * @return the local part of the QName
+ */
+ public String getLocalPart() {
+ return node.getLocalPart();
+ }
+
+ /**
+ * Get the display name, that is the lexical QName in the form [prefix:]local-part
+ *
+ * @return the lexical QName
+ */
+ public String getDisplayName() {
+ return node.getDisplayName();
+ }
+
+ /**
+ * Get the name in the form of a StructuredQName
+ *
+ * @return the name in the form of a StructuredQName
+ */
+ public StructuredQName getStructuredQName() {
+ return new StructuredQName(getPrefix(), getURI(), getLocalPart());
+ }
+
+ /**
+ * Test whether this name is in the same namespace as another name
+ *
+ * @return true if the two names are in the same namespace
+ */
+ public boolean isInSameNamespace(/*@NotNull*/ NodeName other) {
+ return node.getURI().equals(other.getURI());
+ }
+
+ /**
+ * Test whether this name is in a given namespace
+ *
+ * @param ns the namespace to be tested against
+ * @return true if the name is in the specified namespace
+ */
+ public boolean isInNamespace(String ns) {
+ return node.getURI().equals(ns);
+ }
+
+ /**
+ * Get a {@link net.sf.saxon.om.NamespaceBinding} whose (prefix, uri) pair are the prefix and URI of this
+ * node name
+ *
+ * @return the corresponding NamespaceBinding
+ */
+
+ public NamespaceBinding getNamespaceBinding() {
+ return NamespaceBinding.makeNamespaceBinding(getPrefix(), getURI());
+ }
+
+ /**
+ * Ask whether this node name representation has a known namecode and fingerprint
+ *
+ * @return true if the methods getFingerprint() and getNameCode() will
+ * return a result other than -1
+ */
+ public boolean hasFingerprint() {
+ return node instanceof FingerprintedNode;
+ }
+
+ /**
+ * Get the fingerprint of this name if known. This method should not to any work to allocate
+ * a fingerprint if none is already available
+ *
+ * @return the fingerprint if known; otherwise -1
+ */
+ public int getFingerprint() {
+ if (hasFingerprint()) {
+ return node.getFingerprint();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Get the nameCode of this name if known. This method should not to any work to allocate
+ * a nameCode if none is already available
+ *
+ * @return the nameCode if known; otherwise -1
+ */
+ public int getNameCode() {
+ if (hasFingerprint()) {
+ return node.getNameCode();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Get the nameCode of this name, allocating a new code from the namepool if necessary
+ *
+ * @param namePool the NamePool used to allocate the name
+ * @return a nameCode for this name, newly allocated if necessary
+ */
+ public int allocateNameCode(NamePool namePool) {
+ return node.getNameCode();
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+ @Override
+ public int hashCode() {
+ return StructuredQName.computeHashCode(getURI(), getLocalPart());
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof NodeName) {
+ NodeName n = (NodeName)obj;
+ if (node instanceof FingerprintedNode && n.hasFingerprint()) {
+ return node.getFingerprint() == n.getFingerprint();
+ } else {
+ return n.getLocalPart().equals(node.getLocalPart()) && n.isInNamespace(node.getURI());
+ }
+ } else {
+ return false;
+ }
+ }
+
+ public boolean isIdentical(IdentityComparable other) {
+ if(other instanceof NodeName) {
+ return this.equals(other) && this.getPrefix().equals(((NodeName)other).getPrefix());
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/sf/saxon/om/NamePool.java b/sf/saxon/om/NamePool.java
new file mode 100644
index 0000000..7821087
--- /dev/null
+++ b/sf/saxon/om/NamePool.java
@@ -0,0 +1,893 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.Whitespace;
+
+import java.io.Serializable;
+import java.util.HashMap;
+
+/**
+ * A NamePool holds a collection of expanded names, each containing a namespace URI,
+ * a namespace prefix, and a local name.
+ *
+ * <p>Each expanded name is allocated a unique integer namecode. The namecode enables
+ * all three parts of the expanded name to be determined, that is, the prefix, the
+ * URI, and the local name.</p>
+ *
+ * <p>The equivalence betweem names depends only on the URI and the local name.
+ * The namecode is designed so that if two namecodes represent names with the same
+ * URI and local name, the two namecodes are the same in the bottom 20 bits. It is
+ * therefore possible to compare two names for equivalence by performing an integer
+ * comparison of these 20 bits. The bottom 20 bits of a namecode are referred to as
+ * a fingerprint.</p>
+ *
+ * <p>The NamePool eliminates duplicate names if they have the same prefix, uri,
+ * and local part. It retains duplicates if they have different prefixes</p>
+ *
+ * <p>Internally the NamePool is organized as a fixed number of hash chains. The selection
+ * of a hash chain is based on hashing the local name, because it is unusual to have many
+ * names that share the same local name but use different URIs. There are 1024 hash chains
+ * and the identifier of the hash chain forms the bottom ten bits of the namecode. The
+ * next ten bits represent the sequential position of an entry within the hash chain. The
+ * upper bits represent the selection of prefix, from among the list of prefixes that have
+ * been used with a given URI. A prefix part of zero means no prefix; if the two prefixes
+ * used with a particular namespace are "xs" and "xsd", say, then these will be prefix
+ * codes 1 and 2.</p>
+ *
+ * <p>Fingerprints in the range 0 to 1023 are reserved for system use, and are allocated as constants
+ * mainly to names in the XSLT and XML Schema namespaces: constants representing these names
+ * are found in {@link StandardNames}.
+ *
+ * <p>Operations that update the NamePool, or that have the potential to update it, are
+ * synchronized. Read-only operations are done without synchronization. Although technically
+ * unsafe, this has not led to any problems in practice. Performance problems due to excessive
+ * contention on the NamePool have occasionally been observed: if this happens, the best strategy
+ * is to consider splitting the workload to use multiple Configurations each with a separate
+ * NamePool.</p>
+ *
+ * <h3>Internal organization of the NamePool</h3>
+ *
+ * <p>The NamePool holds two kinds of entry: name entries, representing
+ * expanded names (local name + prefix + URI), identified by a name code,
+ * and URI entries (prefix + URI) identified by a URI code.</p>
+ *
+ * <p>The data structure of the name table is as follows.</p>
+ *
+ * <p>There is a fixed size hash table; names are allocated to slots in this
+ * table by hashing on the local name. Each entry in the table is the head of
+ * a chain of NameEntry objects representing names that have the same hash code.</p>
+ *
+ * <p>Each NameEntry represents a distinct name (same URI and local name). It contains
+ * the local name as a string, plus a short integer representing the URI (as an
+ * offset into the array uris[] - this is known as the URIcode).</p>
+ *
+ * <p>The fingerprint of a name consists of the hash slot number (in the bottom 10 bits)
+ * concatenated with the depth of the entry down the chain of hash synonyms (in the
+ * next 10 bits). Fingerprints with depth 0 (i.e., in the range 0-1023) are reserved
+ * for predefined names (names of XSLT elements and attributes, and of built-in types).
+ * These names are not stored in the name pool, but are accessible as if they were.</p>
+ *
+ * <p>A nameCode contains the fingerprint in the bottom 20 bits. It also contains
+ * a 10-bit prefix index. This distinguishes the prefix used, among all the
+ * prefixes that have been used with this namespace URI. If the prefix index is
+ * zero, the prefix is null. Otherwise, it indexes an array of
+ * prefix Strings associated with the namespace URI. Note that the data structures
+ * and algorithms are optimized for the case where URIs usually use the same prefix.</p>
+ *
+ * <p>The nameCode -1 is reserved to mean "not known" or inapplicable. The fingerprint -1
+ * has the same meaning. Note that masking the nameCode -1 to extract its bottom 20 bits is
+ * incorrect, and will lead to errors.</p>
+ *
+ * <p>Modified in 9.4 to remove namespace codes.</p>
+ *
+ * @author Michael H. Kay
+ */
+
+public class NamePool implements Serializable {
+
+
+ /**
+ * FP_MASK is a mask used to obtain a fingerprint from a nameCode. Given a
+ * nameCode nc, the fingerprint is <code>nc & NamePool.FP_MASK</code>.
+ * (In practice, Saxon code often uses the literal constant 0xfffff,
+ * to extract the bottom 20 bits).
+ *
+ * <p>The difference between a fingerprint and a nameCode is that
+ * a nameCode contains information
+ * about the prefix of a name, the fingerprint depends only on the namespace
+ * URI and local name. Note that the "null" nameCode (-1) does not produce
+ * the "null" fingerprint (also -1) when this mask is applied.</p>
+ */
+
+ public static final int FP_MASK = 0xfffff;
+
+ // Since fingerprints in the range 0-1023 belong to predefined names, user-defined names
+ // will always have a fingerprint above this range, which can be tested by a mask.
+
+ public static final int USER_DEFINED_MASK = 0xffc00;
+
+ // Limit: maximum number of prefixes allowed for one URI
+
+ public static final int MAX_PREFIXES_PER_URI = 1023;
+
+ /**
+ * Internal structure of a NameEntry, the entry on the hash chain of names.
+ */
+
+ private static class NameEntry implements Serializable {
+ String localName;
+ short uriCode;
+ /*@Nullable*/ NameEntry nextEntry; // link to next NameEntry with the same hashcode
+
+ /**
+ * Create a NameEntry for a QName
+ * @param uriCode the numeric code representing the namespace URI
+ * @param localName the local part of the QName
+ */
+ public NameEntry(short uriCode, String localName) {
+ this.uriCode = uriCode;
+ this.localName = localName.intern();
+ nextEntry = null;
+ }
+
+ }
+
+ private NameEntry[] hashslots = new NameEntry[1024];
+
+ /**
+ * Table of URIs, indexed by URI code
+ */
+ private String[] uris = new String[100];
+
+ /**
+ * Table of prefixes for each URI: for each URI code there is an array of prefixes holding
+ * the prefixes that have been encountered in conjunction with that URI
+ */
+ private String[][] prefixesForUri = new String[100][0];
+
+ /**
+ * Number of entries used in the uris and prefixesForUri arrays
+ */
+ short urisUsed = 0;
+
+ // General purpose cache for data held by clients of the namePool
+
+ private HashMap<Class, Object> clientData;
+
+ /**
+ * Create a NamePool
+ */
+
+ public NamePool() {
+
+ uris[NamespaceConstant.NULL_CODE] = NamespaceConstant.NULL;
+ prefixesForUri[NamespaceConstant.NULL_CODE] = new String[]{""};
+
+ uris[NamespaceConstant.XML_CODE] = NamespaceConstant.XML;
+ prefixesForUri[NamespaceConstant.XML_CODE] = new String[]{"xml"};
+
+ uris[NamespaceConstant.XSLT_CODE] = NamespaceConstant.XSLT;
+ prefixesForUri[NamespaceConstant.XSLT_CODE] = new String[]{"xsl"};
+
+ uris[NamespaceConstant.SAXON_CODE] = NamespaceConstant.SAXON;
+ prefixesForUri[NamespaceConstant.SAXON_CODE] = new String[]{"saxon"};
+
+ uris[NamespaceConstant.SCHEMA_CODE] = NamespaceConstant.SCHEMA;
+ prefixesForUri[NamespaceConstant.SCHEMA_CODE] = new String[]{"xs"};
+
+ uris[NamespaceConstant.XSI_CODE] = NamespaceConstant.SCHEMA_INSTANCE;
+ prefixesForUri[NamespaceConstant.XSI_CODE] = new String[]{"xsi"};
+
+ urisUsed = 6;
+
+ }
+
+ /**
+ * Get a name entry corresponding to a given name code
+ * @param nameCode the integer name code
+ * @return the NameEntry for this name code, or null if there is none.
+ */
+
+ private NameEntry getNameEntry(int nameCode) {
+ int hash = nameCode & 0x3ff;
+ int depth = (nameCode >> 10) & 0x3ff;
+ NameEntry entry = hashslots[hash];
+
+ for (int i = 1; i < depth; i++) {
+ if (entry == null) {
+ return null;
+ }
+ entry = entry.nextEntry;
+ }
+ return entry;
+ }
+
+ /**
+ * Search an array of shorts (e.g. prefix codes) for a given value
+ * @param codes the array to be searched
+ * @param value the value being sought
+ * @return the position of the first occurrence of the value in the array, or -1 if not found
+ */
+
+ private static int search(String[] codes, String value) {
+ for (int i=0; i<codes.length; i++) {
+ if (codes[i].equals(value)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Get a namespace binding for a given namecode.
+ *
+ * @param namecode a code identifying an expanded QName, e.g. of an element or attribute
+ * @return an object identifying the namespace binding used in the given name. The namespace binding
+ * identifies both the prefix and the URI.
+ */
+
+ public NamespaceBinding getNamespaceBinding(int namecode) {
+ short uriCode;
+ int fp = namecode & FP_MASK;
+ if ((fp & USER_DEFINED_MASK) == 0) {
+ uriCode = StandardNames.getURICode(fp);
+ if (uriCode == NamespaceConstant.XML_CODE) {
+ return NamespaceBinding.XML;
+ }
+ } else {
+ NameEntry entry = getNameEntry(namecode);
+ if (entry == null) {
+ return null;
+ } else {
+ uriCode = entry.uriCode;
+ }
+ }
+ int prefixIndex = (namecode >> 20) & 0x3ff;
+ return new NamespaceBinding(getPrefixWithIndex(uriCode, prefixIndex), getURIFromURICode(uriCode));
+ }
+
+ /**
+ * Get a namespace binding for a given namecode.
+ *
+ * @param namecode a code identifying an expanded QName, e.g. of an element or attribute
+ * @return an object identifying the namespace binding used in the given name. The namespace binding
+ * identifies both the prefix and the URI.
+ */
+
+ public StructuredQName getStructuredQName(int namecode) {
+ short uriCode;
+ String localName;
+ int fp = namecode & FP_MASK;
+ if ((fp & USER_DEFINED_MASK) == 0) {
+ uriCode = StandardNames.getURICode(fp);
+ localName = StandardNames.getLocalName(fp);
+ } else {
+ NameEntry entry = getNameEntry(namecode);
+ if (entry == null) {
+ return null;
+ } else {
+ uriCode = entry.uriCode;
+ localName = entry.localName;
+ }
+ }
+ int prefixIndex = (namecode >> 20) & 0x3ff;
+ return new StructuredQName(getPrefixWithIndex(uriCode, prefixIndex), getURIFromURICode(uriCode), localName);
+ }
+
+
+ /**
+ * Determine whether a given namecode has a non-empty prefix (and therefore, in the case of attributes,
+ * whether the name is in a non-null namespace
+ * @param nameCode the name code to be tested
+ * @return true if the name has a non-empty prefix
+ */
+
+ public static boolean isPrefixed(int nameCode) {
+ return (nameCode & 0x3ff00000) != 0;
+ }
+
+
+ /**
+ * Allocate the uri code for a given URI; create one if not found
+ * @param uri The namespace URI. Supply "" or null for the "null namespace"
+ * @return an integer code that uniquely identifies this URI within the namepool.
+ */
+
+ public synchronized short allocateCodeForURI(String uri) {
+ //System.err.println("allocateCodeForURI");
+ return allocateCodeForURIInternal(uri);
+ }
+
+ /**
+ * Allocate the uri code for a given URI; create one if not found. This non-synchronized
+ * version of the external method is provided to avoid the overhead of synchronizing a second time.
+ * @param uri The namespace URI. Supply "" or null for the "null namespace"
+ * @return an integer code that uniquely identifies this URI within the namepool.
+ */
+
+ private short allocateCodeForURIInternal(String uri) {
+ if (uri == null) {
+ return NamespaceConstant.NULL_CODE;
+ }
+ for (short j = 0; j < urisUsed; j++) {
+ if (uris[j].equals(uri)) {
+ return j;
+ }
+ }
+ if (urisUsed >= uris.length) {
+ if (urisUsed > 32000) {
+ throw new NamePoolLimitException("Too many namespace URIs");
+ }
+ String[][] p = new String[urisUsed * 2][0];
+ String[] u = new String[urisUsed * 2];
+ System.arraycopy(prefixesForUri, 0, p, 0, urisUsed);
+ System.arraycopy(uris, 0, u, 0, urisUsed);
+ prefixesForUri = p;
+ uris = u;
+ }
+ uris[urisUsed] = uri;
+ return urisUsed++;
+ }
+
+
+ /**
+ * Get the uri code for a given URI
+ * @param uri the URI whose code is required
+ * @return the associated integer URI code, or -1 if not present in the name pool
+ */
+
+ public short getCodeForURI(String uri) {
+ for (short j = 0; j < urisUsed; j++) {
+ if (uris[j].equals(uri)) {
+ return j;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Suggest a prefix for a given URI. If there are several, it's undefined which one is returned.
+ * If there are no prefixes registered for this URI, return null.
+ * @param URI the namespace URI
+ * @return a prefix that has previously been associated with this URI, if available; otherwise null
+ */
+
+ public String suggestPrefixForURI(String URI) {
+ if (URI.equals(NamespaceConstant.XML)) {
+ return "xml";
+ }
+ short uriCode = getCodeForURI(URI);
+ if (uriCode == -1) {
+ return null;
+ }
+ if (prefixesForUri[uriCode].length >= 1) {
+ return prefixesForUri[uriCode][0];
+ }
+ return null;
+ }
+
+ /**
+ * Get a prefix among all the prefixes used with a given URI, given its index
+ * @param uriCode the integer code identifying the URI
+ * @param index indicates which of the prefixes associated with this URI is required
+ * @return the prefix with the given index. If the index is 0, the prefix is always "".
+ */
+
+ private String getPrefixWithIndex(short uriCode, int index) {
+ if (index == 0) {
+ return "";
+ }
+ return prefixesForUri[uriCode][index-1];
+ }
+
+ /**
+ * Allocate a name from the pool, or a new Name if there is not a matching one there
+ *
+ * @param prefix the namespace prefix. Use "" for the null prefix, representing the absent namespace
+ * @param uri the namespace URI. Use "" or null for the non-namespace.
+ * @param localName the local part of the name
+ * @return an integer (the "namecode") identifying the name within the namepool.
+ * The Name itself may be retrieved using the getName(int) method
+ */
+
+ public synchronized int allocate(String prefix, String uri, String localName) {
+ //System.err.println("Allocate " + prefix + " : " + uri + " : " + localName);
+ if (NamespaceConstant.isReserved(uri) || NamespaceConstant.SAXON.equals(uri)) {
+ int fp = StandardNames.getFingerprint(uri, localName);
+ if (fp != -1) {
+ short uriCode = StandardNames.getURICode(fp);
+ int pindex;
+ if (prefix.length() == 0) {
+ pindex = 0;
+ } else {
+ final String[] prefixes = prefixesForUri[uriCode];
+ int prefixPosition = search(prefixes, prefix);
+ if (prefixPosition < 0) {
+ if (prefixes.length == MAX_PREFIXES_PER_URI) {
+ throw new NamePoolLimitException("NamePool limit exceeded: max " +
+ MAX_PREFIXES_PER_URI + " prefixes per URI");
+ }
+ String[] p2 = new String[prefixes.length + 1];
+ System.arraycopy(prefixes, 0, p2, 0, prefixes.length);
+ p2[prefixes.length] = prefix;
+ prefixesForUri[uriCode] = p2;
+ prefixPosition = prefixes.length;
+ }
+ pindex = prefixPosition + 1;
+ }
+ return (pindex << 20) + fp;
+ }
+ }
+ // otherwise register the name in this NamePool
+ short uriCode = allocateCodeForURIInternal(uri);
+
+ int hash = (localName.hashCode() & 0x7fffffff) % 1023;
+ int depth = 1;
+
+ final String[] prefixes = prefixesForUri[uriCode];
+ int prefixIndex;
+ if (prefix.length() == 0) {
+ prefixIndex = 0;
+ } else {
+ int prefixPosition = search(prefixes, prefix);
+ if (prefixPosition < 0) {
+ if (prefixes.length == MAX_PREFIXES_PER_URI) {
+ throw new NamePoolLimitException("NamePool limit exceeded: max " +
+ MAX_PREFIXES_PER_URI + " prefixes per URI");
+ }
+ String[] p2 = new String[prefixes.length + 1];
+ System.arraycopy(prefixes, 0, p2, 0, prefixes.length);
+ p2[prefixes.length] = prefix;
+ prefixesForUri[uriCode] = p2;
+ prefixPosition = prefixes.length;
+ }
+ prefixIndex = prefixPosition + 1;
+ }
+
+ NameEntry entry;
+
+ if (hashslots[hash] == null) {
+ entry = new NameEntry(uriCode, localName);
+ hashslots[hash] = entry;
+ } else {
+ entry = hashslots[hash];
+ while (true) {
+ boolean sameLocalName = (entry.localName.equals(localName));
+ boolean sameURI = (entry.uriCode == uriCode);
+
+ if (sameLocalName && sameURI) {
+ // may need to add a new prefix to the entry
+ break;
+ } else {
+ NameEntry next = entry.nextEntry;
+ depth++;
+ if (depth >= 1024) {
+ throw new NamePoolLimitException("Saxon name pool is full");
+ }
+ if (next == null) {
+ entry.nextEntry = new NameEntry(uriCode, localName);
+ break;
+ } else {
+ entry = next;
+ }
+ }
+ }
+ }
+ return ((prefixIndex << 20) + (depth << 10) + hash);
+ }
+
+ /**
+ * Get the namespace-URI of a name, given its name code or fingerprint
+ * @param nameCode the name code or fingerprint of a name
+ * @return the namespace URI corresponding to this name code. Returns "" for the
+ * no-namespace.
+ * @throws IllegalArgumentException if the nameCode is not known to the NamePool.
+ */
+
+ /*@NotNull*/
+ public String getURI(int nameCode) {
+ if ((nameCode & USER_DEFINED_MASK) == 0) {
+ return StandardNames.getURI(nameCode & FP_MASK);
+ }
+ NameEntry entry = getNameEntry(nameCode);
+ if (entry == null) {
+ unknownNameCode(nameCode);
+ return ""; // to keep the compiler happy
+ }
+ return uris[entry.uriCode];
+ }
+
+ /**
+ * Get the URI code of a name, given its name code or fingerprint
+ * @param nameCode the name code or fingerprint of a name in the name pool
+ * @return the integer code identifying the namespace URI part of the name
+ */
+
+ public short getURICode(int nameCode) {
+ if ((nameCode & USER_DEFINED_MASK) == 0) {
+ return StandardNames.getURICode(nameCode & FP_MASK);
+ }
+ NameEntry entry = getNameEntry(nameCode);
+ if (entry == null) {
+ unknownNameCode(nameCode);
+ return -1;
+ }
+ return entry.uriCode;
+ }
+
+ /**
+ * Get the local part of a name, given its name code or fingerprint
+ * @param nameCode the integer name code or fingerprint of the name
+ * @return the local part of the name represented by this name code or fingerprint
+ */
+
+ public String getLocalName(int nameCode) {
+ if ((nameCode & USER_DEFINED_MASK) == 0) {
+ return StandardNames.getLocalName(nameCode & FP_MASK);
+ }
+ NameEntry entry = getNameEntry(nameCode);
+ if (entry == null) {
+ unknownNameCode(nameCode);
+ return null;
+ }
+ return entry.localName;
+ }
+
+ /**
+ * Get the prefix part of a name, given its name code
+ * @param nameCode the integer name code of a name in the name pool
+ * @return the prefix of this name. Note that if a fingerprint rather than a full name code is supplied
+ * the returned prefix will be ""
+ */
+
+ public String getPrefix(int nameCode) {
+ int prefixIndex = (nameCode >> 20) & 0x3ff;
+ if (prefixIndex == 0) {
+ return "";
+ }
+ short uriCode = getURICode(nameCode);
+ return prefixesForUri[uriCode][prefixIndex-1];
+ }
+
+ /**
+ * Get the display form of a name (the QName), given its name code or fingerprint
+ * @param nameCode the integer name code or fingerprint of a name in the name pool
+ * @return the corresponding lexical QName (if a fingerprint was supplied, this will
+ * simply be the local name)
+ */
+
+ public String getDisplayName(int nameCode) {
+ if ((nameCode & USER_DEFINED_MASK) == 0) {
+ // This indicates a standard name known to the system (but it might have a non-standard prefix)
+ short uriCode = getURICode(nameCode);
+ if (uriCode == NamespaceConstant.XML_CODE) {
+ return "xml:" + StandardNames.getLocalName(nameCode & FP_MASK);
+ } else {
+ if (isPrefixed(nameCode)) {
+ return getPrefix(nameCode) + ':' + StandardNames.getLocalName(nameCode & FP_MASK);
+ } else {
+ return StandardNames.getLocalName(nameCode & FP_MASK);
+ }
+ }
+ } else {
+ NameEntry entry = getNameEntry(nameCode);
+ if (entry == null) {
+ unknownNameCode(nameCode);
+ return null;
+ }
+ if (isPrefixed(nameCode)) {
+ return getPrefix(nameCode) + ':' + entry.localName;
+ } else {
+ return entry.localName;
+ }
+ }
+ }
+
+ /**
+ * Get the Clark form of a name, given its name code or fingerprint
+ * @param nameCode the integer name code or fingerprint of a name in the name pool
+ * @return the local name if the name is in the null namespace, or "{uri}local"
+ * otherwise. The name is always interned.
+ */
+
+ public String getClarkName(int nameCode) {
+ if ((nameCode & USER_DEFINED_MASK) == 0) {
+ return StandardNames.getClarkName(nameCode & FP_MASK);
+ }
+ NameEntry entry = getNameEntry(nameCode);
+ if (entry == null) {
+ unknownNameCode(nameCode);
+ return null;
+ }
+ if (entry.uriCode == 0) {
+ return entry.localName;
+ } else {
+ String n = '{' + getURIFromURICode(entry.uriCode) + '}' + entry.localName;
+ return n.intern();
+ }
+ }
+
+ /**
+ * Get the EQName form of a name, given its name code or fingerprint
+ * @param nameCode the integer name code or fingerprint of a name in the name pool
+ * @return the name in the form Q{}local for a name in no namespace, or Q{uri}local for
+ * a name in a namespace
+ */
+
+ public String getEQName(int nameCode) {
+ if ((nameCode & USER_DEFINED_MASK) == 0) {
+ return 'Q' + StandardNames.getClarkName(nameCode & FP_MASK);
+ }
+ NameEntry entry = getNameEntry(nameCode);
+ if (entry == null) {
+ unknownNameCode(nameCode);
+ return null;
+ }
+ return "Q{" + getURIFromURICode(entry.uriCode) + "}" + entry.localName;
+ }
+
+ /**
+ * Allocate a fingerprint given a Clark Name
+ * @param expandedName the name in Clark notation, that is "localname" or "{uri}localName"
+ * @return the fingerprint of the name, which need not previously exist in the name pool
+ */
+
+ public int allocateClarkName(String expandedName) {
+ String namespace;
+ String localName;
+ if (expandedName.charAt(0) == '{') {
+ int closeBrace = expandedName.indexOf('}');
+ if (closeBrace < 0) {
+ throw new IllegalArgumentException("No closing '}' in Clark name");
+ }
+ namespace = expandedName.substring(1, closeBrace);
+ if (closeBrace == expandedName.length()) {
+ throw new IllegalArgumentException("Missing local part in Clark name");
+ }
+ localName = expandedName.substring(closeBrace + 1);
+ } else {
+ namespace = "";
+ localName = expandedName;
+ }
+
+ return allocate("", namespace, localName);
+ }
+
+ /**
+ * Parse a Clark-format expanded name, returning the URI and local name
+ * @param expandedName the name in Clark notation, that is "localname" or "{uri}localName"
+ * @return an array of two strings, the URI and the local name respectively
+ */
+
+ public static String[] parseClarkName(String expandedName) {
+ String namespace;
+ String localName;
+ if (expandedName.charAt(0) == '{') {
+ int closeBrace = expandedName.indexOf('}');
+ if (closeBrace < 0) {
+ throw new IllegalArgumentException("No closing '}' in Clark name");
+ }
+ namespace = expandedName.substring(1, closeBrace);
+ if (closeBrace == expandedName.length()) {
+ throw new IllegalArgumentException("Missing local part in Clark name");
+ }
+ localName = expandedName.substring(closeBrace + 1);
+ } else {
+ namespace = "";
+ localName = expandedName;
+ }
+ return new String[] {namespace, localName};
+ }
+
+
+ /**
+ * Internal error: name not found in namepool
+ * (Usual cause is allocating a name code from one name pool and trying to
+ * find it in another)
+ * @param nameCode the absent name code
+ */
+
+ private static void unknownNameCode(int nameCode) {
+ throw new IllegalArgumentException("Unknown name code " + nameCode);
+ }
+
+ /**
+ * Get a fingerprint for the name with a given uri and local name.
+ * These must be present in the NamePool.
+ * The fingerprint has the property that if two fingerprint are the same, the names
+ * are the same (ie. same local name and same URI).
+ * @param uri the namespace URI of the required QName
+ * @param localName the local part of the required QName
+ * @return the integer fingerprint, or -1 if this is not found in the name pool
+ */
+
+ public int getFingerprint(String uri, String localName) {
+ // A read-only version of allocate()
+
+ short uriCode;
+ if (uri.length() == 0) {
+ uriCode = 0;
+ } else {
+ if (NamespaceConstant.isReserved(uri) || uri.equals(NamespaceConstant.SAXON)) {
+ int fp = StandardNames.getFingerprint(uri, localName);
+ if (fp != -1) {
+ return fp;
+ // otherwise, look for the name in this namepool
+ }
+ }
+ uriCode = -1;
+ for (short j = 0; j < urisUsed; j++) {
+ if (uris[j].equals(uri)) {
+ uriCode = j;
+ break;
+ }
+ }
+ if (uriCode == -1) {
+ return -1;
+ }
+ }
+
+ int hash = (localName.hashCode() & 0x7fffffff) % 1023;
+ int depth = 1;
+
+ NameEntry entry;
+
+ if (hashslots[hash] == null) {
+ return -1;
+ }
+
+ entry = hashslots[hash];
+ while (true) {
+ if (entry.uriCode == uriCode && entry.localName.equals(localName)) {
+ break;
+ } else {
+ NameEntry next = entry.nextEntry;
+ depth++;
+ if (next == null) {
+ return -1;
+ } else {
+ entry = next;
+ }
+ }
+ }
+ return (depth << 10) + hash;
+ }
+
+ /**
+ * Get the namespace URI from a URI code.
+ * @param code a code that identifies the URI within the name pool
+ * @return the URI represented by this code
+ */
+
+ public String getURIFromURICode(short code) {
+ return uris[code];
+ }
+
+ /**
+ * Get the nameCode for a lexical QName, given a namespace resolver.
+ * @param qname the lexical QName (with leading and trailing whitespace allowed).
+ * @param useDefault if true, an absent prefix is resolved by the NamespaceResolver
+ * to the namespace URI assigned to the prefix "". If false, an absent prefix is
+ * interpreted as meaning the name is in no namespace.
+ * @param resolver NamespaceResolver used to resolve the namespace prefix to a namespace URI
+ * @param checker NameChecker used to check names against the XML 1.0 or 1.1 specification
+ * @return the corresponding nameCode
+ * @throws net.sf.saxon.trans.XPathException if the string is not a valid lexical QName or
+ * if the namespace prefix has not been declared
+ */
+
+ public int allocateLexicalQName(CharSequence qname, boolean useDefault,
+ NamespaceResolver resolver, NameChecker checker)
+ throws XPathException {
+ try {
+ String[] parts = checker.getQNameParts(Whitespace.trimWhitespace(qname));
+ String uri = resolver.getURIForPrefix(parts[0], useDefault);
+ if (uri == null) {
+ throw new XPathException("Namespace prefix '" + parts[0] + "' has not been declared");
+ }
+ return allocate(parts[0], uri, parts[1]);
+ } catch (QNameException e) {
+ throw new XPathException(e.getMessage());
+ }
+ }
+
+ /**
+ * Save client data on behalf of a user of the namepool
+ * @param key the class that is maintaining private data in the name pool
+ * @param value the private data maintained in the name pool on behalf of this class
+ */
+
+ public void setClientData(Class key, Object value) {
+ if (clientData == null) {
+ clientData = new HashMap<Class, Object>(10);
+ }
+ clientData.put(key, value);
+ }
+
+ /**
+ * Retrieve client data on behalf of a user of the namepool
+ * @param key the class that is maintaining private data in the name pool
+ * @return the private data maintained in the name pool on behalf of this class
+ */
+
+ public Object getClientData(Class key) {
+ if (clientData == null) {
+ return null;
+ }
+ return clientData.get(key);
+ }
+
+ /**
+ * Diagnostic print of the namepool contents.
+ */
+
+ public synchronized void diagnosticDump() {
+ System.err.println("Contents of NamePool " + this);
+ for (int i = 0; i < 1024; i++) {
+ NameEntry entry = hashslots[i];
+ int depth = 0;
+ while (entry != null) {
+ System.err.println("Fingerprint " + depth + '/' + i);
+ System.err.println(" local name = " + entry.localName +
+ " uri code = " + entry.uriCode);
+ entry = entry.nextEntry;
+ depth++;
+ }
+ }
+
+ for (int u = 0; u < urisUsed; u++) {
+ System.err.println("URI " + u + " = " + uris[u]);
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ for (int p=0; p< prefixesForUri[u].length; p++) {
+ fsb.append(prefixesForUri[u][p] + ", ");
+ }
+ System.err.println("Prefix codes for URI " + u + " = " + fsb.toString());
+ }
+ }
+
+ /**
+ * Statistics summarizing the namepool contents.
+ * This method outputs summary statistical information to System.err
+ */
+
+ public synchronized void statistics() {
+ int slots = 0;
+ int entries = 0;
+ for (int i = 0; i < 1024; i++) {
+ NameEntry entry = hashslots[i];
+ if (entry != null) slots++;
+ while (entry != null) {
+ entry = entry.nextEntry;
+ entries++;
+ }
+ }
+ System.err.println("NamePool contents: " + entries + " entries in " + slots + " chains. " +
+ + urisUsed + " URIs");
+ }
+
+ /**
+ * Uncaught Exception raised when some limit in the design of the name pool is exceeded
+ */
+ public static class NamePoolLimitException extends RuntimeException {
+
+ /**
+ * Create the exception
+ * @param message the error message associated with the error
+ */
+
+ public NamePoolLimitException(String message) {
+ super(message);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/om/NamespaceBinding.java b/sf/saxon/om/NamespaceBinding.java
new file mode 100644
index 0000000..cb9429f
--- /dev/null
+++ b/sf/saxon/om/NamespaceBinding.java
@@ -0,0 +1,127 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.lib.NamespaceConstant;
+
+/**
+ * Represents the binding of a prefix to a URI. Also, in some contexts, represents an unbinding, by
+ * virtue of the URI being set to a zero length string.
+ *
+ * @since 9.4
+ */
+public final class NamespaceBinding {
+
+ private String prefix;
+ private String uri;
+
+ public final static NamespaceBinding XML = new NamespaceBinding("xml", NamespaceConstant.XML);
+ public final static NamespaceBinding DEFAULT_UNDECLARATION = new NamespaceBinding("", "");
+
+ public final static NamespaceBinding[] EMPTY_ARRAY = new NamespaceBinding[0];
+
+ /**
+ * Create a binding of a prefix to a URI
+ *
+ * @param prefix the prefix: either an NCName, or a zero-length string to bind the default namespace.
+ * Must not be null.
+ * @param uri the namespace URI: either a URI, or a zero-length string to unbind the prefix. Must
+ * not be null.
+ */
+
+ public NamespaceBinding(String prefix, String uri) {
+ this.prefix = prefix;
+ this.uri = uri;
+ if (prefix == null || uri == null) {
+ throw new NullPointerException();
+ }
+ }
+
+ /**
+ * Create a binding of a prefix to a URI. Static factory method for the convenience of compiled bytecode;
+ * reuses standard NamespaceBinding objects where possible
+ *
+ * @param prefix the prefix: either an NCName, or a zero-length string to bind the default namespace.
+ * Must not be null.
+ * @param uri the namespace URI: either a URI, or a zero-length string to unbind the prefix. Must
+ * not be null.
+ * @return the namespace binding object
+ */
+
+ public static NamespaceBinding makeNamespaceBinding(CharSequence prefix, CharSequence uri) {
+ if (prefix.length() == 0 && uri.length() == 0) {
+ return DEFAULT_UNDECLARATION;
+ } else if (prefix.equals("xml") && uri.equals(NamespaceConstant.XML)) {
+ return XML;
+ } else {
+ return new NamespaceBinding(prefix.toString(), uri.toString());
+ }
+ }
+
+ /**
+ * Get the prefix part of the binding
+ *
+ * @return the prefix. Never null. The zero-length string indicates a binding for the default namespace.
+ */
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Get the URI part of the binding
+ *
+ * @return the URI. Never null. The zero-length string indicates an unbinding of the prefix. For the
+ * default namespace (prefix="") this indicates that the prefix refers to names in no namespace; for other
+ * prefixes, it indicates that the prefix is not bound to any namespace and therefore cannot be used.
+ */
+
+ public String getURI() {
+ return uri;
+ }
+
+ /**
+ * Ask whether this is a binding for the XML namespace
+ *
+ * @return true if this is the binding of the prefix "xml" to the standard XML namespace.
+ */
+
+ public boolean isXmlNamespace() {
+ return prefix.equals("xml");
+ }
+
+ /**
+ * Ask whether this is an undeclaration of the default prefix, that is, a namespace binding
+ * corresponding to <code>xmlns=""</code>
+ *
+ * @return true if this corresponding to <code>xmlns=""</code>
+ */
+
+ public boolean isDefaultUndeclaration() {
+ return prefix.length()==0 && uri.length()==0;
+ }
+
+ /**
+ * Test if this namespace binding is the same as another
+ *
+ * @param obj the comparand
+ * @return true if the comparand is a Namespace binding of the same prefix to the same URI
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof NamespaceBinding &&
+ prefix.equals(((NamespaceBinding) obj).getPrefix()) &&
+ uri.equals(((NamespaceBinding) obj).getURI());
+ }
+
+ @Override
+ public int hashCode() {
+ return prefix.hashCode() ^ uri.hashCode();
+ }
+}
+
diff --git a/sf/saxon/om/NamespaceException.java b/sf/saxon/om/NamespaceException.java
new file mode 100644
index 0000000..6756117
--- /dev/null
+++ b/sf/saxon/om/NamespaceException.java
@@ -0,0 +1,31 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * A NamespaceException represents an error condition whereby a QName (for example a variable
+ * name or template name) uses a namespace prefix that is not declared
+ */
+
+public class NamespaceException extends Exception {
+
+ String prefix;
+
+ public NamespaceException (String prefix) {
+ this.prefix = prefix;
+ }
+
+ /*@NotNull*/ public String getMessage() {
+ return "Namespace prefix " + prefix + " has not been declared";
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+}
diff --git a/sf/saxon/om/NamespaceResolver.java b/sf/saxon/om/NamespaceResolver.java
new file mode 100644
index 0000000..ad0cb33
--- /dev/null
+++ b/sf/saxon/om/NamespaceResolver.java
@@ -0,0 +1,42 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import java.util.Iterator;
+
+/**
+ * Abstract class that supports lookup of a lexical QName to get the expanded QName.
+ */
+
+public interface NamespaceResolver {
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ * @param prefix the namespace prefix. May be the zero-length string, indicating
+ * that there is no prefix. This indicates either the default namespace or the
+ * null namespace, depending on the value of useDefault.
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is "". If false, the method returns "" when the prefix is "".
+ * @return the uri for the namespace, or null if the prefix is not in scope.
+ * The "null namespace" is represented by the pseudo-URI "".
+ */
+
+ /*@Nullable*/ public abstract String getURIForPrefix(String prefix, boolean useDefault);
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ * @return an iterator over all the prefixes for which a namespace binding exists, including
+ * the zero-length string to represent the null/absent prefix if it is bound
+ */
+
+ public abstract Iterator<String> iteratePrefixes();
+
+}
+
diff --git a/sf/saxon/om/NoElementsSpaceStrippingRule.java b/sf/saxon/om/NoElementsSpaceStrippingRule.java
new file mode 100644
index 0000000..a77b882
--- /dev/null
+++ b/sf/saxon/om/NoElementsSpaceStrippingRule.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+
+import net.sf.saxon.event.Stripper;
+
+/**
+ * A whitespace stripping rule that strips all elements unless xml:space indicates that whitespace
+ * should be preserved.
+ */
+
+public class NoElementsSpaceStrippingRule implements SpaceStrippingRule {
+
+ private final static NoElementsSpaceStrippingRule THE_INSTANCE = new NoElementsSpaceStrippingRule();
+
+ public static NoElementsSpaceStrippingRule getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * Decide whether an element is in the set of white-space preserving element types
+ *
+ * @param fingerprint identifies the element being tested
+ * @return STRIP_DEFAULT: strip spaces unless xml:space tells you not to.
+ */
+
+ public byte isSpacePreserving(NodeName fingerprint) {
+ return Stripper.ALWAYS_PRESERVE;
+ }
+
+}
+
diff --git a/sf/saxon/om/NoNamespaceName.java b/sf/saxon/om/NoNamespaceName.java
new file mode 100644
index 0000000..107e12a
--- /dev/null
+++ b/sf/saxon/om/NoNamespaceName.java
@@ -0,0 +1,175 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * An implementation of NodeName for the common case of a name in no namespace
+ */
+public class NoNamespaceName implements NodeName {
+
+ private String localName;
+ private int nameCode = -1;
+
+ public NoNamespaceName(String localName) {
+ this.localName = localName;
+ }
+
+ public NoNamespaceName(String localName, int nameCode) {
+ this.localName = localName;
+ this.nameCode = nameCode;
+ }
+
+ /**
+ * Get the prefix of the QName.
+ *
+ * @return the prefix. Returns the empty string if the name is unprefixed.
+ */
+ public String getPrefix() {
+ return "";
+ }
+
+ /**
+ * Get the namespace URI of the QName.
+ *
+ * @return the URI. Returns the empty string to represent the no-namespace
+ */
+ public String getURI() {
+ return "";
+ }
+
+ /**
+ * Get the local part of the QName
+ *
+ * @return the local part of the QName
+ */
+ public String getLocalPart() {
+ return localName;
+ }
+
+ /**
+ * Get the display name, that is the lexical QName in the form [prefix:]local-part
+ *
+ * @return the lexical QName
+ */
+ public String getDisplayName() {
+ return localName;
+ }
+
+ /**
+ * Get the name in the form of a StructuredQName
+ *
+ * @return the name in the form of a StructuredQName
+ */
+ public StructuredQName getStructuredQName() {
+ return new StructuredQName("", "", getLocalPart());
+ }
+
+ /**
+ * Test whether this name is in the same namespace as another name
+ *
+ * @return true if the two names are in the same namespace
+ */
+ public boolean isInSameNamespace(NodeName other) {
+ return other.getURI().length()==0;
+ }
+
+ /**
+ * Test whether this name is in a given namespace
+ *
+ * @param ns the namespace to be tested against
+ * @return true if the name is in the specified namespace
+ */
+ public boolean isInNamespace(String ns) {
+ return ns.length()==0;
+ }
+
+ /**
+ * Get a {@link net.sf.saxon.om.NamespaceBinding} whose (prefix, uri) pair are the prefix and URI of this
+ * node name
+ *
+ * @return the corresponding NamespaceBinding
+ */
+ public NamespaceBinding getNamespaceBinding() {
+ return NamespaceBinding.DEFAULT_UNDECLARATION;
+ }
+
+ /**
+ * Ask whether this node name representation has a known namecode and fingerprint
+ *
+ * @return true if the methods getFingerprint() and getNameCode() will
+ * return a result other than -1
+ */
+ public boolean hasFingerprint() {
+ return nameCode != -1;
+ }
+
+ /**
+ * Get the fingerprint of this name if known. This method should not to any work to allocate
+ * a fingerprint if none is already available
+ *
+ * @return the fingerprint if known; otherwise -1
+ */
+ public int getFingerprint() {
+ return nameCode;
+ }
+
+ /**
+ * Get the nameCode of this name if known. This method should not to any work to allocate
+ * a nameCode if none is already available
+ *
+ * @return the nameCode if known; otherwise -1
+ */
+ public int getNameCode() {
+ return nameCode;
+ }
+
+ /**
+ * Get the nameCode of this name, allocating a new code from the namepool if necessary
+ *
+ * @param namePool the NamePool used to allocate the name
+ * @return a nameCode for this name, newly allocated if necessary
+ */
+ public int allocateNameCode(NamePool namePool) {
+ if (nameCode == -1) {
+ return (nameCode = namePool.allocate("", "", localName));
+ } else {
+ return nameCode;
+ }
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+ @Override
+ public int hashCode() {
+ return StructuredQName.computeHashCode("", localName);
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ @Override
+ public boolean equals(/*@NotNull*/ Object obj) {
+ return obj instanceof NodeName &&
+ ((NodeName) obj).getLocalPart().equals(localName) &&
+ ((NodeName) obj).isInNamespace("");
+ }
+
+ @Override
+ public String toString() {
+ return localName;
+ }
+
+ public boolean isIdentical(IdentityComparable other) {
+ if(other instanceof NodeName) {
+ return this.equals(other) && this.getPrefix().equals(((NodeName)other).getPrefix());
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/sf/saxon/om/NodeInfo.java b/sf/saxon/om/NodeInfo.java
new file mode 100644
index 0000000..41fef22
--- /dev/null
+++ b/sf/saxon/om/NodeInfo.java
@@ -0,0 +1,595 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.SchemaType;
+
+import javax.xml.transform.Source;
+
+/**
+ * The NodeInfo interface represents a node in Saxon's implementation of the XPath 2.0 data model.
+ * <p>
+ * Note that several NodeInfo objects may represent the same node. To test node identity, the
+ * method {@link #isSameNodeInfo(NodeInfo)} should be used. An exception to this rule applies for
+ * document nodes, where the correspondence between document nodes and DocumentInfo objects is one to
+ * one. NodeInfo objects are never reused: a given NodeInfo object represents the same node for its entire
+ * lifetime.
+ * <p>
+ * This is the primary interface for accessing trees in Saxon, and it forms part of the public
+ * Saxon API. The only subclass of NodeInfo that applications should normally use is {@link DocumentInfo},
+ * which represents a document node. Methods that form part of the public API are (since Saxon 8.4)
+ * labelled with a JavaDoc "since" tag: classes and methods that have no such label should not be
+ * regarded as stable interfaces.
+ * <p>
+ * The interface represented by this class is at a slightly higher level than the abstraction described
+ * in the W3C data model specification, in that it includes support for the XPath axes, rather than exposing
+ * the lower-level properties (such as "parent" and "children") directly. All navigation within trees,
+ * except for a few convenience methods, is done by following the axes using the {@link #iterateAxis} method.
+ * This allows different implementations of the XPath tree model to implement axis navigation in different ways.
+ * Some implementations may choose to use the helper methods provided in class {@link net.sf.saxon.tree.util.Navigator}.
+ * <p>
+ * Note that the stability of this interface applies to classes that use the interface,
+ * not to classes that implement it. The interface may be extended in future to add new methods.
+ * <p>
+ * New implementations of NodeInfo are advised also to implement the methods in interface
+ * ExtendedNodeInfo, which will be moved into this interface at some time in the future.
+ *
+ * @author Michael H. Kay
+ * @since 8.4. Extended with three extra methods, previously in ExtendedNodeInfo, in 9.1
+ */
+
+public interface NodeInfo extends Source, Item {
+
+ /**
+ * Get the kind of node. This will be a value such as {@link net.sf.saxon.type.Type#ELEMENT}
+ * or {@link net.sf.saxon.type.Type#ATTRIBUTE}. There are seven kinds of node: documents, elements, attributes,
+ * text, comments, processing-instructions, and namespaces.
+ *
+ * @return an integer identifying the kind of node. These integer values are the
+ * same as those used in the DOM
+ * @see net.sf.saxon.type.Type
+ * @since 8.4
+ */
+
+ public int getNodeKind();
+
+ /**
+ * Determine whether this is the same node as another node.
+ * <p>
+ * Note that two different NodeInfo instances can represent the same conceptual node.
+ * Therefore the "==" operator should not be used to test node identity. The equals()
+ * method should give the same result as isSameNodeInfo(), but since this rule was introduced
+ * late it might not apply to all implementations.
+ * <p>
+ * Note: a.isSameNodeInfo(b) if and only if generateId(a)==generateId(b).
+ * <p>
+ * This method has the same semantics as isSameNode() in DOM Level 3, but
+ * works on Saxon NodeInfo objects rather than DOM Node objects.
+ *
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(NodeInfo other);
+
+ /**
+ * The equals() method compares nodes for identity. It is defined to give the same result
+ * as isSameNodeInfo().
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ * @since 8.7 Previously, the effect of the equals() method was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics. It is safer to use isSameNodeInfo() for this reason.
+ * The equals() method has been defined because it is useful in contexts such as a Java Set or HashMap.
+ */
+
+ public boolean equals(Object other);
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode();
+
+ /**
+ * Get the System ID for the node. Note this is not the
+ * same as the base URI: the base URI can be modified by xml:base, but
+ * the system ID cannot. The base URI is used primarily for resolving
+ * relative URIs within the content of the document. The system ID is
+ * used primarily in conjunction with a line number, for identifying the
+ * location of elements within the source XML, in particular when errors
+ * are found. For a document node, the System ID represents the value of
+ * the document-uri property as defined in the XDM data model.
+ *
+ * @return the System Identifier of the entity in the source document
+ * containing the node, or null if not known or not applicable.
+ * @since 8.4
+ */
+
+ /*@Nullable*/ public String getSystemId();
+
+ /**
+ * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
+ * in the node. This will be the same as the System ID unless xml:base has been used. Where the
+ * node does not have a base URI of its own, the base URI of its parent node is returned.
+ *
+ * @return the base URI of the node. This may be null if the base URI is unknown, including the case
+ * where the node has no parent.
+ * @since 8.4
+ */
+
+ public String getBaseURI();
+
+ /**
+ * Get line number. Line numbers are not maintained by default, except for
+ * stylesheets and schema documents. Line numbering can be requested using the
+ * -l option on the command line, or by setting options on the TransformerFactory
+ * or the Configuration before the source document is built.
+ * <p>
+ * The granularity of line numbering is normally the element level: for other nodes
+ * such as text nodes and attributes, the line number of the parent element will normally be returned.
+ * <p>
+ * In the case of a tree constructed by taking input from a SAX parser, the line number will reflect the
+ * SAX rules: that is, the line number of an element is the line number where the start tag ends. This
+ * may be a little confusing where elements have many attributes spread over multiple lines, or where
+ * single attributes (as can easily happen with XSLT 2.0 stylesheets) occupy several lines.
+ * <p>
+ * In the case of a tree constructed by a stylesheet or query, the line number may reflect the line in
+ * the stylesheet or query that caused the node to be constructed.
+ * <p>
+ * The line number can be read from within an XPath expression using the Saxon extension function
+ * saxon:line-number()
+ *
+ * @return the line number of the node in its original source document; or
+ * -1 if not available
+ * @since 8.4
+ */
+
+ public int getLineNumber();
+
+ /**
+ * Get column number. Column numbers are not maintained by default. Column numbering
+ * can be requested in the same way as line numbering; but a tree implementation can ignore
+ * the request.
+ * <p>
+ * The granularity of column numbering is normally the element level: for other nodes
+ * such as text nodes and attributes, the line number of the parent element will normally be returned.
+ * <p>
+ * In the case of a tree constructed by taking input from a SAX parser, the column number will reflect the
+ * SAX rules: that is, the column number of an element is the column number where the start tag ends. This
+ * may be a little confusing where elements have many attributes spread over multiple lines, or where
+ * single attributes (as can easily happen with XSLT 2.0 stylesheets) occupy several lines.
+ * <p>
+ * In the case of a tree constructed by a stylesheet or query, the column number may reflect the line in
+ * the stylesheet or query that caused the node to be constructed.
+ * <p>
+ * The column number can be read from within an XPath expression using the Saxon extension function
+ * saxon:column-number()
+ *
+ * @return the column number of the node in its original source document; or
+ * -1 if not available
+ * @since 9.1
+ */
+
+ public int getColumnNumber();
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * <p>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. To obtain a global ordering
+ * of nodes, the application should first compare the result of getDocumentNumber(),
+ * and only if the document number is the same should compareOrder() be called.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return -1 if this node precedes the other node, +1 if it follows the
+ * other node, or 0 if they are the same node. (In this case,
+ * isSameNode() will always return true, and the two nodes will
+ * produce the same result for generateId())
+ * @since 8.4
+ */
+
+ public int compareOrder(NodeInfo other);
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link AxisInfo#FOLLOWING} if it is on the following axis; {@link AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+
+ public int comparePosition(NodeInfo other);
+
+ /**
+ * Return the string value of the node as defined in the XPath data model.
+ * <p>
+ * The interpretation of this depends on the type
+ * of node. For an element it is the accumulated character content of the element,
+ * including descendant elements.
+ * <p>
+ * This method returns the string value as if the node were untyped. Unlike the string value
+ * accessor in the XPath 2.0 data model, it does not report an error if the element has a complex
+ * type, instead it returns the concatenation of the descendant text nodes as it would if the element
+ * were untyped.
+ *
+ * @return the string value of the node
+ * @since 8.4
+ */
+
+ public String getStringValue();
+
+ /**
+ * Get name code. The name code is a coded form of the node name: two nodes
+ * with the same name code have the same namespace URI, the same local name,
+ * and the same prefix. By masking the name code with {@link NamePool#FP_MASK}, you get a
+ * fingerprint: two nodes with the same fingerprint have the same local name
+ * and namespace URI.
+ *
+ * @return an integer name code, which may be used to obtain the actual node
+ * name from the name pool. For unnamed nodes (text nodes, comments, document nodes,
+ * and namespace nodes for the default namespace), returns -1.
+ * @see net.sf.saxon.om.NamePool#allocate allocate
+ * @see net.sf.saxon.om.NamePool#getFingerprint getFingerprint
+ * @since 8.4
+ */
+
+ public int getNameCode();
+
+ /**
+ * Get fingerprint. The fingerprint is a coded form of the expanded name
+ * of the node: two nodes
+ * with the same name code have the same namespace URI and the same local name.
+ * The fingerprint contains no information about the namespace prefix. For a name
+ * in the null namespace, the fingerprint is the same as the name code.
+ *
+ * @return an integer fingerprint; two nodes with the same fingerprint have
+ * the same expanded QName. For unnamed nodes (text nodes, comments, document nodes,
+ * and namespace nodes for the default namespace), returns -1.
+ * @since 8.4
+ */
+
+ public int getFingerprint();
+
+ /**
+ * Get the local part of the name of this node. This is the name after the ":" if any.
+ *
+ * @return the local part of the name. For an unnamed node, returns "". Unlike the DOM
+ * interface, this returns the full name in the case of a non-namespaced name.
+ * @since 8.4
+ */
+
+ public String getLocalPart();
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ *
+ * @return The URI of the namespace of this node. For an unnamed node,
+ * or for an element or attribute that is not in a namespace, or for a processing
+ * instruction, returns an empty string.
+ * @since 8.4
+ */
+
+ public String getURI();
+
+ /**
+ * Get the display name of this node, in the form of a lexical QName.
+ * For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ *
+ * @return The display name of this node. For a node with no name, returns
+ * an empty string.
+ * @since 8.4
+ */
+
+ public String getDisplayName();
+
+ /**
+ * Get the prefix of the name of the node. This is defined only for elements and attributes.
+ * If the node has no prefix, or for other kinds of node, returns a zero-length string.
+ * @return The prefix of the name of the node.
+ * @since 8.4
+ */
+
+ public String getPrefix();
+
+ /**
+ * Get the configuration used to build the tree containing this node.
+ * @return the Configuration
+ * @since 8.4
+ */
+
+ public Configuration getConfiguration();
+
+ /**
+ * Get the NamePool that holds the namecode for this node
+ * @return the namepool
+ * @since 8.4
+ */
+
+ public NamePool getNamePool();
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as an integer;
+ * this is the fingerprint of the name of the type, as defined in the name pool. Anonymous types
+ * are given a system-defined name. The value of the type annotation can be used to retrieve the
+ * actual schema type definition using the method {@link Configuration#getSchemaType}.
+ * <p>
+ * The bit IS_DTD_TYPE (1<<30) will be set in the case of an attribute node if the type annotation
+ * is one of ID, IDREF, or IDREFS and this is derived from DTD rather than schema validation.
+ *
+ * @return the type annotation of the node, under the mask NamePool.FP_MASK, and optionally the
+ * bit setting IS_DTD_TYPE in the case of a DTD-derived ID or IDREF/S type (which is treated
+ * as untypedAtomic for the purposes of obtaining the typed value).
+ *
+ * <p>For elements and attributes, this is the type annotation as defined in XDM. For document
+ * nodes, it should be one of XS_UNTYPED if the document has not been validated, or XS_ANY_TYPE
+ * if validation has taken place (that is, if any node in the document has an annotation other than
+ * Untyped or UntypedAtomic).</p>
+ * @since 8.4. Refinement for document nodes introduced in 9.2
+ */
+
+ public int getTypeAnnotation();
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ *
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+
+ public SchemaType getSchemaType();
+
+
+ /**
+ * Bit setting in the returned type annotation indicating a DTD_derived type on an attribute node
+ */
+
+ public static int IS_DTD_TYPE = 1<<30;
+
+ /**
+ * Bit setting for use alongside a type annotation indicating that the is-nilled property is set
+ */
+
+ public static int IS_NILLED = 1<<29;
+
+
+ /**
+ * Get the typed value. This will either be a single AtomicValue or a value whose items are
+ * atomic values.
+ * @since 8.5. Changed in 9.5 to return the new type AtomicSequence.
+ * @throws XPathException if the node has no typed value, for example if
+ * it is an element node with element-only content
+ */
+
+ public AtomicSequence atomize() throws XPathException;
+
+ /**
+ * Get the NodeInfo object representing the parent of this node
+ *
+ * @return the parent of this node; null if this node has no parent
+ * @since 8.4
+ */
+
+ /*@Nullable*/ public NodeInfo getParent();
+
+ /**
+ * Return an iteration over all the nodes reached by the given axis from this node
+ *
+ * @param axisNumber an integer identifying the axis; one of the constants
+ * defined in class {@link AxisInfo}
+ * @return an AxisIterator that delivers the nodes reached by the axis in
+ * turn. The nodes are returned in axis order (document order for a forwards
+ * axis, reverse document order for a reverse axis).
+ * @see AxisInfo
+ * @throws UnsupportedOperationException if the namespace axis is
+ * requested and this axis is not supported for this implementation.
+ * @since 8.4
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber);
+
+
+ /**
+ * Return an iteration over all the nodes reached by the given axis from this node
+ * that match a given NodeTest
+ *
+ * @exception UnsupportedOperationException if the namespace axis is
+ * requested and this axis is not supported for this implementation.
+ * @param axisNumber an integer identifying the axis; one of the constants
+ * defined in class {@link AxisInfo}
+ * @param nodeTest A condition to be satisfied by the returned nodes; nodes
+ * that do not satisfy this condition are not included in the result
+ * @return an AxisIterator that delivers the nodes reached by the axis in
+ * turn. The nodes are returned in axis order (document order for a forwards
+ * axis, reverse document order for a reverse axis).
+ * @see AxisInfo
+ * @since 8.4
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest);
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+
+ /*@Nullable*/ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local);
+
+ /**
+ * Get the root node of the tree containing this node
+ *
+ * @return the NodeInfo representing the top-level ancestor of this node.
+ * This will not necessarily be a document node. If this node has no parent,
+ * then the method returns this node.
+ * @since 8.4
+ */
+
+ public NodeInfo getRoot();
+
+ /**
+ * Get the root node, if it is a document node.
+ *
+ * @return the DocumentInfo representing the containing document. If this
+ * node is part of a tree that does not have a document node as its
+ * root, returns null.
+ * @since 8.4
+ */
+
+ /*@Nullable*/ public DocumentInfo getDocumentRoot();
+
+ /**
+ * Determine whether the node has any children.
+ * <p>
+ * Note: the result is equivalent to <br />
+ * <code>iterateAxis(Axis.CHILD).next() != null</code>
+ *
+ * @return True if the node has one or more children
+ * @since 8.4
+ */
+
+ public boolean hasChildNodes();
+
+ /**
+ * Construct a character string that uniquely identifies this node.
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ *
+ * @param buffer a buffer which will be updated to hold a string
+ * that uniquely identifies this node, across all documents.
+ * @since 8.7
+ * <p>Changed in Saxon 8.7 to generate the ID value in a client-supplied buffer</p>
+ */
+
+ public void generateId(FastStringBuffer buffer);
+
+ /**
+ * Get the document number of the document containing this node. For a free-standing
+ * orphan node, just return the hashcode.
+ * @return the document number of the document containing this node
+ * @since 8.4
+ */
+
+ public long getDocumentNumber();
+
+ /**
+ * Copy this node to a given Receiver.
+ * <p>
+ * This method is primarily for internal use. It should not be considered a stable
+ * part of the Saxon API.
+ *
+ * @param out the Receiver to which the node should be copied. It is the caller's
+ * responsibility to ensure that this Receiver is open before the method is called
+ * (or that it is self-opening), and that it is closed after use.
+ * @param copyOptions a selection of the options defined in {@link net.sf.saxon.om.CopyOptions}
+ * @param locationId If non-zero, identifies the location of the instruction
+* that requested this copy. If zero, indicates that the location information
+ * @throws XPathException if any downstream error occurs
+ */
+
+ public void copy(Receiver out, int copyOptions, int locationId) throws XPathException;
+
+ /**
+ * Don't copy any namespace nodes.
+ */
+
+ public static final int NO_NAMESPACES = 0;
+
+ /**
+ * Copy namespaces declared (or undeclared) on this element, but not namespaces inherited from a parent element
+ */
+ public static final int LOCAL_NAMESPACES = 1;
+
+ /**
+ * Copy all in-scope namespaces
+ */
+ public static final int ALL_NAMESPACES = 2;
+
+ /**
+ * Get all namespace declarations and undeclarations defined on this element.
+ * <p>
+ * This method is intended primarily for internal use. User applications needing
+ * information about the namespace context of a node should use <code>iterateAxis(Axis.NAMESPACE)</code>.
+ * (However, not all implementations support the namespace axis, whereas all implementations are
+ * required to support this method.)
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p>
+ * For a node other than an element, the method returns null.</p>
+ */
+
+ public NamespaceBinding[] getDeclaredNamespaces(/*@Nullable*/ NamespaceBinding[] buffer);
+
+ /**
+ * Determine whether this node has the is-id property
+ * @return true if the node is an ID
+ */
+
+ public boolean isId();
+
+ /**
+ * Determine whether this node has the is-idref property
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref();
+
+ /**
+ * Determine whether the node has the is-nilled property
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled();
+
+}
+
diff --git a/sf/saxon/om/NodeName.java b/sf/saxon/om/NodeName.java
new file mode 100644
index 0000000..a394d7f
--- /dev/null
+++ b/sf/saxon/om/NodeName.java
@@ -0,0 +1,114 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * This interface represents a node name. Actually it represents any QName, but it is intended for use
+ * as an element or attribute name. Various implementations are available.
+ *
+ * <p>An important requirement of an implementation of this interface is that the hashCode() and
+ * equals() methods are implemented correctly, so that any two node names compare equal if and only
+ * if the local name and namespace URI parts are equal under Unicode codepoint comparison. To ensure this,
+ * the hashCode must be computed using an algorithm equivalent to that used by the implementation class
+ * {@link FingerprintedQName}</p>
+ */
+
+public interface NodeName extends IdentityComparable{
+
+ /**
+ * Get the prefix of the QName.
+ * @return the prefix. Returns the empty string if the name is unprefixed.
+ */
+
+ String getPrefix();
+
+ /**
+ * Get the namespace URI of the QName.
+ * @return the URI. Returns the empty string to represent the no-namespace
+ */
+
+ String getURI();
+
+ /**
+ * Get the local part of the QName
+ * @return the local part of the QName
+ */
+
+ String getLocalPart();
+
+ /**
+ * Get the display name, that is the lexical QName in the form [prefix:]local-part
+ * @return the lexical QName
+ */
+
+ String getDisplayName();
+
+ /**
+ * Get the name in the form of a StructuredQName
+ * @return the name in the form of a StructuredQName
+ */
+
+ StructuredQName getStructuredQName();
+
+ /**
+ * Test whether this name is in the same namespace as another name
+ * @param other the other name
+ * @return true if the two names are in the same namespace
+ */
+
+ boolean isInSameNamespace(NodeName other);
+
+ /**
+ * Test whether this name is in a given namespace
+ * @param ns the namespace to be tested against
+ * @return true if the name is in the specified namespace
+ */
+
+ boolean isInNamespace(String ns);
+
+ /**
+ * Get a {@link NamespaceBinding} whose (prefix, uri) pair are the prefix and URI of this
+ * node name
+ * @return the corresponding NamespaceBinding
+ */
+
+ NamespaceBinding getNamespaceBinding();
+
+ /**
+ * Ask whether this node name representation has a known namecode and fingerprint
+ * @return true if the methods getFingerprint() and getNameCode() will
+ * return a result other than -1
+ */
+
+ boolean hasFingerprint();
+
+ /**
+ * Get the fingerprint of this name if known. This method should not to any work to allocate
+ * a fingerprint if none is already available
+ * @return the fingerprint if known; otherwise -1
+ */
+
+ int getFingerprint();
+
+ /**
+ * Get the nameCode of this name if known. This method should not to any work to allocate
+ * a nameCode if none is already available
+ * @return the nameCode if known; otherwise -1
+ */
+
+ int getNameCode();
+
+ /**
+ * Get the nameCode of this name, allocating a new code from the namepool if necessary
+ * @param namePool the NamePool used to allocate the name
+ * @return a nameCode for this name, newly allocated if necessary
+ */
+
+ int allocateNameCode(NamePool namePool);
+
+}
diff --git a/sf/saxon/om/NotationSet.java b/sf/saxon/om/NotationSet.java
new file mode 100644
index 0000000..66843a8
--- /dev/null
+++ b/sf/saxon/om/NotationSet.java
@@ -0,0 +1,23 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * A set of declared notations (in the sense of XSD xs:notation)
+ */
+public interface NotationSet {
+
+ /**
+ * Ask whether a given notation name is present in this set of notations
+ * @param uri the URI part of the notation name
+ * @param local the local part of the notation name
+ * @return true if the notation name is present
+ */
+ public boolean isDeclaredNotation(String uri, String local);
+}
+
diff --git a/sf/saxon/om/QNameException.java b/sf/saxon/om/QNameException.java
new file mode 100644
index 0000000..02c8cfb
--- /dev/null
+++ b/sf/saxon/om/QNameException.java
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+/**
+ * A QNameException represents an error condition whereby a QName (for example a variable
+ * name or template name) is malformed
+ */
+
+public class QNameException extends Exception {
+
+ String message;
+
+ public QNameException (String message) {
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+}
diff --git a/sf/saxon/om/SelectedElementsSpaceStrippingRule.java b/sf/saxon/om/SelectedElementsSpaceStrippingRule.java
new file mode 100644
index 0000000..48cc840
--- /dev/null
+++ b/sf/saxon/om/SelectedElementsSpaceStrippingRule.java
@@ -0,0 +1,230 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+
+import net.sf.saxon.event.Stripper;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.style.StylesheetModule;
+import net.sf.saxon.trans.Rule;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+
+import java.util.HashMap;
+
+/**
+ * A whitespace stripping rule that strips elected elements unless xml:space indicates that whitespace
+ * should be preserved.
+ */
+
+public class SelectedElementsSpaceStrippingRule implements SpaceStrippingRule {
+
+ // This is a cut-down version of the Mode class, which until 9.3 was used for the job, even though
+ // it is over-engineered for the task.
+
+ private Rule anyElementRule = null;
+ private Rule unnamedElementRuleChain = null;
+ private HashMap<NodeName, Rule> namedElementRules = new HashMap<NodeName, Rule>(32);
+ private int sequence = 0;
+ private boolean rejectDuplicates; // in XSLT 3.0, duplicate conflicting rules are a static error
+
+ /**
+ * Create the ruleset
+ */
+
+ public SelectedElementsSpaceStrippingRule(boolean rejectDuplicates) {
+ this.rejectDuplicates = rejectDuplicates;
+ }
+
+ /**
+ * Decide whether an element is in the set of white-space preserving element names
+ *
+ *
+ * @param fingerprint Identifies the name of the element whose whitespace is to
+ * be preserved
+ * @return ALWAYS_PRESERVE if the element is in the set of white-space preserving
+ * element types, ALWAYS_STRIP if the element is to be stripped regardless of the
+ * xml:space setting, and STRIP_DEFAULT otherwise
+ */
+
+ public byte isSpacePreserving(NodeName fingerprint) throws XPathException {
+
+ Rule rule = getRule(fingerprint);
+
+ if (rule==null) {
+ return Stripper.ALWAYS_PRESERVE;
+ }
+
+ return (rule.getAction() == Stripper.PRESERVE ? Stripper.ALWAYS_PRESERVE : Stripper.STRIP_DEFAULT);
+
+ }
+
+ /**
+ * Add a rule
+ *
+ * @param test a NodeTest (*, *:local, prefix:*, or QName)
+ * @param action StripRuleTarget.STRIP or StripRuleTarget.PRESERVE
+ * @param module the stylesheet module containing the rule
+ * @param lineNumber the line where the strip-space or preserve-space instruction appears
+ * @throws XPathException if this rule is a conflicting duplicate
+ */
+
+ public void addRule(NodeTest test, Stripper.StripRuleTarget action,
+ StylesheetModule module, int lineNumber) throws XPathException {
+
+ // for fast lookup, we maintain one list for each element name for patterns that can only
+ // match elements of a given name, one list for each node type for patterns that can only
+ // match one kind of non-element node, and one generic list.
+ // Each list is sorted in precedence/priority order so we find the highest-priority rule first
+
+ int precedence = module.getPrecedence();
+ int minImportPrecedence = module.getMinImportPrecedence();
+ double priority = test.getDefaultPriority();
+ Pattern pattern = new ItemTypePattern(test);
+ pattern.setSystemId(module.getSourceElement().getSystemId());
+ pattern.setLineNumber(lineNumber);
+ Rule newRule = new Rule(pattern, action, precedence, minImportPrecedence, priority, sequence++);
+ newRule.setRank((precedence << 16) + sequence);
+ if (test instanceof NodeKindTest) {
+ newRule.setAlwaysMatches(true);
+ anyElementRule = addRuleToList(newRule, anyElementRule, true);
+ } else if (test instanceof NameTest) {
+ newRule.setAlwaysMatches(true);
+ int fp = test.getFingerprint();
+ CodedName key = new CodedName(fp, ((NameTest) test).getNamePool());
+ Rule chain = namedElementRules.get(key);
+ namedElementRules.put(key, addRuleToList(newRule, chain, true));
+ } else {
+ unnamedElementRuleChain = addRuleToList(newRule, unnamedElementRuleChain, false);
+ }
+
+ }
+
+ /**
+ * Insert a new rule into this list before others of the same precedence
+ * (we rely on the fact that all rules in a list have the same priority)
+ * @param newRule the new rule to be added into the list
+ * @param list the Rule at the head of the list, or null if the list is empty
+ * @param dropRemainder if only one rule needs to be retained
+ * @return the new head of the list (which might be the old head, or the new rule if it
+ * was inserted at the start)
+ */
+
+
+ private Rule addRuleToList(Rule newRule, Rule list, boolean dropRemainder) throws XPathException {
+ if (list == null) {
+ return newRule;
+ }
+ int precedence = newRule.getPrecedence();
+ Rule rule = list;
+ Rule prev = null;
+ while (rule != null) {
+ if (rule.getPrecedence() <= precedence) {
+ if (rejectDuplicates && rule.getPrecedence() == precedence && !rule.getAction().equals(newRule.getAction())) {
+ throw new XPathException(
+ "There are conflicting xsl:strip-space and xsl:preserve-space declarations for " +
+ rule.getPattern() + " at the same import precedence", "XTSE0270");
+ }
+ newRule.setNext(dropRemainder ? null : rule);
+ if (prev == null) {
+ return newRule;
+ } else {
+ prev.setNext(newRule);
+ }
+ break;
+ } else {
+ prev = rule;
+ rule = rule.getNext();
+ }
+ }
+ if (rule == null) {
+ prev.setNext(newRule);
+ newRule.setNext(null);
+ }
+ return list;
+ }
+
+ /**
+ * Get the rule corresponding to a given element node, by finding the best pattern match.
+ *
+ * @param nodeName the name of the element node to be matched
+ * @return the best matching rule, if any (otherwise null).
+ */
+
+
+ /*@Nullable*/ public Rule getRule(NodeName nodeName) {
+
+ // search the specific list for this node type / node name
+
+ Rule bestRule = namedElementRules.get(nodeName);
+
+ // search the list for *:local and prefix:* node tests
+
+ if (unnamedElementRuleChain != null) {
+ bestRule = searchRuleChain(nodeName, bestRule, unnamedElementRuleChain);
+ }
+
+ // See if there is a "*" rule matching all elements
+
+ if (anyElementRule != null) {
+ bestRule = searchRuleChain(nodeName, bestRule, anyElementRule);
+ }
+
+ return bestRule;
+ }
+
+ /**
+ * Search a chain of rules
+ * @param nodeName the name of the element node being matched
+ * @param bestRule the best rule so far in terms of precedence and priority (may be null)
+ * @param head the rule at the head of the chain to be searched
+ * @return the best match rule found in the chain, or the previous best rule, or null
+ */
+
+ private Rule searchRuleChain(NodeName nodeName, Rule bestRule, Rule head) {
+ while (head != null) {
+ if (bestRule != null) {
+ int rank = head.compareRank(bestRule);
+ if (rank < 0) {
+ // if we already have a match, and the precedence or priority of this
+ // rule is lower, quit the search
+ break;
+ } else if (rank == 0) {
+ // this rule has the same precedence and priority as the matching rule already found
+ if (head.isAlwaysMatches() ||
+ ((NodeTest)head.getPattern().getItemType()).matches(Type.ELEMENT, nodeName, -1)) {
+ // reportAmbiguity(bestRule, head);
+ // We no longer report the recoverable error XTRE0270, we always
+ // take the recovery action.
+ // choose whichever one comes last (assuming the error wasn't fatal)
+ bestRule = head;
+ break;
+ } else {
+ // keep searching other rules of the same precedence and priority
+ }
+ } else {
+ // this rule has higher rank than the matching rule already found
+ if (head.isAlwaysMatches() ||
+ ((NodeTest)head.getPattern().getItemType()).matches(Type.ELEMENT, nodeName, -1)) {
+ bestRule = head;
+ }
+ }
+ } else if (head.isAlwaysMatches() ||
+ ((NodeTest)head.getPattern().getItemType()).matches(Type.ELEMENT, nodeName, -1)) {
+ bestRule = head;
+ break;
+ }
+ head = head.getNext();
+ }
+ return bestRule;
+ }
+
+
+
+}
+
diff --git a/sf/saxon/om/Sequence.java b/sf/saxon/om/Sequence.java
new file mode 100644
index 0000000..4dab793
--- /dev/null
+++ b/sf/saxon/om/Sequence.java
@@ -0,0 +1,47 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This interface represents an XDM Value, that is, a sequence of items.
+ *
+ * Note that different implementations of Sequence might have very different
+ * With some sequences, calling iterate() may trigger evaluation of the logic
+ * that computes the sequence, and calling iterate() again may cause re-evaluation.
+ *
+ * Users should avoid assuming that a sequence of length one will always
+ * be represented as an instance of Item. If you are confident that the sequence
+ * will be of length one, call the head() function to get the first item.
+ *
+ * @since 9.5
+ */
+public interface Sequence {
+
+ /**
+ * Get the first item in the sequence.
+ * @return the first item in the sequence if there is one, or null if the sequence
+ * is empty
+ * @throws XPathException in the situation where the sequence is evaluated lazily, and
+ * evaluation of the first item causes a dynamic error.
+ */
+
+ public Item head() throws XPathException;
+
+ /**
+ * Get an iterator
+ * performance characteristics, though all should exhibit the same behaviour.over all the items in the sequence
+ * @return an iterator over all the items
+ * @throws XPathException in the situation where the sequence is evaluated lazily, and
+ * constructing an iterator over the items causes a dynamic error.
+ */
+
+ public SequenceIterator<? extends Item> iterate() throws XPathException;
+}
+
diff --git a/sf/saxon/om/SequenceIterator.java b/sf/saxon/om/SequenceIterator.java
new file mode 100644
index 0000000..f01bf48
--- /dev/null
+++ b/sf/saxon/om/SequenceIterator.java
@@ -0,0 +1,166 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A SequenceIterator is used to iterate over any XPath 2 sequence (of values or nodes).
+ * To get the next item in a sequence, call next(); if this returns null, you've
+ * reached the end of the sequence.
+ * <p>
+ * A SequenceIterator keeps track of the current Item and the current position.
+ * The objects returned by the SequenceIterator will always be either nodes
+ * (class NodeInfo) or singleton values (class AtomicValue): these are represented
+ * collectively by the interface Item.
+ * <p>
+ * This interface forms part of the public Saxon API. The JavaDoc "since" flag is used from
+ * release 8.4 onwards to indicate methods that are considered to be a stable part
+ * of the API. Methods without a "since" flag should not be regarded as a stable part
+ * of the API.
+ * <p>
+ * Note that the stability of this interface applies to classes that use the interface,
+ * not to classes that implement it. The interface may be extended in future to add new methods.
+ *
+ * @author Michael H. Kay
+ * @since 8.4
+ */
+
+public interface SequenceIterator<T extends Item> {
+
+ /**
+ * Get the next item in the sequence. This method changes the state of the
+ * iterator, in particular it affects the result of subsequent calls of
+ * position() and current().
+ * @throws XPathException if an error occurs retrieving the next item
+ * @return the next item, or null if there are no more items. Once a call
+ * on next() has returned null, no further calls should be made. The preferred
+ * action for an iterator if subsequent calls on next() are made is to return
+ * null again, and all implementations within Saxon follow this rule.
+ * @since 8.4
+ */
+
+ /*@Nullable*/ public T next() throws XPathException;
+
+ /**
+ * Get the current value in the sequence (the one returned by the
+ * most recent call on next()). This will be null before the first
+ * call of next(). This method does not change the state of the iterator.
+ *
+ * @return the current item, the one most recently returned by a call on
+ * next(). Returns null if next() has not been called, or if the end
+ * of the sequence has been reached.
+ * @since 8.4
+ */
+
+ /*@Nullable*/ public T current();
+
+ /**
+ * Get the current position. This will usually be zero before the first call
+ * on next(), otherwise it will be the number of times that next() has
+ * been called. Once next() has returned null, the preferred action is
+ * for subsequent calls on position() to return -1, but not all existing
+ * implementations follow this practice. (In particular, the EmptyIterator
+ * is stateless, and always returns 0 as the value of position(), whether
+ * or not next() has been called.)
+ * <p>
+ * This method does not change the state of the iterator.
+ *
+ * @return the current position, the position of the item returned by the
+ * most recent call of next(). This is 1 after next() has been successfully
+ * called once, 2 after it has been called twice, and so on. If next() has
+ * never been called, the method returns zero. If the end of the sequence
+ * has been reached, the value returned will always be <= 0; the preferred
+ * value is -1.
+ *
+ * @since 8.4
+ */
+
+ public int position();
+
+ /**
+ * Close the iterator. This indicates to the supplier of the data that the client
+ * does not require any more items to be delivered by the iterator. This may enable the
+ * supplier to release resources. After calling close(), no further calls on the
+ * iterator should be made; if further calls are made, the effect of such calls is undefined.
+ *
+ * <p>(Currently, closing an iterator is important only when the data is being "pushed" in
+ * another thread. Closing the iterator terminates that thread and means that it needs to do
+ * no additional work. Indeed, failing to close the iterator may cause the push thread to hang
+ * waiting for the buffer to be emptied.)</p>
+ *
+ * @since 9.1
+ */
+
+ public void close();
+
+ /**
+ * Get another SequenceIterator that iterates over the same items as the original,
+ * but which is repositioned at the start of the sequence.
+ *
+ * <p>This method allows access to all the items in the sequence without disturbing the
+ * current position of the iterator. Internally, its main use is in evaluating the last()
+ * function.</p>
+ *
+ * <p>This method does not change the state of the iterator.</p>
+ *
+ * <p>Some implementations of this method may regenerate the input sequence, creating
+ * new nodes with different identity from the original. This is not recommended, but is
+ * hard to prevent. This causes no problem for the primary usage of this method to support
+ * the last() function, but it has been known to cause trouble in other situations.</p>
+ *
+ * @exception XPathException if any error occurs
+ * @return a SequenceIterator that iterates over the same items,
+ * positioned before the first item
+ * @since 8.4
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<T> getAnother() throws XPathException;
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ * @since 8.6
+ */
+
+ public int getProperties();
+
+ /**
+ * Property value: the iterator is "grounded". This means that (a) the
+ * iterator must be an instance of {@link net.sf.saxon.tree.iter.GroundedIterator}, and (b) the
+ * implementation of the materialize() method must be efficient (in particular,
+ * it should not involve the creation of new objects)
+ */
+
+ public static final int GROUNDED = 1;
+
+ /**
+ * Property value: the iterator knows the number of items that it will deliver.
+ * This means that (a) the iterator must be an instance of {@link net.sf.saxon.expr.LastPositionFinder},
+ * and (b) the implementation of the getLastPosition() method must be efficient (in particular,
+ * it should take constant time, rather than time proportional to the length of the sequence)
+ */
+
+ public static final int LAST_POSITION_FINDER = 1<<1;
+
+ /**
+ * Property value: the iterator knows whether there are more items still to come. This means
+ * that (a) the iterator must be an instance of {@link net.sf.saxon.tree.iter.LookaheadIterator}, and (b) the
+ * implementation of the hasNext() method must be efficient (more efficient than the client doing
+ * it)
+ */
+
+ public static final int LOOKAHEAD = 1<<2;
+
+}
+
diff --git a/sf/saxon/om/SequenceTool.java b/sf/saxon/om/SequenceTool.java
new file mode 100644
index 0000000..9472c92
--- /dev/null
+++ b/sf/saxon/om/SequenceTool.java
@@ -0,0 +1,313 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.functions.Count;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.wrapper.VirtualNode;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+/**
+ * Utility class for manipulating sequences. Some of these methods should be regarded
+ * as temporary scaffolding while the model is in transition.
+ */
+public class SequenceTool {
+
+ /**
+ * Constant returned by compareTo() method to indicate an indeterminate ordering between two values
+ */
+
+ public static final int INDETERMINATE_ORDERING = Integer.MIN_VALUE;
+
+ /**
+ * Produce a GroundedValue containing the same values as a supplied sequence.
+ * @param sequence the supplied sequence
+ * @return a GroundedValue containing the same items
+ * @throws XPathException
+ */
+
+ public static GroundedValue toGroundedValue(Sequence sequence) throws XPathException {
+ if (sequence instanceof GroundedValue) {
+ return (GroundedValue)sequence;
+ } else {
+ return new SequenceExtent(sequence.iterate()).reduce();
+ }
+ }
+
+ /**
+ * Produce a GroundedValue containing the same values as a supplied sequence.
+ * @param iterator the supplied sequence. The iterator may or may not be consumed as a result of
+ * passing it to this method.
+ * @return a GroundedValue containing the same items
+ * @throws XPathException if a failure occurs reading the input iterator
+ */
+
+ public static GroundedValue toGroundedValue(SequenceIterator iterator) throws XPathException {
+ if ((iterator.getProperties() & SequenceIterator.GROUNDED) != 0) {
+ return ((GroundedIterator)iterator).materialize();
+ } else {
+ return new SequenceExtent(iterator).reduce();
+ }
+ }
+
+ /**
+ * Produce a Sequence containing the same values as a supplied sequence; the input is
+ * read progressively as required, and saved in a buffer as it is read in case it is needed
+ * again. But if the iterator is already backed by a grounded value, we return an iterator
+ * over that value.
+ * @param iterator the supplied sequence. The iterator may or may not be consumed as a result of
+ * passing it to this method.
+ * @return a GroundedValue containing the same items
+ * @throws XPathException if a failure occurs reading the input iterator
+ */
+
+ public static Sequence toMemoSequence(SequenceIterator iterator) throws XPathException {
+ if ((iterator.getProperties() & SequenceIterator.GROUNDED) != 0) {
+ return ((GroundedIterator)iterator).materialize();
+ } else {
+ return new MemoSequence(iterator);
+ }
+ }
+
+
+ /**
+ * Construct a sequence capable of returning the same items as an iterator,
+ * without incurring the cost of evaluating the iterator and storing all
+ * the items.
+ * @param iterator the supplied sequence. The iterator may or may not be consumed as a result of
+ * passing it to this method.
+ * @return a Sequence containing the same items as the supplied iterator
+ * @throws XPathException if a failure occurs reading the input iterator
+ */
+
+ public static Sequence toLazySequence(SequenceIterator iterator) throws XPathException {
+ if ((iterator.getProperties() & SequenceIterator.GROUNDED) != 0) {
+ return ((GroundedIterator)iterator).materialize();
+ } else {
+ return new LazySequence(iterator);
+ }
+ }
+
+ /**
+ * Get the length of a sequence (the number of items it contains)
+ * @param sequence the sequence
+ * @return the length in items
+ * @throws XPathException
+ */
+
+ public static int getLength(Sequence sequence) throws XPathException {
+ if (sequence instanceof Item) {
+ return 1;
+ }
+ if (sequence instanceof GroundedValue) {
+ return ((GroundedValue)sequence).getLength();
+ }
+ return Count.count(sequence.iterate());
+ }
+
+ /**
+ * Get the item at a given offset in a sequence. Uses zero-base indexing
+ * @param sequence the input sequence
+ * @param index the 0-based subscript
+ * @return the n'th item if it exists, or null otherwise
+ */
+
+ public static Item itemAt(Sequence sequence, int index) throws XPathException {
+ if (sequence instanceof Item && index==0) {
+ return (Item)sequence;
+ }
+ return toGroundedValue(sequence).itemAt(index);
+ }
+
+ /**
+ * Static method to make an Item from a Value
+ * @param sequence the value to be converted
+ * @return null if the value is an empty sequence; or the only item in the value
+ * if it is a singleton sequence
+ * @throws net.sf.saxon.trans.XPathException if the supplied Sequence contains multiple items
+ */
+
+ /*@Nullable*/
+ public static Item asItem(/*@NotNull*/ Sequence sequence) throws XPathException {
+ if (sequence instanceof Item) {
+ return (Item)sequence;
+ }
+ SequenceIterator iter = sequence.iterate();
+ Item first = iter.next();
+ if (first == null) {
+ return null;
+ }
+ if (iter.next() != null) {
+ throw new XPathException("Sequence contains more than one item");
+ }
+ return first;
+ }
+
+ /**
+ * Convert an XPath value to a Java object.
+ * An atomic value is returned as an instance
+ * of the best available Java class. If the item is a node, the node is "unwrapped",
+ * to return the underlying node in the original model (which might be, for example,
+ * a DOM or JDOM node).
+ * @param item the item to be converted
+ * @return the value after conversion
+ * @throws net.sf.saxon.trans.XPathException if an error occurs: for example, if the XPath value is
+ * an integer and is too big to fit in a Java long
+ */
+
+ public static Object convertToJava(/*@NotNull*/ Item item) throws XPathException {
+ if (item instanceof NodeInfo) {
+ Object node = item;
+ while (node instanceof VirtualNode) {
+ // strip off any layers of wrapping
+ node = ((VirtualNode)node).getRealNode();
+ }
+ return node;
+ } else if (item instanceof FunctionItem) {
+ return item;
+ } else if (item instanceof ObjectValue) {
+ return ((ObjectValue)item).getObject();
+ } else {
+ AtomicValue value = (AtomicValue)item;
+ switch (value.getItemType().getPrimitiveType()) {
+ case StandardNames.XS_STRING:
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_ANY_URI:
+ case StandardNames.XS_DURATION:
+ return value.getStringValue();
+ case StandardNames.XS_BOOLEAN:
+ return (((BooleanValue)value).getBooleanValue() ? Boolean.TRUE : Boolean.FALSE );
+ case StandardNames.XS_DECIMAL:
+ return ((DecimalValue)value).getDecimalValue();
+ case StandardNames.XS_INTEGER:
+ return ((NumericValue) value).longValue();
+ case StandardNames.XS_DOUBLE:
+ return ((DoubleValue) value).getDoubleValue();
+ case StandardNames.XS_FLOAT:
+ return ((FloatValue) value).getFloatValue();
+ case StandardNames.XS_DATE_TIME:
+ return ((DateTimeValue)value).getCalendar().getTime();
+ case StandardNames.XS_DATE:
+ return ((DateValue)value).getCalendar().getTime();
+ case StandardNames.XS_TIME:
+ return value.getStringValue();
+ case StandardNames.XS_BASE64_BINARY:
+ return ((Base64BinaryValue)value).getBinaryValue();
+ case StandardNames.XS_HEX_BINARY:
+ return ((HexBinaryValue)value).getBinaryValue();
+ default:
+ return item;
+ }
+ }
+ }
+
+ public static String getStringValue(Sequence sequence) throws XPathException {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ SequenceIterator iter = sequence.iterate();
+ boolean first = true;
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (first) {
+ first = false;
+ } else {
+ fsb.append(' ');
+ }
+ fsb.append(item.getStringValueCS());
+ }
+ return fsb.toString();
+ }
+
+ public static ItemType getItemType(Sequence sequence, TypeHierarchy th) {
+ if (sequence instanceof Item) {
+ return Type.getItemType(((Item)sequence), th);
+ } else if (sequence instanceof EmptySequence) {
+ return ErrorType.getInstance();
+ } else if (sequence instanceof GroundedValue) {
+ try {
+ ItemType type = null;
+ SequenceIterator iter = sequence.iterate();
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (type == null) {
+ type = Type.getItemType(item, th);
+ } else {
+ type = Type.getCommonSuperType(type, Type.getItemType(item, th), th);
+ }
+ if (type == AnyItemType.getInstance()) {
+ break;
+ }
+ }
+ return (type == null ? ErrorType.getInstance() : type);
+ } catch (XPathException err) {
+ return AnyItemType.getInstance();
+ }
+ } else {
+ return AnyItemType.getInstance();
+ }
+
+// it = Type.getItemType(value[start], th);
+// for (int i=start+1; i<end; i++) {
+// if (it == AnyItemType.getInstance()) {
+// // make a quick exit
+// return it;
+// }
+// it = Type.getCommonSuperType(it, Type.getItemType(value[i], th), th);
+// }
+ }
+
+ public static int getCardinality(Sequence sequence) {
+ if (sequence instanceof Item) {
+ return StaticProperty.EXACTLY_ONE;
+ }
+ if (sequence instanceof EmptySequence || sequence instanceof EmptyAtomicSequence) {
+ return StaticProperty.ALLOWS_ZERO;
+ }
+ try {
+ SequenceIterator iter = sequence.iterate();
+ Item item = iter.next();
+ if (item == null) {
+ return StaticProperty.ALLOWS_ZERO;
+ }
+ item = iter.next();
+ return (item == null ? StaticProperty.EXACTLY_ONE : StaticProperty.ALLOWS_ONE_OR_MORE);
+ } catch (XPathException err) {
+ return StaticProperty.ALLOWS_ONE_OR_MORE;
+ }
+ }
+
+ /**
+ * Process the value as an instruction, without returning any tail calls
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ * @throws net.sf.saxon.trans.XPathException if an error occurs (for example if the value is
+ * a closure that needs to be evaluated)
+ */
+
+ public static void process(Sequence value, XPathContext context, int locationId) throws XPathException {
+ SequenceIterator iter = value.iterate();
+ SequenceReceiver out = context.getReceiver();
+ while (true) {
+ Item it = iter.next();
+ if (it==null) break;
+ out.append(it, locationId, NodeInfo.ALL_NAMESPACES);
+ }
+ }
+}
+
diff --git a/sf/saxon/om/SingletonSet.java b/sf/saxon/om/SingletonSet.java
new file mode 100644
index 0000000..3421852
--- /dev/null
+++ b/sf/saxon/om/SingletonSet.java
@@ -0,0 +1,55 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * A set with exactly one (non-null) member
+ * @param <T> the type of the member
+ */
+public class SingletonSet<T> extends AbstractSet<T> {
+
+ final private T member;
+
+ public SingletonSet(T e) {
+ member = e;
+ }
+
+ public Iterator<T> iterator() {
+ return new Iterator<T>() {
+ private boolean more = true;
+ public boolean hasNext() {
+ return more;
+ }
+ public T next() {
+ if (more) {
+ more = false;
+ return member;
+ }
+ throw new NoSuchElementException();
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public int size() {
+ return 1;
+ }
+
+ public boolean contains(Object o) {
+ return o.equals(member);
+ }
+ }
+
+
+
diff --git a/sf/saxon/om/SpaceStrippingRule.java b/sf/saxon/om/SpaceStrippingRule.java
new file mode 100644
index 0000000..192dad5
--- /dev/null
+++ b/sf/saxon/om/SpaceStrippingRule.java
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Interface for deciding whether a particular element is to have whitespace text nodes stripped
+ */
+
+public interface SpaceStrippingRule {
+
+ /**
+ * Decide whether an element is in the set of white-space preserving element types
+ *
+ *
+ * @param nodeName Identifies the name of the element whose whitespace is to
+ * be preserved
+ * @return {@link net.sf.saxon.event.Stripper#ALWAYS_PRESERVE} if the element is in the set of white-space preserving
+ * element types, {@link net.sf.saxon.event.Stripper#ALWAYS_STRIP} if the element is to be stripped regardless of the
+ * xml:space setting, and {@link net.sf.saxon.event.Stripper#STRIP_DEFAULT} otherwise
+ * @throws net.sf.saxon.trans.XPathException if the rules are ambiguous and ambiguities are to be
+ * reported as errors
+ */
+
+ public abstract byte isSpacePreserving(NodeName nodeName) throws XPathException;
+
+}
+
diff --git a/sf/saxon/om/StandardNames.java b/sf/saxon/om/StandardNames.java
new file mode 100644
index 0000000..cd14855
--- /dev/null
+++ b/sf/saxon/om/StandardNames.java
@@ -0,0 +1,852 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.lib.NamespaceConstant;
+
+import java.util.HashMap;
+
+
+/**
+ * Well-known names used in XSLT processing. These names must all have
+ * fingerprints in the range 0-1023, to avoid clashing with codes allocated
+ * in a NamePool. We use the top three bits for the namespace, and the bottom
+ * seven bits for the local name.
+ * <p/>
+ * <p>Codes in the range 0-100 are used for standard node kinds such as ELEMENT,
+ * DOCUMENT, etc, and for built-in types such as ITEM and EMPTY.</p>
+ */
+
+public abstract class StandardNames {
+
+
+ private static final int DFLT_NS = 0;
+ private static final int XSL_NS = 1;
+ private static final int SAXON_NS = 2;
+ private static final int XML_NS = 3;
+ private static final int XS_NS = 4;
+ private static final int XSI_NS = 5;
+ private static final int SCM_NS = 6;
+
+ public static final int DFLT = 0; // 0
+ public static final int XSL = 128; // 128
+ public static final int SAXON = 128 * 2; // 256
+ public static final int XML = 128 * 3; // 384
+ public static final int XS = 128 * 4; // 512
+ public static final int XSI = 128 * 5; // 640
+ public static final int SCM = 128 * 6; // 768
+
+ public static final int XSL_ACCUMULATOR = XSL;
+ public static final int XSL_ACCUMULATOR_RULE = XSL + 1;
+ public static final int XSL_ANALYZE_STRING = XSL + 2;
+ public static final int XSL_APPLY_IMPORTS = XSL + 3;
+ public static final int XSL_APPLY_TEMPLATES = XSL + 4;
+ public static final int XSL_ASSERT = XSL + 5;
+ public static final int XSL_ATTRIBUTE = XSL + 6;
+ public static final int XSL_ATTRIBUTE_SET = XSL + 7;
+ public static final int XSL_BREAK = XSL + 8;
+ public static final int XSL_CALL_TEMPLATE = XSL + 9;
+ public static final int XSL_CATCH = XSL + 10;
+ public static final int XSL_CHARACTER_MAP = XSL + 11;
+ public static final int XSL_CHOOSE = XSL + 12;
+ public static final int XSL_COMMENT = XSL + 13;
+ public static final int XSL_COPY = XSL + 14;
+ public static final int XSL_COPY_OF = XSL + 15;
+ public static final int XSL_DECIMAL_FORMAT = XSL + 17;
+ public static final int XSL_DOCUMENT = XSL + 18;
+ public static final int XSL_ELEMENT = XSL + 19;
+ public static final int XSL_EVALUATE = XSL + 20;
+ public static final int XSL_FALLBACK = XSL + 22;
+ public static final int XSL_FOR_EACH = XSL + 23;
+ public static final int XSL_FORK = XSL + 24;
+ public static final int XSL_FOR_EACH_GROUP = XSL + 26;
+ public static final int XSL_FUNCTION = XSL + 27;
+ public static final int XSL_IF = XSL + 28;
+ public static final int XSL_IMPORT = XSL + 29;
+ public static final int XSL_IMPORT_SCHEMA = XSL + 30;
+ public static final int XSL_INCLUDE = XSL + 35;
+ public static final int XSL_ITERATE = XSL + 36;
+ public static final int XSL_KEY = XSL + 37;
+ public static final int XSL_MAP = XSL + 38;
+ public static final int XSL_MAP_ENTRY = XSL + 39;
+ public static final int XSL_MATCHING_SUBSTRING = XSL + 40;
+ public static final int XSL_MERGE = XSL + 41;
+ public static final int XSL_MERGE_ACTION = XSL + 42;
+ public static final int XSL_MERGE_KEY = XSL + 43;
+ public static final int XSL_MERGE_SOURCE = XSL + 44;
+ public static final int XSL_MESSAGE = XSL + 45;
+ public static final int XSL_MODE = XSL + 46;
+ public static final int XSL_NAMESPACE = XSL + 47;
+ public static final int XSL_NAMESPACE_ALIAS = XSL + 48;
+ public static final int XSL_NEXT_ITERATION = XSL + 49;
+ public static final int XSL_NEXT_MATCH = XSL + 50;
+ public static final int XSL_NON_MATCHING_SUBSTRING = XSL + 51;
+ public static final int XSL_NUMBER = XSL + 52;
+ public static final int XSL_OTHERWISE = XSL + 53;
+ public static final int XSL_ON_COMPLETION = XSL + 54;
+ public static final int XSL_OUTPUT = XSL + 55;
+ public static final int XSL_OUTPUT_CHARACTER = XSL + 56;
+ public static final int XSL_PARAM = XSL + 60;
+ public static final int XSL_PERFORM_SORT = XSL + 61;
+ public static final int XSL_PRESERVE_SPACE = XSL + 62;
+ public static final int XSL_PROCESSING_INSTRUCTION = XSL + 63;
+ public static final int XSL_RESULT_DOCUMENT = XSL + 64;
+ public static final int XSL_SEQUENCE = XSL + 65;
+ public static final int XSL_SORT = XSL + 66;
+ public static final int XSL_STREAM = XSL + 67;
+ public static final int XSL_STRIP_SPACE = XSL + 70;
+ public static final int XSL_STYLESHEET = XSL + 71;
+ public static final int XSL_TEMPLATE = XSL + 72;
+ public static final int XSL_TEXT = XSL + 73;
+ public static final int XSL_TRANSFORM = XSL + 74;
+ public static final int XSL_TRY = XSL + 75;
+ public static final int XSL_VALUE_OF = XSL + 76;
+ public static final int XSL_VARIABLE = XSL + 77;
+ public static final int XSL_WHEN = XSL + 78;
+ public static final int XSL_WITH_PARAM = XSL + 79;
+
+
+ public static final int XSL_DEFAULT_COLLATION = XSL + 100;
+ public static final int XSL_EXCLUDE_RESULT_PREFIXES = XSL + 101;
+ public static final int XSL_EXPAND_TEXT = XSL + 102;
+ public static final int XSL_EXTENSION_ELEMENT_PREFIXES = XSL + 103;
+ public static final int XSL_INHERIT_NAMESPACES = XSL + 104;
+ public static final int XSL_ON_EMPTY = XSL + 105;
+ public static final int XSL_TYPE = XSL + 106;
+ public static final int XSL_USE_ATTRIBUTE_SETS = XSL + 107;
+ public static final int XSL_USE_WHEN = XSL + 108;
+ public static final int XSL_VALIDATION = XSL + 109;
+ public static final int XSL_VERSION = XSL + 110;
+ public static final int XSL_XPATH_DEFAULT_NAMESPACE = XSL + 111;
+
+
+
+ public static final int SAXON_ASSIGN = SAXON + 1;
+ //public static final int SAXON_BINARY_OUTPUT = SAXON + 2;
+ public static final int SAXON_CALL_TEMPLATE = SAXON + 3;
+ public static final int SAXON_CATCH = SAXON + 4;
+ public static final int SAXON_COLLATION = SAXON + 5;
+ //public static final int SAXON_CONTINUE = SAXON + 6;
+ public static final int SAXON_DOCTYPE = SAXON + 7;
+ public static final int SAXON_ENTITY_REF = SAXON + 8;
+ //public static final int SAXON_FINALLY = SAXON + 9;
+ public static final int SAXON_IMPORT_QUERY = SAXON + 10;
+ //public static final int SAXON_ITERATE = SAXON + 11;
+ //public static final int SAXON_MODE = SAXON + 12;
+ public static final int SAXON_SCRIPT = SAXON + 13;
+ public static final int SAXON_TRY = SAXON + 14;
+ public static final int SAXON_WHILE = SAXON + 15;
+
+ // Schema extension elements
+ public static final int SAXON_PARAM = SAXON + 20;
+ public static final int SAXON_PREPROCESS = SAXON + 21;
+
+ private static final String SAXON_B = '{' + NamespaceConstant.SAXON + '}';
+ public static final String SAXON_ASSIGNABLE = SAXON_B + "assignable";
+ public static final String SAXON_ATTRIBUTE_ORDER = SAXON_B + "attribute-order";
+ public static final String SAXON_CHARACTER_REPRESENTATION = SAXON_B + "character-representation";
+ public static final String SAXON_DOUBLE_SPACE = SAXON_B + "double-space";
+ public static final String SAXON_EXPLAIN = SAXON_B + "explain";
+ public static final String SAXON_READ_ONCE = SAXON_B + "read-once";
+ public static final String SAXON_INDENT_SPACES = SAXON_B + "indent-spaces";
+ public static final String SAXON_LINE_LENGTH = SAXON_B + "line-length";
+ public static final String SAXON_NEXT_IN_CHAIN = SAXON_B + "next-in-chain";
+ public static final String SAXON_RECOGNIZE_BINARY = SAXON_B + "recognize-binary";
+ public static final String SAXON_REQUIRE_WELL_FORMED = SAXON_B + "require-well-formed";
+ public static final String SAXON_SUPPRESS_INDENTATION = SAXON_B + "suppress-indentation";
+ public static final String SAXON_MEMO_FUNCTION = SAXON_B + "memo-function";
+ public static final String SAXON_THREADS = SAXON_B + "threads";
+
+ // Not sure about this one...
+ public static final int SAXON_JAVA_LANG_OBJECT = SAXON + 29;
+
+ public static final int XML_BASE = XML + 1;
+ public static final int XML_SPACE = XML + 2;
+ public static final int XML_LANG = XML + 3;
+ public static final int XML_ID = XML + 4;
+ public static final int XML_LANG_TYPE = XML + 5;
+ public static final int XML_SPACE_TYPE = 6;
+
+ public static final NodeName XML_ID_NAME = new FingerprintedQName("xml", NamespaceConstant.XML, "id", XML_ID);
+
+
+ public static final String ALPHANUMERIC = "alphanumeric";
+ public static final String ARCHIVE = "archive";
+ public static final String AS = "as";
+ public static final String BYTE_ORDER_MARK = "byte-order-mark";
+ public static final String CASE_ORDER = "case-order";
+ public static final String CDATA_SECTION_ELEMENTS = "cdata-section-elements";
+ public static final String CHARACTER = "character";
+ public static final String CLASS = "class";
+ public static final String COLLATION = "collation";
+ public static final String COPY_NAMESPACES = "copy-namespaces";
+ public static final String COUNT = "count";
+ public static final String DATA_TYPE = "data-type";
+ public static final String DECIMAL_SEPARATOR = "decimal-separator";
+ public static final String DECOMPOSITION = "decomposition";
+ public static final String DEFAULT = "default";
+ public static final String DEFAULT_COLLATION = "default-collation";
+ public static final String DEFAULT_MODE = "default-mode";
+ public static final String DEFAULT_VALIDATION = "default-validation";
+ public static final String DIGIT = "digit";
+ public static final String DISABLE_OUTPUT_ESCAPING = "disable-output-escaping";
+ public static final String DOCTYPE_PUBLIC = "doctype-public";
+ public static final String DOCTYPE_SYSTEM = "doctype-system";
+ public static final String ELEMENTS = "elements";
+ public static final String ESCAPE_URI_ATTRIBUTES = "escape-uri-attributes";
+ public static final String ENCODING = "encoding";
+ public static final String EXCLUDE_RESULT_PREFIXES = "exclude-result-prefixes";
+ public static final String EXTENSION_ELEMENT_PREFIXES = "extension-element-prefixes";
+ public static final String FLAGS = "flags";
+ public static final String FORMAT = "format";
+ public static final String FROM = "from";
+ public static final String GROUP_ADJACENT = "group-adjacent";
+ public static final String GROUP_BY = "group-by";
+ public static final String GROUP_ENDING_WITH = "group-ending-with";
+ public static final String GROUP_STARTING_WITH = "group-starting-with";
+ public static final String GROUPING_SEPARATOR = "grouping-separator";
+ public static final String GROUPING_SIZE = "grouping-size";
+ public static final String HREF = "href";
+ public static final String HTML_VERSION = "html-version";
+ public static final String ID = "id";
+ public static final String IGNORE_CASE = "ignore-case";
+ public static final String IGNORE_MODIFIERS = "ignore-modifiers";
+ public static final String IGNORE_SYMBOLS = "ignore-symbols";
+ public static final String IGNORE_WIDTH = "ignore-width";
+ public static final String INCLUDE_CONTENT_TYPE = "include-content-type";
+ public static final String INDENT = "indent";
+ public static final String INFINITY = "infinity";
+ public static final String INHERIT_NAMESPACES = "inherit-namespaces";
+ public static final String INITIAL_VALUE = "initial-value";
+ public static final String INPUT_TYPE_ANNOTATIONS = "input-type-annotations";
+ public static final String LANG = "lang";
+ public static final String LANGUAGE = "language";
+ public static final String LETTER_VALUE = "letter-value";
+ public static final String LEVEL = "level";
+ public static final String MATCH = "match";
+ public static final String MEDIA_TYPE = "media-type";
+ public static final String METHOD = "method";
+ public static final String MINUS_SIGN = "minus-sign";
+ public static final String MODE = "mode";
+ public static final String NAME = "name";
+ public static final String NAMESPACE = "namespace";
+ public static final String NAN = "NaN";
+ public static final String NEW_VALUE = "new-value";
+ public static final String NORMALIZATION_FORM = "normalization-form";
+ public static final String OMIT_XML_DECLARATION = "omit-xml-declaration";
+ public static final String ON_MULTIPLE_MATCH = "on-multiple-match";
+ public static final String ON_NO_MATCH = "on-no-match";
+ public static final String ORDER = "order";
+ public static final String ORDINAL = "ordinal";
+ public static final String OUTPUT_VERSION = "output-version";
+ public static final String OVERRIDE = "override";
+ public static final String PATTERN_SEPARATOR = "pattern-separator";
+ public static final String PERCENT = "percent";
+ public static final String PER_MILLE = "per-mille";
+ public static final String PHASE = "phase";
+ public static final String POST_DESCENT = "post-descent";
+ public static final String PRIORITY = "priority";
+ public static final String REGEX = "regex";
+ public static final String REQUIRED = "required";
+ public static final String RESULT_PREFIX = "result-prefix";
+ public static final String RULES = "rules";
+ public static final String SCHEMA_LOCATION = "schema-location";
+ public static final String SELECT = "select";
+ public static final String SEPARATOR = "separator";
+ public static final String STABLE = "stable";
+ public static final String STANDALONE = "standalone";
+ public static final String START_AT = "start-at";
+ public static final String STATIC = "static";
+ public static final String STREAMABLE = "streamable";
+ public static final String STRENGTH = "strength";
+ public static final String STRING = "string";
+ public static final String STYLESHEET_PREFIX = "stylesheet-prefix";
+ public static final String TERMINATE = "terminate";
+ public static final String TEST = "test";
+ public static final String TUNNEL = "tunnel";
+ public static final String TYPE = "type";
+ public static final String UNDECLARE_PREFIXES = "undeclare-prefixes";
+ public static final String USE = "use";
+ public static final String USE_ATTRIBUTE_SETS = "use-attribute-sets";
+ public static final String USE_CHARACTER_MAPS = "use-character-maps";
+ public static final String USE_WHEN = "use-when";
+ public static final String VALIDATION = "validation";
+ public static final String VALUE = "value";
+ public static final String VERSION = "version";
+ public static final String WARNING_ON_MULTIPLE_MATCH = "warning-on-multiple-match";
+ public static final String WARNING_ON_NO_MATCH = "warning-on-no-match";
+ public static final String XPATH_DEFAULT_NAMESPACE = "xpath-default-namespace";
+ public static final String ZERO_DIGIT = "zero-digit";
+
+ public static final int XS_STRING = XS + 1;
+ public static final int XS_BOOLEAN = XS + 2;
+ public static final int XS_DECIMAL = XS + 3;
+ public static final int XS_FLOAT = XS + 4;
+ public static final int XS_DOUBLE = XS + 5;
+ public static final int XS_DURATION = XS + 6;
+ public static final int XS_DATE_TIME = XS + 7;
+ public static final int XS_TIME = XS + 8;
+ public static final int XS_DATE = XS + 9;
+ public static final int XS_G_YEAR_MONTH = XS + 10;
+ public static final int XS_G_YEAR = XS + 11;
+ public static final int XS_G_MONTH_DAY = XS + 12;
+ public static final int XS_G_DAY = XS + 13;
+ public static final int XS_G_MONTH = XS + 14;
+ public static final int XS_HEX_BINARY = XS + 15;
+ public static final int XS_BASE64_BINARY = XS + 16;
+ public static final int XS_ANY_URI = XS + 17;
+ public static final int XS_QNAME = XS + 18;
+ public static final int XS_NOTATION = XS + 19;
+ //public static final int XS_PRECISION_DECIMAL = XS + 20;
+ public static final int XS_INTEGER = XS + 21;
+
+ // Note that any type code <= XS_INTEGER is considered to represent a
+ // primitive type: see Type.isPrimitiveType()
+
+ public static final int XS_NON_POSITIVE_INTEGER = XS + 22;
+ public static final int XS_NEGATIVE_INTEGER = XS + 23;
+ public static final int XS_LONG = XS + 24;
+ public static final int XS_INT = XS + 25;
+ public static final int XS_SHORT = XS + 26;
+ public static final int XS_BYTE = XS + 27;
+ public static final int XS_NON_NEGATIVE_INTEGER = XS + 28;
+ public static final int XS_POSITIVE_INTEGER = XS + 29;
+ public static final int XS_UNSIGNED_LONG = XS + 30;
+ public static final int XS_UNSIGNED_INT = XS + 31;
+ public static final int XS_UNSIGNED_SHORT = XS + 32;
+ public static final int XS_UNSIGNED_BYTE = XS + 33;
+ public static final int XS_NORMALIZED_STRING = XS + 41;
+ public static final int XS_TOKEN = XS + 42;
+ public static final int XS_LANGUAGE = XS + 43;
+ public static final int XS_NMTOKEN = XS + 44;
+ public static final int XS_NMTOKENS = XS + 45; // NB: list type
+ public static final int XS_NAME = XS + 46;
+ public static final int XS_NCNAME = XS + 47;
+ public static final int XS_ID = XS + 48;
+ public static final int XS_IDREF = XS + 49;
+ public static final int XS_IDREFS = XS + 50; // NB: list type
+ public static final int XS_ENTITY = XS + 51;
+ public static final int XS_ENTITIES = XS + 52; // NB: list type
+ public static final int XS_DATE_TIME_STAMP = XS + 53;
+
+ public static final int XS_ANY_TYPE = XS + 60;
+ public static final int XS_ANY_SIMPLE_TYPE = XS + 61;
+
+ public static final int XS_INVALID_NAME = XS + 62;
+ public static final int XS_ERROR = XS + 63;
+
+
+
+ public static final int XS_ALL = XS + 64;
+ public static final int XS_ALTERNATIVE = XS + 65;
+ public static final int XS_ANNOTATION = XS + 66;
+ public static final int XS_ANY = XS + 67;
+ public static final int XS_ANY_ATTRIBUTE = XS + 68;
+ public static final int XS_APPINFO = XS + 69;
+ public static final int XS_ASSERT = XS + 70;
+ public static final int XS_ASSERTION = XS + 71;
+ public static final int XS_ATTRIBUTE = XS + 72;
+ public static final int XS_ATTRIBUTE_GROUP = XS + 73;
+ public static final int XS_CHOICE = XS + 74;
+ public static final int XS_COMPLEX_CONTENT = XS + 75;
+ public static final int XS_COMPLEX_TYPE = XS + 76;
+ public static final int XS_DEFAULT_OPEN_CONTENT = XS + 77;
+ public static final int XS_DOCUMENTATION = XS + 78;
+ public static final int XS_ELEMENT = XS + 79;
+ public static final int XS_ENUMERATION = XS + 80;
+ public static final int XS_EXTENSION = XS + 81;
+ public static final int XS_FIELD = XS + 82;
+ public static final int XS_FRACTION_DIGITS = XS + 83;
+ public static final int XS_GROUP = XS + 84;
+ public static final int XS_IMPORT = XS + 85;
+ public static final int XS_INCLUDE = XS + 86;
+ public static final int XS_KEY = XS + 87;
+ public static final int XS_KEYREF = XS + 88;
+ public static final int XS_LENGTH = XS + 89;
+ public static final int XS_LIST = XS + 90;
+ public static final int XS_MAX_EXCLUSIVE = XS + 91;
+ public static final int XS_MAX_INCLUSIVE = XS + 92;
+ public static final int XS_MAX_LENGTH = XS + 93;
+ public static final int XS_MAX_SCALE = XS + 94;
+ public static final int XS_MIN_EXCLUSIVE = XS + 95;
+ public static final int XS_MIN_INCLUSIVE = XS + 96;
+ public static final int XS_MIN_LENGTH = XS + 97;
+ public static final int XS_MIN_SCALE = XS + 98;
+ public static final int XS_notation = XS + 99;
+ public static final int XS_OPEN_CONTENT = XS + 100;
+ public static final int XS_OVERRIDE = XS + 101;
+ public static final int XS_PATTERN = XS + 102;
+ public static final int XS_REDEFINE = XS + 103;
+ public static final int XS_RESTRICTION = XS + 104;
+ public static final int XS_SCHEMA = XS + 105;
+ public static final int XS_SELECTOR = XS + 106;
+ public static final int XS_SEQUENCE = XS + 107;
+ public static final int XS_SIMPLE_CONTENT = XS + 108;
+ public static final int XS_SIMPLE_TYPE = XS + 109;
+ public static final int XS_EXPLICIT_TIMEZONE = XS + 110;
+ public static final int XS_TOTAL_DIGITS = XS + 111;
+ public static final int XS_UNION = XS + 112;
+ public static final int XS_UNIQUE = XS + 113;
+ public static final int XS_WHITE_SPACE = XS + 114;
+
+ public static final int XS_UNTYPED = XS + 118;
+ public static final int XS_UNTYPED_ATOMIC = XS + 119;
+ public static final int XS_ANY_ATOMIC_TYPE = XS + 120;
+ public static final int XS_YEAR_MONTH_DURATION = XS + 121;
+ public static final int XS_DAY_TIME_DURATION = XS + 122;
+ public static final int XS_NUMERIC = XS + 123;
+
+
+
+
+
+
+
+ public static final int XSI_TYPE = XSI + 1;
+ public static final int XSI_NIL = XSI + 2;
+ public static final int XSI_SCHEMA_LOCATION = XSI + 3;
+ public static final int XSI_NO_NAMESPACE_SCHEMA_LOCATION = XSI + 4;
+ public static final int XSI_SCHEMA_LOCATION_TYPE = XSI + 5;
+
+ public static final int SCM_SCHEMA = SCM + 1;
+ public static final int SCM_ELEMENT_DECLARATION = SCM + 2;
+ public static final int SCM_ATTRIBUTE_DECLARATION = SCM + 3;
+ public static final int SCM_COMPLEX_TYPE_DEFINITION = SCM + 4;
+ public static final int SCM_SIMPLE_TYPE_DEFINITION = SCM + 5;
+ public static final int SCM_ATTRIBUTE_GROUP_DECLARATION = SCM + 6;
+ public static final int SCM_MODEL_GROUP_DECLARATION = SCM + 7;
+ public static final int SCM_NOTATION_DECLARATION = SCM + 2;
+ public static final int SCM_ANNOTATION = SCM + 2;
+ public static final int SCM_NAME = SCM + 2;
+ public static final int SCM_TARGET_NAMESPACE = SCM + 2;
+
+
+ private static String[] localNames = new String[1023];
+ private static HashMap<String, Integer> lookup = new HashMap<String, Integer>(1023);
+ public static StructuredQName[] errorVariables = {
+ new StructuredQName("err", NamespaceConstant.ERR, "code"),
+ new StructuredQName("err", NamespaceConstant.ERR, "description"),
+ new StructuredQName("err", NamespaceConstant.ERR, "value"),
+ new StructuredQName("err", NamespaceConstant.ERR, "module"),
+ new StructuredQName("err", NamespaceConstant.ERR, "line-number"),
+ new StructuredQName("err", NamespaceConstant.ERR, "column-number")
+ };
+ // key is an expanded QName in Clark notation
+ // value is a fingerprint, as a java.lang.Integer
+
+ private StandardNames() {
+ //pool = namePool;
+ }
+
+ private static void bindXSLTName(int constant, String localName) {
+ localNames[constant] = localName;
+ lookup.put('{' + NamespaceConstant.XSLT + '}' + localName, constant);
+ }
+
+ private static void bindSaxonName(int constant, String localName) {
+ localNames[constant] = localName;
+ lookup.put('{' + NamespaceConstant.SAXON + '}' + localName, constant);
+ }
+
+ private static void bindXMLName(int constant, String localName) {
+ localNames[constant] = localName;
+ lookup.put('{' + NamespaceConstant.XML + '}' + localName, constant);
+ }
+
+ private static void bindXSName(int constant, String localName) {
+ localNames[constant] = localName;
+ lookup.put('{' + NamespaceConstant.SCHEMA + '}' + localName, constant);
+ }
+
+ private static void bindXSIName(int constant, String localName) {
+ localNames[constant] = localName;
+ lookup.put('{' + NamespaceConstant.SCHEMA_INSTANCE + '}' + localName, constant);
+ }
+
+ static {
+
+ bindXSLTName(XSL_ACCUMULATOR, "accumulator");
+ bindXSLTName(XSL_ACCUMULATOR_RULE, "accumulator-rule");
+ bindXSLTName(XSL_ANALYZE_STRING, "analyze-string");
+ bindXSLTName(XSL_APPLY_IMPORTS, "apply-imports");
+ bindXSLTName(XSL_APPLY_TEMPLATES, "apply-templates");
+ bindXSLTName(XSL_ASSERT, "assert");
+ bindXSLTName(XSL_ATTRIBUTE, "attribute");
+ bindXSLTName(XSL_ATTRIBUTE_SET, "attribute-set");
+ bindXSLTName(XSL_BREAK, "break");
+ bindXSLTName(XSL_CALL_TEMPLATE, "call-template");
+ bindXSLTName(XSL_CATCH, "catch");
+ bindXSLTName(XSL_CHARACTER_MAP, "character-map");
+ bindXSLTName(XSL_CHOOSE, "choose");
+ bindXSLTName(XSL_COMMENT, "comment");
+ bindXSLTName(XSL_COPY, "copy");
+ bindXSLTName(XSL_COPY_OF, "copy-of");
+ bindXSLTName(XSL_DECIMAL_FORMAT, "decimal-format");
+ bindXSLTName(XSL_DOCUMENT, "document");
+ bindXSLTName(XSL_ELEMENT, "element");
+ bindXSLTName(XSL_EVALUATE, "evaluate");
+ bindXSLTName(XSL_FALLBACK, "fallback");
+ bindXSLTName(XSL_FOR_EACH, "for-each");
+ bindXSLTName(XSL_FOR_EACH_GROUP, "for-each-group");
+ bindXSLTName(XSL_FORK, "fork");
+ bindXSLTName(XSL_FUNCTION, "function");
+ bindXSLTName(XSL_IF, "if");
+ bindXSLTName(XSL_IMPORT, "import");
+ bindXSLTName(XSL_IMPORT_SCHEMA, "import-schema");
+ bindXSLTName(XSL_INCLUDE, "include");
+ bindXSLTName(XSL_ITERATE, "iterate");
+ bindXSLTName(XSL_KEY, "key");
+ bindXSLTName(XSL_MAP, "map");
+ bindXSLTName(XSL_MAP_ENTRY, "map-entry");
+ bindXSLTName(XSL_MATCHING_SUBSTRING, "matching-substring");
+ bindXSLTName(XSL_MERGE, "merge");
+ bindXSLTName(XSL_MERGE_SOURCE, "merge-source");
+ bindXSLTName(XSL_MERGE_ACTION, "merge-action");
+ bindXSLTName(XSL_MERGE_KEY, "merge-key");
+ bindXSLTName(XSL_MESSAGE, "message");
+ bindXSLTName(XSL_MODE, "mode");
+ bindXSLTName(XSL_NEXT_MATCH, "next-match");
+ bindXSLTName(XSL_NUMBER, "number");
+ bindXSLTName(XSL_NAMESPACE, "namespace");
+ bindXSLTName(XSL_NAMESPACE_ALIAS, "namespace-alias");
+ bindXSLTName(XSL_NEXT_ITERATION, "next-iteration");
+ bindXSLTName(XSL_NON_MATCHING_SUBSTRING, "non-matching-substring");
+ bindXSLTName(XSL_ON_COMPLETION, "on-completion");
+ bindXSLTName(XSL_OTHERWISE, "otherwise");
+ bindXSLTName(XSL_OUTPUT, "output");
+ bindXSLTName(XSL_OUTPUT_CHARACTER, "output-character");
+ bindXSLTName(XSL_PARAM, "param");
+ bindXSLTName(XSL_PERFORM_SORT, "perform-sort");
+ bindXSLTName(XSL_PRESERVE_SPACE, "preserve-space");
+ bindXSLTName(XSL_PROCESSING_INSTRUCTION, "processing-instruction");
+ bindXSLTName(XSL_RESULT_DOCUMENT, "result-document");
+ bindXSLTName(XSL_SEQUENCE, "sequence");
+ bindXSLTName(XSL_SORT, "sort");
+ bindXSLTName(XSL_STREAM, "stream");
+ bindXSLTName(XSL_STRIP_SPACE, "strip-space");
+ bindXSLTName(XSL_STYLESHEET, "stylesheet");
+ bindXSLTName(XSL_TEMPLATE, "template");
+ bindXSLTName(XSL_TEXT, "text");
+ bindXSLTName(XSL_TRANSFORM, "transform");
+ bindXSLTName(XSL_TRY, "try");
+ bindXSLTName(XSL_VALUE_OF, "value-of");
+ bindXSLTName(XSL_VARIABLE, "variable");
+ bindXSLTName(XSL_WITH_PARAM, "with-param");
+ bindXSLTName(XSL_WHEN, "when");
+
+ bindXSLTName(XSL_DEFAULT_COLLATION, "default-collation");
+ bindXSLTName(XSL_XPATH_DEFAULT_NAMESPACE, "xpath-default-namespace");
+ bindXSLTName(XSL_EXPAND_TEXT, "expand-text");
+ bindXSLTName(XSL_EXCLUDE_RESULT_PREFIXES, "exclude-result-prefixes");
+ bindXSLTName(XSL_EXTENSION_ELEMENT_PREFIXES, "extension-element-prefixes");
+ bindXSLTName(XSL_INHERIT_NAMESPACES, "inherit-namespaces");
+ bindXSLTName(XSL_ON_EMPTY, "on-empty");
+ bindXSLTName(XSL_TYPE, "type");
+ bindXSLTName(XSL_USE_ATTRIBUTE_SETS, "use-attribute-sets");
+ bindXSLTName(XSL_USE_WHEN, "use-when");
+ bindXSLTName(XSL_VALIDATION, "validation");
+ bindXSLTName(XSL_VERSION, "version");
+
+ bindSaxonName(SAXON_ASSIGN, "assign");
+ //bindSaxonName(SAXON_BINARY_OUTPUT, "binary-output");
+ bindSaxonName(SAXON_CALL_TEMPLATE, "call-template");
+ //bindSaxonName(SAXON_CATCH, "catch");
+ bindSaxonName(SAXON_COLLATION, "collation");
+ //bindSaxonName(SAXON_CONTINUE, "continue");
+ bindSaxonName(SAXON_DOCTYPE, "doctype");
+ bindSaxonName(SAXON_ENTITY_REF, "entity-ref");
+ //bindSaxonName(SAXON_FINALLY, "finally");
+ bindSaxonName(SAXON_IMPORT_QUERY, "import-query");
+ //bindSaxonName(SAXON_ITERATE, "iterate");
+ //bindSaxonName(SAXON_MODE, "mode");
+ bindSaxonName(SAXON_SCRIPT, "script");
+ //bindSaxonName(SAXON_TRY, "try");
+ bindSaxonName(SAXON_WHILE, "while");
+ bindSaxonName(SAXON_PARAM, "param");
+ bindSaxonName(SAXON_PREPROCESS, "preprocess");
+
+ bindXMLName(XML_BASE, "base");
+ bindXMLName(XML_SPACE, "space");
+ bindXMLName(XML_LANG, "lang");
+ bindXMLName(XML_ID, "id");
+ bindXMLName(XML_LANG_TYPE, "_langType");
+ bindXMLName(XML_SPACE_TYPE, "_spaceType");
+
+ bindXSName(XS_STRING, "string");
+ bindXSName(XS_BOOLEAN, "boolean");
+ bindXSName(XS_DECIMAL, "decimal");
+ bindXSName(XS_FLOAT, "float");
+ bindXSName(XS_DOUBLE, "double");
+ bindXSName(XS_DURATION, "duration");
+ bindXSName(XS_DATE_TIME, "dateTime");
+ bindXSName(XS_TIME, "time");
+ bindXSName(XS_DATE, "date");
+ bindXSName(XS_G_YEAR_MONTH, "gYearMonth");
+ bindXSName(XS_G_YEAR, "gYear");
+ bindXSName(XS_G_MONTH_DAY, "gMonthDay");
+ bindXSName(XS_G_DAY, "gDay");
+ bindXSName(XS_G_MONTH, "gMonth");
+ bindXSName(XS_HEX_BINARY, "hexBinary");
+ bindXSName(XS_BASE64_BINARY, "base64Binary");
+ bindXSName(XS_ANY_URI, "anyURI");
+ bindXSName(XS_QNAME, "QName");
+ bindXSName(XS_NOTATION, "NOTATION");
+ //bindXSName(XS_PRECISION_DECIMAL, "precisionDecimal");
+ bindXSName(XS_INTEGER, "integer");
+ bindXSName(XS_NON_POSITIVE_INTEGER, "nonPositiveInteger");
+ bindXSName(XS_NEGATIVE_INTEGER, "negativeInteger");
+ bindXSName(XS_LONG, "long");
+ bindXSName(XS_INT, "int");
+ bindXSName(XS_SHORT, "short");
+ bindXSName(XS_BYTE, "byte");
+ bindXSName(XS_NON_NEGATIVE_INTEGER, "nonNegativeInteger");
+ bindXSName(XS_POSITIVE_INTEGER, "positiveInteger");
+ bindXSName(XS_UNSIGNED_LONG, "unsignedLong");
+ bindXSName(XS_UNSIGNED_INT, "unsignedInt");
+ bindXSName(XS_UNSIGNED_SHORT, "unsignedShort");
+ bindXSName(XS_UNSIGNED_BYTE, "unsignedByte");
+ bindXSName(XS_NORMALIZED_STRING, "normalizedString");
+ bindXSName(XS_TOKEN, "token");
+ bindXSName(XS_LANGUAGE, "language");
+ bindXSName(XS_NMTOKEN, "NMTOKEN");
+ bindXSName(XS_NMTOKENS, "NMTOKENS"); // NB: list type
+ bindXSName(XS_NAME, "Name");
+ bindXSName(XS_NCNAME, "NCName");
+ bindXSName(XS_ID, "ID");
+ bindXSName(XS_IDREF, "IDREF");
+ bindXSName(XS_IDREFS, "IDREFS"); // NB: list type
+ bindXSName(XS_ENTITY, "ENTITY");
+ bindXSName(XS_ENTITIES, "ENTITIES"); // NB: list type
+ bindXSName(XS_DATE_TIME_STAMP, "dateTimeStamp");
+
+ bindXSName(XS_ANY_TYPE, "anyType");
+ bindXSName(XS_ANY_SIMPLE_TYPE, "anySimpleType");
+ bindXSName(XS_INVALID_NAME, "invalidName");
+ bindXSName(XS_ERROR, "error");
+
+ bindXSName(XS_ALL, "all");
+ bindXSName(XS_ALTERNATIVE, "alternative");
+ bindXSName(XS_ANNOTATION, "annotation");
+ bindXSName(XS_ANY, "any");
+ bindXSName(XS_ANY_ATTRIBUTE, "anyAttribute");
+ bindXSName(XS_APPINFO, "appinfo");
+ bindXSName(XS_ASSERT, "assert");
+ bindXSName(XS_ASSERTION, "assertion");
+ bindXSName(XS_ATTRIBUTE, "attribute");
+ bindXSName(XS_ATTRIBUTE_GROUP, "attributeGroup");
+ bindXSName(XS_CHOICE, "choice");
+ bindXSName(XS_COMPLEX_CONTENT, "complexContent");
+ bindXSName(XS_COMPLEX_TYPE, "complexType");
+ bindXSName(XS_DEFAULT_OPEN_CONTENT, "defaultOpenContent");
+ bindXSName(XS_DOCUMENTATION, "documentation");
+ bindXSName(XS_ELEMENT, "element");
+ bindXSName(XS_ENUMERATION, "enumeration");
+ bindXSName(XS_EXPLICIT_TIMEZONE, "explicitTimezone");
+ bindXSName(XS_EXTENSION, "extension");
+ bindXSName(XS_FIELD, "field");
+ bindXSName(XS_FRACTION_DIGITS, "fractionDigits");
+ bindXSName(XS_GROUP, "group");
+ bindXSName(XS_IMPORT, "import");
+ bindXSName(XS_INCLUDE, "include");
+ bindXSName(XS_KEY, "key");
+ bindXSName(XS_KEYREF, "keyref");
+ bindXSName(XS_LENGTH, "length");
+ bindXSName(XS_LIST, "list");
+ bindXSName(XS_MAX_EXCLUSIVE, "maxExclusive");
+ bindXSName(XS_MAX_INCLUSIVE, "maxInclusive");
+ bindXSName(XS_MAX_LENGTH, "maxLength");
+ bindXSName(XS_MAX_SCALE, "maxScale");
+ bindXSName(XS_MIN_EXCLUSIVE, "minExclusive");
+ bindXSName(XS_MIN_INCLUSIVE, "minInclusive");
+ bindXSName(XS_MIN_LENGTH, "minLength");
+ bindXSName(XS_MIN_SCALE, "minScale");
+ bindXSName(XS_notation, "notation");
+ bindXSName(XS_OPEN_CONTENT, "openContent");
+ bindXSName(XS_OVERRIDE, "override");
+ bindXSName(XS_PATTERN, "pattern");
+ bindXSName(XS_REDEFINE, "redefine");
+ bindXSName(XS_RESTRICTION, "restriction");
+ bindXSName(XS_SCHEMA, "schema");
+ bindXSName(XS_SELECTOR, "selector");
+ bindXSName(XS_SEQUENCE, "sequence");
+ bindXSName(XS_SIMPLE_CONTENT, "simpleContent");
+ bindXSName(XS_SIMPLE_TYPE, "simpleType");
+ bindXSName(XS_TOTAL_DIGITS, "totalDigits");
+ bindXSName(XS_UNION, "union");
+ bindXSName(XS_UNIQUE, "unique");
+ bindXSName(XS_WHITE_SPACE, "whiteSpace");
+
+ bindXSName(XS_UNTYPED, "untyped");
+ bindXSName(XS_UNTYPED_ATOMIC, "untypedAtomic");
+ bindXSName(XS_ANY_ATOMIC_TYPE, "anyAtomicType");
+ bindXSName(XS_YEAR_MONTH_DURATION, "yearMonthDuration");
+ bindXSName(XS_DAY_TIME_DURATION, "dayTimeDuration");
+ bindXSName(XS_NUMERIC, "_numeric_");
+
+ bindXSIName(XSI_TYPE, "type");
+ bindXSIName(XSI_NIL, "nil");
+ bindXSIName(XSI_SCHEMA_LOCATION, "schemaLocation");
+ bindXSIName(XSI_NO_NAMESPACE_SCHEMA_LOCATION, "noNamespaceSchemaLocation");
+ bindXSIName(XSI_SCHEMA_LOCATION_TYPE, "anonymous_schemaLocationType");
+ }
+
+ /**
+ * Get the fingerprint of a system-defined name, from its URI and local name
+ *
+ * @param uri the namespace URI
+ * @param localName the local part of the name
+ * @return the standard fingerprint, or -1 if this is not a built-in name
+ */
+
+ public static int getFingerprint(String uri, String localName) {
+ Integer fp = lookup.get('{' + uri + '}' + localName);
+ if (fp == null) {
+ return -1;
+ } else {
+ return fp;
+ }
+ }
+
+ /**
+ * Get the local part of a system-defined name
+ * @param fingerprint the fingerprint of the name
+ * @return the local part of the name
+ */
+
+ public static String getLocalName(int fingerprint) {
+ return localNames[fingerprint];
+ }
+
+ /**
+ * Get the namespace URI part of a system-defined name
+ * @param fingerprint the fingerprint of the name
+ * @return the namespace URI part of the name
+ * @throws IllegalArgumentException if the fingerprint does not define a known name
+ */
+
+ /*@NotNull*/
+ public static String getURI(int fingerprint) {
+ int c = fingerprint >> 7;
+ switch (c) {
+ case DFLT_NS:
+ return "";
+ case XSL_NS:
+ return NamespaceConstant.XSLT;
+ case SAXON_NS:
+ return NamespaceConstant.SAXON;
+ case XML_NS:
+ return NamespaceConstant.XML;
+ case XS_NS:
+ return NamespaceConstant.SCHEMA;
+ case XSI_NS:
+ return NamespaceConstant.SCHEMA_INSTANCE;
+ case SCM_NS:
+ return NamespaceConstant.SCM;
+ default:
+ throw new IllegalArgumentException("Unknown system fingerprint " + fingerprint);
+ }
+ }
+
+ /**
+ * Get the namespace URI part of a system-defined name as a URI code
+ * @param fingerprint the fingerprint of the name
+ * @return the namespace URI part of the name, as a URI code
+ */
+
+ public static short getURICode(int fingerprint) {
+ int c = fingerprint >> 7;
+ switch (c) {
+ case DFLT_NS:
+ return 0;
+ case XSL_NS:
+ return NamespaceConstant.XSLT_CODE;
+ case SAXON_NS:
+ return NamespaceConstant.SAXON_CODE;
+ case XML_NS:
+ return NamespaceConstant.XML_CODE;
+ case XS_NS:
+ return NamespaceConstant.SCHEMA_CODE;
+ case XSI_NS:
+ return NamespaceConstant.XSI_CODE;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Get the Clark form of a system-defined name, given its name code or fingerprint
+ * @param fingerprint the fingerprint of the name
+ * @return the local name if the name is in the null namespace, or "{uri}local" otherwise.
+ */
+
+ public static String getClarkName(int fingerprint) {
+ String uri = getURI(fingerprint);
+ if (uri.length() == 0) {
+ return getLocalName(fingerprint);
+ } else {
+ return '{' + uri + '}' + getLocalName(fingerprint);
+ }
+ }
+
+ /**
+ * Get the conventional prefix of a system-defined name
+ * @param fingerprint the fingerprint of the name
+ * @return the conventional prefix of the name
+ */
+
+ public static String getPrefix(int fingerprint) {
+ int c = fingerprint >> 7;
+ switch (c) {
+ case DFLT_NS:
+ return "";
+ case XSL_NS:
+ return "xsl";
+ case SAXON_NS:
+ return "saxon";
+ case XML_NS:
+ return "xml";
+ case XS_NS:
+ return "xs";
+ case XSI_NS:
+ return "xsi";
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Get the lexical display form of a system-defined name
+ * @param fingerprint the fingerprint of the name
+ * @return the lexical display form of the name, using a conventional prefix
+ */
+
+ public static String getDisplayName(int fingerprint) {
+ if (fingerprint == -1) {
+ return "(anonymous type)";
+ }
+ if (fingerprint > 1023) {
+ return "(" + fingerprint + ')';
+ }
+ if ((fingerprint >> 7) == DFLT) {
+ return getLocalName(fingerprint);
+ }
+ return getPrefix(fingerprint) + ':' + getLocalName(fingerprint);
+ }
+
+ /**
+ * Get a StructuredQName representing a system-defined name
+ * @param fingerprint the fingerprint of the name
+ * @return a StructuredQName representing the system-defined name
+ */
+
+ public static StructuredQName getStructuredQName(int fingerprint) {
+ return new StructuredQName(getPrefix(fingerprint), getURI(fingerprint), getLocalName(fingerprint));
+ }
+
+}
+
diff --git a/sf/saxon/om/StructuredQName.java b/sf/saxon/om/StructuredQName.java
new file mode 100644
index 0000000..de13fae
--- /dev/null
+++ b/sf/saxon/om/StructuredQName.java
@@ -0,0 +1,414 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.namespace.QName;
+
+/**
+ * This class provides an economical representation of a QName triple (prefix, URI, and localname).
+ * The value is stored internally as a character array containing the concatenation of URI, localname,
+ * and prefix (in that order) with two integers giving the start positions of the localname and prefix.
+ *
+ * <p><i>Instances of this class are immutable.</i></p>
+ */
+
+public class StructuredQName {
+
+ private char[] content;
+ private int localNameStart;
+ private int prefixStart;
+
+ /**
+ * Construct a StructuredQName from a prefix, URI, and local name. This method performs no validation.
+ * @param prefix The prefix. Use an empty string to represent the null prefix.
+ * @param uri The namespace URI. Use an empty string or null to represent the no-namespace
+ * @param localName The local part of the name
+ */
+
+ public StructuredQName(String prefix, /*@Nullable*/ String uri, String localName) {
+ if (uri == null) {
+ uri = "";
+ }
+ int plen = prefix.length();
+ int ulen = uri.length();
+ int llen = localName.length();
+ localNameStart = ulen;
+ prefixStart = ulen + llen;
+ content = new char[ulen + llen + plen];
+ uri.getChars(0, ulen, content, 0);
+ localName.getChars(0, llen, content, ulen);
+ prefix.getChars(0, plen, content, ulen+llen);
+ }
+
+ /**
+ * Make a structuredQName from a Clark name
+ * @param expandedName the name in Clark notation "{uri}local" if in a namespace, or "local" otherwise.
+ * The format "{}local" is also accepted for a name in no namespace. The EQName syntax (Q{uri}local) is
+ * also accepted.
+ * @return the constructed StructuredQName
+ * @throws IllegalArgumentException if the Clark name is malformed
+ */
+
+ public static StructuredQName fromClarkName(String expandedName) {
+ String namespace;
+ String localName;
+ if (expandedName.startsWith("Q{")) {
+ expandedName = expandedName.substring(1);
+ }
+ if (expandedName.charAt(0) == '{') {
+ int closeBrace = expandedName.indexOf('}');
+ if (closeBrace < 0) {
+ throw new IllegalArgumentException("No closing '}' in Clark name");
+ }
+ namespace = expandedName.substring(1, closeBrace);
+ if (closeBrace == expandedName.length()) {
+ throw new IllegalArgumentException("Missing local part in Clark name");
+ }
+ localName = expandedName.substring(closeBrace + 1);
+ } else {
+ namespace = "";
+ localName = expandedName;
+ }
+ return new StructuredQName("", namespace, localName);
+ }
+
+ /**
+ * Make a structured QName from a lexical QName, using a supplied NamespaceResolver to
+ * resolve the prefix
+ *
+ * @param lexicalName the QName as a lexical name (prefix:local)
+ * @param useDefault set to true if an absent prefix implies use of the default namespace;
+ * set to false if an absent prefix implies no namespace
+ * @param allowEQName true if the EQName syntax Q{uri}local is acceptable
+ * @param checker NameChecker to be used to check conformance against XML 1.0 or 1.1 lexical rules
+ * @param resolver NamespaceResolver used to look up a URI for the prefix
+ * @return the StructuredQName object corresponding to this lexical QName
+ * @throws XPathException if the namespace prefix is not in scope or if the value is lexically
+ * invalid. Error code FONS0004 is set if the namespace prefix has not been declared; error
+ * code FOCA0002 is set if the name is lexically invalid.
+ */
+
+ public static StructuredQName fromLexicalQName(CharSequence lexicalName, boolean useDefault,
+ boolean allowEQName, NameChecker checker, NamespaceResolver resolver)
+ throws XPathException {
+ lexicalName = Whitespace.trimWhitespace(lexicalName);
+ if (allowEQName && lexicalName.length() >= 4 && lexicalName.charAt(0) == 'Q' && lexicalName.charAt(1) == '{') {
+ String name = lexicalName.toString();
+ int endBrace = name.indexOf('}');
+ if (endBrace < 0) {
+ throw new XPathException("Invalid EQName: closing brace not found");
+ } else if (endBrace == name.length() - 1) {
+ throw new XPathException("Invalid EQName: local part is missing");
+ }
+ String uri = name.substring(2, endBrace);
+ String local = name.substring(endBrace + 1);
+ if (!checker.isValidNCName(local)) {
+ throw new XPathException("Invalid EQName: local part is not a valid NCName");
+ }
+ return new StructuredQName("", uri, local);
+ }
+ try {
+ String[] parts = checker.getQNameParts(lexicalName);
+ String uri = resolver.getURIForPrefix(parts[0], useDefault);
+ if (uri == null) {
+ XPathException de = new XPathException("Namespace prefix '" + parts[0] + "' has not been declared");
+ de.setErrorCode("FONS0004");
+ throw de;
+ }
+ return new StructuredQName(parts[0], uri, parts[1]);
+ } catch (QNameException e) {
+ XPathException de = new XPathException(e.getMessage());
+ de.setErrorCode("FOCA0002");
+ throw de;
+ }
+ }
+
+ /**
+ * Make a structured QName from a lexical QName, using a supplied NamespaceResolver to
+ * resolve the prefix
+ *
+ * @param eqName the QName as an EQname (Q{uri}local), or an unqualified local name. The format is not checked.
+ * @return the StructuredQName object corresponding to this lexical QName
+ * @throws IllegalArgumentException if the eqName is obviously invalid
+ */
+
+ public static StructuredQName fromEQName(CharSequence eqName) {
+ eqName = Whitespace.trimWhitespace(eqName);
+ if (eqName.length() >= 4 && eqName.charAt(0) == 'Q' && eqName.charAt(1) == '{') {
+ String name = eqName.toString();
+ int endBrace = name.indexOf('}');
+ if (endBrace < 0) {
+ throw new IllegalArgumentException("Invalid EQName: closing brace not found");
+ } else if (endBrace == name.length() - 1) {
+ throw new IllegalArgumentException("Invalid EQName: local part is missing");
+ }
+ String uri = name.substring(2, endBrace);
+ String local = name.substring(endBrace + 1);
+ return new StructuredQName("", uri, local);
+ } else {
+ return new StructuredQName("", "", eqName.toString());
+ }
+ }
+
+
+ /**
+ * Get the prefix of the QName.
+ * @return the prefix. Returns the empty string if the name is unprefixed.
+ */
+
+ public String getPrefix() {
+ return new String(content, prefixStart, content.length - prefixStart);
+ }
+
+ /**
+ * Get the namespace URI of the QName.
+ * @return the URI. Returns the empty string to represent the no-namespace
+ */
+
+ public String getURI() {
+ if (localNameStart == 0) {
+ return "";
+ }
+ return new String(content, 0, localNameStart);
+ }
+
+ /**
+ * Get the local part of the QName
+ * @return the local part of the QName
+ */
+
+ public String getLocalPart() {
+ return new String(content, localNameStart, prefixStart - localNameStart);
+ }
+
+ /**
+ * Get the display name, that is the lexical QName in the form [prefix:]local-part
+ * @return the lexical QName
+ */
+
+ public String getDisplayName() {
+ if (prefixStart == content.length) {
+ return getLocalPart();
+ } else {
+ FastStringBuffer buff = new FastStringBuffer(content.length - localNameStart + 1);
+ buff.append(content, prefixStart, content.length - prefixStart);
+ buff.append(':');
+ buff.append(content, localNameStart, prefixStart - localNameStart);
+ return buff.toString();
+ }
+ }
+
+ /**
+ * Get the name as a StructuredQName (which it already is; but this satisfies the NodeName interface)
+ */
+
+ public StructuredQName getStructuredQName() {
+ return this;
+ }
+
+ /**
+ * Get the expanded QName in Clark format, that is "{uri}local" if it is in a namespace, or just "local"
+ * otherwise.
+ * @return the QName in Clark notation
+ */
+
+ public String getClarkName() {
+ FastStringBuffer buff = new FastStringBuffer(content.length - prefixStart + 2);
+ if (localNameStart > 0) {
+ buff.append('{');
+ buff.append(content, 0, localNameStart);
+ buff.append('}');
+ }
+ buff.append(content, localNameStart, prefixStart - localNameStart);
+ return buff.toString();
+ }
+
+ /**
+ * Get the expanded QName as an EQName, that is "Q{uri}local" for a name in a namespace,
+ * or "Q{}local" otherwise
+ * @return the QName in EQName notation
+ */
+
+ public String getEQName() {
+ FastStringBuffer buff = new FastStringBuffer(content.length - prefixStart + 2);
+ buff.append("Q{");
+ if (localNameStart > 0) {
+ buff.append(content, 0, localNameStart);
+ }
+ buff.append('}');
+ buff.append(content, localNameStart, prefixStart - localNameStart);
+ return buff.toString();
+ }
+
+ /**
+ * The toString() method displays the QName as a lexical QName, that is prefix:local
+ * @return the lexical QName
+ */
+
+ public String toString() {
+ return getDisplayName();
+ }
+
+ /**
+ * Compare two StructuredQName values for equality. This compares the URI and local name parts,
+ * excluding any prefix
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof StructuredQName) {
+ StructuredQName sq2 = (StructuredQName)other;
+ if (localNameStart != sq2.localNameStart || prefixStart != sq2.prefixStart) {
+ return false;
+ }
+ for (int i=prefixStart-1; i>=0; i--) {
+ // compare from the end of the local name to maximize chance of finding a difference quickly
+ if (content[i] != sq2.content[i]) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get a hashcode to reflect the equals() method
+ * @return a hashcode based on the URI and local part only, ignoring the prefix.
+ */
+
+ public int hashCode() {
+ int h = 0x8004a00b;
+ h ^= prefixStart;
+ h ^= localNameStart;
+ for (int i=prefixStart-1; i>=0; i--) {
+ h ^= (content[i] << (i&0x1f));
+ }
+ return h;
+ }
+
+ /**
+ * Expose the hashCode algorithm so that other implementations of QNames can construct a compatible hashcode
+ * @param uri the namespace URI
+ * @param local the local name
+ * @return a hash code computed from the URI and local name
+ */
+
+ public static int computeHashCode(CharSequence uri, CharSequence local) {
+ int h = 0x8004a00b;
+ int localLen = local.length();
+ int uriLen = uri.length();
+ int totalLen = localLen + uriLen;
+ h ^= totalLen;
+ h ^= uriLen;
+ for (int i=0; i<uriLen; i++) {
+ h ^= (uri.charAt(i) << (i&0x1f));
+ }
+ for (int i=0, j=uriLen; i<localLen; i++, j++) {
+ h ^= (local.charAt(i) << (j&0x1f));
+ }
+ return h;
+ }
+
+ /**
+ * Test whether this name is in the same namespace as another name
+ * @param other the other name
+ * @return true if the two names are in the same namespace
+ */
+
+ public boolean isInSameNamespace(NodeName other) {
+ if (this == other) {
+ return true;
+ }
+ if (other instanceof StructuredQName) {
+ StructuredQName q2 = (StructuredQName)other;
+ if (localNameStart != q2.localNameStart) {
+ return false;
+ }
+ for (int i=localNameStart-1; i>=0; i--) {
+ // compare from the end of the URI to maximize chance of finding a difference quickly
+ if (content[i] != q2.content[i]) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return getURI().equals(other.getURI());
+ }
+ }
+
+ /**
+ * Test whether this name is in a given namespace
+ * @param ns the namespace to be tested against
+ * @return true if the name is in the specified namespace
+ */
+
+ public boolean isInNamespace(String ns) {
+ if (ns.length()==0) {
+ return localNameStart == 0;
+ } else {
+ return localNameStart == ns.length() && getURI().equals(ns);
+ }
+ }
+
+ /**
+ * Convert the StructuredQName to a javax.xml.namespace.QName
+ * @return an object of class javax.xml.namespace.QName representing this qualified name
+ */
+
+ public QName toJaxpQName() {
+ return new javax.xml.namespace.QName(getURI(), getLocalPart(), getPrefix());
+ }
+
+ /**
+ * Get the NamespaceBinding (prefix/uri pair) corresponding to this name
+ * @return a NamespaceBinding containing the prefix and URI present in this QName
+ */
+
+ public NamespaceBinding getNamespaceBinding() {
+ return NamespaceBinding.makeNamespaceBinding(
+ new CharSlice(content, prefixStart, content.length - prefixStart),
+ new CharSlice(content, 0, localNameStart));
+ }
+
+ /**
+ * Ask whether this node name representation has a known namecode and fingerprint
+ *
+ * @return true if the methods getFingerprint() and getNameCode() will
+ * return a result other than -1
+ */
+ public boolean hasFingerprint() {
+ return false;
+ }
+
+ /**
+ * Get the fingerprint of this name if known. This method should not to any work to allocate
+ * a fingerprint if none is already available
+ *
+ * @return the fingerprint if known; otherwise -1
+ */
+ public int getFingerprint() {
+ return -1;
+ }
+
+ /**
+ * Get the nameCode of this name if known. This method should not to any work to allocate
+ * a nameCode if none is already available
+ *
+ * @return the fingerprint if known; otherwise -1
+ */
+ public int getNameCode() {
+ return -1;
+ }
+}
+
diff --git a/sf/saxon/om/StylesheetSpaceStrippingRule.java b/sf/saxon/om/StylesheetSpaceStrippingRule.java
new file mode 100644
index 0000000..b003531
--- /dev/null
+++ b/sf/saxon/om/StylesheetSpaceStrippingRule.java
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+
+import net.sf.saxon.event.Stripper;
+
+import java.util.Arrays;
+
+/**
+ * A whitespace stripping rule that strips whitespace according to the rules defined for XSLT stylesheets
+ */
+
+public class StylesheetSpaceStrippingRule implements SpaceStrippingRule {
+
+ // Any child of one of the following elements is removed from the tree,
+ // regardless of any xml:space attributes. Note that this array must be in numeric
+ // order for binary chop to work correctly.
+
+ private static final int[] specials = {
+ StandardNames.XSL_ANALYZE_STRING,
+ StandardNames.XSL_APPLY_IMPORTS,
+ StandardNames.XSL_APPLY_TEMPLATES,
+ StandardNames.XSL_ATTRIBUTE_SET,
+ StandardNames.XSL_CALL_TEMPLATE,
+ StandardNames.XSL_CHARACTER_MAP,
+ StandardNames.XSL_CHOOSE,
+ StandardNames.XSL_EVALUATE,
+ StandardNames.XSL_MERGE,
+ StandardNames.XSL_MERGE_SOURCE,
+ StandardNames.XSL_NEXT_ITERATION,
+ StandardNames.XSL_NEXT_MATCH,
+ StandardNames.XSL_STYLESHEET,
+ StandardNames.XSL_TRANSFORM
+ };
+
+ private NamePool namePool;
+
+ public StylesheetSpaceStrippingRule(NamePool pool) {
+ this.namePool = pool;
+ }
+
+ /**
+ * Decide whether an element is in the set of white-space preserving element types
+ * @param elementName identifies the element being tested
+ */
+
+ public byte isSpacePreserving(/*@NotNull*/ NodeName elementName) {
+ if (!elementName.hasFingerprint()) {
+ elementName.allocateNameCode(namePool);
+ }
+ int fingerprint = elementName.getFingerprint();
+ if (fingerprint == (StandardNames.XSL_TEXT & NamePool.FP_MASK)) {
+ return Stripper.ALWAYS_PRESERVE;
+ }
+
+ if (Arrays.binarySearch(specials, fingerprint) >= 0) {
+ return Stripper.ALWAYS_STRIP;
+ }
+
+ return Stripper.STRIP_DEFAULT;
+ }
+
+
+}
+
diff --git a/sf/saxon/om/TreeModel.java b/sf/saxon/om/TreeModel.java
new file mode 100644
index 0000000..776dd94
--- /dev/null
+++ b/sf/saxon/om/TreeModel.java
@@ -0,0 +1,186 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.om;
+
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.tree.linked.LinkedTreeBuilder;
+import net.sf.saxon.tree.tiny.TinyBuilder;
+import net.sf.saxon.tree.tiny.TinyBuilderCondensed;
+
+import java.io.Serializable;
+
+/**
+ * A TreeModel represents an implementation of the Saxon NodeInfo interface, which itself
+ * is essentially an implementation of the XDM model defined in W3C specifications (except
+ * that Saxon's NodeInfo understands the 13 XPath axes, rather than merely supporting
+ * parent and child properties).
+ *
+ * <p>This class serves two purposes: it acts as a factory for obtaining a Builder which
+ * can be used to build trees using this tree model; and it provides static constants
+ * that can be used to identify the built-in tree models.</p>
+ *
+ */
+
+public abstract class TreeModel implements Serializable {
+
+ /**
+ * The TinyTree implementation. This is normally the default implementation
+ * of the tree model.
+ */
+ public final static TreeModel TINY_TREE = new TinyTree();
+
+ /**
+ * The CondensedTinyTree implementation. This is a variant of the TinyTree that
+ * uses less memory but takes a little longer to build. Run-time performance
+ * is the same as the TinyTree.
+ */
+ public final static TreeModel TINY_TREE_CONDENSED = new TinyTreeCondensed();
+
+ /**
+ * The LinkedTree. This takes more memory than the TinyTree. The main advantage
+ * of this model is that it is updateable: the nodes in a LinkedTree can be modified
+ * using XQuery Updates.
+ */
+ public final static TreeModel LINKED_TREE = new LinkedTree();
+
+ /**
+ * Make a Builder, which can then be used to construct an instance of this tree model
+ * from a stream of events
+ * @param pipe A PipelineConfiguration, which can be constructed using the method
+ * {@link net.sf.saxon.Configuration#makePipelineConfiguration()}.
+ * @return a newly created Builder
+ */
+
+ public abstract Builder makeBuilder(PipelineConfiguration pipe);
+
+ /**
+ * Get the integer constant used to identify this tree model in some legacy interfaces
+ * @return an integer constant used to identify the model, for example {@link Builder#TINY_TREE}
+ */
+
+ public int getSymbolicValue() {
+ return Builder.UNSPECIFIED_TREE_MODEL;
+ }
+
+ /**
+ * Get the tree model corresponding to a given integer constant
+ * @param symbolicValue one of the constants {@link Builder#TINY_TREE},
+ * {@link Builder#TINY_TREE_CONDENSED}, or {@link Builder#LINKED_TREE}
+ * @return the corresponding TreeModel
+ */
+
+ public static TreeModel getTreeModel(int symbolicValue) {
+ switch (symbolicValue) {
+ case Builder.TINY_TREE:
+ return TreeModel.TINY_TREE;
+ case Builder.TINY_TREE_CONDENSED:
+ return TreeModel.TINY_TREE_CONDENSED;
+ case Builder.LINKED_TREE:
+ return TreeModel.LINKED_TREE;
+ default:
+ throw new IllegalArgumentException("tree model " + symbolicValue);
+ }
+ }
+
+ /**
+ * Ask whether this tree model supports updating (that is, whether the nodes
+ * in the constructed tree will implement {@link MutableNodeInfo}, which is necessary
+ * if they are to support XQuery Update. This method can be overridden in subclasses;
+ * the default implementation returns false.
+ * @return true if the tree model implementation supports updating, that is, if its
+ * nodes support the MutableNodeInfo interface.
+ */
+
+ public boolean isMutable() {
+ return false;
+ }
+
+ /**
+ * Ask whether this tree model supports the use of type annotations on element and attribute
+ * nodes. If false, all nodes in this tree are untyped (for elements) or untypedAtomic (for attributes).
+ * The default implementation returns false.
+ * @return true if type annotations other than xs:untyped and xs:untypedAtomic are supported.
+ */
+
+ public boolean isSchemaAware() {
+ return false;
+ }
+
+ /**
+ * Get a name that identifies the tree model
+ * @return an identifying name for the tree model
+ */
+
+ public String getName() {
+ return toString();
+ }
+
+ private static class TinyTree extends TreeModel {
+
+ public Builder makeBuilder(PipelineConfiguration pipe) {
+ return new TinyBuilder(pipe);
+ }
+
+ public int getSymbolicValue() {
+ return Builder.TINY_TREE;
+ }
+
+ public boolean isSchemaAware() {
+ return true;
+ }
+
+ public String getName() {
+ return "TinyTree";
+ }
+ }
+
+ private static class TinyTreeCondensed extends TreeModel {
+ public Builder makeBuilder(PipelineConfiguration pipe) {
+ return new TinyBuilderCondensed(pipe);
+ }
+
+ public int getSymbolicValue() {
+ return Builder.TINY_TREE_CONDENSED;
+ }
+
+ public boolean isSchemaAware() {
+ return true;
+ }
+
+ public String getName() {
+ return "TinyTreeCondensed";
+ }
+ }
+
+ private static class LinkedTree extends TreeModel {
+
+ /*@NotNull*/ public Builder makeBuilder(PipelineConfiguration pipe) {
+ return new LinkedTreeBuilder(pipe);
+ }
+
+ public int getSymbolicValue() {
+ return Builder.LINKED_TREE;
+ }
+
+ public boolean isMutable() {
+ return true;
+ }
+
+ public boolean isSchemaAware() {
+ return true;
+ }
+
+ public String getName() {
+ return "LinkedTree";
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/om/package.html b/sf/saxon/om/package.html
new file mode 100644
index 0000000..583819c
--- /dev/null
+++ b/sf/saxon/om/package.html
@@ -0,0 +1,50 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.om</title>
+</head>
+
+<body>
+
+<p>This package defines the interface to the Saxon tree structure. This
+structure is used to represent both the source document and the stylesheet.
+Essentially, this class represents Saxon's realization of the XPath data model.</p>
+
+<p>The classes in the package are rather a miscellany. What they have in common is that
+they describe the way the Saxon tree structure is accessed, in a way that it independent
+of the two tree implementations (in packages <code>net.sf.saxon.tree</code> and
+<code>net.sf.saxon.tinytree</code>).</p>
+
+<p>Broadly speaking, the classes fall into four categories:</p>
+
+<ul>
+<li>Interface classes: DocumentInfo, NodeInfo, Item, ValueRepresentation. These describe the interface
+offered by the object model to the rest of the system, including the application.</li>
+
+<li>Iterator classes: SequenceIterator, AxisIterator, ArrayIterator, EmptyIterator, SingletonIterator, and others.
+These classes provide mechanisms for iterating over sequences. The most general, and the one which
+applications are most likely to use, is the SequenceIterator interface itself. AxisIterator is a
+specialization of this interface whose main difference is that it cannot throw exceptions. The other
+classes are implementations of SequenceIterator for use in particular circumstances.</li>
+
+<liul>Shared implementation classes: DocumentPool, NamePool, Navigator, XMLChar.
+These contain functionality that is shared between the various tree implementations. (However, there is
+also some shared functionality in the <code>net.sf.saxon.tree</code> package). These classes are
+not generally needed by applications, with the exception of NamePool, which complex applications may
+need to manipulate.</li>
+
+<li>Information classes: Axis, NamespaceConstant. These contain constants.</li>
+</ul>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/package.html b/sf/saxon/package.html
new file mode 100644
index 0000000..741d3f6
--- /dev/null
+++ b/sf/saxon/package.html
@@ -0,0 +1,52 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon</title>
+</head>
+
+<body>
+
+<p>This package provides the core classes of the SAXON XSLT library. </p>
+
+<p>Some of the more important classes are listed below: </p>
+
+<p>{@link net.sf.saxon.Query}:<br>
+This is the command line interface to the XQuery processor, allowing you to
+run a supplied query against a given source document.</p>
+
+<p>{@link net.sf.saxon.Transform}:<br>
+This is the command line interface to the XSLT processor, allowing you to
+apply a given stylesheet to a given source document.</p>
+
+<p>{@link net.sf.saxon.Configuration}:<br>
+This class holds all the Saxon configuration information, and acts as the fundamental factory class
+holding central resources and creating components such as validators and serializers.
+</p>
+
+<p><b>{@link net.sf.saxon.PreparedStylesheet}</b>:<br>
+This represents a compiled XSLT stylesheet in memory. It is Saxon's implementation of the
+<tt>javax.xml.transform.Templates</tt> interface defined in JAXP 1.1</p>
+
+<p><b>{@link net.sf.saxon.Controller}</b>:<br>
+This class represents the context information for a single execution of an XSLT stylesheet,
+and allows the application to process the tree navigationally. It is Saxon's implementation
+of the {@link javax.xml.transform.Transformer} interface defined in JAXP 1.1. It calls
+user-supplied handlers registered with the RuleManager.
+</p>
+
+
+
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br>
+30 July 2010</i></p>
+</body>
+</html>
diff --git a/sf/saxon/pattern/AncestorQualifiedPattern.java b/sf/saxon/pattern/AncestorQualifiedPattern.java
new file mode 100644
index 0000000..174692f
--- /dev/null
+++ b/sf/saxon/pattern/AncestorQualifiedPattern.java
@@ -0,0 +1,363 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+
+import java.util.Iterator;
+
+/**
+ * An AncestorQualifiedPattern represents a path of the form A/B or A//B, where nodes must match the
+ * pattern B and also have a parent/ancestor (respectively) that matches A.
+ */
+
+public final class AncestorQualifiedPattern extends Pattern {
+
+ private Pattern basePattern;
+ private Pattern upperPattern;
+ private byte upwardsAxis = AxisInfo.PARENT;
+ private ItemType refinedItemType;
+
+ public AncestorQualifiedPattern(Pattern base, Pattern upper, byte axis) {
+ this.basePattern = base;
+ this.upperPattern = upper;
+ this.upwardsAxis = axis;
+ }
+
+ public void setLineNumber(int lineNumber) {
+ super.setLineNumber(lineNumber);
+ basePattern.setLineNumber(lineNumber);
+ upperPattern.setLineNumber(lineNumber);
+ }
+
+ public void setSystemId(String systemId) {
+ super.setSystemId(systemId);
+ basePattern.setSystemId(systemId);
+ upperPattern.setSystemId(systemId);
+ }
+
+ /**
+ * Set the executable containing this pattern
+ *
+ * @param executable the executable
+ */
+
+ public void setExecutable(Executable executable) {
+ super.setExecutable(executable);
+ basePattern.setExecutable(executable);
+ upperPattern.setExecutable(executable);
+ }
+
+ /**
+ * Replace any calls on current() by a variable reference bound to the supplied binding
+ */
+ @Override
+ public void bindCurrent(Binding binding) {
+ basePattern.bindCurrent(binding);
+ upperPattern.bindCurrent(binding);
+ }
+
+ /**
+ * Get the base pattern, the pattern applying to the node itself
+ * @return the base pattern
+ */
+ public Pattern getBasePattern() {
+ return basePattern;
+ }
+
+ /**
+ * Get the pattern applying to the parent node, if there is one
+ * @return the parent pattern, for example if the pattern is a/b[1]/c then the parent
+ * pattern is a/b[1]
+ */
+
+ public Pattern getUpperPattern() {
+ return upperPattern;
+ }
+
+ /**
+ * Get the upwards axis, that is, the axis by which the upper pattern is reached.
+ * Typically Axis.PARENT or Axis.ANCESTOR
+ */
+
+ public byte getUpwardsAxis() {
+ return upwardsAxis;
+ }
+
+ /**
+ * Test whether a pattern is motionless, that is, whether it can be evaluated against a node
+ * without repositioning the input stream. This is a necessary condition for patterns used
+ * as the match pattern of a streamed template rule.
+ * @param allowExtensions if false, the result is determined strictly according to the W3C
+ * guaranteed streamability rules. If true, Saxon extensions are permitted: that is, constructs
+ * may be recognized as motionless by Saxon even if they are not recognized as motionless by
+ * the W3C rules.
+ * @return true if the pattern is motionless, that is, if it can be evaluated against a streamed
+ * node without changing the position in the streamed input file
+ */
+
+ public boolean isMotionless(boolean allowExtensions) {
+ return basePattern.isMotionless(allowExtensions) && upperPattern.isMotionless(allowExtensions);
+ }
+
+ /**
+ * Simplify the pattern: perform any context-independent optimisations
+ * @param visitor an expression visitor
+ */
+
+ public Pattern simplify(ExpressionVisitor visitor) throws XPathException {
+ upperPattern = upperPattern.simplify(visitor);
+ basePattern = basePattern.simplify(visitor);
+ return this;
+ }
+
+ /**
+ * Type-check the pattern, performing any type-dependent optimizations.
+ * @param visitor an expression visitor
+ * @param contextItemType the type of the context item at the point where the pattern appears
+ * @return the optimised Pattern
+ */
+
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ basePattern = basePattern.analyze(visitor, contextItemType);
+ upperPattern = upperPattern.analyze(visitor, contextItemType);
+ if (upwardsAxis == AxisInfo.PARENT) {
+ ItemType type = basePattern.getItemType();
+ if (type instanceof NodeTest) {
+ // Check that this step in the pattern makes sense in the context of the parent step
+ AxisExpression step;
+ if (type.getPrimitiveType() == Type.ATTRIBUTE) {
+ step = new AxisExpression(AxisInfo.ATTRIBUTE, (NodeTest)type);
+ } else {
+ step = new AxisExpression(AxisInfo.CHILD, (NodeTest)type);
+ }
+ StaticContext env = visitor.getStaticContext();
+ step.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), getLineNumber()));
+ step.setContainer(this);
+ Expression exp = visitor.typeCheck(step, new ExpressionVisitor.ContextItemType(upperPattern.getItemType(), false));
+ refinedItemType = exp.getItemType(env.getConfiguration().getTypeHierarchy());
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Get the dependencies of the pattern. The only possible dependency for a pattern is
+ * on local variables. This is analyzed in those patterns where local variables may appear.
+ */
+
+ public int getDependencies() {
+ return basePattern.getDependencies() | upperPattern.getDependencies();
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+
+ Iterator[] pair = {basePattern.iterateSubExpressions(), upperPattern.iterateSubExpressions()};
+ return new MultiIterator<Expression>(pair);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = upperPattern.replaceSubExpression(original, replacement);
+ found |= basePattern.replaceSubExpression(original, replacement);
+ return found;
+
+ }
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ * @param slotManager
+ * @param nextFree the next slot that is free to be allocated @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ // See tests cnfr23, idky239, match54
+ // SlotManager slotManager = env.getStyleElement().getContainingSlotManager();
+ nextFree = upperPattern.allocateSlots(slotManager, nextFree);
+ nextFree = basePattern.allocateSlots(slotManager, nextFree);
+ return nextFree;
+ }
+
+ /**
+ * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>Unlike the corresponding method on {@link net.sf.saxon.expr.Expression}, this method does not return anything:
+ * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
+ * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
+ * variable references within the pattern.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public void promote(PromotionOffer offer, Expression parent) throws XPathException {
+ basePattern.promote(offer, parent);
+ upperPattern.promote(offer, parent);
+ }
+
+
+ /**
+ * Determine whether the pattern matches a given item.
+ * @param item the item to be tested
+ * @return true if the pattern matches, else false
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ return item instanceof NodeInfo && matchesBeneathAnchor((NodeInfo) item, null, context);
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ if (!basePattern.matches(node, context)) {
+ return false;
+ }
+ if (upperPattern != null) {
+ switch (upwardsAxis) {
+ case AxisInfo.PARENT: {
+ NodeInfo par = node.getParent();
+ if (par == null) {
+ return false;
+ }
+ if (!upperPattern.matchesBeneathAnchor(par, anchor, context)) {
+ return false;
+ }
+ break;
+ }
+ case AxisInfo.ANCESTOR: {
+ NodeInfo anc = node.getParent();
+ while (true) {
+ if (anc == null) {
+ return false;
+ }
+ if (upperPattern.matchesBeneathAnchor(anc, anchor, context)) {
+ break;
+ }
+ anc = anc.getParent();
+ }
+ break;
+ }
+ case AxisInfo.ANCESTOR_OR_SELF: {
+ NodeInfo anc = node;
+ while (true) {
+ if (anc == null) {
+ return false;
+ }
+ if (upperPattern.matchesBeneathAnchor(anc, anchor, context)) {
+ break;
+ }
+ anc = anc.getParent();
+ }
+ break;
+ }
+ default:
+ throw new XPathException("Unsupported axis " + AxisInfo.axisName[upwardsAxis] + " in pattern");
+ }
+ }
+
+
+ return true;
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Node.NODE
+ *
+ * @return the type of node matched by this pattern. e.g. Node.ELEMENT or Node.TEXT
+ */
+
+ public int getNodeKind() {
+ return basePattern.getNodeKind();
+ }
+
+ /**
+ * Determine the fingerprint of nodes to which this pattern applies.
+ * Used for optimisation.
+ *
+ * @return the fingerprint of nodes matched by this pattern.
+ */
+
+ public int getFingerprint() {
+ return basePattern.getFingerprint();
+ }
+
+ /**
+ * Get a NodeTest that all the nodes matching this pattern must satisfy
+ */
+
+ public ItemType getItemType() {
+ if (refinedItemType != null) {
+ return refinedItemType;
+ }
+ return basePattern.getItemType();
+ }
+
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof AncestorQualifiedPattern) {
+ AncestorQualifiedPattern aqp = (AncestorQualifiedPattern)other;
+ return basePattern.equals(aqp.basePattern) && upperPattern.equals(aqp.upperPattern) && upwardsAxis == aqp.upwardsAxis;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return 88267 ^ basePattern.hashCode() ^ upperPattern.hashCode() ^ (upwardsAxis<<22);
+ }
+
+
+}
+
diff --git a/sf/saxon/pattern/AnchorPattern.java b/sf/saxon/pattern/AnchorPattern.java
new file mode 100644
index 0000000..309a880
--- /dev/null
+++ b/sf/saxon/pattern/AnchorPattern.java
@@ -0,0 +1,85 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+
+/**
+ * This is a special pattern that matches the "anchor node"; it is not used for XSLT patterns,
+ * but for the selectors that arise when evaluating XPath expressions in streaming mode; the anchor
+ * node is the context node for the streamed XPath evaluation.
+ */
+public class AnchorPattern extends Pattern {
+
+ private NodeTest nodeTest = AnyNodeTest.getInstance();
+
+ /**
+ * Type-check the pattern.
+ * @param visitor the expression visitor
+ * @param contextItemType the type of the context item at the point where the pattern
+ * is defined. Set to null if it is known that the context item is undefined.
+ * @return the optimised Pattern
+ */
+
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ if (contextItemType.itemType instanceof NodeTest) {
+ nodeTest = (NodeTest)contextItemType.itemType;
+ }
+ return this;
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ return anchor == null || node == anchor;
+ }
+
+ /**
+ * Determine whether this Pattern matches the given Node. This is the main external interface
+ * for matching patterns: it sets current() to the node being tested
+ *
+ *
+ *
+ * @param item The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ // return false;
+ return true;
+ }
+
+ /**
+ * Get a NodeTest that all the nodes matching this pattern must satisfy
+ * @return a NodeTest, as specific as possible, which all the matching nodes satisfy
+ */
+
+ public ItemType getItemType() {
+ return nodeTest;
+ }
+
+ /*@NotNull*/ public String toString() {
+ return ".";
+ }
+}
+
diff --git a/sf/saxon/pattern/AnyChildNodeTest.java b/sf/saxon/pattern/AnyChildNodeTest.java
new file mode 100644
index 0000000..666b29f
--- /dev/null
+++ b/sf/saxon/pattern/AnyChildNodeTest.java
@@ -0,0 +1,128 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.Type;
+
+/**
+* An AnyChildNodePattern is the pattern node(), which matches any node except a root node,
+* an attribute node, or a namespace node: in other words, any node that is the child of another
+* node.
+*/
+
+public final class AnyChildNodeTest extends NodeTest {
+
+ private final static AnyChildNodeTest THE_INSTANCE = new AnyChildNodeTest();
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular instance
+ */
+
+ public static AnyChildNodeTest getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private AnyChildNodeTest() {}
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, NodeName name, int annotation) {
+ return (nodeKind == Type.ELEMENT ||
+ nodeKind == Type.TEXT ||
+ nodeKind == Type.COMMENT ||
+ nodeKind == Type.PROCESSING_INSTRUCTION);
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided
+ * so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ */
+
+ public boolean matches(TinyTree tree, int nodeNr) {
+ int nodeKind = tree.nodeKind[nodeNr];
+ return (nodeKind == Type.ELEMENT ||
+ nodeKind == Type.TEXT ||
+ nodeKind == Type.WHITESPACE_TEXT ||
+ nodeKind == Type.COMMENT ||
+ nodeKind == Type.PROCESSING_INSTRUCTION);
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes.
+ * @param node the node to be matched
+ */
+
+ public boolean matches(NodeInfo node) {
+ int nodeKind = node.getNodeKind();
+ return (nodeKind == Type.ELEMENT ||
+ nodeKind == Type.TEXT ||
+ nodeKind == Type.COMMENT ||
+ nodeKind == Type.PROCESSING_INSTRUCTION);
+ }
+
+
+
+ /**
+ * Determine the default priority to use if this pattern appears as a match pattern
+ * for a template with no explicit priority attribute.
+ */
+
+ public double getDefaultPriority() {
+ return -0.5;
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ return 1<<Type.ELEMENT | 1<<Type.TEXT | 1<<Type.COMMENT | 1<<Type.PROCESSING_INSTRUCTION;
+ }
+
+ /*@NotNull*/ public String toString() {
+ return "node()";
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return "AnyChildNodeTest".hashCode();
+ }
+
+}
+
diff --git a/sf/saxon/pattern/AnyNodeTest.java b/sf/saxon/pattern/AnyNodeTest.java
new file mode 100644
index 0000000..64d5e62
--- /dev/null
+++ b/sf/saxon/pattern/AnyNodeTest.java
@@ -0,0 +1,150 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+
+/**
+ * NodeTest is an interface that enables a test of whether a node has a particular
+ * name and type. An AnyNodeTest matches any node.
+ *
+ * @author Michael H. Kay
+ */
+
+public final class AnyNodeTest extends NodeTest implements QNameTest {
+
+ private static AnyNodeTest THE_INSTANCE = new AnyNodeTest();
+
+ /**
+ * Get an instance of AnyNodeTest
+ */
+
+ public static AnyNodeTest getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * Private constructor
+ */
+
+ private AnyNodeTest() {}
+
+ /**
+ * Test whether a given item conforms to this type
+ * @param item The item to be tested
+ * @param allowURIPromotion
+ * @param config
+ * @return true if the item is an instance of this type; false otherwise
+ */
+
+ public boolean matchesItem(Item item, boolean allowURIPromotion, Configuration config) {
+ return (item instanceof NodeInfo);
+ }
+
+ public ItemType getSuperType(TypeHierarchy th) {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, NodeName name, int annotation) {
+ return nodeKind != Type.PARENT_POINTER;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided
+ * so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ */
+
+ public boolean matches(TinyTree tree, int nodeNr) {
+ return tree.nodeKind[nodeNr] != Type.PARENT_POINTER;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes.
+ * @param node the node to be matched
+ */
+
+ public boolean matches(NodeInfo node) {
+ return true;
+ }
+
+ /**
+ * Test whether this QNameTest matches a given QName
+ * @param qname the QName to be matched
+ * @return true if the name matches, false if not
+ */
+
+ public boolean matches(StructuredQName qname) {
+ return true;
+ }
+
+ /**
+ * Determine the default priority of this node test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return -0.5;
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ return 1<<Type.ELEMENT | 1<<Type.TEXT | 1<<Type.COMMENT | 1<<Type.PROCESSING_INSTRUCTION |
+ 1<<Type.ATTRIBUTE | 1<<Type.NAMESPACE | 1<<Type.DOCUMENT;
+ }
+
+ /*@NotNull*/ public String toString() {
+ return "node()";
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return "AnyNodeTest".hashCode();
+ }
+
+}
+
diff --git a/sf/saxon/pattern/BooleanExpressionPattern.java b/sf/saxon/pattern/BooleanExpressionPattern.java
new file mode 100644
index 0000000..cf257f5
--- /dev/null
+++ b/sf/saxon/pattern/BooleanExpressionPattern.java
@@ -0,0 +1,171 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.MonoIterator;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+
+import java.util.Iterator;
+
+/**
+ * A BooleanExpressionPattern is a pattern of the form ?{ Expr } introduced in XSLT 3.0. It matches
+ * an item if the expression has an effective boolean value of true() when evaluated with that item
+ * as the singleton focus.
+ * @author Michael H. Kay
+ */
+
+public class BooleanExpressionPattern extends Pattern {
+
+ private Expression expression;
+ /**
+ * Create a BooleanExpressionPattern
+ * @param expression the expression to be evaluated
+ */
+
+ public BooleanExpressionPattern(Expression expression) {
+ this.expression = expression;
+ }
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ * @param slotManager holds details of the allocated slots
+ * @param nextFree the next slot that is free to be allocated @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ return ExpressionTool.allocateSlots(expression, nextFree, slotManager);
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ *
+ * @return an iterator over the subexpressions. Default implementation returns an empty sequence
+ */
+ /*@NotNull*/
+ @Override
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(expression);
+ }
+
+ /**
+ * Type-check the pattern.
+ *
+ * @param visitor the expression visitor
+ * @param contextItemType the type of the context item at the point where the pattern
+ * is defined. Set to null if it is known that the context item is undefined.
+ * @return the optimised Pattern
+ */
+ @Override
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(getItemType(), false);
+ expression = visitor.typeCheck(expression, cit);
+ return this;
+ }
+
+
+ /**
+ * Determine whether this Pattern matches the given item. This is the main external interface
+ * for matching patterns: it sets current() to the node being tested
+ * @param item The item to be tested against the Pattern
+ * @param context The context in which the match is to take place. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the item matches the Pattern, false otherwise
+ */
+
+ public boolean matches(Item item, XPathContext context) {
+ XPathContext c2 = context.newMinorContext();
+ UnfailingIterator<Item> iter = SingletonIterator.makeIterator(item);
+ iter.next();
+ c2.setCurrentIterator(iter);
+ try {
+ return expression.effectiveBooleanValue(c2);
+ } catch (XPathException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Get an Itemtype that all the items matching this pattern must satisfy
+ */
+
+ public ItemType getItemType() {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Determine the default priority of this item type test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return 1;
+ }
+
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Type.NODE. For patterns that
+ * do not match nodes, return -1.
+ *
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getNodeKind() {
+ return -1;
+ }
+
+ /**
+ * Determine the name fingerprint of nodes to which this pattern applies. Used for
+ * optimisation.
+ *
+ * @return A fingerprint that the nodes must match, or -1 if it can match multiple fingerprints
+ */
+
+ public int getFingerprint() {
+ return -1;
+ }
+
+ /**
+ * Display the pattern for diagnostics
+ */
+
+ public String toString() {
+ return "?{" + expression.toString() + "}";
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ return (other instanceof BooleanExpressionPattern) &&
+ ((BooleanExpressionPattern)other).expression.equals(expression);
+ }
+
+ /**
+ * Hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return 0x7aeffea9 ^ expression.hashCode();
+ }
+
+
+
+}
+
diff --git a/sf/saxon/pattern/CombinedNodeTest.java b/sf/saxon/pattern/CombinedNodeTest.java
new file mode 100644
index 0000000..3bd9734
--- /dev/null
+++ b/sf/saxon/pattern/CombinedNodeTest.java
@@ -0,0 +1,391 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.*;
+import net.sf.saxon.z.IntSet;
+
+/**
+ * A CombinedNodeTest combines two nodetests using one of the operators
+ * union (=or), intersect (=and), difference (= "and not"). This arises
+ * when optimizing a union (etc) of two path expressions using the same axis.
+ * A CombinedNodeTest is also used to support constructs such as element(N,T),
+ * which can be expressed as (element(N,*) AND element(*,T))
+ *
+ * @author Michael H. Kay
+ */
+
+public class CombinedNodeTest extends NodeTest {
+
+ private NodeTest nodetest1;
+ private NodeTest nodetest2;
+ private int operator;
+
+ /**
+ * Create a NodeTest that combines two other node tests
+ *
+ * @param nt1 the first operand. Note that if the defaultPriority of the pattern
+ * is required, it will be taken from that of the first operand.
+ * @param operator one of Token.UNION, Token.INTERSECT, Token.EXCEPT
+ * @param nt2 the second operand
+ */
+
+ public CombinedNodeTest(NodeTest nt1, int operator, NodeTest nt2) {
+ nodetest1 = nt1;
+ this.operator = operator;
+ nodetest2 = nt2;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, NodeName name, int annotation) {
+ switch (operator) {
+ case Token.UNION:
+ return nodetest1 == null ||
+ nodetest2 == null ||
+ nodetest1.matches(nodeKind, name, annotation) ||
+ nodetest2.matches(nodeKind, name, annotation);
+ case Token.INTERSECT:
+ return (nodetest1 == null || nodetest1.matches(nodeKind, name, annotation)) &&
+ (nodetest2 == null || nodetest2.matches(nodeKind, name, annotation));
+ case Token.EXCEPT:
+ return (nodetest1 == null || nodetest1.matches(nodeKind, name, annotation)) &&
+ !(nodetest2 == null || nodetest2.matches(nodeKind, name, annotation));
+ default:
+ throw new IllegalArgumentException("Unknown operator in Combined Node Test");
+ }
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided
+ * so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ */
+
+ public boolean matches(/*@NotNull*/ TinyTree tree, int nodeNr) {
+ switch (operator) {
+ case Token.UNION:
+ return nodetest1 == null ||
+ nodetest2 == null ||
+ nodetest1.matches(tree, nodeNr) ||
+ nodetest2.matches(tree, nodeNr);
+ case Token.INTERSECT:
+ return (nodetest1 == null || nodetest1.matches(tree, nodeNr)) &&
+ (nodetest2 == null || nodetest2.matches(tree, nodeNr));
+ case Token.EXCEPT:
+ return (nodetest1 == null || nodetest1.matches(tree, nodeNr)) &&
+ !(nodetest2 == null || nodetest2.matches(tree, nodeNr));
+ default:
+ throw new IllegalArgumentException("Unknown operator in Combined Node Test");
+ }
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes.
+ *
+ * @param node the node to be matched
+ */
+
+ public boolean matches(/*@NotNull*/ NodeInfo node) {
+ switch (operator) {
+ case Token.UNION:
+ return nodetest1 == null ||
+ nodetest2 == null ||
+ nodetest1.matches(node) ||
+ nodetest2.matches(node);
+ case Token.INTERSECT:
+ return (nodetest1 == null || nodetest1.matches(node)) &&
+ (nodetest2 == null || nodetest2.matches(node));
+ case Token.EXCEPT:
+ return (nodetest1 == null || nodetest1.matches(node)) &&
+ !(nodetest2 == null || nodetest2.matches(node));
+ default:
+ throw new IllegalArgumentException("Unknown operator in Combined Node Test");
+ }
+ }
+
+ public String toString() {
+ if (nodetest1 instanceof NameTest && operator == Token.INTERSECT) {
+ int kind = nodetest1.getPrimitiveType();
+ String skind = (kind == Type.ELEMENT ? "element(" : "attribute(");
+ String content = "";
+ if (nodetest2 instanceof ContentTypeTest) {
+ final SchemaType schemaType = ((ContentTypeTest) nodetest2).getSchemaType();
+ content = ", " + schemaType.getDisplayName();
+ }
+ String name;
+ if (nodetest1 instanceof NameTest) {
+ name = ((NameTest)nodetest1).getNodeName().getDisplayName();
+ } else {
+ name = nodetest1.toString();
+ }
+ return skind + name + content + ')';
+ } else {
+ String nt1 = (nodetest1 == null ? "true()" : nodetest1.toString());
+ String nt2 = (nodetest2 == null ? "true()" : nodetest2.toString());
+ return '(' + nt1 + ' ' + Token.tokens[operator] + ' ' + nt2 + ')';
+ }
+ }
+
+
+ /**
+ * Get the supertype of this type. This isn't actually a well-defined concept: the types
+ * form a lattice rather than a strict hierarchy.
+ *
+ * @param th the type hierarchy cache
+ */
+
+ public ItemType getSuperType(TypeHierarchy th) {
+ switch (operator) {
+ case Token.UNION:
+ return Type.getCommonSuperType(nodetest1, nodetest2, th);
+ case Token.INTERSECT:
+ return nodetest1;
+ case Token.EXCEPT:
+ return nodetest1;
+ default:
+ throw new IllegalArgumentException("Unknown operator in Combined Node Test");
+ }
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ switch (operator) {
+ case Token.UNION:
+ return nodetest1.getNodeKindMask() | nodetest2.getNodeKindMask();
+ case Token.INTERSECT:
+ return nodetest1.getNodeKindMask() & nodetest2.getNodeKindMask();
+ case Token.EXCEPT:
+ return nodetest1.getNodeKindMask();
+ default:
+ return 0;
+ }
+
+ }
+
+ /**
+ * Get the basic kind of object that this ItemType matches: for a NodeTest, this is the kind of node,
+ * or Type.Node if it matches different kinds of nodes.
+ *
+ * @return the node kind matched by this node test
+ */
+
+ public int getPrimitiveType() {
+ int mask = getNodeKindMask();
+ if (mask == (1 << Type.ELEMENT)) {
+ return Type.ELEMENT;
+ }
+ if (mask == (1 << Type.ATTRIBUTE)) {
+ return Type.ATTRIBUTE;
+ }
+ if (mask == (1 << Type.DOCUMENT)) {
+ return Type.DOCUMENT;
+ }
+ return Type.NODE;
+ }
+
+ /**
+ * Get the set of node names allowed by this NodeTest. This is returned as a set of Integer fingerprints.
+ * A null value indicates that all names are permitted (i.e. that there are no constraints on the node name).
+ * The default implementation returns null.
+ */
+
+ /*@NotNull*/
+ public IntSet getRequiredNodeNames() {
+ IntSet s1 = nodetest1.getRequiredNodeNames();
+ IntSet s2 = nodetest2.getRequiredNodeNames();
+ switch (operator) {
+ case Token.UNION: {
+ return s1.union(s2);
+ }
+ case Token.INTERSECT: {
+ return s1.intersect(s2);
+ }
+ case Token.EXCEPT: {
+ return s1.except(s2);
+ }
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Get the content type allowed by this NodeTest (that is, the type annotation of the matched nodes).
+ * Return AnyType if there are no restrictions. The default implementation returns AnyType.
+ */
+
+ public SchemaType getContentType() {
+ SchemaType type1 = nodetest1.getContentType();
+ SchemaType type2 = nodetest2.getContentType();
+ if (type1.isSameType(type2)) return type1;
+ if (operator == Token.INTERSECT) {
+ if (type2 instanceof AnyType || (type2 instanceof AnySimpleType && type1.isSimpleType())) {
+ return type1;
+ }
+ if (type1 instanceof AnyType || (type1 instanceof AnySimpleType && type2.isSimpleType())) {
+ return type2;
+ }
+ }
+ return AnyType.getInstance();
+ }
+
+ /**
+ * Get the item type of the atomic values that will be produced when an item
+ * of this type is atomized (assuming that atomization succeeds)
+ */
+
+ /*@NotNull*/
+ public AtomicType getAtomizedItemType() {
+ AtomicType type1 = nodetest1.getAtomizedItemType();
+ AtomicType type2 = nodetest2.getAtomizedItemType();
+ if (type1.isSameType(type2)) return type1;
+ if (operator == Token.INTERSECT) {
+ if (type2.equals(BuiltInAtomicType.ANY_ATOMIC)) {
+ return type1;
+ }
+ if (type1.equals(BuiltInAtomicType.ANY_ATOMIC)) {
+ return type2;
+ }
+ }
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+
+ /**
+ * Ask whether values of this type are atomizable
+ *
+ * @return true unless it is known that these items will be elements with element-only
+ * content, in which case return false
+ */
+
+ public boolean isAtomizable() {
+ return nodetest1.isAtomizable() && nodetest2.isAtomizable();
+ }
+
+ /**
+ * Get the name of the nodes matched by this nodetest, if it matches a specific name.
+ * Return -1 if the node test matches nodes of more than one name
+ */
+
+ public int getFingerprint() {
+ int fp1 = nodetest1.getFingerprint();
+ int fp2 = nodetest2.getFingerprint();
+ if (fp1 == fp2) return fp1;
+ if (fp2 == -1 && operator == Token.INTERSECT) return fp1;
+ if (fp1 == -1 && operator == Token.INTERSECT) return fp2;
+ return -1;
+ }
+
+ /**
+ * Determine whether the content type (if present) is nillable
+ *
+ * @return true if the content test (when present) can match nodes that are nilled
+ */
+
+ public boolean isNillable() {
+ // this should err on the safe side
+ return nodetest1.isNillable() && nodetest2.isNillable();
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return nodetest1.hashCode() ^ nodetest2.hashCode();
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ public boolean equals(Object other) {
+ return other instanceof CombinedNodeTest &&
+ ((CombinedNodeTest) other).nodetest1.equals(nodetest1) &&
+ ((CombinedNodeTest) other).nodetest2.equals(nodetest2) &&
+ ((CombinedNodeTest) other).operator == operator;
+ }
+
+ /**
+ * Get the default priority of this nodeTest when used as a pattern. In the case of a union, this will always
+ * be (arbitrarily) the default priority of the first operand. In other cases, again somewhat arbitrarily, it
+ * is 0.25, reflecting the common usage of an intersection to represent the pattern element(E, T).
+ */
+
+ public double getDefaultPriority() {
+ if (operator == Token.UNION) {
+ return nodetest1.getDefaultPriority();
+ } else {
+ // typically it's element(E, T)
+ return 0.25;
+ }
+ }
+
+ /**
+ * Get the two parts of the combined node test
+ *
+ * @return the two operands
+ */
+
+ /*@NotNull*/
+ public NodeTest[] getComponentNodeTests() {
+ return new NodeTest[]{nodetest1, nodetest2};
+ }
+
+ /**
+ * Get the operator used to combine the two node tests: one of {@link net.sf.saxon.expr.parser.Token#UNION},
+ * {@link net.sf.saxon.expr.parser.Token#INTERSECT}, {@link net.sf.saxon.expr.parser.Token#EXCEPT},
+ *
+ * @return the operator
+ */
+
+ public int getOperator() {
+ return operator;
+ }
+
+ /**
+ * Visit all the schema components used in this ItemType definition
+ *
+ * @param visitor the visitor class to be called when each component is visited
+ */
+
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException {
+ nodetest1.visitNamedSchemaComponents(visitor);
+ nodetest2.visitNamedSchemaComponents(visitor);
+ }
+}
+
diff --git a/sf/saxon/pattern/ConditionalPattern.java b/sf/saxon/pattern/ConditionalPattern.java
new file mode 100644
index 0000000..379ac7a
--- /dev/null
+++ b/sf/saxon/pattern/ConditionalPattern.java
@@ -0,0 +1,328 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.MultiIterator;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * A ConditionalPattern tests a node against one of a number of patterns depending on the value of a condition.
+ * This is used only as a streaming selection.
+ */
+public class ConditionalPattern extends Pattern {
+
+ private Expression[] conditions;
+ private Pattern[] patterns;
+
+ public ConditionalPattern(Expression[] conditions, Pattern[] patterns) {
+ this.conditions = conditions;
+ this.patterns = patterns;
+ }
+
+
+ /**
+ * Set the executable containing this pattern
+ *
+ * @param executable the executable
+ */
+
+ public void setExecutable(Executable executable) {
+ for (int i=0; i<conditions.length; i++) {
+ patterns[i].setExecutable(executable);
+ }
+ super.setExecutable(executable);
+ }
+
+ /**
+ * Simplify the pattern: perform any context-independent optimisations
+ *
+ * @param visitor an expression visitor
+ */
+
+ public Pattern simplify(ExpressionVisitor visitor) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i] = visitor.simplify(conditions[i]);
+ patterns[i] = patterns[i].simplify(visitor);
+ }
+ return this;
+ }
+
+ /**
+ * Type-check the pattern.
+ * This is only needed for patterns that contain variable references or function calls.
+ *
+ * @return the optimised Pattern
+ */
+
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i] = visitor.typeCheck(conditions[i], contextItemType);
+ patterns[i] = patterns[i].analyze(visitor, contextItemType);
+ }
+ return this;
+ }
+
+ /**
+ * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>Unlike the corresponding method on {@link net.sf.saxon.expr.Expression}, this method does not return anything:
+ * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
+ * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
+ * variable references within the pattern.
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public void promote(PromotionOffer offer, Expression parent) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ conditions[i] = conditions[i].promote(offer, parent);
+ patterns[i].promote(offer, parent);
+ }
+ }
+
+ /**
+ * Replace a subexpression by a replacement subexpression
+ * @param original the expression to be replaced
+ * @param replacement the new expression to be inserted in its place
+ * @return true if the replacement was carried out
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+ for (int i=0; i<conditions.length; i++) {
+ found |= conditions[i].replaceSubExpression(original, replacement);
+ found |= patterns[i].replaceSubExpression(original, replacement);
+ }
+ return found;
+ }
+
+ /**
+ * Set the original text
+ */
+
+ public void setOriginalText(String pattern) {
+ super.setOriginalText(pattern);
+ for (int i=0; i<conditions.length; i++) {
+ patterns[i].setOriginalText(pattern);
+ }
+ }
+
+//#ifdefined STREAM
+ @Override
+ public boolean isMotionless(boolean allowExtensions) {
+ for (Expression condition : conditions) {
+ if (condition.getStreamability(Expression.NAVIGATION_CONTEXT, allowExtensions, null) != Expression.W3C_MOTIONLESS) {
+ return false;
+ }
+ }
+ for (Pattern p : patterns) {
+ if (!p.isMotionless(allowExtensions)) {
+ return false;
+ }
+ }
+ return true;
+ }
+//#endif
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ *
+ * @param slotManager
+ * @param nextFree the next slot that is free to be allocated @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+
+ for (int i=0; i<conditions.length; i++) {
+ nextFree = ExpressionTool.allocateSlots(conditions[i], nextFree, slotManager);
+ nextFree = patterns[i].allocateSlots(slotManager, nextFree);
+ }
+ return nextFree;
+ }
+
+ /**
+ * Determine if the supplied node matches the pattern
+ *
+ *
+ *
+ *
+ * @param item the node to be compared
+ * @return true if the node matches either of the operand patterns
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ for (int i=0; i<conditions.length; i++) {
+ boolean b = conditions[i].effectiveBooleanValue(context);
+ if (b) {
+ return patterns[i].matches(item, context);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ XPathContext c2 = context.newContext();
+ UnfailingIterator iter = SingletonIterator.makeIterator(anchor);
+ iter.next();
+ c2.setCurrentIterator(iter);
+ for (int i=0; i<conditions.length; i++) {
+ boolean b = conditions[i].effectiveBooleanValue(c2);
+ if (b) {
+ return patterns[i].matchesBeneathAnchor(node, anchor, context);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Type.NODE
+ *
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getNodeKind() {
+ int type = patterns[0].getNodeKind();
+ for (int i=1; i<patterns.length; i++) {
+ int p = patterns[i].getNodeKind();
+ if (p != type) {
+ return Type.NODE;
+ }
+ }
+ return type;
+ }
+
+ /**
+ * Get a NodeTest that all the nodes matching this pattern must satisfy
+ */
+
+ public ItemType getItemType() {
+ int nodeType = getNodeKind();
+ if (nodeType == Type.NODE) {
+ return AnyNodeTest.getInstance();
+ } else {
+ return NodeKindTest.makeNodeKindTest(nodeType);
+ }
+ }
+
+
+ /**
+ * Get the dependencies of the pattern. The only possible dependency for a pattern is
+ * on local variables. This is analyzed in those patterns where local variables may appear.
+ *
+ * @return the dependencies, as a bit-significant mask
+ */
+
+ public int getDependencies() {
+ int d = 0;
+ for (int i=0; i<conditions.length; i++) {
+ d |= conditions[i].getDependencies();
+ d |= patterns[i].getDependencies();
+ }
+ return d;
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ * @return an iterator over the subexpressions.
+ */
+
+ /*@NotNull*/
+ public Iterator iterateSubExpressions() {
+ Iterator[] c = new Iterator[conditions.length*2];
+ for (int i=0; i<conditions.length; i+=2) {
+ c[i] = conditions[i].iterateSubExpressions();
+ c[i+1] = patterns[i].iterateSubExpressions();
+ }
+ return new MultiIterator(c);
+ }
+
+ /**
+ * Override method to set the system ID
+ */
+
+ public void setSystemId(String systemId) {
+ super.setSystemId(systemId);
+ for (int i=0; i<conditions.length; i+=2) {
+ patterns[i].setSystemId(systemId);
+ }
+ }
+
+ /**
+ * Override method to set the line number
+ */
+
+ public void setLineNumber(int lineNumber) {
+ super.setLineNumber(lineNumber);
+ for (int i=0; i<conditions.length; i+=2) {
+ patterns[i].setLineNumber(lineNumber);
+ }
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ if (other instanceof ConditionalPattern) {
+ return Arrays.equals(conditions, ((ConditionalPattern)other).conditions) &&
+ Arrays.equals(patterns, ((ConditionalPattern)other).patterns);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ int h = 0x836b92a0;
+ for (int i=0; i<conditions.length; i+=2) {
+ h ^= patterns[i].hashCode() ^ conditions[i].hashCode();
+ }
+ return h;
+ }
+
+}
+
diff --git a/sf/saxon/pattern/ContentTypeTest.java b/sf/saxon/pattern/ContentTypeTest.java
new file mode 100644
index 0000000..5ba6f2d
--- /dev/null
+++ b/sf/saxon/pattern/ContentTypeTest.java
@@ -0,0 +1,267 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.functions.Nilled;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.*;
+
+/**
+ * NodeTest is an interface that enables a test of whether a node matches particular
+ * conditions. ContentTypeTest tests for an element or attribute node with a particular
+ * type annotation.
+ *
+ * @author Michael H. Kay
+ */
+
+public class ContentTypeTest extends NodeTest {
+
+ private int kind; // element or attribute
+ private SchemaType schemaType;
+ private int schemaTypeFingerprint;
+ private Configuration config;
+ private boolean nillable = false;
+
+ /**
+ * Create a ContentTypeTest
+ * @param nodeKind the kind of nodes to be matched: always elements or attributes
+ * @param schemaType the required type annotation, as a simple or complex schema type
+ * @param config the Configuration, supplied because this KindTest needs access to schema information
+ * @param nillable indicates whether an element with xsi:nil=true satisifies the test
+ */
+
+ public ContentTypeTest(int nodeKind, SchemaType schemaType, Configuration config, boolean nillable) {
+ this.kind = nodeKind;
+ this.schemaType = schemaType;
+ this.schemaTypeFingerprint = schemaType.getFingerprint();
+ this.config = config;
+ this.nillable = nillable;
+ }
+
+ /**
+ * Indicate whether nilled elements should be matched (the default is false)
+ * @param nillable true if nilled elements should be matched
+ */
+ public void setNillable(boolean nillable) {
+ this.nillable = nillable;
+ }
+
+ /**
+ * The test is nillable if a question mark was specified as the occurrence indicator
+ * @return true if the test is nillable
+ */
+
+ public boolean isNillable() {
+ return nillable;
+ }
+
+ public SchemaType getSchemaType() {
+ return schemaType;
+ }
+
+ public int getNodeKind() {
+ return kind;
+ }
+
+ public ItemType getSuperType(TypeHierarchy th) {
+ return NodeKindTest.makeNodeKindTest(kind);
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, NodeName name, int annotation) {
+ return kind == nodeKind && matchesAnnotation(annotation);
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided
+ * so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ */
+
+ public boolean matches(/*@NotNull*/ TinyTree tree, int nodeNr) {
+ return kind == tree.getNodeKind(nodeNr) &&
+ matchesAnnotation(tree.getTypeAnnotation(nodeNr)) &&
+ (nillable || !tree.isNilled(nodeNr));
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes.
+ * @param node the node to be matched
+ */
+
+ public boolean matches(/*@NotNull*/ NodeInfo node) {
+ return node.getNodeKind() == kind &&
+ matchesAnnotation(node.getTypeAnnotation())
+ && (nillable || !Nilled.isNilled(node));
+ }
+
+ private boolean matchesAnnotation(int annotation) {
+ if (schemaTypeFingerprint == StandardNames.XS_ANY_TYPE) {
+ return true;
+ }
+
+ if (annotation == schemaTypeFingerprint) {
+ return true;
+ }
+
+ // see if the type annotation is a subtype of the required type
+
+
+ try {
+ SchemaType type = config.getSchemaType(annotation).getBaseType();
+ if (type == null) {
+ // only true if annotation = XS_ANY_TYPE
+ return false;
+ }
+ ItemType actual = new ContentTypeTest(kind, type, config, false);
+ return config.getTypeHierarchy().isSubType(actual, this);
+ } catch (UnresolvedReferenceException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ //return false;
+ }
+
+ /**
+ * Determine the default priority of this node test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return 0;
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getPrimitiveType() {
+ return kind;
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ return 1<<kind;
+ }
+
+ /**
+ * Get the content type allowed by this NodeTest (that is, the type annotation of the matched nodes).
+ * Return AnyType if there are no restrictions. The default implementation returns AnyType.
+ */
+
+ public SchemaType getContentType() {
+ return schemaType;
+ }
+
+ /**
+ * Get the item type of the atomic values that will be produced when an item
+ * of this type is atomized (assuming that atomization succeeds)
+ */
+
+ /*@NotNull*/
+ public AtomicType getAtomizedItemType() {
+ SchemaType type = schemaType;
+ if (type.isAtomicType()) {
+ return (AtomicType)type;
+ } else if (type instanceof ListType) {
+ SimpleType mem = ((ListType)type).getItemType();
+ if (mem.isAtomicType()) {
+ return (AtomicType)mem;
+ }
+ } else if (type instanceof ComplexType && ((ComplexType)type).isSimpleContent()) {
+ SimpleType ctype = ((ComplexType)type).getSimpleContentType();
+ assert ctype != null;
+ if (ctype.isAtomicType()) {
+ return (AtomicType)ctype;
+ } else if (ctype instanceof ListType) {
+ SimpleType mem = ((ListType)ctype).getItemType();
+ if (mem.isAtomicType()) {
+ return (AtomicType)mem;
+ }
+ }
+ }
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+
+ /**
+ * Ask whether values of this type are atomizable
+ * @return true unless it is known that these items will be elements with element-only
+ * content, in which case return false
+ */
+
+ public boolean isAtomizable() {
+ return !(schemaType.isComplexType() &&
+ ((ComplexType)schemaType).getVariety() == ComplexType.VARIETY_ELEMENT_ONLY);
+ }
+
+ /**
+ * Visit all the schema components used in this ItemType definition
+ * @param visitor the visitor class to be called when each component is visited
+ */
+
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException {
+ visitor.visitSchemaComponent(schemaType);
+ }
+
+ public String toString() {
+ return (kind == Type.ELEMENT ? "element(*, " : "attribute(*, ") +
+ schemaType.getDescription() + ')';
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return kind<<20 ^ schemaTypeFingerprint;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ public boolean equals(Object other) {
+ return other instanceof ContentTypeTest &&
+ ((ContentTypeTest)other).kind == kind &&
+ ((ContentTypeTest)other).schemaType == schemaType &&
+ ((ContentTypeTest)other).schemaTypeFingerprint == schemaTypeFingerprint &&
+ ((ContentTypeTest)other).nillable == nillable;
+ }
+
+}
+
diff --git a/sf/saxon/pattern/DocumentNodeTest.java b/sf/saxon/pattern/DocumentNodeTest.java
new file mode 100644
index 0000000..f9dd062
--- /dev/null
+++ b/sf/saxon/pattern/DocumentNodeTest.java
@@ -0,0 +1,162 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.Type;
+
+/**
+ * A DocumentNodeTest implements the test document-node(element(~,~))
+ */
+
+// This is messy because the standard interface for a NodeTest does not allow
+// any navigation from the node in question - it only tests for the node kind,
+// node name, and type annotation of the node.
+
+public class DocumentNodeTest extends NodeTest {
+
+
+ private NodeTest elementTest;
+
+ public DocumentNodeTest(NodeTest elementTest) {
+ this.elementTest = elementTest;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, NodeName name, int annotation) {
+ throw new UnsupportedOperationException("DocumentNodeTest doesn't support this method");
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided
+ * so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ */
+
+ public boolean matches(TinyTree tree, int nodeNr) {
+ if (tree.getNodeKind(nodeNr) != Type.DOCUMENT) {
+ return false;
+ }
+ return matches(tree.getNode(nodeNr));
+ }
+
+ /**
+ * Determine whether this Pattern matches the given Node.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matches(NodeInfo node) {
+ if (node.getNodeKind() != Type.DOCUMENT) {
+ return false;
+ }
+ AxisIterator iter = node.iterateAxis(AxisInfo.CHILD);
+ // The match is true if there is exactly one element node child, no text node
+ // children, and the element node matches the element test.
+ boolean found = false;
+ while (true) {
+ NodeInfo n = (NodeInfo)iter.next();
+ if (n==null) {
+ return found;
+ }
+ int kind = n.getNodeKind();
+ if (kind==Type.TEXT) {
+ return false;
+ } else if (kind==Type.ELEMENT) {
+ if (found) {
+ return false;
+ }
+ if (elementTest.matches(n)) {
+ found = true;
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine the default priority of this node test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return elementTest.getDefaultPriority();
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getPrimitiveType() {
+ return Type.DOCUMENT;
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ return 1<<Type.DOCUMENT;
+ }
+
+ /**
+ * Get the element test contained within this document test
+ * @return the contained element test
+ */
+
+ public NodeTest getElementTest() {
+ return elementTest;
+ }
+
+ public String toString() {
+ return "document-node(" + elementTest.toString() + ')';
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return elementTest.hashCode()^12345;
+ }
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ return other instanceof DocumentNodeTest &&
+ ((DocumentNodeTest)other).elementTest.equals(elementTest);
+ }
+
+}
+
diff --git a/sf/saxon/pattern/EmptySequenceTest.java b/sf/saxon/pattern/EmptySequenceTest.java
new file mode 100644
index 0000000..2d505f7
--- /dev/null
+++ b/sf/saxon/pattern/EmptySequenceTest.java
@@ -0,0 +1,167 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaComponentVisitor;
+import net.sf.saxon.type.Type;
+
+/**
+ * NodeTest is an interface that enables a test of whether a node has a particular
+ * name and type. An EmptySequenceTest matches no nodes or atomic values: it corresponds to the
+ * type empty-sequence().
+ *
+ * @author Michael H. Kay
+ */
+
+public final class EmptySequenceTest extends NodeTest {
+
+ private static EmptySequenceTest THE_INSTANCE = new EmptySequenceTest();
+
+ /**
+ * Get a NoNodeTest instance
+ */
+
+ public static EmptySequenceTest getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * Private constructor
+ */
+
+ private EmptySequenceTest() {}
+
+ public final int getPrimitiveType() {
+ return Type.EMPTY;
+ }
+
+ /**
+ * Get the primitive item type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue it is Type.ATOMIC_VALUE. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that INTEGER is considered to be a primitive type.
+ */
+
+ /*@NotNull*/
+ public ItemType getPrimitiveItemType() {
+ return this;
+ }
+
+ /**
+ * Test whether a given item conforms to this type
+ * @param item The item to be tested
+ * @param allowURIPromotion
+ * @param config
+ * @return true if the item is an instance of this type; false otherwise
+ */
+
+ public boolean matchesItem(Item item, boolean allowURIPromotion, Configuration config) {
+ return false;
+ }
+
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, NodeName name, int annotation) {
+ return false;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided
+ * so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ */
+
+ public boolean matches(TinyTree tree, int nodeNr) {
+ return false;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes.
+ * @param node the node to be matched
+ */
+
+ public boolean matches(NodeInfo node) {
+ return false;
+ }
+
+
+ /**
+ * Determine the default priority of this node test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return -0.5;
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ return 0;
+ }
+
+ /**
+ * Visit all the schema components used in this ItemType definition
+ * @param visitor the visitor class to be called when each component is visited
+ */
+
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException {
+ // no action
+ }
+
+ /*@NotNull*/ public String toString() {
+ return "empty-sequence()";
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return "NoNodeTest".hashCode();
+ }
+
+
+
+
+}
+
diff --git a/sf/saxon/pattern/ExceptPattern.java b/sf/saxon/pattern/ExceptPattern.java
new file mode 100644
index 0000000..4600e55
--- /dev/null
+++ b/sf/saxon/pattern/ExceptPattern.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A pattern formed as the difference of two other patterns
+ */
+
+public class ExceptPattern extends VennPattern {
+
+ /**
+ * Constructor
+ *
+ * @param p1 the left-hand operand
+ * @param p2 the right-hand operand
+ */
+
+ public ExceptPattern(Pattern p1, Pattern p2) {
+ super(p1, p2);
+ }
+
+ /**
+ * Get an ItemType that all the items matching this pattern must satisfy
+ */
+ @Override
+ public ItemType getItemType() {
+ return p1.getItemType();
+ }
+
+
+ /**
+ * Determine if the supplied node matches the pattern
+ * @param item the node to be compared
+ * @return true if the node matches either of the operand patterns
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ return p1.matches(item, context) && !p2.matches(item, context);
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ return p1.matchesBeneathAnchor(node, anchor, context) &&
+ !p2.matchesBeneathAnchor(node, anchor, context);
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ if (other instanceof ExceptPattern) {
+ Set s0 = new HashSet(10);
+ gatherComponentPatterns(s0);
+ Set s1 = new HashSet(10);
+ ((ExceptPattern)other).gatherComponentPatterns(s1);
+ return s0.equals(s1);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return 0x9bd7dfa6 ^ p1.hashCode() ^ p2.hashCode();
+ }
+
+
+}
+
diff --git a/sf/saxon/pattern/GeneralNodePattern.java b/sf/saxon/pattern/GeneralNodePattern.java
new file mode 100644
index 0000000..828bea6
--- /dev/null
+++ b/sf/saxon/pattern/GeneralNodePattern.java
@@ -0,0 +1,301 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.functions.Current;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.type.ItemType;
+
+import java.util.Iterator;
+
+/**
+ * A GeneralNodePattern represents a pattern which, because of the presence of positional
+ * predicates or otherwise, can only be evaluated "the hard way", by evaluating the equivalent
+ * expression with successive ancestors of the tested node as context item.
+ */
+
+public final class GeneralNodePattern extends Pattern {
+
+
+ private Expression equivalentExpr = null;
+ private NodeTest itemType = null;
+
+ /**
+ * Create a GeneralNodePattern
+ */
+
+ public GeneralNodePattern(Expression expr, NodeTest itemType) {
+ equivalentExpr = expr;
+ this.itemType = itemType;
+ }
+
+ /**
+ * Test whether a pattern is motionless, that is, whether it can be evaluated against a node
+ * without repositioning the input stream. This is a necessary condition for patterns used
+ * as the match pattern of a streamed template rule.
+ * @return true if the pattern is motionless, that is, if it can be evaluated against a streamed
+ * node without changing the position in the streamed input file
+ * @param allowExtensions
+ */
+
+ public boolean isMotionless(boolean allowExtensions) {
+ return false;
+ }
+
+ /**
+ * Simplify the pattern: perform any context-independent optimisations
+ * @param visitor an expression visitor
+ */
+
+ public Pattern simplify(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Type-check the pattern, performing any type-dependent optimizations.
+ * @param visitor an expression visitor
+ * @param contextItemType the type of the context item at the point where the pattern appears
+ * @return the optimised Pattern
+ */
+
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ equivalentExpr = visitor.typeCheck(equivalentExpr, contextItemType);
+ ItemType type = equivalentExpr.getItemType(visitor.getConfiguration().getTypeHierarchy());
+ if (!(type instanceof NodeTest)) {
+ throw new XPathException("GeneralNodePattern can only match nodes");
+ }
+ return this;
+ }
+
+ /**
+ * Get the dependencies of the pattern. The only possible dependency for a pattern is
+ * on local variables. This is analyzed in those patterns where local variables may appear.
+ */
+
+ public int getDependencies() {
+ return equivalentExpr.getDependencies() & StaticProperty.DEPENDS_ON_LOCAL_VARIABLES;
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(equivalentExpr);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ return equivalentExpr.replaceSubExpression(original, replacement);
+ }
+
+ /**
+ * Replace any calls on current() by a variable reference bound to the supplied binding
+ */
+ @Override
+ public void bindCurrent(Binding binding) {
+ if (ExpressionTool.callsFunction(equivalentExpr, Current.FN_CURRENT)) {
+ replaceCurrent(equivalentExpr, binding);
+ }
+ }
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ * @param slotManager
+ * @param nextFree the next slot that is free to be allocated @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ return ExpressionTool.allocateSlots(equivalentExpr, nextFree, slotManager);
+ }
+
+ /**
+ * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>Unlike the corresponding method on {@link net.sf.saxon.expr.Expression}, this method does not return anything:
+ * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
+ * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
+ * variable references within the pattern.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public void promote(PromotionOffer offer, Expression parent) throws XPathException {
+
+ Binding[] savedBindingList = offer.bindingList;
+ equivalentExpr = equivalentExpr.promote(offer, parent);
+ offer.bindingList = savedBindingList;
+ }
+
+
+ /**
+ * Determine whether the pattern matches a given item.
+ * @param
+ * item the item to be tested
+ * @return true if the pattern matches, else false
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ if (!itemType.matches(item, context)) {
+ return false;
+ }
+ AxisIterator anc = ((NodeInfo)item).iterateAxis(AxisInfo.ANCESTOR_OR_SELF);
+ while (true) {
+ NodeInfo a = anc.next();
+ if (a == null) {
+ return false;
+ }
+ if (matchesBeneathAnchor((NodeInfo)item, a, context)) {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which if present must match any AnchorPattern subpattern; may be null
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ if (!itemType.matches(node, context)) {
+ return false;
+ }
+
+ // for a positional pattern, we do it the hard way: test whether the
+ // node is a member of the nodeset obtained by evaluating the
+ // equivalent expression
+
+ if (anchor == null) {
+ AxisIterator ancestors = node.iterateAxis(AxisInfo.ANCESTOR_OR_SELF);
+ while (true) {
+ NodeInfo ancestor = ancestors.next();
+ if (ancestor == null) {
+ return false;
+ }
+ if (matchesBeneathAnchor(node, ancestor, context)) {
+ return true;
+ }
+ }
+ }
+
+ // System.err.println("Testing positional pattern against node " + node.generateId());
+ XPathContext c2 = context.newMinorContext();
+ UnfailingIterator single = SingletonIterator.makeIterator(anchor);
+ single.next();
+ c2.setCurrentIterator(single);
+ try {
+ SequenceIterator nsv = equivalentExpr.iterate(c2);
+ while (true) {
+ NodeInfo n = (NodeInfo) nsv.next();
+ if (n == null) {
+ return false;
+ }
+ if (n.isSameNodeInfo(node)) {
+ return true;
+ }
+ }
+ } catch (XPathException e) {
+ XPathException err = new XPathException("An error occurred matching pattern {" + toString() + "}: ", e);
+ err.setXPathContext(c2);
+ err.setErrorCodeQName(e.getErrorCodeQName());
+ err.setLocator(this);
+ c2.getController().recoverableError(err);
+ return false;
+ }
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Node.NODE
+ *
+ * @return the type of node matched by this pattern. e.g. Node.ELEMENT or Node.TEXT
+ */
+
+ public int getNodeKind() {
+ return itemType.getPrimitiveType();
+ }
+
+ /**
+ * Determine the fingerprint of nodes to which this pattern applies.
+ * Used for optimisation.
+ *
+ * @return the fingerprint of nodes matched by this pattern.
+ */
+
+ public int getFingerprint() {
+ return itemType.getFingerprint();
+ }
+
+ /**
+ * Get a NodeTest that all the nodes matching this pattern must satisfy
+ */
+
+ public ItemType getItemType() {
+ return itemType;
+ }
+
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof GeneralNodePattern) {
+ GeneralNodePattern lpp = (GeneralNodePattern)other;
+ return equivalentExpr.equals(lpp.equivalentExpr);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return 83641 ^ equivalentExpr.hashCode();
+ }
+
+}
+
diff --git a/sf/saxon/pattern/GeneralPositionalPattern.java b/sf/saxon/pattern/GeneralPositionalPattern.java
new file mode 100644
index 0000000..804a7a3
--- /dev/null
+++ b/sf/saxon/pattern/GeneralPositionalPattern.java
@@ -0,0 +1,339 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ManualIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.type.ErrorType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.NumericValue;
+
+import java.util.Iterator;
+
+/**
+ * A GeneralPositionalPattern is a pattern of the form A[P] where A is an axis expression using the child axis
+ * and P is an expression that depends on the position. When this kind of pattern is used for matching streamed nodes,
+ * it relies on the histogram data of preceding siblings maintained as part of a
+ * {@link com.saxonica.stream.om.FleetingParentNode}
+ *
+ * This class handles cases where the predicate P is arbitrarily complex. Simple comparisons of position() against
+ * an integer value are handled by the class SimplePositionalPattern.
+ */
+public class GeneralPositionalPattern extends Pattern {
+
+ private NodeTest nodeTest;
+ private Expression positionExpr;
+ private boolean usesPosition = true;
+
+ /**
+ * Create a PositionalChildPattern
+ */
+
+ public GeneralPositionalPattern(NodeTest base, Expression positionExpr) {
+ this.nodeTest = base;
+ this.positionExpr = positionExpr;
+ }
+
+
+ /**
+ * Get the filter assocated with the pattern
+ * @return the filter predicate
+ */
+
+ public Expression getPositionExpr() {
+ return positionExpr;
+ }
+
+ /**
+ * Get the base pattern
+ * @return the base pattern before filtering
+ */
+
+ public NodeTest getNodeTest() {
+ return nodeTest;
+ }
+
+//#ifdefined STREAM
+ /**
+ * Test whether a pattern is motionless, that is, whether it can be evaluated against a node
+ * without repositioning the input stream. This is a necessary condition for patterns used
+ * as the match pattern of a streamed template rule.
+ * @param allowExtensions true if Saxon streamability extensions are allowed in the analysis
+ * @return true if the pattern is motionless, that is, if it can be evaluated against a streamed
+ * node without changing the position in the streamed input file
+ */
+
+ public boolean isMotionless(boolean allowExtensions) {
+ return positionExpr.getStreamability(Expression.INSPECTION_CONTEXT, allowExtensions, null) == Expression.W3C_MOTIONLESS;
+ }
+//#endif
+
+ /**
+ * Simplify the pattern: perform any context-independent optimisations
+ * @param visitor an expression visitor
+ */
+
+ public Pattern simplify(ExpressionVisitor visitor) throws XPathException {
+ positionExpr = visitor.simplify(positionExpr);
+ return this;
+ }
+
+ /**
+ * Type-check the pattern, performing any type-dependent optimizations.
+ * @param visitor an expression visitor
+ * @param contextItemType the type of the context item at the point where the pattern appears
+ * @return the optimised Pattern
+ */
+
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ // analyze each component of the pattern
+ Optimizer opt = visitor.getConfiguration().obtainOptimizer();
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(getItemType(), false);
+
+ positionExpr = visitor.typeCheck(positionExpr, cit);
+ positionExpr = ExpressionTool.unsortedIfHomogeneous(opt, positionExpr);
+ positionExpr = visitor.optimize(positionExpr, cit);
+
+ if (Literal.isConstantBoolean(positionExpr, true)) {
+ return new ItemTypePattern(nodeTest);
+ } else if (Literal.isConstantBoolean(positionExpr, false)) {
+ // if a filter is constant false, the pattern doesn't match anything
+ return new ItemTypePattern(ErrorType.getInstance());
+ }
+
+ if ((positionExpr.getDependencies() & StaticProperty.DEPENDS_ON_POSITION) == 0) {
+ usesPosition = false;
+ }
+
+ return this;
+
+ }
+
+ /**
+ * Get the dependencies of the pattern. The only possible dependency for a pattern is
+ * on local variables. This is analyzed in those patterns where local variables may appear.
+ */
+
+ public int getDependencies() {
+ // the only dependency that's interesting is a dependency on local variables
+ return positionExpr.getDependencies() & StaticProperty.DEPENDS_ON_LOCAL_VARIABLES;
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(positionExpr);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+
+ if (positionExpr == original) {
+ positionExpr = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ * @param slotManager
+ * @param nextFree the next slot that is free to be allocated @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ return ExpressionTool.allocateSlots(positionExpr, nextFree, slotManager);
+ }
+
+ /**
+ * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>Unlike the corresponding method on {@link net.sf.saxon.expr.Expression}, this method does not return anything:
+ * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
+ * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
+ * variable references within the pattern.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public void promote(PromotionOffer offer, Expression parent) throws XPathException {
+
+ Binding[] savedBindingList = offer.bindingList;
+ positionExpr = positionExpr.promote(offer, parent);
+ offer.bindingList = savedBindingList;
+ }
+
+ /**
+ * Determine whether the pattern matches a given item.
+ *
+ *
+ *
+ *
+ * @param item the item to be tested
+ * @return true if the pattern matches, else false
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ return item instanceof NodeInfo && matchesBeneathAnchor((NodeInfo) item, null, context);
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ return internalMatches(node, anchor, context);
+ }
+
+ /**
+ * Test whether the pattern matches, but without changing the current() node
+ */
+
+ private boolean internalMatches(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ // System.err.println("Matching node type and fingerprint");
+ if (!nodeTest.matches(node, context)) {
+ return false;
+ }
+
+ XPathContext c2 = context.newMinorContext();
+ UnfailingIterator iter = SingletonIterator.makeIterator(node);
+ iter.next();
+ c2.setCurrentIterator(iter);
+
+ try {
+ XPathContext c = c2;
+ if (usesPosition) {
+ ManualIterator man = new ManualIterator(node, getActualPosition(node, context, Integer.MAX_VALUE));
+ XPathContext c3 = c2.newMinorContext();
+ c3.setCurrentIterator(man);
+ c = c3;
+ }
+ Item predicate = positionExpr.evaluateItem(c);
+ if (predicate instanceof NumericValue) {
+ NumericValue position = (NumericValue)positionExpr.evaluateItem(context);
+ if (position.isWholeNumber() && position.compareTo(0) > 0) {
+ int requiredPos = (int)position.longValue();
+ return getActualPosition(node, context, requiredPos) == requiredPos;
+ } else {
+ return false;
+ }
+ } else {
+ return ExpressionTool.effectiveBooleanValue(predicate);
+ }
+
+ } catch (XPathException e) {
+ if ("XTDE0640".equals(e.getErrorCodeLocalPart())) {
+ // Treat circularity error as fatal (test error213)
+ throw e;
+ }
+ XPathException err = new XPathException("An error occurred matching pattern {" + toString() + "}: ", e);
+ err.setXPathContext(c2);
+ err.setErrorCodeQName(e.getErrorCodeQName());
+ err.setLocator(this);
+ c2.getController().recoverableError(err);
+ return false;
+ }
+ }
+
+ private int getActualPosition(NodeInfo node, XPathContext context, int max) throws XPathException {
+ return context.getConfiguration().getSiblingPosition(node, nodeTest, max);
+ }
+
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Node.NODE
+ *
+ * @return the type of node matched by this pattern. e.g. Node.ELEMENT or Node.TEXT
+ */
+
+ public int getNodeKind() {
+ return nodeTest.getPrimitiveType();
+ }
+
+ /**
+ * Determine the fingerprint of nodes to which this pattern applies.
+ * Used for optimisation.
+ *
+ * @return the fingerprint of nodes matched by this pattern.
+ */
+
+ public int getFingerprint() {
+ return nodeTest.getFingerprint();
+ }
+
+ /**
+ * Get an ItemType that all the nodes matching this pattern must satisfy
+ */
+
+ public ItemType getItemType() {
+ return nodeTest;
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof GeneralPositionalPattern) {
+ GeneralPositionalPattern fp = (GeneralPositionalPattern)other;
+ return nodeTest.equals(fp.nodeTest) && positionExpr.equals(fp.positionExpr);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return nodeTest.hashCode() ^ positionExpr.hashCode();
+ }
+
+
+}
+// Copyright (c) 2012 Saxonica Limited
\ No newline at end of file
diff --git a/sf/saxon/pattern/IdrefTest.java b/sf/saxon/pattern/IdrefTest.java
new file mode 100644
index 0000000..658d08c
--- /dev/null
+++ b/sf/saxon/pattern/IdrefTest.java
@@ -0,0 +1,90 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.PrependIterator;
+
+import java.io.Serializable;
+
+/**
+ * IdrefTest is a test that cannot be represented directly in XPath or
+ * XSLT patterns, but which is used internally for matching IDREF nodes: it tests
+ * whether the node has the is-idref property
+ *
+ * @author Michael H. Kay
+ */
+
+public class IdrefTest implements PatternFinder, Serializable {
+
+ private static IdrefTest THE_INSTANCE = new IdrefTest();
+
+ /**
+ * Get the singleton instance of this class
+ */
+
+ public static IdrefTest getInstance() {
+ return THE_INSTANCE;
+ }
+
+ /**
+ * Create a IdrefTest
+ */
+
+ private IdrefTest() {}
+
+ /**
+ * Select nodes in a document using this PatternFinder.
+ *
+ * @param doc the document node at the root of a tree
+ * @param context the dynamic evaluation context
+ * @return an iterator over the selected nodes in the document.
+ */
+
+ public SequenceIterator selectNodes(DocumentInfo doc, final XPathContext context) throws XPathException {
+
+ AxisIterator allElements = doc.iterateAxis(AxisInfo.DESCENDANT, NodeKindTest.ELEMENT);
+ MappingFunction atts = new MappingFunction() {
+ public SequenceIterator map(Item item) {
+ return new PrependIterator((NodeInfo)item, ((NodeInfo)item).iterateAxis(AxisInfo.ATTRIBUTE));
+ }
+ };
+ SequenceIterator allAttributes = new MappingIterator(allElements, atts);
+ ItemMappingFunction test = new ItemMappingFunction() {
+ /*@Nullable*/ public Item mapItem(Item item) {
+ if ((matches((NodeInfo)item))) {
+ return item;
+ } else {
+ return null;
+ }
+ }
+ };
+ return new ItemMappingIterator(allAttributes, test);
+
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node.
+ * @param node the node to be matched
+ * @return true if the node matches the test
+ */
+
+ private boolean matches(NodeInfo node) {
+ return node.isIdref();
+ }
+
+
+ public String toString() {
+ return "is-idref()";
+ }
+
+}
+
diff --git a/sf/saxon/pattern/IntersectPattern.java b/sf/saxon/pattern/IntersectPattern.java
new file mode 100644
index 0000000..6084761
--- /dev/null
+++ b/sf/saxon/pattern/IntersectPattern.java
@@ -0,0 +1,96 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A pattern formed as the difference of two other patterns
+ */
+
+public class IntersectPattern extends VennPattern {
+
+ /**
+ * Constructor
+ *
+ * @param p1 the left-hand operand
+ * @param p2 the right-hand operand
+ */
+
+ public IntersectPattern(Pattern p1, Pattern p2) {
+ super(p1, p2);
+ }
+
+ /**
+ * Get an ItemType that all the items matching this pattern must satisfy
+ */
+ @Override
+ public ItemType getItemType() {
+ return p1.getItemType();
+ }
+
+ /**
+ * Determine if the supplied node matches the pattern
+ * @param item the node to be compared
+ * @return true if the node matches either of the operand patterns
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ return p1.matches(item, context) && p2.matches(item, context);
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ return p1.matchesBeneathAnchor(node, anchor, context) &&
+ p2.matchesBeneathAnchor(node, anchor, context);
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ if (other instanceof IntersectPattern) {
+ Set s0 = new HashSet(10);
+ gatherComponentPatterns(s0);
+ Set s1 = new HashSet(10);
+ ((IntersectPattern)other).gatherComponentPatterns(s1);
+ return s0.equals(s1);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return 0x13d7dfa6 ^ p1.hashCode() ^ p2.hashCode();
+ }
+
+
+}
+
diff --git a/sf/saxon/pattern/ItemTypePattern.java b/sf/saxon/pattern/ItemTypePattern.java
new file mode 100644
index 0000000..09fd421
--- /dev/null
+++ b/sf/saxon/pattern/ItemTypePattern.java
@@ -0,0 +1,140 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.type.ItemType;
+
+/**
+ * A ItemTypePattern is a pattern that consists simply of an ItemType. In the past the ItemType
+ * was always a NodeTest, but XSLT 3.0 introduces the ability for a pattern to also match atomic
+ * values.
+ * @author Michael H. Kay
+ */
+
+public class ItemTypePattern extends Pattern {
+
+ private ItemType itemType;
+ private double priority = Double.NaN;
+
+ /**
+ * Create an ItemTypePattern that matches all items of a given type
+ * @param test the type that the items must satisfy for the pattern to match
+ */
+
+ public ItemTypePattern(ItemType test) {
+ itemType = test;
+ }
+
+ /**
+ * Set a priority to override the default priority. This is used when the pattern is written in a complex
+ * form such as a[true()] justifying a priority of 0.5, but then simplifies down to an ItemTypePattern
+ * @param priority the priority to be used if no explicit priority is given in the template rule
+ */
+
+ public void setPriority(double priority) {
+ this.priority = priority;
+ }
+
+
+ /**
+ * Determine whether this Pattern matches the given Node. This is the main external interface
+ * for matching patterns: it sets current() to the node being tested
+ *
+ *
+ *
+ * @param item The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param context The context in which the match is to take place. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key(). Not used (and can be
+ * set to null) in the case of patterns that are NodeTests
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matches(Item item, XPathContext context) {
+ return itemType.matches(item, context);
+ }
+
+ /**
+ * Get a NodeTest that all the nodes matching this pattern must satisfy
+ */
+
+ public ItemType getItemType() {
+ return itemType;
+ }
+
+ /**
+ * Determine the default priority of this item type test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return (Double.isNaN(priority) ? itemType.getDefaultPriority() : priority);
+ }
+
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Type.NODE. For patterns that
+ * do not match nodes, return -1.
+ *
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getNodeKind() {
+ if (itemType instanceof NodeTest) {
+ return itemType.getPrimitiveType();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Determine the name fingerprint of nodes to which this pattern applies. Used for
+ * optimisation.
+ *
+ * @return A fingerprint that the nodes must match, or -1 if it can match multiple fingerprints
+ */
+
+ public int getFingerprint() {
+ if (itemType instanceof NodeTest) {
+ return ((NodeTest)itemType).getFingerprint();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Display the pattern for diagnostics
+ */
+
+ public String toString() {
+ return itemType.toString();
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ return (other instanceof ItemTypePattern) &&
+ ((ItemTypePattern)other).itemType.equals(itemType);
+ }
+
+ /**
+ * Hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return 0x7aeffea8 ^ itemType.hashCode();
+ }
+
+
+
+}
+
diff --git a/sf/saxon/pattern/LocalNameTest.java b/sf/saxon/pattern/LocalNameTest.java
new file mode 100644
index 0000000..a536317
--- /dev/null
+++ b/sf/saxon/pattern/LocalNameTest.java
@@ -0,0 +1,163 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.tree.tiny.TinyTree;
+
+/**
+ * NodeTest is an interface that enables a test of whether a node has a particular
+ * name and type. A LocalNameTest matches the node type and the local name,
+ * it represents an XPath 2.0 test of the form *:name.
+ *
+ * @author Michael H. Kay
+ */
+
+public final class LocalNameTest extends NodeTest implements QNameTest {
+
+ private NamePool namePool;
+ private int nodeKind;
+ private String localName;
+
+ public LocalNameTest(NamePool pool, int nodeKind, String localName) {
+ namePool = pool;
+ this.nodeKind = nodeKind;
+ this.localName = localName;
+ }
+
+ /**
+ * Get the node kind matched by this test
+ * @return the matching node kind
+ */
+
+ public int getNodeKind() {
+ return nodeKind;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, /*@Nullable*/ NodeName name, int annotation) {
+ return name != null && nodeKind == this.nodeKind && localName.equals(name.getLocalPart());
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ */
+
+ public boolean matches(TinyTree tree, int nodeNr) {
+ int nameCode = tree.getNameCode(nodeNr);
+ return nameCode != -1 &&
+ tree.getNodeKind(nodeNr) == nodeKind &&
+ localName.equals(namePool.getLocalName(nameCode & NamePool.FP_MASK));
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes.
+ * @param node the node to be matched
+ */
+
+ public boolean matches(NodeInfo node) {
+ return localName.equals(node.getLocalPart()) && nodeKind == node.getNodeKind();
+ }
+
+ /**
+ * Test whether this QNameTest matches a given QName
+ * @param qname the QName to be matched
+ * @return true if the name matches, false if not
+ */
+
+ public boolean matches(StructuredQName qname) {
+ return localName.equals(qname.getLocalPart());
+ }
+
+ /**
+ * Determine the default priority of this node test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return -0.25;
+ }
+
+ /**
+ * Get the local name used in this LocalNameTest
+ * @return the local name matched by the test
+ */
+
+ public String getLocalName() {
+ return localName;
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Type.NODE
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getPrimitiveType() {
+ return nodeKind;
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ return 1<<nodeKind;
+ }
+
+ public String toString() {
+ return "*:" + localName;
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return nodeKind<<20 ^ localName.hashCode();
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ public boolean equals(Object other) {
+ return other instanceof LocalNameTest &&
+ ((LocalNameTest)other).namePool == namePool &&
+ ((LocalNameTest)other).nodeKind == nodeKind &&
+ ((LocalNameTest)other).localName.equals(localName);
+ }
+
+}
+
diff --git a/sf/saxon/pattern/NameTest.java b/sf/saxon/pattern/NameTest.java
new file mode 100644
index 0000000..55aa0e8
--- /dev/null
+++ b/sf/saxon/pattern/NameTest.java
@@ -0,0 +1,323 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.z.IntSet;
+import net.sf.saxon.z.IntSingletonSet;
+import net.sf.saxon.om.*;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+
+/**
+ * NodeTest is an interface that enables a test of whether a node has a particular
+ * name and type. A NameTest matches the node kind and the namespace URI and the local
+ * name.
+ *
+ * @author Michael H. Kay
+ */
+
+public class NameTest extends NodeTest implements QNameTest {
+
+ private int nodeKind;
+ private int fingerprint;
+ private NamePool namePool;
+ /*@Nullable*/ private String uri = null; // the URI corresponding to the fingerprint - computed lazily
+ /*@Nullable*/ private String localName = null; //the local name corresponding to the fingerprint - computed lazily
+
+ /**
+ * Create a NameTest to match nodes by name
+ * @param nodeKind the kind of node, for example {@link Type#ELEMENT}
+ * @param uri the namespace URI of the required nodes. Supply "" to match nodes that are in
+ * no namespace
+ * @param localName the local name of the required nodes. Supply "" to match unnamed nodes
+ * @param namePool the namePool holding the name codes
+ * @since 9.0
+ */
+
+ public NameTest(int nodeKind, String uri, String localName, NamePool namePool) {
+ this.nodeKind = nodeKind;
+ this.fingerprint = namePool.allocate("", uri, localName) & NamePool.FP_MASK;
+ this.namePool = namePool;
+ }
+
+ /**
+ * Create a NameTest to match nodes by their nameCode allocated from the NamePool
+ * @param nodeKind the kind of node, for example {@link Type#ELEMENT}
+ * @param nameCode the nameCode representing the name of the node
+ * @param namePool the namePool holding the name codes
+ * @since 8.4
+ */
+
+ public NameTest(int nodeKind, int nameCode, NamePool namePool) {
+ this.nodeKind = nodeKind;
+ this.fingerprint = nameCode & 0xfffff;
+ this.namePool = namePool;
+ }
+
+ /**
+ * Create a NameTest to match nodes by name
+ * @param nodeKind the kind of node, for example {@link Type#ELEMENT}
+ * @param name the name of the nodes that this NameTest will match
+ * @param pool the namePool holding the name codes
+ * @since 9.4
+ */
+
+ public NameTest(int nodeKind, NodeName name, NamePool pool) {
+ this.nodeKind = nodeKind;
+ this.fingerprint = name.allocateNameCode(pool) & NamePool.FP_MASK;
+ this.namePool = pool;
+ }
+
+ /**
+ * Create a NameTest for nodes of the same type and name as a given node
+ * @param node the node whose node kind and node name will form the basis of the NameTest
+ */
+
+ public NameTest(NodeInfo node) {
+ this.nodeKind = node.getNodeKind();
+ this.fingerprint = node.getFingerprint();
+ this.namePool = node.getNamePool();
+ }
+
+ /**
+ * Get the NamePool associated with this NameTest
+ * @return the NamePool
+ */
+
+ public NamePool getNamePool() {
+ return namePool;
+ }
+
+ /**
+ * Get the node kind that this name test matches
+ * @return the matching node kind
+ */
+
+ public int getNodeKind() {
+ return nodeKind;
+ }
+
+ /**
+ * Get the name of the nodes that this name test matches
+ * @return the matching node name
+ */
+
+ public NodeName getNodeName() {
+ return new CodedName(fingerprint, namePool);
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, NodeName name, int annotation) {
+ if (nodeKind != this.nodeKind) {
+ return false;
+ }
+ if (name.hasFingerprint()) {
+ return (name.getFingerprint() == this.fingerprint);
+ } else {
+ computeUriAndLocal();
+ return name.isInNamespace(uri) && name.getLocalPart().equals(localName);
+ }
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ */
+
+ public boolean matches(TinyTree tree, int nodeNr) {
+ return ((tree.getNameCode(nodeNr)&0xfffff) == this.fingerprint && tree.getNodeKind(nodeNr) == this.nodeKind);
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes.
+ * @param node the node to be matched
+ */
+
+ public boolean matches(NodeInfo node) {
+ if (node.getNodeKind() != nodeKind) {
+ return false;
+ }
+
+ // Two different algorithms are used for name matching. If the fingerprint of the node is readily
+ // available, we use it to do an integer comparison. Otherwise, we do string comparisons on the URI
+ // and local name. In practice, Saxon's native node implementations use fingerprint matching, while
+ // DOM and JDOM nodes use string comparison of names
+
+ if (node instanceof FingerprintedNode) {
+ return node.getFingerprint() == fingerprint;
+ } else {
+ computeUriAndLocal();
+ return localName.equals(node.getLocalPart()) && uri.equals(node.getURI());
+ }
+ }
+
+ private void computeUriAndLocal() {
+ if (uri == null) {
+ uri = namePool.getURI(fingerprint);
+ }
+ if (localName == null) {
+ localName = namePool.getLocalName(fingerprint);
+ }
+ }
+
+ /**
+ * Test whether the NameTest matches a given QName
+ * @param qname the QName to be matched
+ * @return true if the name matches
+ */
+
+ public boolean matches(StructuredQName qname) {
+ computeUriAndLocal();
+ return qname.getLocalPart().equals(localName) && qname.getURI().equals(uri);
+ }
+
+ /**
+ * Determine the default priority of this node test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return 0.0;
+ }
+
+ /**
+ * Get the fingerprint required
+ */
+
+ public int getFingerprint() {
+ return fingerprint;
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Type.NODE
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getPrimitiveType() {
+ return nodeKind;
+ }
+
+ /**
+ * Get the type from which this item type is derived by restriction. This
+ * is the supertype in the XPath type heirarchy, as distinct from the Schema
+ * base type: this means that the supertype of xs:boolean is xs:anyAtomicType,
+ * whose supertype is item() (rather than xs:anySimpleType).
+ * <p>
+ * In fact the concept of "supertype" is not really well-defined, because the types
+ * form a lattice rather than a hierarchy. The only real requirement on this function
+ * is that it returns a type that strictly subsumes this type, ideally as narrowly
+ * as possible.
+ * @return the supertype, or null if this type is item()
+ * @param th the type hierarchy cache
+ */
+
+ public ItemType getSuperType(TypeHierarchy th) {
+ return NodeKindTest.makeNodeKindTest(nodeKind);
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ return 1<<nodeKind;
+ }
+
+ /**
+ * Get the set of node names allowed by this NodeTest. This is returned as a set of Integer fingerprints.
+ * A null value indicates that all names are permitted (i.e. that there are no constraints on the node name.
+ * The default implementation returns null.
+ */
+
+ /*@NotNull*/
+ public IntSet getRequiredNodeNames() {
+ return new IntSingletonSet(fingerprint);
+ }
+
+ /**
+ * Get the namespace URI matched by this nametest
+ * @return the namespace URI (using "" for the "null namepace")
+ */
+
+ public String getNamespaceURI() {
+ computeUriAndLocal();
+ return uri;
+ }
+
+ /**
+ * Get the local name matched by this nametest
+ * @return the local name
+ */
+
+ public String getLocalPart() {
+ computeUriAndLocal();
+ return localName;
+ }
+
+ public String toString() {
+ switch (nodeKind) {
+ case Type.ELEMENT:
+ return "element(" + namePool.getEQName(fingerprint) + ")";
+ case Type.ATTRIBUTE:
+ return "attribute(" + namePool.getEQName(fingerprint) + ")";
+ case Type.PROCESSING_INSTRUCTION:
+ return "processing-instruction(" + namePool.getDisplayName(fingerprint) + ')';
+ case Type.NAMESPACE:
+ return "namespace-node(" + namePool.getDisplayName(fingerprint) + ')';
+ }
+ return namePool.getDisplayName(fingerprint);
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return nodeKind<<20 ^ fingerprint;
+ }
+
+ /**
+ * Determines whether two NameTests are equal
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof NameTest &&
+ ((NameTest)other).namePool == namePool &&
+ ((NameTest)other).nodeKind == nodeKind &&
+ ((NameTest)other).fingerprint == fingerprint;
+ }
+
+
+}
+
diff --git a/sf/saxon/pattern/NamespaceTest.java b/sf/saxon/pattern/NamespaceTest.java
new file mode 100644
index 0000000..6509039
--- /dev/null
+++ b/sf/saxon/pattern/NamespaceTest.java
@@ -0,0 +1,183 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+/**
+ * NodeTest is an interface that enables a test of whether a node has a particular
+ * name and type. A NamespaceTest matches the node type and the namespace URI.
+ *
+ * @author Michael H. Kay
+ */
+
+public final class NamespaceTest extends NodeTest implements QNameTest {
+
+ private NamePool namePool;
+ private int nodeKind;
+ private short uriCode;
+ private String uri;
+
+ public NamespaceTest(NamePool pool, int nodeKind, String uri) {
+ namePool = pool;
+ this.nodeKind = nodeKind;
+ this.uri = uri;
+ this.uriCode = pool.allocateCodeForURI(uri);
+ }
+
+ /**
+ * Get the node kind matched by this test
+ * @return the matching node kind
+ */
+
+ public int getNodeKind() {
+ return nodeKind;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, /*@Nullable*/ NodeName name, int annotation) {
+ return name != null && name.isInNamespace(uri);
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided
+ * so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ */
+
+ public boolean matches(TinyTree tree, int nodeNr) {
+ int nameCode = tree.getNameCode(nodeNr);
+ return nameCode != -1 &&
+ tree.getNodeKind(nodeNr) == nodeKind &&
+ uriCode == namePool.getURICode(nameCode);
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes.
+ * @param node the node to be matched
+ */
+
+ public boolean matches(NodeInfo node) {
+ return node.getNodeKind()==nodeKind && node.getURI().equals(uri);
+ }
+
+ /**
+ * Test whether this NamespaceTest matches a given QName
+ * @param qname the QName to be matched
+ * @return true if the name matches, false if not
+ */
+
+ public boolean matches(StructuredQName qname) {
+ return qname.getURI().equals(uri);
+ }
+
+ /**
+ * Determine the default priority of this node test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return -0.25;
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Type.NODE
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getPrimitiveType() {
+ return nodeKind;
+ }
+
+ /**
+ * Get the type from which this item type is derived by restriction. This
+ * is the supertype in the XPath type heirarchy, as distinct from the Schema
+ * base type: this means that the supertype of xs:boolean is xs:anyAtomicType,
+ * whose supertype is item() (rather than xs:anySimpleType).
+ * <p>
+ * In fact the concept of "supertype" is not really well-defined, because the types
+ * form a lattice rather than a hierarchy. The only real requirement on this function
+ * is that it returns a type that strictly subsumes this type, ideally as narrowly
+ * as possible.
+ * @return the supertype, or null if this type is item()
+ * @param th the type hierarchy cache
+ */
+
+ public ItemType getSuperType(TypeHierarchy th) {
+ return NodeKindTest.makeNodeKindTest(nodeKind);
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ return 1<<nodeKind;
+ }
+
+ /**
+ * Get the namespace URI matched by this NamespaceTest
+ * @return the namespace URI matched by this NamespaceTest
+ */
+
+ public String getNamespaceURI() {
+ return uri;
+ }
+
+ public String toString() {
+ return '{' + uri + "}:*";
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return uriCode << 5 + nodeKind;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ public boolean equals(Object other) {
+ return other instanceof NamespaceTest &&
+ ((NamespaceTest)other).namePool == namePool &&
+ ((NamespaceTest)other).nodeKind == nodeKind &&
+ ((NamespaceTest)other).uriCode == uriCode;
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/pattern/NodeKindTest.java b/sf/saxon/pattern/NodeKindTest.java
new file mode 100644
index 0000000..24300c0
--- /dev/null
+++ b/sf/saxon/pattern/NodeKindTest.java
@@ -0,0 +1,272 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.*;
+
+/**
+ * NodeTest is an interface that enables a test of whether a node has a particular
+ * name and kind. A NodeKindTest matches the node kind only.
+ *
+ * @author Michael H. Kay
+ */
+
+public class NodeKindTest extends NodeTest {
+
+ public static final NodeKindTest DOCUMENT = new NodeKindTest(Type.DOCUMENT);
+ public static final NodeKindTest ELEMENT = new NodeKindTest(Type.ELEMENT);
+ public static final NodeKindTest ATTRIBUTE = new NodeKindTest(Type.ATTRIBUTE);
+ public static final NodeKindTest TEXT = new NodeKindTest(Type.TEXT);
+ public static final NodeKindTest COMMENT = new NodeKindTest(Type.COMMENT);
+ public static final NodeKindTest PROCESSING_INSTRUCTION = new NodeKindTest(Type.PROCESSING_INSTRUCTION);
+ public static final NodeKindTest NAMESPACE = new NodeKindTest(Type.NAMESPACE);
+
+
+ private int kind;
+
+ private NodeKindTest(int nodeKind) {
+ kind = nodeKind;
+ }
+
+ /**
+ * Get the node kind matched by this test
+ * @return the matching node kind
+ */
+
+ public int getNodeKind() {
+ return kind;
+ }
+
+ /**
+ * Make a test for a given kind of node
+ */
+
+ public static NodeTest makeNodeKindTest(int kind) {
+ switch (kind) {
+ case Type.DOCUMENT:
+ return DOCUMENT;
+ case Type.ELEMENT:
+ return ELEMENT;
+ case Type.ATTRIBUTE:
+ return ATTRIBUTE;
+ case Type.COMMENT:
+ return COMMENT;
+ case Type.TEXT:
+ return TEXT;
+ case Type.PROCESSING_INSTRUCTION:
+ return PROCESSING_INSTRUCTION;
+ case Type.NAMESPACE:
+ return NAMESPACE;
+ case Type.NODE:
+ return AnyNodeTest.getInstance();
+ default:
+ throw new IllegalArgumentException("Unknown node kind " + kind + " in NodeKindTest");
+ }
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ *
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ */
+ @Override
+ public boolean matches(int nodeKind, NodeName name, int annotation) {
+ return (kind == nodeKind);
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided
+ * so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object.
+ *
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ */
+
+ public boolean matches(TinyTree tree, int nodeNr) {
+ return tree.getNodeKind(nodeNr) == kind;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes.
+ * @param node the node to be matched
+ */
+
+ public boolean matches(NodeInfo node) {
+ return node.getNodeKind() == kind;
+ }
+
+
+ /**
+ * Determine the default priority of this node test when used on its own as a Pattern
+ */
+
+ public final double getDefaultPriority() {
+ return -0.5;
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getPrimitiveType() {
+ return kind;
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on.
+ */
+
+ public int getNodeKindMask() {
+ return 1<<kind;
+ }
+
+ /**
+ * Get the content type allowed by this NodeTest (that is, the type of content allowed).
+ * Return AnyType if there are no restrictions.
+ */
+
+ public SchemaType getContentType() {
+ switch (kind) {
+ case Type.DOCUMENT:
+ return AnyType.getInstance();
+ case Type.ELEMENT:
+ return AnyType.getInstance();
+ case Type.ATTRIBUTE:
+ return AnySimpleType.getInstance();
+ case Type.COMMENT:
+ return BuiltInAtomicType.STRING;
+ case Type.TEXT:
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ case Type.PROCESSING_INSTRUCTION:
+ return BuiltInAtomicType.STRING;
+ case Type.NAMESPACE:
+ return BuiltInAtomicType.STRING;
+ default:
+ throw new AssertionError("Unknown node kind");
+ }
+ }
+
+ /**
+ * Get the content type allowed by this NodeTest (that is, the type annotation).
+ * Return AnyType if there are no restrictions. The default implementation returns AnyType.
+ */
+
+ /*@NotNull*/
+ public AtomicType getAtomizedItemType() {
+ switch (kind) {
+ case Type.DOCUMENT:
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ case Type.ELEMENT:
+ return BuiltInAtomicType.ANY_ATOMIC;
+ case Type.ATTRIBUTE:
+ return BuiltInAtomicType.ANY_ATOMIC;
+ case Type.COMMENT:
+ return BuiltInAtomicType.STRING;
+ case Type.TEXT:
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ case Type.PROCESSING_INSTRUCTION:
+ return BuiltInAtomicType.STRING;
+ case Type.NAMESPACE:
+ return BuiltInAtomicType.STRING;
+ default:
+ throw new AssertionError("Unknown node kind");
+ }
+ }
+
+ /*@NotNull*/ public String toString() {
+ return toString(kind);
+ }
+
+ public static String toString(int kind) {
+ switch (kind) {
+ case Type.DOCUMENT:
+ return( "document-node()" );
+ case Type.ELEMENT:
+ return( "element()" );
+ case Type.ATTRIBUTE:
+ return( "attribute()" );
+ case Type.COMMENT:
+ return( "comment()" );
+ case Type.TEXT:
+ return( "text()" );
+ case Type.PROCESSING_INSTRUCTION:
+ return( "processing-instruction()" );
+ case Type.NAMESPACE:
+ return( "namespace()" );
+ default:
+ return( "** error **");
+ }
+ }
+
+ /**
+ * Get the name of a node kind
+ * @param kind the node kind, for example Type.ELEMENT or Type.ATTRIBUTE
+ * @return the name of the node kind, for example "element" or "attribute"
+ */
+
+ public static String nodeKindName(int kind) {
+ switch (kind) {
+ case Type.DOCUMENT:
+ return( "document" );
+ case Type.ELEMENT:
+ return( "element" );
+ case Type.ATTRIBUTE:
+ return( "attribute" );
+ case Type.COMMENT:
+ return( "comment" );
+ case Type.TEXT:
+ return( "text" );
+ case Type.PROCESSING_INSTRUCTION:
+ return( "processing-instruction" );
+ case Type.NAMESPACE:
+ return( "namespace" );
+ default:
+ return( "** error **");
+ }
+ }
+
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return kind;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ public boolean equals(Object other) {
+ return other instanceof NodeKindTest &&
+ ((NodeKindTest)other).kind == kind;
+ }
+
+}
+
diff --git a/sf/saxon/pattern/NodeSetPattern.java b/sf/saxon/pattern/NodeSetPattern.java
new file mode 100644
index 0000000..5c696c1
--- /dev/null
+++ b/sf/saxon/pattern/NodeSetPattern.java
@@ -0,0 +1,220 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.Iterator;
+
+/**
+* A NodeSetPattern is a pattern based on an expression that is evaluated to return a set of nodes;
+* a node matches the pattern if it is a member of this node-set.
+ *
+ * <p>In XSLT 2.0 there are two forms of NodeSetPattern allowed, represented by calls on the id() and
+ * key() functions. In XSLT 3.0, additional forms are allowed, for example a variable reference, and
+ * a call to the doc() function. This class provides the general capability to use any expression
+ * at the head of a pattern. This is used also to support streaming, where streaming XPath expressions
+ * are mapped to patterns.</p>
+*/
+
+public class NodeSetPattern extends Pattern {
+
+ protected Expression expression;
+ /*@Nullable*/ protected ItemType itemType;
+
+ /**
+ * Create a node-set pattern.
+ * @param exp an expression that can be evaluated to return a node-set; a node matches the pattern
+ * if it is present in this node-set. The expression must not depend on the focus, though it can depend on
+ * other aspects of the dynamic context such as local or global variables.
+ */
+ public NodeSetPattern(Expression exp) {
+ expression = exp;
+ if ((expression.getDependencies() & StaticProperty.DEPENDS_ON_NON_DOCUMENT_FOCUS) != 0) {
+ throw new IllegalArgumentException("Expression used in pattern must not depend on focus");
+ }
+ }
+
+ /**
+ * Type-check the pattern.
+ * Default implementation does nothing. This is only needed for patterns that contain
+ * variable references or function calls.
+ * @return the optimised Pattern
+ */
+
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ expression = visitor.typeCheck(expression, contextItemType);
+ RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, expression.toString(), 0);
+ expression = TypeChecker.staticTypeCheck(expression, SequenceType.NODE_SEQUENCE, false, role, visitor);
+ itemType = expression.getItemType(visitor.getConfiguration().getTypeHierarchy());
+ return this;
+ }
+
+ /**
+ * Get the dependencies of the pattern. The only possible dependency for a pattern is
+ * on local variables. This is analyzed in those patterns where local variables may appear.
+ */
+
+ public int getDependencies() {
+ return expression.getDependencies();
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(expression);
+ }
+
+ /**
+ * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>Unlike the corresponding method on {@link net.sf.saxon.expr.Expression}, this method does not return anything:
+ * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
+ * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
+ * variable references within the pattern.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public void promote(PromotionOffer offer, Expression parent) throws XPathException {
+ expression = expression.promote(offer, parent);
+ }
+
+ /**
+ * Replace a subexpression by a replacement subexpression
+ * @param original the expression to be replaced
+ * @param replacement the new expression to be inserted in its place
+ * @return true if the replacement was carried out
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ if (expression == original) {
+ expression = replacement;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+//#ifdefined STREAM
+ /**
+ * Test whether a pattern is motionless, that is, whether it can be evaluated against a node
+ * without repositioning the input stream. This is a necessary condition for patterns used
+ * as the match pattern of a streamed template rule.
+ *
+ * @return true if the pattern is motionless, that is, if it can be evaluated against a streamed
+ * node without changing the position in the streamed input file
+ * @param allowExtensions
+ */
+ @Override
+ public boolean isMotionless(boolean allowExtensions) {
+ return expression.getStreamability(Expression.NAVIGATION_CONTEXT, allowExtensions, null) == Expression.W3C_MOTIONLESS;
+ }
+//#endif
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ * @param slotManager the slot manager representing the stack frame for local variables
+ * @param nextFree the next slot that is free to be allocated @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ return ExpressionTool.allocateSlots(expression, nextFree, slotManager);
+ }
+
+
+ /**
+ * Determine whether this Pattern matches the given Node
+ * @param item The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ if (item instanceof NodeInfo) {
+ NodeInfo e = (NodeInfo)item;
+ SequenceIterator iter = expression.iterate(context);
+ while (true) {
+ NodeInfo node = (NodeInfo)iter.next();
+ if (node == null) {
+ return false;
+ }
+ if (node.isSameNodeInfo(e)) {
+ return true;
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Determine the type of nodes to which this pattern applies.
+ * @return Type.NODE (may be overridden in subclasses)
+ */
+
+ public int getNodeKind() {
+ if (itemType instanceof NodeTest) {
+ return ((NodeTest)itemType).getPrimitiveType();
+ } else {
+ return Type.NODE;
+ }
+ }
+
+ /**
+ * Get a NodeTest that all the nodes matching this pattern must satisfy
+ */
+
+ public ItemType getItemType() {
+ if (itemType instanceof NodeTest) {
+ return (NodeTest)itemType;
+ } else {
+ return AnyNodeTest.getInstance();
+ }
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(Object other) {
+ return (other instanceof NodeSetPattern) &&
+ ((NodeSetPattern)other).expression.equals(expression);
+ }
+
+ /**
+ * Hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return 0x73108728 ^ expression.hashCode();
+ }
+
+}
+
diff --git a/sf/saxon/pattern/NodeTest.java b/sf/saxon/pattern/NodeTest.java
new file mode 100644
index 0000000..509d526
--- /dev/null
+++ b/sf/saxon/pattern/NodeTest.java
@@ -0,0 +1,271 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.z.IntSet;
+import net.sf.saxon.z.IntUniversalSet;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.TinyTree;
+import net.sf.saxon.type.*;
+
+import java.io.Serializable;
+
+/**
+ * A NodeTest is a simple kind of pattern that enables a context-free test of whether
+ * a node matches a given node kind and name. There are several kinds of node test: a full name test, a prefix test, and an
+ * "any node of a given type" test, an "any node of any type" test, a "no nodes"
+ * test (used, e.g. for "@comment()").
+ *
+ * <p>As well as being used to support XSLT pattern matching, NodeTests act as predicates in
+ * axis steps, and also act as item types for type matching.</p>
+ *
+ * <p>For use in user-written application calling {@link NodeInfo#iterateAxis(byte, NodeTest)},
+ * it is possible to write a user-defined subclass of <code>NodeTest</code> that implements
+ * a single method, {@link #matches(int, NodeName, int)}</p>
+ *
+ * @author Michael H. Kay
+ */
+
+public abstract class NodeTest implements ItemType, Serializable {
+
+ public boolean matches(Item item, /*@NotNull*/ XPathContext context) {
+ return matchesItem(item, false, context.getConfiguration());
+ }
+
+ /**
+ * Test whether a given item conforms to this type. This implements a method of the ItemType interface.
+ * @param item The item to be tested
+ * @param allowURIPromotion true of promotion of anyURI to string is permitted
+ * @param config the Saxon Configuration
+ * @return true if the item is an instance of this type; false otherwise
+ */
+
+ public boolean matchesItem(Item item, boolean allowURIPromotion, Configuration config) {
+ return item instanceof NodeInfo && matches((NodeInfo)item);
+ }
+
+ /**
+ * Get the type from which this item type is derived by restriction. This
+ * is the supertype in the XPath type heirarchy, as distinct from the Schema
+ * base type: this means that the supertype of xs:boolean is xs:anyAtomicType,
+ * whose supertype is item() (rather than xs:anySimpleType).
+ * <p>
+ * In fact the concept of "supertype" is not really well-defined, because the types
+ * form a lattice rather than a hierarchy. The only real requirement on this function
+ * is that it returns a type that strictly subsumes this type, ideally as narrowly
+ * as possible.
+ * @return the supertype, or null if this type is item()
+ * @param th the type hierarchy cache
+ */
+
+ public ItemType getSuperType(TypeHierarchy th) {
+ return AnyNodeTest.getInstance();
+ // overridden for AnyNodeTest itself
+ }
+
+ /**
+ * Get the primitive item type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue it is Type.ATOMIC_VALUE. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that INTEGER is considered to be a primitive type.
+ */
+
+ /*@NotNull*/ public ItemType getPrimitiveItemType() {
+ int p = getPrimitiveType();
+ if (p == Type.NODE) {
+ return AnyNodeTest.getInstance();
+ } else {
+ return NodeKindTest.makeNodeKindTest(p);
+ }
+ }
+
+ /**
+ * Get the basic kind of object that this ItemType matches: for a NodeTest, this is the kind of node,
+ * or Type.Node if it matches different kinds of nodes.
+ * @return the node kind matched by this node test
+ */
+
+ public int getPrimitiveType() {
+ return Type.NODE;
+ }
+
+ /**
+ * Get the name of the nodes matched by this nodetest, if it matches a specific name.
+ * Return -1 if the node test matches nodes of more than one name
+ */
+
+ public int getFingerprint() {
+ return -1;
+ }
+
+ /**
+ * Determine whether this item type is an atomic type
+ *
+ * @return true if this is ANY_ATOMIC_TYPE or a subtype thereof
+ */
+ public boolean isAtomicType() {
+ return false;
+ }
+
+ /**
+ * Determine whether this item type is atomic (that is, whether it can ONLY match
+ * atomic values)
+ *
+ * @return false: this is not ANY_ATOMIC_TYPE or a subtype thereof
+ */
+
+ public boolean isPlainType() {
+ return false;
+ }
+
+ /**
+ * Get the item type of the atomic values that will be produced when an item
+ * of this type is atomized (assuming that atomization succeeds)
+ */
+
+ /*@NotNull*/ public AtomicType getAtomizedItemType() {
+ // This is overridden for a ContentTypeTest
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+
+ /**
+ * Ask whether values of this type are atomizable
+ * @return true unless it is known that these items will be elements with element-only
+ * content, in which case return false
+ */
+
+ public boolean isAtomizable() {
+ // This is overridden for a ContentTypeTest
+ return true;
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node on a TinyTree. The node
+ * must be a document, element, text, comment, or processing instruction node.
+ * This method is provided so that when navigating a TinyTree a node can be rejected without
+ * actually instantiating a NodeInfo object. The default implementation instantiates the node
+ * and then calls the method {@link #matches(NodeInfo)}
+ * @param tree the TinyTree containing the node
+ * @param nodeNr the number of the node within the TinyTree
+ * @return true if the node matches the NodeTest, otherwise false
+ *
+ */
+
+ public boolean matches(/*@NotNull*/ TinyTree tree, int nodeNr) {
+ return matches(tree.getNode(nodeNr));
+ }
+
+ /**
+ * Test whether this node test is satisfied by a given node. This method is only
+ * fully supported for a subset of NodeTests, because it doesn't provide all the information
+ * needed to evaluate all node tests. In particular (a) it can't be used to evaluate a node
+ * test of the form element(N,T) or schema-element(E) where it is necessary to know whether the
+ * node is nilled, and (b) it can't be used to evaluate a node test of the form
+ * document-node(element(X)). This in practice means that it is used (a) to evaluate the
+ * simple node tests found in the XPath 1.0 subset used in XML Schema, and (b) to evaluate
+ * node tests where the node kind is known to be an attribute.
+ * @param nodeKind The kind of node to be matched
+ * @param name identifies the expanded name of the node to be matched.
+ * The value should be null for a node with no name.
+ * @param annotation The actual content type of the node
+ * @return true if the node matches this node tst
+ *
+ */
+
+ public abstract boolean matches(int nodeKind, NodeName name, int annotation);
+
+ /**
+ * Test whether this node test is satisfied by a given node. This alternative
+ * method is used in the case of nodes where calculating the fingerprint is expensive,
+ * for example DOM or JDOM nodes. The default implementation calls the method
+ * {@link #matches(int, NodeName, int)}
+ * @param node the node to be matched
+ * @return true if the node test is satisfied by the supplied node, false otherwise
+ */
+
+ public boolean matches(/*@NotNull*/ NodeInfo node) {
+ return matches(node.getNodeKind(), new NameOfNode(node), node.getTypeAnnotation());
+ }
+
+ /**
+ * Get a mask indicating which kinds of nodes this NodeTest can match. This is a combination
+ * of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on. The default
+ * implementation indicates that nodes of all kinds are matched.
+ * @return a combination of bits: 1<<Type.ELEMENT for element nodes, 1<<Type.TEXT for text nodes, and so on
+ */
+
+ public int getNodeKindMask() {
+ return 1<<Type.ELEMENT | 1<<Type.TEXT | 1<<Type.COMMENT | 1<<Type.PROCESSING_INSTRUCTION |
+ 1<<Type.ATTRIBUTE | 1<<Type.NAMESPACE | 1<<Type.DOCUMENT;
+ }
+
+ /**
+ * Get the content type allowed by this NodeTest (that is, the type annotation of the matched nodes).
+ * Return AnyType if there are no restrictions. The default implementation returns AnyType.
+ * @return the type annotation that all nodes matching this NodeTest must satisfy
+ */
+
+ public SchemaType getContentType() {
+ int m = getNodeKindMask();
+ switch (m) {
+ case 1<<Type.DOCUMENT:
+ return AnyType.getInstance();
+ case 1<<Type.ELEMENT:
+ return AnyType.getInstance();
+ case 1<<Type.ATTRIBUTE:
+ return AnySimpleType.getInstance();
+ case 1<<Type.COMMENT:
+ return BuiltInAtomicType.STRING;
+ case 1<<Type.TEXT:
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ case 1<<Type.PROCESSING_INSTRUCTION:
+ return BuiltInAtomicType.STRING;
+ case 1<<Type.NAMESPACE:
+ return BuiltInAtomicType.STRING;
+ default:
+ return AnyType.getInstance();
+ }
+ }
+
+ /**
+ * Get the set of node names allowed by this NodeTest. This is returned as a set of Integer fingerprints.
+ * If all names are permitted (i.e. there are no constraints on the node name), returns IntUniversalSet.getInstance().
+ * The default implementation returns the universal set.
+ * @return the set of integer fingerprints of the node names that this node test can match.
+ */
+
+ /*@NotNull*/ public IntSet getRequiredNodeNames() {
+ return IntUniversalSet.getInstance();
+ }
+
+ /**
+ * Determine whether the content type (if present) is nillable
+ * @return true if the content test (when present) can match nodes that are nilled
+ */
+
+ public boolean isNillable() {
+ return true;
+ }
+
+ /**
+ * Visit all the schema components used in this ItemType definition
+ * @param visitor the visitor class to be called when each component is visited
+ */
+
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException {
+ // no action
+ }
+
+
+}
+
diff --git a/sf/saxon/pattern/Pattern.java b/sf/saxon/pattern/Pattern.java
new file mode 100644
index 0000000..d7236ff
--- /dev/null
+++ b/sf/saxon/pattern/Pattern.java
@@ -0,0 +1,490 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionParser;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.functions.Current;
+import net.sf.saxon.om.*;
+import net.sf.saxon.sxpath.SimpleContainer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.PrependIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A Pattern represents the result of parsing an XSLT pattern string. <br>
+ * Patterns are created by calling the static method Pattern.make(string). <br>
+ * The pattern is used to test a particular node by calling match().
+ */
+
+public abstract class Pattern implements PatternFinder, Serializable, Container {
+
+ private String originalText;
+ private Executable executable;
+ private String systemId; // the module where the pattern occurred
+ private int lineNumber; // the line number where the pattern occurred
+
+ /**
+ * Static factory method to make a Pattern by parsing a String. <br>
+ *
+ * @param pattern The pattern text as a String
+ * @param env An object defining the compile-time context for the expression
+ * @param exec The executable containing this pattern
+ * @return The pattern object
+ * @throws net.sf.saxon.trans.XPathException
+ * if the pattern is invalid
+ */
+
+ public static Pattern make(String pattern, StaticContext env, Executable exec) throws XPathException {
+
+ PatternParser parser = (PatternParser)env.getConfiguration().newExpressionParser("PATTERN", false, env.getXPathLanguageLevel());
+ ((ExpressionParser)parser).setLanguage(ExpressionParser.XSLT_PATTERN, env.getXPathLanguageLevel());
+ SimpleContainer container = new SimpleContainer(exec);
+ container.setLocation(env.getSystemId(), env.getLineNumber());
+ ((ExpressionParser)parser).setDefaultContainer(container);
+ Pattern pat = parser.parsePattern(pattern, env);
+ pat.setSystemId(env.getSystemId());
+ pat.setLineNumber(env.getLineNumber());
+ // System.err.println("Simplified [" + pattern + "] to " + pat.getClass() + " default prio = " + pat.getDefaultPriority());
+ // set the pattern text for use in diagnostics
+ pat.setOriginalText(pattern);
+ pat.setExecutable(exec);
+ ExpressionVisitor visitor = ExpressionVisitor.make(env, exec);
+ pat = pat.simplify(visitor);
+ return pat;
+ }
+
+ protected static void replaceCurrent(Expression exp, Binding binding) {
+ Iterator<Expression> iter = exp.iterateSubExpressions();
+ while (iter.hasNext()) {
+ Expression child = iter.next();
+ if (child instanceof Current) {
+ LocalVariableReference ref = new LocalVariableReference(binding);
+ exp.replaceSubExpression(child, ref);
+ } else {
+ replaceCurrent(child, binding);
+ }
+ }
+ }
+
+ /**
+ * Replace any calls on current() by a variable reference bound to the supplied binding
+ */
+
+ public void bindCurrent(Binding binding) {
+ // default: no action
+ }
+
+ /**
+ * Get the executable containing this pattern
+ *
+ * @return the executable
+ */
+
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Set the executable containing this pattern
+ *
+ * @param executable the executable
+ */
+
+ public void setExecutable(Executable executable) {
+ this.executable = executable;
+ }
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ */
+
+ public LocationProvider getLocationProvider() {
+ return executable.getLocationMap();
+ }
+
+ /**
+ * Get the granularity of the container.
+ *
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 1;
+ }
+
+ /**
+ * Set the original text of the pattern for use in diagnostics
+ *
+ * @param text the original text of the pattern
+ */
+
+ public void setOriginalText(String text) {
+ originalText = text;
+ }
+
+ /**
+ * Simplify the pattern by applying any context-independent optimisations.
+ * Default implementation does nothing.
+ *
+ * @param visitor the expression visitor
+ * @return the optimised Pattern
+ */
+
+ public Pattern simplify(ExpressionVisitor visitor) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Type-check the pattern.
+ *
+ * @param visitor the expression visitor
+ * @param contextItemType the type of the context item at the point where the pattern
+ * is defined. Set to null if it is known that the context item is undefined.
+ * @return the optimised Pattern
+ */
+
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Get the dependencies of the pattern. The only possible dependency for a pattern is
+ * on local variables. This is analyzed in those patterns where local variables may appear.
+ *
+ * @return the dependencies, as a bit-significant mask
+ */
+
+ public int getDependencies() {
+ return 0;
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ *
+ * @return an iterator over the subexpressions. Default implementation returns an empty sequence
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ List<Expression> list = Collections.emptyList();
+ return list.iterator();
+ }
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ *
+ * @param slotManager the slot manager representing the stack frame for local variables
+ * @param nextFree the next slot that is free to be allocated
+ * @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ return nextFree;
+ }
+
+ /**
+ * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>Unlike the corresponding method on {@link Expression}, this method does not return anything:
+ * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
+ * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
+ * variable references within the pattern.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public void promote(PromotionOffer offer, Expression parent) throws XPathException {
+ // default implementation does nothing
+ }
+
+ /**
+ * Set the system ID where the pattern occurred
+ *
+ * @param systemId the URI of the module containing the pattern
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Set the line number where the pattern occurred
+ *
+ * @param lineNumber the line number of the pattern in the source module
+ */
+
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Test whether a pattern is motionless, that is, whether it can be evaluated against a node
+ * without repositioning the input stream. This is a necessary condition for patterns used
+ * as the match pattern of a streamed template rule.
+ *
+ * @param allowExtensions if false, the result is determined strictly according to the W3C
+ * "guaranteed streamability rules. If true, Saxon extensions are permitted: that is, constructs
+ * may be recognized as motionless by Saxon even if they are not recognized as motionless by
+ * the W3C rules.
+ * @return true if the pattern is motionless, that is, if it can be evaluated against a streamed
+ * node without changing the position in the streamed input file
+ */
+
+ public boolean isMotionless(boolean allowExtensions) {
+ // default implementation for subclasses
+ return true;
+ }
+
+ /**
+ * Determine whether this Pattern matches the given item. This is the main external interface
+ * for matching patterns: it sets current() to the node being tested
+ *
+ * @param item The item to be tested against the Pattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ * @throws XPathException if an error occurs while matching the pattern (the caller will usually
+ * treat this the same as a false result)
+ */
+
+ public abstract boolean matches(Item item, XPathContext context) throws XPathException;
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ *
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ * @throws XPathException if an error occurs while matching the pattern (the caller will usually
+ * treat this the same as a false result)
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ return matches(node, context);
+ }
+
+ /**
+ * Select nodes in a document using this PatternFinder.
+ *
+ * @param doc the document node at the root of a tree
+ * @param context the dynamic evaluation context
+ * @return an iterator over the selected nodes in the document.
+ */
+
+ public SequenceIterator<? extends NodeInfo> selectNodes(DocumentInfo doc, final XPathContext context) throws XPathException {
+ final int kind = getNodeKind();
+ switch (kind) {
+ case Type.DOCUMENT:
+ if (matches(doc, context)) {
+ return SingletonIterator.makeIterator(doc);
+ } else {
+ return EmptyIterator.getInstance();
+ }
+ case Type.ATTRIBUTE: {
+ AxisIterator allElements = doc.iterateAxis(AxisInfo.DESCENDANT, NodeKindTest.ELEMENT);
+ MappingFunction<NodeInfo, NodeInfo> atts = new MappingFunction<NodeInfo, NodeInfo>() {
+ public SequenceIterator<NodeInfo> map(NodeInfo item) {
+ return item.iterateAxis(AxisInfo.ATTRIBUTE);
+ }
+ };
+ SequenceIterator<NodeInfo> allAttributes =
+ new MappingIterator<NodeInfo, NodeInfo>(allElements, atts);
+ ItemMappingFunction<NodeInfo, NodeInfo> test = new ItemMappingFunction<NodeInfo, NodeInfo>() {
+ public NodeInfo mapItem(NodeInfo item) throws XPathException {
+ if ((matches(item, context))) {
+ return item;
+ } else {
+ return null;
+ }
+ }
+ };
+ return new ItemMappingIterator(allAttributes, test);
+ }
+ case Type.ELEMENT:
+ case Type.COMMENT:
+ case Type.TEXT:
+ case Type.PROCESSING_INSTRUCTION: {
+ AxisIterator allChildren = doc.iterateAxis(AxisInfo.DESCENDANT, NodeKindTest.makeNodeKindTest(kind));
+ ItemMappingFunction test = new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ if ((matches(item, context))) {
+ return item;
+ } else {
+ return null;
+ }
+ }
+ };
+ return new ItemMappingIterator(allChildren, test);
+ }
+ case Type.NODE: {
+ AxisIterator allChildren = doc.iterateAxis(AxisInfo.DESCENDANT);
+ MappingFunction<NodeInfo, NodeInfo> attsOrSelf = new MappingFunction<NodeInfo, NodeInfo>() {
+ public SequenceIterator<NodeInfo> map(NodeInfo item) {
+ return new PrependIterator(item, item.iterateAxis(AxisInfo.ATTRIBUTE));
+ }
+ };
+ SequenceIterator<NodeInfo> attributesOrSelf =
+ new MappingIterator<NodeInfo, NodeInfo>(allChildren, attsOrSelf);
+ ItemMappingFunction test = new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ if ((matches(item, context))) {
+ return item;
+ } else {
+ return null;
+ }
+ }
+ };
+ return new ItemMappingIterator(attributesOrSelf, test);
+ }
+ case Type.NAMESPACE:
+ throw new UnsupportedOperationException("Patterns can't match namespace nodes");
+ default:
+ throw new UnsupportedOperationException("Unknown node kind");
+ }
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Type.NODE. For patterns that
+ * do not match nodes, return -1.
+ *
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ public int getNodeKind() {
+ return Type.NODE;
+ }
+
+ /**
+ * Determine the name fingerprint of nodes to which this pattern applies. Used for
+ * optimisation.
+ *
+ * @return A fingerprint that the nodes must match, or -1 if it can match multiple fingerprints,
+ * or it if matches atomic values
+ */
+
+ public int getFingerprint() {
+ return -1;
+ }
+
+ /**
+ * Get an ItemType that all the items matching this pattern must satisfy
+ *
+ * @return an ItemType, as specific as possible, which all the matching items satisfy
+ */
+
+ public abstract ItemType getItemType();
+
+ /**
+ * Determine the default priority to use if this pattern appears as a match pattern
+ * for a template with no explicit priority attribute.
+ *
+ * @return the default priority for the pattern
+ */
+
+ public double getDefaultPriority() {
+ return 0.5;
+ }
+
+ /**
+ * Get the system id of the entity in which the pattern occurred
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Get the line number on which the pattern was defined
+ */
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Get the column number (always -1)
+ */
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Get the public ID (always null)
+ */
+
+ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Get the original pattern text
+ */
+
+ public String toString() {
+ if (originalText != null) {
+ return originalText;
+ } else {
+ return "pattern matching " + getItemType().toString();
+ }
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ *
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+
+ public int getHostLanguage() {
+ return Configuration.XSLT;
+ }
+
+ /**
+ * Replace a subexpression by a replacement subexpression
+ *
+ * @param original the expression to be replaced
+ * @param replacement the new expression to be inserted in its place
+ * @return true if the replacement was carried out
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ throw new IllegalArgumentException("Invalid replacement");
+ }
+}
+
diff --git a/sf/saxon/pattern/PatternFinder.java b/sf/saxon/pattern/PatternFinder.java
new file mode 100644
index 0000000..b911f85
--- /dev/null
+++ b/sf/saxon/pattern/PatternFinder.java
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Serializable;
+
+/**
+ * This interface enables a client to find all nodes in a document that match a particular pattern.
+ * In fact, it allows any subset of nodes in a document to be located. It is used specifically by the
+ * internal implementation of keys. In XSLT, the criterion for including nodes in a key is that they
+ * match an XSLT pattern. Internally, however, keys are used for a wider range of purposes, and the
+ * nodes indexed by the key are defined by a PatternFinder
+ */
+
+public interface PatternFinder extends Serializable {
+
+ /**
+ * Select nodes in a document using this PatternFinder.
+ *
+ * @param doc the document node at the root of a tree
+ * @param context the dynamic evaluation context
+ * @return an iterator over the selected nodes in the document.
+ * @throws XPathException if a dynamic error is encountered
+ */
+
+ /*@Nullable*/ public SequenceIterator<? extends NodeInfo> selectNodes(DocumentInfo doc, XPathContext context) throws XPathException;
+
+}
+
diff --git a/sf/saxon/pattern/PatternMaker.java b/sf/saxon/pattern/PatternMaker.java
new file mode 100644
index 0000000..74cdcc3
--- /dev/null
+++ b/sf/saxon/pattern/PatternMaker.java
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This is a singleton class used to convert an expression to an equivalent pattern.
+ * This version of the class is used to generate conventional XSLT match patterns;
+ * there is another version used to generate patterns suitable for streamed evaluation
+ * in Saxon-EE.
+ */
+public class PatternMaker {
+
+ /**
+ * Static factory method to make a pattern by converting an expression. The supplied
+ * expression is the equivalent expression to the pattern, in the sense that it takes
+ * the same syntactic form.
+ *
+ * <p>Note that this method does NOT check all the rules for XSLT patterns; it deliberately allows
+ * a (slightly) wider class of expressions to be converted than XSLT allows.</p>
+ *
+ * <p>The expression root() at the start of the expression has a special meaning: it refers to
+ * the root of the subtree in which the pattern must match, which can be supplied at run-time
+ * during pattern matching. This is used for patterns that represent streamable path expressions.</p>
+ *
+ * @param expression the expression to be converted, which must already have been simplified and
+ * type-checked
+ * @param config the Saxon configuration
+ * @param is30 set to true if XSLT 3.0 syntax is to be accepted
+ * @return the compiled pattern
+ * @throws net.sf.saxon.trans.XPathException if the expression cannot be converted
+ */
+
+ /*@NotNull*/
+ public static Pattern fromExpression(Expression expression, Configuration config, boolean is30) throws XPathException {
+ Pattern result = expression.toPattern(config, is30);
+ result.setOriginalText(expression.toString());
+ result.setSystemId(expression.getSystemId());
+ result.setLineNumber(expression.getLineNumber());
+ result.setExecutable(expression.getExecutable());
+ return result;
+ }
+
+ public static byte getAxisForPathStep(Expression step) throws XPathException {
+ if (step instanceof AxisExpression) {
+ return AxisInfo.inverseAxis[((AxisExpression) step).getAxis()];
+ } else if (step instanceof FilterExpression) {
+ return getAxisForPathStep(((FilterExpression) step).getControllingExpression());
+ } else if (step instanceof FirstItemExpression) {
+ return getAxisForPathStep(((FirstItemExpression) step).getBaseExpression());
+ } else if (step instanceof SubscriptExpression) {
+ return getAxisForPathStep(((SubscriptExpression) step).getBaseExpression());
+ } else if (step instanceof SlashExpression) {
+ return getAxisForPathStep(((SlashExpression) step).getFirstStep());
+ } else if (step instanceof ContextItemExpression) {
+ return AxisInfo.SELF;
+ } else {
+ throw new XPathException("The path in a pattern must contain simple steps");
+ }
+ }
+}
+
diff --git a/sf/saxon/pattern/PatternParser.java b/sf/saxon/pattern/PatternParser.java
new file mode 100644
index 0000000..459261a
--- /dev/null
+++ b/sf/saxon/pattern/PatternParser.java
@@ -0,0 +1,34 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: mike
+ * Date: 08/02/2013
+ * Time: 10:46
+ * To change this template use File | Settings | File Templates.
+ */
+public interface PatternParser {
+
+ /**
+ * Parse a string representing an XSLT pattern
+ * @param pattern the pattern expressed as a String
+ * @param env the static context for the pattern
+ * @return a Pattern object representing the result of parsing
+ * @throws net.sf.saxon.trans.XPathException if the pattern contains a syntax error
+ */
+
+ public Pattern parsePattern(String pattern, StaticContext env) throws XPathException;
+
+
+}
+
diff --git a/sf/saxon/pattern/PatternParser20.java b/sf/saxon/pattern/PatternParser20.java
new file mode 100644
index 0000000..cd66716
--- /dev/null
+++ b/sf/saxon/pattern/PatternParser20.java
@@ -0,0 +1,250 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionParser;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.expr.parser.Tokenizer;
+import net.sf.saxon.functions.Doc;
+import net.sf.saxon.functions.Id;
+import net.sf.saxon.functions.KeyFn;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+
+/**
+ * Parser for XSLT patterns. This is created by overriding selected parts of the standard ExpressionParser.
+ */
+
+public class PatternParser20 extends ExpressionParser implements PatternParser {
+
+ int inPredicate = 0;
+
+ /**
+ * Parse a string representing an XSLT pattern
+ *
+ * @param pattern the pattern expressed as a String
+ * @param env the static context for the pattern
+ * @return a Pattern object representing the result of parsing
+ * @throws XPathException if the pattern contains a syntax error
+ */
+
+ /*@NotNull*/
+ public Pattern parsePattern(String pattern, StaticContext env) throws XPathException {
+ this.env = env;
+ nameChecker = env.getConfiguration().getNameChecker();
+ language = XSLT_PATTERN;
+ Expression exp = parse(pattern, 0, Token.EOF, env.getLineNumber(), env);
+ exp.setContainer(defaultContainer);
+ ExpressionVisitor visitor = ExpressionVisitor.make(env, exp.getExecutable());
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(AnyItemType.getInstance(), true);
+ Pattern pat = PatternMaker.fromExpression(exp.simplify(visitor).typeCheck(visitor, cit), env.getConfiguration(), false);
+ if (exp instanceof FilterExpression && pat instanceof ItemTypePattern) {
+ // the pattern has been simplified but needs to retain a default priority based on its syntactic form (test match89)
+ ((ItemTypePattern) pat).setPriority(0.5);
+ }
+ return pat;
+ }
+
+ /**
+ * Callback to tailor the tokenizer
+ */
+
+ protected void customizeTokenizer(Tokenizer t) {
+ t.disallowUnionKeyword = true;
+ }
+
+ /**
+ * Override the parsing of top-level expressions
+ *
+ * @return the parsed expression
+ * @throws XPathException
+ */
+
+ /*@NotNull*/
+ public Expression parseExpression() throws XPathException {
+ if (inPredicate > 0) {
+ return super.parseExpression();
+ } else {
+ return parseBinaryExpression(parsePathExpression(), 10);
+ }
+ }
+
+ /**
+ * Parse a basic step expression (without the predicates)
+ *
+ * @param firstInPattern true only if we are parsing the first step in a
+ * RelativePathPattern in the XSLT Pattern syntax
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@NotNull*/
+ protected Expression parseBasicStep(boolean firstInPattern) throws XPathException {
+ if (inPredicate > 0) {
+ return super.parseBasicStep(firstInPattern);
+ } else {
+ switch (t.currentToken) {
+ case Token.DOLLAR:
+ grumble("A variable reference is not allowed in an XSLT pattern (except in a predicate)");
+ return null;
+
+ case Token.LPAR:
+ grumble("Parentheses are not allowed in an XSLT 2.0 pattern");
+ return null;
+
+ case Token.STRING_LITERAL:
+ case Token.NUMBER:
+ case Token.KEYWORD_CURLY:
+ case Token.ELEMENT_QNAME:
+ case Token.ATTRIBUTE_QNAME:
+ case Token.NAMESPACE_QNAME:
+ case Token.PI_QNAME:
+ case Token.TAG:
+ case Token.INLINE_FUNCTION_LITERAL:
+ case Token.DOTDOT:
+ grumble("Token " + currentTokenDisplay() + " not allowed here in an XSLT pattern");
+ return null;
+
+ case Token.FUNCTION:
+ if (!firstInPattern) {
+ grumble("In an XSLT pattern, a function call is allowed only as the first step in a path");
+ }
+ return super.parseBasicStep(firstInPattern);
+ default:
+ return super.parseBasicStep(firstInPattern);
+
+ }
+ }
+ }
+
+ @Override
+ protected void testPermittedAxis(byte axis) throws XPathException {
+ if (inPredicate == 0) {
+ if (axis != AxisInfo.CHILD && axis != AxisInfo.ATTRIBUTE) {
+ grumble("Unless XSLT 3.0 is enabled, the only axes allowed in a pattern are the child and attribute axes");
+ }
+
+ }
+ }
+
+ /**
+ * Parse a type pattern of the form "~ ItemType" (introduced in XSLT 3.0)
+ *
+ * @return the type pattern, wrapped in a PatternSponsor to satisfy the parsing interface
+ * @throws XPathException if any error is found, for example if XSLT 3.0 is not enabled
+ */
+
+ /*@NotNull*/
+ protected Expression parseTypePattern() throws XPathException {
+ grumble("Type patterns (~ItemType) require XSLT 3.0 to be enabled");
+ return null;
+ }
+
+ /**
+ * Parse an expression appearing within a predicate. This enables full XPath parsing, without
+ * the normal rules that apply within an XSLT pattern
+ *
+ * @return the parsed expression that appears within the predicate
+ * @throws XPathException
+ */
+
+ /*@NotNull*/
+ protected Expression parsePredicate() throws XPathException {
+ boolean disallow = t.disallowUnionKeyword;
+ t.disallowUnionKeyword = false;
+ ++inPredicate;
+ Expression exp = parseExpression();
+ --inPredicate;
+ t.disallowUnionKeyword = disallow;
+ return exp;
+ }
+
+ /**
+ * Parse a function call appearing within a pattern. Unless within a predicate, this
+ * imposes the constraints on which function calls are allowed to appear in a pattern
+ *
+ * @return the expression that results from the parsing (usually a FunctionCall)
+ * @throws XPathException
+ */
+
+ /*@NotNull*/
+ protected Expression parseFunctionCall() throws XPathException {
+ Expression fn = super.parseFunctionCall();
+ if (inPredicate > 0) {
+ return fn;
+ } else {
+ if (fn instanceof Id) {
+
+ // Only one argument allowed, which must be a string literal or variable reference
+ if (((Id) fn).getNumberOfArguments() != 1) {
+ grumble("id() in an XSLT 2.0 pattern must have only one argument");
+ } else {
+ Expression arg = ((Id) fn).getArguments()[0];
+ if (!(arg instanceof VariableReference || arg instanceof StringLiteral)) {
+ grumble("Argument to id() in a pattern must be a variable reference or string literal");
+ }
+ }
+
+ } else if (fn instanceof KeyFn) {
+
+ // Only two arguments allowed
+ if (((KeyFn) fn).getNumberOfArguments() != 2) {
+ grumble("key() in an XSLT 2.0 pattern must have exactly two arguments");
+ } else {
+ Expression arg0 = ((KeyFn) fn).getArguments()[0];
+ if (!(arg0 instanceof StringLiteral)) {
+ grumble("First argument to key() in an XSLT 2.0 pattern must be a string literal");
+ }
+ Expression arg1 = ((KeyFn) fn).getArguments()[1];
+ if (!(arg1 instanceof VariableReference || arg1 instanceof Literal)) {
+ grumble("Second argument to id() in an XSLT 2.0 pattern must be a variable reference or literal");
+ }
+ }
+
+ } else if (fn instanceof Doc) {
+
+ grumble("The doc() function is not allowed in an XSLT 2.0 pattern");
+
+ } else {
+ grumble("The " + fn.toString() + " function is not allowed at the head of a pattern");
+ }
+ }
+ return fn;
+ }
+
+ public Expression parseFunctionArgument() throws XPathException {
+ if (inPredicate > 0) {
+ return super.parseFunctionArgument();
+ } else {
+ switch (t.currentToken) {
+ case Token.DOLLAR:
+ return parseVariableReference();
+
+ case Token.STRING_LITERAL:
+ return parseStringLiteral();
+
+ case Token.NUMBER:
+ return parseNumericLiteral();
+
+ default:
+ grumble("A function argument in an XSLT pattern must be a variable reference or literal");
+ return null;
+ }
+ }
+ }
+
+ public Expression makeTracer(int startOffset, Expression exp, int construct, StructuredQName qName) {
+ // Suppress tracing of pattern evaluation
+ return exp;
+ }
+}
+
diff --git a/sf/saxon/pattern/PatternSponsor.java b/sf/saxon/pattern/PatternSponsor.java
new file mode 100644
index 0000000..fd9d5d2
--- /dev/null
+++ b/sf/saxon/pattern/PatternSponsor.java
@@ -0,0 +1,421 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.BooleanValue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * The PatternSponsor class allows a Pattern to be treated like an expression. Although
+ * patterns are not evaluated at run-time in the same way as expressions, they need to
+ * be manipulated in much the same way as expressions at compile time: for example variables
+ * need to be bound, dependencies need to be analyzed, and so on. This is especially true
+ * of patterns appearing in the xsl:number and xsl:for-each-group instructions (less so for
+ * the more common match patterns in xsl:template).
+ *
+ * <p>This class implements the Expression interface, so that an Expression can have a
+ * PatternSponsor as a subexpression; it wraps a Pattern.</p>
+ *
+ * <p>A Pattern is treated as a boolean expression that returns true if the pattern matches
+ * the context node. It is evaluated in that way only when obtained through the s9api
+ * {@link net.sf.saxon.s9api.XPathCompiler#compilePattern(String)} method.</p>
+ */
+
+public class PatternSponsor extends Expression {
+
+ private Pattern pattern;
+
+ /**
+ * Create a sponsor expression for a pattern
+ * @param pattern the pattern being wrapped
+ */
+
+ public PatternSponsor(Pattern pattern) {
+ this.pattern = pattern;
+ }
+
+ /**
+ * Get the wrapped pattern
+ * @return the wrapped pattern
+ */
+
+ public Pattern getPattern() {
+ return pattern;
+ }
+
+ /**
+ * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
+ * This method indicates which of these methods is provided directly. The other methods will always be available
+ * indirectly, using an implementation that relies on one of the other methods.
+ */
+
+ public int getImplementationMethod() {
+ return EVALUATE_METHOD;
+ }
+
+ /**
+ * Simplify an expression. This performs any static optimization (by rewriting the expression
+ * as a different expression). The default implementation does nothing.
+ *
+ * @return the simplified expression
+ * @throws XPathException if an error is discovered during expression
+ * rewriting
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/
+ public Expression simplify(ExpressionVisitor visitor) throws XPathException {
+ pattern = pattern.simplify(visitor);
+ return this;
+ }
+
+ /**
+ * Perform optimisation of an expression and its subexpressions.
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable, and after all type checking has been done.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten if appropriate to optimize execution
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression optimize(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ return this;
+ }
+
+ /**
+ * Perform type checking of an expression and its subexpressions.
+ * <p/>
+ * <p>This checks statically that the operands of the expression have
+ * the correct type; if necessary it generates code to do run-time type checking or type
+ * conversion. A static type error is reported only if execution cannot possibly succeed, that
+ * is, if a run-time type error is inevitable. The call may return a modified form of the expression.</p>
+ * <p/>
+ * <p>This method is called after all references to functions and variables have been resolved
+ * to the declaration of the function or variable. However, the types of such functions and
+ * variables may not be accurately known if they have not been explicitly declared.</p>
+ *
+ * @param visitor an expression visitor
+ * @param contextItemType the static type of "." at the point where this expression is invoked.
+ * The parameter is set to null if it is known statically that the context item will be undefined.
+ * If the type of the context item is not known statically, the argument is set to
+ * {@link net.sf.saxon.type.Type#ITEM_TYPE}
+ * @return the original expression, rewritten to perform necessary
+ * run-time type checks, and to perform other type-related
+ * optimizations
+ * @throws XPathException if an error is discovered during this phase
+ * (typically a type error)
+ */
+
+ /*@NotNull*/
+ public Expression typeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ pattern = pattern.analyze(visitor, contextItemType);
+ return this;
+ }
+
+ protected int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/
+ public Expression copy() {
+ throw new UnsupportedOperationException("PatternSponsor.copy()");
+ }
+
+ /**
+ * Offer promotion for this subexpression. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @return if the offer is not accepted, return this expression unchanged.
+ * Otherwise return the result of rewriting the expression to promote
+ * this subexpression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public Expression promote(PromotionOffer offer, Expression parent) throws XPathException {
+ pattern.promote(offer, this);
+ return this;
+ }
+
+ /**
+ * <p>Determine the static cardinality of the expression. This establishes how many items
+ * there will be in the result of the expression, at compile time (i.e., without
+ * actually evaluating the result.</p>
+ * <p/>
+ * <p>This method should always return a result, though it may be the best approximation
+ * that is available at the time.</p>
+ *
+ * @return one of the values {@link StaticProperty#ALLOWS_ONE},
+ * {@link StaticProperty#ALLOWS_ZERO_OR_MORE}, {@link StaticProperty#ALLOWS_ZERO_OR_ONE},
+ * {@link StaticProperty#ALLOWS_ONE_OR_MORE}, {@link StaticProperty#EMPTY}.
+ */
+
+ public int getCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Determine the data type of the expression, if possible. All expression return
+ * sequences, in general; this method determines the type of the items within the
+ * sequence, assuming that (a) this is known in advance, and (b) it is the same for
+ * all items in the sequence.
+ * <p/>
+ * <p>This method should always return a result, though it may be the best approximation
+ * that is available at the time.</p>
+ *
+ * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER,
+ * Type.NODE, or Type.ITEM (meaning not known at compile time)
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Determine which aspects of the context the expression depends on. The result is
+ * a bitwise-or'ed value composed from constants such as {@link net.sf.saxon.expr.StaticProperty#DEPENDS_ON_CONTEXT_ITEM} and
+ * {@link net.sf.saxon.expr.StaticProperty#DEPENDS_ON_CURRENT_ITEM}. The default implementation combines the intrinsic
+ * dependencies of this expression with the dependencies of the subexpressions,
+ * computed recursively. This is overridden for expressions such as FilterExpression
+ * where a subexpression's dependencies are not necessarily inherited by the parent
+ * expression.
+ *
+ * @return a set of bit-significant flags identifying the dependencies of
+ * the expression
+ */
+
+ public int getDependencies() {
+ return pattern.getDependencies();
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression. Default implementation
+ * returns a zero-length array, appropriate for an expression that has no
+ * sub-expressions.
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return pattern.iterateSubExpressions();
+ }
+
+ /**
+ * Get the immediate sub-expressions of this expression, with information about the relationship
+ * of each expression to its parent expression. Default implementation
+ * works off the results of iterateSubExpressions()
+ *
+ * @return an iterator containing the sub-expressions of this expression
+ */
+
+ /*@NotNull*/
+ public Iterator<SubExpressionInfo> iterateSubExpressionInfo() {
+ // default implementation
+ List<SubExpressionInfo> list = new ArrayList<SubExpressionInfo>();
+ for (Iterator<Expression> kids = iterateSubExpressions(); kids.hasNext(); ) {
+ list.add(new SubExpressionInfo(kids.next(), true, true, NAVIGATION_CONTEXT));
+ }
+ return list.iterator();
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ return pattern.replaceSubExpression(original, replacement);
+ }
+
+
+ /**
+ * Get the container that immediately contains this expression. This method
+ * returns null for an outermost expression; it also return null in the case
+ * of literal values. For an XPath expression occurring within an XSLT stylesheet,
+ * this method returns the XSLT instruction containing the XPath expression.
+ *
+ * @return the expression that contains this expression, if known; return null
+ * if there is no containing expression or if the containing expression is unknown.
+ */
+
+ public Container getContainer() {
+ return pattern;
+ }
+
+ /**
+ * Convert this expression to an equivalent XSLT pattern
+ *
+ * @param config the Saxon configuration
+ * @param is30 true if this is XSLT 3.0
+ * @return the equivalent pattern
+ * @throws net.sf.saxon.trans.XPathException
+ * if conversion is not possible
+ */
+ @Override
+ public Pattern toPattern(Configuration config, boolean is30) throws XPathException {
+ return pattern;
+ }
+
+ /**
+ * Evaluate an expression as a single item. This always returns either a single Item or
+ * null (denoting the empty sequence). No conversion is done. This method should not be
+ * used unless the static type of the expression is a subtype of "item" or "item?": that is,
+ * it should not be called if the expression may return a sequence. There is no guarantee that
+ * this condition will be detected.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the node or atomic value that results from evaluating the
+ * expression; or null to indicate that the result is an empty
+ * sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public Item evaluateItem(XPathContext context) throws XPathException {
+ return BooleanValue.get(effectiveBooleanValue(context));
+ }
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @param context supplies the context for evaluation
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(XPathContext context) throws XPathException {
+ return SingletonIterator.makeIterator(evaluateItem(context));
+ }
+
+ /**
+ * Get the effective boolean value of the expression. Returns true if the underlying
+ * pattern matches the context node, otherwise false.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the effective boolean value
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ public boolean effectiveBooleanValue(XPathContext context) throws XPathException {
+ return pattern.matches(context.getContextItem(), context);
+ }
+
+ /**
+ * Evaluate an expression as a String. This function must only be called in contexts
+ * where it is known that the expression will return a single string (or where an empty sequence
+ * is to be treated as a zero-length string). Implementations should not attempt to convert
+ * the result to a string, other than converting () to "". This method is used mainly to
+ * evaluate expressions produced by compiling an attribute value template.
+ *
+ * @param context The context in which the expression is to be evaluated
+ * @return the value of the expression, evaluated in the current context.
+ * The expression must return a string or (); if the value of the
+ * expression is (), this method returns "".
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ * @throws ClassCastException if the result type of the
+ * expression is not xs:string?
+ */
+
+ public CharSequence evaluateAsString(XPathContext context) throws XPathException {
+ return evaluateItem(context).getStringValueCS();
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(XPathContext context) throws XPathException {
+ throw new UnsupportedOperationException("Patterns cannot be evaluated in push mode");
+ }
+
+ /**
+ * Diagnostic print of expression structure. The abstract expression tree
+ * is written to the supplied output destination.
+ */
+
+ public void explain(ExpressionPresenter out) {
+ out.startElement("pattern");
+ out.emitAttribute("match", pattern.toString());
+ out.endElement();
+ }
+
+ /**
+ * Check statically that the results of the expression are capable of constructing the content
+ * of a given schema type.
+ *
+ * @param parentType The schema type
+ * @param env the static context
+ * @param whole true if this expression is expected to make the whole content of the type, false
+ * if it is expected to make up only a part
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression doesn't match the required content type
+ */
+
+ public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
+ throw new UnsupportedOperationException("checkPermittedContents() is not applicable to a pattern");
+ }
+}
+
diff --git a/sf/saxon/pattern/PatternThatSetsCurrent.java b/sf/saxon/pattern/PatternThatSetsCurrent.java
new file mode 100644
index 0000000..b655e5c
--- /dev/null
+++ b/sf/saxon/pattern/PatternThatSetsCurrent.java
@@ -0,0 +1,226 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.Binding;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.flwor.LocalVariableBinding;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.functions.Current;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.Iterator;
+
+/**
+ * This class represents a pattern that sets the value of current() to the
+ * node being matched, and then wraps another pattern that uses the value
+ * of current()
+ */
+public class PatternThatSetsCurrent extends Pattern {
+
+ private LocalVariableBinding binding;
+ private Pattern wrappedPattern;
+
+ public PatternThatSetsCurrent(Pattern wrappedPattern) {
+ this.wrappedPattern = wrappedPattern;
+ this.binding = new LocalVariableBinding(Current.FN_CURRENT, SequenceType.SINGLE_ITEM);
+ setExecutable(wrappedPattern.getExecutable());
+ setOriginalText(wrappedPattern.toString());
+ setLineNumber(wrappedPattern.getLineNumber());
+ setSystemId(wrappedPattern.getSystemId());
+ }
+
+ /**
+ * Get the binding of the item being matched by the pattern, that is, the binding that
+ * represents the value of the current() function
+ * @return the binding of the item being matched by the pattern
+ */
+
+ public Binding getCurrentBinding() {
+ return binding;
+ }
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ *
+ * @param slotManager the slot manager representing the stack frame for local variables
+ * @param nextFree the next slot that is free to be allocated
+ * @return the next slot that is free to be allocated
+ */
+ @Override
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ binding.setSlotNumber(nextFree++);
+ return wrappedPattern.allocateSlots(slotManager, nextFree);
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ *
+ * @return an iterator over the subexpressions. Default implementation returns an empty sequence
+ */
+ @Override
+ public Iterator<Expression> iterateSubExpressions() {
+ return wrappedPattern.iterateSubExpressions();
+ }
+
+ /**
+ * Determine whether this Pattern matches the given item. This is the main external interface
+ * for matching patterns: it sets current() to the node being tested
+ *
+ * @param item The item to be tested against the Pattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the item matches the Pattern, false otherwise
+ */
+ @Override
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ context.setLocalVariable(binding.getLocalSlotNumber(), item);
+ return wrappedPattern.matches(item, context);
+ }
+
+ /**
+ * Get an ItemType that all the items matching this pattern must satisfy
+ * @return an ItemType, as specific as possible, which all the matching items satisfy
+ */
+ @Override
+ public ItemType getItemType() {
+ return wrappedPattern.getItemType();
+ }
+
+ /**
+ * Simplify the pattern by applying any context-independent optimisations.
+ * Default implementation does nothing.
+ *
+ * @param visitor the expression visitor
+ * @return the optimised Pattern
+ */
+ @Override
+ public Pattern simplify(ExpressionVisitor visitor) throws XPathException {
+ wrappedPattern = wrappedPattern.simplify(visitor);
+ return this;
+ }
+
+ /**
+ * Type-check the pattern.
+ *
+ * @param visitor the expression visitor
+ * @param contextItemType the type of the context item at the point where the pattern
+ * is defined. Set to null if it is known that the context item is undefined.
+ * @return the optimised Pattern
+ */
+ @Override
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ wrappedPattern = wrappedPattern.analyze(visitor, contextItemType);
+ return this;
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Type.NODE. For patterns that
+ * do not match nodes, return -1.
+ *
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+ @Override
+ public int getNodeKind() {
+ return wrappedPattern.getNodeKind();
+ }
+
+ /**
+ * Determine the name fingerprint of nodes to which this pattern applies. Used for
+ * optimisation.
+ *
+ * @return A fingerprint that the nodes must match, or -1 if it can match multiple fingerprints,
+ * or it if matches atomic values
+ */
+ @Override
+ public int getFingerprint() {
+ return wrappedPattern.getFingerprint();
+ }
+
+ /**
+ * Determine the default priority to use if this pattern appears as a match pattern
+ * for a template with no explicit priority attribute.
+ *
+ * @return the default priority for the pattern
+ */
+ @Override
+ public double getDefaultPriority() {
+ return wrappedPattern.getDefaultPriority();
+ }
+
+ /**
+ * Get the original pattern text
+ */
+ @Override
+ public String toString() {
+ return wrappedPattern.toString();
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ *
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+ @Override
+ public int getHostLanguage() {
+ return wrappedPattern.getHostLanguage();
+ }
+
+ /**
+ * Replace a subexpression by a replacement subexpression
+ *
+ * @param original the expression to be replaced
+ * @param replacement the new expression to be inserted in its place
+ * @return true if the replacement was carried out
+ */
+ @Override
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ return wrappedPattern.replaceSubExpression(original, replacement);
+ }
+
+ /**
+ * Test whether a pattern is motionless, that is, whether it can be evaluated against a node
+ * without repositioning the input stream. This is a necessary condition for patterns used
+ * as the match pattern of a streamed template rule.
+ *
+ * @param allowExtensions if false, the result is determined strictly according to the W3C
+ * "guaranteed streamability rules. If true, Saxon extensions are permitted: that is, constructs
+ * may be recognized as motionless by Saxon even if they are not recognized as motionless by
+ * the W3C rules.
+ * @return true if the pattern is motionless, that is, if it can be evaluated against a streamed
+ * node without changing the position in the streamed input file
+ */
+ @Override
+ public boolean isMotionless(boolean allowExtensions) {
+ return wrappedPattern.isMotionless(allowExtensions);
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ *
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+ @Override
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ return wrappedPattern.matchesBeneathAnchor(node, anchor, context);
+ }
+
+}
+
diff --git a/sf/saxon/pattern/PatternWithPredicate.java b/sf/saxon/pattern/PatternWithPredicate.java
new file mode 100644
index 0000000..a3b6402
--- /dev/null
+++ b/sf/saxon/pattern/PatternWithPredicate.java
@@ -0,0 +1,199 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.functions.Current;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.ItemType;
+
+import java.util.Iterator;
+
+/**
+ * Class for handling patterns with simple non-positional boolean predicates
+ */
+public class PatternWithPredicate extends Pattern {
+
+ private Pattern basePattern;
+ private Expression predicate;
+
+ public PatternWithPredicate(Pattern basePattern, Expression predicate) {
+ this.basePattern = basePattern;
+ this.predicate = predicate;
+ }
+
+ /**
+ * Replace any calls on current() by a variable reference bound to the supplied binding
+ */
+ @Override
+ public void bindCurrent(Binding binding) {
+ if (predicate instanceof Current) {
+ predicate = new LocalVariableReference(binding);
+ } else if (ExpressionTool.callsFunction(predicate, Current.FN_CURRENT)) {
+ replaceCurrent(predicate, binding);
+ }
+ }
+
+ /**
+ * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>Unlike the corresponding method on {@link net.sf.saxon.expr.Expression}, this method does not return anything:
+ * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
+ * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
+ * variable references within the pattern.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public void promote(PromotionOffer offer, Expression parent) throws XPathException {
+ Binding[] savedBindingList = offer.bindingList;
+ basePattern.promote(offer, parent);
+ predicate = predicate.promote(offer, parent);
+ offer.bindingList = savedBindingList;
+ }
+
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ * @param slotManager holds details of the allocated slots
+ * @param nextFree the next slot that is free to be allocated @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ return ExpressionTool.allocateSlots(predicate, nextFree, slotManager);
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Type.NODE. For patterns that
+ * do not match nodes, return -1.
+ *
+ * @return the type of node matched by this pattern. e.g. Type.ELEMENT or Type.TEXT
+ */
+
+ @Override
+ public int getNodeKind() {
+ return basePattern.getNodeKind();
+ }
+
+ /**
+ * Determine the name fingerprint of nodes to which this pattern applies. Used for
+ * optimisation.
+ *
+ * @return A fingerprint that the nodes must match, or -1 if it can match multiple fingerprints,
+ * or it if matches atomic values
+ */
+ @Override
+ public int getFingerprint() {
+ return basePattern.getFingerprint();
+ }
+
+ /**
+ * Determine whether this Pattern matches the given Node.
+ * @param item The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+ @Override
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ if (!basePattern.matches(item, context)) {
+ return false;
+ }
+ XPathContext c2 = context.newMinorContext();
+ SingletonIterator si = (SingletonIterator)SingletonIterator.makeIterator(item);
+ si.next();
+ c2.setCurrentIterator(si);
+ try {
+ return predicate.effectiveBooleanValue(c2);
+ } catch (XPathException e) {
+ if ("XTDE0640".equals(e.getErrorCodeLocalPart())) {
+ // Treat circularity error as fatal (test error213)
+ throw e;
+ }
+ XPathException err = new XPathException("An error occurred matching pattern {" + toString() + "}: ", e);
+ err.setXPathContext(c2);
+ err.setErrorCodeQName(e.getErrorCodeQName());
+ err.setLocator(this);
+ c2.getController().recoverableError(err);
+ return false;
+ }
+ }
+
+ /**
+ * Get an ItemType that all the items matching this pattern must satisfy
+ *
+ * @return an ItemType, as specific as possible, which all the matching items satisfy
+ */
+ /*@Nullable*/@Override
+ public ItemType getItemType() {
+ return basePattern.getItemType();
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ *
+ * @return an iterator over the subexpressions. Default implementation returns an empty sequence
+ */
+ /*@NotNull*/
+ @Override
+ public Iterator<Expression> iterateSubExpressions() {
+ return new PairIterator<Expression>(new PatternSponsor(basePattern), predicate);
+ }
+
+ /**
+ * Type-check the pattern.
+ *
+ * @param visitor the expression visitor
+ * @param contextItemType the type of the context item at the point where the pattern
+ * is defined. Set to null if it is known that the context item is undefined.
+ * @return the optimised Pattern
+ */
+ @Override
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(getItemType(), false);
+ predicate = visitor.typeCheck(predicate, cit);
+ return this;
+ }
+
+ /**
+ * Determine the default priority to use if this pattern appears as a match pattern
+ * for a template with no explicit priority attribute.
+ *
+ * @return the default priority for the pattern
+ */
+ @Override
+ public double getDefaultPriority() {
+ return basePattern.getDefaultPriority() + 0.5;
+ }
+
+//#ifdefined STREAM
+ @Override
+ public boolean isMotionless(boolean allowExtensions) {
+ return basePattern.isMotionless(allowExtensions) &&
+ predicate.getStreamability(Expression.INSPECTION_CONTEXT, allowExtensions, null) == Expression.W3C_MOTIONLESS;
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/pattern/QNameTest.java b/sf/saxon/pattern/QNameTest.java
new file mode 100644
index 0000000..2c4af1c
--- /dev/null
+++ b/sf/saxon/pattern/QNameTest.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.om.StructuredQName;
+
+import java.io.Serializable;
+
+/**
+ * Interface for tests against a QName. This is implemented by a subset of NodeTests, specifically
+ * those that are only concerned with testing a name. It is used for matching error
+ * codes against the codes specified in a try/catch clause.
+ */
+
+public interface QNameTest extends Serializable {
+
+ /**
+ * Test whether the QNameTest matches a given QName
+ * @param qname the QName to be matched
+ * @return true if the name matches, false if not
+ */
+
+ boolean matches(StructuredQName qname);
+}
\ No newline at end of file
diff --git a/sf/saxon/pattern/SchemaNodeTest.java b/sf/saxon/pattern/SchemaNodeTest.java
new file mode 100644
index 0000000..a6f5f23
--- /dev/null
+++ b/sf/saxon/pattern/SchemaNodeTest.java
@@ -0,0 +1,15 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+/**
+ * Marker interface for schema-element() and schema-attribute() node tests
+ */
+public interface SchemaNodeTest {
+}
+
diff --git a/sf/saxon/pattern/SimplePositionalPattern.java b/sf/saxon/pattern/SimplePositionalPattern.java
new file mode 100644
index 0000000..e8be62f
--- /dev/null
+++ b/sf/saxon/pattern/SimplePositionalPattern.java
@@ -0,0 +1,298 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.NumericValue;
+
+import java.util.Iterator;
+
+/**
+ * A SimplePositionalPattern is a pattern of the form A[P] where A is an axis expression using the child axis
+ * and P is an expression that depends on the position. When this kind of pattern is used for matching streamed nodes,
+ * it relies on the histogram data of preceding siblings maintained as part of a
+ * {@link com.saxonica.stream.om.FleetingParentNode}
+ *
+ * This class handles cases where the predicate P is a simple comparison of the value of position() against an
+ * integer (which may be constant or computed. More general predicates are handled by the class
+ * GeneralPositionalPattern.
+ */
+
+
+public final class SimplePositionalPattern extends Pattern {
+
+ private NodeTest nodeTest;
+ private Expression positionExpr;
+ private int operator;
+
+
+ /**
+ * Create a SimplePositionalPattern
+ * @param nodeTest the test that the node must satisfy
+ * @param positionTest expression to evaluate the required position of the node
+ * @param operator comparison between the required position and the actual position
+ */
+
+ public SimplePositionalPattern(NodeTest nodeTest, Expression positionTest, int operator) {
+ this.nodeTest = nodeTest;
+ this.positionExpr = positionTest;
+ this.operator = operator;
+ }
+
+ /**
+ * Get the filter assocated with the pattern
+ * @return the filter predicate
+ */
+
+ public Expression getPositionExpr() {
+ return positionExpr;
+ }
+
+ /**
+ * Get the base pattern
+ * @return the base pattern before filtering
+ */
+
+ public int getOperator() {
+ return operator;
+ }
+
+ /**
+ * Simplify the pattern: perform any context-independent optimisations
+ * @param visitor an expression visitor
+ */
+
+ public Pattern simplify(ExpressionVisitor visitor) throws XPathException {
+
+ positionExpr = visitor.simplify(positionExpr);
+ return this;
+ }
+
+ /**
+ * Type-check the pattern, performing any type-dependent optimizations.
+ * @param visitor an expression visitor
+ * @param contextItemType the type of the context item at the point where the pattern appears
+ * @return the optimised Pattern
+ */
+
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+
+ // analyze each component of the pattern
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(getItemType(), false);
+
+ positionExpr = visitor.typeCheck(positionExpr, cit);
+ positionExpr = visitor.optimize(positionExpr, cit);
+
+ return this;
+
+ }
+
+ /**
+ * Get the dependencies of the pattern. The only possible dependency for a pattern is
+ * on local variables. This is analyzed in those patterns where local variables may appear.
+ */
+
+ public int getDependencies() {
+ int dependencies = positionExpr.getDependencies();
+ // the only dependency that's interesting is a dependency on local variables
+ dependencies &= StaticProperty.DEPENDS_ON_LOCAL_VARIABLES;
+ return dependencies;
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MonoIterator<Expression>(positionExpr);
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ boolean found = false;
+
+ if (positionExpr == original) {
+ positionExpr = replacement;
+ found = true;
+ }
+ return found;
+ }
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ * @param slotManager
+ * @param nextFree the next slot that is free to be allocated @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ nextFree = ExpressionTool.allocateSlots(positionExpr, nextFree, slotManager);
+ return nextFree;
+ }
+
+ /**
+ * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>Unlike the corresponding method on {@link net.sf.saxon.expr.Expression}, this method does not return anything:
+ * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
+ * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
+ * variable references within the pattern.
+ *
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public void promote(PromotionOffer offer, Expression parent) throws XPathException {
+
+ Binding[] savedBindingList = offer.bindingList;
+ positionExpr = positionExpr.promote(offer, parent);
+ offer.bindingList = savedBindingList;
+ }
+
+ /**
+ * Determine whether the pattern matches a given item.
+ * @param item the item to be tested
+ * @return true if the pattern matches, else false
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ return item instanceof NodeInfo && matchesBeneathAnchor((NodeInfo) item, null, context);
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Node.NODE
+ *
+ * @return the type of node matched by this pattern. e.g. Node.ELEMENT or Node.TEXT
+ */
+
+ public int getNodeKind() {
+ return nodeTest.getPrimitiveType();
+ }
+
+ /**
+ * Determine the fingerprint of nodes to which this pattern applies.
+ * Used for optimisation.
+ *
+ * @return the fingerprint of nodes matched by this pattern.
+ */
+
+ public int getFingerprint() {
+ return nodeTest.getFingerprint();
+ }
+
+ /**
+ * Get an ItemType that all the nodes matching this pattern must satisfy
+ */
+
+ public ItemType getItemType() {
+ return nodeTest.getPrimitiveItemType();
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof SimplePositionalPattern) {
+ SimplePositionalPattern fp = (SimplePositionalPattern)other;
+ return nodeTest.equals(fp.nodeTest) && positionExpr.equals(fp.positionExpr) && operator == fp.operator;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return nodeTest.hashCode() ^ positionExpr.hashCode();
+ }
+
+ /**
+ * Test whether a pattern is motionless, that is, whether it can be evaluated against a node
+ * without repositioning the input stream. This is a necessary condition for patterns used
+ * as the match pattern of a streamed template rule.
+ * @param allowExtensions true if Saxon streamability extensions are allowed in the analysis
+ * @return true if the pattern is motionless, that is, if it can be evaluated against a streamed
+ * node without changing the position in the streamed input file
+ */
+
+ public boolean isMotionless(boolean allowExtensions) {
+ return true;
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ if (!nodeTest.matches(node, context)) {
+ return false;
+ }
+
+ NumericValue reqPos = (NumericValue)positionExpr.evaluateItem(context);
+ if (reqPos.isNaN()) {
+ return (operator == Token.FNE);
+ }
+
+ int actualPos = context.getConfiguration().getSiblingPosition(node, nodeTest, Integer.MAX_VALUE);
+
+ int c = Double.compare((double) actualPos, reqPos.getDoubleValue());
+ switch (operator) {
+ case Token.FEQ:
+ return c == 0;
+ case Token.FNE:
+ return c != 0;
+ case Token.FGT:
+ return c > 0;
+ case Token.FLT:
+ return c < 0;
+ case Token.FGE:
+ return c >= 0;
+ case Token.FLE:
+ return c <= 0;
+ default:
+ throw new UnsupportedOperationException("Unknown operator " + operator);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/pattern/UnionPattern.java b/sf/saxon/pattern/UnionPattern.java
new file mode 100644
index 0000000..9588f07
--- /dev/null
+++ b/sf/saxon/pattern/UnionPattern.java
@@ -0,0 +1,99 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A pattern formed as the union (or) of two other patterns
+ */
+
+public class UnionPattern extends VennPattern {
+
+ /**
+ * Constructor
+ *
+ * @param p1 the left-hand operand
+ * @param p2 the right-hand operand
+ */
+
+ public UnionPattern(Pattern p1, Pattern p2) {
+ super(p1, p2);
+ }
+
+ /**
+ * Get an ItemType that all the items matching this pattern must satisfy
+ */
+ @Override
+ public ItemType getItemType() {
+ ItemType t1 = p1.getItemType();
+ ItemType t2 = p2.getItemType();
+ return Type.getCommonSuperType(t1, t2);
+ }
+
+ /**
+ * Determine if the supplied node matches the pattern
+ * @param item the node to be compared
+ * @return true if the node matches either of the operand patterns
+ */
+
+ public boolean matches(Item item, XPathContext context) throws XPathException {
+ return p1.matches(item, context) || p2.matches(item, context);
+ }
+
+ /**
+ * Determine whether this pattern matches a given Node within the subtree rooted at a given
+ * anchor node. This method is used when the pattern is used for streaming.
+ * @param node The NodeInfo representing the Element or other node to be tested against the Pattern
+ * @param anchor The anchor node, which must match any AnchorPattern subpattern
+ * @param context The dynamic context. Only relevant if the pattern
+ * uses variables, or contains calls on functions such as document() or key().
+ * @return true if the node matches the Pattern, false otherwise
+ */
+
+ public boolean matchesBeneathAnchor(NodeInfo node, NodeInfo anchor, XPathContext context) throws XPathException {
+ return p1.matchesBeneathAnchor(node, anchor, context) ||
+ p2.matchesBeneathAnchor(node, anchor, context);
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ if (other instanceof UnionPattern) {
+ Set<Pattern> s0 = new HashSet<Pattern>(10);
+ gatherComponentPatterns(s0);
+ Set<Pattern> s1 = new HashSet<Pattern>(10);
+ ((UnionPattern)other).gatherComponentPatterns(s1);
+ return s0.equals(s1);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return 0x9bd723a6 ^ p1.hashCode() ^ p2.hashCode();
+ }
+
+
+}
+
diff --git a/sf/saxon/pattern/UnionQNameTest.java b/sf/saxon/pattern/UnionQNameTest.java
new file mode 100644
index 0000000..fbe744c
--- /dev/null
+++ b/sf/saxon/pattern/UnionQNameTest.java
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.om.StructuredQName;
+
+import java.util.List;
+
+/**
+ * A QNameTest that is the union of a number of supplied QNameTests
+ */
+public class UnionQNameTest implements QNameTest {
+
+ List<QNameTest> tests;
+
+ public UnionQNameTest(List<QNameTest> tests) {
+ this.tests = tests;
+ }
+
+ /**
+ * Test whether the QNameTest matches a given QName
+ * @param qname the QName to be matched
+ * @return true if the name matches, false if not
+ */
+
+ public boolean matches(StructuredQName qname) {
+ for (QNameTest test : tests) {
+ if (test.matches(qname)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
diff --git a/sf/saxon/pattern/VennPattern.java b/sf/saxon/pattern/VennPattern.java
new file mode 100644
index 0000000..8d0c3fb
--- /dev/null
+++ b/sf/saxon/pattern/VennPattern.java
@@ -0,0 +1,318 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pattern;
+
+import net.sf.saxon.expr.Binding;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.MultiIterator;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PromotionOffer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Abstract pattern formed as the union, intersection, or difference of two other patterns;
+ * concrete subclasses are used for the different operators
+ */
+
+public abstract class VennPattern extends Pattern {
+
+ protected Pattern p1, p2;
+ private int nodeKind = Type.NODE;
+
+ /**
+ * Constructor
+ *
+ * @param p1 the left-hand operand
+ * @param p2 the right-hand operand
+ */
+
+ public VennPattern(Pattern p1, Pattern p2) {
+ this.p1 = p1;
+ this.p2 = p2;
+ if (p1.getNodeKind() == p2.getNodeKind()) {
+ nodeKind = p1.getNodeKind();
+ }
+ }
+
+
+ /**
+ * Set the executable containing this pattern
+ *
+ * @param executable the executable
+ */
+
+ public void setExecutable(Executable executable) {
+ p1.setExecutable(executable);
+ p2.setExecutable(executable);
+ super.setExecutable(executable);
+ }
+
+ /**
+ * Simplify the pattern: perform any context-independent optimisations
+ *
+ * @param visitor an expression visitor
+ */
+
+ public Pattern simplify(ExpressionVisitor visitor) throws XPathException {
+ p1 = p1.simplify(visitor);
+ p2 = p2.simplify(visitor);
+ return this;
+ }
+
+ /**
+ * Type-check the pattern.
+ * This is only needed for patterns that contain variable references or function calls.
+ *
+ * @return the optimised Pattern
+ */
+
+ public Pattern analyze(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) throws XPathException {
+ mustBeNodePattern(p1);
+ p1 = p1.analyze(visitor, contextItemType);
+ mustBeNodePattern(p2);
+ p2 = p2.analyze(visitor, contextItemType);
+ return this;
+ }
+
+ private void mustBeNodePattern(Pattern p) throws XPathException {
+ if (p instanceof ItemTypePattern) {
+ ItemType it = p.getItemType();
+ if (!(it instanceof NodeTest)) {
+ XPathException err = new XPathException("The operands of a union, intersect, or except pattern " +
+ "must be patterns that match nodes", "XPTY0004");
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+ }
+
+ /**
+ * Replace any calls on current() by a variable reference bound to the supplied binding
+ */
+ @Override
+ public void bindCurrent(Binding binding) {
+ p1.bindCurrent(binding);
+ p2.bindCurrent(binding);
+ }
+
+ /**
+ * Offer promotion for subexpressions within this pattern. The offer will be accepted if the subexpression
+ * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
+ * By default the offer is not accepted - this is appropriate in the case of simple expressions
+ * such as constant values and variable references where promotion would give no performance
+ * advantage. This method is always called at compile time.
+ * <p/>
+ * <p>Unlike the corresponding method on {@link net.sf.saxon.expr.Expression}, this method does not return anything:
+ * it can make internal changes to the pattern, but cannot return a different pattern. Only certain
+ * kinds of promotion are applicable within a pattern: specifically, promotions affecting local
+ * variable references within the pattern.
+ * @param offer details of the offer, for example the offer to move
+ * expressions that don't depend on the context to an outer level in
+ * the containing expression
+ * @param parent the parent expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any error is detected
+ */
+
+ public void promote(PromotionOffer offer, Expression parent) throws XPathException {
+ p1.promote(offer, parent);
+ p2.promote(offer, parent);
+ }
+
+ /**
+ * Replace a subexpression by a replacement subexpression
+ * @param original the expression to be replaced
+ * @param replacement the new expression to be inserted in its place
+ * @return true if the replacement was carried out
+ */
+
+ public boolean replaceSubExpression(Expression original, Expression replacement) {
+ return p1.replaceSubExpression(original, replacement) ||
+ p2.replaceSubExpression(original, replacement);
+ }
+
+ /**
+ * Set the original text
+ */
+
+ public void setOriginalText(String pattern) {
+ super.setOriginalText(pattern);
+ p1.setOriginalText(pattern);
+ p2.setOriginalText(pattern);
+ }
+
+ /**
+ * Test whether a pattern is motionless, that is, whether it can be evaluated against a node
+ * without repositioning the input stream. This is a necessary condition for patterns used
+ * as the match pattern of a streamed template rule.
+ *
+ * @return true if the pattern is motionless, that is, if it can be evaluated against a streamed
+ * node without changing the position in the streamed input file
+ * @param allowExtensions true if Saxon extensions are allowed
+ */
+ @Override
+ public boolean isMotionless(boolean allowExtensions) {
+ return p1.isMotionless(allowExtensions) && p2.isMotionless(allowExtensions);
+ }
+
+ /**
+ * Allocate slots to any variables used within the pattern
+ *
+ * @param slotManager represents the stack frame on which slots are allocated
+ * @param nextFree the next slot that is free to be allocated @return the next slot that is free to be allocated
+ */
+
+ public int allocateSlots(SlotManager slotManager, int nextFree) {
+ nextFree = p1.allocateSlots(slotManager, nextFree);
+ nextFree = p2.allocateSlots(slotManager, nextFree);
+ return nextFree;
+ }
+
+ /**
+ * Gather the component (non-Venn) patterns of this Venn pattern
+ * @param set the set into which the components will be added
+ */
+
+ public void gatherComponentPatterns(Set<Pattern> set) {
+ if (p1 instanceof VennPattern) {
+ ((VennPattern)p1).gatherComponentPatterns(set);
+ } else {
+ set.add(p1);
+ }
+ if (p2 instanceof VennPattern) {
+ ((VennPattern)p2).gatherComponentPatterns(set);
+ } else {
+ set.add(p2);
+ }
+ }
+
+ /**
+ * Determine the types of nodes to which this pattern applies. Used for optimisation.
+ * For patterns that match nodes of several types, return Node.NODE
+ *
+ * @return the type of node matched by this pattern. e.g. Node.ELEMENT or Node.TEXT
+ */
+
+ public int getNodeKind() {
+ return nodeKind;
+ }
+
+ /**
+ * Get an ItemType that all the items matching this pattern must satisfy
+ */
+
+ public ItemType getItemType() {
+ if (nodeKind == Type.NODE) {
+ return AnyNodeTest.getInstance();
+ } else if (nodeKind == -1) {
+ // pattern does not match nodes
+ return AnyItemType.getInstance();
+ } else {
+ return NodeKindTest.makeNodeKindTest(nodeKind);
+ }
+ }
+
+
+ /**
+ * Get the dependencies of the pattern. The only possible dependency for a pattern is
+ * on local variables. This is analyzed in those patterns where local variables may appear.
+ *
+ * @return the dependencies, as a bit-significant mask
+ */
+
+ public int getDependencies() {
+ return p1.getDependencies() | p2.getDependencies();
+ }
+
+ /**
+ * Iterate over the subexpressions within this pattern
+ * @return an iterator over the subexpressions.
+ */
+
+ /*@NotNull*/
+ public Iterator<Expression> iterateSubExpressions() {
+ return new MultiIterator<Expression>(
+ new Iterator[]{p1.iterateSubExpressions(), p2.iterateSubExpressions()});
+ }
+
+ /**
+ * Get the LHS of the union
+ *
+ * @return the first operand of the union
+ */
+
+ public Pattern getLHS() {
+ return p1;
+ }
+
+ /**
+ * Get the RHS of the union
+ *
+ * @return the second operand of the union
+ */
+
+ public Pattern getRHS() {
+ return p2;
+ }
+
+ /**
+ * Override method to set the system ID, so it's set on both halves
+ */
+
+ public void setSystemId(String systemId) {
+ super.setSystemId(systemId);
+ p1.setSystemId(systemId);
+ p2.setSystemId(systemId);
+ }
+
+ /**
+ * Override method to set the system ID, so it's set on both halves
+ */
+
+ public void setLineNumber(int lineNumber) {
+ super.setLineNumber(lineNumber);
+ p1.setLineNumber(lineNumber);
+ p2.setLineNumber(lineNumber);
+ }
+
+ /**
+ * Determine whether this pattern is the same as another pattern
+ * @param other the other object
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof VennPattern) {
+ Set<Pattern> s0 = new HashSet<Pattern>(10);
+ gatherComponentPatterns(s0);
+ Set<Pattern> s1 = new HashSet<Pattern>(10);
+ ((VennPattern)other).gatherComponentPatterns(s1);
+ return s0.equals(s1);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Hashcode supporting equals()
+ */
+
+ public int hashCode() {
+ return 0x9bd723a6 ^ p1.hashCode() ^ p2.hashCode();
+ }
+
+
+}
\ No newline at end of file
diff --git a/sf/saxon/pattern/package.html b/sf/saxon/pattern/package.html
new file mode 100644
index 0000000..ea2d434
--- /dev/null
+++ b/sf/saxon/pattern/package.html
@@ -0,0 +1,51 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.pattern</title>
+</head>
+
+<body>
+
+<p>This package provides classes associated with XSLT pattern handling. </p>
+
+<p>The principal classes are:</p>
+
+<p><b>Pattern</b>:<br>
+This represents an XSLT Pattern. There is a static method Pattern.make() which is
+used to construct a Pattern from a String (it is a factory method rather than a
+ constructor, because it typically returns some subclass of Pattern according
+ to the syntax supplied). Subclasses of Pattern represent different kinds of pattern
+ such as LocationPathPattern and IDKeyPattern. What they all have in common is a match()
+ method, which determines whether a given node matches the pattern. A pattern is
+ not in itself an Expression, but the class <code>PatternSponsor</code> is used
+ to wrap a pattern making it look like an expression for the benefit of the static analysis
+ (allowing the same mechanisms to be used for example to find all the references to a variable).</p>
+
+<p><b>NodeTest</b>:<br>
+This represents a NodeTest within a step of an XPath expression. A NodeTest performs several
+roles: as well as its use in conjuction with an axis to form a step of a path expression,
+it acts as an ItemType used in handling type checking of nodes, and (wrapped in a NodeTestPattern)
+it acts as an XSLT pattern for use in constructs such as the <code>match</code> attribute of
+<code>xsl:template</code> and <code>xsl:key</code>.
+A NodeTest is used directly to implement simple patterns such as <code>match="item"</code>
+or <code>match="*"</code>. There are several subclasses of NodeTest, depending on the conditions
+to be matched: node type, node name, namespace URI, and so on. The class AnyNodeTest matches any node,
+while NoNodeTest matches nothing. NodeTests can also be combined using the operators of intersection,
+difference, and union, to describe the more complex types that are sometimes computed by the type
+checking machinery.</p>
+
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/pull/NamespaceContextImpl.java b/sf/saxon/pull/NamespaceContextImpl.java
new file mode 100644
index 0000000..64bb2b7
--- /dev/null
+++ b/sf/saxon/pull/NamespaceContextImpl.java
@@ -0,0 +1,107 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pull;
+
+import net.sf.saxon.om.NamespaceResolver;
+
+import javax.xml.namespace.NamespaceContext;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class bridges between the JAXP 1.3 NamespaceContext interface and Saxon's
+ * equivalent NamespaceResolver interface. It allows any implementation of the Saxon
+ * NamespaceResolver to be wrapped as a JAXP NamespaceContext.
+ */
+
+public class NamespaceContextImpl implements NamespaceContext, NamespaceResolver {
+
+ NamespaceResolver resolver;
+
+ /**
+ * Constructor: wrap a Saxon NamespaceResolver as a JAXP NamespaceContext
+ * @param resolver the Saxon NamespaceResolver
+ */
+
+ public NamespaceContextImpl(NamespaceResolver resolver) {
+ this.resolver = resolver;
+ }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ * @param prefix the namespace prefix
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is ""
+ * @return the uri for the namespace, or null if the prefix is not in scope
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(String prefix, boolean useDefault) {
+ return resolver.getURIForPrefix(prefix, useDefault);
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ return resolver.iteratePrefixes();
+ }
+
+ /**
+ * Implement the JAXP getNamespaceURI() method in terms of the Saxon-specific methods
+ * @param prefix a namespace prefix
+ * @return the corresponding URI, if the prefix is bound, or "" otherwise
+ */
+
+ public String getNamespaceURI(String prefix) {
+ if (prefix.equals("xmlns")) {
+ return "http://www.w3.org/2000/xmlns/";
+ }
+ return resolver.getURIForPrefix(prefix, true);
+ }
+
+ /**
+ * Get the prefix bound to a particular namespace URI, if there is one, or null if not (JAXP method)
+ * @param uri the namespace URI
+ * @return the prefix bound to the URI if there is one, or null if not
+ */
+
+ public String getPrefix(String uri) {
+ Iterator prefixes = iteratePrefixes();
+ while (prefixes.hasNext()) {
+ String p = (String)prefixes.next();
+ String u = resolver.getURIForPrefix(p, true);
+ if (u.equals(uri)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all the prefixes mapped to a given namespace URI (JAXP method)
+ * @param uri the namespace URI
+ * @return an iterator over all the prefixes bound to this namespace URI
+ */
+ public Iterator getPrefixes(String uri) {
+ List list = new ArrayList(4);
+ Iterator prefixes = iteratePrefixes();
+ while (prefixes.hasNext()) {
+ String p = (String)prefixes.next();
+ String u = resolver.getURIForPrefix(p, true);
+ if (u.equals(uri)) {
+ list.add(p);
+ }
+ }
+ return list.iterator();
+ }
+}
+
diff --git a/sf/saxon/pull/PullConsumer.java b/sf/saxon/pull/PullConsumer.java
new file mode 100644
index 0000000..5c8d48f
--- /dev/null
+++ b/sf/saxon/pull/PullConsumer.java
@@ -0,0 +1,45 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pull;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A PullConsumer consumes all the events supplied by a PullProvider, doing nothing
+ * with them. The class exists so that PullFilters on the pipeline can produce side-effects.
+ * For example, this class can be used to validate a document, where the side effects are
+ * error messages.
+ */
+
+public class PullConsumer {
+
+ private PullProvider in;
+
+ /**
+ * Create a PullConsumer that swallows the events read from a given pull provider
+ * @param in the PullProvider from which events are to be read and swallowed up
+ */
+
+ public PullConsumer(PullProvider in) {
+ this.in = in;
+ }
+
+ /**
+ * Consume the input
+ * @throws net.sf.saxon.trans.XPathException
+ */
+
+ public void consume() throws XPathException {
+ while (true) {
+ if (in.next() == PullProvider.END_OF_INPUT) {
+ return;
+ }
+ }
+ }
+}
+
diff --git a/sf/saxon/pull/PullFilter.java b/sf/saxon/pull/PullFilter.java
new file mode 100644
index 0000000..0279c64
--- /dev/null
+++ b/sf/saxon/pull/PullFilter.java
@@ -0,0 +1,273 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pull;
+
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.value.AtomicValue;
+
+import javax.xml.transform.SourceLocator;
+import java.util.List;
+
+/**
+ * PullFilter is a pass-through filter class that links one PullProvider to another PullProvider
+ * in a pipeline. This class passes all events through completely unchanged. The class is designed
+ * so that subclasses can modify this behavior by altering some of the events.
+*/
+
+public class PullFilter implements PullProvider {
+
+ private PullProvider base;
+ private PipelineConfiguration pipe;
+ protected int currentEvent;
+
+ /**
+ * Create a PullFilter
+ * @param base the PullProvider to which requests are to be passed
+ */
+
+ public PullFilter(/*@NotNull*/ PullProvider base) {
+ this.base = base;
+ if (base.getPipelineConfiguration() != null) {
+ setPipelineConfiguration(base.getPipelineConfiguration());
+ }
+ }
+
+ /**
+ * Set configuration information. This must only be called before any events
+ * have been read.
+ */
+
+ public void setPipelineConfiguration(PipelineConfiguration pipe) {
+ this.pipe = pipe;
+ base.setPipelineConfiguration(pipe);
+ }
+
+ /**
+ * Get configuration information.
+ */
+
+ public PipelineConfiguration getPipelineConfiguration() {
+ return pipe;
+ }
+
+ /**
+ * Helper method to get the current namePool
+ * @return the NamePool
+ */
+
+ public final NamePool getNamePool() {
+ return getPipelineConfiguration().getConfiguration().getNamePool();
+ }
+
+ /**
+ * Get the underlying PullProvider
+ * @return the underlying PullProvider
+ */
+
+ public PullProvider getUnderlyingProvider() {
+ return base;
+ }
+
+ /**
+ * Get the next event.
+ *
+ * <p>Note that a subclass that overrides this method is responsible for ensuring
+ * that current() works properly. This can be achieved by setting the field
+ * currentEvent to the event returned by any call on next().</p>
+ *
+ * @return an integer code indicating the type of event. The code
+ * {@link #END_OF_INPUT} is returned at the end of the sequence.
+ */
+
+ public int next() throws XPathException {
+ return base.next();
+ }
+
+ /**
+ * Get the event most recently returned by next(), or by other calls that change
+ * the position, for example getStringValue() and skipToMatchingEnd(). This
+ * method does not change the position of the PullProvider.
+ *
+ * @return the current event
+ */
+
+ public int current() {
+ return currentEvent;
+ }
+
+ /**
+ * Get the attributes associated with the current element. This method must
+ * be called only after a START_ELEMENT event has been notified. The contents
+ * of the returned AttributeCollection are guaranteed to remain unchanged
+ * until the next START_ELEMENT event, but may be modified thereafter. The object
+ * should not be modified by the client.
+ * <p/>
+ * <p>Attributes may be read before or after reading the namespaces of an element,
+ * but must not be read after the first child node has been read, or after calling
+ * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>
+ *
+ * @return an AttributeCollection representing the attributes of the element
+ * that has just been notified.
+ */
+
+ public AttributeCollection getAttributes() throws XPathException {
+ return base.getAttributes();
+ }
+
+ /**
+ * Get the namespace declarations associated with the current element. This method must
+ * be called only after a START_ELEMENT event has been notified. In the case of a top-level
+ * START_ELEMENT event (that is, an element that either has no parent node, or whose parent
+ * is not included in the sequence being read), the NamespaceDeclarations object returned
+ * will contain a namespace declaration for each namespace that is in-scope for this element
+ * node. In the case of a non-top-level element, the NamespaceDeclarations will contain
+ * a set of namespace declarations and undeclarations, representing the differences between
+ * this element and its parent.
+ * <p/>
+ * <p>It is permissible for this method to return namespace declarations that are redundant.</p>
+ * <p/>
+ * <p>The NamespaceDeclarations object is guaranteed to remain unchanged until the next START_ELEMENT
+ * event, but may then be overwritten. The object should not be modified by the client.</p>
+ * <p/>
+ * <p>Namespaces may be read before or after reading the attributes of an element,
+ * but must not be read after the first child node has been read, or after calling
+ * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>*
+ */
+
+ public NamespaceBinding[] getNamespaceDeclarations() throws XPathException {
+ return base.getNamespaceDeclarations();
+ }
+
+ /**
+ * Skip the current subtree. This method may be called only immediately after
+ * a START_DOCUMENT or START_ELEMENT event. This call returns the matching
+ * END_DOCUMENT or END_ELEMENT event; the next call on next() will return
+ * the event following the END_DOCUMENT or END_ELEMENT.
+ */
+
+ public int skipToMatchingEnd() throws XPathException {
+ return base.skipToMatchingEnd();
+ }
+
+ /**
+ * Close the event reader. This indicates that no further events are required.
+ * It is not necessary to close an event reader after {@link #END_OF_INPUT} has
+ * been reported, but it is recommended to close it if reading terminates
+ * prematurely. Once an event reader has been closed, the effect of further
+ * calls on next() is undefined.
+ */
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Get the nameCode identifying the name of the current node. This method
+ * can be used after the {@link #START_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
+ * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events. With some PullProvider implementations,
+ * it can also be used after {@link #END_ELEMENT}, but this is not guaranteed: a client who
+ * requires the information at that point (for example, to do serialization) should insert an
+ * {@link com.saxonica.pull.ElementNameTracker} into the pipeline.
+ * If called at other times, the result is undefined and may result in an IllegalStateException.
+ * If called when the current node is an unnamed namespace node (a node representing the default namespace)
+ * the returned value is -1.
+ *
+ * @return the nameCode. The nameCode can be used to obtain the prefix, local name,
+ * and namespace URI from the name pool.
+ */
+
+ public int getNameCode() {
+ return base.getNameCode();
+ }
+
+ /**
+ * Get the fingerprint of the name of the element. This is similar to the nameCode, except that
+ * it does not contain any information about the prefix: so two elements with the same fingerprint
+ * have the same name, excluding prefix. This method
+ * can be used after the {@link #START_ELEMENT}, {@link #END_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
+ * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events.
+ * If called at other times, the result is undefined and may result in an IllegalStateException.
+ * If called when the current node is an unnamed namespace node (a node representing the default namespace)
+ * the returned value is -1.
+ *
+ * @return the fingerprint. The fingerprint can be used to obtain the local name
+ * and namespace URI from the name pool.
+ */
+
+ public int getFingerprint() {
+ return base.getFingerprint();
+ }
+
+ /**
+ * Get the string value of the current element, text node, processing-instruction,
+ * or top-level attribute or namespace node, or atomic value.
+ * <p/>
+ * <p>In other situations the result is undefined and may result in an IllegalStateException.</p>
+ * <p/>
+ * <p>If the most recent event was a {@link #START_ELEMENT}, this method causes the content
+ * of the element to be read. The next event notified will be the corresponding {@link #END_ELEMENT}.</p>
+ *
+ * @return the String Value of the node in question, defined according to the rules in the
+ * XPath data model.
+ */
+
+ public CharSequence getStringValue() throws XPathException {
+ return base.getStringValue();
+ }
+
+ /**
+ * Get an atomic value. This call may be used only when the last event reported was
+ * ATOMIC_VALUE. This indicates that the PullProvider is reading a sequence that contains
+ * a free-standing atomic value; it is never used when reading the content of a node.
+ */
+
+ public AtomicValue getAtomicValue() {
+ return base.getAtomicValue();
+ }
+
+ /**
+ * Get the type annotation of the current attribute or element node, or atomic value.
+ * The result of this method is undefined unless the most recent event was START_ELEMENT,
+ * ATTRIBUTE, or ATOMIC_VALUE.
+ *
+ * @return the type annotation.
+ */
+
+ public SchemaType getSchemaType() {
+ return base.getSchemaType();
+ }
+
+ /**
+ * Get the location of the current event.
+ * For an event stream representing a real document, the location information
+ * should identify the location in the lexical XML source. For a constructed document, it should
+ * identify the location in the query or stylesheet that caused the node to be created.
+ * A value of null can be returned if no location information is available.
+ */
+
+ public SourceLocator getSourceLocator() {
+ return base.getSourceLocator();
+ }
+
+ /**
+ * Get a list of unparsed entities.
+ *
+ * @return a list of unparsed entities, or null if the information is not available, or
+ * an empty list if there are no unparsed entities.
+ */
+
+ public List getUnparsedEntities() {
+ return base.getUnparsedEntities();
+ }
+}
+
diff --git a/sf/saxon/pull/PullProvider.java b/sf/saxon/pull/PullProvider.java
new file mode 100644
index 0000000..09f6d75
--- /dev/null
+++ b/sf/saxon/pull/PullProvider.java
@@ -0,0 +1,350 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pull;
+
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.value.AtomicValue;
+
+import javax.xml.transform.SourceLocator;
+import java.util.List;
+
+/**
+ * PullProvider is Saxon's pull-based interface for reading XML documents and XDM sequences.
+ * A PullProvider can deliver any sequence of nodes or atomic values. An atomic value
+ * in the sequence is delivered as a single event; a node is delivered as a sequence
+ * of events equivalent to a recursive walk of the XML tree. Within this sequence,
+ * the start and end of a document, or of an element, are delivered as separate
+ * events; other nodes are delivered as individual events.
+ */
+
+public interface PullProvider {
+
+ // Start by defining the different types of event
+
+ /**
+ * START_OF_INPUT is the initial state when the PullProvider is instantiated.
+ * This event is never notified by the next() method, but it is returned
+ * from a call of current() prior to the first call on next().
+ */
+
+ public static final int START_OF_INPUT = 0;
+
+ /**
+ * ATOMIC_VALUE is notified when the PullProvider is reading a sequence of items,
+ * and one of the items is an atomic value rather than a node. This will always
+ * be a top-level event (it will never be nested in Start/End Document or
+ * Start/End Element).
+ */
+
+ public static final int ATOMIC_VALUE = 1;
+
+ /**
+ * START_DOCUMENT is notified when a document node is encountered. This will
+ * always be a top-level event (it will never be nested in Start/End Document or
+ * Start/End Element). Note however that multiple document nodes can occur in
+ * a sequence, and the start and end of each one will be notified.
+ */
+
+ public static final int START_DOCUMENT = 2;
+
+ /**
+ * END_DOCUMENT is notified at the end of processing a document node, that is,
+ * after all the descendants of the document node have been notified. The event
+ * will always be preceded by the corresponding START_DOCUMENT event.
+ */
+
+ public static final int END_DOCUMENT = 3;
+
+ /**
+ * START_ELEMENT is notified when an element node is encountered. This may either
+ * be a top-level element (an element node that participates in the sequence being
+ * read in its own right) or a nested element (reported because it is a descendant
+ * of an element or document node that participates in the sequence.)
+ *
+ * <p>Following the notification of START_ELEMENT, the client may obtain information
+ * about the element node, such as its name and type annotation. The client may also
+ * call getAttributes() to obtain information about the attributes of the element
+ * node, and/or getNamespaceDeclarations() to get information about the namespace
+ * declarations. The client may then do one of the following:</p>
+ *
+ * <ul>
+ * <li>Call skipToMatchingEnd() to move straight to the corresponding END_ELEMENT event (which
+ * will then be the current event)</li>
+ * <li>Call next(), repeatedly, to be notified of events relating to the children and
+ * descendants of this element node</li>
+ * <li>Call getStringValue() to obtain the string value of the element node, after which
+ * the next event notified will be the corresponding END_ELEMENT event</li>
+ * <li>Call getTypedValue() to obtain the typed value of the element node, after which
+ * the next event notified will be the corresponding END_ELEMENT event</li>
+ * </ul>
+ */
+
+ public static final int START_ELEMENT = 4;
+
+ /**
+ * END_ELEMENT is notified at the end of an element node, that is, after all the children
+ * and descendants of the element have either been processed or skipped. It may relate to
+ * a top-level element, or to a nested element. For an empty element (one with no children)
+ * the END_ELEMENT event will immediately follow the corresponding START_ELEMENT event.
+ * No information (such as the element name) is available after an END_ELEMENT event: if the
+ * client requires such information, it must remember it, typically on a Stack.
+ */
+
+ public static final int END_ELEMENT = 5;
+
+ /**
+ * The ATTRIBUTE event is notified only for an attribute node that appears in its own right
+ * as a top-level item in the sequence being read. ATTRIBUTE events are not notified for
+ * the attributes of an element that has been notified: such attributes must be read using the
+ * {@link #getAttributes()} method.
+ */
+
+ public static final int ATTRIBUTE = 6;
+
+ /**
+ * The NAMESPACE event is notified only for a namespace node that appears in its own right
+ * as a top-level item in the sequence being read. NAMESPACE events are not notified for
+ * the namespaces of an element that has been notified: such attributes must be read using the
+ * {@link #getNamespaceDeclarations()} method.
+ */
+
+ public static final int NAMESPACE = 7;
+
+ /**
+ * A TEXT event is notified for a text node. This may either be a top-level text
+ * node, or a text node nested within an element or document node. At the top level,
+ * text nodes may be zero-length and may be consecutive in the sequence being read.
+ * Nested within an element or document node, text nodes will never be zero-length,
+ * and adjacent text nodes will have been coalesced into one. (This might not always
+ * be true when reading third-party data models such as a DOM.) Whitespace-only
+ * text nodes will be notified unless something has been done (e.g. xsl:strip-space)
+ * to remove them.
+ */
+
+ public static final int TEXT = 8;
+
+ /**
+ * A COMMENT event is notified for a comment node, which may be either a top-level
+ * comment or one nested within an element or document node.
+ */
+
+ public static final int COMMENT = 9;
+
+ /**
+ * A PROCESSING_INSTRUCTION event is notified for a processing instruction node,
+ * which may be either a top-level comment or one nested within an element or document node.
+ * As defined in the XPath data model, the "target" of a processing instruction is represented
+ * as the node name (which only has a local part, no prefix or URI), and the "data" of the
+ * processing instruction is represented as the string-value of the node.
+ */
+
+ public static final int PROCESSING_INSTRUCTION = 10;
+
+ /**
+ * The END_OF_INPUT event is returned to indicate the end of the sequence being read.
+ * After this event, the result of any further calls on the next() method is undefined.
+ */
+
+ public static final int END_OF_INPUT = -1;
+
+ /**
+ * Set configuration information. This must only be called before any events
+ * have been read.
+ * @param pipe the pipeline configuration
+ */
+
+ public void setPipelineConfiguration(PipelineConfiguration pipe);
+
+ /**
+ * Get configuration information.
+ * @return the pipeline configuration
+ */
+
+ public PipelineConfiguration getPipelineConfiguration();
+
+ /**
+ * Get the next event
+ * @return an integer code indicating the type of event. The code
+ * {@link #END_OF_INPUT} is returned at the end of the sequence.
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ public int next() throws XPathException;
+
+ /**
+ * Get the event most recently returned by next(), or by other calls that change
+ * the position, for example getStringValue() and skipToMatchingEnd(). This
+ * method does not change the position of the PullProvider.
+ * @return the current event
+ */
+
+ public int current();
+
+ /**
+ * Get the attributes associated with the current element. This method must
+ * be called only after a START_ELEMENT event has been notified. The contents
+ * of the returned AttributeCollection are guaranteed to remain unchanged
+ * until the next START_ELEMENT event, but may be modified thereafter. The object
+ * should not be modified by the client.
+ *
+ * <p>Attributes may be read before or after reading the namespaces of an element,
+ * but must not be read after the first child node has been read, or after calling
+ * one of the methods skipToMatchingEnd(), getStringValue(), or getTypedValue().</p>
+ *
+ * @return an AttributeCollection representing the attributes of the element
+ * that has just been notified.
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ public AttributeCollection getAttributes() throws XPathException;
+
+ /**
+ * Get the namespace declarations associated with the current element. This method must
+ * be called only after a START_ELEMENT event has been notified. In the case of a top-level
+ * START_ELEMENT event (that is, an element that either has no parent node, or whose parent
+ * is not included in the sequence being read), the NamespaceDeclarations object returned
+ * will contain a namespace declaration for each namespace that is in-scope for this element
+ * node. In the case of a non-top-level element, the NamespaceDeclarations will contain
+ * a set of namespace declarations and undeclarations, representing the differences between
+ * this element and its parent.
+ *
+ * <p>It is permissible for this method to return namespace declarations that are redundant.</p>
+ *
+ * <p>The NamespaceDeclarations object is guaranteed to remain unchanged until the next START_ELEMENT
+ * event, but may then be overwritten. The object should not be modified by the client.</p>
+ *
+ * <p>Namespaces may be read before or after reading the attributes of an element,
+ * but must not be read after the first child node has been read, or after calling
+ * one of the methods skipToMatchingEnd(), getStringValue(), or getTypedValue().</p>
+ *
+ * @return the namespace declarations associated with the current START_ELEMENT event.
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ public NamespaceBinding[] getNamespaceDeclarations() throws XPathException;
+
+ /**
+ * Skip the current subtree. This method may be called only immediately after
+ * a START_DOCUMENT or START_ELEMENT event. This call returns the matching
+ * END_DOCUMENT or END_ELEMENT event; the next call on next() will return
+ * the event following the END_DOCUMENT or END_ELEMENT.
+ * @return the matching END_DOCUMENT or END_ELEMENT event
+ * @throws IllegalStateException if the method is called at any time other than
+ * immediately after a START_DOCUMENT or START_ELEMENT event.
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ public int skipToMatchingEnd() throws XPathException;
+
+ /**
+ * Close the event reader. This indicates that no further events are required.
+ * It is not necessary to close an event reader after {@link #END_OF_INPUT} has
+ * been reported, but it is recommended to close it if reading terminates
+ * prematurely. Once an event reader has been closed, the effect of further
+ * calls on next() is undefined.
+ */
+
+ public void close();
+
+ /**
+ * Get the nameCode identifying the name of the current node. This method
+ * can be used after the {@link #START_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
+ * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events. With some PullProvider implementations,
+ * it can also be used after {@link #END_ELEMENT}, but this is not guaranteed: a client who
+ * requires the information at that point (for example, to do serialization) should insert an
+ * {@link com.saxonica.pull.ElementNameTracker} into the pipeline.
+ * If called at other times, the result is undefined and may result in an IllegalStateException.
+ * If called when the current node is an unnamed namespace node (a node representing the default namespace)
+ * the returned value is -1.
+ * @return the nameCode. The nameCode can be used to obtain the prefix, local name,
+ * and namespace URI from the name pool.
+ */
+
+ public int getNameCode();
+
+ /**
+ * Get the fingerprint of the name of the element. This is similar to the nameCode, except that
+ * it does not contain any information about the prefix: so two elements with the same fingerprint
+ * have the same name, excluding prefix. This method
+ * can be used after the {@link #START_ELEMENT}, {@link #END_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
+ * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events.
+ * If called at other times, the result is undefined and may result in an IllegalStateException.
+ * If called when the current node is an unnamed namespace node (a node representing the default namespace)
+ * the returned value is -1.
+ * @return the fingerprint. The fingerprint can be used to obtain the local name
+ * and namespace URI from the name pool.
+ */
+
+ public int getFingerprint();
+
+ /**
+ * Get the string value of the current element, text node, processing-instruction,
+ * or top-level attribute or namespace node, or atomic value.
+ *
+ * <p>In other situations the result is undefined and may result in an IllegalStateException.</p>
+ *
+ * <p>If the most recent event was a {@link #START_ELEMENT}, this method causes the content
+ * of the element to be read. The current event on completion of this method will be the
+ * corresponding {@link #END_ELEMENT}. The next call of next() will return the event following
+ * the END_ELEMENT event.</p>
+ *
+ * @return the String Value of the node in question, defined according to the rules in the
+ * XPath data model.
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ /*@Nullable*/ public CharSequence getStringValue() throws XPathException;
+
+ /**
+ * Get the type annotation of the current attribute or element node, or atomic value.
+ * The result of this method is undefined unless the most recent event was START_ELEMENT,
+ * ATTRIBUTE, or ATOMIC_VALUE.
+ *
+ * @return the type annotation.
+ *
+ * @since 9.4; replace the method getTypeAnnotation() which returned the integer fingerprint of the type
+ */
+
+ public SchemaType getSchemaType();
+
+ /**
+ * Get an atomic value. This call may be used only when the last event reported was
+ * ATOMIC_VALUE. This indicates that the PullProvider is reading a sequence that contains
+ * a free-standing atomic value; it is never used when reading the content of a node.
+ * @return the atomic value
+ */
+
+ public AtomicValue getAtomicValue();
+
+ /**
+ * Get the location of the current event.
+ * For an event stream representing a real document, the location information
+ * should identify the location in the lexical XML source. For a constructed document, it should
+ * identify the location in the query or stylesheet that caused the node to be created.
+ * A value of null can be returned if no location information is available.
+ * @return the SourceLocator giving the location of the current event, or null if
+ * no location information is available
+ */
+
+ public SourceLocator getSourceLocator();
+
+ /**
+ * Get a list of unparsed entities.
+ * @return a list of unparsed entities, or null if the information is not available, or
+ * an empty list if there are no unparsed entities. Each item in the list will
+ * be an instance of {@link net.sf.saxon.pull.UnparsedEntity}
+ */
+
+ public List getUnparsedEntities();
+
+}
+
diff --git a/sf/saxon/pull/PullPushCopier.java b/sf/saxon/pull/PullPushCopier.java
new file mode 100644
index 0000000..dfe366d
--- /dev/null
+++ b/sf/saxon/pull/PullPushCopier.java
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pull;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class copies a document by using the pull interface to read the input document,
+ * and the push interface to write the output document.
+ */
+public class PullPushCopier {
+
+ private PullProvider in;
+ private Receiver out;
+
+ /**
+ * Create a PullPushCopier
+ * @param in a PullProvider from which events will be read
+ * @param out a Receiver to which copies of the same events will be written
+ */
+
+ public PullPushCopier(PullProvider in, Receiver out) {
+ this.out = out;
+ this.in = in;
+ }
+
+ /**
+ * Copy the input to the output. This method will open the output Receiver before appending to
+ * it, and will close it afterwards.
+ * @throws XPathException
+ */
+
+ public void copy() throws XPathException {
+ out.open();
+ PullPushTee tee = new PullPushTee(in, out);
+ new PullConsumer(tee).consume();
+ out.close();
+ }
+
+ /**
+ * Copy the input to the output. This method relies on the caller to open the output Receiver before
+ * use and to close it afterwards.
+ * @throws XPathException
+ */
+
+ public void append() throws XPathException {
+ PullPushTee tee = new PullPushTee(in, out);
+ new PullConsumer(tee).consume();
+ }
+}
+
diff --git a/sf/saxon/pull/PullPushTee.java b/sf/saxon/pull/PullPushTee.java
new file mode 100644
index 0000000..912259a
--- /dev/null
+++ b/sf/saxon/pull/PullPushTee.java
@@ -0,0 +1,194 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pull;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.CodedName;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+
+import java.util.List;
+
+/**
+ * PullPushTee is a pass-through filter class that links one PullProvider to another PullProvider
+ * in a pipeline, copying all events that are read into a push pipeline, supplied in the form
+ * of a Receiver.
+ *
+ * <p>This class can be used to insert a schema validator into a pull pipeline, since Saxon's schema
+ * validation is push-based. It could also be used to insert a serializer into the pipeline, allowing
+ * the XML document being "pulled" to be displayed for diagnostic purposes.</p>
+*/
+
+public class PullPushTee extends PullFilter {
+
+ private Receiver branch;
+ boolean previousAtomic = false;
+
+ /**
+ * Create a PullPushTee
+ * @param base the PullProvider to which requests are to be passed
+ * @param branch the Receiver to which all events are to be copied, as "push" events.
+ * This Receiver must already be open before use
+ */
+
+ public PullPushTee(/*@NotNull*/ PullProvider base, Receiver branch) throws XPathException {
+ super(base);
+ this.branch = branch;
+ //branch.open();
+ }
+
+ /**
+ * Get the Receiver to which events are being tee'd.
+ * @return the Receiver
+ */
+
+ public Receiver getReceiver() {
+ return branch;
+ }
+
+ /**
+ * Get the next event. This implementation gets the next event from the underlying PullProvider,
+ * copies it to the branch Receiver, and then returns the event to the caller.
+ *
+ * @return an integer code indicating the type of event. The code
+ * {@link #END_OF_INPUT} is returned at the end of the sequence.
+ */
+
+ public int next() throws XPathException {
+ currentEvent = super.next();
+ copyEvent(currentEvent);
+ return currentEvent;
+ }
+
+
+ /**
+ * Copy a pull event to a Receiver
+ * @param event the pull event to be copied
+ */
+
+ private void copyEvent(int event) throws XPathException {
+ PullProvider in = getUnderlyingProvider();
+ Receiver out = branch;
+ switch (event) {
+ case START_DOCUMENT:
+ out.startDocument(0);
+ break;
+
+ case START_ELEMENT:
+ out.startElement(new CodedName(in.getNameCode(), getNamePool()), in.getSchemaType(), 0, 0);
+
+ NamespaceBinding[] decl = in.getNamespaceDeclarations();
+ for (NamespaceBinding aDecl : decl) {
+ if (aDecl == null) {
+ break;
+ }
+ out.namespace(aDecl, 0);
+ }
+
+ AttributeCollection atts = in.getAttributes();
+ for (int i=0; i<atts.getLength(); i++) {
+ out.attribute(atts.getNodeName(i), atts.getTypeAnnotation(i),
+ atts.getValue(i), 0, atts.getProperties(i));
+ }
+
+ out.startContent();
+ break;
+
+ case TEXT:
+
+ out.characters(in.getStringValue(), 0, 0);
+ break;
+
+ case COMMENT:
+
+ out.comment(in.getStringValue(), 0, 0);
+ break;
+
+ case PROCESSING_INSTRUCTION:
+
+ out.processingInstruction(
+ in.getPipelineConfiguration().getConfiguration().getNamePool().getLocalName(in.getNameCode()),
+ in.getStringValue(), 0, 0);
+ break;
+
+ case END_ELEMENT:
+
+ out.endElement();
+ break;
+
+ case END_DOCUMENT:
+ List entities = in.getUnparsedEntities();
+ if (entities != null) {
+ for (int i=0; i<entities.size(); i++) {
+ UnparsedEntity ue = (UnparsedEntity)entities.get(i);
+ out.setUnparsedEntity(ue.getName(), ue.getSystemId(), ue.getPublicId());
+ }
+ }
+ out.endDocument();
+ break;
+
+ case END_OF_INPUT:
+ in.close();
+ //out.close();
+ break;
+
+ case ATOMIC_VALUE:
+ if (out instanceof SequenceReceiver) {
+ ((SequenceReceiver)out).append(super.getAtomicValue(), 0, 0);
+ break;
+ } else {
+ if (previousAtomic) {
+ out.characters(" ", 0, 0);
+ }
+ CharSequence chars = in.getStringValue();
+ out.characters(chars, 0, 0);
+ break;
+ }
+
+ case ATTRIBUTE:
+ if (out instanceof SequenceReceiver) {
+ Orphan o = new Orphan(in.getPipelineConfiguration().getConfiguration());
+ o.setNodeName(new CodedName(getNameCode(), getNamePool()));
+ o.setNodeKind(Type.ATTRIBUTE);
+ o.setStringValue(getStringValue());
+ ((SequenceReceiver)out).append(o, 0, 0);
+ break;
+ } else {
+ out.attribute(new CodedName(getNameCode(), getNamePool()),
+ (SimpleType)in.getSchemaType(), getStringValue(), 0, 0);
+ break;
+ //throw new XPathException("Cannot serialize a free-standing attribute node");
+ }
+
+ case NAMESPACE:
+ if (out instanceof SequenceReceiver) {
+ Orphan o = new Orphan(in.getPipelineConfiguration().getConfiguration());
+ o.setNodeName(new CodedName(getNameCode(), getNamePool()));
+ o.setNodeKind(Type.NAMESPACE);
+ o.setStringValue(getStringValue());
+ ((SequenceReceiver)out).append(o, 0, 0);
+ break;
+ } else {
+ out.namespace(getNamePool().getNamespaceBinding(getNameCode()), 0);
+ break;
+ //throw new XPathException("Cannot serialize a free-standing namespace node");
+ }
+
+ default:
+ throw new UnsupportedOperationException("" + event);
+
+ }
+ previousAtomic = (event == ATOMIC_VALUE);
+ }
+}
+
diff --git a/sf/saxon/pull/PullSource.java b/sf/saxon/pull/PullSource.java
new file mode 100644
index 0000000..1040a6c
--- /dev/null
+++ b/sf/saxon/pull/PullSource.java
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pull;
+
+import javax.xml.transform.Source;
+
+/**
+ * A PullSource is a JAXP Source that encapsulates a PullProvider - that is, an object
+ * that supplies an XML document as a sequence of events that are read under the control
+ * of the recipient. Note that although PullSource implements the JAXP Source interface,
+ * it is not necessarily acceptable to every JAXP implementation that accepts a Source
+ * as input: Source is essentially a marker interface and users of Source objects need
+ * to understand the individual implementation.
+ */
+
+public class PullSource implements Source {
+
+ private String systemId;
+ private PullProvider provider;
+
+ /**
+ * Create a PullSource based on a supplied PullProvider
+ * @param provider the underlying PullProvider
+ */
+
+ public PullSource(/*@NotNull*/ PullProvider provider) {
+ this.provider = provider;
+ if (provider.getSourceLocator() != null) {
+ systemId = provider.getSourceLocator().getSystemId();
+ }
+ }
+
+ /**
+ * Get the PullProvider
+ * @return the underlying PullProvider
+ */
+
+ public PullProvider getPullProvider() {
+ return provider;
+ }
+
+ /**
+ * Set the system identifier for this Source.
+ * <p/>
+ * <p>The system identifier is optional if the source does not
+ * get its data from a URL, but it may still be useful to provide one.
+ * The application can use a system identifier, for example, to resolve
+ * relative URIs and to include in error messages and warnings.</p>
+ *
+ * @param systemId The system identifier as a URL string.
+ */
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the system identifier that was set with setSystemId.
+ *
+ * @return The system identifier that was set with setSystemId, or null
+ * if setSystemId was not called.
+ */
+ public String getSystemId() {
+ return systemId;
+ }
+}
+
diff --git a/sf/saxon/pull/StaxBridge.java b/sf/saxon/pull/StaxBridge.java
new file mode 100644
index 0000000..95b7c2e
--- /dev/null
+++ b/sf/saxon/pull/StaxBridge.java
@@ -0,0 +1,1055 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pull;
+
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.*;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.serialize.XMLEmitter;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.tree.tiny.CompressedWhitespace;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.stream.*;
+import javax.xml.stream.events.EntityDeclaration;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * This class implements the Saxon PullProvider API on top of a standard StAX parser
+ * (or any other StAX XMLStreamReader implementation)
+ */
+
+public class StaxBridge implements PullProvider, SaxonLocator, SourceLocationProvider {
+
+ private XMLStreamReader reader;
+ private StaxAttributes attributes = new StaxAttributes();
+ private PipelineConfiguration pipe;
+ /*@Nullable*/ private List unparsedEntities = null;
+ int currentEvent = START_OF_INPUT;
+ int depth = 0;
+ boolean ignoreIgnorable = false;
+
+ /**
+ * Create a new instance of the class
+ */
+
+ public StaxBridge() {
+
+ }
+
+ /**
+ * Supply an input stream containing XML to be parsed. A StAX parser is created using
+ * the JAXP XMLInputFactory.
+ * @param systemId The Base URI of the input document
+ * @param inputStream the stream containing the XML to be parsed
+ * @throws XPathException if an error occurs creating the StAX parser
+ */
+
+ public void setInputStream(String systemId, InputStream inputStream) throws XPathException {
+ try {
+ XMLInputFactory factory = XMLInputFactory.newInstance();
+ //XMLInputFactory factory = new WstxInputFactory();
+ factory.setXMLReporter(new StaxErrorReporter());
+ reader = factory.createXMLStreamReader(systemId, inputStream);
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ /**
+ * Supply an XMLStreamReader: the events reported by this XMLStreamReader will be translated
+ * into PullProvider events
+ * @param reader the supplier of XML events, typically an XML parser
+ */
+
+ public void setXMLStreamReader(XMLStreamReader reader) {
+ this.reader = reader;
+ }
+
+ /**
+ * Set configuration information. This must only be called before any events
+ * have been read.
+ */
+
+ public void setPipelineConfiguration(PipelineConfiguration pipe) {
+ this.pipe = new PipelineConfiguration(pipe);
+ this.pipe.setLocationProvider(this);
+ ignoreIgnorable = pipe.getConfiguration().getStripsWhiteSpace() != Whitespace.NONE;
+ }
+
+ /**
+ * Get configuration information.
+ */
+
+ public PipelineConfiguration getPipelineConfiguration() {
+ return pipe;
+ }
+
+ /**
+ * Get the XMLStreamReader used by this StaxBridge. This is available only after
+ * setInputStream() or setXMLStreamReader() has been called
+ * @return the instance of XMLStreamReader allocated when setInputStream() was called,
+ * or the instance supplied directly to setXMLStreamReader()
+ */
+
+ public XMLStreamReader getXMLStreamReader() {
+ return reader;
+ }
+
+ /**
+ * Get the name pool
+ * @return the name pool
+ */
+
+ public NamePool getNamePool() {
+ return pipe.getConfiguration().getNamePool();
+ }
+
+ /**
+ * Get the next event
+ *
+ * @return an integer code indicating the type of event. The code
+ * {@link #END_OF_INPUT} is returned at the end of the sequence.
+ */
+
+ public int next() throws XPathException {
+ if (currentEvent == START_OF_INPUT) {
+ // StAX isn't reporting START_DOCUMENT so we supply it ourselves
+ currentEvent = START_DOCUMENT;
+ return currentEvent;
+ }
+ if (currentEvent == END_OF_INPUT || currentEvent == END_DOCUMENT) {
+ try {
+ reader.close();
+ } catch (XMLStreamException e) {
+ //
+ }
+ return END_OF_INPUT;
+ }
+ try {
+ if (reader.hasNext()) {
+ int event = reader.next();
+ //System.err.println("Read event " + event);
+ currentEvent = translate(event);
+ } else {
+ currentEvent = END_OF_INPUT;
+ }
+ } catch (XMLStreamException e) {
+ String message = e.getMessage();
+ // Following code recognizes the messages produced by the Sun Zephyr parser
+ if (message.startsWith("ParseError at")) {
+ int c = message.indexOf("\nMessage: ");
+ if (c > 0) {
+ message = message.substring(c + 10);
+ }
+ }
+ XPathException err = new XPathException("Error reported by XML parser: " + message);
+ err.setErrorCode(SaxonErrorCode.SXXP0003);
+ err.setLocator(translateLocation(e.getLocation()));
+ throw err;
+ }
+ return currentEvent;
+ }
+
+
+ private int translate(int event) throws XPathException {
+ //System.err.println("EVENT " + event);
+ switch (event) {
+ case XMLStreamConstants.ATTRIBUTE:
+ return ATTRIBUTE;
+ case XMLStreamConstants.CDATA:
+ return TEXT;
+ case XMLStreamConstants.CHARACTERS:
+ if (depth == 0 && reader.isWhiteSpace()) {
+ return next();
+// } else if (reader.isWhiteSpace()) {
+// return next();
+ } else {
+// System.err.println("TEXT[" + new String(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength()) + "]");
+// System.err.println(" ARRAY length " + reader.getTextCharacters().length + "[" + new String(reader.getTextCharacters(), 0, reader.getTextStart() + reader.getTextLength()) + "]");
+// System.err.println(" START: " + reader.getTextStart() + " LENGTH " + reader.getTextLength());
+ return TEXT;
+ }
+ case XMLStreamConstants.COMMENT:
+ return COMMENT;
+ case XMLStreamConstants.DTD:
+ unparsedEntities = (List)reader.getProperty("javax.xml.stream.entities");
+ return next();
+ case XMLStreamConstants.END_DOCUMENT:
+ return END_DOCUMENT;
+ case XMLStreamConstants.END_ELEMENT:
+ depth--;
+ return END_ELEMENT;
+ case XMLStreamConstants.ENTITY_DECLARATION:
+ return next();
+ case XMLStreamConstants.ENTITY_REFERENCE:
+ return next();
+ case XMLStreamConstants.NAMESPACE:
+ return NAMESPACE;
+ case XMLStreamConstants.NOTATION_DECLARATION:
+ return next();
+ case XMLStreamConstants.PROCESSING_INSTRUCTION:
+ return PROCESSING_INSTRUCTION;
+ case XMLStreamConstants.SPACE:
+ if (depth == 0) {
+ return next();
+ } else if (ignoreIgnorable) {
+ // (Brave attempt, but Woodstox doesn't seem to report ignorable whitespace)
+ return next();
+ } else {
+ return TEXT;
+ }
+ case XMLStreamConstants.START_DOCUMENT:
+ return next(); // we supplied the START_DOCUMENT ourselves
+ //return START_DOCUMENT;
+ case XMLStreamConstants.START_ELEMENT:
+ depth++;
+ return START_ELEMENT;
+ default:
+ throw new IllegalStateException("Unknown StAX event " + event);
+
+
+ }
+ }
+
+ /**
+ * Get the event most recently returned by next(), or by other calls that change
+ * the position, for example getStringValue() and skipToMatchingEnd(). This
+ * method does not change the position of the PullProvider.
+ *
+ * @return the current event
+ */
+
+ public int current() {
+ return currentEvent;
+ }
+
+ /**
+ * Get the attributes associated with the current element. This method must
+ * be called only after a START_ELEMENT event has been notified. The contents
+ * of the returned AttributeCollection are guaranteed to remain unchanged
+ * until the next START_ELEMENT event, but may be modified thereafter. The object
+ * should not be modified by the client.
+ * <p/>
+ * <p>Attributes may be read before or after reading the namespaces of an element,
+ * but must not be read after the first child node has been read, or after calling
+ * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>
+ *
+ * @return an AttributeCollection representing the attributes of the element
+ * that has just been notified.
+ */
+
+ public AttributeCollection getAttributes() throws XPathException {
+ return attributes;
+ }
+
+ /**
+ * Get the namespace declarations associated with the current element. This method must
+ * be called only after a START_ELEMENT event has been notified. In the case of a top-level
+ * START_ELEMENT event (that is, an element that either has no parent node, or whose parent
+ * is not included in the sequence being read), the NamespaceDeclarations object returned
+ * will contain a namespace declaration for each namespace that is in-scope for this element
+ * node. In the case of a non-top-level element, the NamespaceDeclarations will contain
+ * a set of namespace declarations and undeclarations, representing the differences between
+ * this element and its parent.
+ * <p/>
+ * <p>It is permissible for this method to return namespace declarations that are redundant.</p>
+ * <p/>
+ * <p>The NamespaceDeclarations object is guaranteed to remain unchanged until the next START_ELEMENT
+ * event, but may then be overwritten. The object should not be modified by the client.</p>
+ * <p/>
+ * <p>Namespaces may be read before or after reading the attributes of an element,
+ * but must not be read after the first child node has been read, or after calling
+ * one of the methods skipToEnd(), getStringValue(), or getTypedValue().</p>*
+ */
+
+ public NamespaceBinding[] getNamespaceDeclarations() throws XPathException {
+ int n = reader.getNamespaceCount();
+ if (n == 0) {
+ return NamespaceBinding.EMPTY_ARRAY;
+ } else {
+ NamespaceBinding[] bindings = new NamespaceBinding[n];
+ for (int i=0; i<n; i++) {
+ String prefix = reader.getNamespacePrefix(i);
+ if (prefix == null) {
+ prefix = "";
+ }
+ String uri = reader.getNamespaceURI(i);
+ if (uri == null) {
+ uri = "";
+ }
+ bindings[i] = new NamespaceBinding(prefix, uri);
+ }
+ return bindings;
+ }
+ }
+
+ /**
+ * Skip the current subtree. This method may be called only immediately after
+ * a START_DOCUMENT or START_ELEMENT event. This call returns the matching
+ * END_DOCUMENT or END_ELEMENT event; the next call on next() will return
+ * the event following the END_DOCUMENT or END_ELEMENT.
+ */
+
+ public int skipToMatchingEnd() throws XPathException {
+ switch (currentEvent) {
+ case START_DOCUMENT:
+ currentEvent = END_DOCUMENT;
+ return currentEvent;
+ case START_ELEMENT:
+ try {
+ int skipDepth = 0;
+ while (reader.hasNext()) {
+ int event = reader.next();
+ if (event == XMLStreamConstants.START_ELEMENT) {
+ skipDepth++;
+ } else if (event == XMLStreamConstants.END_ELEMENT) {
+ if (skipDepth-- == 0) {
+ currentEvent = END_ELEMENT;
+ return currentEvent;
+ }
+ }
+ }
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ throw new IllegalStateException(
+ "Element start has no matching element end");
+ default:
+ throw new IllegalStateException(
+ "Cannot call skipToMatchingEnd() except when at start of element or document");
+
+ }
+ }
+
+ /**
+ * Close the event reader. This indicates that no further events are required.
+ * It is not necessary to close an event reader after {@link #END_OF_INPUT} has
+ * been reported, but it is recommended to close it if reading terminates
+ * prematurely. Once an event reader has been closed, the effect of further
+ * calls on next() is undefined.
+ */
+
+ public void close() {
+ try {
+ reader.close();
+ } catch (XMLStreamException e) {
+ //
+ }
+ }
+
+ /**
+ * Get the nameCode identifying the name of the current node. This method
+ * can be used after the {@link #START_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
+ * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events. With some PullProvider implementations,
+ * including this one, it can also be used after {@link #END_ELEMENT}.
+ * If called at other times, the result is undefined and may result in an IllegalStateException.
+ * If called when the current node is an unnamed namespace node (a node representing the default namespace)
+ * the returned value is -1.
+ *
+ * @return the nameCode. The nameCode can be used to obtain the prefix, local name,
+ * and namespace URI from the name pool.
+ */
+
+ public int getNameCode() {
+ if (currentEvent == START_ELEMENT || currentEvent == END_ELEMENT) {
+ String local = reader.getLocalName();
+ String uri = reader.getNamespaceURI();
+ String prefix = reader.getPrefix();
+ if (prefix==null) {
+ prefix = "";
+ }
+ if (uri == null) {
+ uri = "";
+ }
+ return getNamePool().allocate(prefix, uri, local);
+ //TODO: keep a local cache as in ReceivingContentHandler
+ } else if (currentEvent == PROCESSING_INSTRUCTION) {
+ String local = reader.getPITarget();
+ return getNamePool().allocate("", "", local);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Get the fingerprint of the name of the element. This is similar to the nameCode, except that
+ * it does not contain any information about the prefix: so two elements with the same fingerprint
+ * have the same name, excluding prefix. This method
+ * can be used after the {@link #START_ELEMENT}, {@link #PROCESSING_INSTRUCTION},
+ * {@link #ATTRIBUTE}, or {@link #NAMESPACE} events.
+ * If called at other times, the result is undefined and may result in an IllegalStateException.
+ * If called when the current node is an unnamed namespace node (a node representing the default namespace)
+ * the returned value is -1.
+ *
+ * @return the fingerprint. The fingerprint can be used to obtain the local name
+ * and namespace URI from the name pool.
+ */
+
+ public int getFingerprint() {
+ int nc = getNameCode();
+ if (nc == -1) {
+ return -1;
+ } else {
+ return nc & NamePool.FP_MASK;
+ }
+ }
+
+ /**
+ * Get the string value of the current element, text node, processing-instruction,
+ * or top-level attribute or namespace node, or atomic value.
+ * <p/>
+ * <p>In other situations the result is undefined and may result in an IllegalStateException.</p>
+ * <p/>
+ * <p>If the most recent event was a {@link #START_ELEMENT}, this method causes the content
+ * of the element to be read. The current event on completion of this method will be the
+ * corresponding {@link #END_ELEMENT}. The next call of next() will return the event following
+ * the END_ELEMENT event.</p>
+ *
+ * @return the String Value of the node in question, defined according to the rules in the
+ * XPath data model.
+ */
+
+ public CharSequence getStringValue() throws XPathException {
+ switch (currentEvent) {
+ case TEXT:
+ CharSlice cs = new CharSlice(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
+ return CompressedWhitespace.compress(cs);
+
+ case COMMENT:
+ return new CharSlice(reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
+
+ case PROCESSING_INSTRUCTION:
+ String s = reader.getPIData();
+ // The BEA parser includes the separator space in the value,
+ // which isn't part of the XPath data model
+ return Whitespace.removeLeadingWhitespace(s);
+
+ case START_ELEMENT:
+ FastStringBuffer combinedText = null;
+ try {
+ int depth = 0;
+ while (reader.hasNext()) {
+ int event = reader.next();
+ if (event == XMLStreamConstants.CHARACTERS) {
+ if (combinedText == null) {
+ combinedText = new FastStringBuffer(FastStringBuffer.SMALL);
+ }
+ combinedText.append(
+ reader.getTextCharacters(), reader.getTextStart(), reader.getTextLength());
+ } else if (event == XMLStreamConstants.START_ELEMENT) {
+ depth++;
+ } else if (event == XMLStreamConstants.END_ELEMENT) {
+ if (depth-- == 0) {
+ currentEvent = END_ELEMENT;
+ if (combinedText != null) {
+ return combinedText.condense();
+ } else {
+ return "";
+ }
+ }
+ }
+ }
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ default:
+ throw new IllegalStateException("getStringValue() called when current event is " + currentEvent);
+
+ }
+ }
+
+ /**
+ * Get an atomic value. This call may be used only when the last event reported was
+ * ATOMIC_VALUE. This indicates that the PullProvider is reading a sequence that contains
+ * a free-standing atomic value; it is never used when reading the content of a node.
+ */
+
+ public AtomicValue getAtomicValue() {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Get the location of the current event. The location is returned as an integer.
+ * This is a value that can be passed to the {@link net.sf.saxon.event.LocationProvider}
+ * held by the {@link net.sf.saxon.event.PipelineConfiguration} to get real location information (line number,
+ * system Id, etc). For an event stream representing a real document, the location information
+ * should identify the location in the lexical XML source. For a constructed document, it should
+ * identify the location in the query or stylesheet that caused the node to be created.
+ * A value of zero can be returned if no location information is available.
+ * @return the location ID
+ */
+
+ public int getLocationId() {
+ return 0;
+ }
+
+ /**
+ * Get the type annotation of the current attribute or element node, or atomic value.
+ * The result of this method is undefined unless the most recent event was START_ELEMENT,
+ * ATTRIBUTE, or ATOMIC_VALUE.
+ *
+ * @return the type annotation.
+ */
+
+ public SchemaType getSchemaType() {
+ if (currentEvent == START_ELEMENT) {
+ return Untyped.getInstance();
+ } else if (currentEvent == ATTRIBUTE) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the location of the current event.
+ * For an event stream representing a real document, the location information
+ * should identify the location in the lexical XML source. For a constructed document, it should
+ * identify the location in the query or stylesheet that caused the node to be created.
+ * A value of null can be returned if no location information is available.
+ */
+
+ public SourceLocator getSourceLocator() {
+ return translateLocation(reader.getLocation());
+ }
+
+ /**
+ * Translate a StAX Location object to a Saxon Locator
+ * @param location the StAX Location object
+ * @return a Saxon/SAX SourceLocator object
+ */
+
+ private ExpressionLocation translateLocation(Location location) {
+ ExpressionLocation loc = new ExpressionLocation();
+ if (location != null) {
+ loc.setLineNumber(location.getLineNumber());
+ loc.setColumnNumber(location.getColumnNumber());
+ loc.setSystemId(location.getSystemId());
+ //loc.setPublicId(location.getPublicId());
+ }
+ return loc;
+ }
+
+ /**
+ * Implement the Saxon AttributeCollection interface over the StAX interface.
+ */
+
+ private class StaxAttributes implements AttributeCollection {
+
+ /**
+ * Set the location provider. This must be set if the methods getSystemId() and getLineNumber()
+ * are to be used to get location information for an attribute.
+ * @param provider the location provider
+ */
+
+ public void setLocationProvider(LocationProvider provider) {
+
+ }
+
+ /**
+ * Return the number of attributes in the list.
+ *
+ * @return The number of attributes in the list.
+ */
+
+ public int getLength() {
+ return reader.getAttributeCount();
+ }
+
+ /**
+ * Get the namecode of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The namecode of the attribute
+ */
+
+ public int getNameCode(int index) {
+ String local = reader.getAttributeLocalName(index);
+ String uri = reader.getAttributeNamespace(index);
+ String prefix = reader.getAttributePrefix(index);
+ if (prefix == null) {
+ prefix = "";
+ }
+ if (uri == null) {
+ uri = "";
+ }
+ return getNamePool().allocate(prefix, uri, local);
+ // TODO: the JavaDoc for XMLStreamReader doesn't say what happens if index is out of range.
+ // The interface definition for PullProvider states that null/-1 is returned.
+ }
+
+ /**
+ * Get the node name of an attribute (by position)
+ *
+ * @param index The position of the attribute in the list
+ * @return The node name of the attribute, or null if there is no attribute in that position
+ */
+ public NodeName getNodeName(int index) {
+ String local = reader.getAttributeLocalName(index);
+ String uri = reader.getAttributeNamespace(index);
+ String prefix = reader.getAttributePrefix(index);
+ if (prefix == null) {
+ prefix = "";
+ }
+ if (uri == null) {
+ uri = "";
+ }
+ return new FingerprintedQName(prefix, uri, local);
+ }
+
+ /**
+ * Get the type annotation of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The type annotation
+ */
+
+ public SimpleType getTypeAnnotation(int index) {
+ String type = reader.getAttributeType(index);
+ if ("ID".equals(type)) {
+ return BuiltInAtomicType.ID;
+ }
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+
+ /**
+ * Get the locationID of an attribute (by position)
+ *
+ * @param index The position of the attribute in the list.
+ * @return The location identifier of the attribute. This can be supplied
+ * to a {@link net.sf.saxon.event.LocationProvider} in order to obtain the
+ * actual system identifier and line number of the relevant location
+ */
+
+ public int getLocationId(int index) {
+ return 0;
+ }
+
+ /**
+ * Get the systemId part of the location of an attribute, at a given index.
+ * <p/>
+ * <p>Attribute location information is not available from a SAX parser, so this method
+ * is not useful for getting the location of an attribute in a source document. However,
+ * in a Saxon result document, the location information represents the location in the
+ * stylesheet of the instruction used to generate this attribute, which is useful for
+ * debugging.</p>
+ *
+ * @param index the required attribute
+ * @return the systemId of the location of the attribute
+ */
+
+ public String getSystemId(int index) {
+ return reader.getLocation().getSystemId();
+ }
+
+ /**
+ * Get the line number part of the location of an attribute, at a given index.
+ * <p/>
+ * <p>Attribute location information is not available from a SAX parser, so this method
+ * is not useful for getting the location of an attribute in a source document. However,
+ * in a Saxon result document, the location information represents the location in the
+ * stylesheet of the instruction used to generate this attribute, which is useful for
+ * debugging.</p>
+ *
+ * @param index the required attribute
+ * @return the line number of the location of the attribute
+ */
+
+ public int getLineNumber(int index) {
+ return reader.getLocation().getLineNumber();
+ }
+
+ /**
+ * Get the properties of an attribute (by position)
+ *
+ * @param index The position of the attribute in the list.
+ * @return The properties of the attribute. This is a set
+ * of bit-settings defined in class {@link net.sf.saxon.event.ReceiverOptions}. The
+ * most interesting of these is {{@link net.sf.saxon.event.ReceiverOptions#DEFAULTED_ATTRIBUTE},
+ * which indicates an attribute that was added to an element as a result of schema validation.
+ */
+
+ public int getProperties(int index) {
+ int properties = 0;
+ if (!reader.isAttributeSpecified(index)) {
+ properties |= ReceiverOptions.DEFAULTED_ATTRIBUTE;
+ }
+ if (isIdref(index)) {
+ properties |= (ReceiverOptions.IS_IDREF | ReceiverOptions.ID_IDREF_CHECKED);
+ }
+ return properties;
+ }
+
+ /**
+ * Get the prefix of the name of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The prefix of the attribute name as a string, or null if there
+ * is no attribute at that position. Returns "" for an attribute that
+ * has no prefix.
+ */
+
+ public String getPrefix(int index) {
+ return getNamePool().getPrefix(getNameCode(index));
+ }
+
+ /**
+ * Get the lexical QName of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The lexical QName of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ public String getQName(int index) {
+ return getNamePool().getDisplayName(getNameCode(index));
+ }
+
+ /**
+ * Get the local name of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The local name of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ public String getLocalName(int index) {
+ return reader.getAttributeLocalName(index);
+ }
+
+ /**
+ * Get the namespace URI of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The local name of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ public String getURI(int index) {
+ return reader.getAttributeNamespace(index);
+ }
+
+ /**
+ * Get the index of an attribute (by name).
+ *
+ * @param uri The namespace uri of the attribute.
+ * @param localname The local name of the attribute.
+ * @return The index position of the attribute
+ */
+
+ public int getIndex(String uri, String localname) {
+ for (int i=0; i<getLength(); i++) {
+ if (getLocalName(i).equals(localname) && getURI(i).equals(uri)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Get the index, given the fingerprint
+ */
+
+ public int getIndexByFingerprint(int fingerprint) {
+ return getIndex(getNamePool().getURI(fingerprint), getNamePool().getLocalName(fingerprint));
+ }
+
+ /**
+ * Get the attribute value using its fingerprint
+ */
+
+ public String getValueByFingerprint(int fingerprint) {
+ return getValue(getIndexByFingerprint(fingerprint));
+ }
+
+ /**
+ * Get the value of an attribute (by name).
+ *
+ * @param uri The namespace uri of the attribute.
+ * @param localname The local name of the attribute.
+ * @return The index position of the attribute
+ */
+
+ public String getValue(String uri, String localname) {
+ return reader.getAttributeValue(uri, localname);
+ }
+
+ /**
+ * Get the value of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The attribute value as a string, or null if
+ * there is no attribute at that position.
+ */
+
+ public String getValue(int index) {
+ return reader.getAttributeValue(index);
+ }
+
+ /**
+ * Determine whether a given attribute has the is-ID property set
+ */
+
+ public boolean isId(int index) {
+ return "ID".equals(reader.getAttributeType(index));
+ }
+
+ /**
+ * Determine whether a given attribute has the is-idref property set
+ */
+
+ public boolean isIdref(int index) {
+ String attributeType = reader.getAttributeType(index);
+ return "IDREF".equals(attributeType) || "IDREFS".equals(attributeType);
+ }
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ * <p/>
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup
+ * triggering the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ public String getPublicId() {
+ return reader.getLocation().getPublicId();
+ }
+
+ /**
+ * Return the system identifier for the current document event.
+ * <p/>
+ * <p>The return value is the system identifier of the document
+ * entity or of the external parsed entity in which the markup
+ * triggering the event appears.</p>
+ * <p/>
+ * <p>If the system identifier is a URL, the parser must resolve it
+ * fully before passing it to the application. For example, a file
+ * name must always be provided as a <em>file:...</em> URL, and other
+ * kinds of relative URI are also resolved against their bases.</p>
+ *
+ * @return A string containing the system identifier, or null
+ * if none is available.
+ * @see #getPublicId
+ */
+ public String getSystemId() {
+ Location location = reader.getLocation();
+ return (location==null ? null : location.getSystemId());
+ }
+
+ /**
+ * Return the line number where the current document event ends.
+ * Lines are delimited by line ends, which are defined in
+ * the XML specification.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of diagnostics;
+ * it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.
+ * In some cases, these "line" numbers match what would be displayed
+ * as columns, and in others they may not match the source text
+ * due to internal entity expansion. </p>
+ * <p/>
+ * <p>The return value is an approximation of the line number
+ * in the document entity or external parsed entity where the
+ * markup triggering the event appears.</p>
+ * <p/>
+ * <p>If possible, the SAX driver should provide the line position
+ * of the first character after the text associated with the document
+ * event. The first line is line 1.</p>
+ *
+ * @return The line number, or -1 if none is available.
+ * @see #getColumnNumber
+ */
+ public int getLineNumber() {
+ Location location = reader.getLocation();
+ return (location==null ? -1 : location.getLineNumber());
+ }
+
+ /**
+ * Return the column number where the current document event ends.
+ * This is one-based number of Java <code>char</code> values since
+ * the last line end.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of diagnostics;
+ * it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.
+ * For example, when lines contain combining character sequences, wide
+ * characters, surrogate pairs, or bi-directional text, the value may
+ * not correspond to the column in a text editor's display. </p>
+ * <p/>
+ * <p>The return value is an approximation of the column number
+ * in the document entity or external parsed entity where the
+ * markup triggering the event appears.</p>
+ * <p/>
+ * <p>If possible, the SAX driver should provide the line position
+ * of the first character after the text associated with the document
+ * event. The first column in each line is column 1.</p>
+ *
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return reader.getLocation().getColumnNumber();
+ }
+
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+ /**
+ * Get a list of unparsed entities.
+ *
+ * @return a list of unparsed entities, or null if the information is not available, or
+ * an empty list if there are no unparsed entities. Each item in the list will
+ * be an instance of {@link net.sf.saxon.pull.UnparsedEntity}
+ */
+
+ public List getUnparsedEntities() {
+ if (unparsedEntities == null) {
+ return null;
+ }
+ List list = new ArrayList(unparsedEntities.size());
+ for (int i=0; i<unparsedEntities.size(); i++) {
+ Object ent = unparsedEntities.get(i);
+ String name = null;
+ String systemId = null;
+ String publicId = null;
+ String baseURI = null;
+ if (ent instanceof EntityDeclaration) {
+ // This is what we would expect from the StAX API spec
+ EntityDeclaration ed = (EntityDeclaration)ent;
+ name = ed.getName();
+ systemId = ed.getSystemId();
+ publicId = ed.getPublicId();
+ baseURI = ed.getBaseURI();
+ } else if (ent.getClass().getName().equals("com.ctc.wstx.ent.UnparsedExtEntity")) {
+ // Woodstox 3.0.0 returns this: use introspection to get the data we need
+ try {
+ Class woodstoxClass = ent.getClass();
+ Class[] noArgs = new Class[0];
+ Method method = woodstoxClass.getMethod("getName", noArgs);
+ name = (String)method.invoke(ent, (Object[]) noArgs);
+ method = woodstoxClass.getMethod("getSystemId", noArgs);
+ systemId = (String)method.invoke(ent, (Object[]) noArgs);
+ method = woodstoxClass.getMethod("getPublicId", noArgs);
+ publicId = (String)method.invoke(ent, (Object[]) noArgs);
+ method = woodstoxClass.getMethod("getBaseURI", noArgs);
+ baseURI = (String)method.invoke(ent, (Object[]) noArgs);
+ } catch (NoSuchMethodException e) {
+ //
+ } catch (IllegalAccessException e) {
+ //
+ } catch (InvocationTargetException e) {
+ //
+ }
+ }
+ if (name != null) {
+ if (baseURI != null && systemId != null) {
+ try {
+ systemId = new URI(baseURI).resolve(systemId).toString();
+ } catch (URISyntaxException err) {
+ //
+ }
+ }
+ UnparsedEntity ue = new UnparsedEntity();
+ ue.setName(name);
+ ue.setSystemId(systemId);
+ ue.setPublicId(publicId);
+ ue.setBaseURI(baseURI);
+ list.add(ue);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Error reporting class for StAX parser errors
+ */
+
+ private class StaxErrorReporter implements XMLReporter {
+
+ public void report(String message, String errorType,
+ Object relatedInformation, Location location)
+ throws XMLStreamException {
+ ExpressionLocation loc = translateLocation(location);
+ XPathException err = new XPathException("Error reported by XML parser: " + message + " (" + errorType + ')');
+ err.setLocator(loc);
+ try {
+ pipe.getErrorListener().error(err);
+ } catch (TransformerException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ }
+
+ /**
+ * Simple test program
+ * Usage: java StaxBridge in.xml [out.xml]
+ * @param args command line arguments
+ */
+
+ public static void main(String[] args) throws Exception {
+ for (int i=0; i<1; i++) {
+ long startTime = System.currentTimeMillis();
+ PipelineConfiguration pipe = new Configuration().makePipelineConfiguration();
+ StaxBridge puller = new StaxBridge();
+ puller.setPipelineConfiguration(pipe);
+ File f = new File(args[0]);
+ puller.setInputStream(f.toURI().toString(), new FileInputStream(f));
+ XMLEmitter emitter = new XMLEmitter();
+ emitter.setPipelineConfiguration(pipe);
+ emitter.setOutputProperties(new Properties());
+ if (args.length > 1) {
+ emitter.setOutputStream(new FileOutputStream(args[1]));
+ } else {
+ emitter.setOutputStream(System.out);
+ }
+ NamespaceReducer r = new NamespaceReducer(emitter);
+ new PullPushCopier(puller, r).copy();
+ System.err.println("Elapsed time: " + (System.currentTimeMillis() - startTime) + "ms");
+ }
+ }
+}
+
diff --git a/sf/saxon/pull/UnparsedEntity.java b/sf/saxon/pull/UnparsedEntity.java
new file mode 100644
index 0000000..f144558
--- /dev/null
+++ b/sf/saxon/pull/UnparsedEntity.java
@@ -0,0 +1,102 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.pull;
+
+/**
+ * This class is used to represent unparsed entities in the PullProvider interface
+ */
+
+public class UnparsedEntity {
+
+ private String name;
+ private String systemId;
+ private String publicId;
+ private String baseURI;
+
+ /**
+ * Get the name of the unparsed entity
+ *
+ * @return the name of the unparsed entity
+ */
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name of the unparsed entity
+ *
+ * @param name the name of the unparsed entity
+ */
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the system identifier of the unparsed entity
+ *
+ * @return the system identifier of the unparsed entity
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Set the system identifier of the unparsed entity
+ *
+ * @param systemId the system identifier of the unparsed entity
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the public identifier of the unparsed entity
+ *
+ * @return the public identifier of the unparsed entity
+ */
+
+ public String getPublicId() {
+ return publicId;
+ }
+
+ /**
+ * Set the public identifier of the unparsed entity
+ *
+ * @param publicId the public identifier of the unparsed entity
+ */
+
+ public void setPublicId(String publicId) {
+ this.publicId = publicId;
+ }
+
+ /**
+ * Get the base URI of the unparsed entity
+ *
+ * @return the base URI of the unparsed entity
+ */
+
+ public String getBaseURI() {
+ return baseURI;
+ }
+
+ /**
+ * Set the base URI of the unparsed entity
+ *
+ * @param baseURI the base URI of the unparsed entity
+ */
+
+ public void setBaseURI(String baseURI) {
+ this.baseURI = baseURI;
+ }
+
+
+}
diff --git a/sf/saxon/pull/package.html b/sf/saxon/pull/package.html
new file mode 100644
index 0000000..9928bdb
--- /dev/null
+++ b/sf/saxon/pull/package.html
@@ -0,0 +1,52 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.pull</title>
+</head>
+
+<body>
+
+
+
+<p>This package provides classes that interface Saxon to an XML parser that supplies data in the form
+ of a stream of events. It provides an interface, <code>PullProvider</code>, that is an abstraction
+over the pull parser interfaces provided on Java and .NET, and that can in principle be implemented
+by other data sources to deliver input documents as if they came from a parser.</p>
+
+<p>This package is almost obsolete. Pull-mode processing of queries, when required at all, is generally
+done using the classes in the {@link net.sf.saxon.evpull} package, which deliver a stream of events
+as objects, rather than as simple integers (analogous to the two styles of pull processing interface
+in StAX).</p>
+
+<p>The API, defined in class <code>PullProvider</code>, is loosely modelled on the StAX <code>XMLReader</code>
+API. It is not identical, because it is designed as an intimate and efficient interface that integrates with
+Saxon concepts such as the <code>SequenceIterator</code> and the <code>NamePool</code>. A class
+<code>StaxBridge</code> is available that provides the <code>PullProvider</code> interface on top of a
+StAX pull parser. In the .NET build, a similar class <code>DotNetPullProvider</code> interfaces Saxon to the
+Microsoft <code>XmlTextReader</code>.</p>
+
+<p>A source of data delivered by a <code>PullProvider</code> may be presented either as a <code>PullSource</code>
+or as a <code>StaxSource</code>. Both these are accepted by any Saxon interface that allows a JAXP
+<code>Source</code> object to be supplied.</p>
+
+<p>Additional implementations of <code>PullProvider</code> are available in <code>Saxon-PE</code> and
+<code>Saxon-EE</code>, specifically, implementations that deliver data by walking a Saxon tree structure
+(represented by class <code>NodeInfo</code>), and implementations that allow queries to be evaluated
+in pull mode, with lazy construction of temporary document and element nodes.</p>
+
+<p>Some examples of application code using the pull interface with Saxon are provided in the
+<code>PullExamples.java</code> file in the samples directory.</p>
+
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+10 June 2009</i></p>
+</body>
+</html>
diff --git a/sf/saxon/query/Annotation.java b/sf/saxon/query/Annotation.java
new file mode 100644
index 0000000..ddf20b9
--- /dev/null
+++ b/sf/saxon/query/Annotation.java
@@ -0,0 +1,67 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.value.AtomicValue;
+
+import java.util.List;
+
+/**
+ * This class represents an annotation that appears in a function or variable declarations
+ */
+public abstract class Annotation {
+
+ public static final StructuredQName UPDATING = new StructuredQName("", NamespaceConstant.XQUERY, "updating");
+ public static final StructuredQName SIMPLE = new StructuredQName("", NamespaceConstant.XQUERY, "simple");
+ public static final StructuredQName PRIVATE = new StructuredQName("", NamespaceConstant.XQUERY, "private");
+ public static final StructuredQName PUBLIC = new StructuredQName("", NamespaceConstant.XQUERY, "public");
+
+ /**
+ * Get the name of the annotation (a QName)
+ * @return the annotation name
+ */
+
+ public abstract StructuredQName getAnnotationQName();
+
+ /**
+ * Add a value to the list of annotation parameters
+ * @param value the value to be added. This will always be a string or number,
+ * but Saxon enforces this only at the level of the query parser
+ */
+
+ public abstract void addAnnotationParameter(AtomicValue value);
+
+ /**
+ * Get the list of annotation parameters
+ * @return the list of parameters
+ */
+
+ public abstract List<AtomicValue> getAnnotationParameters();
+
+ /**
+ * Determine whether two annotations are mutually exclusive
+ * @param one the first annotation
+ * @param two the second annotation
+ * @return true if these two annotations cannot appear together on the same function or variable declaration
+ */
+
+ public static boolean mutuallyExclusive(Annotation one, Annotation two) {
+ StructuredQName a = one.getAnnotationQName();
+ StructuredQName b = two.getAnnotationQName();
+ return (
+ (a.equals(PRIVATE) && b.equals(PUBLIC)) ||
+ (a.equals(PUBLIC) && b.equals(PRIVATE)) ||
+ (a.equals(UPDATING) && b.equals(SIMPLE)) ||
+ (a.equals(SIMPLE) && b.equals(UPDATING))
+ );
+
+ }
+}
+
diff --git a/sf/saxon/query/Declaration.java b/sf/saxon/query/Declaration.java
new file mode 100644
index 0000000..67397ca
--- /dev/null
+++ b/sf/saxon/query/Declaration.java
@@ -0,0 +1,18 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * This interface represents the union of global variable declarations and function declarations in XQuery
+ * modules
+ */
+public interface Declaration extends SourceLocator {
+}
+
diff --git a/sf/saxon/query/DynamicQueryContext.java b/sf/saxon/query/DynamicQueryContext.java
new file mode 100644
index 0000000..90cafaf
--- /dev/null
+++ b/sf/saxon/query/DynamicQueryContext.java
@@ -0,0 +1,475 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.instruct.GlobalParameterSet;
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.lib.StandardErrorListener;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.lib.UnparsedTextURIResolver;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.DateTimeValue;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.URIResolver;
+import java.io.PrintStream;
+
+/**
+ * This object represents a dynamic context for query execution. This class is used
+ * by the application writer to set up aspects of the dynamic context; it is not used
+ * operationally (or modified) by the XQuery processor itself, which copies all required
+ * information into its own internal representation.
+ */
+
+public class DynamicQueryContext {
+
+ /*@Nullable*/ private Item contextItem;
+ /*@Nullable*/ private GlobalParameterSet parameters;
+ private Configuration config;
+ private URIResolver uriResolver;
+ private ErrorListener errorListener;
+ /*@Nullable*/ private TraceListener traceListener;
+ private UnparsedTextURIResolver unparsedTextURIResolver;
+ private DateTimeValue currentDateTime;
+ private PrintStream traceFunctionDestination;
+ private int validationMode = Validation.DEFAULT;
+ private boolean applyConversionRules = true;
+
+ /**
+ * Create the dynamic context for a query
+ * @param config the Saxon configuration
+
+ * @since 8.4.
+ */
+
+ public DynamicQueryContext(/*@NotNull*/ Configuration config) {
+ this.config = config;
+ uriResolver = config.getURIResolver();
+ errorListener = config.getErrorListener();
+ try {
+ traceListener = config.makeTraceListener();
+ } catch (XPathException err) {
+ throw new IllegalStateException(err.getMessage());
+ }
+ if (errorListener instanceof StandardErrorListener) {
+ errorListener = ((StandardErrorListener)errorListener).makeAnother(Configuration.XQUERY);
+ ((StandardErrorListener)errorListener).setRecoveryPolicy(Configuration.DO_NOT_RECOVER);
+ }
+ traceFunctionDestination = config.getStandardErrorOutput();
+ }
+
+ /**
+ * Ask whether source documents loaded using the doc(), document(), and collection()
+ * functions, or supplied as a StreamSource or SAXSource to the transform() or addParameter() method
+ * should be subjected to schema validation
+ *
+ * @return the schema validation mode previously set using setSchemaValidationMode(),
+ * or the default mode {@link net.sf.saxon.lib.Validation#DEFAULT} otherwise.
+ */
+
+ public int getSchemaValidationMode() {
+ return validationMode;
+ }
+
+ /**
+ * Say whether source documents loaded using the doc(), document(), and collection()
+ * functions, or supplied as a StreamSource or SAXSource to the transform() or addParameter() method,
+ * should be subjected to schema validation. The default value is taken
+ * from the corresponding property of the Configuration.
+ *
+ * @param validationMode the validation (or construction) mode to be used for source documents.
+ * One of {@link net.sf.saxon.lib.Validation#STRIP}, {@link net.sf.saxon.lib.Validation#PRESERVE}, {@link net.sf.saxon.lib.Validation#STRICT},
+ * {@link net.sf.saxon.lib.Validation#LAX}
+ * @since 9.2
+ */
+
+ public void setSchemaValidationMode(int validationMode) {
+ this.validationMode = validationMode;
+ }
+
+ /**
+ * Say whether the function conversion rules should be applied to supplied
+ * parameter values. For example, this allows an integer to be supplied as the value
+ * for a parameter where the expected type is xs:double. The default is true.
+ * @param convert true if function conversion rules are to be applied to supplied
+ * values; if false, the supplied value must match the required type exactly.
+ * @since 9.3
+ */
+
+ public void setApplyFunctionConversionRulesToExternalVariables(boolean convert) {
+ applyConversionRules = convert;
+ }
+
+ /**
+ * Ask whether the function conversion rules should be applied to supplied
+ * parameter values. For example, this allows an integer to be supplied as the value
+ * for a parameter where the expected type is xs:double. The default is true.
+ * @return true if function conversion rules are to be applied to supplied
+ * values; if false, the supplied value must match the required type exactly.
+ * @since 9.3
+ */
+
+ public boolean isApplyFunctionConversionRulesToExternalVariables() {
+ return applyConversionRules;
+ }
+
+
+ /**
+ * Set the context item for evaluating the expression to be a node. If this method is not called,
+ * the context node will be undefined. The context node is available as the value of
+ * the expression ".".
+ * To obtain a NodeInfo by parsing a source document, see the method
+ * {@link net.sf.saxon.Configuration#buildDocument buildDocument}
+ * in class QueryProcessor.
+ *
+ * @param node The node that is to be the context node for the query
+ * @since 8.4
+ * @deprecated From Saxon 8.7, the method {@link #setContextItem(Item)} is preferred
+ */
+
+ public void setContextNode(NodeInfo node) {
+ if (node==null) {
+ throw new NullPointerException("Context node cannot be null");
+ }
+ setContextItem(node);
+ }
+
+ /**
+ * Set the context item for evaluating the expression. If this method is not called,
+ * the context node will be undefined. The context item is available as the value of
+ * the expression ".",.
+ * To obtain a node by parsing a source document, see the method
+ * {@link net.sf.saxon.Configuration#buildDocument buildDocument}
+ * in class QueryProcessor.
+ * @param item The item that is to be the context item for the query
+ * @throws IllegalArgumentException if the supplied item is a node that was built under the wrong
+ * Saxon Configuration
+ * @throws NullPointerException if the supplied item is null
+ * @since 8.4
+ */
+
+ public void setContextItem(Item item) {
+ if (item==null) {
+ throw new NullPointerException("Context item cannot be null");
+ }
+ if (item instanceof NodeInfo) {
+ if (!((NodeInfo)item).getConfiguration().isCompatible(config)) {
+ throw new IllegalArgumentException(
+ "Supplied node must be built using the same or a compatible Configuration");
+ }
+ }
+ contextItem = item;
+ }
+
+ /**
+ * Get the context item for the query, as set using setContextItem() or setContextNode().
+ * @return the context item if set, or null otherwise.
+ * @since 8.4
+ */
+
+ /*@Nullable*/ public Item getContextItem() {
+ return contextItem;
+ }
+
+ /**
+ * Set a parameter for the query.
+ *
+ * @param expandedName The name of the parameter in "{uri}local-name" format.
+ * It is not an error to supply a value for a parameter that has not been
+ * declared, the parameter will simply be ignored. If the parameter has
+ * been declared in the query (as an external global variable) then it
+ * will be initialized with the value supplied.
+ * @param value The value of the parameter. This can be any valid Java
+ * object. It follows the same conversion rules as a value returned
+ * from a Saxon extension function. An error will occur at query
+ * execution time if the supplied value cannot be converted to the required
+ * type as declared in the query. For precise control of the type of the
+ * value, instantiate one of the classes in the net.sf.saxon.value package,
+ * for example net.sf.saxon.value.DayTimeDuration.
+ * @since 8.4
+ */
+
+ public void setParameter(String expandedName, Object value) {
+ if (parameters==null) {
+ parameters = new GlobalParameterSet();
+ }
+ parameters.put(StructuredQName.fromClarkName(expandedName), value);
+ }
+
+ /**
+ * Set a parameter for the query.
+ *
+ * @param expandedName The name of the parameter in "{uri}local-name" format.
+ * It is not an error to supply a value for a parameter that has not been
+ * declared, the parameter will simply be ignored. If the parameter has
+ * been declared in the query (as an external global variable) then it
+ * will be initialized with the value supplied.
+ * @param value The value of the parameter. This must be an XPath value in its Saxon
+ * representation: no conversion occurs. The value is not checked at this stage
+ * against its required type; such checking will happen later, when the query
+ * is executed.
+ * @since 8.8
+ */
+
+ public void setParameterValue(String expandedName, Sequence value) {
+ if (parameters==null) {
+ parameters = new GlobalParameterSet();
+ }
+ parameters.put(StructuredQName.fromClarkName(expandedName), value);
+ }
+
+
+ /**
+ * Reset the parameters to an empty list.
+ */
+
+ public void clearParameters() {
+ parameters = null;
+ }
+
+ /**
+ * Get the actual value of a parameter to the query.
+ *
+ * @param expandedName the name of the required parameter, in
+ * "{uri}local-name" format
+ * @return the value of the parameter, if it exists, or null otherwise
+ */
+
+ /*@Nullable*/ public Object getParameter(String expandedName) {
+ if (parameters==null) {
+ return null;
+ }
+ return parameters.get(StructuredQName.fromClarkName(expandedName));
+ }
+
+ /**
+ * Set all the supplied parameters as a HashMap. The key is the expanded QName in Clark notation,
+ * the value is the value as supplied to setParameterValue
+ * @param params a structure containing all the parameters
+ */
+
+ public void setParameters(GlobalParameterSet params) {
+ parameters = params;
+ }
+
+ /**
+ * Get all the supplied parameters.
+ * @return a structure containing all the parameters
+ */
+
+ public GlobalParameterSet getParameters() {
+ if (parameters == null) {
+ return new GlobalParameterSet();
+ } else {
+ return parameters;
+ }
+ }
+
+ /**
+ * Set an object that will be used to resolve URIs used in
+ * fn:document() and related functions.
+ *
+ * @param resolver An object that implements the URIResolver interface, or
+ * null.
+ * @since 8.4
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ // System.err.println("Setting uriresolver to " + resolver + " on " + this);
+ uriResolver = resolver;
+ }
+
+ /**
+ * Get the URI resolver.
+ *
+ * @return the user-supplied URI resolver if there is one, or the
+ * system-defined one otherwise
+ * @since 8.4
+ */
+
+ public URIResolver getURIResolver() {
+ return uriResolver;
+ }
+
+ /**
+ * Set an object that will be used to resolve URIs used in
+ * fn:unparsed-text() and related functions.
+ *
+ * @param resolver An object that implements the UnparsedTextURIResolver interface, or
+ * null.
+ * @since 9.5
+ */
+
+ public void setUnparsedTextURIResolver(UnparsedTextURIResolver resolver) {
+ // System.err.println("Setting uriresolver to " + resolver + " on " + this);
+ unparsedTextURIResolver = resolver;
+ }
+
+ /**
+ * Get the URI resolver for unparsed text.
+ *
+ * @return the user-supplied unparsed text URI resolver if there is one, or the
+ * system-defined one otherwise
+ * @since 8.4
+ */
+
+ public UnparsedTextURIResolver getUnparsedTextURIResolver() {
+ return unparsedTextURIResolver;
+ }
+
+
+ /**
+ * Set the error listener. The error listener receives reports of all run-time
+ * errors and can decide how to report them.
+ *
+ * @param listener the ErrorListener to be used
+ * @since 8.4
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ errorListener = listener;
+ }
+
+ /**
+ * Get the error listener.
+ *
+ * @return the ErrorListener in use
+ * @since 8.4
+ */
+
+ public ErrorListener getErrorListener() {
+ return errorListener;
+ }
+
+ /**
+ * Set the trace listener. The trace listener receives reports of all run-time
+ * expression evaluation.
+ *
+ * @param listener the TraceListener to be used
+ * @since 9.0
+ */
+
+ public void setTraceListener(/*@Nullable*/ TraceListener listener) {
+ traceListener = listener;
+ }
+
+ /**
+ * Get the trace listener.
+ *
+ * @return the TraceListener in use, or null if none is in use
+ * @since 9.0
+ */
+
+ /*@Nullable*/ public TraceListener getTraceListener() {
+ return traceListener;
+ }
+
+ /**
+ * Set the destination for output from the fn:trace() function.
+ * By default, the destination is System.err. If a TraceListener is in use,
+ * this is ignored, and the trace() output is sent to the TraceListener.
+ * @param stream the PrintStream to which trace output will be sent. If set to
+ * null, trace output is suppressed entirely. It is the caller's responsibility
+ * to close the stream after use.
+ * @since 9.1
+ */
+
+ public void setTraceFunctionDestination(/*@Nullable*/ PrintStream stream) {
+ traceFunctionDestination = stream;
+ }
+
+ /**
+ * Get the destination for output from the fn:trace() function.
+ * @return the PrintStream to which trace output will be sent. If no explicitly
+ * destination has been set, returns System.err. If the destination has been set
+ * to null to suppress trace output, returns null.
+ * @since 9.1
+ */
+
+ public PrintStream getTraceFunctionDestination() {
+ return traceFunctionDestination;
+ }
+
+
+ /**
+ * Get the date and time set previously using {@link #setCurrentDateTime(net.sf.saxon.value.DateTimeValue)}
+ * or null if none has been set.
+ * @return the current date and time, if it has been set.
+ * @since 8.5
+ */
+
+ public DateTimeValue getCurrentDateTime() {
+ return currentDateTime;
+ }
+
+ /**
+ * Set a value to be used as the current date and time for the query. By default, the "real" current date and
+ * time are used. The main purpose of this method is that it allows repeatable results to be achieved when
+ * testing queries.
+ * <p>This method also has the effect of setting the implicit timezone.</p>
+ * @param dateTime The value to be used as the current date and time. This must include a timezone. The timezone
+ * from this value will also be used as the implicit timezone
+ * @since 8.5
+ * @throws net.sf.saxon.trans.XPathException if the dateTime does not include a timezone
+ */
+
+ public void setCurrentDateTime(/*@NotNull*/ DateTimeValue dateTime) throws XPathException {
+ currentDateTime = dateTime;
+ if (dateTime.getComponent(Component.TIMEZONE) == null) {
+ throw new XPathException("Supplied date/time must include a timezone");
+ }
+ }
+
+ /**
+ * Get the Configuration associated with this dynamic query context
+ * @return the Configuration
+ * @since 8.8
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Apply the settings from this DynamicQueryContext to a Controller
+ * @param controller the Controller whose settings are to be initialized
+ */
+
+ public void initializeController(/*@NotNull*/ Controller controller) {
+ for (StructuredQName name : getParameters().getKeys()) {
+ String clarkName = name.getClarkName();
+ controller.setParameter(clarkName, parameters.get(name));
+ }
+ controller.setURIResolver(getURIResolver());
+ controller.setErrorListener(getErrorListener());
+ controller.addTraceListener(getTraceListener());
+ if (unparsedTextURIResolver != null) {
+ controller.setUnparsedTextURIResolver(unparsedTextURIResolver);
+ }
+ controller.setTraceFunctionDestination(getTraceFunctionDestination());
+ controller.setSchemaValidationMode(getSchemaValidationMode());
+ DateTimeValue currentDateTime = getCurrentDateTime();
+ if (currentDateTime != null) {
+ try {
+ controller.setCurrentDateTime(currentDateTime);
+ } catch (XPathException e) {
+ throw new AssertionError(e); // the value should already have been checked
+ }
+ }
+ controller.getBindery().setApplyFunctionConversionRulesToExternalVariables(applyConversionRules);
+ }
+
+}
+
diff --git a/sf/saxon/query/ImportedFunctionLibrary.java b/sf/saxon/query/ImportedFunctionLibrary.java
new file mode 100644
index 0000000..aecf037
--- /dev/null
+++ b/sf/saxon/query/ImportedFunctionLibrary.java
@@ -0,0 +1,181 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * This implementation of FunctionLibrary contains all the functions imported into a Query Module.
+ * It is implemented as a view of the "global" XQueryFunctionLibrary for the whole query, selecting
+ * only those functions that are in an imported namespace.
+ */
+
+public class ImportedFunctionLibrary implements FunctionLibrary, XQueryFunctionBinder {
+
+ private transient QueryModule importingModule;
+ private XQueryFunctionLibrary baseLibrary;
+ /*@NotNull*/ private HashSet namespaces = new HashSet(5);
+
+ /**
+ * Create an imported function library
+ * @param importingModule the module importing the library
+ * @param baseLibrary the function library of which this is a subset view
+ */
+
+ public ImportedFunctionLibrary(QueryModule importingModule, XQueryFunctionLibrary baseLibrary) {
+ this.importingModule = importingModule;
+ this.baseLibrary = baseLibrary;
+ }
+
+ /**
+ * Add an imported namespace
+ * @param namespace the imported namespace
+ */
+
+ public void addImportedNamespace(String namespace) {
+ namespaces.add(namespace);
+ }
+
+ /**
+ * Bind an extension function, given the URI and local parts of the function name,
+ * and the list of expressions supplied as arguments. This method is called at compile
+ * time.
+ *
+ *
+ *
+ * @param functionName the name of the function to be bound
+ * @param arity
+ * @param staticArgs The expressions supplied statically in arguments to the function call.
+ * The length of this array represents the arity of the function. The intention is
+ * that the static type of the arguments (obtainable via getItemType() and getCardinality()) may
+ * be used as part of the binding algorithm. In some cases it may be possible for the function
+ * to be pre-evaluated at compile time, for example if these expressions are all constant values.
+ * <p/>
+ * The conventions of the XPath language demand that the results of a function depend only on the
+ * values of the expressions supplied as arguments, and not on the form of those expressions. For
+ * example, the result of f(4) is expected to be the same as f(2+2). The actual expression is supplied
+ * here to enable the binding mechanism to select the most efficient possible implementation (including
+ * compile-time pre-evaluation where appropriate).
+ * @param env
+ * @param container
+ * @return An object representing the function to be called, if one is found;
+ * null if no function was found matching the required name and arity.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a function is found with the required name and arity, but
+ * the implementation of the function cannot be loaded or used; or if an error occurs
+ * while searching for the function.
+ */
+
+ /*@Nullable*/ public Expression bind(/*@NotNull*/ StructuredQName functionName, int arity, Expression[] staticArgs, StaticContext env, Container container) throws XPathException {
+ final String uri = functionName.getURI();
+ if (namespaces.contains(uri)) {
+ Expression call = baseLibrary.bind(functionName, arity, staticArgs, env, container);
+ if (call != null) {
+ // Check that the result type and all the argument types are in the static context of the
+ // calling module
+ XQueryFunction def = baseLibrary.getDeclaration(functionName, staticArgs);
+ importingModule.checkImportedFunctionSignature(def);
+ }
+ return call;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the function declaration corresponding to a given function name and arity
+ * @return the XQueryFunction if there is one, or null if not.
+ */
+
+ /*@Nullable*/ public XQueryFunction getDeclaration(/*@NotNull*/ StructuredQName functionName, Expression[] staticArgs) {
+ String uri = functionName.getURI();
+ if (namespaces.contains(uri)) {
+ return baseLibrary.getDeclaration(functionName, staticArgs);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ *
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ /*@NotNull*/ public FunctionLibrary copy() {
+ ImportedFunctionLibrary lib = new ImportedFunctionLibrary(importingModule, baseLibrary);
+ Iterator iter = namespaces.iterator();
+ while (iter.hasNext()) {
+ String ns = (String)iter.next();
+ lib.addImportedNamespace(ns);
+ }
+ return lib;
+ }
+
+ /**
+ * Set the module that imports this function libary
+ * @param importingModule the importing module
+ */
+
+ public void setImportingModule(QueryModule importingModule) {
+ this.importingModule = importingModule;
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ * <p/>
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ * @throws net.sf.saxon.trans.XPathException
+ * in the event of certain errors, for example attempting to get a function
+ * that is private
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext) throws XPathException {
+ if (namespaces.contains(functionName.getURI())) {
+ return baseLibrary.getFunctionItem(functionName, arity, staticContext);
+ } else {
+ return null;
+ }
+ }
+//#endif
+
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ return namespaces.contains(functionName.getURI()) && baseLibrary.isAvailable(functionName, arity);
+ }
+}
+
diff --git a/sf/saxon/query/LanguageFeature.java b/sf/saxon/query/LanguageFeature.java
new file mode 100644
index 0000000..4ec5a9e
--- /dev/null
+++ b/sf/saxon/query/LanguageFeature.java
@@ -0,0 +1,142 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.StructuredQName;
+
+import java.util.*;
+
+/**
+ * LanguageFeature represents a named XQuery 3.0 language feature as used in the require/prohibit feature declaration.
+ */
+
+public class LanguageFeature {
+
+ public static final int ALWAYS = 0; // feature is always supported in Saxon
+ public static final int NEVER = 1; // feature is never supported in Saxon
+ public static final int OPTIONAL = 2; // feature can be enabled or disabled in Saxon
+
+ public static final StructuredQName ALL_OPTIONAL_FEATURES =
+ new StructuredQName("", NamespaceConstant.XQUERY, "all-optional-features");
+
+ public final static LanguageFeature TYPED_DATA = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "typed-data"), null, OPTIONAL);
+ public final static LanguageFeature TYPED_DATA_SCHEMAS = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "typed-data-schemas"), TYPED_DATA, OPTIONAL);
+ public final static LanguageFeature TYPED_DATA_ALL_OPTIONAL_FEATURES = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "typed-data-all-optional-features"), TYPED_DATA, OPTIONAL);
+ public final static LanguageFeature STATIC_TYPING = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "static-typing"), null, NEVER);
+ public final static LanguageFeature STATIC_TYPING_ALL_OPTIONAL_FEATURES = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "static-typing-all-optional-features"), STATIC_TYPING, NEVER);
+ public final static LanguageFeature SERIALIZATION = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "serialization"), null, ALWAYS);
+ public final static LanguageFeature SERIALIZATION_ALL_OPTIONAL_FEATURES = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "serialization-all-optional-features"), SERIALIZATION, ALWAYS);
+ public final static LanguageFeature MODULE = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "module"), null, OPTIONAL);
+ public final static LanguageFeature MODULE_ALL_OPTIONAL_FEATURES = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "module-all-optional-features"), MODULE, OPTIONAL);
+ public final static LanguageFeature HIGHER_ORDER_FUNCTION = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "higher-order-function"), null, ALWAYS);
+ public final static LanguageFeature HIGHER_ORDER_FUNCTION_ALL_OPTIONAL_FEATURES = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "higher-order-function-all-optional-features"), HIGHER_ORDER_FUNCTION, ALWAYS);
+ public final static LanguageFeature ALL_EXTENSIONS = new LanguageFeature(
+ new StructuredQName("", NamespaceConstant.XQUERY, "all-extensions"), null, OPTIONAL);
+
+ // Map containing all features, indexed by name
+ private static Map<StructuredQName, LanguageFeature> features = new HashMap<StructuredQName, LanguageFeature>();
+
+ private static void add(LanguageFeature f) {
+ features.put(f.getName(), f);
+ }
+
+ static {
+ add(TYPED_DATA);
+ add(TYPED_DATA_SCHEMAS);
+ add(TYPED_DATA_ALL_OPTIONAL_FEATURES);
+ add(STATIC_TYPING);
+ add(STATIC_TYPING_ALL_OPTIONAL_FEATURES);
+ add(SERIALIZATION);
+ add(SERIALIZATION_ALL_OPTIONAL_FEATURES);
+ add(MODULE);
+ add(MODULE_ALL_OPTIONAL_FEATURES);
+ add(HIGHER_ORDER_FUNCTION);
+ add(HIGHER_ORDER_FUNCTION_ALL_OPTIONAL_FEATURES);
+ }
+
+ /**
+ * Get the list of all recognized optional features
+ * @return the collection of optional features defined in the XQuery specification
+ */
+
+ public static Collection<LanguageFeature> getAllOptionalFeatures() {
+ return features.values();
+ }
+
+ private StructuredQName name;
+ private LanguageFeature parent;
+ private int availability;
+ private Set<LanguageFeature> children = new HashSet<LanguageFeature>();
+
+ public LanguageFeature(StructuredQName name, LanguageFeature parent, int availability) {
+ this.name = name;
+ if (parent != null) {
+ this.parent = parent;
+ parent.children.add(this);
+ }
+ this.availability = availability;
+ }
+
+ /**
+ * Get the name of the feature
+ * @return the name of the feature
+ */
+ public StructuredQName getName() {
+ return name;
+ }
+
+ /**
+ * Get the parent (container) of the feature
+ * @return the parent (container) of the feature, or null if there is no parent
+ */
+ public LanguageFeature getParent() {
+ return parent;
+ }
+
+ /**
+ * Get the children of this feature
+ * @return the features of which this is the parent
+ */
+ public Set<LanguageFeature> getChildren() {
+ return children;
+ }
+
+ /**
+ * Determine the availability of the feature in Saxon
+ * @return whether the feature is always supported in Saxon, never supported in Saxon, or can be enabled/disabled
+ * in Saxon
+ */
+
+ public int getAvailability() {
+ return availability;
+ }
+
+ /**
+ * Get the feature with a given name
+ * @param name the name of the feature
+ * @return the corresponding feature, or null if the name is unknown
+ */
+
+ public static LanguageFeature getFeature(StructuredQName name) {
+ return features.get(name);
+ }
+
+}
+
diff --git a/sf/saxon/query/QueryLibrary.java b/sf/saxon/query/QueryLibrary.java
new file mode 100644
index 0000000..9a5ba84
--- /dev/null
+++ b/sf/saxon/query/QueryLibrary.java
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A QueryLibrary represents an independently compiled set of query modules that does not include a
+ * main module. Such a library can be compiled once, and then linked to different main modules without
+ * recompilation. The library contains one top-level module (itself a library module) together with the tree
+ * of modules that it imports; it is identified by the module URI of the top-level module.
+ *
+ * <p>This is an abstract class; the concrete implementation is in Saxon-EE.</p>
+ */
+public abstract class QueryLibrary extends QueryModule {
+
+ public QueryLibrary(StaticQueryContext sqc) throws XPathException {
+ super(sqc);
+ }
+
+ /**
+ * Link this library module to a module that imports it
+ * @param importer the importing module (a user of the library)
+ */
+
+ public abstract void link(QueryModule importer) throws XPathException;
+}
+
diff --git a/sf/saxon/query/QueryModule.java b/sf/saxon/query/QueryModule.java
new file mode 100644
index 0000000..6008bf9
--- /dev/null
+++ b/sf/saxon/query/QueryModule.java
@@ -0,0 +1,1876 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.parser.CodeInjector;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.functions.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.TraceCodeInjector;
+import net.sf.saxon.trans.DecimalFormatManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * This class represents a query module, and includes information about the static context of the query module.
+ * The class is intended for internal Saxon use. User settings that affect the static context are made in the
+ * StaticQueryContext object, and those settings are copied to each QueryModule when the query module is compiled.
+ */
+
+public class QueryModule implements StaticContext {
+ private boolean isMainModule;
+ private Configuration config;
+ /*@Nullable*/ private StaticQueryContext userQueryContext;
+ private QueryModule topModule;
+ /*@Nullable*/ private URI locationURI;
+ private String baseURI;
+ /*@Nullable*/ private String moduleNamespace; // null only if isMainModule is false
+ private HashMap<String, String> explicitPrologNamespaces;
+ private Stack<ActiveNamespace> activeNamespaces;
+ private HashMap<StructuredQName, GlobalVariable> variables;
+ // global variables declared in this module
+ private HashMap<StructuredQName, GlobalVariable> libraryVariables;
+ // all global variables defined in library modules
+ // defined only on the top-level module
+ private HashMap<StructuredQName, UndeclaredVariable> undeclaredVariables;
+ /*@Nullable*/ private HashSet<String> importedSchemata; // The schema target namespaces imported into this module
+ private HashMap<String, HashSet<String>> loadedSchemata;
+ // For the top-level module only, all imported schemas for all modules,
+ // Key is the targetNamespace, value is the set of absolutized location URIs
+ /*@Nullable*/ private Executable executable;
+ /*@Nullable*/ private List<QueryModule> importers; // A list of QueryModule objects representing the modules that import this one,
+ // Null for the main module
+ // This is needed *only* to implement the rules banning cyclic imports
+ private FunctionLibraryList functionLibraryList;
+ private XQueryFunctionLibrary globalFunctionLibrary; // used only on a top-level module
+ private int localFunctionLibraryNr;
+ private int importedFunctionLibraryNr;
+ private int unboundFunctionLibraryNr;
+ private Set<String> importedModuleNamespaces;
+ private boolean inheritNamespaces = true;
+ private boolean preserveNamespaces = true;
+ private int constructionMode = Validation.PRESERVE;
+ private String defaultFunctionNamespace;
+ private String defaultElementNamespace;
+ private boolean preserveSpace = false;
+ private boolean defaultEmptyLeast = true;
+ /*@Nullable*/ private String defaultCollationName;
+ private int revalidationMode = Validation.SKIP;
+ private boolean isUpdating = false;
+ private DecimalValue languageVersion = DecimalValue.ONE;
+ private ItemType requiredContextItemType = AnyItemType.getInstance(); // must be the same for all modules
+ /*@Nullable*/ private DecimalFormatManager decimalFormatManager = null; // used only in XQuery 3.0
+ private CodeInjector codeInjector;
+ private Map<LanguageFeature, SourceLocator> featuresUsed = new HashMap<LanguageFeature, SourceLocator>();
+ private Set<LanguageFeature> featuresProhibited = new HashSet<LanguageFeature>();
+ private Set<LanguageFeature> featuresRequired = new HashSet<LanguageFeature>();
+
+ /**
+ * Create a QueryModule for a main module, copying the data that has been set up in a
+ * StaticQueryContext object
+ * @param sqc the StaticQueryContext object from which this module is initialized
+ * @throws XPathException if information supplied is invalid
+ */
+
+ public QueryModule(/*@NotNull*/ StaticQueryContext sqc) throws XPathException {
+ config = sqc.getConfiguration();
+ isMainModule = true;
+ topModule = this;
+ activeNamespaces = new Stack<ActiveNamespace>();
+ baseURI = sqc.getBaseURI();
+ try {
+ locationURI = (baseURI == null ? null : new URI(baseURI));
+ } catch (URISyntaxException err) {
+ throw new XPathException("Invalid location URI: " + baseURI);
+ }
+ executable = null;
+ importers = null;
+ init(sqc);
+ for (Iterator<GlobalVariable> vars = sqc.iterateDeclaredGlobalVariables(); vars.hasNext();) {
+ declareVariable(vars.next());
+ }
+ }
+
+ /**
+ * Create a QueryModule for a library module.
+ * @param config the Saxon configuration
+ * @param importer the module that imported this module. This may be null, in the case where
+ * the library module is being imported into an XSLT stylesheet
+ */
+
+ public QueryModule(Configuration config, /*@Nullable*/ QueryModule importer) {
+ this.config = config;
+ importers = null;
+ if (importer == null) {
+ topModule = this;
+ } else {
+ topModule = importer.topModule;
+ userQueryContext = importer.userQueryContext;
+ importers = new ArrayList<QueryModule>(2);
+ importers.add(importer);
+ }
+ init(userQueryContext);
+ activeNamespaces = new Stack<ActiveNamespace>();
+ executable = null;
+ }
+
+ /**
+ * Initialize data from a user-supplied StaticQueryContext object
+ * @param sqc the user-supplied StaticQueryContext. Null if this is a library module imported
+ * into XSLT.
+ */
+
+ private void init(/*@Nullable*/ StaticQueryContext sqc) {
+ //reset();
+ userQueryContext = sqc;
+ variables = new HashMap<StructuredQName, GlobalVariable>(10);
+ undeclaredVariables = new HashMap<StructuredQName, UndeclaredVariable>(5);
+ if (isTopLevelModule()) {
+ libraryVariables = new HashMap<StructuredQName, GlobalVariable>(10);
+ }
+ importedSchemata = null;
+ importedModuleNamespaces = new HashSet<String>(5);
+ moduleNamespace = null;
+ activeNamespaces = new Stack<ActiveNamespace>();
+
+ explicitPrologNamespaces = new HashMap<String, String>(10);
+ if (sqc != null) {
+ //executable = sqc.getExecutable();
+ inheritNamespaces = sqc.isInheritNamespaces();
+ preserveNamespaces = sqc.isPreserveNamespaces();
+ preserveSpace = sqc.isPreserveBoundarySpace();
+ defaultEmptyLeast = sqc.isEmptyLeast();
+ defaultFunctionNamespace = sqc.getDefaultFunctionNamespace();
+ defaultElementNamespace = sqc.getDefaultElementNamespace();
+ defaultCollationName = sqc.getDefaultCollationName();
+ constructionMode = sqc.getConstructionMode();
+ if (constructionMode == Validation.PRESERVE && !sqc.isSchemaAware()) {
+ // if not schema-aware, generate untyped output by default
+ constructionMode = Validation.STRIP;
+ }
+ requiredContextItemType = sqc.getRequiredContextItemType();
+ isUpdating = sqc.isUpdatingEnabled();
+ languageVersion = sqc.getLanguageVersion();
+ codeInjector = sqc.getCodeInjector();
+ //allowTypedNodes = sqc.isAllowTypedNodes();
+ }
+ initializeFunctionLibraries(sqc);
+ }
+
+ /**
+ * Supporting method to load an imported library module.
+ * Used also by saxon:import-query in XSLT.
+ * <p/>
+ * This method is intended for internal use only.
+ * @param baseURI The base URI and location URI of the module
+ * @param executable The Executable
+ * @param importer The importing query module (used to check for cycles). This is null
+ * when loading a query module from XSLT.
+ * @param query The text of the query, after decoding and normalizing line endings
+ * @param namespaceURI namespace of the query module to be loaded
+ * @param allowCycles True if cycles of module imports (disallowed by the spec) are to be permitted
+ * @return The StaticQueryContext representing the loaded query module
+ * @throws XPathException if an error occurs
+ */
+
+ /*@NotNull*/ public static QueryModule makeQueryModule(
+ String baseURI, /*@NotNull*/ Executable executable, /*@NotNull*/ QueryModule importer,
+ String query, String namespaceURI, boolean allowCycles) throws XPathException {
+ Configuration config = executable.getConfiguration();
+ QueryModule module = new QueryModule(config, importer);
+ try {
+ module.setLocationURI(new URI(baseURI));
+ } catch (URISyntaxException e) {
+ throw new XPathException("Invalid location URI " + baseURI, e);
+ }
+ module.setBaseURI(baseURI);
+ module.setExecutable(executable);
+ module.setModuleNamespace(namespaceURI);
+
+ executable.addQueryLibraryModule(module);
+ QueryParser qp = (QueryParser)config.newExpressionParser(
+ "XQ", importer.isUpdating(), importer.getLanguageVersion());
+ if (importer.getCodeInjector() != null) {
+ qp.setCodeInjector(importer.getCodeInjector());
+ } else if (config.isCompileWithTracing()) {
+ qp.setCodeInjector(new TraceCodeInjector());
+ }
+ qp.setDisableCycleChecks(allowCycles);
+ qp.parseLibraryModule(query, module);
+
+ String namespace = module.getModuleNamespace();
+ if (namespace == null) {
+ XPathException err = new XPathException("Imported module must be a library module");
+ err.setErrorCode("XQST0059");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ if (!namespace.equals(namespaceURI)) {
+ XPathException err = new XPathException("Imported module's namespace does not match requested namespace");
+ err.setErrorCode("XQST0059");
+ err.setIsStaticError(true);
+ throw err;
+ }
+
+ for (Map.Entry<LanguageFeature, SourceLocator> entry : module.getFeaturesUsed().entrySet()) {
+ if (importer.getFeaturesProhibited().contains(entry.getKey())) {
+ XPathException err = new XPathException("Imported module uses feature " +
+ entry.getKey().getName().getLocalPart() +
+ ", which is prohibited in the importing module", entry.getValue());
+ err.setErrorCode("XQST0120");
+ throw err;
+ }
+ importer.getFeaturesUsed().put(entry.getKey(), entry.getValue());
+ }
+
+ return module;
+ }
+
+ /**
+ * Reset function libraries
+ * @param sqc The static query context set up by the caller
+ */
+
+ private void initializeFunctionLibraries(/*@Nullable*/ StaticQueryContext sqc) {
+ Configuration config = getConfiguration();
+ if (isTopLevelModule()) {
+ globalFunctionLibrary = new XQueryFunctionLibrary(config);
+ }
+
+ int functionSet = StandardFunction.CORE;
+ if (isUpdating()) {
+ functionSet |= StandardFunction.XQUPDATE;
+ }
+ if (getLanguageVersion().equals(DecimalValue.THREE)) {
+ functionSet |= StandardFunction.XPATH30;
+ }
+
+ functionLibraryList = new FunctionLibraryList();
+ functionLibraryList.addFunctionLibrary(
+ SystemFunctionLibrary.getSystemFunctionLibrary(functionSet));
+ functionLibraryList.addFunctionLibrary(config.getVendorFunctionLibrary());
+ functionLibraryList.addFunctionLibrary(new ConstructorFunctionLibrary(config));
+
+ localFunctionLibraryNr = functionLibraryList.addFunctionLibrary(
+ new XQueryFunctionLibrary(config));
+
+ importedFunctionLibraryNr = functionLibraryList.addFunctionLibrary(
+ new ImportedFunctionLibrary(this, getTopLevelModule().getGlobalFunctionLibrary()));
+
+ if (sqc != null && sqc.getExtensionFunctionLibrary() != null) {
+ functionLibraryList.addFunctionLibrary(sqc.getExtensionFunctionLibrary());
+ }
+
+ functionLibraryList.addFunctionLibrary(config.getIntegratedFunctionLibrary());
+ config.addExtensionBinders(functionLibraryList);
+
+ unboundFunctionLibraryNr = functionLibraryList.addFunctionLibrary(
+ new UnboundFunctionLibrary());
+ }
+
+ /**
+ * Get the Saxon Configuration
+ * @return the Saxon Configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the NamePool used for compiling expressions
+ * @return the name pool
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ /**
+ * Test whether this is a "top-level" module. This is true for a main module and also for a
+ * module directly imported into an XSLT stylesheet. It may also be true in future for independently-compiled
+ * modules
+ * @return true if this is top-level module
+ */
+
+ public boolean isTopLevelModule() {
+ return this == topModule;
+ }
+
+ /**
+ * Set whether this is a "Main" module, in the sense of the XQuery language specification
+ * @param main true if this is a main module, false if it is a library module
+ */
+
+ public void setIsMainModule(boolean main) {
+ isMainModule = main;
+ }
+
+ /**
+ * Ask whether this is a "main" module, in the sense of the XQuery language specification
+ * @return true if this is a main module, false if it is a library model
+ */
+
+ public boolean isMainModule() {
+ return isMainModule;
+ }
+
+ /**
+ * Check whether this module is allowed to import a module with namespace N. Note that before
+ * calling this we have already handled the exception case where a module imports another in the same
+ * namespace (this is the only case where cycles are allowed, though as a late change to the spec they
+ * are no longer useful, since they cannot depend on each other cyclically)
+ * @param namespace the namespace to be tested
+ * @return true if the import is permitted
+ */
+
+ public boolean mayImportModule(/*@NotNull*/ String namespace) {
+ if (namespace.equals(moduleNamespace)) {
+ return false;
+ }
+ if (importers == null) {
+ return true;
+ }
+ for (QueryModule importer : importers) {
+ if (!importer.mayImportModule(namespace)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Ask whether expressions compiled under this static context are schema-aware.
+ * They must be schema-aware if the expression is to handle typed (validated) nodes
+ *
+ * @return true if expressions are schema-aware
+ */
+ public boolean isSchemaAware() {
+ return executable.isSchemaAware();
+ }
+
+ /**
+ * Set the namespace inheritance mode
+ * @param inherit true if namespaces are inherited, false if not
+ * @since 8.4
+ */
+
+ public void setInheritNamespaces(boolean inherit) {
+ inheritNamespaces = inherit;
+ }
+
+ /**
+ * Get the namespace inheritance mode
+ * @return true if namespaces are inherited, false if not
+ * @since 8.4
+ */
+
+ public boolean isInheritNamespaces() {
+ return inheritNamespaces;
+ }
+
+ /**
+ * Set the namespace copy mode
+ * @param inherit true if namespaces are preserved, false if not
+ */
+
+ public void setPreserveNamespaces(boolean inherit) {
+ preserveNamespaces = inherit;
+ }
+
+ /**
+ * Get the namespace copy mode
+ * @return true if namespaces are preserved, false if not
+ */
+
+ public boolean isPreserveNamespaces() {
+ return preserveNamespaces;
+ }
+
+ /**
+ * Set the construction mode for this module
+ * @param mode one of {@link net.sf.saxon.lib.Validation#STRIP}, {@link net.sf.saxon.lib.Validation#PRESERVE}
+ */
+
+ public void setConstructionMode(int mode) {
+ constructionMode = mode;
+ }
+
+ /**
+ * Get the current construction mode
+ * @return one of {@link net.sf.saxon.lib.Validation#STRIP}, {@link net.sf.saxon.lib.Validation#PRESERVE}
+ */
+
+ public int getConstructionMode() {
+ return constructionMode;
+ }
+
+ /**
+ * Set the policy for preserving boundary space
+ * @param preserve true if boundary space is to be preserved, false if it is to be stripped
+ */
+
+ public void setPreserveBoundarySpace(boolean preserve) {
+ preserveSpace = preserve;
+ }
+
+ /**
+ * Ask whether the policy for boundary space is "preserve" or "strip"
+ * @return true if the policy is to preserve boundary space, false if it is to strip it
+ */
+
+ public boolean isPreserveBoundarySpace() {
+ return preserveSpace;
+ }
+
+ /**
+ * Set the option for where an empty sequence appears in the collation order, if not otherwise
+ * specified in the "order by" clause
+ * @param least true if the empty sequence is considered less than any other value (the default),
+ * false if it is considered greater than any other value
+ */
+
+ public void setEmptyLeast(boolean least) {
+ defaultEmptyLeast = least;
+ }
+
+ /**
+ * Ask what is the option for where an empty sequence appears in the collation order, if not otherwise
+ * specified in the "order by" clause
+ * @return true if the empty sequence is considered less than any other value (the default),
+ * false if it is considered greater than any other value
+ */
+
+ public boolean isEmptyLeast() {
+ return defaultEmptyLeast;
+ }
+
+
+ /**
+ * Get the function library object that holds details of global functions
+ * @return the library of global functions
+ */
+
+ public XQueryFunctionLibrary getGlobalFunctionLibrary() {
+ return globalFunctionLibrary;
+ }
+
+ /**
+ * Get the function library object that holds details of imported functions
+ * @return the library of imported functions
+ */
+
+ /*@NotNull*/ public ImportedFunctionLibrary getImportedFunctionLibrary() {
+ return (ImportedFunctionLibrary)functionLibraryList.get(importedFunctionLibraryNr);
+ }
+
+ /**
+ * Register that this module imports a particular module namespace
+ * <p>This method is intended for internal use.</p>
+ * @param uri the URI of the imported namespace.
+ */
+
+ public void addImportedNamespace(String uri) {
+ if (importedModuleNamespaces == null) {
+ importedModuleNamespaces = new HashSet<String>(5);
+ }
+ importedModuleNamespaces.add(uri);
+ getImportedFunctionLibrary().addImportedNamespace(uri);
+ }
+
+ /**
+ * Ask whether this module directly imports a particular namespace
+ * <p>This method is intended for internal use.</p>
+ * @param uri the URI of the possibly-imported namespace.
+ * @return true if the schema for the namespace has been imported
+ */
+
+ public boolean importsNamespace(String uri) {
+ return importedModuleNamespaces != null &&
+ importedModuleNamespaces.contains(uri);
+ }
+
+ /**
+ * Get the QueryModule for the top-level module. This will normally be a main module,
+ * but in the case of saxon:import-query it will be the library module that is imported into
+ * the stylesheet
+ * @return the StaticQueryContext object associated with the top level module
+ */
+
+ public QueryModule getTopLevelModule() {
+ return topModule;
+ }
+
+ /**
+ * Get the Executable, an object representing the compiled query and its environment.
+ * <p/>
+ * This method is intended for internal use only.
+ * @return the Executable
+ */
+
+ /*@Nullable*/ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Set the executable.
+ * <p/>
+ * This method is intended for internal use only.
+ * @param executable the Executable
+ */
+
+ public void setExecutable(Executable executable) {
+ this.executable = executable;
+// if (!executable.isSchemaAware()) {
+// constructionMode = Validation.STRIP;
+// }
+ }
+
+ /**
+ * Get the StaticQueryContext object containing options set up by the user
+ * @return the user-created StaticQueryContext object
+ */
+
+ /*@Nullable*/ public StaticQueryContext getUserQueryContext() {
+ return userQueryContext;
+ }
+
+ /**
+ * Get the LocationMap, an data structure used to identify the location of compiled expressions within
+ * the query source text.
+ * <p/>
+ * This method is intended for internal use only.
+ * @return the LocationMap
+ */
+
+ public LocationMap getLocationMap() {
+ return executable.getLocationMap();
+ }
+
+ /**
+ * Set the namespace for a library module.
+ * <p/>
+ * This method is for internal use only.
+ * @param uri the module namespace URI of the library module. Null is allowed only
+ * for a main module, not for a library module.
+ */
+
+ public void setModuleNamespace(/*@Nullable*/ String uri) {
+ moduleNamespace = uri;
+ }
+
+ /**
+ * Get the namespace of the current library module.
+ * <p/>
+ * This method is intended primarily for internal use.
+ * @return the module namespace, or null if this is a main module
+ */
+
+ /*@Nullable*/ public String getModuleNamespace() {
+ return moduleNamespace;
+ }
+
+ /**
+ * Set the location URI for a module
+ * @param uri the location URI
+ */
+
+ public void setLocationURI(URI uri) {
+ locationURI = uri;
+ }
+
+ /**
+ * Get the location URI for a module
+ * @return the location URI
+ */
+
+ /*@Nullable*/ public URI getLocationURI() {
+ return locationURI;
+ }
+
+ /**
+ * Get the System ID for a module
+ * @return the location URI
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return (locationURI == null ? null : locationURI.toString());
+ }
+
+ /**
+ * Set the base URI for a module
+ * @param uri the base URI
+ */
+
+ public void setBaseURI(String uri) {
+ baseURI = uri;
+ }
+
+ /**
+ * Get the base URI for a module
+ * @return the base URI
+ */
+
+ public String getBaseURI() {
+ return baseURI;
+ }
+
+
+ /**
+ * Get the stack frame map for global variables.
+ * <p/>
+ * This method is intended for internal use.
+ * @return the stack frame map (a SlotManager) for global variables.
+ */
+
+ public SlotManager getGlobalStackFrameMap() {
+ return executable.getGlobalVariableMap();
+ }
+
+ /**
+ * Declare a global variable. A variable must normally be declared before an expression referring
+ * to it is compiled, but there are exceptions where a set of modules in the same namespace
+ * import each other cyclically. Global variables are normally declared in the Query Prolog, but
+ * they can also be predeclared using the Java API. All global variables are held in the QueryModule
+ * for the main module. The fact that a global variable is present therefore does not mean that it
+ * is visible: there are two additional conditions (a) the module namespace must be imported into the
+ * module where the reference appears, and (b) the declaration must not be in the same module and textually
+ * after the reference.
+ * <p/>
+ * <p>Note that the same VariableDeclaration object cannot be used with more than one query. This is because
+ * the VariableDeclaration is modified internally to hold a list of references to all the places where
+ * the variable is used.</p>
+ * @param var the Variable declaration being declared
+ * @throws XPathException if a static error is detected
+ */
+
+ public void declareVariable(/*@NotNull*/ GlobalVariable var) throws XPathException {
+ StructuredQName key = var.getVariableQName();
+ if (variables.get(key) != null) {
+ GlobalVariable old = variables.get(key);
+ if (old == var || old.getUltimateOriginalVariable() == var.getUltimateOriginalVariable()) {
+ // do nothing
+ } else {
+ String oldloc = " (see line " + old.getLineNumber();
+ String oldSysId = old.getSystemId();
+ if (oldSysId != null &&
+ !oldSysId.equals(var.getSystemId())) {
+ oldloc += " in module ((GlobalVariableDefinition)old).getSystemId()";
+ }
+ oldloc += ")";
+ XPathException err = new XPathException("Duplicate definition of global variable "
+ + var.getVariableQName().getDisplayName()
+ + oldloc);
+ err.setErrorCode("XQST0049");
+ err.setIsStaticError(true);
+ ExpressionLocation loc = new ExpressionLocation();
+ loc.setLineNumber(var.getLineNumber());
+ loc.setSystemId(var.getSystemId());
+ err.setLocator(loc);
+ throw err;
+ }
+ }
+ variables.put(key, var);
+
+ final HashMap<StructuredQName, GlobalVariable> libVars = getTopLevelModule().libraryVariables;
+ GlobalVariable old = libVars.get(key);
+ if (old == null || old == var) {
+ // do nothing
+ } else {
+ XPathException err = new XPathException("Duplicate definition of global variable "
+ + var.getVariableQName().getDisplayName()
+ + " (see line " + old.getLineNumber() + " in module " + old.getSystemId() + ')');
+ err.setErrorCode("XQST0049");
+ err.setIsStaticError(true);
+ ExpressionLocation loc = new ExpressionLocation();
+ loc.setLineNumber(var.getLineNumber());
+ loc.setSystemId(var.getSystemId());
+ err.setLocator(loc);
+ throw err;
+ }
+
+ if (!isMainModule()) {
+ libVars.put(key, var);
+ }
+ }
+
+ /**
+ * Get all global variables declared in or imported into this module
+ * @return an iterator over the global variables
+ */
+
+ public Iterator<GlobalVariable> getGlobalVariables() {
+ return libraryVariables.values().iterator();
+ }
+
+ /**
+ * Fixup all references to global variables.
+ * <p/>
+ * This method is for internal use by the Query Parser only.
+ * @param globalVariableMap a SlotManager that holds details of the assignment of slots to global variables.
+ * @param contextItemVar the variable holding the context item
+ * @return a list containing the global variable definitions.
+ * @throws XPathException if compiling a global variable definition fails
+ */
+
+ public List<GlobalVariable> fixupGlobalVariables(
+ SlotManager globalVariableMap, GlobalVariable contextItemVar) throws XPathException {
+ List<GlobalVariable> varDefinitions = new ArrayList<GlobalVariable>(20);
+ List<Iterator<GlobalVariable>> iters = new ArrayList<Iterator<GlobalVariable>>();
+ iters.add(variables.values().iterator());
+ iters.add(libraryVariables.values().iterator());
+
+
+
+ for (Iterator<GlobalVariable> iter : iters) {
+ while (iter.hasNext()) {
+ GlobalVariable var = iter.next();
+ if (!varDefinitions.contains(var)) {
+ int slot = globalVariableMap.allocateSlotNumber(var.getVariableQName());
+ var.compile(getExecutable(), slot);
+ varDefinitions.add(var);
+ }
+ Expression select = var.getSelectExpression();
+ if (select != null) {
+ boolean changed = false;
+ // eliminate dependencies on the focus
+ if (select instanceof Position) {
+ select = Literal.makeLiteral(IntegerValue.PLUS_ONE);
+ changed = true;
+ } else if (select instanceof Last) {
+ select = Literal.makeLiteral(IntegerValue.PLUS_ONE);
+ changed = true;
+ } else if (select instanceof ContextItemExpression) {
+ select = new VariableReference(contextItemVar);
+ changed = true;
+ } else if (select instanceof AxisExpression) {
+ select = new SimpleStepExpression(new VariableReference(contextItemVar), select);
+ changed = true;
+ } else if(select instanceof ParentNodeExpression || select instanceof RootExpression) {
+ select = new SlashExpression(new VariableReference(contextItemVar), select);
+ changed = true;
+ } else {
+ int dependencies = select.getDependencies();
+ if ((dependencies & (StaticProperty.DEPENDS_ON_CONTEXT_ITEM | StaticProperty.DEPENDS_ON_CONTEXT_DOCUMENT)) != 0) {
+ changed = ExpressionTool.factorOutDot(select, contextItemVar);
+ }
+ if ((dependencies & StaticProperty.DEPENDS_ON_POSITION) != 0) {
+ ExpressionTool.ExpressionSelector selector = new ExpressionTool.ExpressionSelector() {
+ public boolean matches(Expression exp) {
+ return exp instanceof Position;
+ }
+ };
+ changed = ExpressionTool.replaceSelectedSubexpressions(select, selector, Literal.makeLiteral(IntegerValue.PLUS_ONE));
+ }
+ if ((dependencies & StaticProperty.DEPENDS_ON_LAST) != 0) {
+ ExpressionTool.ExpressionSelector selector = new ExpressionTool.ExpressionSelector() {
+ public boolean matches(Expression exp) {
+ return exp instanceof Last;
+ }
+ };
+ changed = ExpressionTool.replaceSelectedSubexpressions(select, selector, Literal.makeLiteral(IntegerValue.PLUS_ONE));
+ }
+ }
+ if (changed) {
+ var.setSelectExpression(select);
+ }
+ }
+ }
+ }
+ return varDefinitions;
+ }
+
+ /**
+ * Look for module cycles. This is a restriction introduced in the PR specification because of
+ * difficulties in defining the formal semantics.
+ * <p/>
+ * <p>[Definition: A module M1 directly depends on another module M2 (different from M1) if a
+ * variable or function declared in M1 depends on a variable or function declared in M2.]
+ * It is a static error [err:XQST0093] to import a module M1 if there exists a sequence
+ * of modules M1 ... Mi ... M1 such that each module directly depends on the next module
+ * in the sequence (informally, if M1 depends on itself through some chain of module dependencies.)</p>
+ * @param referees a Stack containing the chain of module import references leading to this
+ * module
+ * @param lineNumber used for diagnostics
+ * @throws net.sf.saxon.trans.XPathException if cycles are found
+ */
+
+ public void lookForModuleCycles(/*@NotNull*/ Stack<QueryModule> referees, int lineNumber) throws XPathException {
+ if (referees.contains(this)) {
+ int s = referees.indexOf(this);
+ referees.push(this);
+ String message = "Circular dependency between modules. ";
+ for (int i = s; i < referees.size() - 1; i++) {
+ QueryModule next = referees.get(i + 1);
+ if (i == s) {
+ message += "Module " + getSystemId() + " references module " + next.getSystemId();
+ } else {
+ message += ", which references module " + next.getSystemId();
+ }
+ }
+ message += '.';
+ XPathException err = new XPathException(message);
+ err.setErrorCode("XQST0093");
+ err.setIsStaticError(true);
+ ExpressionLocation loc = new ExpressionLocation();
+ loc.setSystemId(getSystemId());
+ loc.setLineNumber(lineNumber);
+ err.setLocator(loc);
+ throw err;
+ } else {
+ referees.push(this);
+ Iterator<GlobalVariable> viter = getModuleVariables();
+ while (viter.hasNext()) {
+ GlobalVariable gv = viter.next();
+ //GlobalVariable gvc = gv.getCompiledVariable(); // will be null if the global variable is unreferenced
+ Expression select = gv.getSelectExpression();
+ if (select != null) {
+ List<Binding> list = new ArrayList<Binding>(10);
+ ExpressionTool.gatherReferencedVariables(select, list);
+ for (Binding b : list) {
+ if (b instanceof GlobalVariable) {
+ String uri = ((GlobalVariable) b).getSystemId();
+ StructuredQName qName = b.getVariableQName();
+ boolean synthetic = NamespaceConstant.SAXON.equals(qName.getURI()) && "gg".equals(qName.getPrefix());
+ if (!synthetic && uri!=null && !uri.equals(getSystemId())) {
+ QueryModule sqc = executable.getQueryModuleWithSystemId(uri, topModule);
+ if (sqc != null) {
+ sqc.lookForModuleCycles(referees, ((GlobalVariable) b).getLineNumber());
+ }
+ }
+ }
+ }
+ List<UserFunction> fList = new ArrayList<UserFunction>(5);
+ ExpressionTool.gatherCalledFunctions(select, fList);
+ for (UserFunction f : fList) {
+ String uri = f.getSystemId();
+ if (uri!=null && !uri.equals(getSystemId())) {
+ QueryModule sqc = executable.getQueryModuleWithSystemId(uri, topModule);
+ if (sqc != null) {
+ sqc.lookForModuleCycles(referees, f.getLineNumber());
+ }
+ }
+ }
+ }
+ }
+ Iterator<XQueryFunction> fiter = getLocalFunctionLibrary().getFunctionDefinitions();
+ while (fiter.hasNext()) {
+ XQueryFunction gf = fiter.next();
+
+ Expression body = gf.getUserFunction().getBody();
+ if (body != null) {
+ List<Binding> vList = new ArrayList<Binding>(10);
+ ExpressionTool.gatherReferencedVariables(body, vList);
+ for (Binding b : vList) {
+ if (b instanceof GlobalVariable) {
+ String uri = ((GlobalVariable) b).getSystemId();
+ StructuredQName qName = b.getVariableQName();
+ boolean synthetic = NamespaceConstant.SAXON.equals(qName.getURI()) && "gg".equals(qName.getPrefix());
+ if (!synthetic && uri!=null && !uri.equals(getSystemId())) {
+ QueryModule sqc = executable.getQueryModuleWithSystemId(uri, topModule);
+ if (sqc != null) {
+ sqc.lookForModuleCycles(referees, ((GlobalVariable) b).getLineNumber());
+ }
+ }
+ }
+ }
+ List<UserFunction> fList = new ArrayList<UserFunction>(10);
+ ExpressionTool.gatherCalledFunctions(body, fList);
+ for (UserFunction f : fList) {
+ String uri = f.getSystemId();
+ if (uri!=null && !uri.equals(getSystemId())) {
+ QueryModule sqc = executable.getQueryModuleWithSystemId(uri, topModule);
+ if (sqc != null) {
+ sqc.lookForModuleCycles(referees, f.getLineNumber());
+ }
+ }
+ }
+ }
+ }
+ referees.pop();
+ }
+ }
+
+ /**
+ * Get global variables declared in this module
+ * @return an Iterator whose items are GlobalVariable objects
+ */
+
+ public Iterator<GlobalVariable> getModuleVariables() {
+ return variables.values().iterator();
+ }
+
+ /**
+ * Check for circular definitions of global variables.
+ * <p>This method is intended for internal use</p>
+ * @param compiledVars a list of {@link GlobalVariable} objects to be checked
+ * @param globalFunctionLibrary the library of global functions
+ * @throws net.sf.saxon.trans.XPathException if a circularity is found
+ */
+
+ public void checkForCircularities(/*@NotNull*/ List<GlobalVariable> compiledVars, /*@NotNull*/ XQueryFunctionLibrary globalFunctionLibrary) throws XPathException {
+ Iterator<GlobalVariable> iter = compiledVars.iterator();
+ Stack<Container> stack = null;
+ while (iter.hasNext()) {
+ if (stack == null) {
+ stack = new Stack<Container>();
+ }
+ GlobalVariable gv = iter.next();
+ if (gv != null) {
+ gv.lookForCycles(stack, globalFunctionLibrary);
+ }
+ }
+ }
+
+
+ /**
+ * Perform type checking on global variables.
+ * <p>This method is intended for internal use</p>
+ * @param compiledVars a list of {@link GlobalVariable} objects to be checked
+ * @throws net.sf.saxon.trans.XPathException if a type error occurs
+ */
+
+ public void typeCheckGlobalVariables(/*@NotNull*/ List<GlobalVariable> compiledVars) throws XPathException {
+ ExpressionVisitor visitor = ExpressionVisitor.make(this, getExecutable());
+ for (GlobalVariable compiledVar : compiledVars) {
+ compiledVar.typeCheck(visitor);
+ }
+ }
+
+ /**
+ * Bind a variable used in a query to the expression in which it is declared.
+ * <p/>
+ * This method is provided for use by the XQuery parser, and it should not be called by the user of
+ * the API, or overridden, unless variables are to be declared using a mechanism other than the
+ * declareVariable method of this class.
+ * @param qName the name of the variable to be bound
+ * @return a VariableReference object representing a reference to a variable on the abstract syntac rtee of
+ * the query.
+ */
+
+ /*@NotNull*/ public Expression bindVariable(/*@NotNull*/ StructuredQName qName) throws XPathException {
+ GlobalVariable var = variables.get(qName);
+ if (var == null) {
+ String uri = qName.getURI();
+ if ((uri.equals("") && isMainModule()) || uri.equals(moduleNamespace) || importsNamespace(uri)) {
+ QueryModule main = getTopLevelModule();
+ var = main.libraryVariables.get(qName);
+ if (var == null) {
+ // If the namespace has been imported there's the possibility that
+ // the variable declaration hasn't yet been read, because of the limited provision
+ // for cyclic imports. In XQuery 3.0 forwards references are more generally allowed.
+ if (getLanguageVersion().equals(DecimalValue.THREE)) {
+ UndeclaredVariable uvar = new UndeclaredVariable();
+ uvar.setVariableQName(qName);
+ VariableReference ref = new VariableReference();
+ uvar.registerReference(ref);
+ undeclaredVariables.put(qName, uvar);
+ return ref;
+ } else {
+ XPathException err = new XPathException("Variable $" + qName.getDisplayName() + " has not been declared");
+ err.setErrorCode("XPST0008");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ } else {
+ if (var.isPrivate()) {
+ XPathException err = new XPathException("Variable $" + qName.getDisplayName() + " is private");
+ err.setErrorCode("XPST0008");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ checkImportedType(var.getRequiredType(), var);
+ }
+ } else {
+ // If the namespace hasn't been imported then we might as well throw the error right away
+ XPathException err = new XPathException("Variable $" + qName.getDisplayName() + " has not been declared");
+ err.setErrorCode("XPST0008");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ } else {
+ if (var.isPrivate() && !var.getSystemId().equals(getSystemId())) {
+ XPathException err = new XPathException("Variable $" + qName.getDisplayName() + " is private");
+ err.setErrorCode("XPST0008");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ }
+ VariableReference vref = new VariableReference();
+ var.registerReference(vref);
+ return vref;
+ }
+
+ /**
+ * Get the function library containing all the in-scope functions available in this static
+ * context (that is, the functions available in this query module).
+ * <p/>
+ * This method is provided for use by advanced applications.
+ * The details of the interface are subject to change.
+ * @return the FunctionLibrary used. For XQuery, this will always be a FunctionLibraryList.
+ * @see net.sf.saxon.functions.FunctionLibraryList
+ */
+
+ public FunctionLibrary getFunctionLibrary() {
+ return functionLibraryList;
+ }
+
+ /**
+ * Get the functions declared locally within this module
+ * @return a FunctionLibrary object containing the function declarations
+ */
+
+ /*@NotNull*/ public XQueryFunctionLibrary getLocalFunctionLibrary() {
+ return (XQueryFunctionLibrary)functionLibraryList.get(localFunctionLibraryNr);
+ }
+
+ /**
+ * Register a user-defined XQuery function.
+ * <p/>
+ * This method is intended for internal use only.
+ * @param function the function being declared
+ * @throws net.sf.saxon.trans.XPathException if an error occurs, for example
+ * a duplicate function name
+ */
+
+ public void declareFunction(/*@NotNull*/ XQueryFunction function) throws XPathException {
+ Configuration config = getConfiguration();
+ if (function.getNumberOfArguments() == 1) {
+ StructuredQName name = function.getFunctionName();
+ int fingerprint = config.getNamePool().getFingerprint(name.getURI(), name.getLocalPart());
+ if (fingerprint != -1) {
+ SchemaType t = config.getSchemaType(fingerprint);
+ if (t != null && t.isAtomicType()) {
+ XPathException err = new XPathException("Function name " + function.getDisplayName() +
+ " clashes with the name of the constructor function for an atomic type");
+ err.setErrorCode("XQST0034");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ }
+ }
+ XQueryFunctionLibrary local = getLocalFunctionLibrary();
+ local.declareFunction(function);
+ //if (!function.isPrivate()) {
+ QueryModule main = getTopLevelModule();
+ main.globalFunctionLibrary.declareFunction(function);
+ //}
+ }
+
+ /**
+ * Bind function calls that could not be bound when first encountered. These
+ * will either be forwards references to functions declared later in the same query module,
+ * or in modules that are being imported recursively, or errors.
+ * <p/>
+ * This method is for internal use only.
+ * @throws net.sf.saxon.trans.XPathException
+ * if a function call refers to a function that has
+ * not been declared
+ */
+
+ public void bindUnboundFunctionCalls() throws XPathException {
+ UnboundFunctionLibrary lib = (UnboundFunctionLibrary)functionLibraryList.get(unboundFunctionLibraryNr);
+ lib.bindUnboundFunctionReferences(functionLibraryList, getConfiguration());
+ }
+
+ /**
+ * Fixup all references to global functions. This method is called
+ * on completion of query parsing. Each XQueryFunction is required to
+ * bind all references to that function to the object representing the run-time
+ * executable code of the function.
+ * <p/>
+ * This method is for internal use only. It is called only on the StaticQueryContext for the main
+ * query body (not for library modules).
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void fixupGlobalFunctions() throws XPathException {
+ globalFunctionLibrary.fixupGlobalFunctions(this);
+ }
+
+ /**
+ * Optimize the body of all global functions.
+ * <p/>
+ * This method is for internal use only. It is called only on the StaticQueryContext for the main
+ * query body (not for library modules).
+ * @throws net.sf.saxon.trans.XPathException if an error occurs during optimization
+ */
+
+ public void optimizeGlobalFunctions() throws XPathException {
+ globalFunctionLibrary.optimizeGlobalFunctions();
+ }
+
+
+ /**
+ * Output "explain" information about each declared function.
+ * <p/>
+ * This method is intended primarily for internal use.
+ * @param out the expression presenter used to display the output
+ */
+
+ public void explainGlobalFunctions(ExpressionPresenter out) {
+ globalFunctionLibrary.explainGlobalFunctions(out);
+ }
+
+ /**
+ * Get the function with a given name and arity. This method is provided so that XQuery functions
+ * can be called directly from a Java application. Note that there is no type checking or conversion
+ * of arguments when this is done: the arguments must be provided in exactly the form that the function
+ * signature declares them.
+ * @param uri the uri of the function name
+ * @param localName the local part of the function name
+ * @param arity the number of arguments.
+ * @return the user-defined function, or null if no function with the given name and arity can be located
+ * @since 8.4
+ */
+
+ public UserFunction getUserDefinedFunction(String uri, String localName, int arity) {
+ return globalFunctionLibrary.getUserDefinedFunction(uri, localName, arity);
+ }
+
+ /**
+ * Bind unbound variables (these are typically variables that reference another module
+ * participating in a same-namespace cycle, since local forwards references are not allowed)
+ * @throws net.sf.saxon.trans.XPathException if an error occurs, for example if the
+ * variable reference cannot be resolved or if the variable is private
+ */
+
+ public void bindUnboundVariables() throws XPathException {
+ for (UndeclaredVariable uv : undeclaredVariables.values()) {
+ StructuredQName qName = uv.getVariableQName();
+ GlobalVariable var = variables.get(qName);
+ if (var == null) {
+ String uri = qName.getURI();
+ if (importsNamespace(uri)) {
+ QueryModule main = getTopLevelModule();
+ var = main.libraryVariables.get(qName);
+ }
+ }
+ if (var == null) {
+ XPathException err = new XPathException("Unresolved reference to variable $" +
+ uv.getVariableQName().getDisplayName());
+ err.setErrorCode("XPST0008");
+ err.setIsStaticError(true);
+ throw err;
+ } else if (var.isPrivate() && !var.getSystemId().equals(getSystemId())) {
+ XPathException err = new XPathException("Cannot reference a private variable in a different module");
+ err.setErrorCode("XPST0008");
+ err.setIsStaticError(true);
+ throw err;
+ } else {
+ checkImportedType(var.getRequiredType(), var);
+ uv.transferReferences(var);
+ }
+ }
+ }
+
+ /**
+ * Add an imported schema to this static context. A query module can reference
+ * types in a schema provided two conditions are satisfied: the schema containing those
+ * types has been loaded into the Configuration, and the target namespace has been imported
+ * by this query module. This method achieves the second of these conditions. It does not
+ * cause the schema to be loaded.
+ * <p/>
+ * @param targetNamespace The target namespace of the schema to be added
+ * @param baseURI The base URI against which the locationURIs are to be absolutized
+ * @param locationURIs a list of strings containing the absolutized URIs of the "location hints" supplied
+ * for this schema
+ * @since 8.4
+ */
+
+ public void addImportedSchema(String targetNamespace, String baseURI, /*@NotNull*/ List<String> locationURIs) {
+ if (importedSchemata == null) {
+ importedSchemata = new HashSet<String>(5);
+ }
+ importedSchemata.add(targetNamespace);
+ HashMap<String, HashSet<String>> loadedSchemata = getTopLevelModule().loadedSchemata;
+ if (loadedSchemata == null) {
+ loadedSchemata = new HashMap<String, HashSet<String>>(5);
+ getTopLevelModule().loadedSchemata = loadedSchemata;
+ }
+ HashSet<String> entries = loadedSchemata.get(targetNamespace);
+ if (entries == null) {
+ entries = new HashSet<String>(locationURIs.size());
+ loadedSchemata.put(targetNamespace, entries);
+ }
+ for (String relative : locationURIs) {
+ try {
+ URI abs = ResolveURI.makeAbsolute(relative, baseURI);
+ entries.add(abs.toString());
+ } catch (URISyntaxException e) {
+ // ignore the URI if it's not valid
+ }
+ }
+ }
+
+ /**
+ * For the top-level module only, get all the schema modules imported anywhere in the query.
+ * @return a Map whose key is the target namespace of a set of schema documents, and whose
+ * value is a Set containing the absolutized location URIs ("hints") of the locations from
+ * which those schema documents were loaded, as strings.
+ */
+
+ public Map<String, HashSet<String>> getAllImportedSchemata() {
+ return loadedSchemata;
+ }
+
+ /**
+ * Get the schema for a given namespace, if it has been imported
+ * @param namespace The namespace of the required schema. Supply "" for
+ * a no-namespace schema.
+ * @return The schema if found, or null if not found.
+ * @since 8.4
+ */
+
+ public boolean isImportedSchema(String namespace) {
+ return importedSchemata != null && importedSchemata.contains(namespace);
+ }
+
+ /**
+ * Get the set of imported schemas
+ * @return a Set, the set of URIs representing the names of imported schemas
+ */
+
+ /*@Nullable*/ public Set<String> getImportedSchemaNamespaces() {
+ if (importedSchemata == null) {
+ return Collections.EMPTY_SET;
+ } else {
+ return importedSchemata;
+ }
+ }
+
+ /**
+ * Specify that typed input nodes are or are not allowed
+ * @param option true if typed input nodes are allowed
+ */
+
+// public void setAllowTypedNodes(boolean option) {
+// if (!isTopLevelModule()) {
+// throw new UnsupportedOperationException("Not a top-level module");
+// }
+// allowTypedNodes = Boolean.valueOf(option);
+// }
+
+ /**
+ * Ask whether all nodes processed by this query will be untyped.
+ * It is then not necessary to generate code that handles typed input nodes.
+ * @return true if the query is to be compiled to permit typed nodes to
+ * appear in the input; false if not; null if no value has been specified.
+ * @since 9.2
+ */
+
+// public Boolean isAllowTypedNodes() {
+// return getTopLevelModule().allowTypedNodes;
+// }
+
+
+ /**
+ * Report a fatal error in the query (via the registered ErrorListener)
+ * @param err the error to be signalled
+ */
+
+ public void reportFatalError(/*@NotNull*/ XPathException err) {
+ if (!err.hasBeenReported()) {
+ try {
+ if (userQueryContext == null) {
+ config.getErrorListener().fatalError(err);
+ } else {
+ userQueryContext.getErrorListener().fatalError(err);
+ }
+ } catch (TransformerException e) {
+ // ignore secondary errors
+ }
+ err.setHasBeenReported(true);
+ }
+ }
+
+ /**
+ * Check that all the types used in the signature of an imported function
+ * are available in the module of the caller of the function
+ * @param fd the declaration of the imported function
+ * @throws XPathException if an error is found
+ */
+
+ public void checkImportedFunctionSignature(/*@NotNull*/ XQueryFunction fd) throws XPathException {
+ checkImportedType(fd.getResultType(), fd);
+ for (int a = 0; a < fd.getNumberOfArguments(); a++) {
+ SequenceType argType = fd.getArgumentTypes()[a];
+ checkImportedType(argType, fd);
+ }
+ }
+
+ /**
+ * Check that a SequenceType used in the definition of an imported variable or function
+ * is available in the importing module
+ * @param importedType the type that is to be checked
+ * @param declaration the containing query or function definition
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is fonnd.
+ */
+
+ public void checkImportedType(/*@NotNull*/ SequenceType importedType, /*@NotNull*/ final Declaration declaration)
+ throws XPathException {
+ ItemType type = importedType.getPrimaryType();
+ type.visitNamedSchemaComponents(new SchemaComponentVisitor() {
+ public void visitSchemaComponent(/*@NotNull*/ SchemaComponent component) throws XPathException {
+ if (component instanceof SchemaDeclaration) {
+ int f = ((SchemaDeclaration)component).getFingerprint();
+ checkSchemaNamespaceImported(f, declaration);
+ } else if (component instanceof SchemaType) {
+ int f = ((SchemaType)component).getFingerprint();
+ checkSchemaNamespaceImported(f, declaration);
+ }
+ }
+ });
+ }
+
+ /**
+ * Construct a dynamic context for early evaluation of constant subexpressions
+ * @return a dynamic context object
+ */
+
+ /*@NotNull*/ public XPathContext makeEarlyEvaluationContext() {
+ return new EarlyEvaluationContext(getConfiguration(), userQueryContext.getCollationMap());
+ }
+
+
+ /**
+ * Get a named collation.
+ * @param name The name of the required collation. Supply null to get the default collation.
+ * @return the collation; or null if the required collation is not found.
+ */
+
+ public StringCollator getCollation(/*@Nullable*/ String name) {
+ if (name == null) {
+ name = defaultCollationName;
+ }
+ return userQueryContext.getCollation(name);
+ }
+
+ /**
+ * Get the name of the default collation.
+ * @return the name of the default collation; or the name of the codepoint collation
+ * if no default collation has been defined
+ */
+
+ /*@Nullable*/ public String getDefaultCollationName() {
+ return defaultCollationName;
+ }
+
+ /**
+ * Set the name of the default collation
+ * @param collation the URI of the default collation
+ * @throws NullPointerException if the supplied collation name is null
+ */
+
+ public void setDefaultCollationName(/*@Nullable*/ String collation) {
+ if (collation == null) {
+ throw new NullPointerException("collationName");
+ }
+ defaultCollationName = collation;
+ }
+
+ public Map<LanguageFeature, SourceLocator> getFeaturesUsed() {
+ return featuresUsed;
+ }
+
+ public Set<LanguageFeature> getFeaturesProhibited() {
+ return featuresProhibited;
+ }
+
+ public Set<LanguageFeature> getFeaturesRequired() {
+ return featuresRequired;
+ }
+
+ public void useFeature(LanguageFeature feature, String construct, SourceLocator locator) throws XPathException {
+ if (featuresProhibited.contains(feature)) {
+ XPathException err = new XPathException("Use of " + construct +
+ " is disallowed because the " + feature.getName().getLocalPart() + " has been prohibited", "XQST0128");
+ err.setLocator(locator);
+ throw err;
+ }
+ featuresUsed.put(feature, locator);
+ if (feature.getParent() != null) {
+ useFeature(feature.getParent(), construct, locator);
+ }
+ }
+
+ /**
+ * Register a namespace that is explicitly declared in the prolog of the query module.
+ * @param prefix The namespace prefix. Must not be null.
+ * @param uri The namespace URI. Must not be null. The value "" (zero-length string) is used
+ * to undeclare a namespace; it is not an error if there is no existing binding for
+ * the namespace prefix.
+ * @throws net.sf.saxon.trans.XPathException if the declaration is invalid
+ */
+
+ public void declarePrologNamespace(/*@Nullable*/ String prefix, /*@Nullable*/ String uri) throws XPathException {
+ if (prefix == null) {
+ throw new NullPointerException("Null prefix supplied to declarePrologNamespace()");
+ }
+ if (uri == null) {
+ throw new NullPointerException("Null namespace URI supplied to declarePrologNamespace()");
+ }
+ if ((prefix.equals("xml") != uri.equals(NamespaceConstant.XML))) {
+ XPathException err = new XPathException("Invalid declaration of the XML namespace");
+ err.setErrorCode("XQST0070");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ if (explicitPrologNamespaces.get(prefix) != null) {
+ XPathException err = new XPathException("Duplicate declaration of namespace prefix \"" + prefix + '"');
+ err.setErrorCode("XQST0033");
+ err.setIsStaticError(true);
+ throw err;
+ } else {
+ explicitPrologNamespaces.put(prefix, uri);
+ }
+ }
+
+ /**
+ * Declare an active namespace, that is, a namespace which as well as affecting the static
+ * context of the query, will also be copied to the result tree when element constructors
+ * are evaluated. When searching for a prefix-URI binding, active namespaces are searched
+ * first, then passive namespaces. Active namespaces are later undeclared (in reverse sequence)
+ * using {@link #undeclareNamespace()}.
+ * <p/>
+ * This method is intended for internal use only.
+ * @param prefix the namespace prefix
+ * @param uri the namespace URI
+ */
+
+ public void declareActiveNamespace(/*@Nullable*/ String prefix, /*@Nullable*/ String uri) {
+ if (prefix == null) {
+ throw new NullPointerException("Null prefix supplied to declareActiveNamespace()");
+ }
+ if (uri == null) {
+ throw new NullPointerException("Null namespace URI supplied to declareActiveNamespace()");
+ }
+
+ ActiveNamespace entry = new ActiveNamespace();
+ entry.prefix = prefix;
+ entry.uri = uri;
+ activeNamespaces.push(entry);
+
+// if (prefix.length() == 0) {
+// defaultElementNamespace = uri;
+// }
+
+ }
+
+ /**
+ * Undeclare the most recently-declared active namespace. This method is called
+ * when a namespace declaration goes out of scope (while processing an element end tag).
+ * It is NOT called when an XML 1.1-style namespace undeclaration is encountered.
+ * <p/>
+ * This method is intended for internal use only.
+ * @see #declareActiveNamespace(String, String)
+ */
+
+ public void undeclareNamespace() {
+ activeNamespaces.pop();
+ }
+
+
+ /**
+ * Get the URI for a prefix.
+ * This method is used by the XQuery parser to resolve namespace prefixes.
+ * <p/>
+ * This method is intended primarily for internal use.
+ * @param prefix The prefix
+ * @return the corresponding namespace URI
+ * @throws net.sf.saxon.trans.XPathException
+ * (with error code XPST0081)
+ * if the prefix has not been declared
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(/*@NotNull*/ String prefix) throws XPathException {
+ String uri = checkURIForPrefix(prefix);
+ if (uri == null) {
+ XPathException err = new XPathException("Prefix " + prefix + " has not been declared");
+ err.setErrorCode("XPST0081");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ return uri;
+ }
+
+ /**
+ * Get the URI for a prefix if there is one, return null if not.
+ * This method is used by the XQuery parser to resolve namespace prefixes.
+ * <p/>
+ * This method is intended primarily for internal use.
+ * @param prefix The prefix. Supply "" to obtain the default namespace for elements and types.
+ * @return the corresponding namespace URI, or null if the prefix has not
+ * been declared. If the prefix is "" and the default namespace is the non-namespace,
+ * return "".
+ */
+
+ /*@Nullable*/ public String checkURIForPrefix(/*@NotNull*/ String prefix) {
+ // Search the active namespaces first, then the passive ones.
+ if (activeNamespaces != null) {
+ for (int i = activeNamespaces.size() - 1; i >= 0; i--) {
+ if ((activeNamespaces.get(i)).prefix.equals(prefix)) {
+ return (activeNamespaces.get(i)).uri;
+ }
+ }
+ }
+ if (prefix.length() == 0) {
+ return defaultElementNamespace;
+ }
+ String uri = explicitPrologNamespaces.get(prefix);
+ if (uri != null) {
+ // A zero-length URI means the prefix was undeclared in the prolog, and we mustn't look elsewhere
+ return (uri.length() == 0 ? null : uri);
+ }
+
+ if (userQueryContext != null) {
+ uri = userQueryContext.getNamespaceForPrefix(prefix);
+ if (uri != null) {
+ return uri;
+ }
+ NamespaceResolver externalResolver = userQueryContext.getExternalNamespaceResolver();
+ if (externalResolver != null) {
+ return externalResolver.getURIForPrefix(prefix, true);
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Get the default XPath namespace for elements and types. Note that this is not necessarily
+ * the default namespace declared in the query prolog; within an expression, it may change in response
+ * to namespace declarations on element constructors.
+ * @return the default namespace, or {@link NamespaceConstant#NULL} for the non-namespace
+ */
+
+ /*@Nullable*/ public String getDefaultElementNamespace() {
+ return checkURIForPrefix("");
+ }
+
+ /**
+ * Set the default element namespace as declared in the query prolog
+ * @param uri the default namespace for elements and types
+ */
+
+ public void setDefaultElementNamespace(String uri) {
+ defaultElementNamespace = uri;
+ }
+
+ /**
+ * Get the default function namespace
+ * @return the default namespace for function names
+ */
+
+ public String getDefaultFunctionNamespace() {
+ return defaultFunctionNamespace;
+ }
+
+ /**
+ * Set the default function namespace
+ * @param uri the default namespace for functions
+ */
+
+ public void setDefaultFunctionNamespace(String uri) {
+ defaultFunctionNamespace = uri;
+ }
+
+ /**
+ * Set the revalidation mode. This is used only if XQuery Updates are in use, in other cases
+ * the value is ignored.
+ * @param mode the revalidation mode. This must be one of {@link Validation#STRICT},
+ * {@link Validation#LAX}, or {@link Validation#SKIP}
+ */
+
+ public void setRevalidationMode(int mode) {
+ if (mode == Validation.STRICT || mode == Validation.LAX || mode == Validation.SKIP) {
+ revalidationMode = mode;
+ } else {
+ throw new IllegalArgumentException("Invalid mode " + mode);
+ }
+ }
+
+ /**
+ * Get the revalidation mode. This is used only if XQuery Updates are in use, in other cases
+ * the value is ignored.
+ * @return the revalidation mode. This will be one of {@link Validation#STRICT},
+ * {@link Validation#LAX}, or {@link Validation#SKIP}
+ */
+
+ public int getRevalidationMode() {
+ return revalidationMode;
+ }
+
+ /**
+ * Get an array containing the namespace codes of all active namespaces.
+ * <p/>
+ * This method is for internal use only.
+ * @return an array of namespace codes. A namespace code is an int that holds a prefix code in the
+ * top half and a uri code in the bottom half.
+ */
+
+ /*@NotNull*/ public NamespaceBinding[] getActiveNamespaceCodes() {
+ // TODO: the result of this method includes undeclarations
+ if (activeNamespaces == null) {
+ return NamespaceBinding.EMPTY_ARRAY;
+ }
+ NamespaceBinding[] nsBindings = new NamespaceBinding[activeNamespaces.size()];
+ int used = 0;
+ HashSet<String> prefixes = new HashSet<String>(10);
+ for (int n = activeNamespaces.size() - 1; n >= 0; n--) {
+ ActiveNamespace an = activeNamespaces.get(n);
+ if (!prefixes.contains(an.prefix)) {
+ prefixes.add(an.prefix);
+ nsBindings[used++] = new NamespaceBinding(an.prefix, an.uri);
+ }
+ }
+ if (used < nsBindings.length) {
+ NamespaceBinding[] nb2 = new NamespaceBinding[used];
+ System.arraycopy(nsBindings, 0, nb2, 0, used);
+ nsBindings = nb2;
+ }
+ return nsBindings;
+ }
+
+ /**
+ * Get a copy of the Namespace Context. This method is used internally
+ * by the query parser when a construct is encountered that needs
+ * to save the namespace context for use at run-time. Note that unlike other implementations of
+ * StaticContext, the state of the QueryModule changes as the query is parsed, with different namespaces
+ * in scope at different times. It's therefore necessary to compute the whole namespace context each time.
+ * <p/>
+ * This method is for internal use only.
+ */
+
+ /*@NotNull*/ public NamespaceResolver getNamespaceResolver() {
+ List<NamespaceBinding> externalNamespaceCodes = null;
+ NamespaceResolver externalResolver = userQueryContext.getExternalNamespaceResolver();
+ if (externalResolver != null) {
+ externalNamespaceCodes = new ArrayList<NamespaceBinding>();
+ Iterator iter = externalResolver.iteratePrefixes();
+ while (iter.hasNext()) {
+ String prefix = (String)iter.next();
+ String uri = externalResolver.getURIForPrefix(prefix, true);
+ NamespaceBinding nscode = new NamespaceBinding(prefix, uri);
+ externalNamespaceCodes.add(nscode);
+ }
+ }
+ HashMap<String, String> userDeclaredNamespaces = userQueryContext.getUserDeclaredNamespaces();
+ List<NamespaceBinding> nsBindings = new ArrayList<NamespaceBinding>();
+
+ for (Map.Entry<String, String> e : userDeclaredNamespaces.entrySet()) {
+ nsBindings.add(new NamespaceBinding(e.getKey(), e.getValue()));
+ }
+ for (Map.Entry<String, String> e : explicitPrologNamespaces.entrySet()) {
+ nsBindings.add(new NamespaceBinding(e.getKey(), e.getValue()));
+ }
+ if (defaultElementNamespace.length() != 0) {
+ nsBindings.add(new NamespaceBinding("", defaultElementNamespace));
+ }
+ nsBindings.addAll(Arrays.asList(getActiveNamespaceCodes()));
+
+ if (externalNamespaceCodes != null) {
+ for (NamespaceBinding externalNamespaceCode : externalNamespaceCodes) {
+ nsBindings.add(externalNamespaceCode);
+ }
+ }
+
+ return new SavedNamespaceContext(nsBindings);
+ }
+
+ /**
+ * Get the required type of the context item. If no type has been explicitly declared for the context
+ * item, an instance of AnyItemType (representing the type item()) is returned.
+ * @return the required type of the context item. Note that this is the same for all modules.
+ * @since 9.3
+ */
+
+ public ItemType getRequiredContextItemType() {
+ return requiredContextItemType;
+ }
+
+ /**
+ * Get a DecimalFormatManager to resolve the names of decimal formats used in calls
+ * to the format-number() function.
+ * @return the decimal format manager for this static context, or null if named decimal
+ * formats are not supported in this environment.
+ */
+
+ /*@Nullable*/ public DecimalFormatManager getDecimalFormatManager() {
+ if (decimalFormatManager == null) {
+ decimalFormatManager = new DecimalFormatManager();
+ }
+ return decimalFormatManager;
+ }
+
+ /**
+ * Issue a compile-time warning. This method is used during XQuery expression compilation to
+ * output warning conditions.
+ * <p/>
+ * This method is intended for internal use only.
+ */
+
+ public void issueWarning(String s, SourceLocator locator) {
+ XPathException err = new XPathException(s);
+ err.setLocator(locator);
+ try {
+ getConfiguration().getErrorListener().warning(err);
+ } catch (TransformerException e) {
+ // ignore any error thrown
+ }
+ }
+
+ /**
+ * Get the line number of the expression within that container.
+ * Used to construct error messages. This method is provided to satisfy the StaticContext interface,
+ * but the value is meaningful only for XPath expressions within a document such as a stylesheet.
+ * @return -1 always
+ */
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+ /**
+ * Determine whether Backwards Compatible Mode is used
+ * @return false; XPath 1.0 compatibility mode is not supported in XQuery
+ * @since 8.4
+ */
+
+ public boolean isInBackwardsCompatibleMode() {
+ return false;
+ }
+
+ /**
+ * Determine whether a built-in type is available in this context. This method caters for differences
+ * between host languages as to which set of types are built in.
+ * @param type the supposedly built-in type. This will always be a type in the
+ * XS or XDT namespace.
+ * @return true if this type can be used in this static context
+ */
+
+ public boolean isAllowedBuiltInType(/*@NotNull*/ BuiltInAtomicType type) {
+ return type.getFingerprint() != StandardNames.XS_DATE_TIME_STAMP ||
+ config.getXsdVersion() == Configuration.XSD11;
+ }
+
+ /**
+ * Set whether the query is allowed to be updating
+ * @param updating true if the query may use the XQuery Update facility (requires Saxon-EE)
+ * @since 9.1
+ */
+
+ public void setUpdating(boolean updating) {
+ isUpdating = updating;
+ }
+
+ /**
+ * Ask whether the query is allowed to be updating
+ * @return true if the query is allowed to use the XQuery Update facility
+ * @since 9.1
+ */
+
+ public boolean isUpdating() {
+ return isUpdating;
+ }
+
+ /**
+ * Get the language version supported
+ * @return the language version (currently "1.0" or "3.0": "1.1" is sometimes accepted in place of "3.0")
+ * @since 9.2; changed in 9.3 to return a DecimalValue instead of a String
+ */
+
+ public DecimalValue getLanguageVersion() {
+ return languageVersion;
+ }
+
+ /**
+ * Get the XPath language level supported
+ * @return 3.0 if XQuery 3.0 is enabled, or 2.0 otherwise
+ */
+
+ public DecimalValue getXPathLanguageLevel() {
+ return DecimalValue.THREE.equals(languageVersion) ? DecimalValue.THREE : DecimalValue.TWO;
+ }
+
+ /**
+ * Get the CodeInjector if one is in use
+ * @return the code injector if there is one
+ */
+
+ public CodeInjector getCodeInjector() {
+ return codeInjector;
+ }
+
+
+ /**
+ * Check that the namespace of a given name is the namespace of an imported schema
+ * @param fingerprint the fingerprint of the "given name"
+ * @param declaration the declaration of the variable or function that has this given name
+ * @throws net.sf.saxon.trans.XPathException
+ * (error XQST0036) if the namespace is not present in a schema
+ * imported by the importing query module
+ */
+
+ private void checkSchemaNamespaceImported(int fingerprint, /*@NotNull*/ Declaration declaration)
+ throws XPathException {
+ String uri = getNamePool().getURI(fingerprint);
+ if (!uri.equals(NamespaceConstant.SCHEMA) && !uri.equals(NamespaceConstant.ANONYMOUS) &&
+ !uri.equals(NamespaceConstant.JAVA_TYPE) && !isImportedSchema(uri)) {
+ String msg = "Schema component " + getNamePool().getDisplayName(fingerprint) + " used in ";
+ if (declaration instanceof GlobalVariable) {
+ msg += "declaration of imported variable " +
+ ((GlobalVariable)declaration).getVariableQName().getDisplayName();
+ } else {
+ msg += "signature of imported function " +
+ ((XQueryFunction)declaration).getDisplayName();
+ }
+ msg += " is not declared in any schema imported by ";
+ String module = getModuleNamespace();
+ if (module == null) {
+ msg += "the main query module";
+ } else {
+ msg += "query module " + module;
+ }
+ XPathException err = new XPathException(msg);
+ err.setErrorCode("XQST0036");
+ err.setIsStaticError(true);
+ err.setLocator(declaration);
+ throw err;
+ }
+ }
+
+ /**
+ * Inner class containing information about an active namespace entry
+ */
+
+ private static class ActiveNamespace {
+ /*@Nullable*/ public String prefix;
+ /*@Nullable*/ public String uri;
+ }
+}
+
diff --git a/sf/saxon/query/QueryParser.java b/sf/saxon/query/QueryParser.java
new file mode 100644
index 0000000..e015647
--- /dev/null
+++ b/sf/saxon/query/QueryParser.java
@@ -0,0 +1,4522 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.flwor.*;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.expr.sort.SortKeyDefinition;
+import net.sf.saxon.functions.*;
+import net.sf.saxon.lib.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.style.AttributeValueTemplate;
+import net.sf.saxon.sxpath.DedicatedStaticContext;
+import net.sf.saxon.sxpath.SimpleContainer;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.NamespaceResolverWithDefault;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.z.IntHashSet;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.stream.StreamSource;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * This class defines extensions to the XPath parser to handle the additional
+ * syntax supported in XQuery
+ */
+public class QueryParser extends ExpressionParser {
+
+ public final static String XQUERY10 = "1.0";
+ public final static String XQUERY30 = "3.0";
+
+ private boolean memoFunction = false;
+ private boolean disableCycleChecks = false;
+ /*@Nullable*/ protected String queryVersion = null;
+ private int errorCount = 0;
+ /*@Nullable*/ private XPathException firstError = null;
+
+ /*@NotNull*/ protected Executable executable;
+
+ private boolean foundCopyNamespaces = false;
+ private boolean foundBoundarySpaceDeclaration = false;
+ private boolean foundOrderingDeclaration = false;
+ private boolean foundEmptyOrderingDeclaration = false;
+ private boolean foundDefaultCollation = false;
+ private boolean foundConstructionDeclaration = false;
+ private boolean foundDefaultFunctionNamespace = false;
+ private boolean foundDefaultElementNamespace = false;
+ private boolean foundBaseURIDeclaration = false;
+ private boolean preambleProcessed = false;
+
+ /*@NotNull*/ public Set importedModules = new HashSet(5);
+ /*@NotNull*/ List<String> namespacesToBeSealed = new ArrayList<String>(10);
+ /*@NotNull*/ List<Import> schemaImports = new ArrayList<Import>(5);
+ /*@NotNull*/ List<Import> moduleImports = new ArrayList<Import>(5);
+
+
+ /*@Nullable*/ private Expression defaultValue = null;
+
+ static Set<String> reservedNames = new HashSet<String>();
+ static {
+ reservedNames.add("attribute");
+ reservedNames.add("comment");
+ reservedNames.add("document-node");
+ reservedNames.add("element");
+ reservedNames.add("empty-sequence");
+ reservedNames.add("function");
+ reservedNames.add("if");
+ reservedNames.add("item");
+ reservedNames.add("namespace-node");
+ reservedNames.add("node");
+ reservedNames.add("processing-instruction");
+ reservedNames.add("schema-attribute");
+ reservedNames.add("schema-element");
+ reservedNames.add("switch");
+ reservedNames.add("text");
+ reservedNames.add("typeswitch");
+ }
+
+
+ /**
+ * Constructor for internal use: this class should be instantiated via the QueryModule
+ */
+
+ public QueryParser() {
+ queryVersion = XQUERY10;
+ }
+
+ /**
+ * Create a new parser of the same kind
+ *
+ * @return a new parser of the same kind as this one
+ */
+
+ public QueryParser newParser() {
+ QueryParser qp = new QueryParser();
+ qp.setLanguage(language, languageVersion);
+ return qp;
+ }
+
+ /**
+ * Create an XQueryExpression
+ *
+ * @param query the source text of the query
+ * @param mainModule the static context of the query
+ * @param config the Saxon configuration
+ * @return the compiled XQuery expression
+ * @throws XPathException if the expression contains static errors
+ */
+
+ /*@NotNull*/
+ public XQueryExpression makeXQueryExpression(
+ /*@NotNull*/ String query,
+ /*@NotNull*/ QueryModule mainModule,
+ /*@NotNull*/ Configuration config) throws XPathException {
+ try {
+ setLanguage(XQUERY, mainModule.getLanguageVersion());
+ if (config.getXMLVersion() == Configuration.XML10) {
+ query = normalizeLineEndings10(query);
+ } else {
+ query = normalizeLineEndings11(query);
+ }
+ Executable exec = mainModule.getExecutable();
+ if (exec == null) {
+ exec = new Executable(config);
+ exec.setHostLanguage(Configuration.XQUERY, mainModule.getLanguageVersion().equals(DecimalValue.THREE));
+ setExecutable(exec);
+ //mainModule.setExecutable(exec);
+ }
+
+ setDefaultContainer(new SimpleContainer(exec));
+
+ Properties outputProps = new Properties(config.getDefaultSerializationProperties());
+ if (outputProps.getProperty(OutputKeys.METHOD) == null) {
+ outputProps.setProperty(OutputKeys.METHOD, "xml");
+ }
+ exec.setDefaultOutputProperties(outputProps);
+
+ //exec.setLocationMap(new LocationMap());
+ FunctionLibraryList libList = new FunctionLibraryList();
+ libList.addFunctionLibrary(new ExecutableFunctionLibrary(config));
+ exec.setFunctionLibrary(libList);
+ // this will be changed later
+ setExecutable(exec);
+
+ setCodeInjector(mainModule.getCodeInjector());
+
+ Expression exp = parseQuery(query, 0, Token.EOF, mainModule);
+ exec.fixupQueryModules(mainModule, !disableCycleChecks);
+
+ // Check for cyclic dependencies among the modules
+
+// if (!disableCycleChecks) {
+// Iterator miter = exec.getQueryLibraryModules();
+// while (miter.hasNext()) {
+// QueryModule module = (QueryModule)miter.next();
+// module.lookForModuleCycles(new Stack(), 1);
+// }
+// }
+
+ // Make the XQueryexpression object
+
+ XQueryExpression queryExp = new XQueryExpression(exp, exec, mainModule, config);
+
+ // Make the function library that's available at run-time (e.g. for saxon:evaluate() and function-lookup()).
+ // This includes all user-defined functions regardless of which module they are in
+
+ FunctionLibrary userlib = exec.getFunctionLibrary();
+ FunctionLibraryList lib = new FunctionLibraryList();
+ lib.addFunctionLibrary(
+ SystemFunctionLibrary.getSystemFunctionLibrary(getPermittedFunctions()));
+ lib.addFunctionLibrary(config.getVendorFunctionLibrary());
+ lib.addFunctionLibrary(new ConstructorFunctionLibrary(config));
+ lib.addFunctionLibrary(config.getIntegratedFunctionLibrary());
+ config.addExtensionBinders(lib);
+ lib.addFunctionLibrary(userlib);
+ exec.setFunctionLibrary(lib);
+
+ return queryExp;
+ } catch (XPathException e) {
+ if (!e.hasBeenReported()) {
+ reportError(e);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Get the permitted set of standard functions in this environment
+ *
+ * @return a code indicating which system library functions are supported in this version of the language
+ */
+
+ public int getPermittedFunctions() {
+ return StandardFunction.CORE;
+ }
+
+ /**
+ * Normalize line endings in the source query, according to the XML 1.1 rules.
+ *
+ * @param in the input query
+ * @return the query with line endings normalized
+ */
+
+ private static String normalizeLineEndings11(/*@NotNull*/ String in) {
+ if (in.indexOf((char) 0xd) < 0 && in.indexOf((char) 0x85) < 0 && in.indexOf((char) 0x2028) < 0) {
+ return in;
+ }
+ FastStringBuffer sb = new FastStringBuffer(in.length());
+ for (int i = 0; i < in.length(); i++) {
+ char ch = in.charAt(i);
+ switch (ch) {
+ case 0x85:
+ case 0x2028:
+ sb.append((char) 0xa);
+ break;
+ case 0xd:
+ if (i < in.length() - 1 && (in.charAt(i + 1) == (char) 0xa || in.charAt(i + 1) == (char) 0x85)) {
+ sb.append((char) 0xa);
+ i++;
+ } else {
+ sb.append((char) 0xa);
+ }
+ break;
+ default:
+ sb.append(ch);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Normalize line endings in the source query, according to the XML 1.0 rules.
+ *
+ * @param in the input query
+ * @return the query text with line endings normalized
+ */
+
+ private static String normalizeLineEndings10(/*@NotNull*/ String in) {
+ if (in.indexOf((char) 0xd) < 0) {
+ return in;
+ }
+ FastStringBuffer sb = new FastStringBuffer(in.length());
+ for (int i = 0; i < in.length(); i++) {
+ char ch = in.charAt(i);
+ switch (ch) {
+ case 0xd:
+ if (i < in.length() - 1 && in.charAt(i + 1) == (char) 0xa) {
+ sb.append((char) 0xa);
+ i++;
+ } else {
+ sb.append((char) 0xa);
+ }
+ break;
+ default:
+ sb.append(ch);
+ }
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Get the executable containing this expression.
+ *
+ * @return the executable
+ */
+
+ /*@NotNull*/
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Set the executable used for this query expression
+ *
+ * @param exec the executable
+ */
+
+ public void setExecutable(/*@NotNull*/ Executable exec) {
+ executable = exec;
+ }
+
+ /**
+ * Disable checks for certain kinds of cycle. This is equivalent to
+ * <p><code>declare option saxon:allow-cycles "true"</code></p>
+ *
+ * @param disable true if checks for import cycles are to be suppressed, that is,
+ * if cycles should be allowed
+ */
+
+ public void setDisableCycleChecks(boolean disable) {
+ disableCycleChecks = disable;
+ }
+
+ /**
+ * Callback to tailor the tokenizer
+ */
+
+ protected void customizeTokenizer(Tokenizer t) {
+ t.isXQuery = true;
+ }
+
+
+ /**
+ * Parse a top-level Query.
+ * Prolog? Expression
+ *
+ * @param queryString The text of the query
+ * @param start Offset of the start of the query
+ * @param terminator Token expected to follow the query (usually Token.EOF)
+ * @param env The static context
+ * @return the Expression object that results from parsing
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression contains a syntax error
+ */
+
+ /*@NotNull*/
+ private Expression parseQuery(String queryString,
+ int start,
+ int terminator,
+ /*@NotNull*/ QueryModule env) throws XPathException {
+ this.env = env;
+ nameChecker = env.getConfiguration().getNameChecker();
+ if (nameChecker == null) {
+ throw new IllegalStateException("nameChecker");
+ }
+ if (defaultContainer == null) {
+ defaultContainer = new TemporaryContainer(env.getLocationMap(), 1);
+ ((TemporaryContainer) defaultContainer).setExecutable(getExecutable());
+ }
+ language = XQUERY;
+ t = new Tokenizer();
+ t.isXQuery = true;
+ try {
+ t.tokenize(queryString, start, -1, 1);
+ } catch (XPathException err) {
+ grumble(err.getMessage());
+ }
+ parseVersionDeclaration();
+ allowXPath30Syntax = (XQUERY30.equals(queryVersion));
+ parseProlog();
+ processPreamble();
+
+ Expression exp = parseExpression();
+
+ // Diagnostic code - show the expression before any optimizations
+// ExpressionPresenter ep = ExpressionPresenter.make(env.getConfiguration());
+// exp.explain(ep);
+// ep.close();
+ // End of diagnostic code
+
+ if (t.currentToken != terminator) {
+ grumble("Unexpected token " + currentTokenDisplay() + " beyond end of query");
+ }
+ setLocation(exp);
+ if (errorCount == 0) {
+ return exp;
+ } else {
+ XPathException err = new XPathException("One or more static errors were reported during query analysis");
+ err.setHasBeenReported(true);
+ err.setErrorCodeQName(firstError.getErrorCodeQName()); // largely for the XQTS test driver
+ throw err;
+ }
+ }
+
+ /**
+ * Parse a library module.
+ * Prolog? Expression
+ *
+ * @param queryString The text of the library module.
+ * @param env The static context. The result of parsing
+ * a library module is that the static context is populated with a set of function
+ * declarations and variable declarations. Each library module must have its own
+ * static context objext.
+ * @throws XPathException if the expression contains a syntax error
+ */
+
+ public final void parseLibraryModule(String queryString, /*@NotNull*/ QueryModule env)
+ throws XPathException {
+ this.env = env;
+ nameChecker = env.getConfiguration().getNameChecker();
+ Executable exec = env.getExecutable();
+ if (exec == null) {
+ throw new IllegalStateException("Query library module has no associated Executable");
+ }
+ executable = exec;
+ defaultContainer = new SimpleContainer(executable);
+ t = new Tokenizer();
+ t.isXQuery = true;
+ try {
+ t.tokenize(queryString, 0, -1, 1);
+ } catch (XPathException err) {
+ grumble(err.getMessage());
+ }
+ parseVersionDeclaration();
+ //t.setAllowExpandedQNameSyntax("3.0".equals(queryVersion));
+ parseModuleDeclaration();
+ parseProlog();
+ processPreamble();
+ if (t.currentToken != Token.EOF) {
+ grumble("Unrecognized content found after the variable and function declarations in a library module");
+ }
+ if (errorCount != 0) {
+ XPathException err = new XPathException("Static errors were reported in the imported library module");
+ err.setErrorCodeQName(firstError.getErrorCodeQName());
+ throw err;
+ }
+ }
+
+ /**
+ * Report a static error
+ *
+ * @param message the error message
+ * @param offset the location in the source query
+ * @throws XPathException always thrown: an exception containing the
+ * supplied message
+ */
+
+ protected void grumble(String message, /*@Nullable*/ StructuredQName errorCode, int offset) throws XPathException {
+ if (offset < 0) {
+ offset = t.currentTokenStartOffset;
+ }
+ if (errorCode == null) {
+ errorCode = new StructuredQName("err", NamespaceConstant.ERR, "XPST0003");
+ }
+ String s = t.recentText(-1);
+ ExpressionLocation loc = makeLocator(offset);
+ String prefix = getLanguage() +
+ ("XPST0003".equals(errorCode.getLocalPart()) ? " syntax error " : " static error ") +
+ (s.startsWith("...") ? "near" : "in") +
+ " #" + s + "#:\n ";
+ XPathException exception = new XPathException(message);
+ exception.setAdditionalLocationText(prefix);
+ exception.setErrorCodeQName(errorCode);
+ exception.setLocator(loc);
+ reportError(exception);
+ }
+
+
+ private void reportError(/*@NotNull*/ XPathException exception) throws XPathException {
+ errorCount++;
+ if (firstError == null) {
+ firstError = exception;
+ }
+ ((QueryModule) env).reportFatalError(exception);
+ throw exception;
+ }
+
+ /**
+ * Make a Locator object representing the current parsing location
+ *
+ * @return a Locator
+ */
+ /*@NotNull*/
+ private ExpressionLocation makeLocator() {
+ int line = t.getLineNumber();
+ int column = t.getColumnNumber();
+
+ ExpressionLocation loc = new ExpressionLocation();
+ loc.setSystemId(env.getSystemId());
+ loc.setLineNumber(line);
+ loc.setColumnNumber(column);
+ return loc;
+ }
+
+ /**
+ * Make a Locator object representing a specific parsing location
+ *
+ * @param offset the (coded) location in the input expression
+ * @return a Locator
+ */
+ /*@NotNull*/
+ private ExpressionLocation makeLocator(int offset) {
+ int line = t.getLineNumber(offset);
+ int column = t.getColumnNumber(offset);
+
+ ExpressionLocation loc = new ExpressionLocation();
+ loc.setSystemId(env.getSystemId());
+ loc.setLineNumber(line);
+ loc.setColumnNumber(column);
+ return loc;
+ }
+
+ private static Pattern encNamePattern = Pattern.compile("^[A-Za-z]([A-Za-z0-9._\\x2D])*$");
+
+ /**
+ * Parse the version declaration if present.
+ *
+ * @throws XPathException in the event of a syntax error.
+ */
+ private void parseVersionDeclaration() throws XPathException {
+ if (t.currentToken == Token.XQUERY_VERSION) {
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ queryVersion = t.currentTokenValue;
+ if (XQUERY10.equals(queryVersion)) {
+ // no action
+ } else if (XQUERY30.equals(queryVersion) || "1.1".equals(queryVersion)) {
+ queryVersion = XQUERY30;
+ if (!env.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.PROFESSIONAL_EDITION)) {
+ grumble("XQuery 3.0 is not supported in Saxon-HE", "XQST0031");
+ queryVersion = XQUERY10;
+ }
+ if (!DecimalValue.THREE.equals(((QueryModule) env).getLanguageVersion())) {
+ grumble("XQuery 3.0 was not enabled when invoking Saxon", "XQST0031");
+ queryVersion = XQUERY10;
+ }
+ } else {
+ grumble("Unsupported XQuery version " + queryVersion, "XQST0031");
+ queryVersion = XQUERY10;
+ }
+ nextToken();
+ if ("encoding".equals(t.currentTokenValue)) {
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ if (!encNamePattern.matcher(t.currentTokenValue).matches()) {
+ grumble("Encoding name contains invalid characters", "XQST0087");
+ }
+ // we ignore the encoding now: it was handled earlier, while decoding the byte stream
+ nextToken();
+ }
+ expect(Token.SEMICOLON);
+ nextToken();
+ } else if (t.currentToken == Token.XQUERY_ENCODING) {
+ if(queryVersion == XQUERY10){
+ grumble("XQuery version declaration omitted (Mandatory when running XQuery 1.0)", "XPST0003");
+ }
+ queryVersion = XQUERY30;
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ if (!encNamePattern.matcher(t.currentTokenValue).matches()) {
+ grumble("Encoding name contains invalid characters", "XQST0087");
+ }
+ // we ignore the encoding now: it was handled earlier, while decoding the byte stream
+ nextToken();
+ expect(Token.SEMICOLON);
+ nextToken();
+ }
+ }
+
+ /**
+ * In a library module, parse the module declaration
+ * Syntax: <"module" "namespace"> prefix "=" uri ";"
+ *
+ * @throws XPathException in the event of a syntax error.
+ */
+
+ private void parseModuleDeclaration() throws XPathException {
+ expect(Token.MODULE_NAMESPACE);
+ nextToken();
+ expect(Token.NAME);
+ String prefix = t.currentTokenValue;
+ nextToken();
+ expect(Token.EQUALS);
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ String uri = URILiteral(t.currentTokenValue);
+ checkProhibitedPrefixes(prefix, uri);
+ if (uri.length() == 0) {
+ grumble("Module namespace cannot be \"\"", "XQST0088");
+ uri = "http://saxon.fallback.namespace/"; // for error recovery
+ }
+ nextToken();
+ expect(Token.SEMICOLON);
+ nextToken();
+ try {
+ ((QueryModule) env).declarePrologNamespace(prefix, uri);
+ } catch (XPathException err) {
+ err.setLocator(makeLocator());
+ reportError(err);
+ }
+ ((QueryModule) env).setModuleNamespace(uri);
+ }
+
+ /**
+ * Parse the query prolog. This method, and its subordinate methods which handle
+ * individual declarations in the prolog, cause the static context to be updated
+ * with relevant context information. On exit, t.currentToken is the first token
+ * that is not recognized as being part of the prolog.
+ *
+ * @throws XPathException in the event of a syntax error.
+ */
+
+ private void parseProlog() throws XPathException {
+ //boolean allowSetters = true;
+ boolean allowModuleDecl = true;
+ boolean allowDeclarations = true;
+
+ while (true) {
+ try {
+ if (t.currentToken == Token.MODULE_NAMESPACE) {
+ String uri = ((QueryModule) env).getModuleNamespace();
+ if (uri == null) {
+ grumble("Module declaration must not be used in a main module");
+ } else {
+ grumble("Module declaration appears more than once");
+ }
+ if (!allowModuleDecl) {
+ grumble("Module declaration must precede other declarations in the query prolog");
+ }
+ }
+ allowModuleDecl = false;
+ switch (t.currentToken) {
+ case Token.DECLARE_NAMESPACE:
+ if (!allowDeclarations) {
+ grumble("Namespace declarations cannot follow variables, functions, or options");
+ }
+ //allowSetters = false;
+ parseNamespaceDeclaration();
+ break;
+ case Token.DECLARE_ANNOTATED:
+ // we have read "declare %"
+ processPreamble();
+ if (allowDeclarations) {
+ sealNamespaces(namespacesToBeSealed, env.getConfiguration());
+ allowDeclarations = false;
+ }
+ nextToken();
+ expect(Token.PERCENT);
+ Map<StructuredQName, Annotation> annotations = parseAnnotations();
+ if (isKeyword("function")) {
+ parseFunctionDeclaration(annotations);
+ } else if (isKeyword("variable")) {
+ parseVariableDeclaration(annotations);
+ } else {
+ grumble("Annotations can appear only in 'declare variable' and 'declare function'");
+ }
+ break;
+ case Token.DECLARE_DEFAULT:
+ nextToken();
+ expect(Token.NAME);
+ if (t.currentTokenValue.equals("element")) {
+ if (!allowDeclarations) {
+ grumble("Namespace declarations cannot follow variables, functions, or options");
+ }
+ //allowSetters = false;
+ parseDefaultElementNamespace();
+ } else if (t.currentTokenValue.equals("function")) {
+ if (!allowDeclarations) {
+ grumble("Namespace declarations cannot follow variables, functions, or options");
+ }
+ //allowSetters = false;
+ parseDefaultFunctionNamespace();
+ } else if (t.currentTokenValue.equals("collation")) {
+ if (!allowDeclarations) {
+ grumble("Collation declarations must appear earlier in the prolog");
+ }
+ parseDefaultCollation();
+ } else if (t.currentTokenValue.equals("order")) {
+ if (!allowDeclarations) {
+ grumble("Order declarations must appear earlier in the prolog");
+ }
+ parseDefaultOrder();
+ } else if (t.currentTokenValue.equals("decimal-format")) {
+ nextToken();
+ parseDefaultDecimalFormat();
+ } else {
+ grumble("After 'declare default', expected 'element', 'function', or 'collation'");
+ }
+ break;
+ case Token.DECLARE_BOUNDARY_SPACE:
+ if (!allowDeclarations) {
+ grumble("'declare boundary-space' must appear earlier in the query prolog");
+ }
+ parseBoundarySpaceDeclaration();
+ break;
+ case Token.DECLARE_ORDERING:
+ if (!allowDeclarations) {
+ grumble("'declare ordering' must appear earlier in the query prolog");
+ }
+ parseOrderingDeclaration();
+ break;
+ case Token.DECLARE_COPY_NAMESPACES:
+ if (!allowDeclarations) {
+ grumble("'declare copy-namespaces' must appear earlier in the query prolog");
+ }
+ parseCopyNamespacesDeclaration();
+ break;
+ case Token.DECLARE_BASEURI:
+ if (!allowDeclarations) {
+ grumble("'declare base-uri' must appear earlier in the query prolog");
+ }
+ parseBaseURIDeclaration();
+ break;
+ case Token.DECLARE_DECIMAL_FORMAT:
+ if (!allowDeclarations) {
+ grumble("'declare decimal-format' must appear earlier in the query prolog");
+ }
+ parseDecimalFormatDeclaration();
+ break;
+ case Token.IMPORT_SCHEMA:
+ //allowSetters = false;
+ if (!allowDeclarations) {
+ grumble("Import schema must appear earlier in the prolog");
+ }
+ parseSchemaImport();
+ break;
+ case Token.IMPORT_MODULE:
+ //allowSetters = false;
+ if (!allowDeclarations) {
+ grumble("Import module must appear earlier in the prolog");
+ }
+ parseModuleImport();
+ break;
+ case Token.DECLARE_VARIABLE:
+ //allowSetters = false;
+ if (allowDeclarations) {
+ sealNamespaces(namespacesToBeSealed, env.getConfiguration());
+ allowDeclarations = false;
+ }
+ processPreamble();
+ parseVariableDeclaration(null);
+ break;
+ case Token.DECLARE_CONTEXT:
+ //allowSetters = false;
+ if (allowDeclarations) {
+ sealNamespaces(namespacesToBeSealed, env.getConfiguration());
+ allowDeclarations = false;
+ }
+ processPreamble();
+ parseContextItemDeclaration();
+ break;
+ case Token.DECLARE_FUNCTION:
+ if (allowDeclarations) {
+ sealNamespaces(namespacesToBeSealed, env.getConfiguration());
+ allowDeclarations = false;
+ }
+ processPreamble();
+ parseFunctionDeclaration(null);
+ break;
+ case Token.DECLARE_UPDATING:
+ nextToken();
+ if (!isKeyword("function")) {
+ grumble("expected 'function' after 'declare updating");
+ }
+ if (allowDeclarations) {
+ sealNamespaces(namespacesToBeSealed, env.getConfiguration());
+ allowDeclarations = false;
+ }
+ processPreamble();
+ parseUpdatingFunctionDeclaration();
+ break;
+ case Token.DECLARE_OPTION:
+ if (allowDeclarations) {
+ sealNamespaces(namespacesToBeSealed, env.getConfiguration());
+ allowDeclarations = false;
+ }
+ parseOptionDeclaration();
+ break;
+ case Token.DECLARE_CONSTRUCTION:
+ if (!allowDeclarations) {
+ grumble("'declare construction' must appear earlier in the query prolog");
+ }
+ parseConstructionDeclaration();
+ break;
+ case Token.DECLARE_REVALIDATION:
+ if (!allowDeclarations) {
+ grumble("'declare revalidation' must appear earlier in the query prolog");
+ }
+ parseRevalidationDeclaration();
+ break;
+ default:
+ return;
+ }
+ expect(Token.SEMICOLON);
+ nextToken();
+ } catch (XPathException err) {
+ if (err.getLocator() == null) {
+ err.setLocator(makeLocator());
+ }
+ if (!err.hasBeenReported()) {
+ errorCount++;
+ if (firstError == null) {
+ firstError = err;
+ }
+ ((QueryModule) env).reportFatalError(err);
+ }
+ // we've reported an error, attempt to recover by skipping to the
+ // next semicolon
+ while (t.currentToken != Token.SEMICOLON) {
+ nextToken();
+ if (t.currentToken == Token.EOF) {
+ return;
+ } else if (t.currentToken == Token.RCURLY) {
+ t.lookAhead();
+ } else if (t.currentToken == Token.TAG) {
+ parsePseudoXML(true);
+ }
+ }
+ nextToken();
+ }
+ }
+ }
+
+ private void sealNamespaces(/*@NotNull*/ List namespacesToBeSealed, /*@NotNull*/ Configuration config) {
+ for (Object aNamespacesToBeSealed : namespacesToBeSealed) {
+ String ns = (String) aNamespacesToBeSealed;
+ config.sealNamespace(ns);
+ }
+ }
+
+ /**
+ * Method called once the setters have been read to do tidying up that can't be done until we've got
+ * to the end
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ private void processPreamble() throws XPathException {
+ if (preambleProcessed) {
+ return;
+ }
+ preambleProcessed = true;
+ if (foundDefaultCollation) {
+ String collationName = env.getDefaultCollationName();
+ URI collationURI;
+ try {
+ collationURI = new URI(collationName);
+ if (!collationURI.isAbsolute()) {
+ URI base = new URI(env.getBaseURI());
+ collationURI = base.resolve(collationURI);
+ collationName = collationURI.toString();
+ }
+ } catch (URISyntaxException err) {
+ grumble("Default collation name '" + collationName + "' is not a valid URI");
+ collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
+ }
+ if (env.getCollation(collationName) == null) {
+ grumble("Default collation name '" + collationName + "' is not a recognized collation", "XQST0038");
+ collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
+ }
+ ((QueryModule) env).setDefaultCollationName(collationName);
+ }
+ for (Import imp : schemaImports) {
+ try {
+ applySchemaImport(imp);
+ } catch (XPathException err) {
+ if (!err.hasBeenReported()) {
+ throw err;
+ }
+ }
+ }
+ for (Import imp : moduleImports) {
+ // Check that this import would not create a cycle involving a change of namespace
+// if (!disableCycleChecks) {
+// if (!imp.namespaceURI.equals(((QueryModule)env).getModuleNamespace())) {
+// QueryModule parent = (QueryModule)env;
+// if (!parent.mayImportModule(imp.namespaceURI)) {
+// XPathException err = new XPathException(
+// "A module cannot import itself directly or indirectly, unless all modules in the cycle are in the same namespace");
+// err.setErrorCode("XQST0073");
+// err.setIsStaticError(true);
+// throw err;
+// }
+// }
+// }
+ try {
+ applyModuleImport(imp);
+ } catch (XPathException err) {
+ if (!err.hasBeenReported()) {
+ throw err;
+ }
+ }
+ }
+ }
+
+ private void parseDefaultCollation() throws XPathException {
+ // <"default" "collation"> StringLiteral
+ if (foundDefaultCollation) {
+ grumble("default collation appears more than once", "XQST0038");
+ }
+ foundDefaultCollation = true;
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ String uri = URILiteral(t.currentTokenValue);
+ ((QueryModule) env).setDefaultCollationName(uri);
+ nextToken();
+ }
+
+ /**
+ * parse "declare default order empty (least|greatest)"
+ *
+ * @throws XPathException if parsing fails
+ */
+ private void parseDefaultOrder() throws XPathException {
+ if (foundEmptyOrderingDeclaration) {
+ grumble("empty ordering declaration appears more than once", "XQST0069");
+ }
+ foundEmptyOrderingDeclaration = true;
+ nextToken();
+ if (!isKeyword("empty")) {
+ grumble("After 'declare default order', expected keyword 'empty'");
+ }
+ nextToken();
+ if (isKeyword("least")) {
+ ((QueryModule) env).setEmptyLeast(true);
+ } else if (isKeyword("greatest")) {
+ ((QueryModule) env).setEmptyLeast(false);
+ } else {
+ grumble("After 'declare default order empty', expected keyword 'least' or 'greatest'");
+ }
+ nextToken();
+ }
+
+ /**
+ * Parse the "declare xmlspace" declaration.
+ * Syntax: <"declare" "boundary-space"> ("preserve" | "strip")
+ *
+ * @throws XPathException if a static error is encountered
+ */
+
+ private void parseBoundarySpaceDeclaration() throws XPathException {
+ if (foundBoundarySpaceDeclaration) {
+ grumble("'declare boundary-space' appears more than once", "XQST0068");
+ }
+ foundBoundarySpaceDeclaration = true;
+ nextToken();
+ expect(Token.NAME);
+ if ("preserve".equals(t.currentTokenValue)) {
+ ((QueryModule) env).setPreserveBoundarySpace(true);
+ } else if ("strip".equals(t.currentTokenValue)) {
+ ((QueryModule) env).setPreserveBoundarySpace(false);
+ } else {
+ grumble("boundary-space must be 'preserve' or 'strip'");
+ }
+ nextToken();
+ }
+
+ /**
+ * Parse the "declare ordering" declaration.
+ * Syntax: <"declare" "ordering"> ("ordered" | "unordered")
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ private void parseOrderingDeclaration() throws XPathException {
+ if (foundOrderingDeclaration) {
+ grumble("ordering mode declaration appears more than once", "XQST0065");
+ }
+ foundOrderingDeclaration = true;
+ nextToken();
+ expect(Token.NAME);
+ if ("ordered".equals(t.currentTokenValue)) {
+ // no action
+ } else if ("unordered".equals(t.currentTokenValue)) {
+ // no action
+ } else {
+ grumble("ordering mode must be 'ordered' or 'unordered'");
+ }
+ nextToken();
+ }
+
+ /**
+ * Parse the "declare copy-namespaces" declaration.
+ * Syntax: <"declare" "copy-namespaces"> ("preserve" | "no-preserve") "," ("inherit" | "no-inherit")
+ *
+ * @throws XPathException if a static error is encountered
+ */
+
+ private void parseCopyNamespacesDeclaration() throws XPathException {
+ if (foundCopyNamespaces) {
+ grumble("declare copy-namespaces appears more than once", "XQST0055");
+ }
+ foundCopyNamespaces = true;
+ nextToken();
+ expect(Token.NAME);
+ if ("preserve".equals(t.currentTokenValue)) {
+ ((QueryModule) env).setPreserveNamespaces(true);
+ } else if ("no-preserve".equals(t.currentTokenValue)) {
+ ((QueryModule) env).setPreserveNamespaces(false);
+ } else {
+ grumble("copy-namespaces must be followed by 'preserve' or 'no-preserve'");
+ }
+ nextToken();
+ expect(Token.COMMA);
+ nextToken();
+ expect(Token.NAME);
+ if ("inherit".equals(t.currentTokenValue)) {
+ ((QueryModule) env).setInheritNamespaces(true);
+ } else if ("no-inherit".equals(t.currentTokenValue)) {
+ ((QueryModule) env).setInheritNamespaces(false);
+ } else {
+ grumble("After the comma in the copy-namespaces declaration, expected 'inherit' or 'no-inherit'");
+ }
+ nextToken();
+ }
+
+
+ /**
+ * Parse the "declare construction" declaration.
+ * Syntax: <"declare" "construction"> ("preserve" | "strip")
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ private void parseConstructionDeclaration() throws XPathException {
+ if (foundConstructionDeclaration) {
+ grumble("declare construction appears more than once", "XQST0067");
+ }
+ foundConstructionDeclaration = true;
+ nextToken();
+ expect(Token.NAME);
+ int val;
+ if ("preserve".equals(t.currentTokenValue)) {
+ val = Validation.PRESERVE;
+// if (!env.getExecutable().isSchemaAware()) {
+// grumble("construction mode preserve is allowed only with a schema-aware query");
+// }
+ } else if ("strip".equals(t.currentTokenValue)) {
+ val = Validation.STRIP;
+ } else {
+ grumble("construction mode must be 'preserve' or 'strip'");
+ val = Validation.STRIP;
+ }
+ ((QueryModule) env).setConstructionMode(val);
+ nextToken();
+ }
+
+ /**
+ * Parse the "declare revalidation" declaration.
+ * Syntax: not allowed unless XQuery update is in use
+ *
+ * @throws XPathException if the syntax is incorrect, or is not allowed in this XQuery processor
+ */
+
+ protected void parseRevalidationDeclaration() throws XPathException {
+ grumble("declare revalidation is allowed only in XQuery Update");
+ }
+
+ /**
+ * Parse (and process) the schema import declaration.
+ * SchemaImport ::= "import" "schema" SchemaPrefix? URILiteral ("at" URILiteral ("," URILiteral)*)?
+ * SchemaPrefix ::= ("namespace" NCName "=") | ("default" "element" "namespace")
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ private void parseSchemaImport() throws XPathException {
+ if (!env.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY)) {
+ throw new XPathException("This Saxon version and license does not allow use of 'import schema'", "XQST0009");
+ }
+ env.getConfiguration().checkLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY, "import schema");
+ ((QueryModule) env).useFeature(LanguageFeature.TYPED_DATA_SCHEMAS, "import schema", makeLocator());
+
+ getExecutable().setSchemaAware(true);
+ Import sImport = new Import();
+ String prefix = null;
+ sImport.namespaceURI = null;
+ sImport.locationURIs = new ArrayList<String>(5);
+ nextToken();
+ if (isKeyword("namespace")) {
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ expect(Token.NAME);
+ prefix = t.currentTokenValue;
+ nextToken();
+ expect(Token.EQUALS);
+ nextToken();
+ } else if (isKeyword("default")) {
+ nextToken();
+ if (!isKeyword("element")) {
+ grumble("In 'import schema', expected 'element namespace'");
+ }
+ nextToken();
+ if (!isKeyword("namespace")) {
+ grumble("In 'import schema', expected keyword 'namespace'");
+ }
+ nextToken();
+ prefix = "";
+ }
+ if (t.currentToken == Token.STRING_LITERAL) {
+ String uri = URILiteral(t.currentTokenValue);
+ checkProhibitedPrefixes(prefix, uri);
+ sImport.namespaceURI = uri;
+ nextToken();
+ if (isKeyword("at")) {
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ sImport.locationURIs.add(URILiteral(t.currentTokenValue));
+ nextToken();
+ while (t.currentToken == Token.COMMA) {
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ sImport.locationURIs.add(URILiteral(t.currentTokenValue));
+ nextToken();
+ }
+ } else if (t.currentToken != Token.SEMICOLON) {
+ grumble("After the target namespace URI, expected 'at' or ';'");
+ }
+ } else {
+ grumble("After 'import schema', expected 'namespace', 'default', or a string-literal");
+ }
+ if (prefix != null) {
+ try {
+ if (prefix.length() == 0) {
+ ((QueryModule) env).setDefaultElementNamespace(sImport.namespaceURI);
+ } else {
+ if (sImport.namespaceURI == null || "".equals(sImport.namespaceURI)) {
+ grumble("A prefix cannot be bound to the null namespace", "XQST0057");
+ }
+ ((QueryModule) env).declarePrologNamespace(prefix, sImport.namespaceURI);
+ }
+ } catch (XPathException err) {
+ err.setLocator(makeLocator());
+ reportError(err);
+ }
+ }
+ for (Object schemaImport : schemaImports) {
+ Import imp = (Import) schemaImport;
+ if (imp.namespaceURI.equals(sImport.namespaceURI)) {
+ grumble("Schema namespace '" + sImport.namespaceURI + "' is imported more than once", "XQST0058");
+ break;
+ }
+ }
+
+ schemaImports.add(sImport);
+
+ }
+
+ private void applySchemaImport(/*@NotNull*/ Import sImport) throws XPathException {
+
+ // Do the importing
+
+ Configuration config = env.getConfiguration();
+ if (!config.isSchemaAvailable(sImport.namespaceURI)) {
+ if (!sImport.locationURIs.isEmpty()) {
+ try {
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ config.readMultipleSchemas(pipe, env.getBaseURI(), sImport.locationURIs, sImport.namespaceURI);
+ namespacesToBeSealed.add(sImport.namespaceURI);
+ } catch (TransformerConfigurationException err) {
+ grumble("Error in schema " + sImport.namespaceURI + ": " + err.getMessage(), "XQST0059");
+ }
+ } else if (sImport.namespaceURI.equals(NamespaceConstant.XML) ||
+ sImport.namespaceURI.equals(NamespaceConstant.FN) ||
+ sImport.namespaceURI.equals(NamespaceConstant.SCHEMA_INSTANCE)) {
+ config.addSchemaForBuiltInNamespace(sImport.namespaceURI);
+ } else {
+ grumble("Unable to locate requested schema " + sImport.namespaceURI, "XQST0059");
+ }
+ }
+ ((QueryModule) env).addImportedSchema(sImport.namespaceURI, env.getBaseURI(), sImport.locationURIs);
+ }
+
+ /**
+ * Parse (and expand) the module import declaration.
+ * Syntax: <"import" "module" ("namespace" NCName "=")? uri ("at" uri ("," uri)*)? ";"
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error is encountered
+ */
+
+ private void parseModuleImport() throws XPathException {
+ QueryModule thisModule = (QueryModule) env;
+ Import mImport = new Import();
+ String prefix = null;
+ mImport.namespaceURI = null;
+ mImport.locationURIs = new ArrayList<String>(5);
+ nextToken();
+ if (t.currentToken == Token.NAME && t.currentTokenValue.equals("namespace")) {
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ expect(Token.NAME);
+ prefix = t.currentTokenValue;
+ nextToken();
+ expect(Token.EQUALS);
+ nextToken();
+ }
+ if (t.currentToken == Token.STRING_LITERAL) {
+ String uri = URILiteral(t.currentTokenValue);
+ checkProhibitedPrefixes(prefix, uri);
+ mImport.namespaceURI = uri;
+ if (mImport.namespaceURI.length() == 0) {
+ grumble("Imported module namespace cannot be \"\"", "XQST0088");
+ mImport.namespaceURI = "http://saxon.fallback.namespace/line" + t.getLineNumber(); // for error recovery
+ }
+ if (importedModules.contains(mImport.namespaceURI)) {
+ grumble("Two 'import module' declarations specify the same module namespace", "XQST0047");
+ }
+ importedModules.add(mImport.namespaceURI);
+ ((QueryModule) env).addImportedNamespace(mImport.namespaceURI);
+ nextToken();
+ if (isKeyword("at")) {
+ do {
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ mImport.locationURIs.add(URILiteral(t.currentTokenValue));
+ nextToken();
+ } while (t.currentToken == Token.COMMA);
+ }
+ } else {
+ grumble("After 'import module', expected 'namespace' or a string-literal");
+ }
+ if (prefix != null) {
+ try {
+ if (mImport.namespaceURI.equals(thisModule.getModuleNamespace()) &&
+ mImport.namespaceURI.equals(thisModule.checkURIForPrefix(prefix))) {
+ // then do nothing: a duplicate declaration in this situation is not an error
+ } else {
+ thisModule.declarePrologNamespace(prefix, mImport.namespaceURI);
+ }
+ } catch (XPathException err) {
+ err.setLocator(makeLocator());
+ reportError(err);
+ }
+ }
+
+// // Check that this import would not create a cycle involving a change of namespace
+// if (!disableCycleChecks) {
+// if (!mImport.namespaceURI.equals(((QueryModule)env).getModuleNamespace())) {
+// QueryModule parent = (QueryModule)env;
+// if (!parent.mayImport(mImport.namespaceURI)) {
+// StaticError err = new StaticError("A module cannot import itself directly or indirectly, unless all modules in the cycle are in the same namespace");
+// err.setErrorCode("XQST0073");
+// throw err;
+// }
+// }
+// }
+
+ moduleImports.add(mImport);
+ ((QueryModule)env).useFeature(LanguageFeature.MODULE, "import module", makeLocator());
+ }
+
+ public void applyModuleImport(/*@NotNull*/ Import mImport) throws XPathException {
+ List<QueryModule> existingModules;
+
+ // resolve the location URIs against the base URI
+ for (int i = 0; i < mImport.locationURIs.size(); i++) {
+ try {
+ String uri = mImport.locationURIs.get(i);
+ URI abs = ResolveURI.makeAbsolute(uri, env.getBaseURI());
+ mImport.locationURIs.set(i, abs.toString());
+ } catch (URISyntaxException e) {
+ grumble("Invalid URI " + mImport.locationURIs.get(i) + ": " + e.getMessage());
+ }
+ }
+
+ // See if the URI is that of a separately-compiled query library
+ QueryLibrary lib = ((QueryModule) env).getUserQueryContext().getCompiledLibrary(mImport.namespaceURI);
+ if (lib != null) {
+ executable.addQueryLibraryModule(lib);
+ existingModules = new ArrayList<QueryModule>();
+ existingModules.add(lib);
+ lib.link(((QueryModule) env));
+
+ } else {
+
+ for (int h = mImport.locationURIs.size() - 1; h >= 0; h--) {
+ if (executable.isQueryLocationHintProcessed(mImport.locationURIs.get(h))) {
+ mImport.locationURIs.remove(h);
+ }
+ }
+
+ }
+
+ // If there are no location URIs left, and we already know a module with the right module URI.
+
+ if (mImport.locationURIs.isEmpty()) {
+ List<QueryModule> list = executable.getQueryLibraryModules(mImport.namespaceURI);
+ if (list != null && !list.isEmpty()) {
+ return;
+ }
+ }
+
+ // Call the module URI resolver to find the remaining modules
+
+ ModuleURIResolver resolver = ((QueryModule) env).getUserQueryContext().getModuleURIResolver();
+
+ String[] hints = new String[mImport.locationURIs.size()];
+ for (int h = 0; h < hints.length; h++) {
+ hints[h] = mImport.locationURIs.get(h);
+ }
+ StreamSource[] sources = null;
+ if (resolver != null) {
+ try {
+ sources = resolver.resolve(mImport.namespaceURI, env.getBaseURI(), hints);
+ } catch (XPathException err) {
+ grumble("Failed to resolve URI of imported module: " + err.getMessage(), "XQST0059");
+ }
+ }
+ if (sources == null) {
+ if (hints.length == 0) {
+ grumble("Cannot locate module for namespace " + mImport.namespaceURI, "XQST0059");
+ }
+ resolver = env.getConfiguration().getStandardModuleURIResolver();
+ sources = resolver.resolve(mImport.namespaceURI, env.getBaseURI(), hints);
+ }
+
+ for (String hint : mImport.locationURIs) {
+ executable.addQueryLocationHintProcessed(hint);
+ }
+
+ for (int m = 0; m < sources.length; m++) {
+ StreamSource ss = sources[m];
+ String baseURI = ss.getSystemId();
+ if (baseURI == null) {
+ if (m < hints.length) {
+ baseURI = hints[m];
+ ss.setSystemId(hints[m]);
+ } else {
+ grumble("No base URI available for imported module", "XQST0059");
+ }
+ }
+ // Although the module hadn't been loaded when we started, it might have been loaded since, as
+ // a result of a reference from another imported module. Note, we are careful here to use URI.equals()
+ // rather that String.equals() to compare URIs, as this gives a slightly more intelligent comparison,
+ // for example the scheme name is case-independent, and file:///x/y/z matches file:/x/y/z.
+ // TODO: use similar logic when loading schema modules
+ // TODO: despite the comment above, we are comparing URIs as strings
+ existingModules = executable.getQueryLibraryModules(mImport.namespaceURI);
+ boolean loaded = false;
+ if (existingModules != null && m < hints.length) {
+ for (QueryModule existingModule : existingModules) {
+ URI uri = existingModule.getLocationURI();
+ if (uri != null && uri.toString().equals(mImport.locationURIs.get(m))) {
+ loaded = true;
+ break;
+ }
+ }
+ }
+ if (loaded) {
+ break;
+ }
+
+ try {
+ String queryText = QueryReader.readSourceQuery(ss, nameChecker);
+ try {
+ if (ss.getInputStream() != null) {
+ ss.getInputStream().close();
+ } else if (ss.getReader() != null) {
+ ss.getReader().close();
+ }
+ } catch (IOException e) {
+ throw new XPathException("Failure while closing file for imported query module");
+ }
+ QueryModule.makeQueryModule(
+ baseURI, executable, (QueryModule) env, queryText, mImport.namespaceURI,
+ disableCycleChecks);
+ } catch (XPathException err) {
+ err.maybeSetLocation(makeLocator());
+ reportError(err);
+ }
+ }
+ }
+
+ /**
+ * Parse the Base URI declaration.
+ * Syntax: <"declare" "base-uri"> uri-literal
+ *
+ * @throws XPathException if a static error is found
+ */
+
+ private void parseBaseURIDeclaration() throws XPathException {
+ if (foundBaseURIDeclaration) {
+ grumble("Base URI Declaration may only appear once", "XQST0032");
+ }
+ foundBaseURIDeclaration = true;
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ String uri = URILiteral(t.currentTokenValue);
+ try {
+ // if the supplied URI is relative, try to resolve it
+ URI baseURI = new URI(uri);
+ if (!baseURI.isAbsolute()) {
+ String oldBase = env.getBaseURI();
+ uri = ResolveURI.makeAbsolute(uri, oldBase).toString();
+ }
+ ((QueryModule) env).setBaseURI(uri);
+ } catch (URISyntaxException err) {
+ // The spec says this "is not intrinsically an error", but can cause a failure later
+ ((QueryModule) env).setBaseURI(uri);
+ }
+ nextToken();
+ }
+
+ /**
+ * Parse the "declare decimal-format" declaration.
+ * Allowed in XQuery 1.1 only
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ protected void parseDecimalFormatDeclaration() throws XPathException {
+ grumble("A decimal-format declaration is allowed only when XQuery 1.1 is enabled");
+ }
+
+ protected void parseDefaultDecimalFormat() throws XPathException {
+ grumble("A decimal-format declaration is allowed only when XQuery 1.1 is enabled");
+ }
+
+ /**
+ * Parse the "default function namespace" declaration.
+ * Syntax: <"declare" "default" "function" "namespace"> StringLiteral
+ *
+ * @throws XPathException to indicate a syntax error
+ */
+
+ private void parseDefaultFunctionNamespace() throws XPathException {
+ if (foundDefaultFunctionNamespace) {
+ grumble("default function namespace appears more than once", "XQST0066");
+ }
+ foundDefaultFunctionNamespace = true;
+ nextToken();
+ expect(Token.NAME);
+ if (!"namespace".equals(t.currentTokenValue)) {
+ grumble("After 'declare default function', expected 'namespace'");
+ }
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ String uri = URILiteral(t.currentTokenValue);
+ if (uri.equals(NamespaceConstant.XML) || uri.equals(NamespaceConstant.XMLNS)) {
+ grumble("Reserved namespace used as default element/type namespace", "XQST0070");
+ }
+ ((QueryModule) env).setDefaultFunctionNamespace(uri);
+ nextToken();
+ }
+
+ /**
+ * Parse the "default element namespace" declaration.
+ * Syntax: <"declare" "default" "element" "namespace"> StringLiteral
+ *
+ * @throws XPathException to indicate a syntax error
+ */
+
+ private void parseDefaultElementNamespace() throws XPathException {
+ if (foundDefaultElementNamespace) {
+ grumble("default element namespace appears more than once", "XQST0066");
+ }
+ foundDefaultElementNamespace = true;
+ nextToken();
+ expect(Token.NAME);
+ if (!"namespace".equals(t.currentTokenValue)) {
+ grumble("After 'declare default element', expected 'namespace'");
+ }
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ String uri = URILiteral(t.currentTokenValue);
+ if (uri.equals(NamespaceConstant.XML) || uri.equals(NamespaceConstant.XMLNS)) {
+ grumble("Reserved namespace used as default element/type namespace", "XQST0070");
+ }
+ ((QueryModule) env).setDefaultElementNamespace(uri);
+ nextToken();
+ }
+
+ /**
+ * Parse a namespace declaration in the Prolog.
+ * Syntax: <"declare" "namespace"> NCName "=" StringLiteral
+ *
+ * @throws XPathException if parsing fails or a static error is found
+ */
+
+ private void parseNamespaceDeclaration() throws XPathException {
+ nextToken();
+ expect(Token.NAME);
+ String prefix = t.currentTokenValue;
+ if (!nameChecker.isValidNCName(prefix)) {
+ grumble("Invalid namespace prefix " + Err.wrap(prefix));
+ }
+ nextToken();
+ expect(Token.EQUALS);
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ String uri = URILiteral(t.currentTokenValue);
+ checkProhibitedPrefixes(prefix, uri);
+ if ("xml".equals(prefix)) {
+ // disallowed here even if bound to the correct namespace - erratum XQ.E19
+ grumble("Namespace prefix 'xml' cannot be declared", "XQST0070");
+ }
+ try {
+ ((QueryModule) env).declarePrologNamespace(prefix, uri);
+ } catch (XPathException err) {
+ err.setLocator(makeLocator());
+ reportError(err);
+ }
+ nextToken();
+ }
+
+ /**
+ * Check that a namespace declaration does not use a prohibited prefix or URI (xml or xmlns)
+ *
+ * @param prefix the prefix to be tested
+ * @param uri the URI being declared
+ * @throws XPathException if the prefix is prohibited
+ */
+
+ private void checkProhibitedPrefixes(/*@Nullable*/ String prefix, /*@Nullable*/ String uri) throws XPathException {
+ if (prefix != null && prefix.length() > 0 && !nameChecker.isValidNCName(prefix)) {
+ grumble("The namespace prefix " + Err.wrap(prefix) + " is not a valid NCName");
+ }
+ if (prefix == null) {
+ prefix = "";
+ }
+ if (uri == null) {
+ uri = "";
+ }
+ if ("xmlns".equals(prefix)) {
+ grumble("The namespace prefix 'xmlns' cannot be redeclared", "XQST0070");
+ }
+ if (uri.equals(NamespaceConstant.XMLNS)) {
+ grumble("The xmlns namespace URI is reserved", "XQST0070");
+ }
+ if (uri.equals(NamespaceConstant.XML) && !prefix.equals("xml")) {
+ grumble("The XML namespace cannot be bound to any prefix other than 'xml'", "XQST0070");
+ }
+ if (prefix.equals("xml") && !uri.equals(NamespaceConstant.XML)) {
+ grumble("The prefix 'xml' cannot be bound to any namespace other than " + NamespaceConstant.XML, "XQST0070");
+ }
+ }
+
+ /**
+ * Parse a global variable definition.
+ * <"declare" "variable" "$"> VarName TypeDeclaration?
+ * ((":=" ExprSingle ) | "external")
+ * XQuery 3.0 allows "external := ExprSingle"
+ *
+ * @param annotations derived from any %-annotations present in XQuery 3.0
+ * @throws XPathException if a static error is found
+ */
+
+ private void parseVariableDeclaration(Map<StructuredQName, Annotation> annotations) throws XPathException {
+ int offset = t.currentTokenStartOffset;
+ GlobalVariable var = new GlobalVariable();
+ var.setExecutable(getExecutable());
+ var.setLineNumber(t.getLineNumber());
+ var.setSystemId(env.getSystemId());
+ if (annotations != null) {
+ var.setPrivate(annotations.get(Annotation.PRIVATE) != null);
+ }
+ nextToken();
+ expect(Token.DOLLAR);
+ t.setState(Tokenizer.BARE_NAME_STATE);
+ nextToken();
+ expect(Token.NAME);
+ String varName = t.currentTokenValue;
+ StructuredQName varQName = makeStructuredQName(t.currentTokenValue, "");
+ var.setVariableQName(varQName);
+
+ String uri = varQName.getURI();
+ String moduleURI = ((QueryModule) env).getModuleNamespace();
+ if (moduleURI != null && !moduleURI.equals(uri)) {
+ grumble("A variable declared in a library module must be in the module namespace", "XQST0048", offset);
+ }
+
+ nextToken();
+ SequenceType requiredType = SequenceType.ANY_SEQUENCE;
+ if (t.currentToken == Token.AS) {
+ t.setState(Tokenizer.SEQUENCE_TYPE_STATE);
+ nextToken();
+ requiredType = parseSequenceType();
+ }
+ var.setRequiredType(requiredType);
+
+ if (t.currentToken == Token.ASSIGN) {
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ Expression exp = parseExprSingle();
+ var.setSelectExpression(makeTracer(offset, exp, StandardNames.XSL_VARIABLE, varQName));
+ } else if (t.currentToken == Token.NAME) {
+ if ("external".equals(t.currentTokenValue)) {
+ GlobalParam par = new GlobalParam();
+ par.setExecutable(var.getExecutable());
+ par.setLineNumber(var.getLineNumber());
+ par.setSystemId(var.getSystemId());
+ par.setVariableQName(var.getVariableQName());
+ par.setRequiredType(var.getRequiredType());
+ var = par;
+ nextToken();
+ if (t.currentToken == Token.ASSIGN && XQUERY30.equals(queryVersion)) {
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ Expression exp = parseExprSingle();
+ var.setSelectExpression(makeTracer(offset, exp, StandardNames.XSL_VARIABLE, varQName));
+ } else if (defaultValue != null) {
+ var.setSelectExpression(defaultValue);
+ }
+
+ } else {
+ grumble("Variable must either be initialized or be declared as external");
+ }
+ } else {
+ grumble("Expected ':=' or 'external' in variable declaration");
+ }
+
+ QueryModule qenv = (QueryModule) env;
+ if (qenv.getModuleNamespace() != null &&
+ !uri.equals(qenv.getModuleNamespace())) {
+ grumble("Variable " + Err.wrap(varName, Err.VARIABLE) + " is not defined in the module namespace");
+ }
+ try {
+ qenv.declareVariable(var);
+ } catch (XPathException e) {
+ grumble(e.getMessage(), e.getErrorCodeQName(), -1);
+ }
+ }
+
+ /**
+ * Parse a context item declaration. Allowed only in XQuery 3.0
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ protected void parseContextItemDeclaration() throws XPathException {
+ grumble("A context item declaration is allowed only in XQuery 3.0");
+ }
+
+ protected boolean isReservedFunctionName(String reservedStr){
+ return reservedNames.contains(reservedStr);
+ }
+
+ /**
+ * Parse a function declaration.
+ * <p>Syntax:<br/>
+ * <"declare" "function"> QName "(" ParamList? ")" ("as" SequenceType)?
+ * (EnclosedExpr | "external")
+ * </p>
+ * <p>On entry, the "declare function" has already been recognized</p>
+ *
+ * @param annotations the list of annotations that have been encountered for this function declaration
+ * @throws XPathException if a syntax error is found
+ */
+
+ protected void parseFunctionDeclaration(Map<StructuredQName, Annotation> annotations) throws XPathException {
+
+ // the next token should be the < QNAME "("> pair
+ int offset = t.currentTokenStartOffset;
+ nextToken();
+ if(t.currentToken == Token.NODEKIND || t.currentToken == Token.IF || t.currentToken == Token.SWITCH || t.currentToken == Token.TYPESWITCH) {
+ t.currentToken = Token.FUNCTION;
+ }
+ expect(Token.FUNCTION);
+
+ String uri;
+ StructuredQName qName;
+ if (t.currentTokenValue.indexOf(':') < 0) {
+ if(languageVersion.equals(DecimalValue.THREE) && isReservedFunctionName(t.currentTokenValue)) {
+ grumble("Function name "+t.currentTokenValue+" is reserved in XQuery 3.0");
+ }
+ uri = env.getDefaultFunctionNamespace();
+ qName = new StructuredQName("", uri, t.currentTokenValue);
+ } else {
+ qName = makeStructuredQName(t.currentTokenValue, "");
+ uri = qName.getURI();
+ }
+
+ if (uri.length() == 0) {
+ grumble("The function must be in a namespace", "XQST0060");
+ }
+
+ String moduleURI = ((QueryModule) env).getModuleNamespace();
+ if (moduleURI != null && !moduleURI.equals(uri)) {
+ grumble("A function in a library module must be in the module namespace", "XQST0048");
+ }
+
+ if (isReservedInQuery(uri)) {
+ grumble("The function name " + t.currentTokenValue + " is in a reserved namespace", "XQST0045");
+ }
+
+ XQueryFunction func = new XQueryFunction();
+ func.setFunctionName(qName);
+ func.setResultType(SequenceType.ANY_SEQUENCE);
+ func.setBody(null);
+ func.setLineNumber(t.getLineNumber(offset));
+ func.setColumnNumber(t.getColumnNumber(offset));
+ func.setSystemId(env.getSystemId());
+ func.setStaticContext((QueryModule) env);
+ func.setMemoFunction(memoFunction);
+ func.setExecutable(getExecutable());
+ if (annotations != null) {
+ func.setUpdating(annotations.get(Annotation.UPDATING) != null);
+ func.setAnnotations(annotations);
+ }
+
+ nextToken();
+ HashSet<StructuredQName> paramNames = new HashSet<StructuredQName>(8);
+ while (t.currentToken != Token.RPAR) {
+ // ParamList ::= Param ("," Param)*
+ // Param ::= "$" VarName TypeDeclaration?
+ expect(Token.DOLLAR);
+ nextToken();
+ expect(Token.NAME);
+ StructuredQName argQName = makeStructuredQName(t.currentTokenValue, "");
+ if (paramNames.contains(argQName)) {
+ grumble("Duplicate parameter name " + Err.wrap(t.currentTokenValue, Err.VARIABLE), "XQST0039");
+ }
+ paramNames.add(argQName);
+ SequenceType paramType = SequenceType.ANY_SEQUENCE;
+ nextToken();
+ if (t.currentToken == Token.AS) {
+ nextToken();
+ paramType = parseSequenceType();
+ }
+
+ UserFunctionParameter arg = new UserFunctionParameter();
+ arg.setRequiredType(paramType);
+ arg.setVariableQName(argQName);
+ func.addArgument(arg);
+ declareRangeVariable(arg);
+ if (t.currentToken == Token.RPAR) {
+ break;
+ } else if (t.currentToken == Token.COMMA) {
+ nextToken();
+ } else {
+ grumble("Expected ',' or ')' after function argument, found '" +
+ Token.tokens[t.currentToken] + '\'');
+ }
+ }
+ t.setState(Tokenizer.BARE_NAME_STATE);
+ nextToken();
+ if (t.currentToken == Token.AS) {
+ if (func.isUpdating()) {
+ grumble("Cannot specify a return type for an updating function", "XUST0028");
+ }
+ t.setState(Tokenizer.SEQUENCE_TYPE_STATE);
+ nextToken();
+ func.setResultType(parseSequenceType());
+ }
+ if (isKeyword("external")) {
+ grumble("Saxon does not allow external functions to be declared", "XPST0017");
+ // TODO: allow this, provided the function is available (and take note of "nondeterministic")
+ } else {
+ expect(Token.LCURLY);
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ func.setBody(parseExpression());
+ if (t.currentToken != Token.RCURLY) {
+ // special case error handling for when the function body is wrongly written as "return XXX"
+ Expression body = func.getBody();
+ if (body instanceof AxisExpression && body.toString().equals("child::return")) {
+ grumble("Incorrect use of 'return' keyword in function body");
+ }
+ }
+ expect(Token.RCURLY);
+ lookAhead(); // must be done manually after an RCURLY
+ }
+ UserFunctionParameter[] params = func.getParameterDefinitions();
+ //noinspection UnusedDeclaration
+ for (UserFunctionParameter param : params) {
+ undeclareRangeVariable();
+ }
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+
+ QueryModule qenv = (QueryModule) env;
+
+ try {
+ qenv.declareFunction(func);
+ } catch (XPathException e) {
+ grumble(e.getMessage(), e.getErrorCodeQName(), -1);
+ }
+ memoFunction = false;
+ }
+
+ /**
+ * Parse an updating function declaration (allowed in XQuery Update only)
+ *
+ * @throws XPathException if parsing fails or if updating functions are not allowed
+ */
+
+ protected void parseUpdatingFunctionDeclaration() throws XPathException {
+ grumble("Updating functions are allowed only in XQuery Update");
+ }
+
+ /**
+ * Parse an option declaration.
+ * <p>Syntax:<br/>
+ * <"declare" "option"> QName "string-literal"
+ * </p>
+ * <p>On entry, the "declare option" has already been recognized</p>
+ *
+ * @throws XPathException if a syntax error is found
+ */
+
+ private void parseOptionDeclaration() throws XPathException {
+ nextToken();
+ expect(Token.NAME);
+ String defaultUri = (allowXPath30Syntax ? NamespaceConstant.XQUERY : "");
+ StructuredQName varName = makeStructuredQName(t.currentTokenValue, defaultUri);
+ String uri = varName.getURI();
+
+ if (uri.length() == 0) {
+ grumble("The QName identifying an option declaration must be prefixed", "XPST0081");
+ return;
+ }
+
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ String value = URILiteral(t.currentTokenValue).trim();
+
+ if (uri.equals(NamespaceConstant.OUTPUT)) {
+ parseOutputDeclaration(varName, value);
+ } else if (uri.equals(NamespaceConstant.SAXON)) {
+ String localName = varName.getLocalPart();
+ if (localName.equals("output")) {
+ setOutputProperty(value);
+ } else if (localName.equals("default")) {
+ defaultValue = setDefaultValue(value);
+ } else if (localName.equals("memo-function")) {
+ if (value.equals("true")) {
+ memoFunction = true;
+ if (env.getConfiguration().getEditionCode().equals("HE")) {
+ warning("saxon:memo-function option is ignored under Saxon-HE");
+ }
+ } else if (value.equals("false")) {
+ memoFunction = false;
+ } else {
+ warning("Value of saxon:memo-function must be 'true' or 'false'");
+ }
+ } else if (localName.equals("allow-cycles")) {
+ if (value.equals("true")) {
+ disableCycleChecks = true;
+ } else if (value.equals("false")) {
+ disableCycleChecks = false;
+ } else {
+ warning("Value of saxon:allow-cycles must be 'true' or 'false'");
+ }
+ } else {
+ warning("Unknown Saxon option declaration: " + varName.getDisplayName());
+ }
+ } else if (uri.equals(NamespaceConstant.XQUERY)) {
+ String localName = varName.getLocalPart();
+// StringTokenizer tokenizer = new StringTokenizer(value, " \t\n\r", false);
+// StructuredQName featureName;
+ if (localName.equals("require-feature") || localName.equals("prohibit-feature")) {
+ warning("The options require-feature and prohibit-feature have been dropped from the specification");
+ } else {
+ warning("Unknown option declaration in XQuery namespace: " + varName.getDisplayName());
+ }
+// boolean requiredFound = false;
+// if (localName.equals("require-feature")) {
+// requiredFound = true;
+// } else if (!localName.equals("prohibit-feature")) {
+// warning("Unknown option declaration in XQuery namespace: " + varName.getDisplayName());
+// }
+// while (tokenizer.hasMoreTokens()) {
+// String val = tokenizer.nextToken();
+// if (val == null) {
+// break;
+// }
+// try {
+// featureName = makeStructuredQNameSilently(val, defaultUri);
+// } catch (XPathException err) {
+// grumble("The feature name " + val + " is not a valid lexical QName", "XQST0081");
+// return;
+// } catch (QNameException err) {
+// grumble("The feature name " + val + " is not a valid lexical QName", "XQST0122");
+// return;
+// }
+//
+// if (featureName.equals(LanguageFeature.ALL_OPTIONAL_FEATURES)) {
+// Set<LanguageFeature> exceptions =
+// (requiredFound ? ((QueryModule)env).getFeaturesProhibited() : ((QueryModule)env).getFeaturesRequired());
+// for (LanguageFeature f : LanguageFeature.getAllOptionalFeatures()) {
+// if (!exceptions.contains(f)) {
+// processRequireProhibitFeatures(requiredFound, f.getName());
+// }
+// }
+// } else {
+// processRequireProhibitFeatures(requiredFound, featureName);
+// }
+// }
+ }
+
+ nextToken();
+ }
+
+// private void processRequireProhibitFeatures(boolean isRequired, StructuredQName featureName) throws XPathException {
+// String localname = featureName.getLocalPart();
+// LanguageFeature feature = LanguageFeature.getFeature(featureName);
+// if (localname.endsWith("-all-optional-features")) {
+// LanguageFeature parent = feature.getParent();
+// processRequireProhibitFeatures(isRequired, parent.getName());
+// for (LanguageFeature child : parent.getChildren()) {
+// if (child != feature) {
+// processRequireProhibitFeatures(isRequired, child.getName());
+// }
+// }
+// return;
+// }
+// if (feature == null) {
+// grumble("The feature name " + featureName.getLocalPart() + " is not recognized", "XQST0123");
+// return;
+// }
+// if (isRequired) {
+// if (feature.getAvailability() == LanguageFeature.NEVER) {
+// grumble("Saxon does not implement the " + featureName.getLocalPart() + " feature", "XQST0120");
+// }
+// if (featureName.equals(LanguageFeature.ALL_EXTENSIONS.getName())) {
+// grumble("The all-extensions feature cannot appear in a require-feature declaration", "XQST0126");
+// }
+// if (((QueryModule)env).getFeaturesProhibited().contains(feature)) {
+// grumble("The " + featureName.getLocalPart() + " feature has already been prohibited", "XQST0127");
+// }
+// if (((QueryModule)env).getFeaturesRequired().contains(feature)) {
+// warning("The " + featureName.getLocalPart() + " feature appears as required more than once");
+// }
+// ((QueryModule)env).getFeaturesRequired().add(feature);
+// } else {
+// if (feature.getAvailability() == LanguageFeature.ALWAYS) {
+// grumble("Saxon does not allow the feature " + featureName.getLocalPart() + " to be disabled", "XQST0128");
+// }
+// if (((QueryModule)env).getFeaturesRequired().contains(feature)) {
+// grumble("The " + featureName.getLocalPart() + " feature has already been required", "XQST0127");
+// }
+// SourceLocator locator = ((QueryModule)env).getFeaturesUsed().get(feature);
+// if (locator != null) {
+// XPathException err = new XPathException("The query uses a construct that requires feature " +
+// featureName.getLocalPart() + ", but this feature is subsequently prohibited (see line " +
+// getTokenizer().getLineNumber() + ")");
+// err.setLocator(locator);
+// err.setErrorCode("XQST0127");
+// throw err;
+// }
+// if (((QueryModule)env).getFeaturesProhibited().contains(feature)) {
+// warning("The " + featureName.getLocalPart() + " feature appears as prohibited more than once");
+// }
+// ((QueryModule)env).getFeaturesProhibited().add(feature);
+// }
+//
+//
+// if (isRequired && localname.equals("schema-aware")) {
+// if (!env.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.SCHEMA_VALIDATION)) {
+// grumble("Schema-awareness is not available with this version of Saxon and this license", "XQST0120");
+// }
+//
+// } else {
+// if (localname.equals("all-extensions")) {
+// //TODO also - all-optional-features
+// }
+//
+// }
+//
+// }
+
+ protected void parseOutputDeclaration(StructuredQName varName, String value) throws XPathException {
+ grumble("Serialization parameters require XQuery 3.0 to be enabled");
+ }
+
+ /**
+ * Handle a saxon:output option declaration. Format:
+ * declare option saxon:output "indent = yes"
+ *
+ * @param property a property name=value pair. The name is the name of a serialization
+ * property, potentially as a prefixed QName; the value is the value of the property. A warning
+ * is output for unrecognized properties or values
+ */
+
+ private void setOutputProperty(/*@NotNull*/ String property) {
+ int equals = property.indexOf("=");
+ if (equals < 0) {
+ badOutputProperty("no equals sign");
+ } else if (equals == 0) {
+ badOutputProperty("starts with '=");
+ }
+ String keyword = Whitespace.trim(property.substring(0, equals));
+ String value = ((equals == property.length() - 1) ? "" : Whitespace.trim(property.substring(equals + 1)));
+
+ Properties props = getExecutable().getDefaultOutputProperties();
+ try {
+ assert keyword != null;
+ int key = makeNameCode(keyword, false) & NamePool.FP_MASK;
+ String lname = env.getNamePool().getLocalName(key);
+ String uri = env.getNamePool().getURI(key);
+ ResultDocument.setSerializationProperty(props,
+ uri, lname,
+ value,
+ env.getNamespaceResolver(),
+ false,
+ env.getConfiguration());
+ } catch (XPathException e) {
+ badOutputProperty(e.getMessage());
+ }
+ }
+
+ private void badOutputProperty(String s) {
+ try {
+ warning("Invalid serialization property (" + s + ")");
+ } catch (XPathException staticError) {
+ //
+ }
+ }
+
+ /**
+ * Parse the expression (inside a string literal) used to define default values
+ * for external variables. This requires instantiating a nested XPath parser.
+ * (This is a Saxon extension for XQuery 1.0 which becomes obsolete with XQuery 3.0)
+ *
+ * @param exp holds the expression used to define a default value
+ * @return the compiled expression that computes the default value
+ */
+
+ /*@Nullable*/
+ public Expression setDefaultValue(String exp) {
+ try {
+ DedicatedStaticContext ic = new DedicatedStaticContext(env.getConfiguration());
+ ic.setNamespaceResolver(env.getNamespaceResolver());
+ Expression expr = ExpressionTool.make(exp, ic, ic, 0, Token.EOF, 1, null);
+
+ ItemType contextItemType = Type.ITEM_TYPE;
+ ExpressionVisitor visitor = ExpressionVisitor.make(ic, getExecutable());
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(contextItemType, true);
+ expr = visitor.typeCheck(expr, cit);
+ expr = visitor.optimize(expr, cit);
+ SlotManager stackFrameMap = ic.getStackFrameMap();
+ ExpressionTool.allocateSlots(expr, stackFrameMap.getNumberOfVariables(), stackFrameMap);
+ return expr;
+ } catch (XPathException e) {
+ try {
+ warning("Invalid expression for default value: " + e.getMessage() + " (ignored)");
+ } catch (XPathException staticError) {
+ //
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Parse a FLWOR expression. This replaces the XPath "for" expression.
+ * Full syntax:
+ * <p/>
+ * [41] FLWORExpr ::= (ForClause | LetClause)+
+ * WhereClause? OrderByClause?
+ * "return" ExprSingle
+ * [42] ForClause ::= <"for" "$"> VarName TypeDeclaration? PositionalVar? "in" ExprSingle
+ * ("," "$" VarName TypeDeclaration? PositionalVar? "in" ExprSingle)*
+ * [43] PositionalVar ::= "at" "$" VarName
+ * [44] LetClause ::= <"let" "$"> VarName TypeDeclaration? ":=" ExprSingle
+ * ("," "$" VarName TypeDeclaration? ":=" ExprSingle)*
+ * [45] WhereClause ::= "where" Expr
+ * [46] OrderByClause ::= (<"order" "by"> | <"stable" "order" "by">) OrderSpecList
+ * [47] OrderSpecList ::= OrderSpec ("," OrderSpec)*
+ * [48] OrderSpec ::= ExprSingle OrderModifier
+ * [49] OrderModifier ::= ("ascending" | "descending")?
+ * (<"empty" "greatest"> | <"empty" "least">)?
+ * ("collation" StringLiteral)?
+ * </p>
+ *
+ * @return the resulting subexpression
+ * @throws XPathException if any error is encountered
+ */
+
+ /*@Nullable*/
+ protected Expression parseFLWORExpression() throws XPathException {
+ int exprOffset = t.currentTokenStartOffset;
+ List<Clause> clauseList = new ArrayList<Clause>(4);
+ boolean foundOrderBy = false;
+ boolean foundWhere = false;
+ while (true) {
+ int offset = t.currentTokenStartOffset;
+ if (t.currentToken == Token.FOR) {
+ if (foundWhere && !XQUERY30.equals(queryVersion)) {
+ grumble("In XQuery 1.0 'for' cannot follow 'where'");
+ }
+ if (foundOrderBy && !XQUERY30.equals(queryVersion)) {
+ grumble("In XQuery 1.0 'for' cannot follow 'order by'");
+ }
+ parseForClause(clauseList);
+ } else if (t.currentToken == Token.LET) {
+ if (foundWhere && !XQUERY30.equals(queryVersion)) {
+ grumble("In XQuery 1.0 'let' cannot follow 'where'");
+ }
+ if (foundOrderBy && !XQUERY30.equals(queryVersion)) {
+ grumble("In XQuery 1.0 'let' cannot follow 'order by'");
+ }
+ parseLetClause(clauseList);
+ } else if (t.currentToken == Token.COUNT) {
+ parseCountClause(clauseList);
+ } else if (t.currentToken == Token.GROUP_BY) {
+ parseGroupByClause(clauseList);
+ } else if (t.currentToken == Token.FOR_TUMBLING || t.currentToken == Token.FOR_SLIDING) {
+ parseWindowClause(clauseList);
+ } else if (t.currentToken == Token.WHERE || isKeyword("where")) {
+ if (foundWhere && !XQUERY30.equals(queryVersion)) {
+ grumble("In XQuery 1.0 only one 'where' clause is allowed");
+ }
+ if (foundOrderBy && !XQUERY30.equals(queryVersion)) {
+ grumble("In XQuery 1.0 'where' cannot follow 'order by'");
+ }
+ nextToken();
+ Expression condition = parseExprSingle();
+ WhereClause clause = new WhereClause(condition);
+ clauseList.add(clause);
+ foundWhere = true;
+ } else if (isKeyword("stable") || isKeyword("order")) {
+ // we read the "stable" keyword but ignore it; Saxon ordering is always stable
+ if (isKeyword("stable")) {
+ nextToken();
+ if (!isKeyword("order")) {
+ grumble("'stable' must be followed by 'order by'");
+ }
+ }
+ if (foundOrderBy && !XQUERY30.equals(queryVersion)) {
+ grumble("In XQuery 1.0 'order by' can only appear once");
+ }
+ foundOrderBy = true;
+ TupleExpression tupleExpression = new TupleExpression();
+ List<LocalVariableReference> vars = new ArrayList<LocalVariableReference>();
+ for (Clause c : clauseList) {
+ for (LocalVariableBinding b : c.getRangeVariables()) {
+ vars.add(new LocalVariableReference(b));
+ }
+ }
+ tupleExpression.setVariables(vars);
+ List sortSpecList;
+ t.setState(Tokenizer.BARE_NAME_STATE);
+ nextToken();
+ if (!isKeyword("by")) {
+ grumble("'order' must be followed by 'by'");
+ }
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ sortSpecList = parseSortDefinition();
+ SortKeyDefinition[] keys = new SortKeyDefinition[sortSpecList.size()];
+ for (int i = 0; i < keys.length; i++) {
+ SortSpec spec = (SortSpec) sortSpecList.get(i);
+ SortKeyDefinition key = new SortKeyDefinition();
+ key.setSortKey(((SortSpec) sortSpecList.get(i)).sortKey, false);
+ key.setOrder(new StringLiteral(spec.ascending ? "ascending" : "descending"));
+ key.setEmptyLeast(spec.emptyLeast);
+
+ if (spec.collation != null) {
+ final StringCollator comparator = env.getCollation(spec.collation);
+ if (comparator == null) {
+ grumble("Unknown collation '" + spec.collation + '\'', "XQST0076");
+ }
+ key.setCollation(comparator);
+ }
+ keys[i] = key;
+ }
+ OrderByClause clause = new OrderByClause(keys, tupleExpression);
+ clauseList.add(clause);
+ } else {
+ break;
+ }
+ setLocation(clauseList.get(clauseList.size() - 1), offset);
+ }
+
+ int returnOffset = t.currentTokenStartOffset;
+ expect(Token.RETURN);
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ Expression returnExpression = parseExprSingle();
+ returnExpression = makeTracer(returnOffset, returnExpression, Location.RETURN_EXPRESSION, null);
+
+ // undeclare all the range variables
+
+ for (int i = clauseList.size() - 1; i >= 0; i--) {
+ Clause clause = clauseList.get(i);
+ for (int n = 0; n < clause.getRangeVariables().length; n++) {
+ undeclareRangeVariable();
+ }
+ }
+
+ if (codeInjector != null) {
+ List<Clause> expandedList = new ArrayList<Clause>(clauseList.size() * 2);
+ expandedList.add(clauseList.get(0));
+ for (int i = 1; i < clauseList.size(); i++) {
+ Clause extra = codeInjector.injectClause(
+ clauseList.get(i - 1),
+ env,
+ defaultContainer);
+ if (extra != null) {
+ expandedList.add(extra);
+ }
+ expandedList.add(clauseList.get(i));
+ }
+ Clause extra = codeInjector.injectClause(
+ clauseList.get(clauseList.size() - 1), env, defaultContainer);
+ if (extra != null) {
+ expandedList.add(extra);
+ }
+ clauseList = expandedList;
+ }
+
+ FLWORExpression expression = new FLWORExpression(clauseList, returnExpression);
+ setLocation(expression, exprOffset);
+ return expression;
+
+ }
+
+ /**
+ * Make a LetExpression. This returns an ordinary LetExpression if tracing is off, and an EagerLetExpression
+ * if tracing is on. This is so that trace events occur in an order that the user can follow.
+ *
+ * @return the constructed "let" expression
+ */
+
+ /*@NotNull*/
+ protected LetExpression makeLetExpression() {
+ if (env.getConfiguration().isCompileWithTracing()) {
+ return new EagerLetExpression();
+ } else {
+ return new LetExpression();
+ }
+ }
+
+ /**
+ * Parse a ForClause.
+ * <p/>
+ * [42] ForClause ::= "for" ForBinding ("," ForBinding)*
+ * [42a] ForBinding ::= "$" VarName TypeDeclaration? ("allowing" "empty")? PositionalVar? "in" ExprSingle
+ * </p>
+ *
+ * @param clauseList - the components of the parsed ForClause are appended to the
+ * supplied list
+ * @throws XPathException if parsing fails
+ */
+ private void parseForClause(/*@NotNull*/ List<Clause> clauseList) throws XPathException {
+ boolean first = true;
+ do {
+ ForClause clause = new ForClause();
+ if (first) {
+ //clause.offset = t.currentTokenStartOffset;
+ }
+ clauseList.add(clause);
+ nextToken();
+ if (first) {
+ first = false;
+ } else {
+ //clause.offset = t.currentTokenStartOffset;
+ }
+ expect(Token.DOLLAR);
+ nextToken();
+ expect(Token.NAME);
+ StructuredQName varQName = makeStructuredQName(t.currentTokenValue, "");
+ SequenceType type = SequenceType.SINGLE_ITEM;
+ nextToken();
+
+ boolean explicitType = false;
+ if (t.currentToken == Token.AS) {
+ explicitType = true;
+ nextToken();
+ type = parseSequenceType();
+ }
+
+ //ForExpression v = new ForExpression();
+ boolean allowingEmpty = false;
+ if (isKeyword("allowing")) {
+ if (!XQUERY30.equals(queryVersion)) {
+ grumble("'allowing empty' requires XQuery 3.0 to be enabled");
+ }
+ allowingEmpty = true;
+ clause.setAllowingEmpty(true);
+ if (!explicitType) {
+ type = SequenceType.OPTIONAL_ITEM;
+ }
+ nextToken();
+ if (!isKeyword("empty")) {
+ grumble("After 'allowing', expected 'empty'");
+ }
+ nextToken();
+ }
+
+ if (explicitType && !allowingEmpty && type.getCardinality() != StaticProperty.EXACTLY_ONE) {
+ warning("Occurrence indicator on singleton range variable has no effect");
+ type = SequenceType.makeSequenceType(type.getPrimaryType(), StaticProperty.EXACTLY_ONE);
+ }
+
+ LocalVariableBinding binding = new LocalVariableBinding(varQName, type);
+ clause.setRangeVariable(binding);
+
+ if (isKeyword("at")) {
+ nextToken();
+ expect(Token.DOLLAR);
+ nextToken();
+ expect(Token.NAME);
+ StructuredQName posQName = makeStructuredQName(t.currentTokenValue, "");
+ if (!scanOnly && posQName.equals(varQName)) {
+ grumble("The two variables declared in a single 'for' clause must have different names", "XQST0089");
+ }
+ LocalVariableBinding pos = new LocalVariableBinding(posQName, SequenceType.SINGLE_INTEGER);
+ clause.setPositionVariable(pos);
+ nextToken();
+ }
+ expect(Token.IN);
+ nextToken();
+ clause.setSequence(parseExprSingle());
+ declareRangeVariable(clause.getRangeVariable());
+ if (clause.getPositionVariable() != null) {
+ declareRangeVariable(clause.getPositionVariable());
+ }
+ if (allowingEmpty) {
+ checkForClauseAllowingEmpty(clause);
+ }
+ } while (t.currentToken == Token.COMMA);
+ }
+
+ /**
+ * Check a ForClause for an "outer for"
+ *
+ * @param clause the clause to be checked
+ * @throws XPathException if invalid
+ */
+
+ protected void checkForClauseAllowingEmpty(ForClause clause) throws XPathException {
+ // overridden in subclass
+ }
+
+ /**
+ * Parse a LetClause.
+ * <p/>
+ * [44] LetClause ::= <"let" "$"> VarName TypeDeclaration? ":=" ExprSingle
+ * ("," "$" VarName TypeDeclaration? ":=" ExprSingle)*
+ * </p>
+ *
+ * @param clauseList - the components of the parsed LetClause are appended to the
+ * supplied list
+ * @throws XPathException if a static error is found
+ */
+ private void parseLetClause(/*@NotNull*/ List<Clause> clauseList) throws XPathException {
+ boolean first = true;
+ do {
+ LetClause clause = new LetClause();
+ if (first) {
+ //clause.offset = t.currentTokenStartOffset;
+ }
+ clauseList.add(clause);
+ nextToken();
+ if (first) {
+ first = false;
+ } else {
+ //clause.offset = t.currentTokenStartOffset;
+ }
+ expect(Token.DOLLAR);
+ nextToken();
+ expect(Token.NAME);
+ String var = t.currentTokenValue;
+
+ StructuredQName varQName = makeStructuredQName(var, "");
+ SequenceType type = SequenceType.ANY_SEQUENCE;
+ nextToken();
+ if (t.currentToken == Token.AS) {
+ nextToken();
+ type = parseSequenceType();
+ }
+ LocalVariableBinding v = new LocalVariableBinding(varQName, type);
+
+ expect(Token.ASSIGN);
+ nextToken();
+ clause.setSequence(parseExprSingle());
+ clause.setRangeVariable(v);
+ declareRangeVariable(v);
+ } while (t.currentToken == Token.COMMA);
+ }
+
+ /**
+ * Parse a count clause.
+ * Not supported in 1.0; subclassed in the XQuery 3.0 parser
+ *
+ * @param clauseList the list of clauses for the expression, to which this
+ * clause will be added
+ * @throws XPathException if a static error is found
+ */
+ protected void parseCountClause(List<Clause> clauseList) throws XPathException {
+ grumble("'count' is not supported in XQuery 1.0");
+ }
+
+ /**
+ * Parse a Group By clause.
+ * Not supported in 1.0; subclassed in the XQuery 3.0 parser
+ *
+ * @param clauseList the list of clauses for the expression, to which this
+ * clause will be added
+ * @throws XPathException if a static error is found
+ */
+ protected void parseGroupByClause(List<Clause> clauseList) throws XPathException {
+ grumble("'group by' is not supported in XQuery 1.0");
+ }
+
+ /**
+ * Parse a tumbling or sliding window clause.
+ * Not supported in 1.0; subclassed in the XQuery 3.0 parser
+ *
+ * @param clauseList the list of clauses for the expression, to which this
+ * clause will be added
+ * @throws XPathException if a static error is found
+ */
+ protected void parseWindowClause(List<Clause> clauseList) throws XPathException {
+ grumble("Sliding and tumbling windows are not supported in XQuery 1.0");
+ }
+
+ /**
+ * Make a string-join expression that concatenates the string-values of items in
+ * a sequence with intervening spaces. This may be simplified later as a result
+ * of type-checking.
+ *
+ * @param exp the base expression, evaluating to a sequence
+ * @param env the static context
+ * @return a call on string-join to create a string containing the
+ * representations of the items in the sequence separated by spaces.
+ */
+
+ /*@Nullable*/
+ public static Expression makeStringJoin(Expression exp, /*@NotNull*/ StaticContext env) {
+
+ exp = Atomizer.makeAtomizer(exp);
+ final TypeHierarchy th = env.getConfiguration().getTypeHierarchy();
+ ItemType t = exp.getItemType(th);
+ if (!t.equals(BuiltInAtomicType.STRING) && !t.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ exp = new AtomicSequenceConverter(exp, BuiltInAtomicType.STRING);
+ ((AtomicSequenceConverter) exp).allocateConverter(env.getConfiguration(), false);
+ }
+
+ if (exp.getCardinality() == StaticProperty.EXACTLY_ONE) {
+ return exp;
+ } else {
+ StringJoin fn = (StringJoin) SystemFunctionCall.makeSystemFunction(
+ "string-join", new Expression[]{exp, new StringLiteral(StringValue.SINGLE_SPACE)});
+ ExpressionTool.copyLocationInfo(exp, fn);
+ return fn;
+ }
+ }
+
+ /**
+ * Parse the "order by" clause.
+ * [46] OrderByClause ::= (<"order" "by"> | <"stable" "order" "by">) OrderSpecList
+ * [47] OrderSpecList ::= OrderSpec ("," OrderSpec)*
+ * [48] OrderSpec ::= ExprSingle OrderModifier
+ * [49] OrderModifier ::= ("ascending" | "descending")?
+ * (<"empty" "greatest"> | <"empty" "least">)?
+ * ("collation" StringLiteral)?
+ *
+ * @return a list of sort specifications (SortSpec), one per sort key
+ * @throws XPathException if parsing fails
+ */
+ /*@NotNull*/
+ private List parseSortDefinition() throws XPathException {
+ List<SortSpec> sortSpecList = new ArrayList<SortSpec>(5);
+ while (true) {
+ SortSpec sortSpec = new SortSpec();
+ sortSpec.sortKey = parseExprSingle();
+ sortSpec.ascending = true;
+ sortSpec.emptyLeast = ((QueryModule) env).isEmptyLeast();
+ sortSpec.collation = env.getDefaultCollationName();
+ //t.setState(t.BARE_NAME_STATE);
+ if (isKeyword("ascending")) {
+ nextToken();
+ } else if (isKeyword("descending")) {
+ sortSpec.ascending = false;
+ nextToken();
+ }
+ if (isKeyword("empty")) {
+ nextToken();
+ if (isKeyword("greatest")) {
+ sortSpec.emptyLeast = false;
+ nextToken();
+ } else if (isKeyword("least")) {
+ sortSpec.emptyLeast = true;
+ nextToken();
+ } else {
+ grumble("'empty' must be followed by 'greatest' or 'least'");
+ }
+ }
+ if (isKeyword("collation")) {
+ sortSpec.collation = readCollationName();
+ }
+ sortSpecList.add(sortSpec);
+ if (t.currentToken == Token.COMMA) {
+ nextToken();
+ } else {
+ break;
+ }
+ }
+ return sortSpecList;
+ }
+
+ protected String readCollationName() throws XPathException {
+ nextToken();
+ expect(Token.STRING_LITERAL);
+ String collationName = URILiteral(t.currentTokenValue);
+ URI collationURI;
+ try {
+ collationURI = new URI(collationName);
+ if (!collationURI.isAbsolute()) {
+ URI base = new URI(env.getBaseURI());
+ collationURI = base.resolve(collationURI);
+ collationName = collationURI.toString();
+ }
+ } catch (URISyntaxException err) {
+ grumble("Collation name '" + collationName + "' is not a valid URI", "XQST0046");
+ collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
+ }
+ nextToken();
+ return collationName;
+ }
+
+ private static class SortSpec {
+ /*@Nullable*/ public Expression sortKey;
+ public boolean ascending;
+ public boolean emptyLeast;
+ public String collation;
+ }
+
+ /**
+ * Parse a Typeswitch Expression.
+ * This construct is XQuery-only.
+ * TypeswitchExpr ::=
+ * "typeswitch" "(" Expr ")"
+ * CaseClause+
+ * "default" ("$" VarName)? "return" ExprSingle
+ * CaseClause ::=
+ * "case" ("$" VarName "as")? SequenceType "return" ExprSingle
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ /*@NotNull*/
+ protected Expression parseTypeswitchExpression() throws XPathException {
+
+ // On entry, the "(" has already been read
+ int offset = t.currentTokenStartOffset;
+ nextToken();
+ Expression operand = parseExpression();
+ List<List<SequenceType>> types = new ArrayList<List<SequenceType>>(10);
+ List<Expression> actions = new ArrayList<Expression>(10);
+ expect(Token.RPAR);
+ nextToken();
+
+ // The code generated takes the form:
+ // let $zzz := operand return
+ // if ($zzz instance of t1) then action1
+ // else if ($zzz instance of t2) then action2
+ // else default-action
+ //
+ // If a variable is declared in a case clause or default clause,
+ // then "action-n" takes the form
+ // let $v as type := $zzz return action-n
+
+ // we were generating "let $v as type := $zzz return action-n" but this gives a compile time error if
+ // there's a case clause that specifies an impossible type.
+
+ LetExpression outerLet = makeLetExpression();
+ outerLet.setRequiredType(SequenceType.ANY_SEQUENCE);
+ outerLet.setVariableQName(new StructuredQName("zz", NamespaceConstant.SAXON, "zz_typeswitchVar"));
+ outerLet.setSequence(operand);
+
+ while (t.currentToken == Token.CASE) {
+ int caseOffset = t.currentTokenStartOffset;
+ List<SequenceType> typeList;
+ Expression action;
+ nextToken();
+ if (t.currentToken == Token.DOLLAR) {
+ nextToken();
+ expect(Token.NAME);
+ final String var = t.currentTokenValue;
+ final StructuredQName varQName = makeStructuredQName(var, "");
+ nextToken();
+ expect(Token.AS);
+ nextToken();
+ typeList = parseSequenceTypeList();
+ action = makeTracer(caseOffset,
+ parseTypeswitchReturnClause(varQName, outerLet),
+ Location.CASE_EXPRESSION,
+ varQName);
+ if (action instanceof TraceExpression) {
+ ((TraceExpression) action).setProperty("type", typeList.get(0).toString());
+ }
+
+ } else {
+ typeList = parseSequenceTypeList();
+ action = makeTracer(caseOffset, parseExprSingle(), Location.CASE_EXPRESSION, null);
+ if (action instanceof TraceExpression) {
+ ((TraceExpression) action).setProperty("type", typeList.get(0).toString());
+ }
+ }
+ if (typeList.size() > 1 && env.getXPathLanguageLevel() != DecimalValue.THREE) {
+ grumble("Typeswitch with a list of types separated by '|' requires XQuery 3.0 to be enabled");
+ }
+ types.add(typeList);
+ actions.add(action);
+ }
+ if (types.isEmpty()) {
+ grumble("At least one case clause is required in a typeswitch");
+ }
+ expect(Token.DEFAULT);
+ final int defaultOffset = t.currentTokenStartOffset;
+ nextToken();
+ Expression defaultAction;
+ if (t.currentToken == Token.DOLLAR) {
+ nextToken();
+ expect(Token.NAME);
+ final String var = t.currentTokenValue;
+ final StructuredQName varQName = makeStructuredQName(var, "");
+ nextToken();
+ expect(Token.RETURN);
+ nextToken();
+ defaultAction = makeTracer(defaultOffset,
+ parseTypeswitchReturnClause(varQName, outerLet),
+ Location.DEFAULT_EXPRESSION,
+ varQName);
+ } else {
+ t.treatCurrentAsOperator();
+ expect(Token.RETURN);
+ nextToken();
+ defaultAction = makeTracer(defaultOffset, parseExprSingle(), Location.DEFAULT_EXPRESSION, null);
+ }
+
+ Expression lastAction = defaultAction;
+ // Note, the ragged "choose" later gets flattened into a single-level choose, saving stack space
+ for (int i = types.size() - 1; i >= 0; i--) {
+ final LocalVariableReference var = new LocalVariableReference(outerLet);
+ setLocation(var);
+ Expression ioe = new InstanceOfExpression(var, types.get(i).get(0));
+ for (int j = 1; j < types.get(i).size(); j++) {
+ ioe = new OrExpression(ioe, new InstanceOfExpression(var, types.get(i).get(j)));
+ }
+ setLocation(ioe);
+ final Expression ife = Choose.makeConditional(ioe, actions.get(i), lastAction);
+ setLocation(ife);
+ lastAction = ife;
+ }
+ outerLet.setAction(lastAction);
+ return makeTracer(offset, outerLet, Location.TYPESWITCH_EXPRESSION, null);
+ }
+
+ /*@NotNull*/
+ private List<SequenceType> parseSequenceTypeList() throws XPathException {
+ List<SequenceType> typeList = new ArrayList<SequenceType>();
+ while (true) {
+ SequenceType type = parseSequenceType();
+ typeList.add(type);
+ t.treatCurrentAsOperator();
+ if (t.currentToken == Token.UNION) {
+ nextToken();
+ } else {
+ break;
+ }
+ }
+ expect(Token.RETURN);
+ nextToken();
+ return typeList;
+ }
+
+ /*@NotNull*/
+ private Expression parseTypeswitchReturnClause(StructuredQName varQName, LetExpression outerLet)
+ throws XPathException {
+ Expression action;
+// t.treatCurrentAsOperator();
+// expect(Token.RETURN);
+// nextToken();
+
+ LetExpression innerLet = makeLetExpression();
+ innerLet.setRequiredType(SequenceType.ANY_SEQUENCE);
+ innerLet.setVariableQName(varQName);
+ innerLet.setSequence(new LocalVariableReference(outerLet));
+
+ declareRangeVariable(innerLet);
+ action = parseExprSingle();
+ undeclareRangeVariable();
+
+ innerLet.setAction(action);
+ return innerLet;
+// if (Literal.isEmptySequence(action)) {
+// // The purpose of simplifying this now is that () is allowed in a branch even in XQuery Update when
+// // other branches of the typeswitch are updating.
+// return action;
+// } else {
+// return innerLet;
+// }
+ }
+
+ /**
+ * Parse a Validate Expression.
+ * This construct is XQuery-only. The syntax allows:
+ * validate mode? { Expr }
+ * mode ::= "strict" | "lax"
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ /*@NotNull*/
+ protected Expression parseValidateExpression() throws XPathException {
+ int offset = t.currentTokenStartOffset;
+ int mode = Validation.STRICT;
+ boolean foundCurly = false;
+ SchemaType requiredType = null;
+ ((QueryModule)env).useFeature(LanguageFeature.TYPED_DATA_SCHEMAS, "validate expression", makeLocator());
+ env.getConfiguration().checkLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY, "validate expression");
+ switch (t.currentToken) {
+ case Token.VALIDATE_STRICT:
+ mode = Validation.STRICT;
+ nextToken();
+ break;
+ case Token.VALIDATE_LAX:
+ mode = Validation.LAX;
+ nextToken();
+ break;
+ case Token.VALIDATE_TYPE:
+ if (XQUERY10.equals(queryVersion)) {
+ grumble("validate-as-type requires XQuery 3.0");
+ }
+ mode = Validation.BY_TYPE;
+ nextToken();
+ expect(Token.KEYWORD_CURLY);
+ if (!nameChecker.isQName(t.currentTokenValue)) {
+ grumble("Schema type name expected after 'validate type");
+ }
+ int typeCode = makeNameCode(t.currentTokenValue, true);
+ requiredType = env.getConfiguration().getSchemaType(typeCode & NamePool.FP_MASK);
+ if (requiredType == null) {
+ grumble("Unknown schema type " + t.currentTokenValue, "XQST0104");
+ }
+ foundCurly = true;
+ break;
+ case Token.KEYWORD_CURLY:
+ if (t.currentTokenValue.equals("validate")) {
+ mode = Validation.STRICT;
+ } else {
+ throw new AssertionError("shouldn't be parsing a validate expression");
+ }
+ foundCurly = true;
+ }
+
+ if (!foundCurly) {
+ expect(Token.LCURLY);
+ }
+ nextToken();
+
+ Expression exp = parseExpression();
+ if (exp instanceof ParentNodeConstructor) {
+ ((ParentNodeConstructor) exp).setValidationAction(mode, (mode == Validation.BY_TYPE ? requiredType : null));
+ } else {
+ // the expression must return a single element or document node. The type-
+ // checking machinery can't handle a union type, so we just check that it's
+ // a node for now. Because we are reusing XSLT copy-of code, we need
+ // an ad-hoc check that the node is of the right kind.
+
+ // below code moved to XQuery-specific path in CopyOf
+// try {
+// RoleLocator role = new RoleLocator(RoleLocator.TYPE_OP, "validate", 0);
+// role.setErrorCode("XQTY0030");
+// setLocation(exp);
+// exp = TypeChecker.staticTypeCheck(exp,
+// SequenceType.SINGLE_NODE,
+// false,
+// role, ExpressionVisitor.make(env, getExecutable()));
+// } catch (XPathException err) {
+// grumble(err.getMessage(), err.getErrorCodeQName(), -1);
+// }
+ exp = new CopyOf(exp, true, mode, requiredType, true);
+ setLocation(exp);
+ ((CopyOf) exp).setRequireDocumentOrElement(true);
+ }
+
+ expect(Token.RCURLY);
+ t.lookAhead(); // always done manually after an RCURLY
+ nextToken();
+ return makeTracer(offset, exp, Location.VALIDATE_EXPRESSION, null);
+ }
+
+ /**
+ * Parse an Extension Expression.
+ * Syntax: "(#" QName arbitrary-text "#)")+ "{" expr? "}"
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ /*@NotNull*/
+ protected Expression parseExtensionExpression() throws XPathException {
+ SchemaType requiredType = null;
+ CharSequence trimmed = Whitespace.removeLeadingWhitespace(t.currentTokenValue);
+ int c = 0;
+ int len = trimmed.length();
+ while (c < len && " \t\r\n".indexOf(trimmed.charAt(c)) < 0) {
+ c++;
+ }
+ String qname = trimmed.subSequence(0, c).toString();
+ String pragmaContents = "";
+ while (c < len && " \t\r\n".indexOf(trimmed.charAt(c)) >= 0) {
+ c++;
+ }
+ if (c < len) {
+ pragmaContents = trimmed.subSequence(c, len).toString();
+ }
+
+ boolean validateType = false;
+ boolean streaming = false;
+ String uri;
+ String localName;
+ if (qname.startsWith("Q{")) {
+ StructuredQName sq = parseExtendedQName(qname, nameChecker);
+ uri = sq.getURI();
+ localName = sq.getLocalPart();
+ } else if (!nameChecker.isQName(qname)) {
+ grumble("First token in pragma must be a valid QName, terminated by whitespace");
+ return new ErrorExpression();
+ } else {
+ int nameCode = makeNameCode(qname, false);
+ uri = env.getNamePool().getURI(nameCode);
+ localName = env.getNamePool().getLocalName(nameCode);
+ }
+ if (uri.equals(NamespaceConstant.SAXON)) {
+ if (localName.equals("validate-type")) {
+ if (!env.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY)) {
+ warning("Ignoring saxon:validate-type. To use this feature " +
+ "you need the Saxon-EE processor from http://www.saxonica.com/");
+ } else {
+ String typeName = Whitespace.trim(pragmaContents);
+ if (!nameChecker.isQName(typeName)) {
+ grumble("Schema type name expected in saxon:validate-type pragma: found " + Err.wrap(typeName));
+ }
+ assert typeName != null;
+ int typeCode = makeNameCode(typeName, true);
+ requiredType = env.getConfiguration().getSchemaType(typeCode & NamePool.FP_MASK);
+ if (requiredType == null) {
+ grumble("Unknown schema type " + typeName);
+ }
+ validateType = true;
+ }
+ } else if (localName.equals("stream")) {
+ if (!env.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY)) {
+ warning("Ignoring saxon:stream. To use this feature " +
+ "you need the Saxon-EE processor from http://www.saxonica.com/");
+ } else {
+ streaming = true;
+ }
+ } else {
+ warning("Ignored pragma " + qname + " (unrecognized Saxon pragma)");
+ }
+ } else if (uri.length() == 0) {
+ grumble("The QName identifying an option declaration must be prefixed", "XPST0081");
+ }
+
+ nextToken();
+ Expression expr;
+ if (t.currentToken == Token.PRAGMA) {
+ expr = parseExtensionExpression();
+ } else {
+ expect(Token.LCURLY);
+ nextToken();
+ if (t.currentToken == Token.RCURLY) {
+ t.lookAhead(); // always done manually after an RCURLY
+ nextToken();
+ grumble("Unrecognized pragma, with no fallback expression", "XQST0079");
+ }
+ expr = parseExpression();
+ expect(Token.RCURLY);
+ t.lookAhead(); // always done manually after an RCURLY
+ nextToken();
+ }
+ if (validateType) {
+ if (expr instanceof ParentNodeConstructor) {
+ ((ParentNodeConstructor) expr).setValidationAction(Validation.BY_TYPE, requiredType);
+ return expr;
+ } else if (expr instanceof AttributeCreator) {
+ if (!(requiredType instanceof SimpleType)) {
+ grumble("The type used for validating an attribute must be a simple type");
+ }
+
+ //noinspection ConstantConditions
+ ((AttributeCreator) expr).setSchemaType((SimpleType) requiredType);
+ ((AttributeCreator) expr).setValidationAction(Validation.BY_TYPE);
+ return expr;
+ } else {
+ CopyOf copy = new CopyOf(expr, true, Validation.BY_TYPE, requiredType, true);
+ copy.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), t.getLineNumber()));
+ return copy;
+ }
+ } else if (streaming) {
+ CopyOf copy = new CopyOf(expr, true, Validation.PRESERVE, null, true);
+ copy.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), t.getLineNumber()));
+ copy.setReadOnce(true);
+ return copy;
+ } else {
+ return expr;
+ }
+ }
+
+ /**
+ * Convert an EQName in format Q{uri}local to a StructuredQName. We have already established
+ * that the string starts with "Q{"
+ *
+ * @param in the string to be checked/parsed
+ * @param checker the name checker (varies according to the XML version supported)
+ * @return the expanded QName
+ * @throws XPathException if the input syntax is incorrect
+ */
+
+ /*@NotNull*/
+ private static StructuredQName parseExtendedQName(/*@NotNull*/ String in, /*@NotNull*/ NameChecker checker) throws XPathException {
+ if (in.length() < 4) {
+ invalidExtendedQName(in, "too short");
+ }
+ int end = in.indexOf('}', 1);
+ if (end < 0) {
+ invalidExtendedQName(in, "no closing '}'");
+ }
+ if (end + 1 >= in.length()) {
+ invalidExtendedQName(in, "no local name after URI");
+ }
+ String uri = in.substring(2, end);
+ String localName = in.substring(end + 2);
+ if (!checker.isValidNCName(localName)) {
+ invalidExtendedQName(in, "invalid local name after colon");
+ }
+ return new StructuredQName("", uri, localName);
+ }
+
+ private static void invalidExtendedQName(String in, String msg) throws XPathException {
+ throw new XPathException("Invalid EQName " + in + " - " + msg, "XPST0003");
+ }
+
+ /**
+ * Parse a node constructor. This is allowed only in XQuery. This method handles
+ * both the XML-like "direct" constructors, and the XQuery-based "computed"
+ * constructors.
+ *
+ * @return an Expression for evaluating the parsed constructor
+ * @throws XPathException in the event of a syntax error.
+ */
+
+ /*@NotNull*/
+ protected Expression parseConstructor() throws XPathException {
+ int offset = t.currentTokenStartOffset;
+ switch (t.currentToken) {
+ case Token.TAG:
+ Expression tag = parsePseudoXML(false);
+ lookAhead();
+ t.setState(Tokenizer.OPERATOR_STATE);
+ nextToken();
+ return tag;
+ case Token.KEYWORD_CURLY:
+ String nodeKind = t.currentTokenValue;
+ if (nodeKind.equals("validate")) {
+ grumble("A validate expression is not allowed within a path expression");
+
+ //if (nodeKind.equals("validate")) {
+ // this allows a validate{} expression to appear as an operand of '/', which the grammar does not allow
+ // return parseValidateExpression();
+ } else if (nodeKind.equals("ordered") || nodeKind.equals("unordered")) {
+ // these are currently no-ops in Saxon
+ nextToken();
+ Expression content = parseExpression();
+ expect(Token.RCURLY);
+ lookAhead(); // must be done manually after an RCURLY
+ nextToken();
+ return content;
+ } else if (nodeKind.equals("document")) {
+ return parseDocumentConstructor(offset);
+
+ } else if ("element".equals(nodeKind)) {
+ return parseComputedElementConstructor(offset);
+
+ } else if ("attribute".equals(nodeKind)) {
+ return parseComputedAttributeConstructor(offset);
+
+ } else if ("text".equals(nodeKind)) {
+ return parseTextNodeConstructor(offset);
+
+ } else if ("comment".equals(nodeKind)) {
+ return parseCommentConstructor(offset);
+
+ } else if ("processing-instruction".equals(nodeKind)) {
+ return parseProcessingInstructionConstructor(offset);
+
+ } else if ("namespace".equals(nodeKind)) {
+ return parseNamespaceConstructor(offset);
+
+ } else {
+ grumble("Unrecognized node constructor " + t.currentTokenValue + "{}");
+
+ }
+ case Token.ELEMENT_QNAME:
+ return parseNamedElementConstructor(offset);
+
+ case Token.ATTRIBUTE_QNAME:
+ return parseNamedAttributeConstructor(offset);
+
+ case Token.NAMESPACE_QNAME:
+ return parseNamedNamespaceConstructor(offset);
+
+ case Token.PI_QNAME:
+ return parseNamedProcessingInstructionConstructor(offset);
+ }
+ return new ErrorExpression();
+ }
+
+ /**
+ * Parse document constructor: document {...}
+ *
+ * @param offset the location in the source query
+ * @return the document constructor instruction
+ * @throws XPathException if parsing fails
+ */
+
+ /*@NotNull*/
+ private Expression parseDocumentConstructor(int offset) throws XPathException {
+ nextToken();
+ Expression content = parseExpression();
+ expect(Token.RCURLY);
+ lookAhead(); // must be done manually after an RCURLY
+ nextToken();
+ DocumentInstr doc = new DocumentInstr(false, null, env.getBaseURI());
+ if (!((QueryModule) env).isPreserveNamespaces()) {
+ content = new CopyOf(content, false, Validation.PRESERVE, null, true);
+ }
+ doc.setValidationAction(((QueryModule) env).getConstructionMode(), null);
+ doc.setContentExpression(content);
+ setLocation(doc, offset);
+ return doc;
+ }
+
+ /**
+ * Parse an element constructor of the form
+ * element {expr} {expr}
+ *
+ * @param offset location of the expression in the source query
+ * @return the compiled instruction
+ * @throws XPathException if a static error is found
+ */
+
+ /*@NotNull*/
+ private Expression parseComputedElementConstructor(int offset) throws XPathException {
+ nextToken();
+ // get the expression that yields the element name
+ Expression name = parseExpression();
+ expect(Token.RCURLY);
+ lookAhead(); // must be done manually after an RCURLY
+ nextToken();
+ expect(Token.LCURLY);
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ Expression content = null;
+ if (t.currentToken != Token.RCURLY) {
+ // get the expression that yields the element content
+ content = parseExpression();
+ // if the child expression creates another element,
+ // suppress validation, as the parent already takes care of it
+ if (content instanceof ElementCreator && ((ElementCreator) content).getSchemaType() == null) {
+ // TODO: does this cover all cases? E.g. the child element might match a wildcard in the parent declaration.
+ ((ElementCreator) content).setValidationAction(Validation.PRESERVE, null);
+ }
+ expect(Token.RCURLY);
+ }
+ lookAhead(); // done manually after an RCURLY
+ nextToken();
+
+ Instruction inst;
+ if (name instanceof Literal) {
+ GroundedValue vName = ((Literal) name).getValue();
+ // if element name is supplied as a literal, treat it like a direct element constructor
+ NodeName elemName;
+ if (vName instanceof StringValue && !(vName instanceof AnyURIValue)) {
+ String lex = ((StringValue) vName).getStringValue();
+ try {
+ elemName = makeNodeName(lex, true);
+ elemName.allocateNameCode(env.getNamePool());
+ } catch (XPathException staticError) {
+ String code = staticError.getErrorCodeLocalPart();
+ if ("XPST0008".equals(code) || "XPST0081".equals(code)) {
+ staticError.setErrorCode("XQDY0074");
+ }
+ staticError.setLocator(makeLocator());
+ staticError.setIsStaticError(false);
+ return new ErrorExpression(staticError);
+ } catch (QNameException qerr) {
+ grumble("Invalid QName in element constructor: " + lex, "XQDY0074", offset);
+ return new ErrorExpression();
+ }
+ } else if (vName instanceof QualifiedNameValue) {
+ String uri = ((QualifiedNameValue) vName).getNamespaceURI();
+ elemName = new FingerprintedQName("", uri, ((QualifiedNameValue) vName).getLocalName());
+ elemName.allocateNameCode(env.getNamePool());
+ } else {
+ grumble("Element name must be either a string or a QName", "XPTY0004", offset);
+ return new ErrorExpression();
+ }
+ inst = new FixedElement(elemName,
+ ((QueryModule) env).getActiveNamespaceCodes(),
+ ((QueryModule) env).isInheritNamespaces(),
+ null,
+ ((QueryModule) env).getConstructionMode());
+ ((FixedElement) inst).setBaseURI(env.getBaseURI());
+ if (content == null) {
+ content = Literal.makeEmptySequence();
+ }
+ if (!((QueryModule) env).isPreserveNamespaces()) {
+ content = new CopyOf(content, false, Validation.PRESERVE, null, true);
+ }
+ ((FixedElement) inst).setContentExpression(content);
+ setLocation(inst, offset);
+ //makeContentConstructor(content, (InstructionWithChildren) inst, offset);
+ return makeTracer(offset, inst, Location.LITERAL_RESULT_ELEMENT, elemName.getStructuredQName());
+ } else {
+ // it really is a computed element constructor: save the namespace context
+ NamespaceResolver ns = new NamespaceResolverWithDefault(
+ env.getNamespaceResolver(),
+ env.getDefaultElementNamespace());
+ inst = new ComputedElement(name, null, ns, null,
+ ((QueryModule) env).getConstructionMode(),
+ ((QueryModule) env).isInheritNamespaces(),
+ true);
+ setLocation(inst);
+ if (content == null) {
+ content = Literal.makeEmptySequence();
+ }
+ if (!((QueryModule) env).isPreserveNamespaces()) {
+ content = new CopyOf(content, false, Validation.PRESERVE, null, true);
+ }
+ ((ComputedElement) inst).setContentExpression(content);
+ setLocation(inst, offset);
+ //makeContentConstructor(content, (InstructionWithChildren) inst, offset);
+ return makeTracer(offset, inst, StandardNames.XSL_ELEMENT, null);
+ }
+ }
+
+ /**
+ * Parse an element constructor of the form
+ * element qname { expr }
+ *
+ * @param offset the position in the source query
+ * @return the compiled instruction
+ * @throws XPathException if parsing fails
+ */
+
+ private Expression parseNamedElementConstructor(int offset) throws XPathException {
+ int nameCode = makeNameCode(t.currentTokenValue, true);
+ Expression content = null;
+ nextToken();
+ if (t.currentToken != Token.RCURLY) {
+ content = parseExpression();
+ expect(Token.RCURLY);
+ }
+ lookAhead(); // after an RCURLY
+ nextToken();
+ FixedElement el2 = new FixedElement(new CodedName(nameCode, env.getNamePool()),
+ ((QueryModule) env).getActiveNamespaceCodes(),
+ ((QueryModule) env).isInheritNamespaces(),
+ null,
+ ((QueryModule) env).getConstructionMode());
+ el2.setBaseURI(env.getBaseURI());
+ setLocation(el2, offset);
+ if (content == null) {
+ content = Literal.makeEmptySequence();
+ }
+ if (!((QueryModule) env).isPreserveNamespaces()) {
+ content = new CopyOf(content, false, Validation.PRESERVE, null, true);
+ }
+ el2.setContentExpression(content);
+ //makeContentConstructor(content, el2, offset);
+ return makeTracer(offset, el2, Location.LITERAL_RESULT_ELEMENT,
+ env.getNamePool().getStructuredQName(nameCode));
+ }
+
+ /**
+ * Parse an attribute constructor of the form
+ * attribute {expr} {expr}
+ *
+ * @param offset position of the expression in the input
+ * @return the compiled instruction
+ * @throws XPathException if a static error is encountered
+ */
+
+ /*@NotNull*/
+ private Expression parseComputedAttributeConstructor(int offset) throws XPathException {
+ nextToken();
+ Expression name = parseExpression();
+ expect(Token.RCURLY);
+ lookAhead(); // must be done manually after an RCURLY
+ nextToken();
+ expect(Token.LCURLY);
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ Expression content = null;
+ if (t.currentToken != Token.RCURLY) {
+ content = parseExpression();
+ expect(Token.RCURLY);
+ }
+ lookAhead(); // after an RCURLY
+ nextToken();
+ if (name instanceof Literal) {
+ GroundedValue vName = ((Literal) name).getValue();
+ if (vName instanceof StringValue && !(vName instanceof AnyURIValue)) {
+ String lex = ((StringValue) vName).getStringValue();
+ if (lex.equals("xmlns") || lex.startsWith("xmlns:")) {
+ grumble("Cannot create a namespace using an attribute constructor", "XQDY0044", offset);
+ }
+ NodeName attributeName;
+ try {
+ attributeName = makeNodeName(lex, false);
+ } catch (XPathException staticError) {
+ String code = staticError.getErrorCodeLocalPart();
+ staticError.setLocator(makeLocator());
+ if ("XPST0008".equals(code) || "XPST0081".equals(code)) {
+ staticError.setErrorCode("XQDY0074");
+ }
+ throw staticError;
+ } catch (QNameException err) {
+ grumble("Invalid QName in attribute constructor: " + lex, "XQDY0074", offset);
+ return new ErrorExpression();
+ }
+ FixedAttribute fatt = new FixedAttribute(attributeName,
+ Validation.STRIP,
+ null);
+ fatt.setRejectDuplicates();
+ makeSimpleContent(content, fatt, offset);
+ return makeTracer(offset, fatt, StandardNames.XSL_ATTRIBUTE, null);
+ } else if (vName instanceof QNameValue) {
+ QNameValue qnv = (QNameValue) vName;
+ NodeName attributeName = new FingerprintedQName(
+ qnv.getPrefix(), qnv.getNamespaceURI(), qnv.getLocalName());
+ attributeName.allocateNameCode(env.getNamePool());
+ FixedAttribute fatt = new FixedAttribute(attributeName,
+ Validation.STRIP,
+ null);
+ fatt.setRejectDuplicates();
+ makeSimpleContent(content, fatt, offset);
+ return makeTracer(offset, fatt, StandardNames.XSL_ATTRIBUTE, null);
+ }
+ }
+ ComputedAttribute att = new ComputedAttribute(name,
+ null,
+ env.getNamespaceResolver(),
+ Validation.STRIP,
+ null,
+ true);
+ att.setRejectDuplicates();
+ makeSimpleContent(content, att, offset);
+ return makeTracer(offset, att, StandardNames.XSL_ATTRIBUTE, null);
+ }
+
+ /**
+ * Parse an attribute constructor of the form
+ * attribute name {expr}
+ *
+ * @param offset position of the expression in the source
+ * @return the parsed expression
+ * @throws XPathException if a static error is found
+ */
+
+ private Expression parseNamedAttributeConstructor(int offset) throws XPathException {
+ String warning = null;
+ if (t.currentTokenValue.equals("xmlns") || t.currentTokenValue.startsWith("xmlns:")) {
+ warning = "Cannot create a namespace declaration using an attribute constructor";
+ }
+ NodeName attNameCode;
+ try {
+ attNameCode = makeNodeName(t.currentTokenValue, false);
+ } catch (QNameException e) {
+ throw new XPathException(e);
+ }
+ Expression attContent = null;
+ nextToken();
+ if (t.currentToken != Token.RCURLY) {
+ attContent = parseExpression();
+ expect(Token.RCURLY);
+ }
+ lookAhead(); // after an RCURLY
+ nextToken();
+ if (warning == null) {
+ FixedAttribute att2 = new FixedAttribute(attNameCode,
+ Validation.STRIP,
+ null);
+ att2.setRejectDuplicates();
+ makeSimpleContent(attContent, att2, offset);
+ return makeTracer(offset, att2, Location.LITERAL_RESULT_ATTRIBUTE, attNameCode.getStructuredQName());
+ } else {
+ warning(warning);
+ XPathException err = new XPathException(warning, "XQDY0044");
+ return new ErrorExpression(err);
+ }
+ }
+
+ private Expression parseTextNodeConstructor(int offset) throws XPathException {
+ nextToken();
+ Expression value = parseExpression();
+ expect(Token.RCURLY);
+ lookAhead(); // after an RCURLY
+ nextToken();
+ Expression select = stringify(value, true, env.getConfiguration());
+ ValueOf vof = new ValueOf(select, false, true);
+ setLocation(vof, offset);
+ return makeTracer(offset, vof, StandardNames.XSL_TEXT, null);
+ }
+
+ private Expression parseCommentConstructor(int offset) throws XPathException {
+ nextToken();
+ Expression value = parseExpression();
+ expect(Token.RCURLY);
+ lookAhead(); // after an RCURLY
+ nextToken();
+ Comment com = new Comment();
+ makeSimpleContent(value, com, offset);
+ return makeTracer(offset, com, StandardNames.XSL_COMMENT, null);
+ }
+
+ /**
+ * Parse a processing instruction constructor of the form
+ * processing-instruction {expr} {expr}
+ *
+ * @param offset the position of the expression in the source query
+ * @return the compiled instruction
+ * @throws XPathException if parsing fails
+ */
+
+ private Expression parseProcessingInstructionConstructor(int offset) throws XPathException {
+ nextToken();
+ Expression name = parseExpression();
+ expect(Token.RCURLY);
+ lookAhead(); // must be done manually after an RCURLY
+ nextToken();
+ expect(Token.LCURLY);
+ t.setState(Tokenizer.DEFAULT_STATE);
+ nextToken();
+ Expression content = null;
+ if (t.currentToken != Token.RCURLY) {
+ content = parseExpression();
+ expect(Token.RCURLY);
+ }
+ lookAhead(); // after an RCURLY
+ nextToken();
+ ProcessingInstruction pi = new ProcessingInstruction(name);
+ makeSimpleContent(content, pi, offset);
+ return makeTracer(offset, pi, StandardNames.XSL_PROCESSING_INSTRUCTION, null);
+ }
+
+ /**
+ * Parse a processing instruction constructor of the form
+ * processing-instruction name { expr }
+ *
+ * @param offset position of the expression in the source
+ * @return the parsed expression
+ * @throws XPathException if a static error is found
+ */
+
+ private Expression parseNamedProcessingInstructionConstructor(int offset) throws XPathException {
+ String target = t.currentTokenValue;
+ String warning = null;
+ if (target.equalsIgnoreCase("xml")) {
+ warning = "A processing instruction must not be named 'xml' in any combination of upper and lower case";
+ }
+ if (!nameChecker.isValidNCName(target)) {
+ grumble("Invalid processing instruction name " + Err.wrap(target));
+ }
+ Expression piName = new StringLiteral(target);
+ Expression piContent = null;
+ nextToken();
+ if (t.currentToken != Token.RCURLY) {
+ piContent = parseExpression();
+ expect(Token.RCURLY);
+ }
+ lookAhead(); // after an RCURLY
+ nextToken();
+ if (warning == null) {
+ ProcessingInstruction pi2 = new ProcessingInstruction(piName);
+ makeSimpleContent(piContent, pi2, offset);
+ return makeTracer(offset, pi2, StandardNames.XSL_PROCESSING_INSTRUCTION, null);
+ } else {
+ warning(warning);
+ return new ErrorExpression(new XPathException(warning, "XQDY0064"));
+ }
+ }
+
+ /*@NotNull*/
+ protected Expression parseTryCatchExpression() throws XPathException {
+ grumble("try/catch expressions are not allowed in XQuery 1.0");
+ return new ErrorExpression();
+ }
+
+ /*@NotNull*/
+ protected Expression parseNamespaceConstructor(int offset) throws XPathException {
+ grumble("Namespace node constructors are not allowed in XQuery 1.0");
+ return new ErrorExpression();
+ }
+
+ /*@NotNull*/
+ protected Expression parseNamedNamespaceConstructor(int offset) throws XPathException {
+ grumble("Namespace node constructors are not allowed in XQuery 1.0");
+ return new ErrorExpression();
+ }
+
+ /**
+ * Make the instructions for the children of a node with simple content (attribute, text, PI, etc)
+ *
+ * @param content the expression making up the simple content
+ * @param inst the skeletal instruction for creating the node
+ * @param offset the character position of this construct within the source query
+ * @throws net.sf.saxon.trans.XPathException
+ * if a static error is encountered
+ */
+
+ protected void makeSimpleContent(/*@Nullable*/ Expression content, /*@NotNull*/ SimpleNodeConstructor inst, int offset) throws XPathException {
+ try {
+ if (content == null) {
+ inst.setSelect(new StringLiteral(StringValue.EMPTY_STRING), env.getConfiguration());
+ } else {
+ inst.setSelect(stringify(content, false, env.getConfiguration()), env.getConfiguration());
+ }
+ setLocation(inst, offset);
+ } catch (XPathException e) {
+ grumble(e.getMessage());
+ }
+ }
+
+ /**
+ * Make a sequence of instructions as the children of an element-construction instruction.
+ * The idea here is to convert an XPath expression that "pulls" the content into a sequence
+ * of XSLT-style instructions that push nodes directly onto the subtree, thus reducing the
+ * need for copying of intermediate nodes.
+ * @param content The content of the element as an expression
+ * @param inst The element-construction instruction (Element or FixedElement)
+ * @param offset the character position in the query
+ */
+// private void makeContentConstructor(Expression content, InstructionWithChildren inst, int offset) {
+// if (content == null) {
+// inst.setChildren(null);
+// } else if (content instanceof AppendExpression) {
+// List instructions = new ArrayList(10);
+// convertAppendExpression((AppendExpression) content, instructions);
+// inst.setChildren((Expression[]) instructions.toArray(new Expression[instructions.size()]));
+// } else {
+// Expression children[] = {content};
+// inst.setChildren(children);
+// }
+// setLocation(inst, offset);
+// }
+
+ /**
+ * Parse pseudo-XML syntax in direct element constructors, comments, CDATA, etc.
+ * This is handled by reading single characters from the Tokenizer until the
+ * end of the tag (or an enclosed expression) is enountered.
+ * This method is also used to read an end tag. Because an end tag is not an
+ * expression, the method in this case returns a StringValue containing the
+ * contents of the end tag.
+ *
+ * @param allowEndTag true if the context allows an End Tag to appear here
+ * @return an Expression representing the result of parsing the constructor.
+ * If an end tag was read, its contents will be returned as a StringValue.
+ * @throws XPathException if parsing fails
+ */
+
+ /*@NotNull*/
+ private Expression parsePseudoXML(boolean allowEndTag) throws XPathException {
+ try {
+ Expression exp;
+ int offset = t.inputOffset;
+ // we're reading raw characters, so we don't want the currentTokenStartOffset
+ char c = t.nextChar();
+ switch (c) {
+ case '!':
+ c = t.nextChar();
+ if (c == '-') {
+ exp = parseCommentConstructor();
+ } else if (c == '[') {
+ grumble("A CDATA section is allowed only in element content");
+ return null;
+ // if CDATA were allowed here, we would have already read it
+ } else {
+ grumble("Expected '--' or '[CDATA[' after '<!'");
+ return null;
+ }
+ break;
+ case '?':
+ exp = parsePIConstructor();
+ break;
+ case '/':
+ if (allowEndTag) {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+ while (true) {
+ c = t.nextChar();
+ if (c == '>') {
+ break;
+ }
+ sb.append(c);
+ }
+ return new StringLiteral(sb.toString());
+ }
+ grumble("Unmatched XML end tag");
+ return new ErrorExpression();
+ default:
+ t.unreadChar();
+ exp = parseDirectElementConstructor();
+ }
+ setLocation(exp, offset);
+ return exp;
+ } catch (StringIndexOutOfBoundsException e) {
+ grumble("End of input encountered while parsing direct constructor");
+ return new ErrorExpression();
+ }
+ }
+
+ /**
+ * Parse a direct element constructor
+ *
+ * @return the expression representing the constructor
+ * @throws XPathException if a syntax error is found
+ * @throws StringIndexOutOfBoundsException
+ * if the end of input is encountered prematurely
+ */
+
+ private Expression parseDirectElementConstructor() throws XPathException, StringIndexOutOfBoundsException {
+ int offset = t.inputOffset - 1;
+ // we're reading raw characters, so we don't want the currentTokenStartOffset
+ char c;
+ FastStringBuffer buff = new FastStringBuffer(FastStringBuffer.SMALL);
+ int namespaceCount = 0;
+ while (true) {
+ c = t.nextChar();
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '/' || c == '>') {
+ break;
+ }
+ buff.append(c);
+ }
+ String elname = buff.toString();
+ if (elname.length() == 0) {
+ grumble("Expected element name after '<'");
+ }
+ //Used LinkedHashMap because it is friendly to retain the order of attributes.
+ LinkedHashMap<String, AttributeDetails> attributes = new LinkedHashMap<String, AttributeDetails>(10);
+ while (true) {
+ // loop through the attributes
+ // We must process namespace declaration attributes first;
+ // their scope applies to all preceding attribute names and values.
+ // But finding the delimiting quote of an attribute value requires the
+ // XPath expressions to be parsed, because they may contain nested quotes.
+ // So we parse in "scanOnly" mode, which ignores any undeclared namespace
+ // prefixes, use the result of this parse to determine the length of the
+ // attribute value, save the value, and reparse it when all the namespace
+ // declarations have been dealt with.
+ c = skipSpaces(c);
+ if (c == '/' || c == '>') {
+ break;
+ }
+ int attOffset = t.inputOffset - 1;
+ buff.setLength(0);
+ // read the attribute name
+ while (true) {
+ buff.append(c);
+ c = t.nextChar();
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '=') {
+ break;
+ }
+ }
+ String attName = buff.toString();
+ if (!nameChecker.isQName(attName)) {
+ grumble("Invalid attribute name " + Err.wrap(attName, Err.ATTRIBUTE));
+ }
+ c = skipSpaces(c);
+ expectChar(c, '=');
+ c = t.nextChar();
+ c = skipSpaces(c);
+ char delim = c;
+ boolean isNamespace = ("xmlns".equals(attName) || attName.startsWith("xmlns:"));
+ int end;
+ if (isNamespace) {
+ end = makeNamespaceContent(t.input, t.inputOffset, delim);
+ } else {
+ Expression avt;
+ try {
+ avt = makeAttributeContent(t.input, t.inputOffset, delim, true);
+ } catch (XPathException err) {
+ if (!err.hasBeenReported()) {
+ grumble(err.getMessage());
+ }
+ throw err;
+ }
+
+ // by convention, this returns the end position when called with scanOnly set
+ end = (int) ((Int64Value) ((Literal) avt).getValue()).longValue();
+
+ }
+ // save the value with its surrounding quotes
+ String val = t.input.substring(t.inputOffset - 1, end + 1);
+ // and without
+ String rval = t.input.substring(t.inputOffset, end);
+
+ // account for any newlines found in the value
+ // (note, subexpressions between curlies will have been parsed using a different tokenizer)
+ String tail = val;
+ int pos;
+ while ((pos = tail.indexOf('\n')) >= 0) {
+ t.incrementLineNumber(t.inputOffset - 1 + pos);
+ tail = tail.substring(pos + 1);
+ }
+ t.inputOffset = end + 1;
+
+ if (isNamespace) {
+ // Processing follows the resolution of bug 5083: doubled curly braces represent single
+ // curly braces, single curly braces are not allowed.
+ FastStringBuffer sb = new FastStringBuffer(rval.length());
+ boolean prevDelim = false;
+ boolean prevOpenCurly = false;
+ boolean prevCloseCurly = false;
+ for (int i = 0; i < rval.length(); i++) {
+ char n = rval.charAt(i);
+ if (n == delim) {
+ prevDelim = !prevDelim;
+ if (prevDelim) {
+ continue;
+ }
+ }
+ if (n == '{') {
+ prevOpenCurly = !prevOpenCurly;
+ if (prevOpenCurly) {
+ continue;
+ }
+ } else if (prevOpenCurly) {
+ grumble("Namespace must not contain an unescaped opening brace", "XQST0022");
+ }
+ if (n == '}') {
+ prevCloseCurly = !prevCloseCurly;
+ if (prevCloseCurly) {
+ continue;
+ }
+ } else if (prevCloseCurly) {
+ grumble("Namespace must not contain an unescaped closing brace", "XPST0003");
+ }
+ sb.append(n);
+ }
+ if (prevOpenCurly) {
+ grumble("Namespace must not contain an unescaped opening brace", "XQST0022");
+ }
+ if (prevCloseCurly) {
+ grumble("Namespace must not contain an unescaped closing brace", "XPST0003");
+ }
+ rval = sb.toString();
+ String uri = URILiteral(rval);
+ if (!StandardURIChecker.getInstance().isValidURI(uri)) {
+ grumble("Namespace must be a valid URI value", "XQST0022");
+ }
+ String prefix;
+ if ("xmlns".equals(attName)) {
+ prefix = "";
+ if (uri.equals(NamespaceConstant.XML)) {
+ grumble("Cannot have the XML namespace as the default namespace", "XQST0070");
+ }
+ } else {
+ prefix = attName.substring(6);
+ if (prefix.equals("xml") && !uri.equals(NamespaceConstant.XML)) {
+ grumble("Cannot bind the prefix 'xml' to a namespace other than the XML namespace", "XQST0070");
+ } else if (uri.equals(NamespaceConstant.XML) && !prefix.equals("xml")) {
+ grumble("Cannot bind a prefix other than 'xml' to the XML namespace", "XQST0070");
+ } else if (prefix.equals("xmlns")) {
+ grumble("Cannot use xmlns as a namespace prefix", "XQST0070");
+ }
+
+ if (uri.length() == 0) {
+ grumble("Namespace URI must not be empty", "XQST0085");
+ }
+ }
+ namespaceCount++;
+ ((QueryModule) env).declareActiveNamespace(prefix, uri);
+ }
+ if (attributes.get(attName) != null) {
+ if (isNamespace) {
+ grumble("Duplicate namespace declaration " + attName, "XQST0071", attOffset);
+ } else {
+ grumble("Duplicate attribute name " + attName, "XQST0040", attOffset);
+ }
+ }
+// if (attName.equals("xml:id") && !nameChecker.isValidNCName(rval)) {
+// grumble("Value of xml:id must be a valid NCName", "XQST0082");
+// }
+ AttributeDetails a = new AttributeDetails();
+ a.value = val;
+ a.startOffset = attOffset;
+ attributes.put(attName, a);
+
+ // on return, the current character is the closing quote
+ c = t.nextChar();
+ if (!(c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '/' || c == '>')) {
+ grumble("There must be whitespace after every attribute except the last");
+ }
+ }
+ String namespace;
+ int elNameCode = 0;
+ if (scanOnly) {
+ elNameCode = StandardNames.XSL_ELEMENT; // any name will do
+ } else {
+ try {
+ String[] parts = nameChecker.getQNameParts(elname);
+ namespace = ((QueryModule) env).checkURIForPrefix(parts[0]);
+ if (namespace == null) {
+ grumble("Undeclared prefix in element name " + Err.wrap(elname, Err.ELEMENT), "XPST0081", offset);
+ }
+ elNameCode = env.getNamePool().allocate(parts[0], namespace, parts[1]);
+ } catch (QNameException e) {
+ grumble("Invalid element name " + Err.wrap(elname, Err.ELEMENT), "XPST0003", offset);
+ }
+ }
+ int validationMode = ((QueryModule) env).getConstructionMode();
+ FingerprintedQName fqn = new FingerprintedQName(
+ env.getNamePool().getPrefix(elNameCode),
+ env.getNamePool().getURI(elNameCode),
+ env.getNamePool().getLocalName(elNameCode),
+ elNameCode);
+ FixedElement elInst = new FixedElement(fqn,
+ ((QueryModule) env).getActiveNamespaceCodes(),
+ ((QueryModule) env).isInheritNamespaces(),
+ null,
+ validationMode);
+
+ elInst.setBaseURI(env.getBaseURI());
+ setLocation(elInst, offset);
+
+ List<Expression> contents = new ArrayList<Expression>(10);
+
+ IntHashSet attFingerprints = new IntHashSet(attributes.size());
+ // we've checked for duplicate lexical QNames, but not for duplicate expanded-QNames
+ for (Map.Entry<String, AttributeDetails> entry : attributes.entrySet()) {
+ String attName = entry.getKey();
+ AttributeDetails a = entry.getValue();
+ String attValue = a.value;
+ int attOffset = a.startOffset;
+
+ if ("xmlns".equals(attName) || attName.startsWith("xmlns:")) {
+ // do nothing
+ } else if (scanOnly) {
+ // This means we are prescanning an attribute constructor, and we found a nested attribute
+ // constructor, which we have prescanned; we now don't want to re-process the nested attribute
+ // constructor because it might depend on things like variables declared in the containing
+ // attribute constructor, and in any case we're going to come back to it again later.
+ // See test qxmp180
+ } else {
+ NodeName attributeName = null;
+ String attNamespace;
+ try {
+ String[] parts = nameChecker.getQNameParts(attName);
+ if (parts[0].length() == 0) {
+ // attributes don't use the default namespace
+ attNamespace = "";
+ } else {
+ attNamespace = ((QueryModule) env).checkURIForPrefix(parts[0]);
+ }
+ if (attNamespace == null) {
+ grumble("Undeclared prefix in attribute name " +
+ Err.wrap(attName, Err.ATTRIBUTE), "XPST0081", attOffset);
+ }
+ attributeName = new FingerprintedQName(parts[0], attNamespace, parts[1]);
+ attributeName.allocateNameCode(env.getNamePool());
+ int key = (attributeName.getFingerprint());
+ if (attFingerprints.contains(key)) {
+ grumble("Duplicate expanded attribute name " + attName, "XQST0040", attOffset);
+ }
+ attFingerprints.add(key);
+ } catch (QNameException e) {
+ grumble("Invalid attribute name " + Err.wrap(attName, Err.ATTRIBUTE), "XPST0003", attOffset);
+ }
+
+ FixedAttribute attInst =
+ new FixedAttribute(attributeName, Validation.STRIP, null);
+
+ setLocation(attInst);
+ Expression select;
+ try {
+ select = makeAttributeContent(attValue, 1, attValue.charAt(0), false);
+ } catch (XPathException err) {
+ throw err.makeStatic();
+ }
+ attInst.setSelect(select, env.getConfiguration());
+ attInst.setRejectDuplicates();
+ setLocation(attInst);
+ contents.add(makeTracer(attOffset, attInst, Location.LITERAL_RESULT_ATTRIBUTE,
+ attributeName.getStructuredQName()));
+ }
+ }
+ if (c == '/') {
+ // empty element tag
+ expectChar(t.nextChar(), '>');
+ } else {
+ readElementContent(elname, contents);
+ }
+
+ Expression[] elk = new Expression[contents.size()];
+ for (int i = 0; i < contents.size(); i++) {
+ // if the child expression creates another element,
+ // suppress validation, as the parent already takes care of it
+ if (validationMode != Validation.STRIP) {
+ contents.get(i).suppressValidation(validationMode);
+ }
+ elk[i] = contents.get(i);
+ }
+ Block block = new Block();
+ block.setChildren(elk);
+ elInst.setContentExpression(block);
+
+ // reset the in-scope namespaces to what they were before
+
+ for (int n = 0; n < namespaceCount; n++) {
+ ((QueryModule) env).undeclareNamespace();
+ }
+
+ return makeTracer(offset, elInst, Location.LITERAL_RESULT_ELEMENT,
+ env.getNamePool().getStructuredQName(elNameCode));
+ }
+
+ /**
+ * Parse the content of an attribute in a direct element constructor. This may contain nested expressions
+ * within curly braces. A particular problem is that the namespaces used in the expression may not yet be
+ * known. This means we need the ability to parse in "scanOnly" mode, where undeclared namespace prefixes
+ * are ignored.
+ * <p/>
+ * The code is based on the XSLT code in {@link AttributeValueTemplate#make}: the main difference is that
+ * character entities and built-in entity references need to be recognized and expanded. Also, whitespace
+ * needs to be normalized, mimicking the action of an XML parser
+ *
+ * @param avt the content of the attribute as written, including variable portions enclosed in curly braces
+ * @param start the character position in the attribute value where parsing should start
+ * @param terminator a character that is to be taken as marking the end of the expression
+ * @param scanOnly if the purpose of this parse is simply to locate the end of the attribute value, and not
+ * to report any semantic errors.
+ * @return the expression that will evaluate the content of the attribute
+ * @throws XPathException if parsing fails
+ */
+
+ /*@Nullable*/
+ private Expression makeAttributeContent(/*@NotNull*/ String avt,
+ int start,
+ char terminator,
+ boolean scanOnly) throws XPathException {
+
+ int lineNumber = t.getLineNumber();
+ List<Expression> components = new ArrayList<Expression>(10);
+
+ int i0, i1, i2, i8, i9, len, last;
+ last = start;
+ len = avt.length();
+ while (last < len) {
+ i2 = avt.indexOf(terminator, last);
+ if (i2 < 0) {
+ XPathException e = new XPathException("Attribute constructor is not properly terminated");
+ e.setIsStaticError(true);
+ throw e;
+ }
+
+ i0 = avt.indexOf("{", last);
+ i1 = avt.indexOf("{{", last);
+ i8 = avt.indexOf("}", last);
+ i9 = avt.indexOf("}}", last);
+
+ if ((i0 < 0 || i2 < i0) && (i8 < 0 || i2 < i8)) { // found end of string
+ addStringComponent(components, avt, last, i2);
+
+ // look for doubled quotes, and skip them (for now)
+ if (i2 + 1 < avt.length() && avt.charAt(i2 + 1) == terminator) {
+ components.add(new StringLiteral(terminator + ""));
+ last = i2 + 2;
+ //continue;
+ } else {
+ last = i2;
+ break;
+ }
+ } else if (i8 >= 0 && (i0 < 0 || i8 < i0)) { // found a "}"
+ if (i8 != i9) { // a "}" that isn't a "}}"
+ XPathException e = new XPathException(
+ "Closing curly brace in attribute value template \"" + avt + "\" must be doubled");
+ e.setIsStaticError(true);
+ throw e;
+ }
+ addStringComponent(components, avt, last, i8 + 1);
+ last = i8 + 2;
+ } else if (i1 >= 0 && i1 == i0) { // found a doubled "{{"
+ addStringComponent(components, avt, last, i1 + 1);
+ last = i1 + 2;
+ } else if (i0 >= 0) { // found a single "{"
+ if (i0 > last) {
+ addStringComponent(components, avt, last, i0);
+ }
+ Expression exp;
+ ExpressionParser parser = newParser();
+ ((QueryParser) parser).executable = executable;
+ parser.setDefaultContainer(getDefaultContainer());
+ parser.setScanOnly(scanOnly);
+ parser.setRangeVariableStack(rangeVariables);
+ exp = parser.parse(avt, i0 + 1, Token.RCURLY, lineNumber, env);
+ if (!scanOnly) {
+ exp = ExpressionVisitor.make(env, exp.getExecutable()).simplify(exp);
+ }
+ last = parser.getTokenizer().currentTokenStartOffset + 1;
+ components.add(makeStringJoin(exp, env));
+
+ } else {
+ throw new IllegalStateException("Internal error parsing direct attribute constructor");
+ }
+ }
+
+ // if this is simply a prescan, return the position of the end of the
+ // AVT, so we can parse it properly later
+
+ if (scanOnly) {
+ return Literal.makeLiteral(Int64Value.makeIntegerValue(last));
+ }
+
+ // is it empty?
+
+ if (components.isEmpty()) {
+ return new StringLiteral(StringValue.EMPTY_STRING);
+ }
+
+ // is it a single component?
+
+ if (components.size() == 1) {
+ return components.get(0);
+ }
+
+ // otherwise, return an expression that concatenates the components
+
+
+ Expression[] args = new Expression[components.size()];
+ components.toArray(args);
+ Concat fn = (Concat) SystemFunctionCall.makeSystemFunction("concat", args);
+ assert fn != null;
+ fn.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), lineNumber));
+ return fn;
+ //return visitor.simplify(fn);
+
+ }
+
+ private void addStringComponent(/*@NotNull*/ List<Expression> components, /*@NotNull*/ String avt, int start, int end)
+ throws XPathException {
+ // analyze fixed text within the value of a direct attribute constructor.
+ if (start < end) {
+ FastStringBuffer sb = new FastStringBuffer(end - start);
+ for (int i = start; i < end; i++) {
+ char c = avt.charAt(i);
+ switch (c) {
+ case '&': {
+ int semic = avt.indexOf(';', i);
+ if (semic < 0) {
+ grumble("No closing ';' found for entity or character reference");
+ } else {
+ String entity = avt.substring(i + 1, semic);
+ sb.append(analyzeEntityReference(entity));
+ i = semic;
+ }
+ break;
+ }
+ case '<':
+ grumble("The < character must not appear in attribute content");
+ break;
+ case '\n':
+ case '\t':
+ sb.append(' ');
+ break;
+ case '\r':
+ sb.append(' ');
+ if (i + 1 < end && avt.charAt(i + 1) == '\n') {
+ i++;
+ }
+ break;
+ default:
+ sb.append(c);
+
+ }
+ }
+ components.add(new StringLiteral(sb.toString()));
+ }
+ }
+
+ /**
+ * Parse the content of an namespace declaration attribute in a direct element constructor. This is simpler
+ * than an ordinary attribute because it does not contain nested expressions in curly braces. (But see bug 5083).
+ *
+ * @param avt the content of the attribute as written, including variable portions enclosed in curly braces
+ * @param start the character position in the attribute value where parsing should start
+ * @param terminator a character that is to be taken as marking the end of the expression
+ * @return the position of the end of the URI value
+ * @throws XPathException if parsing fails
+ */
+
+ private int makeNamespaceContent(/*@NotNull*/ String avt, int start, char terminator) throws XPathException {
+
+ int i2, len, last;
+ last = start;
+ len = avt.length();
+ while (last < len) {
+ i2 = avt.indexOf(terminator, last);
+ if (i2 < 0) {
+ XPathException e = new XPathException("Namespace declaration is not properly terminated");
+ e.setIsStaticError(true);
+ throw e;
+ }
+
+ // look for doubled quotes, and skip them (for now)
+ if (i2 + 1 < avt.length() && avt.charAt(i2 + 1) == terminator) {
+ last = i2 + 2;
+ //continue;
+ } else {
+ last = i2;
+ break;
+ }
+ }
+
+ // return the position of the end of the literal
+ return last;
+
+ }
+
+ /**
+ * Read the content of a direct element constructor
+ *
+ * @param startTag the element start tag
+ * @param components an empty list, to which the expressions comprising the element contents are added
+ * @throws XPathException if any static errors are detected
+ */
+ private void readElementContent(String startTag, /*@NotNull*/ List<Expression> components) throws XPathException {
+ final TypeHierarchy th = env.getConfiguration().getTypeHierarchy();
+ try {
+ boolean afterEnclosedExpr = false;
+ while (true) {
+ // read all the components of the element value
+ FastStringBuffer text = new FastStringBuffer(FastStringBuffer.SMALL);
+ char c;
+ boolean containsEntities = false;
+ while (true) {
+ c = t.nextChar();
+ if (c == '<') {
+ // See if we've got a CDATA section
+ if (t.nextChar() == '!') {
+ if (t.nextChar() == '[') {
+ readCDATASection(text);
+ containsEntities = true;
+ continue;
+ } else {
+ t.unreadChar();
+ t.unreadChar();
+ }
+ } else {
+ t.unreadChar();
+ }
+ break;
+ } else if (c == '&') {
+ text.append(readEntityReference());
+ containsEntities = true;
+ } else if (c == '}') {
+ c = t.nextChar();
+ if (c != '}') {
+ grumble("'}' must be written as '}}' within element content");
+ }
+ text.append(c);
+ } else if (c == '{') {
+ c = t.nextChar();
+ if (c != '{') {
+ c = '{';
+ break;
+ }
+ text.append(c);
+ } else {
+ if (!nameChecker.isValidChar(c) && !UTF16CharacterSet.isSurrogate(c)) {
+ grumble("Character code " + c + " is not a valid XML character");
+ }
+ text.append(c);
+ }
+ }
+ if (text.length() > 0 &&
+ (containsEntities |
+ ((QueryModule) env).isPreserveBoundarySpace() ||
+ !Whitespace.isWhite(text))) {
+ ValueOf inst = new ValueOf(new StringLiteral(new StringValue(text.condense())), false, false);
+ setLocation(inst);
+ components.add(inst);
+ afterEnclosedExpr = false;
+ }
+ if (c == '<') {
+ Expression exp = parsePseudoXML(true);
+ // An end tag can appear here, and is returned as a string value
+ if (exp instanceof StringLiteral) {
+ String endTag = ((StringLiteral) exp).getStringValue();
+ if (Whitespace.isWhitespace(endTag.charAt(0))) {
+ grumble("End tag contains whitespace before the name");
+ }
+ endTag = Whitespace.trim(endTag);
+ if (endTag.equals(startTag)) {
+ return;
+ } else {
+ grumble("End tag </" + endTag +
+ "> does not match start tag <" + startTag + '>', "XQST0118");
+ // error code allocated by spec bug 11609
+ }
+ } else {
+ components.add(exp);
+ }
+ } else {
+ // we read an '{' indicating an enclosed expression
+ if (afterEnclosedExpr) {
+ Expression previousComponent = components.get(components.size() - 1);
+ ItemType previousItemType = previousComponent.getItemType(th);
+ if (!(previousItemType instanceof NodeTest)) {
+ // Add a zero-length text node, to prevent {"a"}{"b"} generating an intervening space
+ // See tests (qxmp132, qxmp261)
+ ValueOf inst = new ValueOf(new StringLiteral(StringValue.EMPTY_STRING), false, false);
+ setLocation(inst);
+ components.add(inst);
+ }
+ }
+ t.unreadChar();
+ t.setState(Tokenizer.DEFAULT_STATE);
+ lookAhead();
+ nextToken();
+ Expression exp = parseExpression();
+ if (!((QueryModule) env).isPreserveNamespaces()) {
+ exp = new CopyOf(exp, false, Validation.PRESERVE, null, true);
+ }
+ components.add(exp);
+ expect(Token.RCURLY);
+ afterEnclosedExpr = true;
+ }
+ }
+ } catch (StringIndexOutOfBoundsException err) {
+ grumble("No closing end tag found for direct element constructor");
+ }
+ }
+
+ /*@Nullable*/
+ private Expression parsePIConstructor() throws XPathException {
+ try {
+ FastStringBuffer pi = new FastStringBuffer(FastStringBuffer.SMALL);
+ int firstSpace = -1;
+ while (!pi.toString().endsWith("?>")) {
+ char c = t.nextChar();
+ if (firstSpace < 0 && " \t\r\n".indexOf(c) >= 0) {
+ firstSpace = pi.length();
+ }
+ pi.append(c);
+ }
+ pi.setLength(pi.length() - 2);
+
+ String target;
+ String data = "";
+ if (firstSpace < 0) {
+ // there is no data part
+ target = pi.toString();
+ } else {
+ // trim leading space from the data part, but not trailing space
+ target = pi.toString().substring(0, firstSpace);
+ firstSpace++;
+ while (firstSpace < pi.length() && " \t\r\n".indexOf(pi.charAt(firstSpace)) >= 0) {
+ firstSpace++;
+ }
+ data = pi.toString().substring(firstSpace);
+ }
+
+ if (!nameChecker.isValidNCName(target)) {
+ grumble("Invalid processing instruction name " + Err.wrap(target));
+ }
+
+ if (target.equalsIgnoreCase("xml")) {
+ grumble("A processing instruction must not be named 'xml' in any combination of upper and lower case");
+ }
+
+ ProcessingInstruction instruction =
+ new ProcessingInstruction(new StringLiteral(target));
+ instruction.setSelect(new StringLiteral(data), env.getConfiguration());
+ setLocation(instruction);
+ return instruction;
+ } catch (StringIndexOutOfBoundsException err) {
+ grumble("No closing '?>' found for processing instruction");
+ return null;
+ }
+ }
+
+ private void readCDATASection(/*@NotNull*/ FastStringBuffer cdata) throws XPathException {
+ try {
+ char c;
+ // CDATA section
+ c = t.nextChar();
+ expectChar(c, 'C');
+ c = t.nextChar();
+ expectChar(c, 'D');
+ c = t.nextChar();
+ expectChar(c, 'A');
+ c = t.nextChar();
+ expectChar(c, 'T');
+ c = t.nextChar();
+ expectChar(c, 'A');
+ c = t.nextChar();
+ expectChar(c, '[');
+ while (!cdata.toString().endsWith("]]>")) {
+ cdata.append(t.nextChar());
+ }
+ cdata.setLength(cdata.length() - 3);
+ } catch (StringIndexOutOfBoundsException err) {
+ grumble("No closing ']]>' found for CDATA section");
+ }
+ }
+
+ /*@Nullable*/
+ private Expression parseCommentConstructor() throws XPathException {
+ try {
+ char c = t.nextChar();
+ // XML-like comment
+ expectChar(c, '-');
+ FastStringBuffer comment = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ while (!comment.toString().endsWith("--")) {
+ comment.append(t.nextChar());
+ }
+ if (t.nextChar() != '>') {
+ grumble("'--' is not permitted in an XML comment");
+ }
+ CharSequence commentText = comment.subSequence(0, comment.length() - 2);
+ Comment instruction = new Comment();
+ instruction.setSelect(new StringLiteral(new StringValue(commentText)), env.getConfiguration());
+ setLocation(instruction);
+ return instruction;
+ } catch (StringIndexOutOfBoundsException err) {
+ grumble("No closing '-->' found for comment constructor");
+ return null;
+ }
+ }
+
+ /**
+ * Convert an expression so it generates a space-separated sequence of strings
+ *
+ * @param exp the expression that calculates the content
+ * @param noNodeIfEmpty if true, no node is produced when the value of the content
+ * expression is an empty sequence. If false, the effect of supplying an empty sequence
+ * is that a node is created whose string-value is a zero-length string. Set to true for
+ * text node constructors, false for other kinds of node.
+ * @param config the Saxon configuration
+ * @return an expression that computes the content and converts the result to a character string
+ * @throws XPathException if parsing fails
+ */
+
+ public static Expression stringify(Expression exp, boolean noNodeIfEmpty, Configuration config) throws XPathException {
+ // Compare with XSLLeafNodeConstructor.makeSimpleContentConstructor
+ // Atomize the result
+ exp = Atomizer.makeAtomizer(exp);
+ // Convert each atomic value to a string
+ exp = new AtomicSequenceConverter(exp, BuiltInAtomicType.STRING);
+ ((AtomicSequenceConverter) exp).allocateConverter(config, false);
+ // Join the resulting strings with a separator
+ exp = SystemFunctionCall.makeSystemFunction("string-join",
+ new Expression[]{exp, new StringLiteral(StringValue.SINGLE_SPACE)});
+ assert exp != null;
+ if (noNodeIfEmpty) {
+ ((StringJoin) exp).setReturnEmptyIfEmpty(true);
+ }
+ // All that's left for the instruction to do is to construct the right kind of node
+ return exp;
+ }
+
+ /**
+ * Method to make a string literal from a token identified as a string
+ * literal. This is trivial in XPath, but in XQuery the method is overridden
+ * to identify pseudo-XML character and entity references
+ *
+ * @param token the string as written (or as returned by the tokenizer)
+ * @return The string value of the string literal, after dereferencing entity and
+ * character references
+ * @throws XPathException if parsing fails
+ */
+
+ @Override
+ /*@NotNull*/
+ protected Literal makeStringLiteral(/*@NotNull*/ String token) throws XPathException {
+ StringLiteral lit;
+ if (token.indexOf('&') == -1) {
+ lit = new StringLiteral(token);
+ } else {
+ FastStringBuffer sb = unescape(token);
+ lit = new StringLiteral(StringValue.makeStringValue(sb));
+ }
+ setLocation(lit);
+ return lit;
+ }
+
+ /**
+ * Unescape character references and built-in entity references in a string
+ *
+ * @param token the input string, which may include XML-style character references or built-in
+ * entity references
+ * @return the string with character references and built-in entity references replaced by their expansion
+ * @throws XPathException if a malformed character or entity reference is found
+ */
+
+ /*@NotNull*/
+ private FastStringBuffer unescape(/*@NotNull*/ String token) throws XPathException {
+ FastStringBuffer sb = new FastStringBuffer(token.length());
+ for (int i = 0; i < token.length(); i++) {
+ char c = token.charAt(i);
+ if (c == '&') {
+ int semic = token.indexOf(';', i);
+ if (semic < 0) {
+ grumble("No closing ';' found for entity or character reference");
+ } else {
+ String entity = token.substring(i + 1, semic);
+ sb.append(analyzeEntityReference(entity));
+ i = semic;
+ }
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb;
+ }
+
+ /**
+ * Read a pseudo-XML character reference or entity reference.
+ *
+ * @return The character represented by the character or entity reference. Note
+ * that this is a string rather than a char because a char only accommodates characters
+ * up to 65535.
+ * @throws XPathException if the character or entity reference is not well-formed
+ */
+
+ /*@Nullable*/
+ private String readEntityReference() throws XPathException {
+ try {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ while (true) {
+ char c = t.nextChar();
+ if (c == ';') {
+ break;
+ }
+ sb.append(c);
+ }
+ String entity = sb.toString();
+ return analyzeEntityReference(entity);
+ } catch (StringIndexOutOfBoundsException err) {
+ grumble("No closing ';' found for entity or character reference");
+ }
+ return null; // to keep the Java compiler happy
+ }
+
+ /*@Nullable*/
+ private String analyzeEntityReference(/*@NotNull*/ String entity) throws XPathException {
+ if ("lt".equals(entity)) {
+ return "<";
+ } else if ("gt".equals(entity)) {
+ return ">";
+ } else if ("amp".equals(entity)) {
+ return "&";
+ } else if ("quot".equals(entity)) {
+ return "\"";
+ } else if ("apos".equals(entity)) {
+ return "'";
+ } else if (entity.length() < 2 || entity.charAt(0) != '#') {
+ grumble("invalid character reference &" + entity + ';');
+ return null;
+ } else {
+ //entity = entity.toLowerCase();
+ return parseCharacterReference(entity);
+ }
+ }
+
+ /*@Nullable*/
+ private String parseCharacterReference(/*@NotNull*/ String entity) throws XPathException {
+ int value = 0;
+ if (entity.charAt(1) == 'x') {
+ if (entity.length() < 3) {
+ grumble("No hex digits in hexadecimal character reference");
+ }
+ entity = entity.toLowerCase();
+ for (int i = 2; i < entity.length(); i++) {
+ int digit = "0123456789abcdef".indexOf(entity.charAt(i));
+ if (digit < 0) {
+ grumble("Invalid hex digit '" + entity.charAt(i) + "' in character reference");
+ }
+ value = (value * 16) + digit;
+ if (value > UTF16CharacterSet.NONBMP_MAX) {
+ grumble("Character reference exceeds Unicode codepoint limit", "XQST0090");
+ }
+ }
+ } else {
+ for (int i = 1; i < entity.length(); i++) {
+ int digit = "0123456789".indexOf(entity.charAt(i));
+ if (digit < 0) {
+ grumble("Invalid digit '" + entity.charAt(i) + "' in decimal character reference");
+ }
+ value = (value * 10) + digit;
+ if (value > UTF16CharacterSet.NONBMP_MAX) {
+ grumble("Character reference exceeds Unicode codepoint limit", "XQST0090");
+ }
+ }
+ }
+
+ NameChecker nc = env.getConfiguration().getNameChecker();
+ if (!nc.isValidChar(value)) {
+ grumble("Invalid XML character reference x"
+ + Integer.toHexString(value), "XQST0090");
+ }
+ // following code borrowed from AElfred
+ // Check for surrogates: 00000000 0000xxxx yyyyyyyy zzzzzzzz
+ // (1101|10xx|xxyy|yyyy + 1101|11yy|zzzz|zzzz:
+ if (value <= 0x0000ffff) {
+ // no surrogates needed
+ return "" + (char) value;
+ } else if (value <= 0x0010ffff) {
+ value -= 0x10000;
+ // > 16 bits, surrogate needed
+ return "" + ((char) (0xd800 | (value >> 10)))
+ + ((char) (0xdc00 | (value & 0x0003ff)));
+ } else {
+ // too big for surrogate
+ grumble("Character reference x" + Integer.toHexString(value) + " is too large", "XQST0090");
+ }
+ return null;
+ }
+
+ /**
+ * Handle a URI literal. This is whitespace-normalized as well as being unescaped
+ *
+ * @param in the string as written
+ * @return the URI after unescaping of entity and character references
+ * followed by whitespace normalization
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error is found while unescaping the URI
+ */
+
+ public String URILiteral(/*@NotNull*/ String in) throws XPathException {
+ return Whitespace.applyWhitespaceNormalization(Whitespace.COLLAPSE, unescape(in)).toString();
+ }
+
+ /**
+ * Convert a QName in expanded-name format "uri":local into Clark format
+ *
+ * @param s the QName in expanded-name format
+ * @return the corresponding expanded name in Clark format
+ * @throws XPathException
+ */
+
+ /*@NotNull*/
+ protected String normalizeEQName(/*@NotNull*/ String s) throws XPathException {
+ // overridden for XQuery
+ if (!Whitespace.containsWhitespace(s) && s.indexOf('&') < 0) {
+ return s;
+ }
+ final StructuredQName sq = StructuredQName.fromClarkName(s);
+ final String in = sq.getURI();
+ final String uri = Whitespace.applyWhitespaceNormalization(Whitespace.COLLAPSE, unescape(in)).toString();
+ return "{" + uri + "}" + sq.getLocalPart();
+ }
+
+ /**
+ * Lookahead one token, catching any exception thrown by the tokenizer. This
+ * method is only called from the query parser when switching from character-at-a-time
+ * mode to tokenizing mode
+ *
+ * @throws XPathException if parsing fails
+ */
+
+ protected void lookAhead() throws XPathException {
+ try {
+ t.lookAhead();
+ } catch (XPathException err) {
+ grumble(err.getMessage());
+ }
+ }
+
+ @Override
+ protected boolean atStartOfRelativePath() {
+ return t.currentToken == Token.TAG || super.atStartOfRelativePath();
+ // "<" after "/" is recognized in XQuery but not in XPath.
+ }
+
+ @Override
+ protected void testPermittedAxis(byte axis) throws XPathException {
+ if (axis == AxisInfo.NAMESPACE && language == XQUERY) {
+ grumble("The namespace axis is not available in XQuery");
+ }
+ }
+
+ /**
+ * Skip whitespace.
+ *
+ * @param c the current character
+ * @return the first character after any whitespace
+ * @throws StringIndexOutOfBoundsException
+ * if the end of input is encountered
+ */
+
+ private char skipSpaces(char c) throws StringIndexOutOfBoundsException {
+ while (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+ c = t.nextChar();
+ }
+ return c;
+ }
+
+ /**
+ * Test whether the current character is the expected character.
+ *
+ * @param actual The character that was read
+ * @param expected The character that was expected
+ * @throws XPathException if they are different
+ */
+
+ private void expectChar(char actual, char expected) throws XPathException {
+ if (actual != expected) {
+ grumble("Expected '" + expected + "', found '" + actual + '\'');
+ }
+ }
+
+ /**
+ * Get the current language (XPath or XQuery)
+ */
+
+ /*@NotNull*/
+ protected String getLanguage() {
+ return "XQuery";
+ }
+
+ private static class AttributeDetails {
+ String value;
+ int startOffset;
+ }
+
+ private static class Import {
+ /*@Nullable*/ String namespaceURI;
+ List<String> locationURIs;
+ }
+}
+
diff --git a/sf/saxon/query/QueryReader.java b/sf/saxon/query/QueryReader.java
new file mode 100644
index 0000000..d8d7c41
--- /dev/null
+++ b/sf/saxon/query/QueryReader.java
@@ -0,0 +1,307 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.stream.StreamSource;
+import java.io.*;
+
+/**
+ * This class contains static methods used to read a query as a byte stream, infer the encoding if
+ * necessary, and return the text of the query as a string; also methods to import functions and variables
+ * from one module into another, and check their consistency.
+ */
+public class QueryReader {
+
+ /**
+ * The class is never instantiated
+ */
+ private QueryReader() {}
+
+ /**
+ * Read a query module given a StreamSource
+ * @param ss the supplied StreamSource. This must contain a non-null systemID which defines the base
+ * URI of the query module, and either an InputStream or a Reader containing the query text. In the
+ * case of an InputStream the method attempts to infer the encoding; in the case of a Reader, this has
+ * already been done, and the encoding specified within the query itself is ignored.
+ * <p>The method reads from the InputStream or Reader contained in the StreamSource up to the end
+ * of file unless a fatal error occurs. It does not close the InputStream or Reader; this is the caller's
+ * responsibility.</p>
+ * @param nameChecker this checks XML names against either the XML 1.0 or XML 1.1 rules
+ * @return the text of the query
+ */
+
+ public static String readSourceQuery(/*@NotNull*/ StreamSource ss, /*@NotNull*/ NameChecker nameChecker) throws XPathException {
+ CharSequence queryText;
+ if (ss.getInputStream() != null) {
+ InputStream is = ss.getInputStream();
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is);
+ }
+ String encoding = readEncoding(is);
+ queryText = readInputStream(is, encoding, nameChecker);
+ } else if (ss.getReader() != null) {
+ queryText = readQueryFromReader(ss.getReader(), nameChecker);
+ } else {
+ throw new XPathException("Module URI Resolver must supply either an InputStream or a Reader");
+ }
+ return queryText.toString();
+ }
+
+ /**
+ * Read an input stream non-destructively to determine the encoding from the Query Prolog
+ * @param is the input stream: this must satisfy the precondition is.markSupported() = true.
+ * @return the encoding to be used: defaults to UTF-8 if no encoding was specified explicitly
+ * in the query prolog
+ * @throws XPathException if the input stream cannot be read
+ */
+
+ public static String readEncoding(/*@NotNull*/ InputStream is) throws XPathException {
+ try {
+ if (!is.markSupported()) {
+ throw new IllegalArgumentException("InputStream must have markSupported() = true");
+ }
+ is.mark(100);
+ byte[] start = new byte[100];
+ int read = is.read(start, 0, 100);
+ if (read == -1) {
+ throw new XPathException("Query source file is empty");
+ }
+ is.reset();
+ return inferEncoding(start, read);
+ } catch (IOException e) {
+ throw new XPathException("Failed to read query source file", e);
+ }
+ }
+
+ /**
+ * Read a query from an InputStream. The method checks that all characters are valid XML
+ * characters, and also performs normalization of line endings.
+ * @param is the input stream
+ * @param encoding the encoding, or null if the encoding is unknown
+ * @param nameChecker the nameChecker to be used for checking characters
+ * @return the content of the InputStream as a string
+ */
+
+ public static String readInputStream(/*@NotNull*/ InputStream is, /*@Nullable*/ String encoding, /*@NotNull*/ NameChecker nameChecker) throws XPathException {
+ if (encoding == null) {
+ if (!is.markSupported()) {
+ is = new BufferedInputStream(is);
+ }
+ encoding = readEncoding(is);
+ }
+ try {
+ Reader reader = new BufferedReader(new InputStreamReader(is, encoding));
+ return readQueryFromReader(reader, nameChecker);
+ } catch (UnsupportedEncodingException encErr) {
+ XPathException err = new XPathException("Unknown encoding " + Err.wrap(encoding), encErr);
+ err.setErrorCode("XQST0087");
+ throw err;
+ }
+ }
+
+ /**
+ * Read a query from a Reader. The method checks that all characters are valid XML
+ * characters.
+ * @param reader The Reader supplying the input
+ * @param nameChecker the NameChecker to be used
+ * @return the text of the query module, as a string
+ * @throws XPathException if the file cannot be read or contains illegal characters
+ */
+
+ private static String readQueryFromReader(/*@NotNull*/ Reader reader, /*@NotNull*/ NameChecker nameChecker) throws XPathException {
+ try {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.LARGE);
+ char[] buffer = new char[2048];
+ boolean first = true;
+ int actual;
+ int line = 1; // track line/column position for reporting bad characters
+ int column = 1;
+ while (true) {
+ actual = reader.read(buffer, 0, 2048);
+ if (actual < 0) {
+ break;
+ }
+ for (int c=0; c<actual;) {
+ int ch32 = buffer[c++];
+ if (ch32 == '\n') {
+ line++;
+ column = 0;
+ }
+ column++;
+ if (UTF16CharacterSet.isHighSurrogate(ch32)) {
+ char low = buffer[c++];
+ ch32 = UTF16CharacterSet.combinePair((char)ch32, low);
+ }
+ if (!nameChecker.isValidChar(ch32)) {
+ XPathException err = new XPathException("The query file contains a character that is illegal in XML " +
+ nameChecker.getXMLVersion() +
+ " (line=" + line +
+ " column=" + column +
+ " value=x" + Integer.toHexString(ch32) + ')');
+ err.setErrorCode("XPST0003");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ }
+ if (first) {
+ first = false;
+ if (buffer[0]=='\ufeff') {
+ sb.append(buffer, 1, actual-1);
+ } else {
+ sb.append(buffer, 0, actual);
+ }
+ } else {
+ sb.append(buffer, 0, actual);
+ }
+ }
+ return sb.condense().toString();
+ } catch (IOException ioErr) {
+ throw new XPathException("Failed to read input file", ioErr);
+ }
+ }
+
+ /**
+ * Attempt to infer the encoding of a file by reading its byte order mark and if necessary
+ * the encoding declaration in the query prolog
+ * @param start the bytes appearing at the start of the file
+ * @param read the number of bytes supplied
+ * @return the inferred encoding
+ * @throws XPathException
+ */
+
+ private static String inferEncoding(byte[] start, int read) throws XPathException {
+ // Debugging code
+// StringBuffer sb = new StringBuffer(read*5);
+// for (int i=0; i<read; i++) sb.append(Integer.toHexString(start[i]&255) + ", ");
+// System.err.println(sb);
+ // End of debugging code
+
+ if (read >= 2) {
+ if (ch(start[0]) == 0xFE && ch(start[1]) == 0xFF) {
+ return "UTF-16";
+ } else if (ch(start[0]) == 0xFF && ch(start[1]) == 0xFE) {
+ return "UTF-16LE";
+ }
+ }
+ if (read >= 3) {
+ if (ch(start[0]) == 0xEF && ch(start[1]) == 0xBB && ch(start[2]) == 0xBF) {
+ return "UTF-8";
+ }
+ }
+
+ // Try to handle a UTF-16 file with no BOM
+ if (read >= 8 && start[0] == 0 && start[2] == 0 && start[4] == 0 && start[6] == 0) {
+ return "UTF-16";
+ }
+ if (read >= 8 && start[1] == 0 && start[3] == 0 && start[5] == 0 && start[7] == 0) {
+ return "UTF-16LE";
+ }
+
+ // In all other cases, we assume an encoding that has ISO646 as a subset
+
+ // Note, we don't care about syntax errors here: they'll be reported later. We just need to
+ // establish the encoding.
+ int i=0;
+ String tok = readToken(start, i, read);
+ if (Whitespace.trim(tok).equals("xquery")) {
+ i += tok.length();
+ } else {
+ return "UTF-8";
+ }
+ tok = readToken(start, i, read);
+ if (Whitespace.trim(tok).equals("encoding")) {
+ // "version" can be omitted in XQuery 3.0
+ i += tok.length();
+ } else {
+ if (Whitespace.trim(tok).equals("version")) {
+ i += tok.length();
+ } else {
+ return "UTF-8";
+ }
+ tok = readToken(start, i, read);
+ if (tok == null) {
+ return "UTF-8";
+ }
+ i += tok.length();
+ tok = readToken(start, i, read);
+ if (Whitespace.trim(tok).equals("encoding")) {
+ i += tok.length();
+ } else {
+ return "UTF-8";
+ }
+ }
+ tok = Whitespace.trim(readToken(start, i, read));
+ if (tok.startsWith("\"") && tok.endsWith("\"") && tok.length()>2) {
+ return tok.substring(1, tok.length()-1);
+ } else if (tok.startsWith("'") && tok.endsWith("'") && tok.length()>2) {
+ return tok.substring(1, tok.length()-1);
+ } else {
+ throw new XPathException("Unrecognized encoding " + Err.wrap(tok) + " in query prolog");
+ }
+
+ }
+
+ /**
+ * Simple tokenizer for use when reading the encoding declaration in the query prolog. A token
+ * is a sequence of characters delimited either by whitespace, or by single or double quotes; the
+ * quotes if present are returned as part of the token.
+ * @param in the character buffer
+ * @param i offset where to start reading
+ * @param len the length of buffer
+ * @return the next token
+ */
+
+ private static String readToken(byte[] in, int i, int len) {
+ int p = i;
+ while (p<len && " \n\r\t".indexOf(ch(in[p])) >= 0) {
+ p++;
+ }
+ if (ch(in[p])=='"') {
+ p++;
+ while (p<len && ch(in[p]) != '"') {
+ p++;
+ }
+ } else if (ch(in[p])=='\'') {
+ p++;
+ while (p<len && ch(in[p]) != '\'') {
+ p++;
+ }
+ } else {
+ while (p<len && " \n\r\t".indexOf(ch(in[p])) < 0) {
+ p++;
+ }
+ }
+ if (p>=len) {
+ return new String(in, i, len-i);
+ }
+ FastStringBuffer sb = new FastStringBuffer(p-i+1);
+ for (int c=i; c<=p; c++) {
+ sb.append((char)ch(in[c]));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Convert a byte containing an ASCII character to that character
+ * @param b the input byte
+ * @return the ASCII character
+ */
+
+ private static int ch(byte b) {
+ return ((int)b) & 0xff;
+ }
+
+}
+
diff --git a/sf/saxon/query/QueryResult.java b/sf/saxon/query/QueryResult.java
new file mode 100644
index 0000000..87a53a7
--- /dev/null
+++ b/sf/saxon/query/QueryResult.java
@@ -0,0 +1,272 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.*;
+import net.sf.saxon.lib.SerializerFactory;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.tiny.TinyBuilder;
+import net.sf.saxon.type.Type;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.stream.StreamResult;
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Properties;
+
+/**
+ * This utility class takes the result sequence produced by a query, and wraps it as
+ * an XML document. The class is never instantiated.
+ */
+public class QueryResult {
+
+ /*@NotNull*/ public static String RESULT_NS = "http://saxon.sf.net/xquery-results";
+
+ private QueryResult() {
+ }
+
+ /**
+ * Convenience method to serialize a node using default serialization options, placing
+ * the result in a string.
+ * @param nodeInfo the node to be serialized. This must not be an attribute or namespace node.
+ * @return the serialization of the node
+ * @since 9.0
+ * @throws net.sf.saxon.trans.XPathException if a serialization error occurs
+ */
+
+ public static String serialize(/*@NotNull*/ NodeInfo nodeInfo) throws XPathException {
+ StringWriter sw = new StringWriter();
+ Properties props = new Properties();
+ props.setProperty("method", "xml");
+ props.setProperty("indent", "yes");
+ props.setProperty("omit-xml-declaration", "yes");
+ serialize(nodeInfo, new StreamResult(sw), props);
+ return sw.toString();
+ }
+
+ /**
+ * Take the results of a query (or any other SequenceIterator) and create
+ * an XML document containing copies of all items in the sequence, each item wrapped in a containing
+ * element that identifies its type
+ * @param iterator The values to be wrapped
+ * @param config The Saxon configuration used to evaluate the query
+ * @return the document containing the wrapped results
+ * @throws XPathException if any failure occurs
+ * @since 8.8
+ */
+
+ /*@Nullable*/ public static DocumentInfo wrap(SequenceIterator iterator, /*@NotNull*/ Configuration config) throws XPathException {
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ TinyBuilder builder = new TinyBuilder(pipe);
+ NamespaceReducer reducer = new NamespaceReducer(builder);
+ ComplexContentOutputter outputter = new ComplexContentOutputter(pipe);
+ outputter.setReceiver(reducer);
+ sendWrappedSequence(iterator, outputter);
+ return (DocumentInfo)builder.getCurrentRoot();
+ }
+
+ /**
+ * Take a sequence supplied in the form of an iterator and generate a wrapped represention of the
+ * items in the sequence, the wrapped representation being a sequence of events sent to a supplied
+ * Receiver, in which each item is wrapped in a containing element that identifies its type
+ * @param iterator the input sequence
+ * @param destination the Receiver to accept the wrapped output
+ * @since 8.8
+ * @throws net.sf.saxon.trans.XPathException if a failure occurs processing the input iterator
+ */
+
+ public static void sendWrappedSequence(SequenceIterator iterator, Receiver destination) throws XPathException {
+ SerializerFactory sf = destination.getPipelineConfiguration().getConfiguration().getSerializerFactory();
+ SequenceCopier.copySequence(iterator, sf.newSequenceWrapper(destination));
+ }
+
+ /**
+ * Serialize a document containing wrapped query results (or any other document, in fact)
+ * as XML.
+ * @param node The document or element to be serialized
+ * @param destination The Result object to contain the serialized form
+ * @param outputProperties Serialization options, as defined in JAXP. The requested properties are
+ * not validated.
+ * @param config The Configuration. This argument is ignored
+ * @throws XPathException If serialization fails
+ * @deprecated since 8.9; use {@link #serialize(NodeInfo, Result, Properties)} instead.
+ */
+
+ //@SuppressWarnings({"UnusedDeclaration"})
+ public static void serialize(/*@NotNull*/ NodeInfo node, /*@NotNull*/ Result destination, /*@NotNull*/ Properties outputProperties, Configuration config)
+ throws XPathException {
+ serialize(node, destination, outputProperties);
+ }
+
+ /**
+ * Serialize a document containing wrapped query results (or any other document, in fact)
+ * as XML.
+ * @param node The document or element to be serialized
+ * @param destination The Result object to contain the serialized form
+ * @param outputProperties Serialization options as defined in JAXP. The requested properties are
+ * not validated.
+ * @throws XPathException If serialization fails
+ * @since 8.9
+ */
+
+ public static void serialize(/*@NotNull*/ NodeInfo node, /*@NotNull*/ Result destination, /*@NotNull*/ Properties outputProperties)
+ throws XPathException {
+ Configuration config = node.getConfiguration();
+ serializeSequence(SingletonIterator.makeIterator(node), config, destination, outputProperties);
+ }
+
+ /**
+ * Serialize an arbitrary sequence, without any special wrapping.
+ * @param iterator the sequence to be serialized
+ * @param config the configuration (gives access to information such as the NamePool)
+ * @param destination the output stream to which the output is to be written
+ * @param outputProps a set of serialization properties as defined in JAXP. The requested properties are
+ * not validated.
+ * @throws XPathException if any failure occurs
+ * @since 8.9
+ */
+
+ public static void serializeSequence(
+ /*@NotNull*/ SequenceIterator iterator, /*@NotNull*/ Configuration config, /*@NotNull*/ OutputStream destination, /*@NotNull*/ Properties outputProps)
+ throws XPathException {
+ serializeSequence(iterator, config, new StreamResult(destination), outputProps);
+ try {
+ destination.flush();
+ } catch (IOException err) {
+ throw new XPathException(err);
+ }
+ }
+
+ /**
+ * Serialize an arbitrary sequence, without any special wrapping.
+ * @param iterator the sequence to be serialized
+ * @param config the configuration (gives access to information such as the NamePool)
+ * @param writer the writer to which the output is to be written
+ * @param outputProps a set of serialization properties as defined in JAXP. The requested properties are
+ * not validated.
+ * @throws XPathException if any failure occurs
+ * @since 8.9
+ */
+
+ public static void serializeSequence(
+ /*@NotNull*/ SequenceIterator iterator, /*@NotNull*/ Configuration config, /*@NotNull*/ Writer writer, /*@NotNull*/ Properties outputProps)
+ throws XPathException {
+ serializeSequence(iterator, config, new StreamResult(writer), outputProps);
+ try {
+ writer.flush();
+ } catch (IOException err) {
+ throw new XPathException(err);
+ }
+ }
+
+ /**
+ * Serialize a sequence to a given result
+ * @param iterator the sequence to be serialized
+ * @param config the Saxon Configuration
+ * @param result the destination to receive the output
+ * @param outputProperties the serialization properties to be used. The requested properties are
+ * not validated.
+ * @throws XPathException if any failure occurs
+ * @since 9.0
+ */
+
+ public static void serializeSequence(
+ /*@NotNull*/ SequenceIterator iterator, /*@NotNull*/ Configuration config, /*@NotNull*/ Result result, /*@NotNull*/ Properties outputProperties)
+ throws XPathException {
+ SerializerFactory sf = config.getSerializerFactory();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ pipe.setSerializing(true);
+ Receiver receiver = sf.getReceiver(result, pipe, outputProperties);
+ TreeReceiver tr = new TreeReceiver(receiver);
+ tr.open();
+ while (true) {
+ Item item = iterator.next();
+ if (item == null) {
+ break;
+ }
+ tr.append(item, 0, NodeInfo.ALL_NAMESPACES);
+ }
+ tr.close();
+ }
+
+
+ /**
+ * Write an updated document back to disk, using the original URI from which it was read
+ * @param doc an updated document. Must be a document node, or a parentless element, or an
+ * element that has a document node as its parent. The document will be written to the URI
+ * found in the systemId property of this node.
+ * @param outputProperties serialization properties
+ * @param backup true if the old document at that location is to be copied to a backup file
+ * @param log destination for progress messages; if null, no progress messages are written
+ * @throws XPathException if the document has no known URI, if the URI is not a writable location,
+ * or if a serialization error occurs.
+ */
+
+ public static void rewriteToDisk(/*@NotNull*/ NodeInfo doc, /*@NotNull*/ Properties outputProperties, boolean backup, /*@Nullable*/ PrintStream log)
+ throws XPathException {
+ switch (doc.getNodeKind()) {
+ case Type.DOCUMENT:
+ // OK
+ break;
+ case Type.ELEMENT:
+ NodeInfo parent = doc.getParent();
+ if (parent != null && parent.getNodeKind() != Type.DOCUMENT) {
+ throw new XPathException("Cannot rewrite an element node unless it is top-level");
+ }
+ break;
+ default:
+ throw new XPathException("Node to be rewritten must be a document or element node");
+ }
+ String uri = doc.getSystemId();
+ if (uri == null || uri.length() == 0) {
+ throw new XPathException("Cannot rewrite a document with no known URI");
+ }
+ URI u;
+ try {
+ u = new URI(uri);
+ } catch (URISyntaxException e) {
+ throw new XPathException("SystemId of updated document is not a valid URI: " + uri);
+ }
+ File existingFile = new File(u);
+ File dir = existingFile.getParentFile();
+ if (backup && existingFile.exists()) {
+ File backupFile = new File(dir, existingFile.getName() + ".bak");
+ if (log != null) {
+ log.println("Creating backup file " + backupFile);
+ }
+ boolean success = existingFile.renameTo(backupFile);
+ if (!success) {
+ throw new XPathException("Failed to create backup file of " + backupFile);
+ }
+ }
+ if (!existingFile.exists()) {
+ if (log != null) {
+ log.println("Creating file " + existingFile);
+ }
+ try {
+ existingFile.createNewFile();
+ } catch (IOException e) {
+ throw new XPathException("Failed to create new file " + existingFile);
+ }
+ } else {
+ if (log != null) {
+ log.println("Overwriting file " + existingFile);
+ }
+ }
+ Configuration config = doc.getConfiguration();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ SerializerFactory factory = config.getSerializerFactory();
+ Receiver r = factory.getReceiver(new StreamResult(existingFile), pipe, outputProperties);
+ doc.copy(r, CopyOptions.ALL_NAMESPACES, 0);
+ r.close();
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/query/SequenceWrapper.java b/sf/saxon/query/SequenceWrapper.java
new file mode 100644
index 0000000..e0b088f
--- /dev/null
+++ b/sf/saxon/query/SequenceWrapper.java
@@ -0,0 +1,292 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.ObjectValue;
+
+/**
+ * This class can be used in a push pipeline: it accepts any sequence as input, and generates
+ * a document in which the items of the sequence are wrapped by elements containing information about
+ * the types of the items in the input sequence.
+ */
+
+public class SequenceWrapper extends SequenceReceiver {
+
+ public static final String RESULT_NS = QueryResult.RESULT_NS;
+
+ private Receiver out;
+ private int depth = 0;
+
+ //@SuppressWarnings({"FieldCanBeLocal"})
+ private FingerprintedQName resultSequence;
+ private FingerprintedQName resultDocument;
+ private FingerprintedQName resultElement;
+ private FingerprintedQName resultAttribute;
+ private FingerprintedQName resultText;
+ private FingerprintedQName resultComment;
+ private FingerprintedQName resultPI;
+ private FingerprintedQName resultNamespace;
+ private FingerprintedQName resultAtomicValue;
+ private FingerprintedQName xsiType;
+
+ /**
+ * Create a sequence wrapper. This creates an XML representation of the items sent to destination
+ * in which the types of all items are made explicit
+ * @param destination the sequence being wrapped
+ */
+
+ public SequenceWrapper(Receiver destination) {
+ super(destination.getPipelineConfiguration());
+ out = destination;
+ // out = new TracingFilter(out);
+ }
+
+ public Receiver getDestination() {
+ return out;
+ }
+
+ public void open() throws XPathException {
+
+ resultSequence = new FingerprintedQName("result", RESULT_NS, "sequence");
+ resultDocument = new FingerprintedQName("result", RESULT_NS, "document");
+ resultElement = new FingerprintedQName("result", RESULT_NS, "element");
+ resultAttribute = new FingerprintedQName("result", RESULT_NS, "attribute");
+ resultText = new FingerprintedQName("result", RESULT_NS, "text");
+ resultComment = new FingerprintedQName("result", RESULT_NS, "comment");
+ resultPI = new FingerprintedQName("result", RESULT_NS, "processing-instruction");
+ resultNamespace = new FingerprintedQName("result", RESULT_NS, "namespace");
+ resultAtomicValue = new FingerprintedQName("result", RESULT_NS, "atomic-value");
+ xsiType = new FingerprintedQName("xsi", NamespaceConstant.SCHEMA_INSTANCE, "type");
+
+ out.open();
+ out.startDocument(0);
+
+ out.startElement(resultSequence, Untyped.getInstance(), 0, 0);
+ out.namespace(new NamespaceBinding("result", RESULT_NS), 0);
+ out.namespace(new NamespaceBinding("xs", NamespaceConstant.SCHEMA), 0);
+ out.namespace(new NamespaceBinding("xsi", NamespaceConstant.SCHEMA_INSTANCE), 0);
+ out.startContent();
+
+ }
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ out.startElement(resultDocument, Untyped.getInstance(), 0, 0);
+ out.startContent();
+ depth++;
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ out.endElement();
+ depth--;
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param nameCode integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties properties of the element node
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (depth++ == 0) {
+ out.startElement(resultElement, Untyped.getInstance(), 0, 0);
+ out.startContent();
+ }
+ out.startElement(nameCode, typeCode, locationId, properties);
+ }
+
+ /**
+ * End of element
+ */
+
+ public void endElement() throws XPathException {
+ out.endElement();
+ if (--depth == 0) {
+ out.endElement(); // close the wrapping element
+ }
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param attName The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ if (depth==0) {
+ out.startElement(resultAttribute, Untyped.getInstance(), 0, 0);
+ if (!attName.isInNamespace("")) {
+ out.namespace(attName.getNamespaceBinding(), 0);
+ }
+ out.attribute(attName, typeCode, value, locationId, properties);
+ out.startContent();
+ out.endElement();
+ } else {
+ out.attribute(attName, typeCode, value, locationId, properties);
+ }
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element; however, duplicates may be reported.
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ *
+ * @param namespaceBinding
+ * @throws IllegalStateException: attempt to output a namespace when there is no open element
+ * start tag
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ if (depth == 0) {
+ out.startElement(resultNamespace, Untyped.getInstance(), 0, 0);
+ out.namespace(namespaceBinding, properties);
+ out.startContent();
+ out.endElement();
+ } else {
+ out.namespace(namespaceBinding, properties);
+ }
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (depth==0) {
+ out.startElement(resultText, Untyped.getInstance(), 0, 0);
+ out.startContent();
+ out.characters(chars, locationId, properties);
+ out.endElement();
+ } else {
+ out.characters(chars, locationId, properties);
+ }
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (depth==0) {
+ out.startElement(resultComment, Untyped.getInstance(), 0, 0);
+ out.startContent();
+ out.comment(chars, locationId, properties);
+ out.endElement();
+ } else {
+ out.comment(chars, locationId, properties);
+ }
+ }
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (depth==0) {
+ out.startElement(resultPI, Untyped.getInstance(), 0, 0);
+ out.startContent();
+ out.processingInstruction(target, data, locationId, properties);
+ out.endElement();
+ } else {
+ out.processingInstruction(target, data, locationId, properties);
+ }
+ }
+
+ /**
+ * Output an item (atomic value or node) to the sequence
+ */
+
+ public void append(/*@NotNull*/ Item item, int locationId, int copyNamespaces) throws XPathException {
+ if (item instanceof FunctionItem) {
+ throw new XPathException("Wrapped output: cannot display a function item");
+ } else if (item instanceof ObjectValue) {
+ throw new XPathException("Wrapped output: cannot display an external Java object");
+ } else if (item instanceof AtomicValue) {
+ final NamePool pool = getNamePool();
+ out.startElement(resultAtomicValue, Untyped.getInstance(), 0, 0);
+ AtomicType type = (AtomicType)((AtomicValue)item).getItemType();
+ int nameCode = type.getNameCode();
+ String prefix = pool.getPrefix(nameCode);
+ String localName = pool.getLocalName(nameCode);
+ String uri = pool.getURI(nameCode);
+ if (prefix.length() == 0) {
+ prefix = pool.suggestPrefixForURI(uri);
+ if (prefix == null) {
+ prefix = "p" + uri.hashCode();
+ }
+ }
+ String displayName = prefix + ':' + localName;
+ out.namespace(new NamespaceBinding(prefix, uri), 0);
+ out.attribute(xsiType, BuiltInAtomicType.UNTYPED_ATOMIC, displayName, 0, 0);
+ out.startContent();
+ out.characters(item.getStringValue(), 0, 0);
+ out.endElement();
+ } else {
+ ((NodeInfo)item).copy(this, (CopyOptions.ALL_NAMESPACES | CopyOptions.TYPE_ANNOTATIONS), locationId);
+ }
+ }
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+
+ public void startContent() throws XPathException {
+ out.startContent();
+ }
+
+ /**
+ * Notify the end of the event stream
+ */
+
+ public void close() throws XPathException {
+ out.endElement(); // close the result:sequence element
+ out.endDocument();
+ out.close();
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/query/StaticQueryContext.java b/sf/saxon/query/StaticQueryContext.java
new file mode 100644
index 0000000..7ee32e7
--- /dev/null
+++ b/sf/saxon/query/StaticQueryContext.java
@@ -0,0 +1,1171 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.CollationMap;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.GlobalParam;
+import net.sf.saxon.expr.instruct.GlobalVariable;
+import net.sf.saxon.expr.instruct.LocationMap;
+import net.sf.saxon.expr.parser.CodeInjector;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.lib.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.TraceCodeInjector;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.SequenceType;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.*;
+
+/**
+ * StaticQueryContext contains information used to build a StaticContext for use when processing XQuery
+ * expressions.
+ *
+ * <p>Despite its name, <code>StaticQueryContext</code> no longer implements the <code>StaticContext</code>
+ * interface, which means it cannot be used directly by Saxon when parsing a query. Instead it is first copied
+ * to create a <code>QueryModule</code> object, which does implement the <code>StaticContext</code> interface.
+ *
+ * <p>The application constructs a StaticQueryContext
+ * and initializes it with information about the context, for example, default namespaces, base URI, and so on.
+ * When a query is compiled using this StaticQueryContext, the query parser makes a copy of the StaticQueryContext
+ * and uses this internally, modifying it with information obtained from the query prolog, as well as information
+ * such as namespace and variable declarations that can occur at any point in the query. The query parser does
+ * not modify the original StaticQueryContext supplied by the calling application, which may therefore be used
+ * for compiling multiple queries, serially or even in multiple threads.</p>
+ *
+ * <p>This class forms part of Saxon's published XQuery API. Methods that
+ * are considered stable are labelled with the JavaDoc "since" tag.
+ * The value 8.4 indicates a method introduced at or before Saxon 8.4; other
+ * values indicate the version at which the method was introduced.</p>
+ *
+ * <p>In the longer term, this entire API may at some stage be superseded by a proposed
+ * standard Java API for XQuery.</p>
+ *
+ * @since 8.4
+ */
+
+public class StaticQueryContext {
+
+ private Configuration config;
+ private NamePool namePool;
+ private String baseURI;
+ private HashMap<String, String> userDeclaredNamespaces;
+ /*@Nullable*/ private Set<GlobalVariable> userDeclaredVariables;
+ private boolean inheritNamespaces = true;
+ private boolean preserveNamespaces = true;
+ private int constructionMode = Validation.PRESERVE;
+ /*@Nullable*/ private NamespaceResolver externalNamespaceResolver = null;
+ private String defaultFunctionNamespace;
+ /*@Nullable*/ private String defaultElementNamespace = NamespaceConstant.NULL;
+ private ItemType requiredContextItemType = AnyItemType.getInstance();
+ private boolean preserveSpace = false;
+ private boolean defaultEmptyLeast = true;
+ /*@Nullable*/ private ModuleURIResolver moduleURIResolver;
+ private ErrorListener errorListener;
+ /*@Nullable*/ private CodeInjector codeInjector;
+ private boolean isUpdating = false;
+ private DecimalValue languageVersion = DecimalValue.ONE;
+ private CollationMap collationMap;
+ //private LocationMap locationMap;
+ private boolean schemaAware;
+
+
+ /**
+ * Private constructor used when copying a context
+ */
+
+ protected StaticQueryContext() {
+ }
+
+ /**
+ * Create a StaticQueryContext using a given Configuration. This creates a StaticQueryContext for a main module
+ * (that is, a module that is not a library module).
+ * @param config the Saxon Configuration
+ *
+ * @since 8.4
+ * @deprecated since 9.2. Use config.newStaticQueryContext(). This will create a StaticQueryContext with
+ * capabilities appropriate to the configuration (for example, offering XQuery 1.1 support).
+ */
+
+ public StaticQueryContext(/*@NotNull*/ Configuration config) {
+ this(config.getDefaultStaticQueryContext());
+ this.config = config;
+ namePool = config.getNamePool();
+ errorListener = config.getErrorListener();
+ moduleURIResolver = config.getModuleURIResolver();
+ if (errorListener instanceof StandardErrorListener) {
+ errorListener = ((StandardErrorListener)errorListener).makeAnother(Configuration.XQUERY);
+ ((StandardErrorListener)errorListener).setRecoveryPolicy(Configuration.DO_NOT_RECOVER);
+ }
+ collationMap = new CollationMap(config.getCollationMap());
+ //locationMap = new LocationMap();
+ schemaAware = (Boolean)config.getConfigurationProperty(FeatureKeys.XQUERY_SCHEMA_AWARE);
+// executable = new Executable(config);
+// executable.setCollationTable(new CollationMap(config.getCollationMap()));
+// executable.setHostLanguage(Configuration.XQUERY);
+// executable.setLocationMap(new LocationMap());
+// executable.setSchemaAware((Boolean)config.getConfigurationProperty(FeatureKeys.XQUERY_SCHEMA_AWARE));
+ reset();
+ }
+
+ /**
+ * Create a StaticQueryContext using a given Configuration. This creates a StaticQueryContext for a main module
+ * (that is, a module that is not a library module).
+ *
+ * <p>This method is primarily for internal use. The recommended way to create a StaticQueryContext is by
+ * using the factory method Configuration.newStaticQueryContext().</p>
+ * @param config the Saxon Configuration
+ * @param initial if set, this is the StaticQueryContext owned by the Configuration
+ */
+
+ public StaticQueryContext(/*@NotNull*/ Configuration config, boolean initial) {
+ if (initial) {
+ this.config = config;
+ namePool = config.getNamePool();
+ errorListener = config.getErrorListener();
+ moduleURIResolver = config.getModuleURIResolver();
+ if (errorListener instanceof StandardErrorListener) {
+ errorListener = ((StandardErrorListener)errorListener).makeAnother(Configuration.XQUERY);
+ ((StandardErrorListener)errorListener).setRecoveryPolicy(Configuration.DO_NOT_RECOVER);
+ }
+ collationMap = new CollationMap(config.getCollationMap());
+ //locationMap = new LocationMap();
+ //schemaAware = (Boolean)config.getConfigurationProperty(FeatureKeys.XQUERY_SCHEMA_AWARE);
+// executable = new Executable(config);
+// executable.setCollationTable(new CollationMap(config.getCollationMap()));
+// executable.setHostLanguage(Configuration.XQUERY);
+// executable.setLocationMap(new LocationMap());
+ reset();
+ } else {
+ copyFrom(config.getDefaultStaticQueryContext());
+ }
+ }
+
+
+ /**
+ * Create a copy of a supplied StaticQueryContext
+ * @param c the StaticQueryContext to be copied
+ */
+
+ public StaticQueryContext(/*@NotNull*/ StaticQueryContext c) {
+ copyFrom(c);
+ }
+
+ private void copyFrom(/*@NotNull*/ StaticQueryContext c) {
+ config = c.config;
+ namePool = c.namePool;
+ baseURI = c.baseURI;
+ moduleURIResolver = c.moduleURIResolver;
+ if (c.userDeclaredNamespaces != null) {
+ userDeclaredNamespaces = new HashMap<String, String>(c.userDeclaredNamespaces);
+ }
+ if (c.userDeclaredVariables != null) {
+ userDeclaredVariables = new HashSet<GlobalVariable>(c.userDeclaredVariables);
+ }
+ inheritNamespaces = c.inheritNamespaces;
+ preserveNamespaces = c.preserveNamespaces;
+ constructionMode = c.constructionMode;
+ externalNamespaceResolver = c.externalNamespaceResolver;
+ defaultElementNamespace = c.defaultElementNamespace;
+ defaultFunctionNamespace = c.defaultFunctionNamespace;
+ requiredContextItemType = c.requiredContextItemType;
+ preserveSpace = c.preserveSpace;
+ defaultEmptyLeast = c.defaultEmptyLeast;
+ moduleURIResolver = c.moduleURIResolver;
+ errorListener = c.errorListener;
+ codeInjector = c.codeInjector;
+ isUpdating = c.isUpdating;
+ languageVersion = c.languageVersion;
+ collationMap = new CollationMap(c.collationMap);
+ //locationMap = new LocationMap();
+ schemaAware = c.schemaAware;
+// executable = new Executable(config);
+// executable.setCollationTable(new CollationMap(c.executable.getCollationTable()));
+// executable.setHostLanguage(Configuration.XQUERY);
+// executable.setLocationMap(new LocationMap());
+// executable.setSchemaAware(c.executable.isSchemaAware());
+ }
+
+
+ /**
+ * Reset the state of this StaticQueryContext to an uninitialized state
+ *
+ * @since 8.4
+ */
+
+ public void reset() {
+ userDeclaredNamespaces = new HashMap<String, String>(10);
+ externalNamespaceResolver = null;
+ errorListener = config.getErrorListener();
+ if (errorListener instanceof StandardErrorListener) {
+ errorListener = ((StandardErrorListener)errorListener).makeAnother(Configuration.XQUERY);
+ ((StandardErrorListener)errorListener).setRecoveryPolicy(Configuration.DO_NOT_RECOVER);
+ }
+ constructionMode = getConfiguration().isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY) ?
+ Validation.PRESERVE : Validation.STRIP;
+ preserveSpace = false;
+ defaultEmptyLeast = true;
+ requiredContextItemType = AnyItemType.getInstance();
+ defaultFunctionNamespace = NamespaceConstant.FN;
+ defaultElementNamespace = NamespaceConstant.NULL;
+ moduleURIResolver = config.getModuleURIResolver();
+ clearNamespaces();
+ isUpdating = false;
+ languageVersion = DecimalValue.ONE;
+
+ }
+
+ /**
+ * Set the Configuration options
+ * @param config the Saxon Configuration
+ * @throws IllegalArgumentException if the configuration supplied is different from the existing
+ * configuration
+ * @since 8.4
+ */
+
+ public void setConfiguration(/*@NotNull*/ Configuration config) {
+ if (this.config != null && this.config != config) {
+ throw new IllegalArgumentException("Configuration cannot be changed dynamically");
+ }
+ this.config = config;
+ namePool = config.getNamePool();
+ }
+
+ /**
+ * Get the Configuration options
+ * @return the Saxon configuration
+ * @since 8.4
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the executable containing this query
+ * @return the executable (which is newly created by this method)
+ */
+
+ /*@NotNull*/ public Executable getExecutable() {
+ Executable executable = new Executable(config);
+ executable.setCollationMap(collationMap);
+ executable.setLocationMap(new LocationMap());
+ executable.setSchemaAware(schemaAware);
+ executable.setHostLanguage(Configuration.XQUERY, getLanguageVersion().equals(DecimalValue.THREE));
+ return executable;
+ }
+
+ /**
+ * Say whether this query is schema-aware
+ * @param aware true if this query is schema-aware
+ * @since 9.2.1.2
+ */
+
+ public void setSchemaAware(boolean aware) {
+ this.schemaAware = aware;
+ }
+
+ /**
+ * Ask whether this query is schema-aware
+ * @return true if this query is schema-aware
+ * @since 9.2.1.2
+ */
+
+ public boolean isSchemaAware() {
+ return schemaAware;
+ }
+
+ /**
+ * Set the Base URI of the query
+ * @param baseURI the base URI of the query, or null to indicate that no base URI is available
+ * @since 8.4
+ */
+
+ public void setBaseURI(String baseURI) {
+ this.baseURI = baseURI;
+ }
+
+ /**
+ * Convenience method for building Saxon's internal representation of a source XML
+ * document. The document will be built using Configuration (and NamePool) associated
+ * with this StaticQueryContext.
+ *
+ * <p>This method is retained for backwards compatibility; however, it is merely a wrapper
+ * around the method {@link Configuration#buildDocument}, which should be called in preference.</p>
+ *
+ * @param source Any javax.xml.transform.Source object representing the document against
+ * which queries will be executed. Note that a Saxon {@link net.sf.saxon.om.DocumentInfo DocumentInfo}
+ * (indeed any {@link net.sf.saxon.om.NodeInfo NodeInfo})
+ * can be used as a Source. To use a third-party DOM Document as a source, create an instance of
+ * {@link javax.xml.transform.dom.DOMSource DOMSource} to wrap it.
+ * <p>For additional control over the way in which the source document is processed,
+ * supply an {@link net.sf.saxon.lib.AugmentedSource AugmentedSource} object and set appropriate
+ * options on the object.</p>
+ * @return the DocumentInfo representing the root node of the resulting document object.
+ * @since 8.4
+ * @deprecated since 9.2: use {@link Configuration#buildDocument(javax.xml.transform.Source)}
+ * @throws XPathException if building the document fails
+ */
+
+ /*@NotNull*/ public DocumentInfo buildDocument(Source source) throws XPathException {
+ return config.buildDocument(source);
+ }
+
+ /**
+ * Set the language version.
+ * @param version The XQuery language version. Must be "1.0" or "3.0" (the value 1.1 is also
+ * accepted temporarily).
+ * Note that XQuery 3.0 language features cannot be used with XQuery Updates. The Query
+ * Prolog must also specify version="3.0".
+ * @since 9.2; changed in 9.3 to expect a DecimalValue rather than a string
+ */
+
+ public void setLanguageVersion(DecimalValue version) {
+ if (DecimalValue.ONE.equals(version) || DecimalValue.THREE.equals(version)) {
+ languageVersion = version;
+ } else if (DecimalValue.ONE_POINT_ONE.equals(version)) {
+ languageVersion = DecimalValue.THREE;
+ } else {
+ throw new IllegalArgumentException("languageVersion = " + version);
+ }
+ }
+
+ /**
+ * Get the language version
+ * @return the language version. Either "1.0" or "1.1". Default is "1.0".
+ * @since 9.2; changed in 9.3 to return a DecimalValue rather than a string.
+ */
+
+ public DecimalValue getLanguageVersion() {
+ return languageVersion;
+ }
+
+ /**
+ * Get any extension function library that was previously set.
+ * @return the extension function library, or null if none has been set. The result will always be null if called
+ * in Saxon-HE; setting an extension function library requires the Saxon-PE or Saxon-EE versions of this class.
+ *
+ * @since 9.4
+ */
+
+
+ /*@Nullable*/ public FunctionLibrary getExtensionFunctionLibrary() {
+ return null;
+ }
+
+
+ /**
+ * Ask whether compile-time generation of trace code was requested
+ * @since 9.0
+ * @return true if compile-time generation of code was requested
+ */
+
+ public boolean isCompileWithTracing() {
+ return codeInjector instanceof TraceCodeInjector;
+ }
+
+ /**
+ * Request compile-time generation of trace code (or not)
+ * @param trace true if compile-time generation of trace code is required
+ * @since 9.0
+ */
+
+ public void setCompileWithTracing(boolean trace) {
+ if (trace) {
+ codeInjector = new TraceCodeInjector();
+ } else {
+ codeInjector = null;
+ }
+ }
+
+ /**
+ * Request that the parser should insert custom code into the expression tree
+ * by calling a supplied CodeInjector to process each expression as it is parsed,
+ * for example for tracing or performance measurement
+ * @param injector the CodeInjector to be used. May be null, in which case
+ * no code is injected
+ * @since 9.4
+ */
+
+ public void setCodeInjector( /*@Nullable*/ CodeInjector injector) {
+ this.codeInjector = injector;
+ }
+
+ /**
+ * Get any CodeInjector that has been registered
+ * @return the registered CodeInjector, or null
+ * @since 9.4
+ */
+
+ /*@Nullable*/ public CodeInjector getCodeInjector() {
+ return codeInjector;
+ }
+
+ /**
+ * Ask whether XQuery Update is allowed
+ * @return true if XQuery update is supported by queries using this context
+ */
+
+ public boolean isUpdating() {
+ return isUpdating;
+ }
+
+ /**
+ * Set the namespace inheritance mode
+ *
+ * @param inherit true if namespaces are inherited, false if not
+ * @since 8.4
+ */
+
+ public void setInheritNamespaces(boolean inherit) {
+ inheritNamespaces = inherit;
+ }
+
+ /**
+ * Get the namespace inheritance mode
+ *
+ * @return true if namespaces are inherited, false if not
+ * @since 8.4
+ */
+
+ public boolean isInheritNamespaces() {
+ return inheritNamespaces;
+ }
+
+ /**
+ * Set the namespace copy mode
+ *
+ * @param inherit true if namespaces are preserved, false if not
+ * @since 8.4
+ */
+
+ public void setPreserveNamespaces(boolean inherit) {
+ preserveNamespaces = inherit;
+ }
+
+ /**
+ * Get the namespace copy mode
+ *
+ * @return true if namespaces are preserved, false if not
+ * @since 8.4
+ */
+
+ public boolean isPreserveNamespaces() {
+ return preserveNamespaces;
+ }
+
+ /**
+ * Set the construction mode for this module
+ *
+ * @param mode one of {@link net.sf.saxon.lib.Validation#STRIP}, {@link net.sf.saxon.lib.Validation#PRESERVE}
+ * @since 8.4
+ */
+
+ public void setConstructionMode(int mode) {
+ constructionMode = mode;
+ }
+
+ /**
+ * Get the current construction mode
+ *
+ * @return one of {@link net.sf.saxon.lib.Validation#STRIP}, {@link net.sf.saxon.lib.Validation#PRESERVE}
+ * @since 8.4
+ */
+
+ public int getConstructionMode() {
+ return constructionMode;
+ }
+
+ /**
+ * Prepare an XQuery query for subsequent evaluation. The source text of the query
+ * is supplied as a String. The base URI of the query is taken from the static context,
+ * and defaults to the current working directory.
+ *
+ * <p>Note that this interface makes the caller responsible for decoding the query and
+ * presenting it as a string of characters. This means it is likely that any encoding
+ * specified in the query prolog will be ignored.</p>
+ *
+ * @param query The XQuery query to be evaluated, supplied as a string.
+ * @return an XQueryExpression object representing the prepared expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if the syntax of the expression is wrong,
+ * or if it references namespaces, variables, or functions that have not been declared,
+ * or contains other static errors.
+ * @since 8.4
+ */
+
+ /*@NotNull*/ public XQueryExpression compileQuery(/*@NotNull*/ String query) throws XPathException {
+ QueryParser qp = (QueryParser)config.newExpressionParser("XQ", isUpdating, languageVersion);
+ if (codeInjector != null) {
+ qp.setCodeInjector(codeInjector);
+ } else if (config.isCompileWithTracing()) {
+ qp.setCodeInjector(new TraceCodeInjector());
+ }
+ QueryModule mainModule = new QueryModule(this);
+ Executable executable = getExecutable();
+ mainModule.setExecutable(executable);
+ if (languageVersion.equals(DecimalValue.THREE)) {
+ qp.setDisableCycleChecks(true);
+ }
+ return qp.makeXQueryExpression(query, mainModule, config);
+ }
+
+ /**
+ * Prepare an XQuery query for subsequent evaluation. The Query is supplied
+ * in the form of a Reader. The base URI of the query is taken from the static context,
+ * and defaults to the current working directory.
+ *
+ * <p>Note that this interface makes the Reader responsible for decoding the query and
+ * presenting it as a stream of characters. This means it is likely that any encoding
+ * specified in the query prolog will be ignored. Also, some implementations of Reader
+ * cannot handle a byte order mark.</p>
+ *
+ * @param source A Reader giving access to the text of the XQuery query to be compiled.
+ * @return an XPathExpression object representing the prepared expression.
+ * @throws net.sf.saxon.trans.XPathException
+ * if the syntax of the expression is wrong, or if it references namespaces,
+ * variables, or functions that have not been declared, or any other static error is reported.
+ * @throws java.io.IOException if a failure occurs reading the supplied input.
+ * @since 8.4
+ */
+
+ /*@Nullable*/ public XQueryExpression compileQuery(/*@NotNull*/ Reader source)
+ throws XPathException, IOException {
+ char[] buffer = new char[4096];
+ StringBuilder sb = new StringBuilder(4096);
+ while (true) {
+ int n = source.read(buffer);
+ if (n > 0) {
+ sb.append(buffer, 0, n);
+ } else {
+ break;
+ }
+ }
+ return compileQuery(sb.toString());
+ }
+
+ /**
+ * Prepare an XQuery query for subsequent evaluation. The Query is supplied
+ * in the form of a InputStream, with an optional encoding. If the encoding is not specified,
+ * the query parser attempts to obtain the encoding by inspecting the input stream: it looks specifically
+ * for a byte order mark, and for the encoding option in the version declaration of an XQuery prolog.
+ * The encoding defaults to UTF-8.
+ * The base URI of the query is taken from the static context,
+ * and defaults to the current working directory.
+ *
+ * @param source An InputStream giving access to the text of the XQuery query to be compiled, as a stream
+ * of octets
+ * @param encoding The encoding used to translate characters to octets in the query source. The parameter
+ * may be null: in this case the query parser attempts to infer the encoding by inspecting the source,
+ * and if that fails, it assumes UTF-8 encoding
+ * @return an XPathExpression object representing the prepared expression.
+ * @throws net.sf.saxon.trans.XPathException
+ * if the syntax of the expression is wrong, or if it references namespaces,
+ * variables, or functions that have not been declared, or any other static error is reported.
+ * @throws java.io.IOException if a failure occurs reading the supplied input.
+ * @since 8.5
+ */
+ /*@Nullable*/
+ public XQueryExpression compileQuery(/*@NotNull*/ InputStream source, /*@Nullable*/ String encoding)
+ throws XPathException, IOException {
+ String query = QueryReader.readInputStream(source, encoding, config.getNameChecker());
+ return compileQuery(query);
+ }
+
+ /**
+ * Compile an XQuery library module for subsequent evaluation. This method is supported
+ * only in Saxon-EE
+ * @param query the content of the module, as a string
+ * @throws XPathException if a static error exists in the query
+ * @throws UnsupportedOperationException if not using Saxon-EE
+ * @since 9.2 (changed in 9.3 to return void)
+ */
+
+ public void compileLibrary(String query) throws XPathException {
+ throw new UnsupportedOperationException("Separate compilation of query libraries requires Saxon-EE");
+ }
+
+ /**
+ * Prepare an XQuery library module for subsequent evaluation. This method is supported
+ * only in Saxon-EE. The effect of the method is that subsequent query compilations using this static
+ * context can import the module URI without specifying a location hint; the import then takes effect
+ * without requiring the module to be compiled each time it is imported.
+ * @param source the content of the module, as a Reader which supplies the source code
+ * @throws XPathException if a static error exists in the query
+ * @throws IOException if the input cannot be read
+ * @throws UnsupportedOperationException if not using Saxon-EE
+ * @since 9.2 (changed in 9.3 to return void)
+ */
+
+ public void compileLibrary(Reader source)
+ throws XPathException, IOException {
+ throw new UnsupportedOperationException("Separate compilation of query libraries requires Saxon-EE");
+ }
+
+ /**
+ * Prepare an XQuery library module for subsequent evaluation. This method is supported
+ * only in Saxon-EE. The effect of the method is that subsequent query compilations using this static
+ * context can import the module URI without specifying a location hint; the import then takes effect
+ * without requiring the module to be compiled each time it is imported.
+ * @param source the content of the module, as an InputStream which supplies the source code
+ * @param encoding the character encoding of the input stream. May be null, in which case the encoding
+ * is inferred, for example by looking at the query declaration
+ * @throws XPathException if a static error exists in the query
+ * @throws IOException if the input cannot be read
+ * @throws UnsupportedOperationException if not using Saxon-EE
+ * @since 9.2 (changed in 9.3 to return void)
+ */
+
+ public void compileLibrary(InputStream source, /*@Nullable*/ String encoding)
+ throws XPathException, IOException {
+ throw new UnsupportedOperationException("Separate compilation of query libraries requires Saxon-EE");
+ }
+
+ /**
+ * Get a previously compiled library module
+ * @param namespace the module namespace
+ * @return the QueryLibrary if a module with this namespace has been compiled as a library module;
+ * otherwise null. Always null when not using Saxon-EE.
+ * @since 9.3
+ */
+
+ /*@Nullable*/ public QueryLibrary getCompiledLibrary(String namespace) {
+ return null;
+ }
+
+
+ /**
+ * Declare a namespace whose prefix can be used in expressions. This is
+ * equivalent to declaring a prefix in the Query prolog.
+ * Any namespace declared in the Query prolog overrides a namespace declared using
+ * this API.
+ *
+ * @param prefix The namespace prefix. Must not be null. Setting this to "" means that the
+ * namespace will be used as the default namespace for elements and types.
+ * @param uri The namespace URI. Must not be null. The value "" (zero-length string) is used
+ * to undeclare a namespace; it is not an error if there is no existing binding for
+ * the namespace prefix.
+ * @throws NullPointerException if either the prefix or URI is null
+ * @throws IllegalArgumentException if the prefix is "xml" and the namespace is not the XML namespace, or vice
+ * versa; or if the prefix is "xmlns", or the URI is "http://www.w3.org/2000/xmlns/"
+ * @since 9.0
+ */
+
+ public void declareNamespace( /*@Nullable*/ String prefix, /*@Nullable*/ String uri) {
+ if (prefix == null) {
+ throw new NullPointerException("Null prefix supplied to declareNamespace()");
+ }
+ if (uri == null) {
+ throw new NullPointerException("Null namespace URI supplied to declareNamespace()");
+ }
+ if ((prefix.equals("xml") != uri.equals(NamespaceConstant.XML))) {
+ throw new IllegalArgumentException("Misdeclaration of XML namespace");
+ }
+ if (prefix.equals("xmlns") || uri.equals(NamespaceConstant.XMLNS)) {
+ throw new IllegalArgumentException("Misdeclaration of xmlns namespace");
+ }
+ if (prefix.length() == 0) {
+ defaultElementNamespace = uri;
+ }
+ if (uri.length() == 0) {
+ userDeclaredNamespaces.remove(prefix);
+ } else {
+ userDeclaredNamespaces.put(prefix, uri);
+ }
+
+ }
+
+ /**
+ * Clear all the user-declared namespaces
+ *
+ * @since 9.0
+ */
+
+ public void clearNamespaces() {
+ userDeclaredNamespaces.clear();
+ declareNamespace("xml", NamespaceConstant.XML);
+ declareNamespace("xs", NamespaceConstant.SCHEMA);
+ declareNamespace("xsi", NamespaceConstant.SCHEMA_INSTANCE);
+ declareNamespace("fn", NamespaceConstant.FN);
+ declareNamespace("local", NamespaceConstant.LOCAL);
+ declareNamespace("err", NamespaceConstant.ERR);
+ declareNamespace("saxon", NamespaceConstant.SAXON);
+ declareNamespace("", "");
+
+ }
+
+ /**
+ * Get the map of user-declared namespaces
+ * @return the user-declared namespaces
+ */
+
+ protected HashMap<String, String> getUserDeclaredNamespaces() {
+ return userDeclaredNamespaces;
+ }
+
+ /**
+ * Get the namespace prefixes that have been declared using the method {@link #declareNamespace}
+ * @return an iterator that returns the namespace prefixes that have been explicitly declared, as
+ * strings. The default namespace for elements and types will be included, using the prefix "".
+ * @since 9.0
+ */
+
+ public Iterator<String> iterateDeclaredPrefixes() {
+ return userDeclaredNamespaces.keySet().iterator();
+ }
+
+ /**
+ * Get the namespace URI for a given prefix, which must have been declared using the method
+ * {@link #declareNamespace}. Note that this method will not call the external namespace resolver
+ * to resolve the prefix.
+ * @param prefix the namespace prefix, or "" to represent the null prefix
+ * @return the namespace URI. Returns "" to represent the non-namespace,
+ * null to indicate that the prefix has not been declared
+ */
+
+ public String getNamespaceForPrefix(String prefix) {
+ return userDeclaredNamespaces.get(prefix);
+ }
+
+ /**
+ * Set an external namespace resolver. If a namespace prefix cannot be resolved using any
+ * other mechanism, then as a last resort the external namespace resolver is called to
+ * obtain a URI for the given prefix.
+ *
+ * <p><i>Changed in Saxon 9.0 so that the namespaces resolved by the external namespace resolver
+ * are available at run-time, just like namespaces declared in the query prolog. In consequence,
+ * the supplied NamespaceResolver must now implement the
+ * {@link net.sf.saxon.om.NamespaceResolver#iteratePrefixes()} method.</i></p>
+ *
+ * @param resolver the external namespace resolver
+ */
+
+ public void setExternalNamespaceResolver(NamespaceResolver resolver) {
+ externalNamespaceResolver = resolver;
+ }
+
+ /**
+ * Get the external namespace resolver that has been registered using
+ * setExternalNamespaceResolver(), if any.
+ * @return the external namespace resolver
+ */
+
+ /*@Nullable*/ public NamespaceResolver getExternalNamespaceResolver() {
+ return externalNamespaceResolver;
+ }
+
+ /**
+ * Get the default function namespace
+ *
+ * @return the default function namespace (defaults to the fn: namespace)
+ * @since 8.4
+ */
+
+ public String getDefaultFunctionNamespace() {
+ return defaultFunctionNamespace;
+ }
+
+ /**
+ * Set the default function namespace
+ *
+ * @param defaultFunctionNamespace The namespace to be used for unprefixed function calls
+ * @since 8.4
+ */
+
+ public void setDefaultFunctionNamespace(String defaultFunctionNamespace) {
+ this.defaultFunctionNamespace = defaultFunctionNamespace;
+ }
+
+ /**
+ * Set the default element namespace
+ * @param uri the namespace URI to be used as the default namespace for elements and types
+ * @since 8.4
+ */
+
+ public void setDefaultElementNamespace(String uri) {
+ defaultElementNamespace = uri;
+ declareNamespace("", uri);
+ }
+
+ /**
+ * Get the default namespace for elements and types
+ * @return the namespace URI to be used as the default namespace for elements and types
+ * @since 8.9 Modified in 8.9 to return the namespace URI as a string rather than an integer code
+ */
+
+ /*@Nullable*/ public String getDefaultElementNamespace() {
+ return defaultElementNamespace;
+ }
+
+ /**
+ * Declare a global variable. This has the same effect as including a global variable declaration
+ * in the Query Prolog of the main query module. A static error occurs when compiling the query if the
+ * query prolog contains a declaration of the same variable.
+ * @param qName the qualified name of the variable
+ * @param type the declared type of the variable
+ * @param value the initial value of the variable. May be null if the variable is external.
+ * @param external true if the variable is external, that is, if its value may be set at run-time.
+ * @throws NullPointerException if the value is null, unless the variable is external
+ * @throws XPathException if the value of the variable is not consistent with its type.
+ * @since 9.1
+ */
+
+ public void declareGlobalVariable(
+ StructuredQName qName, /*@NotNull*/ SequenceType type, /*@Nullable*/ Sequence value, boolean external)
+ throws XPathException {
+ if (value == null && !external) {
+ throw new NullPointerException("No initial value for declared variable");
+ }
+ if (!type.matches(value, getConfiguration())) {
+ throw new XPathException("Value of declared variable does not match its type");
+ }
+ GlobalVariable var = (external ? new GlobalParam() : new GlobalVariable());
+ var.setExecutable(getExecutable());
+ var.setVariableQName(qName);
+ var.setRequiredType(type);
+ var.setSelectExpression(Literal.makeLiteral(SequenceTool.toGroundedValue(value)));
+ if (userDeclaredVariables == null) {
+ userDeclaredVariables = new HashSet<GlobalVariable>();
+ }
+ userDeclaredVariables.add(var);
+ }
+
+ /**
+ * Iterate over all the declared global variables
+ * @return an iterator over all the global variables that have been declared. They are returned
+ * as instances of class {@link GlobalVariable}
+ * @since 9.1
+ */
+
+ public Iterator<GlobalVariable> iterateDeclaredGlobalVariables() {
+ if (userDeclaredVariables == null) {
+ List<GlobalVariable> empty = Collections.emptyList();
+ return empty.iterator();
+ } else {
+ return userDeclaredVariables.iterator();
+ }
+ }
+
+ /**
+ * Clear all declared global variables
+ * @since 9.1
+ */
+
+ public void clearDeclaredGlobalVariables() {
+ userDeclaredVariables = null;
+ }
+
+ /**
+ * Set a user-defined ModuleURIResolver for resolving URIs used in "import module"
+ * declarations in the XQuery prolog.
+ * This will be used for resolving URIs in XQuery "import module" declarations, overriding
+ * any ModuleURIResolver that was specified as part of the configuration.
+ * @param resolver the ModuleURIResolver to be used
+ */
+
+ public void setModuleURIResolver(ModuleURIResolver resolver) {
+ moduleURIResolver = resolver;
+ }
+
+ /**
+ * Get the user-defined ModuleURIResolver for resolving URIs used in "import module"
+ * declarations in the XQuery prolog; returns null if none has been explicitly set either
+ * on the StaticQueryContext or on the Configuration.
+ * @return the registered ModuleURIResolver
+ */
+
+ /*@Nullable*/ public ModuleURIResolver getModuleURIResolver() {
+ return moduleURIResolver;
+ }
+
+
+ /**
+ * Declare a named collation. Collations are only available in a query if this method
+ * has been called externally to declare the collation and associate it with an
+ * implementation, in the form of a Java Comparator. The default collation is the
+ * Unicode codepoint collation, unless otherwise specified.
+ *
+ * @param name The name of the collation (technically, a URI)
+ * @param comparator The Java Comparator used to implement the collating sequence
+ * @since 8.4.
+ */
+
+ public void declareCollation(String name, Comparator comparator) {
+ declareCollation(name, new SimpleCollation(comparator));
+ }
+
+
+ /**
+ * Declare a named collation. Collations are only available in a query if this method
+ * has been called externally to declare the collation and associate it with an
+ * implementation, in the form of a Java StringCollator. The default collation is the
+ * Unicode codepoint collation, unless otherwise specified.
+ *
+ * @param name The name of the collation (technically, a URI)
+ * @param comparator The Java Comparator used to implement the collating sequence
+ * @since 8.9.
+ */
+
+ public void declareCollation(String name, StringCollator comparator) {
+ collationMap.setNamedCollation(name, comparator);
+ }
+
+ /**
+ * Set the default collation.
+ * @param name The collation name, as specified in the query prolog. The name
+ * is not validated until it is used.
+ * @since 8.4. Changed in 8.6 so it no longer validates the collation name: this is
+ * because the base URI is not necessarily known at the point where the default
+ * collation is declared.
+ * @throws NullPointerException if the supplied value is null
+ */
+
+ public void declareDefaultCollation(String name) {
+ collationMap.setDefaultCollationName(name);
+ }
+
+ /**
+ * Get a named collation.
+ * @param name the name of the collation, as an absolute URI
+ * @return the collation identified by the given name, as set previously using declareCollation.
+ * If no collation with this name has been declared, the method calls the CollationURIResolver
+ * to locate a collation with this name.
+ * Return null if no collation with this name is found.
+ * @throws NullPointerException if the collation name argument is null.
+ * @since 8.4
+ */
+
+ public StringCollator getCollation(/*@Nullable*/ String name) {
+ if (name == null) {
+ throw new NullPointerException("collationName");
+ }
+ return collationMap.getNamedCollation(name);
+ }
+
+ /**
+ * Get the collation map
+ * @return the collation map, which identifies all the known collations
+ */
+
+ public CollationMap getCollationMap() {
+ return collationMap;
+ }
+
+ /**
+ * Get the name of the default collation.
+ *
+ * @return the name of the default collation; or the name of the codepoint collation
+ * if no default collation has been defined. The name is returned in the form
+ * it was specified; that is, it is not yet resolved against the base URI. (This
+ * is because the base URI declaration can follow the default collation declaration
+ * in the query prolog.) If no default collation has been specified, the "default default"
+ * (that is, the Unicode codepoint collation) is returned.
+ * @since 8.4
+ */
+
+ /*@Nullable*/ public String getDefaultCollationName() {
+ return collationMap.getDefaultCollationName();
+ }
+
+ /**
+ * Get a CollationMap that maps all registered collations to Comparators.
+ * Note that this returns a snapshot copy of the data held by the static context.
+ * This method is provided for internal use by the query processor.
+ * <p/>
+ * This method is intended for internal use.
+ * @return the CollationMap containing all the collations defined in this static context
+ */
+
+ /*@NotNull*/ public CollationMap getAllCollations() {
+ return new CollationMap(collationMap);
+ }
+
+ /**
+ * Declare the static type of the context item. If this type is declared, and if a context item
+ * is supplied when the query is invoked, then the context item must conform to this type (no
+ * type conversion will take place to force it into this type).
+ * @param type the required type of the context item
+ */
+
+ public void setRequiredContextItemType(ItemType type) {
+ requiredContextItemType = type;
+ }
+
+ /**
+ * Get the required type of the context item. If no type has been explicitly declared for the context
+ * item, an instance of AnyItemType (representing the type item()) is returned.
+ * @return the required type of the context item
+ */
+
+ public ItemType getRequiredContextItemType() {
+ return requiredContextItemType;
+ }
+
+ /**
+ * Get the NamePool used for compiling expressions
+ * @return the name pool
+ * @since 8.4
+ */
+
+ public NamePool getNamePool() {
+ return namePool;
+ }
+
+ /**
+ * Get the system ID of the container of the expression. Used to construct error messages.
+ * Note that the systemID and the Base URI are currently identical, but they might be distinguished
+ * in the future.
+ *
+ * @return the Base URI
+ * @since 8.4
+ */
+
+ public String getSystemId() {
+ return baseURI;
+ }
+
+ /**
+ * Get the Base URI of the query, for resolving any relative URI's used
+ * in the expression.
+ * Note that the systemID and the Base URI are currently identical, but they might be distinguished
+ * in the future.
+ * Used by the document() function.
+ *
+ * @return the base URI of the query
+ * @since 8.4
+ */
+
+ public String getBaseURI() {
+ return baseURI;
+ }
+
+ /**
+ * Set the policy for preserving boundary space
+ * @param preserve true if boundary space is to be preserved, false if it is to be stripped
+ * @since 9.0
+ */
+
+ public void setPreserveBoundarySpace(boolean preserve) {
+ preserveSpace = preserve;
+ }
+
+ /**
+ * Ask whether the policy for boundary space is "preserve" or "strip"
+ * @return true if the policy is to preserve boundary space, false if it is to strip it
+ * @since 9.0
+ */
+
+ public boolean isPreserveBoundarySpace() {
+ return preserveSpace;
+ }
+
+ /**
+ * Set the option for where an empty sequence appears in the collation order, if not otherwise
+ * specified in the "order by" clause
+ * @param least true if the empty sequence is considered less than any other value (the default),
+ * false if it is considered greater than any other value
+ * @since 9.0
+ */
+
+ public void setEmptyLeast(boolean least) {
+ defaultEmptyLeast = least;
+ }
+
+ /**
+ * Ask where an empty sequence should appear in the collation order, if not otherwise
+ * specified in the "order by" clause
+ * @return true if the empty sequence is considered less than any other value (the default),
+ * false if it is considered greater than any other value
+ * @since 9.0
+ */
+
+ public boolean isEmptyLeast() {
+ return defaultEmptyLeast;
+ }
+
+ /**
+ * Set the ErrorListener to be used to report compile-time errors in a query. This will also
+ * be the default for the run-time error listener used to report dynamic errors
+ * @param listener the ErrorListener to be used
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ errorListener = listener;
+ if (errorListener instanceof StandardErrorListener) {
+ errorListener = ((StandardErrorListener)errorListener).makeAnother(Configuration.XQUERY);
+ ((StandardErrorListener)errorListener).setRecoveryPolicy(Configuration.DO_NOT_RECOVER);
+ }
+ }
+
+ /**
+ * Get the ErrorListener in use for this static context
+ * @return the registered ErrorListener
+ */
+
+ public ErrorListener getErrorListener() {
+ if (errorListener == null) {
+ errorListener = config.getErrorListener();
+ }
+ return errorListener;
+ }
+
+ /**
+ * Say whether the query is allowed to be updating. XQuery update syntax will be rejected
+ * during query compilation unless this flag is set.
+ * @param updating true if the query is allowed to use the XQuery Update facility
+ * (requires Saxon-EE). If set to false, the query must not be an updating query. If set
+ * to true, it may be either an updating or a non-updating query.
+ * @since 9.1
+ */
+
+ public void setUpdatingEnabled(boolean updating) {
+ isUpdating = updating;
+ }
+
+ /**
+ * Ask whether the query is allowed to be updating
+ * @return true if the query is allowed to use the XQuery Update facility. Note that this
+ * does not necessarily mean that the query is an updating query; but if the value is false,
+ * the it must definitely be non-updating.
+ * @since 9.1
+ */
+
+ public boolean isUpdatingEnabled() {
+ return isUpdating;
+ }
+
+
+
+// public static void main(String[] args) throws Exception {
+// StaticQueryContext c = new StaticQueryContext(new Configuration());
+// c.declareGlobalVariable(
+// new StructuredQName("", "", "ping"),
+// SequenceType.SINGLE_STRING,
+// new StringValue("pong"),
+// true
+// );
+// XQueryExpression exp = c.compileQuery("$ping");
+// DynamicQueryContext env = new DynamicQueryContext(c.getConfiguration());
+// //env.setParameterValue("ping", new StringValue("pang"));
+// exp.run(env, new StreamResult(System.out), null);
+//
+// }
+
+}
+
diff --git a/sf/saxon/query/UnboundFunctionLibrary.java b/sf/saxon/query/UnboundFunctionLibrary.java
new file mode 100644
index 0000000..1ae25d7
--- /dev/null
+++ b/sf/saxon/query/UnboundFunctionLibrary.java
@@ -0,0 +1,210 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.type.AnyFunctionType;
+import com.saxonica.functions.hof.CallableFunctionItem;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An UnboundFunctionLibrary is not a real function library; rather, it is used to keep track of function calls
+ * that cannot yet be bound to a known declared function, but will have to be bound when all user-declared functions
+ * are available.
+*/
+
+public class UnboundFunctionLibrary implements FunctionLibrary {
+
+ private List<UserFunctionReference> unboundFunctionReferences = new ArrayList<UserFunctionReference>(20);
+ private List<StaticContext> correspondingStaticContext = new ArrayList<StaticContext>(20);
+ private boolean resolving = false;
+
+ /**
+ * Create an UnboundFunctionLibrary
+ */
+
+ public UnboundFunctionLibrary() {
+ }
+
+ /**
+ * Identify a (namespace-prefixed) function appearing in the expression. This
+ * method is called by the XQuery parser to resolve function calls found within
+ * the query.
+ * <p>Note that a function call may appear earlier in the query than the definition
+ * of the function to which it is bound. Unlike XSLT, we cannot search forwards to
+ * find the function definition. Binding of function calls is therefore a two-stage
+ * process; at the time the function call is parsed, we simply register it as
+ * pending; subsequently at the end of query parsing all the pending function
+ * calls are resolved. Another consequence of this is that we cannot tell at the time
+ * a function call is parsed whether it is a call to an internal (XSLT or XQuery)
+ * function or to an extension function written in Java.
+ * @return an Expression representing the function call. This will normally be
+ * a FunctionCall, but it may be rewritten as some other expression.
+ * @throws net.sf.saxon.trans.XPathException if the function call is invalid, either because it is
+ * an unprefixed call to a non-system function, or because it is calling a system
+ * function that is available in XSLT only. A prefixed function call that cannot
+ * be recognized at this stage is assumed to be a forwards reference, and is bound
+ * later when bindUnboundFunctionCalls() is called.
+ */
+
+ /*@Nullable*/ public Expression bind(StructuredQName functionName, /*@NotNull*/ int arity, Expression[] arguments, StaticContext env, Container container) throws XPathException {
+ if (resolving) {
+ return null;
+ }
+ UserFunctionCall ufc = new UserFunctionCall();
+ ufc.setFunctionName(functionName);
+ ufc.setArguments(arguments);
+ unboundFunctionReferences.add(ufc);
+ correspondingStaticContext.add(env);
+ return ufc;
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ * <p/>
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ * @throws net.sf.saxon.trans.XPathException
+ * in the event of certain errors, for example attempting to get a function
+ * that is private
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext) throws XPathException {
+ if (resolving) {
+ return null;
+ }
+ XQueryFunctionLibrary.UnresolvedCallable uc = new XQueryFunctionLibrary.UnresolvedCallable(functionName, arity);
+ unboundFunctionReferences.add(uc);
+ correspondingStaticContext.add(null);
+ CallableFunctionItem fi = new CallableFunctionItem(functionName, arity, uc, AnyFunctionType.getInstance());
+ //uc.setFunctionItem(fi);
+ return fi;
+ }
+//#endif
+
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ return false; // function-available() is not used in XQuery
+ }
+
+ /**
+ * Bind function calls that could not be bound when first encountered. These
+ * will either be forwards references to functions declared later in the query,
+ * or errors. This method is for internal use.
+ * @param lib A library containing all the XQuery functions that have been declared;
+ * the method searches this library for this missing function call
+ * @param config The Saxon configuration
+ * @throws XPathException if a function call refers to a function that has
+ * not been declared
+ */
+
+ public void bindUnboundFunctionReferences(/*@NotNull*/ XQueryFunctionBinder lib, /*@NotNull*/ Configuration config) throws XPathException {
+ resolving = true;
+ for (int i=0; i< unboundFunctionReferences.size(); i++) {
+ UserFunctionReference ref = unboundFunctionReferences.get(i);
+ if (ref instanceof UserFunctionCall) {
+ UserFunctionCall ufc = (UserFunctionCall) ref;
+ QueryModule importingModule = (QueryModule)correspondingStaticContext.get(i);
+ if (importingModule == null) {
+ // means we must have already been here
+ continue;
+ }
+ correspondingStaticContext.set(i, null); // for garbage collection purposes
+ // The original UserFunctionCall is effectively a dummy: we weren't able to find a function
+ // definition at the time. So we try again.
+ final StructuredQName q = ufc.getFunctionName();
+ final int arity = ufc.getNumberOfArguments();
+
+ XQueryFunction fd = lib.getDeclaration(q, ufc.getArguments());
+ if (fd != null) {
+ fd.registerReference(ufc);
+ ufc.setStaticType(fd.getResultType());
+ // Check that the result type and all the argument types are in the static context of the
+ // calling module
+ importingModule.checkImportedFunctionSignature(fd);
+ } else {
+ String msg = "Cannot find a matching " + arity +
+ "-argument function named " + q.getClarkName() + "()";
+ if (!config.getBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
+ msg += ". Note: external function calls have been disabled";
+ }
+ XPathException err = new XPathException(msg, ufc);
+ err.setErrorCode("XPST0017");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ } else if (ref instanceof XQueryFunctionLibrary.UnresolvedCallable) {
+ XQueryFunctionLibrary.UnresolvedCallable uc = (XQueryFunctionLibrary.UnresolvedCallable)ref;
+ final StructuredQName q = uc.getFunctionName();
+ final int arity = uc.getArity();
+
+ XQueryFunction fd = lib.getDeclaration(q, new Expression[arity]);
+ if (fd != null) {
+ fd.registerReference(uc);
+ //uc.setStaticType(fd.getResultType());
+ // Check that the result type and all the argument types are in the static context of the
+ // calling module
+ //importingModule.checkImportedFunctionSignature(fd);
+ } else {
+ String msg = "Cannot find a matching " + arity +
+ "-argument function named " + q.getClarkName() + "()";
+ if (!config.getBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
+ msg += ". Note: external function calls have been disabled";
+ }
+ XPathException err = new XPathException(msg);
+ err.setErrorCode("XPST0017");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ }
+ }
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ *
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ /*@NotNull*/ public FunctionLibrary copy() {
+ UnboundFunctionLibrary qfl = new UnboundFunctionLibrary();
+ qfl.unboundFunctionReferences = new ArrayList<UserFunctionReference>(unboundFunctionReferences);
+ qfl.correspondingStaticContext = new ArrayList<StaticContext>(correspondingStaticContext);
+ qfl.resolving = resolving;
+ return qfl;
+ }
+
+}
+
diff --git a/sf/saxon/query/UndeclaredVariable.java b/sf/saxon/query/UndeclaredVariable.java
new file mode 100644
index 0000000..5905860
--- /dev/null
+++ b/sf/saxon/query/UndeclaredVariable.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.expr.BindingReference;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.GlobalVariable;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.Collections;
+
+/**
+ * An UndeclaredVariable object is created when a reference is encountered to a variable
+ * that has not yet been declared. This can happen as a result of recursive module imports.
+ * These references are resolved at the end of query parsing.
+ */
+
+public class UndeclaredVariable extends GlobalVariable {
+
+ public UndeclaredVariable(){}
+
+ public void transferReferences(GlobalVariable var) {
+ for (BindingReference ref : references) {
+ var.registerReference(ref);
+ }
+ references = Collections.EMPTY_LIST;
+ }
+
+ /*@NotNull*/ public void compile(Executable exec, int slot) throws XPathException {
+ throw new UnsupportedOperationException("Attempt to compile a place-holder for an undeclared variable");
+ }
+}
+
diff --git a/sf/saxon/query/UpdateAgent.java b/sf/saxon/query/UpdateAgent.java
new file mode 100644
index 0000000..48124f5
--- /dev/null
+++ b/sf/saxon/query/UpdateAgent.java
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * An UpdateAgent is a callback class that is called to handle a document after it has been updated.
+ * Typically the UpdateAgent might take responsibility for writing the updated document back to
+ * persistent storage.
+ */
+public interface UpdateAgent {
+
+ /**
+ * Handle an updated document.
+ * This method is called by {@link XQueryExpression#runUpdate(DynamicQueryContext, UpdateAgent)}
+ * once for each document (or more generally, for the root of each tree) that has been modified
+ * by the update query.
+ * @param node the root of the tree that has been updated
+ * @param controller the Controller that was used for executing the query
+ * @throws XPathException if the callback code cannot handle the updated document
+ */
+
+ public void update(NodeInfo node, Controller controller) throws XPathException;
+}
+
diff --git a/sf/saxon/query/XQueryExpression.java b/sf/saxon/query/XQueryExpression.java
new file mode 100644
index 0000000..eb2586e
--- /dev/null
+++ b/sf/saxon/query/XQueryExpression.java
@@ -0,0 +1,979 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.*;
+import net.sf.saxon.evpull.ComplexContentProcessor;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.evpull.EventIteratorToReceiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.GlobalParam;
+import net.sf.saxon.expr.instruct.GlobalVariable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.lib.SerializerFactory;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Result;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.stream.StreamResult;
+import java.io.OutputStream;
+import java.util.*;
+
+/**
+ * XQueryExpression represents a compiled query. This object is immutable and thread-safe,
+ * the same compiled query may be executed many times in series or in parallel. The object
+ * can be created only by using the compileQuery method of the QueryProcessor class.
+ * <p/>
+ * <p>Various methods are provided for evaluating the query, with different options for
+ * delivery of the results.</p>
+ */
+public class XQueryExpression implements Container {
+
+ private Expression expression;
+ private SlotManager stackFrameMap;
+ private Executable executable;
+ private QueryModule staticContext;
+ private PathMap pathMap;
+ private boolean allowDocumentProjection;
+ private boolean isUpdating;
+
+ /**
+ * The constructor is protected, to ensure that instances can only be
+ * created using the compileQuery() methods of StaticQueryContext
+ *
+ * @param exp an expression to be wrapped as an XQueryExpression
+ * @param exec the executable
+ * @param mainModule the static context of the main module
+ * @param config the configuration
+ * @throws XPathException if an error occurs
+ */
+
+ protected XQueryExpression(/*@NotNull*/ Expression exp, Executable exec, /*@NotNull*/ QueryModule mainModule, /*@NotNull*/ Configuration config)
+ throws XPathException {
+ stackFrameMap = config.makeSlotManager();
+ executable = exec;
+ exp.setContainer(this);
+ Optimizer optimizer = config.obtainOptimizer();
+ try {
+ ExpressionVisitor visitor = ExpressionVisitor.make(mainModule, exec);
+ visitor.setExecutable(exec);
+ exp = visitor.simplify(exp);
+ exp.checkForUpdatingSubexpressions();
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(mainModule.getUserQueryContext().getRequiredContextItemType(), true);
+ exp = visitor.typeCheck(exp, cit);
+// ExpressionPresenter presenter = new ExpressionPresenter(config,
+// ExpressionPresenter.defaultDestination(config, new FileOutputStream("c:/projects/montreal/before50.xml")));
+// exp.explain(presenter);
+// presenter.close();
+ if (optimizer.getOptimizationLevel() != Optimizer.NO_OPTIMIZATION) {
+ exp = exp.optimize(visitor, cit);
+
+ }
+ } catch (XPathException err) {
+ //err.printStackTrace();
+ mainModule.reportFatalError(err);
+ throw err;
+ }
+ ExpressionTool.allocateSlots(exp, 0, stackFrameMap);
+ if ((Boolean)config.getConfigurationProperty(FeatureKeys.GENERATE_BYTE_CODE)
+ && config.isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY)) {
+ if (config.isTiming()) {
+ config.getStandardErrorOutput().println("Generating byte code...");
+ }
+ Expression cexp = optimizer.compileToByteCode(exp, "main",
+ Expression.PROCESS_METHOD | Expression.ITERATE_METHOD);
+ if (cexp != null) {
+ exp = cexp;
+ }
+ }
+
+ expression = exp;
+ executable.setConfiguration(config);
+ executable.setCollationMap(mainModule.getUserQueryContext().getAllCollations());
+ staticContext = mainModule;
+ isUpdating = exp.isUpdatingExpression();
+ }
+
+ /**
+ * Get the expression wrapped in this XQueryExpression object
+ *
+ * @return the underlying expression
+ */
+
+ public Expression getExpression() {
+ return expression;
+ }
+
+ /**
+ * Get the granularity of the container.
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 2;
+ }
+
+ /**
+ * Ask whether this query uses the context item
+ *
+ * @return true if the context item is referenced either in the query body or in the initializer
+ * of any global variable
+ */
+
+ public boolean usesContextItem() {
+ StructuredQName contextVar = getExecutable().getInitialContextItemVariableName();
+ Binding[] binding = null;
+ if (contextVar != null) {
+ GlobalVariable cvar = getExecutable().getGlobalVariable(contextVar);
+ binding = new Binding[]{cvar};
+ }
+ if (ExpressionTool.dependsOnFocus(expression)) {
+ return true;
+ }
+ if (binding != null && ExpressionTool.dependsOnVariable(expression, binding)) {
+ return true;
+ }
+ HashMap<StructuredQName, GlobalVariable> map = executable.getCompiledGlobalVariables();
+ if (map != null) {
+ for (GlobalVariable var : map.values()) {
+ Expression select = var.getSelectExpression();
+ if (select != null && ExpressionTool.dependsOnFocus(select)) {
+ return true;
+ }
+ if (select != null && binding != null && ExpressionTool.dependsOnVariable(select, binding)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Ask whether this is an update query
+ *
+ * @return true if the body of the query is an updating expression
+ * (as defined by the XQuery Update specification). Note that a query can use Update syntax
+ * (notably the copy-modify syntax) without being an updating expression.
+ */
+
+ public boolean isUpdateQuery() {
+ return isUpdating;
+ }
+
+ /**
+ * Get the stack frame map used for the outermost level of this query
+ *
+ * @return the stack frame map
+ */
+
+ public SlotManager getStackFrameMap() {
+ return stackFrameMap;
+ }
+
+ /**
+ * Replace one subexpression by a replacement subexpression. For internal use only
+ *
+ * @param original the original subexpression
+ * @param replacement the replacement subexpression
+ * @return true if the original subexpression is found
+ */
+
+// public boolean replaceSubExpression(Expression original, Expression replacement) {
+// boolean found = false;
+// if (expression == original) {
+// expression = replacement;
+// found = true;
+// }
+// return found;
+// }
+
+
+ /**
+ * Get the static context in which this expression was compiled. This is essentially an internal
+ * copy of the original user-created StaticQueryContext object, augmented with information obtained
+ * from the query prolog of the main query module, and with information about functions and variables
+ * imported from other library modules. The user-created StaticQueryContext object is not modified
+ * by Saxon, whereas the QueryModule object includes additional information found in the query prolog.
+ *
+ * @return the QueryModule object representing the static context of the main module of the query.
+ * This is available for inspection, but must not be modified or reused by the application.
+ */
+ public QueryModule getStaticContext() {
+ return staticContext;
+ }
+
+ /**
+ * Get a list containing the names of the external variables in the query.
+ * <p/>
+ * <p><i>Changed in Saxon 9.0 to return an array of StructuredQName objects rather than
+ * integer fingerprints.</i></p>
+ *
+ * @return an array of StructuredQName objects, representing the names of external variables defined
+ * in the query
+ */
+
+ /*@NotNull*/ public StructuredQName[] getExternalVariableNames() {
+ List list = stackFrameMap.getVariableMap();
+ StructuredQName[] names = new StructuredQName[stackFrameMap.getNumberOfVariables()];
+ for (int i = 0; i < names.length; i++) {
+ names[i] = (StructuredQName)list.get(i);
+ }
+ return names;
+ }
+
+ /**
+ * Execute a the compiled Query, returning the results as a List.
+ *
+ * @param env Provides the dynamic query evaluation context
+ * @return The results of the expression, as a List. The List represents the sequence
+ * of items returned by the expression. Each item in the list will either be an
+ * object representing a node, or an object representing an atomic value.
+ * For the types of Java object that may be returned, see the description of the
+ * {@link net.sf.saxon.xpath.XPathEvaluator#evaluate evaluate} method
+ * of class XPathProcessor
+ * @throws XPathException if a dynamic error occurs during query evaluation
+ */
+
+ /*@NotNull*/ public List<Object> evaluate(/*@NotNull*/ DynamicQueryContext env) throws XPathException {
+ if (isUpdating) {
+ throw new XPathException("Cannot call evaluate() on an updating query");
+ }
+ SequenceIterator iterator = iterator(env);
+ ArrayList<Object> list = new ArrayList<Object>(100);
+ while (true) {
+ Item item = iterator.next();
+ if (item == null) {
+ return list;
+ }
+ list.add(SequenceTool.convertToJava(item));
+ }
+ }
+
+ /**
+ * Execute the compiled Query, returning the first item in the result.
+ * This is useful where it is known that the expression will only return
+ * a singleton value (for example, a single node, or a boolean).
+ *
+ * @param env Provides the dynamic query evaluation context
+ * @return The first item in the sequence returned by the expression. If the expression
+ * returns an empty sequence, this method returns null. Otherwise, it returns the first
+ * item in the result sequence, represented as a Java object using the same mapping as for
+ * the {@link XQueryExpression#evaluate evaluate} method
+ */
+
+ /*@Nullable*/ public Object evaluateSingle(/*@NotNull*/ DynamicQueryContext env) throws XPathException {
+ if (isUpdating) {
+ throw new XPathException("Cannot call evaluateSingle() on an updating query");
+ }
+ SequenceIterator iterator = iterator(env);
+ Item item = iterator.next();
+ if (item == null) {
+ return null;
+ }
+ return SequenceTool.convertToJava(item);
+ }
+
+ /**
+ * Get an iterator over the results of the expression. This returns results without
+ * any conversion of the returned items to "native" Java classes. The iterator will
+ * deliver a sequence of Item objects, each item being either a NodeInfo (representing
+ * a node) or an AtomicValue (representing an atomic value).
+ * <p/>
+ * <p>To get the results of the query in the form of an XML document in which each
+ * item is wrapped by an element indicating its type, use:</p>
+ * <p/>
+ * <p><code>QueryResult.wrap(iterator(env))</code></p>
+ * <p/>
+ * <p>To serialize the results to a file, use the QueryResult.serialize() method.</p>
+ *
+ * @param env Provides the dynamic query evaluation context
+ * @return an iterator over the results of the query. The class SequenceIterator
+ * is modeled on the standard Java Iterator class, but has extra functionality
+ * and can throw exceptions when errors occur.
+ * @throws XPathException if a dynamic error occurs in evaluating the query. Some
+ * dynamic errors will not be reported by this method, but will only be reported
+ * when the individual items of the result are accessed using the returned iterator.
+ */
+
+ /*@NotNull*/ public SequenceIterator iterator(/*@NotNull*/ DynamicQueryContext env) throws XPathException {
+ if (isUpdating) {
+ throw new XPathException("Cannot call iterator() on an updating query");
+ }
+ if (!env.getConfiguration().isCompatible(getExecutable().getConfiguration())) {
+ throw new XPathException("The query must be compiled and executed under the same Configuration");
+ }
+ Controller controller = newController();
+ env.initializeController(controller);
+
+ try {
+ Item contextItem = env.getContextItem();
+ if (contextItem instanceof DocumentInfo && ((DocumentInfo)contextItem).isTyped() && !getExecutable().isSchemaAware()) {
+ throw new XPathException("A typed input document can only be used with a schema-aware query");
+ }
+
+ XPathContextMajor context = initialContext(env, controller);
+
+ // In tracing/debugging mode, evaluate all the global variables first
+ if (controller.getTraceListener() != null) {
+ controller.preEvaluateGlobals(context);
+ }
+
+ context.openStackFrame(stackFrameMap);
+
+ SequenceIterator iterator = expression.iterate(context);
+ return new ErrorReportingIterator(iterator, controller.getErrorListener());
+ } catch (XPathException err) {
+ TransformerException terr = err;
+ while (terr.getException() instanceof TransformerException) {
+ terr = (TransformerException)terr.getException();
+ }
+ XPathException de = XPathException.makeXPathException(terr);
+ controller.reportFatalError(de);
+ throw de;
+ }
+ }
+
+ /**
+ * Run the query, sending the results directly to a JAXP Result object. This way of executing
+ * the query is most efficient in the case of queries that produce a single document (or parentless
+ * element) as their output, because it avoids constructing the result tree in memory: instead,
+ * it is piped straight to the serializer.
+ *
+ * @param env the dynamic query context
+ * @param result the destination for the results of the query. The query is effectively wrapped
+ * in a document{} constructor, so that the items in the result are concatenated to form a single
+ * document; this is then written to the requested Result destination, which may be (for example)
+ * a DOMResult, a SAXResult, or a StreamResult
+ * @param outputProperties Supplies serialization properties, in JAXP format, if the result is to
+ * be serialized. This parameter can be defaulted to null.
+ * @throws XPathException if the query fails.
+ */
+
+ public void run(/*@NotNull*/ DynamicQueryContext env, /*@NotNull*/ Result result, Properties outputProperties) throws XPathException {
+ if (isUpdating) {
+ throw new XPathException("Cannot call run() on an updating query");
+ }
+ if (!env.getConfiguration().isCompatible(getExecutable().getConfiguration())) {
+ throw new XPathException("The query must be compiled and executed under the same Configuration");
+ }
+ Item contextItem = env.getContextItem();
+ if (contextItem instanceof DocumentInfo && ((DocumentInfo)contextItem).isTyped() && !getExecutable().isSchemaAware()) {
+ throw new XPathException("A typed input document can only be used with a schema-aware query");
+ }
+ Controller controller = newController();
+ env.initializeController(controller);
+
+ if (allowDocumentProjection) {
+ controller.setUseDocumentProjection(getPathMap());
+ }
+ Properties actualProperties = validateOutputProperties(controller, outputProperties);
+
+ //controller.defineGlobalParameters();
+
+ XPathContextMajor context = initialContext(env, controller);
+
+ // In tracing/debugging mode, evaluate all the global variables first
+ TraceListener tracer = controller.getTraceListener();
+ if (tracer != null) {
+ controller.preEvaluateGlobals(context);
+ tracer.open(controller);
+ }
+
+ context.openStackFrame(stackFrameMap);
+
+ boolean mustClose = (result instanceof StreamResult &&
+ ((StreamResult)result).getOutputStream() == null);
+ SerializerFactory sf = context.getConfiguration().getSerializerFactory();
+ PipelineConfiguration pipe = controller.makePipelineConfiguration();
+ pipe.setHostLanguage(Configuration.XQUERY);
+ Receiver receiver = sf.getReceiver(result, pipe, actualProperties);
+ context.changeOutputDestination(receiver, null);
+ context.getReceiver().open();
+
+ // Run the query
+ try {
+ expression.process(context);
+ } catch (XPathException err) {
+ controller.reportFatalError(err);
+ throw err;
+ }
+
+ if (tracer != null) {
+ tracer.close();
+ }
+
+ context.getReceiver().close();
+ if (mustClose) {
+ OutputStream os = ((StreamResult)result).getOutputStream();
+ if (os != null) {
+ try {
+ os.close();
+ } catch (java.io.IOException err) {
+ throw new XPathException(err);
+ }
+ }
+ }
+ }
+
+ /*@NotNull*/ private Properties validateOutputProperties(/*@NotNull*/ Controller controller, /*@Nullable*/ Properties outputProperties) throws XPathException {
+ // Validate the serialization properties requested
+
+ Properties baseProperties = controller.getOutputProperties();
+ if (outputProperties != null) {
+ Enumeration iter = outputProperties.propertyNames();
+ while (iter.hasMoreElements()) {
+ String key = (String)iter.nextElement();
+ String value = outputProperties.getProperty(key);
+ try {
+ SaxonOutputKeys.checkOutputProperty(key, value, controller.getConfiguration());
+ baseProperties.setProperty(key, value);
+ } catch (XPathException dynamicError) {
+ try {
+ outputProperties.remove(key);
+ controller.getErrorListener().warning(dynamicError);
+ } catch (TransformerException err2) {
+ throw XPathException.makeXPathException(err2);
+ }
+ }
+ }
+ }
+ if (baseProperties.getProperty("method") == null) {
+ // XQuery forces the default method to XML, unlike XSLT where it depends on the contents of the result tree
+ baseProperties.setProperty("method", "xml");
+ }
+ return baseProperties;
+ }
+
+ /**
+ * Run the query in pull mode.
+ * <p/>
+ * <p>For maximum effect this method should be used when lazyConstructionMode has been set in the Configuration.</p>
+ * <p/>
+ * <p><b>Note: this method usually has very similar performance to the
+ * {@link #run(DynamicQueryContext,javax.xml.transform.Result,java.util.Properties)} method (which does
+ * the same thing), but sometimes it is significantly slower. Therefore, the run() method is preferred.</b></p>
+ *
+ * @param dynamicEnv the dynamic context for query evaluation
+ * @param destination the destination of the query results
+ * @param outputProperties the serialization parameters
+ * @throws XPathException if a dynamic error occurs during the evaluation
+ * @see FeatureKeys#LAZY_CONSTRUCTION_MODE
+ */
+
+ public void pull(/*@NotNull*/ DynamicQueryContext dynamicEnv, /*@NotNull*/ Result destination, Properties outputProperties) throws XPathException {
+ if (isUpdating) {
+ throw new XPathException("Cannot call pull() on an updating query");
+ }
+ Configuration config = dynamicEnv.getConfiguration();
+ try {
+ Controller controller = newController();
+ //initializeController(dynamicEnv, controller);
+ EventIterator iter = iterateEvents(controller, dynamicEnv);
+ //iter = new Decomposer(iter, config);
+
+ Properties actualProperties = validateOutputProperties(controller, outputProperties);
+ SerializerFactory sf = config.getSerializerFactory();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ pipe.setSerializing(true);
+ Receiver receiver = sf.getReceiver(destination, pipe, actualProperties);
+
+ receiver = new NamespaceReducer(receiver);
+ if ("yes".equals(actualProperties.getProperty(SaxonOutputKeys.WRAP))) {
+ receiver = config.getSerializerFactory().newSequenceWrapper(receiver);
+ } else {
+ receiver = new TreeReceiver(receiver);
+ }
+ EventIteratorToReceiver.copy(iter, (SequenceReceiver)receiver);
+ } catch (XPathException err) {
+ config.reportFatalError(err);
+ throw err;
+ }
+
+ }
+
+ /**
+ * Run the query returning the results as an EventIterator
+ *
+ * @param controller The Controller used to run the query
+ * @param dynamicEnv the XQuery dynamic context for evaluating the query
+ * @return an EventIterator representing the results of the query as a sequence of events
+ */
+
+ /*@NotNull*/ public EventIterator iterateEvents(/*@NotNull*/ Controller controller, /*@NotNull*/ DynamicQueryContext dynamicEnv) throws XPathException {
+ if (isUpdating) {
+ throw new XPathException("Cannot call iterateEvents() on an updating query");
+ }
+ dynamicEnv.initializeController(controller);
+
+ XPathContextMajor context = initialContext(dynamicEnv, controller);
+
+ // In tracing/debugging mode, evaluate all the global variables first
+ if (controller.getTraceListener() != null) {
+ controller.preEvaluateGlobals(context);
+ }
+
+ context.openStackFrame(stackFrameMap);
+
+ final Configuration config = executable.getConfiguration();
+
+ EventIterator ei = expression.iterateEvents(context);
+ //ei = new TracingEventIterator(EventStackIterator.flatten(ei));
+ return new ComplexContentProcessor(config, ei);
+ }
+
+ /**
+ * Run an updating query
+ *
+ * @param dynamicEnv the dynamic context for query execution
+ * @return a set of nodes representing the roots of trees that have been modified as a result
+ * of executing the update. Note that this method will never modify persistent data on disk; it returns
+ * the root nodes of the affected documents (which will often be document nodes whose document-uri can
+ * be ascertained), and it is the caller's responsibility to decide what to do with them.
+ * <p>On completion of this method it is generally unsafe to rely on the contents or relationships
+ * of NodeInfo objects that were obtained before the updating query was run. Such objects may or may not
+ * reflect the results of the update operations. This is especially true in the case of nodes that
+ * are part of a subtree that has been deleted (detached from its parent). Instead, the new updated
+ * tree should be accessed by navigation from the root nodes returned by this method.</p>
+ * @throws XPathException if evaluation of the update query fails, or it this is not an updating query
+ */
+
+ /*@NotNull*/ public Set<MutableNodeInfo> runUpdate(/*@NotNull*/ DynamicQueryContext dynamicEnv) throws XPathException {
+ if (!isUpdating) {
+ throw new XPathException("Calling runUpdate() on a non-updating query");
+ }
+
+ Configuration config = executable.getConfiguration();
+ Controller controller = newController();
+ dynamicEnv.initializeController(controller);
+ XPathContextMajor context = initialContext(dynamicEnv, controller);
+ try {
+ PendingUpdateList pul = config.newPendingUpdateList();
+ context.openStackFrame(stackFrameMap);
+ expression.evaluatePendingUpdates(context, pul);
+ pul.apply(context, staticContext.getRevalidationMode());
+ return pul.getAffectedTrees();
+ // Only mark a document for rewriting to disk if it is in the document pool,
+ // that is, if it was supplied using doc() or similar functions.
+// Code deleted as fix to bug 2091267
+// Set rewrittenTrees = new HashSet();
+// for (Iterator iter = pul.getAffectedTrees().iterator(); iter.hasNext();) {
+// NodeInfo node = (NodeInfo)iter.next();
+// if (node.isSameNodeInfo(controller.getDocumentPool().find(node.getSystemId()))) {
+// rewrittenTrees.add(node);
+// }
+// }
+// return rewrittenTrees;
+ } catch (XPathException e) {
+ if (!e.hasBeenReported()) {
+ try {
+ controller.getErrorListener().fatalError(e);
+ } catch (TransformerException e2) {
+ // ignore secondary error
+ }
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Run an updating query, writing back eligible updated node to persistent storage.
+ *
+ * <p>A node is eligible to be written back to disk if it is present in the document pool,
+ * which generally means that it was originally read using the doc() or collection() function.</p>
+ *
+ * <p>On completion of this method it is generally unsafe to rely on the contents or relationships
+ * of NodeInfo objects that were obtained before the updating query was run. Such objects may or may not
+ * reflect the results of the update operations. This is especially true in the case of nodes that
+ * are part of a subtree that has been deleted (detached from its parent). Instead, the new updated
+ * tree should be accessed by navigation from the root nodes returned by this method.</p>
+ *
+ * <p>If one or more eligible updated nodes cannot be written back to disk, perhaps because the URI
+ * identifies a non-updatable location, then an exception is thrown. In this case it is undefined
+ *
+ * @param dynamicEnv the dynamic context for query execution
+ * @param agent a callback class that is called to process each document updated by the query
+ * @throws XPathException if evaluation of the update query fails, or it this is not an updating query
+ */
+
+ public void runUpdate(/*@NotNull*/ DynamicQueryContext dynamicEnv, /*@NotNull*/ UpdateAgent agent) throws XPathException {
+ if (!isUpdating) {
+ throw new XPathException("Calling runUpdate() on a non-updating query");
+ }
+
+ Configuration config = executable.getConfiguration();
+ Controller controller = newController();
+ dynamicEnv.initializeController(controller);
+ XPathContextMajor context = initialContext(dynamicEnv, controller);
+ try {
+ PendingUpdateList pul = config.newPendingUpdateList();
+ context.openStackFrame(stackFrameMap);
+ expression.evaluatePendingUpdates(context, pul);
+ pul.apply(context, staticContext.getRevalidationMode());
+ for (MutableNodeInfo mutableNodeInfo : pul.getAffectedTrees()) {
+ agent.update(mutableNodeInfo, controller);
+ }
+ } catch (XPathException e) {
+ if (!e.hasBeenReported()) {
+ try {
+ controller.getErrorListener().fatalError(e);
+ } catch (TransformerException e2) {
+ // ignore secondary error
+ }
+ }
+ throw e;
+ }
+ }
+
+
+ private XPathContextMajor initialContext(DynamicQueryContext dynamicEnv, Controller controller) throws XPathException {
+
+ // The way the initial context item is handled in XQuery is somewhat convoluted. We create a global variable
+ // or parameter called saxon:context-item based on the context item declaration in the query if there is one;
+ // this is used to take advantage of the circularity detection for global variables. References to the context
+ // item within a global variable are bound to $saxon:context-item, to allow forwards references to work in the
+ // case where the context item is initialized within the query prolog. But not all top-level references to the
+ // context item are caught by this mechanism; for example implicit references caused by making dynamic function
+ // calls on context-dependent functions. So we bind the context item in the XPathContext object as well.
+
+ Item contextItem = dynamicEnv.getContextItem();
+
+ StructuredQName varQName = executable.getInitialContextItemVariableName();
+ if (varQName != null && contextItem != null) {
+ controller.setParameter(varQName, contextItem);
+ }
+
+ controller.defineGlobalParameters();
+
+ XPathContextMajor context = controller.newXPathContext();
+
+ if (contextItem != null) {
+ // Check the type of the externally-supplied context item against the API-defined required type
+ if (!staticContext.getUserQueryContext().getRequiredContextItemType().matchesItem(
+ contextItem, false, dynamicEnv.getConfiguration())) {
+ throw new XPathException("The supplied context item does not match the required context item type", "XPTY0004");
+ }
+ // Now check it against the required type defined in the query prolog
+ if (varQName != null) {
+ GlobalVariable var = executable.getGlobalVariable(varQName);
+ if (var == null) {
+ throw new IllegalStateException("Context item variable not found in executable");
+ }
+ controller.setParameter(varQName, contextItem);
+ try {
+ // do early evaluation to force the type-checking of the context item
+ controller.getBindery().useGlobalParameter(
+ varQName, var.getSlotNumber(), var.getRequiredType(), context);
+ } catch (XPathException err) {
+ err.maybeSetLocation(var);
+ if (!err.hasBeenReported()) {
+ try {
+ controller.getErrorListener().fatalError(err);
+ } catch (TransformerException e) {
+ // no action
+ }
+ }
+ throw err;
+ }
+ }
+ UnfailingIterator single = SingletonIterator.makeIterator(contextItem);
+ single.next();
+ context.setCurrentIterator(single);
+ controller.setInitialContextItem(contextItem);
+ } else {
+ // there might be a default value for the context item
+ if (varQName != null) {
+ GlobalVariable var = executable.getGlobalVariable(varQName);
+ if (var == null) {
+ throw new IllegalStateException("Context item variable not found in executable");
+ }
+ if (!(var instanceof GlobalParam && var.getSelectExpression() instanceof ErrorExpression)) {
+ try {
+ // do early evaluation to force the type-checking of the context item
+ controller.getBindery().useGlobalParameter(varQName, var.getSlotNumber(), var.getRequiredType(), context);
+ } catch (XPathException err) {
+ err.maybeSetLocation(var);
+ if (!err.hasBeenReported()) {
+ try {
+ controller.getErrorListener().fatalError(err);
+ } catch (TransformerException e) {
+ // no action
+ }
+ }
+ throw err;
+ }
+ Sequence val = var.getSelectValue(context);
+ contextItem = val.head();
+ UnfailingIterator single = SingletonIterator.makeIterator(contextItem);
+ single.next();
+ context.setCurrentIterator(single);
+ controller.setInitialContextItem(contextItem);
+ }
+ }
+ }
+
+ return context;
+ }
+
+ /**
+ * Get a controller that can be used to execute functions in this compiled query.
+ * Functions in the query module can be found using {@link QueryModule#getUserDefinedFunction}.
+ * They can then be called directly from the Java application using {@link net.sf.saxon.expr.instruct.UserFunction#call}
+ * The same Controller can be used for a series of function calls. Note that the Controller should only be used
+ * in a single thread.
+ *
+ * @return a newly constructed Controller
+ */
+
+ /*@NotNull*/ public Controller newController() {
+ Controller controller = new Controller(executable.getConfiguration(), executable);
+ executable.initializeBindery(controller.getBindery());
+ if (isUpdating && controller.getModel() == TreeModel.TINY_TREE) {
+ controller.setModel(TreeModel.LINKED_TREE);
+ }
+ return controller;
+ }
+
+ /**
+ * Diagnostic method: display a representation of the compiled query on the
+ * selected output stream.
+ *
+ * @param out an ExpressionPresenter to which the XML representation of the compiled query
+ * will be sent
+ */
+
+ public void explain(/*@NotNull*/ ExpressionPresenter out) {
+ out.startElement("query");
+ getExecutable().getKeyManager().explainKeys(out);
+ getExecutable().explainGlobalVariables(out);
+ staticContext.explainGlobalFunctions(out);
+ out.startElement("body");
+ expression.explain(out);
+ out.endElement();
+ out.endElement();
+ out.close();
+ }
+
+ /**
+ * Get the Executable (representing a complete stylesheet or query) of which this Container forms part
+ */
+
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Get the path map for the query expression
+ *
+ * @return the path map (which is constructed if this has not already been done)
+ */
+
+ public PathMap getPathMap() {
+ if (pathMap == null) {
+ pathMap = new PathMap(expression);
+ }
+ HashMap<StructuredQName, GlobalVariable> map = executable.getCompiledGlobalVariables();
+ if (map != null) {
+ for (GlobalVariable var : map.values()) {
+ Expression select = var.getSelectExpression();
+ if (select != null) {
+ select.addToPathMap(pathMap, null);
+ }
+ }
+ }
+ return pathMap;
+ }
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ */
+
+ public LocationProvider getLocationProvider() {
+ return executable.getLocationMap();
+ }
+
+ /**
+ * Indicate that document projection is or is not allowed
+ *
+ * @param allowed true if projection is allowed
+ */
+
+ public void setAllowDocumentProjection(boolean allowed) {
+ allowDocumentProjection = allowed;
+ }
+
+ /**
+ * Ask whether document projection is allowed
+ *
+ * @return true if document projection is allowed
+ */
+
+ public boolean isDocumentProjectionAllowed() {
+ return allowDocumentProjection;
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ * <p/>
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup that
+ * triggered the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Return the system identifier for the current document event.
+ * <p/>
+ * <p>The return value is the system identifier of the document
+ * entity or of the external parsed entity in which the markup that
+ * triggered the event appears.</p>
+ * <p/>
+ * <p>If the system identifier is a URL, the parser must resolve it
+ * fully before passing it to the application.</p>
+ *
+ * @return A string containing the system identifier, or null
+ * if none is available.
+ * @see #getPublicId
+ */
+ /*@Nullable*/ public String getSystemId() {
+ return null;
+ }
+
+ /**
+ * Return the line number where the current document event ends.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of error
+ * reporting; it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.</p>
+ * <p/>
+ * <p>The return value is an approximation of the line number
+ * in the document entity or external parsed entity where the
+ * markup that triggered the event appears.</p>
+ *
+ * @return The line number, or -1 if none is available.
+ * @see #getColumnNumber
+ */
+ public int getLineNumber() {
+ return -1;
+ }
+
+ /**
+ * Return the character position where the current document event ends.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of error
+ * reporting; it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.</p>
+ * <p/>
+ * <p>The return value is an approximation of the column number
+ * in the document entity or external parsed entity where the
+ * markup that triggered the event appears.</p>
+ *
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ *
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+
+ public int getHostLanguage() {
+ return Configuration.XQUERY;
+ }
+
+ /**
+ * ErrorReportingIterator is an iterator that wraps a base iterator and reports
+ * any exceptions that are raised to the ErrorListener
+ */
+
+ private class ErrorReportingIterator implements SequenceIterator {
+ private SequenceIterator base;
+ private ErrorListener listener;
+
+ public ErrorReportingIterator(SequenceIterator base, ErrorListener listener) {
+ this.base = base;
+ this.listener = listener;
+ }
+
+ /*@Nullable*/ public Item next() throws XPathException {
+ try {
+ return base.next();
+ } catch (XPathException e1) {
+ e1.maybeSetLocation(expression);
+ try {
+ listener.fatalError(e1);
+ } catch (TransformerException e2) {
+ //
+ }
+ e1.setHasBeenReported(true);
+ throw e1;
+ }
+ }
+
+ /*@Nullable*/ public Item current() {
+ return base.current();
+ }
+
+ public int position() {
+ return base.position();
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new ErrorReportingIterator(base.getAnother(), listener);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/query/XQueryFunction.java b/sf/saxon/query/XQueryFunction.java
new file mode 100644
index 0000000..1f33c17
--- /dev/null
+++ b/sf/saxon/query/XQueryFunction.java
@@ -0,0 +1,696 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.functions.ExecutableFunctionLibrary;
+import net.sf.saxon.functions.FunctionLibraryList;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.*;
+
+/**
+ * A user-defined function in an XQuery module
+ */
+
+public class XQueryFunction implements InstructionInfo, Container, Declaration {
+ private StructuredQName functionName;
+ private List<UserFunctionParameter> arguments;
+ private SequenceType resultType;
+ /*@Nullable*/ private Expression body = null;
+ /*@NotNull*/ private List<UserFunctionReference> references = new ArrayList<UserFunctionReference>(10);
+ private int lineNumber;
+ private int columnNumber;
+ private String systemId;
+ private Executable executable;
+ /*@Nullable*/ private UserFunction compiledFunction = null;
+ private boolean memoFunction;
+ private NamespaceResolver namespaceResolver;
+ private QueryModule staticContext;
+ private boolean isUpdating = false;
+ private Map<StructuredQName, Annotation> annotationMap = new HashMap<StructuredQName, Annotation>();
+
+ /**
+ * Create an XQuery function
+ */
+
+ public XQueryFunction() {
+ arguments = new ArrayList<UserFunctionParameter>(8);
+ }
+
+ /**
+ * Get the granularity of the container.
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 2;
+ }
+
+ /**
+ * Set the name of the function
+ * @param name the name of the function as a StructuredQName object
+ */
+
+ public void setFunctionName(StructuredQName name) {
+ functionName = name;
+ }
+
+ /**
+ * Add an argument to the list of arguments
+ * @param argument the formal declaration of the argument to be added
+ */
+
+ public void addArgument(UserFunctionParameter argument) {
+ arguments.add(argument);
+ }
+
+ /**
+ * Set the required result type of the function
+ * @param resultType the declared result type of the function
+ */
+
+ public void setResultType(SequenceType resultType) {
+ this.resultType = resultType;
+ }
+
+ /**
+ * Set the body of the function
+ * @param body the expression forming the body of the function
+ */
+
+ public void setBody(/*@Nullable*/ Expression body) {
+ this.body = body;
+ if (body != null) {
+ body.setContainer(this);
+ }
+ }
+
+ /**
+ * Get the body of the function
+ * @return the expression making up the body of the function
+ */
+
+ /*@Nullable*/ public Expression getBody() {
+ return body;
+ }
+
+ /**
+ * Set the system ID of the module containing the function
+ * @param systemId the system ID (= base URI) of the module containing the function
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Set the line number of the function declaration within its module
+ * @param line the line number of the function declaration
+ */
+
+ public void setLineNumber(int line) {
+ lineNumber = line;
+ }
+
+ /**
+ * Set the column number of the function declaration
+ * @param column the column number of the function declaration
+ */
+
+ public void setColumnNumber(int column) {
+ columnNumber = column;
+ }
+
+ /**
+ * Get the name of the function as a structured QName
+ * @return the name of the function as a structured QName
+ */
+
+ public StructuredQName getFunctionName() {
+ return functionName;
+ }
+
+ /**
+ * Get the name of the function for display in error messages
+ * @return the name of the function as a lexical QName
+ */
+
+ public String getDisplayName() {
+ return functionName.getDisplayName();
+ }
+
+ /**
+ * Get an identifying key for this function, which incorporates the URI and local part of the
+ * function name plus the arity
+ * @return an identifying key
+ */
+
+ /*@NotNull*/ public String getIdentificationKey() {
+ return functionName.getClarkName() + '/' + arguments.size();
+ }
+
+ /**
+ * Construct what the identification key would be for a function with given URI, local name, and arity
+ * @param uri the URI part of the function name
+ * @param localName the local part of the function name
+ * @param arity the number of arguments in the function
+ * @return an identifying key
+ */
+
+ public static String getIdentificationKey(/*@NotNull*/ String uri, /*@NotNull*/ String localName, int arity) {
+ FastStringBuffer sb = new FastStringBuffer(uri.length() + localName.length() + 8);
+ sb.append('{');
+ sb.append(uri);
+ sb.append('}');
+ sb.append(localName);
+ sb.append('/');
+ sb.append(arity+"");
+ return sb.toString();
+ }
+
+ /**
+ * Construct what the identification key would be for a function with given URI, local name, and arity
+ * @param qName the name of the function
+ * @param arity the number of arguments
+ * @return an identifying key
+ */
+
+ public static String getIdentificationKey(/*@NotNull*/ StructuredQName qName, int arity) {
+ String uri = qName.getURI();
+ String localName = qName.getLocalPart();
+ FastStringBuffer sb = new FastStringBuffer(uri.length() + localName.length() + 8);
+ sb.append('{');
+ sb.append(uri);
+ sb.append('}');
+ sb.append(localName);
+ sb.append("/"+arity);
+ //sb.append(arity+"");
+ return sb.toString();
+ }
+
+ /**
+ * Get the result type of the function
+ * @return the declared result type
+ */
+
+ public SequenceType getResultType() {
+ return resultType;
+ }
+
+ /**
+ * Set the executable in which this function is contained
+ * @param exec the executable
+ */
+
+ public void setExecutable(Executable exec) {
+ executable = exec;
+ }
+
+ /**
+ * Get the executable in which this function is contained
+ * @return the executable
+ */
+
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ * @return the location provider
+ */
+
+ public LocationProvider getLocationProvider() {
+ return executable.getLocationMap();
+ }
+
+ /**
+ * Set the static context for this function
+ * @param env the static context for the module in which the function is declared
+ */
+
+ public void setStaticContext(QueryModule env) {
+ staticContext = env;
+ }
+
+ /**
+ * Get the static context for this function
+ * @return the static context for the module in which the function is declared
+ */
+
+ public StaticContext getStaticContext() {
+ return staticContext;
+ }
+
+ /**
+ * Get the declared types of the arguments of this function
+ * @return an array, holding the types of the arguments in order
+ */
+
+ /*@NotNull*/ public SequenceType[] getArgumentTypes() {
+ SequenceType[] types = new SequenceType[arguments.size()];
+ for (int i=0; i<arguments.size(); i++) {
+ types[i] = arguments.get(i).getRequiredType();
+ }
+ return types;
+ }
+
+ /**
+ * Get the definitions of the arguments to this function
+ * @return an array of UserFunctionParameter objects, one for each argument
+ */
+
+ public UserFunctionParameter[] getParameterDefinitions() {
+ UserFunctionParameter[] params = new UserFunctionParameter[arguments.size()];
+ return arguments.toArray(params);
+ }
+
+ /**
+ * Get the arity of the function
+ * @return the arity (the number of arguments)
+ */
+
+ public int getNumberOfArguments() {
+ return arguments.size();
+ }
+
+ /**
+ * Register a call on this function
+ * @param ufc a user function call that references this function.
+ */
+
+ public void registerReference(UserFunctionReference ufc) {
+ references.add(ufc);
+ }
+
+ /**
+ * Set that this is, or is not, a memo function. A memo function remembers the results of calls
+ * on the function so that the a subsequent call with the same arguments simply look up the result
+ * @param isMemoFunction true if this is a memo function.
+ */
+
+ public void setMemoFunction(boolean isMemoFunction) {
+ memoFunction = isMemoFunction;
+ }
+
+ /**
+ * Find out whether this is a memo function
+ * @return true if this is a memo function
+ */
+
+ public boolean isMemoFunction() {
+ return memoFunction;
+ }
+
+ /**
+ * Set whether this is an updating function (as defined in XQuery Update)
+ * @param isUpdating true if this is an updating function
+ */
+
+ public void setUpdating(boolean isUpdating) {
+ this.isUpdating = isUpdating;
+ }
+
+ /**
+ * Ask whether this is an updating function (as defined in XQuery Update)
+ * @return true if this is an updating function
+ */
+
+ public boolean isUpdating() {
+ return isUpdating;
+ }
+
+ /**
+ * Set the annotations on this function
+ * @param annotations the annotations, indexed by annotation name
+ */
+
+ public void setAnnotations(Map<StructuredQName, Annotation> annotations) {
+ this.annotationMap = annotations;
+ if (annotations.get(Annotation.UPDATING) != null) {
+ setUpdating(true);
+ }
+ }
+
+ /**
+ * Ask whether this is a private function (as defined in XQuery 3.0)
+ * @return true if this is a private function
+ */
+
+ public boolean isPrivate() {
+ return annotationMap.get(Annotation.PRIVATE) != null;
+ }
+
+ /**
+ * Compile this function to create a run-time definition that can be interpreted (note, this
+ * has nothing to do with Java code generation)
+ * @throws XPathException if errors are found
+ */
+
+ public void compile() throws XPathException {
+ Configuration config = staticContext.getConfiguration();
+ try {
+ // If a query function is imported into several modules, then the compile()
+ // method will be called once for each importing module. If the compiled
+ // function already exists, then this is a repeat call, and the only thing
+ // needed is to fix up references to the function from within the importing
+ // module.
+
+ if (compiledFunction == null) {
+ SlotManager map = config.makeSlotManager();
+ UserFunctionParameter[] params = getParameterDefinitions();
+ for (int i=0; i<params.length; i++) {
+ params[i].setSlotNumber(i);
+ map.allocateSlotNumber(params[i].getVariableQName());
+ }
+
+ // type-check the body of the function
+
+ ExpressionVisitor visitor = ExpressionVisitor.make(staticContext, getExecutable());
+ body = visitor.simplify(body);
+ body = visitor.typeCheck(body, null);
+
+ // Try to extract new global variables from the body of the function
+ //body = config.getOptimizer().promoteExpressionsToGlobal(body, visitor);
+
+ body.setContainer(this);
+ RoleLocator role =
+ new RoleLocator(RoleLocator.FUNCTION_RESULT, functionName, 0);
+ //role.setSourceLocator(this);
+ body = TypeChecker.staticTypeCheck(body, resultType, false, role, visitor);
+ if (config.isCompileWithTracing()) {
+ namespaceResolver = staticContext.getNamespaceResolver();
+ TraceExpression trace = new TraceExpression(body);
+ trace.setConstructType(StandardNames.XSL_FUNCTION);
+ trace.setObjectName(functionName);
+ trace.setNamespaceResolver(staticContext.getNamespaceResolver());
+ trace.setLocationId(staticContext.getLocationMap().allocateLocationId(systemId, lineNumber));
+ body = trace;
+ }
+
+ compiledFunction = config.newUserFunction(memoFunction);
+ compiledFunction.setBody(body);
+ compiledFunction.setHostLanguage(Configuration.XQUERY);
+ compiledFunction.setFunctionName(functionName);
+ compiledFunction.setParameterDefinitions(params);
+ compiledFunction.setResultType(getResultType());
+ compiledFunction.setLineNumber(lineNumber);
+ compiledFunction.setSystemId(systemId);
+ compiledFunction.setExecutable(executable);
+ compiledFunction.setStackFrameMap(map);
+ compiledFunction.setUpdating(isUpdating);
+ compiledFunction.setAnnotationMap(annotationMap);
+
+ for (int i=0; i<params.length; i++) {
+ UserFunctionParameter param = params[i];
+ int refs = ExpressionTool.getReferenceCount(body, param, false);
+ param.setReferenceCount(refs);
+ }
+ }
+ // bind all references to this function to the UserFunction object
+
+ fixupReferences();
+
+ // register this function with the function library available at run-time (e.g. for saxon:evaluate())
+
+ FunctionLibraryList libList = executable.getFunctionLibrary();
+ if (libList.getLibraryList().size() == 1 && libList.getLibraryList().get(0) instanceof ExecutableFunctionLibrary) {
+ ExecutableFunctionLibrary lib = (ExecutableFunctionLibrary)libList.getLibraryList().get(0);
+ lib.addFunction(compiledFunction);
+ } else {
+// throw new AssertionError("executable.getFunctionLibrary() is an instance of " +
+// executable.getFunctionLibrary().getClass().getName());
+ }
+
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ throw e;
+ }
+ }
+
+ /**
+ * Optimize the body of this function
+ * @throws net.sf.saxon.trans.XPathException if execution fails, for example because the function is updating
+ * and contains constructs not allowed in an updating function, or vice-versa.
+ */
+
+ public void optimize() throws XPathException {
+ body.checkForUpdatingSubexpressions();
+ if (isUpdating) {
+ if (!ExpressionTool.isAllowedInUpdatingContext(body)) {
+ XPathException err = new XPathException(
+ "The body of an updating function must be an updating expression", "XUST0002");
+ err.setLocator(body);
+ throw err;
+ }
+ } else {
+ //body.checkForUpdatingSubexpressions();
+ if (body.isUpdatingExpression()) {
+ XPathException err = new XPathException(
+ "The body of a non-updating function must be a non-updating expression", "XUST0001");
+ err.setLocator(body);
+ throw err;
+ }
+ }
+ ExpressionVisitor visitor = ExpressionVisitor.make(staticContext, getExecutable());
+ Configuration config = staticContext.getConfiguration();
+ Optimizer opt = config.obtainOptimizer();
+ int arity = arguments.size();
+ if (opt.getOptimizationLevel() != Optimizer.NO_OPTIMIZATION) {
+ body = visitor.optimize(body, null);
+
+ // Try to extract new global variables from the body of the function
+ Expression b2 = opt.promoteExpressionsToGlobal(body, visitor);
+ if (b2 != null) {
+ body = visitor.optimize(b2, null);
+ }
+
+ // mark tail calls within the function body
+
+ if (!isUpdating) {
+ // TODO: implement tail call optimization for updating functions. Requires TailCallLoop to
+ // be an updating expression (like Block)
+ int tailCalls = ExpressionTool.markTailFunctionCalls(body, functionName, arity);
+ if (tailCalls != 0) {
+ compiledFunction.setBody(body);
+ compiledFunction.setTailRecursive(tailCalls > 0, tailCalls > 1);
+ body = new TailCallLoop(compiledFunction);
+ }
+ }
+ body.setContainer(this);
+ compiledFunction.setBody(body);
+ }
+ compiledFunction.computeEvaluationMode();
+ ExpressionTool.allocateSlots(body, arity, compiledFunction.getStackFrameMap());
+ if (config.isGenerateByteCode(Configuration.XQUERY)) {
+ Expression cbody = opt.compileToByteCode(body, getFunctionName().getDisplayName(),
+ Expression.PROCESS_METHOD | Expression.ITERATE_METHOD);
+ if (cbody != null) {
+ body = cbody;
+ }
+ compiledFunction.setBody(body);
+ compiledFunction.computeEvaluationMode();
+ }
+ }
+
+ /**
+ * Fix up references to this function
+ */
+
+ public void fixupReferences() throws XPathException {
+ for (UserFunctionReference ufc : references) {
+ ufc.setFunction(compiledFunction);
+ if (ufc instanceof UserFunctionCall) {
+ ((UserFunctionCall)ufc).computeArgumentEvaluationModes();
+ }
+ }
+ }
+
+ /**
+ * Type-check references to this function
+ * @param visitor the expression visitor
+ */
+
+ public void checkReferences(ExpressionVisitor visitor) throws XPathException {
+ for (UserFunctionReference ufr : references) {
+ if (ufr instanceof UserFunctionCall) {
+ UserFunctionCall ufc = (UserFunctionCall)ufr;
+ ufc.checkFunctionCall(compiledFunction, visitor);
+ ufc.computeArgumentEvaluationModes();
+ }
+ }
+
+ // clear the list of references, so that more can be added in another module
+ references = new ArrayList<UserFunctionReference>(0);
+
+ }
+
+ /**
+ * Produce diagnostic output showing the compiled and optimized expression tree for a function
+ * @param out the destination to be used
+ */
+ public void explain(/*@NotNull*/ ExpressionPresenter out) {
+ out.startElement("declareFunction");
+ out.emitAttribute("name", functionName.getDisplayName());
+ out.emitAttribute("arity", ""+getNumberOfArguments());
+ if (compiledFunction == null) {
+ out.emitAttribute("unreferenced", "true");
+ } else {
+ if (compiledFunction.isMemoFunction()) {
+ out.emitAttribute("memo", "true");
+ }
+ out.emitAttribute("tailRecursive", (compiledFunction.isTailRecursive() ? "true" : "false"));
+ body.explain(out);
+ }
+ out.endElement();
+ }
+
+ /**
+ * Get the callable compiled function contained within this XQueryFunction definition.
+ * @return the compiled function object
+ */
+
+ /*@Nullable*/ public UserFunction getUserFunction() {
+ return compiledFunction;
+ }
+
+ /**
+ * Get the type of construct. This will be a constant in
+ * class {@link Location}.
+ */
+
+ public int getConstructType() {
+ return StandardNames.XSL_FUNCTION;
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ */
+
+ public StructuredQName getObjectName() {
+ return functionName;
+ }
+
+ /**
+ * Get the system identifier (URI) of the source module containing
+ * the instruction. This will generally be an absolute URI. If the system
+ * identifier is not known, the method may return null. In some cases, for example
+ * where XML external entities are used, the correct system identifier is not
+ * always retained.
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Get the line number of the instruction in the source stylesheet module.
+ * If this is not known, or if the instruction is an artificial one that does
+ * not relate to anything in the source code, the value returned may be -1.
+ */
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Return the column number
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+ /**
+ * Get the namespace context of the instruction. This will not always be available, in which
+ * case the method returns null.
+ */
+
+ public NamespaceResolver getNamespaceResolver() {
+ return namespaceResolver;
+ }
+
+ /**
+ * Get the value of a particular property of the instruction. Properties
+ * of XSLT instructions are generally known by the name of the stylesheet attribute
+ * that defines them.
+ * @param name The name of the required property
+ * @return The value of the requested property, or null if the property is not available
+ */
+
+ /*@Nullable*/ public Object getProperty(String name) {
+ if ("name".equals(name)) {
+ return functionName.getDisplayName();
+ } else if ("as".equals(name)) {
+ return resultType.toString();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get an iterator over all the properties available. The values returned by the iterator
+ * will be of type String, and each string can be supplied as input to the getProperty()
+ * method to retrieve the value of the property.
+ */
+
+ /*@NotNull*/ public Iterator getProperties() {
+ return new PairIterator("name", "as");
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+
+ public int getHostLanguage() {
+ return Configuration.XQUERY;
+ }
+
+}
+
diff --git a/sf/saxon/query/XQueryFunctionBinder.java b/sf/saxon/query/XQueryFunctionBinder.java
new file mode 100644
index 0000000..61ce4b3
--- /dev/null
+++ b/sf/saxon/query/XQueryFunctionBinder.java
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.om.StructuredQName;
+
+/**
+ * XQueryFunctionBinder is an extension of the FunctionLibrary interface used for function libraries
+ * that contain user-written XQuery functions. It provides a method that allows the XQueryFunction
+ * with a given name and arity to be located.
+ */
+
+public interface XQueryFunctionBinder extends FunctionLibrary {
+
+ /**
+ * Get the function declaration corresponding to a given function name and arity
+ * @param functionName the name of the function as a QName
+ * @param staticArgs the expressions supplied as arguments in the function call (typically,
+ * we only need to know the number of arguments)
+ * @return the XQueryFunction if there is one, or null if not.
+ */
+
+ /*@Nullable*/ public XQueryFunction getDeclaration(StructuredQName functionName, Expression[] staticArgs);
+
+}
\ No newline at end of file
diff --git a/sf/saxon/query/XQueryFunctionLibrary.java b/sf/saxon/query/XQueryFunctionLibrary.java
new file mode 100644
index 0000000..dbf7507
--- /dev/null
+++ b/sf/saxon/query/XQueryFunctionLibrary.java
@@ -0,0 +1,375 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.query;
+
+import com.saxonica.functions.hof.CallableFunctionItem;
+import com.saxonica.functions.hof.SpecificFunctionType;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.UserFunction;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.FunctionItemType;
+import net.sf.saxon.value.SequenceExtent;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * An XQueryFunctionLibrary is a function library containing all the user-defined functions available for use within a
+ * particular XQuery module: that is, the functions declared in that module, and the functions imported from other
+ * modules. It also contains (transiently during compilation) a list of function calls within the module that have not
+ * yet been bound to a specific function declaration.
+*/
+
+public class XQueryFunctionLibrary implements FunctionLibrary, XQueryFunctionBinder {
+
+ private Configuration config;
+
+ // The functions in this library are represented using a HashMap
+ // The key of the hashmap is a String that encodes the QName of the function and its arity
+ // The value in the hashmap is an XQueryFunction
+ /*@NotNull*/ private HashMap<String, XQueryFunction> functions =
+ new HashMap<String, XQueryFunction>(20);
+
+ /**
+ * Create an XQueryFunctionLibrary
+ * @param config the Saxon configuration
+ */
+
+ public XQueryFunctionLibrary(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Set the Configuration options
+ * @param config the Saxon configuration
+ */
+
+ public void setConfiguration(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Get the Configuration options
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Register a user-defined XQuery function
+ * @param function the function to be registered
+ * @throws XPathException if there is an existing function with the same name and arity
+ */
+
+ public void declareFunction(/*@NotNull*/ XQueryFunction function) throws XPathException {
+ String keyObj = function.getIdentificationKey();
+ XQueryFunction existing = functions.get(keyObj);
+ if (existing == function) {
+ return;
+ }
+ if (existing != null) {
+ XPathException err = new XPathException("Duplicate definition of function " +
+ function.getDisplayName() +
+ " (see line " + existing.getLineNumber() + " in " + existing.getSystemId() + ')');
+ err.setErrorCode("XQST0034");
+ err.setIsStaticError(true);
+ err.setLocator(function);
+ throw err;
+ }
+ functions.put(keyObj, function);
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ *
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext)
+ throws XPathException {
+ String functionKey = XQueryFunction.getIdentificationKey(functionName, arity);
+ XQueryFunction fd = functions.get(functionKey);
+ if (fd != null) {
+ if (fd.isPrivate() && fd.getStaticContext() != staticContext) {
+ throw new XPathException("Cannot call the private function " +
+ functionName.getDisplayName() + " from outside its module", "XPST0017");
+ }
+ final UserFunction fn = fd.getUserFunction();
+ FunctionItemType type = new SpecificFunctionType(
+ fd.getArgumentTypes(), fd.getResultType());
+ if (fn == null) {
+ // not yet compiled
+ UnresolvedCallable uc = new UnresolvedCallable(functionName, arity);
+ fd.registerReference(uc);
+ return new CallableFunctionItem(functionName, arity, uc, type);
+ } else {
+ return new CallableFunctionItem(fn);
+ }
+ } else {
+ return null;
+ }
+ }
+//#endif
+
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ String functionKey = XQueryFunction.getIdentificationKey(functionName, arity);
+ return functions.get(functionKey) != null;
+ }
+
+ /**
+ * Inner class containing information about a reference to a function whose declaration
+ * has not yet been encountered. The references gets fixed up later, once information
+ * about all user-declared functions is available.
+ */
+
+ public static class UnresolvedCallable implements UserFunctionReference, Callable {
+ StructuredQName name;
+ int arity;
+ UserFunction function;
+ //CallableFunctionItem functionItem;
+
+ public UnresolvedCallable(StructuredQName name, int arity) {
+ this.name = name;
+ this.arity = arity;
+ }
+
+ public StructuredQName getFunctionName() {
+ return name;
+ }
+
+ public int getArity() {
+ return arity;
+ }
+
+ //public void setFunctionItem(CallableFunctionItem fi) {
+ // this.functionItem = fi;
+ //}
+
+ /**
+ * Evaluate the expression
+ *
+ * @param context the dynamic evaluation context
+ * @param arguments the values of the arguments, supplied as Sequences
+ * @return the result of the evaluation, in the form of a Sequence
+ * @throws net.sf.saxon.trans.XPathException
+ * if a dynamic error occurs during the evaluation of the expression
+ */
+ public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
+ if (function == null) {
+ throw new XPathException("Forwards reference to XQuery function has not been resolved");
+ }
+ Sequence[] args = new Sequence[arguments.length];
+ for (int i=0; i<arguments.length; i++) {
+ args[i] = SequenceExtent.makeSequenceExtent(arguments[i].iterate());
+ }
+ return function.call(context.newCleanContext(), args);
+ }
+
+ public void setFunction(UserFunction function) {
+ this.function = function;
+ //if (functionItem != null) {
+ // functionItem.setCallable(function.asCallable());
+ // functionItem.setType(function.getFunctionType());
+ //}
+ }
+
+ public UserFunction getFunction() {
+ return function;
+ }
+ }
+
+ /**
+ * Identify a (namespace-prefixed) function appearing in the expression. This
+ * method is called by the XQuery parser to resolve function calls found within
+ * the query.
+ * <p>Note that a function call may appear earlier in the query than the definition
+ * of the function to which it is bound. Unlike XSLT, we cannot search forwards to
+ * find the function definition. Binding of function calls is therefore a two-stage
+ * process; at the time the function call is parsed, we simply register it as
+ * pending; subsequently at the end of query parsing all the pending function
+ * calls are resolved. Another consequence of this is that we cannot tell at the time
+ * a function call is parsed whether it is a call to an internal (XSLT or XQuery)
+ * function or to an extension function written in Java.
+ * @return an Expression representing the function call. This will normally be
+ * a FunctionCall, but it may be rewritten as some other expression.
+ * @throws XPathException if the function call is invalid, either because it is
+ * an unprefixed call to a non-system function, or because it is calling a system
+ * function that is available in XSLT only. A prefixed function call that cannot
+ * be recognized at this stage is assumed to be a forwards reference, and is bound
+ * later when bindUnboundFunctionCalls() is called.
+ */
+
+ /*@Nullable*/ public Expression bind(/*@NotNull*/ StructuredQName functionName, /*@NotNull*/ int arity, Expression[] arguments, StaticContext env, Container container) throws XPathException {
+ String functionKey = XQueryFunction.getIdentificationKey(
+ functionName, arguments.length);
+ XQueryFunction fd = functions.get(functionKey);
+ if (fd != null) {
+ if (fd.isPrivate() && fd.getStaticContext() != env) {
+ throw new XPathException("Cannot call the private function " +
+ functionName.getDisplayName() + " from outside its module", "XPST0017");
+ }
+ UserFunctionCall ufc = new UserFunctionCall();
+ ufc.setFunctionName(fd.getFunctionName());
+ ufc.setArguments(arguments);
+ ufc.setStaticType(fd.getResultType());
+ UserFunction fn = fd.getUserFunction();
+ if (fn == null) {
+ // not yet compiled
+ fd.registerReference(ufc);
+ } else {
+ ufc.setFunction(fn);
+ ExpressionVisitor visitor = ExpressionVisitor.make(fd.getStaticContext(), fd.getExecutable());
+ ufc.checkFunctionCall(fn, visitor);
+ }
+ return ufc;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the function declaration corresponding to a given function name and arity
+ * @return the XQueryFunction if there is one, or null if not.
+ */
+
+ public XQueryFunction getDeclaration(/*@NotNull*/ StructuredQName functionName, /*@NotNull*/ Expression[] staticArgs) {
+ String functionKey = XQueryFunction.getIdentificationKey(
+ functionName, staticArgs.length);
+ return functions.get(functionKey);
+ }
+
+ /**
+ * Get the function declaration corresponding to a given function name and arity, supplied
+ * in the form "{uri}local/arity"
+ * @param functionKey a string in the form "{uri}local/arity" identifying the required function
+ * @return the XQueryFunction if there is one, or null if not.
+ */
+
+ public XQueryFunction getDeclarationByKey(String functionKey) {
+ return functions.get(functionKey);
+ }
+
+ /**
+ * Get an iterator over the Functions defined in this module
+ * @return an Iterator, whose items are {@link XQueryFunction} objects. It returns
+ * all function known to this module including those imported from elsewhere; they
+ * can be distinguished by their namespace.
+ */
+
+ public Iterator<XQueryFunction> getFunctionDefinitions() {
+ return functions.values().iterator();
+ }
+
+ /**
+ * Fixup all references to global functions. This method is called
+ * on completion of query parsing. Each XQueryFunction is required to
+ * bind all references to that function to the object representing the run-time
+ * executable code of the function.
+ * <p>
+ * This method is for internal use.
+ * @param env the static context for the main query body.
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ protected void fixupGlobalFunctions(/*@NotNull*/ QueryModule env) throws XPathException {
+ ExpressionVisitor visitor = ExpressionVisitor.make(env, env.getExecutable());
+ for (XQueryFunction fn : functions.values()) {
+ fn.compile();
+ }
+ for (XQueryFunction fn : functions.values()) {
+ visitor.setExecutable(fn.getExecutable());
+ fn.checkReferences(visitor);
+ }
+ }
+
+ /**
+ * Optimize the body of all global functions. This may involve inlining functions calls
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ protected void optimizeGlobalFunctions() throws XPathException {
+ for (XQueryFunction fn : functions.values()) {
+ fn.optimize();
+ }
+ }
+
+
+
+ /**
+ * Output "explain" information about each declared function
+ * @param out the ExpressionPresenter that renders the output
+ */
+
+ public void explainGlobalFunctions(/*@NotNull*/ ExpressionPresenter out) {
+ for (XQueryFunction fn : functions.values()) {
+ fn.explain(out);
+ }
+ }
+
+ /**
+ * Get the function with a given name and arity. This method is provided so that XQuery functions
+ * can be called directly from a Java application. Note that there is no type checking or conversion
+ * of arguments when this is done: the arguments must be provided in exactly the form that the function
+ * signature declares them.
+ * @param uri the uri of the function name
+ * @param localName the local part of the function name
+ * @param arity the number of arguments.
+ * @return the function identified by the URI, local name, and arity; or null if there is no such function
+ */
+
+ /*@Nullable*/ public UserFunction getUserDefinedFunction(/*@NotNull*/ String uri, /*@NotNull*/ String localName, int arity) {
+ String functionKey = XQueryFunction.getIdentificationKey(uri, localName, arity);
+ XQueryFunction fd = functions.get(functionKey);
+ if (fd==null) {
+ return null;
+ }
+ return fd.getUserFunction();
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ *
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ /*@NotNull*/ public FunctionLibrary copy() {
+ XQueryFunctionLibrary qfl = new XQueryFunctionLibrary(config);
+ qfl.functions = new HashMap<String, XQueryFunction>(functions);
+ return qfl;
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/query/package.html b/sf/saxon/query/package.html
new file mode 100644
index 0000000..9ef1ec7
--- /dev/null
+++ b/sf/saxon/query/package.html
@@ -0,0 +1,130 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.query</title>
+</head>
+
+<body>
+
+<p>This package provides a relatively low-level API for executing XQuery queries
+directly from a Java application. This is no longer the recommended API for writing Java
+ applications using XQuery: there are two higher-level APIs, s9api ({@link net.sf.saxon.s9api},
+ and XQJ ({@link javax.xml.xquery}. This package also includes internal supporting code
+that implements XQuery within Saxon.</p>
+
+<p>For details of the API, see the JavaDoc documentation of individual classes, starting
+with the {@link net.sf.saxon.query.StaticQueryContext}.</p>
+
+<p>The first thing you need to do is to create a {@link net.sf.saxon.Configuration} object.
+This holds values of all the system settings, corresponding to flags available on the command line.
+You don't need to set any properties in the <code>Configuration</code> object if you are happy
+with the default settings. For schema-aware processing, you will need to create a
+{@link com.saxonica.config.EnterpriseConfiguration} instead.</p>
+
+<p>Then you need to create a {@link net.sf.saxon.query.StaticQueryContext} object, which you can
+ do using the <code>newStaticQueryContext()</code> method on the <code>Configuration</code>. As the name
+implies, this holds information about the static (compile-time) context for a query. Most aspects
+of the static context can be defined in the Query Prolog, but this object allows you to initialize
+the static context from the application instead if you need to. Some of the facilities provided are
+very much for advanced users only, for example the ability to declare variables and functions, and
+the ability to specify a <code>NamePool</code> to be used. One aspect of the static context that you may need
+to use is the ability to declare collations. Using the method <code>declareCollation</code> you can
+create a mapping between a collation URI (which can then be used anywhere in the Query) and a Java
+<code>StringCollator</code> object used to implement that collation.</p>
+
+<p>Having created, and possibly configured, the <code>Configuration</code> and
+<code>StaticQueryContext</code> objects, you can now compile a Query using the <code>compileQuery</code>
+method on the <code>StaticQueryContext</code>. The text of the
+Query can be supplied either as a <code>String</code> or as a Java <code>Reader</code>. There
+are thus two different <code>compileQuery</code> methods. Each of them returns the compiled
+query in the form of a <code>XQueryExpression</code>. The <code>XQueryExpression</code>, as you would expect,
+can be executed repeatedly, as often as you want, in the same or in different threads.</p>
+
+<p>Before you run your query, you may want to build one or more trees representing
+XML documents that can be used as input to your query. You don't need to do this: if the query
+loads its source documents using the <code>doc()</code> function then this will be done
+automatically, but doing it yourself gives you more control. A document node at the root of
+a tree is represented in Saxon by the {@link net.sf.saxon.om.DocumentInfo} interface.
+The <code>Configuration</code> provides a convenience method, <code>buildDocument()</code>,
+that allows an instance of <code>DocumentInfo</code> to be constructed. The input parameter to
+this is defined by the class <code>javax.xml.transform.Source</code>, which is part of the
+standard Java JAXP API: the <code>Source</code> interface is an umbrella for different kinds of
+XML document source, including a <code>StreamSource</code> which parses raw XML from a byte
+or character stream, <code>SAXSource</code> which takes the input from a SAX parser (or an
+object that is simulating a SAX parser), and <code>DOMSource</code> which provides the input
+from a DOM. Saxon-PE also provides wrappers for third-party document models, for example a
+ {@link net.sf.saxon.option.jdom.DocumentWrapper} which allows
+the input to be taken from a JDOM document.</p>
+
+<p>To execute your compiled query, you need to create a {@link net.sf.saxon.query.DynamicQueryContext} object
+that holds the run-time context information. The main things you can set in the run-time context are:</p>
+
+<ul>
+<li>Values of parameters (external global variables). You can set these using the <code>setParameter()</code>
+method. The mappings from Java classes to XQuery/XPath data types is the same as the mapping used for the
+returned values from an external Java method call, and is described under
+<a href="extensibility.html#function-result">Result of an Extension Function</a>.</li>
+<li>The context node can be set using the method <code>setContextNode()</code>. For some reason
+it isn't possible to set a context item other than a node.</li>
+<li>You can also set a URIResolver and/or ErrorListener. These default to the ones that were
+used during Query compilation.</li>
+</ul>
+
+<p>You are now ready to evaluate the query. There are several methods on the <code>QueryExpression</code>
+object that you can use to achieve this. The <code>evaluate()</code> method returns the result sequence
+as a Java <code>java.util.List</code>. The <code>evaluateSingle()</code> method is suitable when you know
+that the result sequence will contain a single item: this returns this item as an Object, or returns null
+if the result is an empty sequence. There is also an <code>iterator</code> method that returns an iterator
+over the results. This is a Saxon object of class <code>net.sf.saxon.SequenceIterator</code>: it is similar
+to the standard Java iterator, but not quite identical; for example, it can throw exceptions.</p>
+
+<p>The <code>evaluate()</code> and <code>evaluateSingle()</code> methods return the result as a Java object
+of the most appropriate type: for example a String is returned as a <code>java.lang.String</code>, a
+boolean as a <code>java.lang.Boolean</code>. A node is returned using the Saxon representation of a node,
+<code>net.sf.saxon.om.NodeInfo</code>. With the standard and tinytree models, this object also implements
+the DOM <code>Node</code> interface (but any attempt to update the node throws an error).</p>
+
+<p>The <code>iterator()</code> method, by contrast, does not do any conversion of the result. It is returned
+using its native Saxon representation, for example a String is returned as an instance of
+<code>sf.net.saxon.value.StringValue</code>. You can then use all the methods available on this class
+to process the returned value.</p>
+
+<p>If you want to process the results of the query in your application, that's all there is to it. But you
+may want to output the results as serialized XML. Saxon provides two ways of doing this: you can produce
+wrapped output, or raw output. Raw output works only if the result consists of a single document or element
+node, and it outputs the subtree rooted at that element node in the form of a serialized XML document. Wrapped
+output works for any result sequence, for example a sequence of integers or a sequence of attribute and
+comment nodes; this works by wrapping each item in the result sequence as an XML element, with details
+of its type and value.</p>
+
+<p>To produce wrapped output, you first wrap the result sequence as an XML tree, and then serialize the
+tree. To produce unwrapped output, you skip the wrapping stage and just call the serializer directly.</p>
+
+<p>Both steps can be done using the <code>QueryResult</code> class. This class doesn't need to be
+instantiated, its methods are static. The method <code>QueryResult.wrap</code> takes as input the iterator
+produced by evaluating the query using the <code>iterator()</code> method, and produces as output
+a <code>DocumentInfo</code> object representing the results wrapped as an XML tree. The method
+<code>QueryResult.serialize</code> takes any document or element node as input, and writes it to
+a specified destination, using specified output properties. The destination is supplied as an object
+of class <code>javax.xml.transform.Result</code>. Like the <code>Source</code>, this is part of the
+JAXP API, and allows the destination to be specified as a StreamResult (representing a byte stream or
+character stream), a SAXResult (which wraps a SAX ContentHandler), or a DOMResult
+ (which delivers the result as a DOM). The output properties are used only when writing to
+ a StreamResult: they correspond to the properties available in the <code>xsl:output</code> element
+ for XSLT. The property names are defined by constants in the JAXP <code>javax.xml.transform.OutputKeys</code>
+ class (or <code>net.sf.saxon.event.SaxonOutputKeys</code> for Saxon extensions): for details of the
+ values that are accepted, see the JavaDoc documentation or the JAXP specification.</p>
+
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+30 July 2010</i></p>
+</body>
+</html>
diff --git a/sf/saxon/regex/ARegexIterator.java b/sf/saxon/regex/ARegexIterator.java
new file mode 100644
index 0000000..38a2c98
--- /dev/null
+++ b/sf/saxon/regex/ARegexIterator.java
@@ -0,0 +1,349 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntToIntHashMap;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ArrayIterator;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class ARegexIterator - provides an iterator over matched and unmatched substrings.
+ * This implementation of RegexIterator uses the modified Jakarta regular expression engine.
+*/
+
+public class ARegexIterator implements RegexIterator {
+
+ private UnicodeString theString; // the input string being matched
+ private UnicodeString regex;
+ private REMatcher matcher; // the Matcher object that does the matching, and holds the state
+ private UnicodeString current; // the string most recently returned by the iterator
+ private UnicodeString next; // if the last string was a matching string, null; otherwise the next substring
+ // matched by the regex
+ private int position = 0; // the value of XPath position()
+ private int prevEnd = 0; // the position in the input string of the end of the last match or non-match
+ private IntToIntHashMap nestingTable = null;
+ // evaluated on demand: a table that indicates for each captured group,
+ // what its immediately-containing captured group is.
+
+ /**
+ * Construct a RegexIterator. Note that the underlying matcher.find() method is called once
+ * to obtain each matching substring. But the iterator also returns non-matching substrings
+ * if these appear between the matching substrings.
+ * @param string the string to be analysed
+ * @param matcher a matcher for the regular expression
+ */
+
+ public ARegexIterator(UnicodeString string, UnicodeString regex, REMatcher matcher) {
+ theString = string;
+ this.regex = regex;
+ this.matcher = matcher;
+ next = null;
+ }
+
+ /**
+ * Get the next item in the sequence
+ * @return the next item in the sequence
+ */
+
+ public Item next() {
+ if (next == null && prevEnd >= 0) {
+ // we've returned a match (or we're at the start), so find the next match
+ if (matcher.match(theString, prevEnd)) {
+ int start = matcher.getParenStart(0);
+ int end = matcher.getParenEnd(0);
+ if (prevEnd == start) {
+ // there's no intervening non-matching string to return
+ next = null;
+ current = theString.substring(start, end);
+ prevEnd = end;
+ } else {
+ // return the non-matching substring first
+ current = theString.substring(prevEnd, start);
+ next = theString.substring(start, end);
+ }
+ } else {
+ // there are no more regex matches, we must return the final non-matching text if any
+ if (prevEnd < theString.length()) {
+ current = theString.substring(prevEnd, theString.length());
+ next = null;
+ } else {
+ // this really is the end...
+ current = null;
+ position = -1;
+ prevEnd = -1;
+ return null;
+ }
+ prevEnd = -1;
+ }
+ } else {
+ // we've returned a non-match, so now return the match that follows it, if there is one
+ if (prevEnd >= 0) {
+ current = next;
+ next = null;
+ prevEnd = matcher.getParenEnd(0);
+ } else {
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+ position++;
+ return currentStringValue();
+ }
+
+ private StringValue currentStringValue() {
+ if (current instanceof BMPString) {
+ return StringValue.makeStringValue(((BMPString)current).getCharSequence());
+ } else {
+ return StringValue.makeStringValue(current.toString());
+ }
+ }
+
+ /**
+ * Get the current item in the sequence
+ * @return the item most recently returned by next()
+ */
+
+ public Item current() {
+ return currentStringValue();
+ }
+
+ /**
+ * Get the position of the current item in the sequence
+ * @return the position of the item most recently returned by next(), starting at 1
+ */
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /**
+ * Get another iterator over the same items
+ * @return a new iterator, positioned before the first item
+ */
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() {
+ return new ARegexIterator(theString, regex, new REMatcher(matcher.getProgram()));
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+ /**
+ * Determine whether the current item is a matching item or a non-matching item
+ * @return true if the current item (the one most recently returned by next()) is
+ * an item that matches the regular expression, or false if it is an item that
+ * does not match
+ */
+
+ public boolean isMatching() {
+ return next == null && prevEnd >= 0;
+ }
+
+ /**
+ * Get a substring that matches a parenthesised group within the regular expression
+ * @param number the number of the group to be obtained
+ * @return the substring of the current item that matches the n'th parenthesized group
+ * within the regular expression
+ */
+
+ public String getRegexGroup(int number) {
+ if (!isMatching()) {
+ return null;
+ }
+ if (number >= matcher.getParenCount() || number < 0) return "";
+ UnicodeString us = matcher.getParen(number);
+ return (us == null ? "" : us.toString());
+ }
+
+ /**
+ * Get a sequence containing all the regex groups (except group 0, because we want to use indexing from 1).
+ * This is used by the saxon:analyze-string() higher-order extension function.
+ */
+
+ public SequenceIterator getRegexGroupIterator() {
+ int c = matcher.getParenCount() - 1;
+ if (c == 0) {
+ return EmptyIterator.getInstance();
+ } else {
+ StringValue[] groups = new StringValue[c];
+ for (int i=1; i<=groups.length; i++) {
+ groups[i-1] = StringValue.makeStringValue(matcher.getParen(i).toString());
+ }
+ return new ArrayIterator(groups);
+ }
+ }
+
+ /**
+ * Process a matching substring, performing specified actions at the start and end of each captured
+ * subgroup. This method will always be called when operating in "push" mode; it writes its
+ * result to context.getReceiver(). The matching substring text is all written to the receiver,
+ * interspersed with calls to the {@link RegexIterator.OnGroup} methods onGroupStart() and onGroupEnd().
+ * @param context the dynamic evaluation context
+ * @param action defines the processing to be performed at the start and end of a group
+ */
+
+ public void processMatchingSubstring(XPathContext context, OnGroup action) throws XPathException {
+ Receiver out = context.getReceiver();
+ int c = matcher.getParenCount()-1;
+ if (c == 0) {
+ out.characters(current.toString(), 0, 0);
+ } else {
+ // Create a map from positions in the string to lists of actions.
+ // The "actions" in each list are: +N: start group N; -N: end group N.
+ IntHashMap<List<Integer>> actions = new IntHashMap<List<Integer>>(c);
+ for (int i=1; i<=c; i++) {
+ int start = matcher.getParenStart(i) - matcher.getParenStart(0);
+ if (start != -1) {
+ int end = matcher.getParenEnd(i) - matcher.getParenStart(0);
+ if (start < end) {
+ // Add the start action after all other actions on the list for the same position
+ List<Integer> s = actions.get(start);
+ if (s == null) {
+ s = new ArrayList<Integer>(4);
+ actions.put(start, s);
+ }
+ s.add(i);
+ // Add the end action before all other actions on the list for the same position
+ List<Integer> e = actions.get(end);
+ if (e == null) {
+ e = new ArrayList<Integer>(4);
+ actions.put(end, e);
+ }
+ e.add(0, -i);
+ } else {
+ // zero-length group (start==end). The problem here is that the information available
+ // from Java isn't sufficient to determine the nesting of groups: match("a", "(a(b?))")
+ // and match("a", "(a)(b?)") will both give the same result for group 2 (start=1, end=1).
+ // So we need to go back to the original regex to determine the group nesting
+ if (nestingTable == null) {
+ computeNestingTable();
+ }
+ int parentGroup = nestingTable.get(i);
+ // insert the start and end events immediately before the end event for the parent group,
+ // if present; otherwise after all existing events for this position
+ List<Integer> s = actions.get(start);
+ if (s == null) {
+ s = new ArrayList<Integer>(4);
+ actions.put(start, s);
+ s.add(i);
+ s.add(-i);
+ } else {
+ int pos = s.size();
+ for (int e=0; e<s.size(); e++) {
+ if (s.get(e) == -parentGroup) {
+ pos = e;
+ break;
+ }
+ }
+ s.add(pos, -i);
+ s.add(pos, i);
+ }
+
+ }
+ }
+
+ }
+ FastStringBuffer buff = new FastStringBuffer(current.length());
+ for (int i=0; i < current.length()+1; i++) {
+ List<Integer> events = actions.get(i);
+ if (events != null) {
+ if (buff.length() > 0) {
+ out.characters(buff, 0, 0);
+ buff.setLength(0);
+ }
+ for (Integer group : events) {
+ if (group > 0) {
+ action.onGroupStart(context, group);
+ } else {
+ action.onGroupEnd(context, -group);
+ }
+ }
+ }
+ if (i < current.length()) {
+ buff.appendWideChar(current.charAt(i));
+ }
+ }
+ if (buff.length() > 0) {
+ out.characters(buff, 0, 0);
+ }
+ }
+
+ }
+
+ public RegexIterator getSnapShot(XPathContext context) throws XPathException {
+ ARegexIterator regexItr = new ARegexIterator(theString, regex, matcher);
+ regexItr.position = this.position;
+ regexItr.current = this.current;
+ regexItr.nestingTable = this.nestingTable;
+ regexItr.prevEnd = this.prevEnd;
+
+ return regexItr;
+ }
+
+ /**
+ * Compute a table showing for each captured group number (opening paren in the regex),
+ * the number of its parent group. This is done by reparsing the source of the regular
+ * expression. This is needed when the result of a match includes an empty group, to determine
+ * its position relative to other groups finishing at the same character position.
+ */
+
+ private void computeNestingTable() {
+ nestingTable = new IntToIntHashMap(16);
+ UnicodeString s = regex;
+ int[] stack = new int[s.length()];
+ int tos = 0;
+ int group = 1;
+ int inBrackets = 0;
+ stack[tos++] = 0;
+ for (int i=0; i<s.length(); i++) {
+ int ch = s.charAt(i);
+ if (ch == '\'') {
+ i++;
+ } else if (ch == '[') {
+ inBrackets++;
+ } else if (ch == ']') {
+ inBrackets--;
+ } else if (ch == '(' && s.charAt(i+1) != '?' && inBrackets == 0) {
+ nestingTable.put(group, stack[tos-1]);
+ stack[tos++] = group++;
+ } else if (ch == ')' && inBrackets == 0) {
+ tos--;
+ }
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/regex/ARegularExpression.java b/sf/saxon/regex/ARegularExpression.java
new file mode 100644
index 0000000..2dbd0da
--- /dev/null
+++ b/sf/saxon/regex/ARegularExpression.java
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+import java.util.List;
+
+/**
+ * Glue class to interface the Jakarta regex engine to Saxon
+ * (The prefix 'A' indicates an Apache regular expression, as distinct from
+ * a JDK regular expression).
+ */
+
+public class ARegularExpression implements RegularExpression {
+
+ UnicodeString rawPattern;
+ String rawFlags;
+ REProgram regex;
+
+ /**
+ * Create and compile a regular expression
+ * @param pattern the regular expression
+ * @param flags the flags (ixsmq)
+ * @param hostLanguage one of "XP20", "XP30", "XSD10", "XSD11". Also allow combinations, e.g. "XP20/XSD11".
+ * @param warnings a list to be populated with any warnings arising during compilation of the regex
+ * @throws XPathException if the regular expression is invalid
+ */
+
+ public ARegularExpression(CharSequence pattern, String flags, String hostLanguage, List<String> warnings) throws XPathException {
+ rawFlags = flags;
+ REFlags reFlags;
+ try {
+ reFlags = new REFlags(flags, hostLanguage);
+ } catch (RESyntaxException err) {
+ throw new XPathException(err.getMessage(), "FORX0001");
+ }
+ try {
+ rawPattern = UnicodeString.makeUnicodeString(pattern);
+ RECompiler comp2 = new RECompiler();
+ comp2.setFlags(reFlags);
+ regex = comp2.compile(rawPattern);
+ if (warnings != null) {
+ for (String s : comp2.getWarnings()) {
+ warnings.add(s);
+ }
+ }
+ } catch (RESyntaxException err) {
+ throw new XPathException(err.getMessage(), "FORX0002");
+ }
+ }
+
+ /**
+ * Determine whether the regular expression matches a given string in its entirety
+ *
+ * @param input the string to match
+ * @return true if the string matches, false otherwise
+ */
+ public boolean matches(CharSequence input) {
+ if (input.length() == 0) {
+ return regex.isNullable();
+ }
+ REMatcher matcher = new REMatcher(regex);
+ return matcher.anchoredMatch(UnicodeString.makeUnicodeString(input));
+ }
+
+ /**
+ * Determine whether the regular expression contains a match of a given string
+ *
+ * @param input the string to match
+ * @return true if the string matches, false otherwise
+ */
+ public boolean containsMatch(CharSequence input) {
+ REMatcher matcher = new REMatcher(regex);
+ return matcher.match(UnicodeString.makeUnicodeString(input), 0);
+ }
+
+ /**
+ * Use this regular expression to tokenize an input string.
+ *
+ * @param input the string to be tokenized
+ * @return a SequenceIterator containing the resulting tokens, as objects of type StringValue
+ */
+ public SequenceIterator<StringValue> tokenize(CharSequence input) {
+ return new ATokenIterator(UnicodeString.makeUnicodeString(input), new REMatcher(regex));
+ }
+
+ /**
+ * Use this regular expression to analyze an input string, in support of the XSLT
+ * analyze-string instruction. The resulting RegexIterator provides both the matching and
+ * non-matching substrings, and allows them to be distinguished. It also provides access
+ * to matched subgroups.
+ *
+ * @param input the character string to be analyzed using the regular expression
+ * @return an iterator over matched and unmatched substrings
+ */
+ public RegexIterator analyze(CharSequence input) {
+ return new ARegexIterator(UnicodeString.makeUnicodeString(input), rawPattern, new REMatcher(regex));
+ }
+
+ /**
+ * Replace all substrings of a supplied input string that match the regular expression
+ * with a replacement string.
+ *
+ * @param input the input string on which replacements are to be performed
+ * @param replacement the replacement string in the format of the XPath replace() function
+ * @return the result of performing the replacement
+ * @throws net.sf.saxon.trans.XPathException
+ * if the replacement string is invalid
+ */
+ public CharSequence replace(CharSequence input, CharSequence replacement) throws XPathException {
+ REMatcher matcher = new REMatcher(regex);
+ UnicodeString in = UnicodeString.makeUnicodeString(input);
+ UnicodeString rep = UnicodeString.makeUnicodeString(replacement);
+ try {
+ return matcher.subst(in, rep);
+ } catch (RESyntaxException err) {
+ throw new XPathException(err.getMessage(), "FORX0004");
+ }
+ }
+}
+
diff --git a/sf/saxon/regex/ATokenIterator.java b/sf/saxon/regex/ATokenIterator.java
new file mode 100644
index 0000000..f0f609d
--- /dev/null
+++ b/sf/saxon/regex/ATokenIterator.java
@@ -0,0 +1,94 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.value.StringValue;
+
+/**
+* A ATokenIterator is an iterator over the strings that result from tokenizing a string using a regular expression
+*/
+
+public class ATokenIterator implements SequenceIterator<StringValue> {
+
+ private UnicodeString input;
+ private REMatcher matcher;
+ /*@Nullable*/ private UnicodeString current;
+ private int position = 0;
+ private int prevEnd = 0;
+
+
+ /**
+ * Construct an ATokenIterator.
+ */
+
+ public ATokenIterator(UnicodeString input, REMatcher matcher) {
+ this.input = input;
+ this.matcher = matcher;
+ prevEnd = 0;
+ }
+
+ public StringValue next() {
+ if (prevEnd < 0) {
+ current = null;
+ position = -1;
+ return null;
+ }
+
+ if (matcher.match(input, prevEnd)) {
+ int start = matcher.getParenStart(0);
+ current = input.substring(prevEnd, start);
+ prevEnd = matcher.getParenEnd(0);
+ } else {
+ current = input.substring(prevEnd, input.length());
+ prevEnd = -1;
+ }
+ position++;
+ return currentStringValue();
+ }
+
+ private StringValue currentStringValue() {
+ if (current instanceof BMPString) {
+ return StringValue.makeStringValue(((BMPString)current).getCharSequence());
+ } else {
+ return StringValue.makeStringValue(current.toString());
+ }
+ }
+
+ public StringValue current() {
+ return (current==null ? null : currentStringValue());
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/
+ public ATokenIterator getAnother() {
+ return new ATokenIterator(input, new REMatcher(matcher.getProgram()));
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/regex/BMPString.java b/sf/saxon/regex/BMPString.java
new file mode 100644
index 0000000..31063e9
--- /dev/null
+++ b/sf/saxon/regex/BMPString.java
@@ -0,0 +1,64 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+/**
+ * An implementation of UnicodeString optimized for strings that contain
+ * no characters outside the BMP (i.e. no characters whose codepoints exceed 65535)
+ */
+public final class BMPString extends UnicodeString {
+ /**
+ * encapsulated
+ */
+ private final CharSequence src;
+
+ /**
+ * @param src - encapsulated CharSequence.
+ * The client must ensure that this contains no surrogate pairs
+ */
+ public BMPString(CharSequence src) {
+ this.src = src;
+ }
+
+ public UnicodeString substring(int beginIndex, int endIndex) {
+ return new BMPString(src.subSequence(beginIndex, endIndex));
+ }
+
+ public int charAt(int pos) {
+ return src.charAt(pos);
+ }
+
+ public int indexOf(int search, int pos) {
+ if (search > 65535) {
+ return -1;
+ } else {
+ for (int i=pos; i<src.length(); i++) {
+ if (src.charAt(i) == (char)search) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ }
+
+ public int length() {
+ return src.length();
+ }
+
+ public boolean isEnd(int pos) {
+ return (pos >= src.length());
+ }
+
+ public String toString() {
+ return src.toString();
+ }
+
+ public CharSequence getCharSequence() {
+ return src;
+ }
+}
diff --git a/sf/saxon/regex/CaseVariants.java b/sf/saxon/regex/CaseVariants.java
new file mode 100644
index 0000000..b568320
--- /dev/null
+++ b/sf/saxon/regex/CaseVariants.java
@@ -0,0 +1,1879 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.z.IntArraySet;
+import net.sf.saxon.z.IntToIntHashMap;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntToIntMap;
+
+/**
+ * This class holds data about the case-variants of Unicode characters. The data is automatically
+ * generated from the Unicode database.
+ */
+public class CaseVariants {
+
+ // TODO: regenerate this for Unicode 6.0.0
+
+ // Use one hashmap for characters with a single case variant, another for characters with multiple
+ // case variants, to reduce the number of objects that need to be allocated
+
+ private static IntToIntMap monoVariants = new IntToIntHashMap(2500);
+ private static IntHashMap<int[]> polyVariants = new IntHashMap<int[]>(100);
+
+ private static void cv(int a, int b) {
+ monoVariants.put(a, b);
+ }
+
+ private static void cv(int a, int b, int c) {
+ int[] v = {b, c};
+ polyVariants.put(a, v);
+ }
+
+ private static void cv(int a, int b, int c, int d) {
+ int[] v = {b, c, d};
+ polyVariants.put(a, v);
+ }
+
+ /**
+ * Get the case variants of a character
+ *
+ * @param code the character whose case variants are required
+ * @return the case variants of the character, excluding the character itself
+ */
+
+ public static int[] getCaseVariants(int code) {
+ int mono = monoVariants.get(code);
+ if (mono != monoVariants.getDefaultValue()) {
+ return new int[]{mono};
+ } else {
+ int[] result = polyVariants.get(code);
+ if (result == null) {
+ return IntArraySet.EMPTY_INT_ARRAY;
+ } else {
+ return result;
+ }
+ }
+ }
+
+ /**
+ * Get the case variants of roman letters (A-Z, a-z), other than the letters A-Z and a-z themselves
+ */
+
+ /*@NotNull*/ public static int[] ROMAN_VARIANTS = {0x0130, 0x0131, 0x212A, 0x017F};
+
+ /**
+ * The following data was generated from the Unicode database as follows:
+ * (a) the database was converted to XML
+ * (b) the following query was run:
+ * let $chars := doc('UnicodeData.xml')/ * /Char[string(toUpper) or string(toLower)]
+ * for $c in $chars
+ * let $variants := ($chars[(code, toUpper) = $c/(code, toUpper)] |
+ * $chars[(code, toLower) = $c/(code, toLower)]) except $c
+ * return
+ * if (count($variants) gt 0) then
+ * concat("cv(0x", $c/code, ", 0x", string-join($variants/code, ", 0x"), ");
")
+ * else ()
+ */
+
+
+ static {
+ cv(0x0041, 0x0061);
+ cv(0x0042, 0x0062);
+ cv(0x0043, 0x0063);
+ cv(0x0044, 0x0064);
+ cv(0x0045, 0x0065);
+ cv(0x0046, 0x0066);
+ cv(0x0047, 0x0067);
+ cv(0x0048, 0x0068);
+ cv(0x0049, 0x0069, 0x0130, 0x0131);
+ cv(0x004A, 0x006A);
+ cv(0x004B, 0x006B, 0x212A);
+ cv(0x004C, 0x006C);
+ cv(0x004D, 0x006D);
+ cv(0x004E, 0x006E);
+ cv(0x004F, 0x006F);
+ cv(0x0050, 0x0070);
+ cv(0x0051, 0x0071);
+ cv(0x0052, 0x0072);
+ cv(0x0053, 0x0073, 0x017F);
+ cv(0x0054, 0x0074);
+ cv(0x0055, 0x0075);
+ cv(0x0056, 0x0076);
+ cv(0x0057, 0x0077);
+ cv(0x0058, 0x0078);
+ cv(0x0059, 0x0079);
+ cv(0x005A, 0x007A);
+ cv(0x0061, 0x0041);
+ cv(0x0062, 0x0042);
+ cv(0x0063, 0x0043);
+ cv(0x0064, 0x0044);
+ cv(0x0065, 0x0045);
+ cv(0x0066, 0x0046);
+ cv(0x0067, 0x0047);
+ cv(0x0068, 0x0048);
+ cv(0x0069, 0x0049, 0x0130, 0x0131);
+ cv(0x006A, 0x004A);
+ cv(0x006B, 0x004B, 0x212A);
+ cv(0x006C, 0x004C);
+ cv(0x006D, 0x004D);
+ cv(0x006E, 0x004E);
+ cv(0x006F, 0x004F);
+ cv(0x0070, 0x0050);
+ cv(0x0071, 0x0051);
+ cv(0x0072, 0x0052);
+ cv(0x0073, 0x0053, 0x017F);
+ cv(0x0074, 0x0054);
+ cv(0x0075, 0x0055);
+ cv(0x0076, 0x0056);
+ cv(0x0077, 0x0057);
+ cv(0x0078, 0x0058);
+ cv(0x0079, 0x0059);
+ cv(0x007A, 0x005A);
+ cv(0x00B5, 0x039C, 0x03BC);
+ cv(0x00C0, 0x00E0);
+ cv(0x00C1, 0x00E1);
+ cv(0x00C2, 0x00E2);
+ cv(0x00C3, 0x00E3);
+ cv(0x00C4, 0x00E4);
+ cv(0x00C5, 0x00E5, 0x212B);
+ cv(0x00C6, 0x00E6);
+ cv(0x00C7, 0x00E7);
+ cv(0x00C8, 0x00E8);
+ cv(0x00C9, 0x00E9);
+ cv(0x00CA, 0x00EA);
+ cv(0x00CB, 0x00EB);
+ cv(0x00CC, 0x00EC);
+ cv(0x00CD, 0x00ED);
+ cv(0x00CE, 0x00EE);
+ cv(0x00CF, 0x00EF);
+ cv(0x00D0, 0x00F0);
+ cv(0x00D1, 0x00F1);
+ cv(0x00D2, 0x00F2);
+ cv(0x00D3, 0x00F3);
+ cv(0x00D4, 0x00F4);
+ cv(0x00D5, 0x00F5);
+ cv(0x00D6, 0x00F6);
+ cv(0x00D8, 0x00F8);
+ cv(0x00D9, 0x00F9);
+ cv(0x00DA, 0x00FA);
+ cv(0x00DB, 0x00FB);
+ cv(0x00DC, 0x00FC);
+ cv(0x00DD, 0x00FD);
+ cv(0x00DE, 0x00FE);
+ cv(0x00E0, 0x00C0);
+ cv(0x00E1, 0x00C1);
+ cv(0x00E2, 0x00C2);
+ cv(0x00E3, 0x00C3);
+ cv(0x00E4, 0x00C4);
+ cv(0x00E5, 0x00C5, 0x212B);
+ cv(0x00E6, 0x00C6);
+ cv(0x00E7, 0x00C7);
+ cv(0x00E8, 0x00C8);
+ cv(0x00E9, 0x00C9);
+ cv(0x00EA, 0x00CA);
+ cv(0x00EB, 0x00CB);
+ cv(0x00EC, 0x00CC);
+ cv(0x00ED, 0x00CD);
+ cv(0x00EE, 0x00CE);
+ cv(0x00EF, 0x00CF);
+ cv(0x00F0, 0x00D0);
+ cv(0x00F1, 0x00D1);
+ cv(0x00F2, 0x00D2);
+ cv(0x00F3, 0x00D3);
+ cv(0x00F4, 0x00D4);
+ cv(0x00F5, 0x00D5);
+ cv(0x00F6, 0x00D6);
+ cv(0x00F8, 0x00D8);
+ cv(0x00F9, 0x00D9);
+ cv(0x00FA, 0x00DA);
+ cv(0x00FB, 0x00DB);
+ cv(0x00FC, 0x00DC);
+ cv(0x00FD, 0x00DD);
+ cv(0x00FE, 0x00DE);
+ cv(0x00FF, 0x0178);
+ cv(0x0100, 0x0101);
+ cv(0x0101, 0x0100);
+ cv(0x0102, 0x0103);
+ cv(0x0103, 0x0102);
+ cv(0x0104, 0x0105);
+ cv(0x0105, 0x0104);
+ cv(0x0106, 0x0107);
+ cv(0x0107, 0x0106);
+ cv(0x0108, 0x0109);
+ cv(0x0109, 0x0108);
+ cv(0x010A, 0x010B);
+ cv(0x010B, 0x010A);
+ cv(0x010C, 0x010D);
+ cv(0x010D, 0x010C);
+ cv(0x010E, 0x010F);
+ cv(0x010F, 0x010E);
+ cv(0x0110, 0x0111);
+ cv(0x0111, 0x0110);
+ cv(0x0112, 0x0113);
+ cv(0x0113, 0x0112);
+ cv(0x0114, 0x0115);
+ cv(0x0115, 0x0114);
+ cv(0x0116, 0x0117);
+ cv(0x0117, 0x0116);
+ cv(0x0118, 0x0119);
+ cv(0x0119, 0x0118);
+ cv(0x011A, 0x011B);
+ cv(0x011B, 0x011A);
+ cv(0x011C, 0x011D);
+ cv(0x011D, 0x011C);
+ cv(0x011E, 0x011F);
+ cv(0x011F, 0x011E);
+ cv(0x0120, 0x0121);
+ cv(0x0121, 0x0120);
+ cv(0x0122, 0x0123);
+ cv(0x0123, 0x0122);
+ cv(0x0124, 0x0125);
+ cv(0x0125, 0x0124);
+ cv(0x0126, 0x0127);
+ cv(0x0127, 0x0126);
+ cv(0x0128, 0x0129);
+ cv(0x0129, 0x0128);
+ cv(0x012A, 0x012B);
+ cv(0x012B, 0x012A);
+ cv(0x012C, 0x012D);
+ cv(0x012D, 0x012C);
+ cv(0x012E, 0x012F);
+ cv(0x012F, 0x012E);
+ cv(0x0130, 0x0049, 0x0069);
+ cv(0x0131, 0x0049, 0x0069);
+ cv(0x0132, 0x0133);
+ cv(0x0133, 0x0132);
+ cv(0x0134, 0x0135);
+ cv(0x0135, 0x0134);
+ cv(0x0136, 0x0137);
+ cv(0x0137, 0x0136);
+ cv(0x0139, 0x013A);
+ cv(0x013A, 0x0139);
+ cv(0x013B, 0x013C);
+ cv(0x013C, 0x013B);
+ cv(0x013D, 0x013E);
+ cv(0x013E, 0x013D);
+ cv(0x013F, 0x0140);
+ cv(0x0140, 0x013F);
+ cv(0x0141, 0x0142);
+ cv(0x0142, 0x0141);
+ cv(0x0143, 0x0144);
+ cv(0x0144, 0x0143);
+ cv(0x0145, 0x0146);
+ cv(0x0146, 0x0145);
+ cv(0x0147, 0x0148);
+ cv(0x0148, 0x0147);
+ cv(0x014A, 0x014B);
+ cv(0x014B, 0x014A);
+ cv(0x014C, 0x014D);
+ cv(0x014D, 0x014C);
+ cv(0x014E, 0x014F);
+ cv(0x014F, 0x014E);
+ cv(0x0150, 0x0151);
+ cv(0x0151, 0x0150);
+ cv(0x0152, 0x0153);
+ cv(0x0153, 0x0152);
+ cv(0x0154, 0x0155);
+ cv(0x0155, 0x0154);
+ cv(0x0156, 0x0157);
+ cv(0x0157, 0x0156);
+ cv(0x0158, 0x0159);
+ cv(0x0159, 0x0158);
+ cv(0x015A, 0x015B);
+ cv(0x015B, 0x015A);
+ cv(0x015C, 0x015D);
+ cv(0x015D, 0x015C);
+ cv(0x015E, 0x015F);
+ cv(0x015F, 0x015E);
+ cv(0x0160, 0x0161);
+ cv(0x0161, 0x0160);
+ cv(0x0162, 0x0163);
+ cv(0x0163, 0x0162);
+ cv(0x0164, 0x0165);
+ cv(0x0165, 0x0164);
+ cv(0x0166, 0x0167);
+ cv(0x0167, 0x0166);
+ cv(0x0168, 0x0169);
+ cv(0x0169, 0x0168);
+ cv(0x016A, 0x016B);
+ cv(0x016B, 0x016A);
+ cv(0x016C, 0x016D);
+ cv(0x016D, 0x016C);
+ cv(0x016E, 0x016F);
+ cv(0x016F, 0x016E);
+ cv(0x0170, 0x0171);
+ cv(0x0171, 0x0170);
+ cv(0x0172, 0x0173);
+ cv(0x0173, 0x0172);
+ cv(0x0174, 0x0175);
+ cv(0x0175, 0x0174);
+ cv(0x0176, 0x0177);
+ cv(0x0177, 0x0176);
+ cv(0x0178, 0x00FF);
+ cv(0x0179, 0x017A);
+ cv(0x017A, 0x0179);
+ cv(0x017B, 0x017C);
+ cv(0x017C, 0x017B);
+ cv(0x017D, 0x017E);
+ cv(0x017E, 0x017D);
+ cv(0x017F, 0x0053, 0x0073);
+ cv(0x0181, 0x0253);
+ cv(0x0182, 0x0183);
+ cv(0x0183, 0x0182);
+ cv(0x0184, 0x0185);
+ cv(0x0185, 0x0184);
+ cv(0x0186, 0x0254);
+ cv(0x0187, 0x0188);
+ cv(0x0188, 0x0187);
+ cv(0x0189, 0x0256);
+ cv(0x018A, 0x0257);
+ cv(0x018B, 0x018C);
+ cv(0x018C, 0x018B);
+ cv(0x018E, 0x01DD);
+ cv(0x018F, 0x0259);
+ cv(0x0190, 0x025B);
+ cv(0x0191, 0x0192);
+ cv(0x0192, 0x0191);
+ cv(0x0193, 0x0260);
+ cv(0x0194, 0x0263);
+ cv(0x0195, 0x01F6);
+ cv(0x0196, 0x0269);
+ cv(0x0197, 0x0268);
+ cv(0x0198, 0x0199);
+ cv(0x0199, 0x0198);
+ cv(0x019A, 0x023D);
+ cv(0x019C, 0x026F);
+ cv(0x019D, 0x0272);
+ cv(0x019E, 0x0220);
+ cv(0x019F, 0x0275);
+ cv(0x01A0, 0x01A1);
+ cv(0x01A1, 0x01A0);
+ cv(0x01A2, 0x01A3);
+ cv(0x01A3, 0x01A2);
+ cv(0x01A4, 0x01A5);
+ cv(0x01A5, 0x01A4);
+ cv(0x01A6, 0x0280);
+ cv(0x01A7, 0x01A8);
+ cv(0x01A8, 0x01A7);
+ cv(0x01A9, 0x0283);
+ cv(0x01AC, 0x01AD);
+ cv(0x01AD, 0x01AC);
+ cv(0x01AE, 0x0288);
+ cv(0x01AF, 0x01B0);
+ cv(0x01B0, 0x01AF);
+ cv(0x01B1, 0x028A);
+ cv(0x01B2, 0x028B);
+ cv(0x01B3, 0x01B4);
+ cv(0x01B4, 0x01B3);
+ cv(0x01B5, 0x01B6);
+ cv(0x01B6, 0x01B5);
+ cv(0x01B7, 0x0292);
+ cv(0x01B8, 0x01B9);
+ cv(0x01B9, 0x01B8);
+ cv(0x01BC, 0x01BD);
+ cv(0x01BD, 0x01BC);
+ cv(0x01BF, 0x01F7);
+ cv(0x01C4, 0x01C5, 0x01C6);
+ cv(0x01C5, 0x01C4, 0x01C6);
+ cv(0x01C6, 0x01C4, 0x01C5);
+ cv(0x01C7, 0x01C8, 0x01C9);
+ cv(0x01C8, 0x01C7, 0x01C9);
+ cv(0x01C9, 0x01C7, 0x01C8);
+ cv(0x01CA, 0x01CB, 0x01CC);
+ cv(0x01CB, 0x01CA, 0x01CC);
+ cv(0x01CC, 0x01CA, 0x01CB);
+ cv(0x01CD, 0x01CE);
+ cv(0x01CE, 0x01CD);
+ cv(0x01CF, 0x01D0);
+ cv(0x01D0, 0x01CF);
+ cv(0x01D1, 0x01D2);
+ cv(0x01D2, 0x01D1);
+ cv(0x01D3, 0x01D4);
+ cv(0x01D4, 0x01D3);
+ cv(0x01D5, 0x01D6);
+ cv(0x01D6, 0x01D5);
+ cv(0x01D7, 0x01D8);
+ cv(0x01D8, 0x01D7);
+ cv(0x01D9, 0x01DA);
+ cv(0x01DA, 0x01D9);
+ cv(0x01DB, 0x01DC);
+ cv(0x01DC, 0x01DB);
+ cv(0x01DD, 0x018E);
+ cv(0x01DE, 0x01DF);
+ cv(0x01DF, 0x01DE);
+ cv(0x01E0, 0x01E1);
+ cv(0x01E1, 0x01E0);
+ cv(0x01E2, 0x01E3);
+ cv(0x01E3, 0x01E2);
+ cv(0x01E4, 0x01E5);
+ cv(0x01E5, 0x01E4);
+ cv(0x01E6, 0x01E7);
+ cv(0x01E7, 0x01E6);
+ cv(0x01E8, 0x01E9);
+ cv(0x01E9, 0x01E8);
+ cv(0x01EA, 0x01EB);
+ cv(0x01EB, 0x01EA);
+ cv(0x01EC, 0x01ED);
+ cv(0x01ED, 0x01EC);
+ cv(0x01EE, 0x01EF);
+ cv(0x01EF, 0x01EE);
+ cv(0x01F1, 0x01F2, 0x01F3);
+ cv(0x01F2, 0x01F1, 0x01F3);
+ cv(0x01F3, 0x01F1, 0x01F2);
+ cv(0x01F4, 0x01F5);
+ cv(0x01F5, 0x01F4);
+ cv(0x01F6, 0x0195);
+ cv(0x01F7, 0x01BF);
+ cv(0x01F8, 0x01F9);
+ cv(0x01F9, 0x01F8);
+ cv(0x01FA, 0x01FB);
+ cv(0x01FB, 0x01FA);
+ cv(0x01FC, 0x01FD);
+ cv(0x01FD, 0x01FC);
+ cv(0x01FE, 0x01FF);
+ cv(0x01FF, 0x01FE);
+ cv(0x0200, 0x0201);
+ cv(0x0201, 0x0200);
+ cv(0x0202, 0x0203);
+ cv(0x0203, 0x0202);
+ cv(0x0204, 0x0205);
+ cv(0x0205, 0x0204);
+ cv(0x0206, 0x0207);
+ cv(0x0207, 0x0206);
+ cv(0x0208, 0x0209);
+ cv(0x0209, 0x0208);
+ cv(0x020A, 0x020B);
+ cv(0x020B, 0x020A);
+ cv(0x020C, 0x020D);
+ cv(0x020D, 0x020C);
+ cv(0x020E, 0x020F);
+ cv(0x020F, 0x020E);
+ cv(0x0210, 0x0211);
+ cv(0x0211, 0x0210);
+ cv(0x0212, 0x0213);
+ cv(0x0213, 0x0212);
+ cv(0x0214, 0x0215);
+ cv(0x0215, 0x0214);
+ cv(0x0216, 0x0217);
+ cv(0x0217, 0x0216);
+ cv(0x0218, 0x0219);
+ cv(0x0219, 0x0218);
+ cv(0x021A, 0x021B);
+ cv(0x021B, 0x021A);
+ cv(0x021C, 0x021D);
+ cv(0x021D, 0x021C);
+ cv(0x021E, 0x021F);
+ cv(0x021F, 0x021E);
+ cv(0x0220, 0x019E);
+ cv(0x0222, 0x0223);
+ cv(0x0223, 0x0222);
+ cv(0x0224, 0x0225);
+ cv(0x0225, 0x0224);
+ cv(0x0226, 0x0227);
+ cv(0x0227, 0x0226);
+ cv(0x0228, 0x0229);
+ cv(0x0229, 0x0228);
+ cv(0x022A, 0x022B);
+ cv(0x022B, 0x022A);
+ cv(0x022C, 0x022D);
+ cv(0x022D, 0x022C);
+ cv(0x022E, 0x022F);
+ cv(0x022F, 0x022E);
+ cv(0x0230, 0x0231);
+ cv(0x0231, 0x0230);
+ cv(0x0232, 0x0233);
+ cv(0x0233, 0x0232);
+ cv(0x023B, 0x023C);
+ cv(0x023C, 0x023B);
+ cv(0x023D, 0x019A);
+ cv(0x0241, 0x0294);
+ cv(0x0253, 0x0181);
+ cv(0x0254, 0x0186);
+ cv(0x0256, 0x0189);
+ cv(0x0257, 0x018A);
+ cv(0x0259, 0x018F);
+ cv(0x025B, 0x0190);
+ cv(0x0260, 0x0193);
+ cv(0x0263, 0x0194);
+ cv(0x0268, 0x0197);
+ cv(0x0269, 0x0196);
+ cv(0x026F, 0x019C);
+ cv(0x0272, 0x019D);
+ cv(0x0275, 0x019F);
+ cv(0x0280, 0x01A6);
+ cv(0x0283, 0x01A9);
+ cv(0x0288, 0x01AE);
+ cv(0x028A, 0x01B1);
+ cv(0x028B, 0x01B2);
+ cv(0x0292, 0x01B7);
+ cv(0x0294, 0x0241);
+ cv(0x0345, 0x0399, 0x03B9, 0x1FBE);
+ cv(0x0386, 0x03AC);
+ cv(0x0388, 0x03AD);
+ cv(0x0389, 0x03AE);
+ cv(0x038A, 0x03AF);
+ cv(0x038C, 0x03CC);
+ cv(0x038E, 0x03CD);
+ cv(0x038F, 0x03CE);
+ cv(0x0391, 0x03B1);
+ cv(0x0392, 0x03B2, 0x03D0);
+ cv(0x0393, 0x03B3);
+ cv(0x0394, 0x03B4);
+ cv(0x0395, 0x03B5, 0x03F5);
+ cv(0x0396, 0x03B6);
+ cv(0x0397, 0x03B7);
+ cv(0x0398, 0x03B8, 0x03D1, 0x03F4);
+ cv(0x0399, 0x0345, 0x03B9, 0x1FBE);
+ cv(0x039A, 0x03BA, 0x03F0);
+ cv(0x039B, 0x03BB);
+ cv(0x039C, 0x00B5, 0x03BC);
+ cv(0x039D, 0x03BD);
+ cv(0x039E, 0x03BE);
+ cv(0x039F, 0x03BF);
+ cv(0x03A0, 0x03C0, 0x03D6);
+ cv(0x03A1, 0x03C1, 0x03F1);
+ cv(0x03A3, 0x03C2, 0x03C3);
+ cv(0x03A4, 0x03C4);
+ cv(0x03A5, 0x03C5);
+ cv(0x03A6, 0x03C6, 0x03D5);
+ cv(0x03A7, 0x03C7);
+ cv(0x03A8, 0x03C8);
+ cv(0x03A9, 0x03C9, 0x2126);
+ cv(0x03AA, 0x03CA);
+ cv(0x03AB, 0x03CB);
+ cv(0x03AC, 0x0386);
+ cv(0x03AD, 0x0388);
+ cv(0x03AE, 0x0389);
+ cv(0x03AF, 0x038A);
+ cv(0x03B1, 0x0391);
+ cv(0x03B2, 0x0392, 0x03D0);
+ cv(0x03B3, 0x0393);
+ cv(0x03B4, 0x0394);
+ cv(0x03B5, 0x0395, 0x03F5);
+ cv(0x03B6, 0x0396);
+ cv(0x03B7, 0x0397);
+ cv(0x03B8, 0x0398, 0x03D1, 0x03F4);
+ cv(0x03B9, 0x0345, 0x0399, 0x1FBE);
+ cv(0x03BA, 0x039A, 0x03F0);
+ cv(0x03BB, 0x039B);
+ cv(0x03BC, 0x00B5, 0x039C);
+ cv(0x03BD, 0x039D);
+ cv(0x03BE, 0x039E);
+ cv(0x03BF, 0x039F);
+ cv(0x03C0, 0x03A0, 0x03D6);
+ cv(0x03C1, 0x03A1, 0x03F1);
+ cv(0x03C2, 0x03A3, 0x03C3);
+ cv(0x03C3, 0x03A3, 0x03C2);
+ cv(0x03C4, 0x03A4);
+ cv(0x03C5, 0x03A5);
+ cv(0x03C6, 0x03A6, 0x03D5);
+ cv(0x03C7, 0x03A7);
+ cv(0x03C8, 0x03A8);
+ cv(0x03C9, 0x03A9, 0x2126);
+ cv(0x03CA, 0x03AA);
+ cv(0x03CB, 0x03AB);
+ cv(0x03CC, 0x038C);
+ cv(0x03CD, 0x038E);
+ cv(0x03CE, 0x038F);
+ cv(0x03D0, 0x0392, 0x03B2);
+ cv(0x03D1, 0x0398, 0x03B8);
+ cv(0x03D5, 0x03A6, 0x03C6);
+ cv(0x03D6, 0x03A0, 0x03C0);
+ cv(0x03D8, 0x03D9);
+ cv(0x03D9, 0x03D8);
+ cv(0x03DA, 0x03DB);
+ cv(0x03DB, 0x03DA);
+ cv(0x03DC, 0x03DD);
+ cv(0x03DD, 0x03DC);
+ cv(0x03DE, 0x03DF);
+ cv(0x03DF, 0x03DE);
+ cv(0x03E0, 0x03E1);
+ cv(0x03E1, 0x03E0);
+ cv(0x03E2, 0x03E3);
+ cv(0x03E3, 0x03E2);
+ cv(0x03E4, 0x03E5);
+ cv(0x03E5, 0x03E4);
+ cv(0x03E6, 0x03E7);
+ cv(0x03E7, 0x03E6);
+ cv(0x03E8, 0x03E9);
+ cv(0x03E9, 0x03E8);
+ cv(0x03EA, 0x03EB);
+ cv(0x03EB, 0x03EA);
+ cv(0x03EC, 0x03ED);
+ cv(0x03ED, 0x03EC);
+ cv(0x03EE, 0x03EF);
+ cv(0x03EF, 0x03EE);
+ cv(0x03F0, 0x039A, 0x03BA);
+ cv(0x03F1, 0x03A1, 0x03C1);
+ cv(0x03F2, 0x03F9);
+ cv(0x03F4, 0x0398, 0x03B8);
+ cv(0x03F5, 0x0395, 0x03B5);
+ cv(0x03F7, 0x03F8);
+ cv(0x03F8, 0x03F7);
+ cv(0x03F9, 0x03F2);
+ cv(0x03FA, 0x03FB);
+ cv(0x03FB, 0x03FA);
+ cv(0x0400, 0x0450);
+ cv(0x0401, 0x0451);
+ cv(0x0402, 0x0452);
+ cv(0x0403, 0x0453);
+ cv(0x0404, 0x0454);
+ cv(0x0405, 0x0455);
+ cv(0x0406, 0x0456);
+ cv(0x0407, 0x0457);
+ cv(0x0408, 0x0458);
+ cv(0x0409, 0x0459);
+ cv(0x040A, 0x045A);
+ cv(0x040B, 0x045B);
+ cv(0x040C, 0x045C);
+ cv(0x040D, 0x045D);
+ cv(0x040E, 0x045E);
+ cv(0x040F, 0x045F);
+ cv(0x0410, 0x0430);
+ cv(0x0411, 0x0431);
+ cv(0x0412, 0x0432);
+ cv(0x0413, 0x0433);
+ cv(0x0414, 0x0434);
+ cv(0x0415, 0x0435);
+ cv(0x0416, 0x0436);
+ cv(0x0417, 0x0437);
+ cv(0x0418, 0x0438);
+ cv(0x0419, 0x0439);
+ cv(0x041A, 0x043A);
+ cv(0x041B, 0x043B);
+ cv(0x041C, 0x043C);
+ cv(0x041D, 0x043D);
+ cv(0x041E, 0x043E);
+ cv(0x041F, 0x043F);
+ cv(0x0420, 0x0440);
+ cv(0x0421, 0x0441);
+ cv(0x0422, 0x0442);
+ cv(0x0423, 0x0443);
+ cv(0x0424, 0x0444);
+ cv(0x0425, 0x0445);
+ cv(0x0426, 0x0446);
+ cv(0x0427, 0x0447);
+ cv(0x0428, 0x0448);
+ cv(0x0429, 0x0449);
+ cv(0x042A, 0x044A);
+ cv(0x042B, 0x044B);
+ cv(0x042C, 0x044C);
+ cv(0x042D, 0x044D);
+ cv(0x042E, 0x044E);
+ cv(0x042F, 0x044F);
+ cv(0x0430, 0x0410);
+ cv(0x0431, 0x0411);
+ cv(0x0432, 0x0412);
+ cv(0x0433, 0x0413);
+ cv(0x0434, 0x0414);
+ cv(0x0435, 0x0415);
+ cv(0x0436, 0x0416);
+ cv(0x0437, 0x0417);
+ cv(0x0438, 0x0418);
+ cv(0x0439, 0x0419);
+ cv(0x043A, 0x041A);
+ cv(0x043B, 0x041B);
+ cv(0x043C, 0x041C);
+ cv(0x043D, 0x041D);
+ cv(0x043E, 0x041E);
+ cv(0x043F, 0x041F);
+ cv(0x0440, 0x0420);
+ cv(0x0441, 0x0421);
+ cv(0x0442, 0x0422);
+ cv(0x0443, 0x0423);
+ cv(0x0444, 0x0424);
+ cv(0x0445, 0x0425);
+ cv(0x0446, 0x0426);
+ cv(0x0447, 0x0427);
+ cv(0x0448, 0x0428);
+ cv(0x0449, 0x0429);
+ cv(0x044A, 0x042A);
+ cv(0x044B, 0x042B);
+ cv(0x044C, 0x042C);
+ cv(0x044D, 0x042D);
+ cv(0x044E, 0x042E);
+ cv(0x044F, 0x042F);
+ cv(0x0450, 0x0400);
+ cv(0x0451, 0x0401);
+ cv(0x0452, 0x0402);
+ cv(0x0453, 0x0403);
+ cv(0x0454, 0x0404);
+ cv(0x0455, 0x0405);
+ cv(0x0456, 0x0406);
+ cv(0x0457, 0x0407);
+ cv(0x0458, 0x0408);
+ cv(0x0459, 0x0409);
+ cv(0x045A, 0x040A);
+ cv(0x045B, 0x040B);
+ cv(0x045C, 0x040C);
+ cv(0x045D, 0x040D);
+ cv(0x045E, 0x040E);
+ cv(0x045F, 0x040F);
+ cv(0x0460, 0x0461);
+ cv(0x0461, 0x0460);
+ cv(0x0462, 0x0463);
+ cv(0x0463, 0x0462);
+ cv(0x0464, 0x0465);
+ cv(0x0465, 0x0464);
+ cv(0x0466, 0x0467);
+ cv(0x0467, 0x0466);
+ cv(0x0468, 0x0469);
+ cv(0x0469, 0x0468);
+ cv(0x046A, 0x046B);
+ cv(0x046B, 0x046A);
+ cv(0x046C, 0x046D);
+ cv(0x046D, 0x046C);
+ cv(0x046E, 0x046F);
+ cv(0x046F, 0x046E);
+ cv(0x0470, 0x0471);
+ cv(0x0471, 0x0470);
+ cv(0x0472, 0x0473);
+ cv(0x0473, 0x0472);
+ cv(0x0474, 0x0475);
+ cv(0x0475, 0x0474);
+ cv(0x0476, 0x0477);
+ cv(0x0477, 0x0476);
+ cv(0x0478, 0x0479);
+ cv(0x0479, 0x0478);
+ cv(0x047A, 0x047B);
+ cv(0x047B, 0x047A);
+ cv(0x047C, 0x047D);
+ cv(0x047D, 0x047C);
+ cv(0x047E, 0x047F);
+ cv(0x047F, 0x047E);
+ cv(0x0480, 0x0481);
+ cv(0x0481, 0x0480);
+ cv(0x048A, 0x048B);
+ cv(0x048B, 0x048A);
+ cv(0x048C, 0x048D);
+ cv(0x048D, 0x048C);
+ cv(0x048E, 0x048F);
+ cv(0x048F, 0x048E);
+ cv(0x0490, 0x0491);
+ cv(0x0491, 0x0490);
+ cv(0x0492, 0x0493);
+ cv(0x0493, 0x0492);
+ cv(0x0494, 0x0495);
+ cv(0x0495, 0x0494);
+ cv(0x0496, 0x0497);
+ cv(0x0497, 0x0496);
+ cv(0x0498, 0x0499);
+ cv(0x0499, 0x0498);
+ cv(0x049A, 0x049B);
+ cv(0x049B, 0x049A);
+ cv(0x049C, 0x049D);
+ cv(0x049D, 0x049C);
+ cv(0x049E, 0x049F);
+ cv(0x049F, 0x049E);
+ cv(0x04A0, 0x04A1);
+ cv(0x04A1, 0x04A0);
+ cv(0x04A2, 0x04A3);
+ cv(0x04A3, 0x04A2);
+ cv(0x04A4, 0x04A5);
+ cv(0x04A5, 0x04A4);
+ cv(0x04A6, 0x04A7);
+ cv(0x04A7, 0x04A6);
+ cv(0x04A8, 0x04A9);
+ cv(0x04A9, 0x04A8);
+ cv(0x04AA, 0x04AB);
+ cv(0x04AB, 0x04AA);
+ cv(0x04AC, 0x04AD);
+ cv(0x04AD, 0x04AC);
+ cv(0x04AE, 0x04AF);
+ cv(0x04AF, 0x04AE);
+ cv(0x04B0, 0x04B1);
+ cv(0x04B1, 0x04B0);
+ cv(0x04B2, 0x04B3);
+ cv(0x04B3, 0x04B2);
+ cv(0x04B4, 0x04B5);
+ cv(0x04B5, 0x04B4);
+ cv(0x04B6, 0x04B7);
+ cv(0x04B7, 0x04B6);
+ cv(0x04B8, 0x04B9);
+ cv(0x04B9, 0x04B8);
+ cv(0x04BA, 0x04BB);
+ cv(0x04BB, 0x04BA);
+ cv(0x04BC, 0x04BD);
+ cv(0x04BD, 0x04BC);
+ cv(0x04BE, 0x04BF);
+ cv(0x04BF, 0x04BE);
+ cv(0x04C1, 0x04C2);
+ cv(0x04C2, 0x04C1);
+ cv(0x04C3, 0x04C4);
+ cv(0x04C4, 0x04C3);
+ cv(0x04C5, 0x04C6);
+ cv(0x04C6, 0x04C5);
+ cv(0x04C7, 0x04C8);
+ cv(0x04C8, 0x04C7);
+ cv(0x04C9, 0x04CA);
+ cv(0x04CA, 0x04C9);
+ cv(0x04CB, 0x04CC);
+ cv(0x04CC, 0x04CB);
+ cv(0x04CD, 0x04CE);
+ cv(0x04CE, 0x04CD);
+ cv(0x04D0, 0x04D1);
+ cv(0x04D1, 0x04D0);
+ cv(0x04D2, 0x04D3);
+ cv(0x04D3, 0x04D2);
+ cv(0x04D4, 0x04D5);
+ cv(0x04D5, 0x04D4);
+ cv(0x04D6, 0x04D7);
+ cv(0x04D7, 0x04D6);
+ cv(0x04D8, 0x04D9);
+ cv(0x04D9, 0x04D8);
+ cv(0x04DA, 0x04DB);
+ cv(0x04DB, 0x04DA);
+ cv(0x04DC, 0x04DD);
+ cv(0x04DD, 0x04DC);
+ cv(0x04DE, 0x04DF);
+ cv(0x04DF, 0x04DE);
+ cv(0x04E0, 0x04E1);
+ cv(0x04E1, 0x04E0);
+ cv(0x04E2, 0x04E3);
+ cv(0x04E3, 0x04E2);
+ cv(0x04E4, 0x04E5);
+ cv(0x04E5, 0x04E4);
+ cv(0x04E6, 0x04E7);
+ cv(0x04E7, 0x04E6);
+ cv(0x04E8, 0x04E9);
+ cv(0x04E9, 0x04E8);
+ cv(0x04EA, 0x04EB);
+ cv(0x04EB, 0x04EA);
+ cv(0x04EC, 0x04ED);
+ cv(0x04ED, 0x04EC);
+ cv(0x04EE, 0x04EF);
+ cv(0x04EF, 0x04EE);
+ cv(0x04F0, 0x04F1);
+ cv(0x04F1, 0x04F0);
+ cv(0x04F2, 0x04F3);
+ cv(0x04F3, 0x04F2);
+ cv(0x04F4, 0x04F5);
+ cv(0x04F5, 0x04F4);
+ cv(0x04F6, 0x04F7);
+ cv(0x04F7, 0x04F6);
+ cv(0x04F8, 0x04F9);
+ cv(0x04F9, 0x04F8);
+ cv(0x0500, 0x0501);
+ cv(0x0501, 0x0500);
+ cv(0x0502, 0x0503);
+ cv(0x0503, 0x0502);
+ cv(0x0504, 0x0505);
+ cv(0x0505, 0x0504);
+ cv(0x0506, 0x0507);
+ cv(0x0507, 0x0506);
+ cv(0x0508, 0x0509);
+ cv(0x0509, 0x0508);
+ cv(0x050A, 0x050B);
+ cv(0x050B, 0x050A);
+ cv(0x050C, 0x050D);
+ cv(0x050D, 0x050C);
+ cv(0x050E, 0x050F);
+ cv(0x050F, 0x050E);
+ cv(0x0531, 0x0561);
+ cv(0x0532, 0x0562);
+ cv(0x0533, 0x0563);
+ cv(0x0534, 0x0564);
+ cv(0x0535, 0x0565);
+ cv(0x0536, 0x0566);
+ cv(0x0537, 0x0567);
+ cv(0x0538, 0x0568);
+ cv(0x0539, 0x0569);
+ cv(0x053A, 0x056A);
+ cv(0x053B, 0x056B);
+ cv(0x053C, 0x056C);
+ cv(0x053D, 0x056D);
+ cv(0x053E, 0x056E);
+ cv(0x053F, 0x056F);
+ cv(0x0540, 0x0570);
+ cv(0x0541, 0x0571);
+ cv(0x0542, 0x0572);
+ cv(0x0543, 0x0573);
+ cv(0x0544, 0x0574);
+ cv(0x0545, 0x0575);
+ cv(0x0546, 0x0576);
+ cv(0x0547, 0x0577);
+ cv(0x0548, 0x0578);
+ cv(0x0549, 0x0579);
+ cv(0x054A, 0x057A);
+ cv(0x054B, 0x057B);
+ cv(0x054C, 0x057C);
+ cv(0x054D, 0x057D);
+ cv(0x054E, 0x057E);
+ cv(0x054F, 0x057F);
+ cv(0x0550, 0x0580);
+ cv(0x0551, 0x0581);
+ cv(0x0552, 0x0582);
+ cv(0x0553, 0x0583);
+ cv(0x0554, 0x0584);
+ cv(0x0555, 0x0585);
+ cv(0x0556, 0x0586);
+ cv(0x0561, 0x0531);
+ cv(0x0562, 0x0532);
+ cv(0x0563, 0x0533);
+ cv(0x0564, 0x0534);
+ cv(0x0565, 0x0535);
+ cv(0x0566, 0x0536);
+ cv(0x0567, 0x0537);
+ cv(0x0568, 0x0538);
+ cv(0x0569, 0x0539);
+ cv(0x056A, 0x053A);
+ cv(0x056B, 0x053B);
+ cv(0x056C, 0x053C);
+ cv(0x056D, 0x053D);
+ cv(0x056E, 0x053E);
+ cv(0x056F, 0x053F);
+ cv(0x0570, 0x0540);
+ cv(0x0571, 0x0541);
+ cv(0x0572, 0x0542);
+ cv(0x0573, 0x0543);
+ cv(0x0574, 0x0544);
+ cv(0x0575, 0x0545);
+ cv(0x0576, 0x0546);
+ cv(0x0577, 0x0547);
+ cv(0x0578, 0x0548);
+ cv(0x0579, 0x0549);
+ cv(0x057A, 0x054A);
+ cv(0x057B, 0x054B);
+ cv(0x057C, 0x054C);
+ cv(0x057D, 0x054D);
+ cv(0x057E, 0x054E);
+ cv(0x057F, 0x054F);
+ cv(0x0580, 0x0550);
+ cv(0x0581, 0x0551);
+ cv(0x0582, 0x0552);
+ cv(0x0583, 0x0553);
+ cv(0x0584, 0x0554);
+ cv(0x0585, 0x0555);
+ cv(0x0586, 0x0556);
+ cv(0x10A0, 0x2D00);
+ cv(0x10A1, 0x2D01);
+ cv(0x10A2, 0x2D02);
+ cv(0x10A3, 0x2D03);
+ cv(0x10A4, 0x2D04);
+ cv(0x10A5, 0x2D05);
+ cv(0x10A6, 0x2D06);
+ cv(0x10A7, 0x2D07);
+ cv(0x10A8, 0x2D08);
+ cv(0x10A9, 0x2D09);
+ cv(0x10AA, 0x2D0A);
+ cv(0x10AB, 0x2D0B);
+ cv(0x10AC, 0x2D0C);
+ cv(0x10AD, 0x2D0D);
+ cv(0x10AE, 0x2D0E);
+ cv(0x10AF, 0x2D0F);
+ cv(0x10B0, 0x2D10);
+ cv(0x10B1, 0x2D11);
+ cv(0x10B2, 0x2D12);
+ cv(0x10B3, 0x2D13);
+ cv(0x10B4, 0x2D14);
+ cv(0x10B5, 0x2D15);
+ cv(0x10B6, 0x2D16);
+ cv(0x10B7, 0x2D17);
+ cv(0x10B8, 0x2D18);
+ cv(0x10B9, 0x2D19);
+ cv(0x10BA, 0x2D1A);
+ cv(0x10BB, 0x2D1B);
+ cv(0x10BC, 0x2D1C);
+ cv(0x10BD, 0x2D1D);
+ cv(0x10BE, 0x2D1E);
+ cv(0x10BF, 0x2D1F);
+ cv(0x10C0, 0x2D20);
+ cv(0x10C1, 0x2D21);
+ cv(0x10C2, 0x2D22);
+ cv(0x10C3, 0x2D23);
+ cv(0x10C4, 0x2D24);
+ cv(0x10C5, 0x2D25);
+ cv(0x1E00, 0x1E01);
+ cv(0x1E01, 0x1E00);
+ cv(0x1E02, 0x1E03);
+ cv(0x1E03, 0x1E02);
+ cv(0x1E04, 0x1E05);
+ cv(0x1E05, 0x1E04);
+ cv(0x1E06, 0x1E07);
+ cv(0x1E07, 0x1E06);
+ cv(0x1E08, 0x1E09);
+ cv(0x1E09, 0x1E08);
+ cv(0x1E0A, 0x1E0B);
+ cv(0x1E0B, 0x1E0A);
+ cv(0x1E0C, 0x1E0D);
+ cv(0x1E0D, 0x1E0C);
+ cv(0x1E0E, 0x1E0F);
+ cv(0x1E0F, 0x1E0E);
+ cv(0x1E10, 0x1E11);
+ cv(0x1E11, 0x1E10);
+ cv(0x1E12, 0x1E13);
+ cv(0x1E13, 0x1E12);
+ cv(0x1E14, 0x1E15);
+ cv(0x1E15, 0x1E14);
+ cv(0x1E16, 0x1E17);
+ cv(0x1E17, 0x1E16);
+ cv(0x1E18, 0x1E19);
+ cv(0x1E19, 0x1E18);
+ cv(0x1E1A, 0x1E1B);
+ cv(0x1E1B, 0x1E1A);
+ cv(0x1E1C, 0x1E1D);
+ cv(0x1E1D, 0x1E1C);
+ cv(0x1E1E, 0x1E1F);
+ cv(0x1E1F, 0x1E1E);
+ cv(0x1E20, 0x1E21);
+ cv(0x1E21, 0x1E20);
+ cv(0x1E22, 0x1E23);
+ cv(0x1E23, 0x1E22);
+ cv(0x1E24, 0x1E25);
+ cv(0x1E25, 0x1E24);
+ cv(0x1E26, 0x1E27);
+ cv(0x1E27, 0x1E26);
+ cv(0x1E28, 0x1E29);
+ cv(0x1E29, 0x1E28);
+ cv(0x1E2A, 0x1E2B);
+ cv(0x1E2B, 0x1E2A);
+ cv(0x1E2C, 0x1E2D);
+ cv(0x1E2D, 0x1E2C);
+ cv(0x1E2E, 0x1E2F);
+ cv(0x1E2F, 0x1E2E);
+ cv(0x1E30, 0x1E31);
+ cv(0x1E31, 0x1E30);
+ cv(0x1E32, 0x1E33);
+ cv(0x1E33, 0x1E32);
+ cv(0x1E34, 0x1E35);
+ cv(0x1E35, 0x1E34);
+ cv(0x1E36, 0x1E37);
+ cv(0x1E37, 0x1E36);
+ cv(0x1E38, 0x1E39);
+ cv(0x1E39, 0x1E38);
+ cv(0x1E3A, 0x1E3B);
+ cv(0x1E3B, 0x1E3A);
+ cv(0x1E3C, 0x1E3D);
+ cv(0x1E3D, 0x1E3C);
+ cv(0x1E3E, 0x1E3F);
+ cv(0x1E3F, 0x1E3E);
+ cv(0x1E40, 0x1E41);
+ cv(0x1E41, 0x1E40);
+ cv(0x1E42, 0x1E43);
+ cv(0x1E43, 0x1E42);
+ cv(0x1E44, 0x1E45);
+ cv(0x1E45, 0x1E44);
+ cv(0x1E46, 0x1E47);
+ cv(0x1E47, 0x1E46);
+ cv(0x1E48, 0x1E49);
+ cv(0x1E49, 0x1E48);
+ cv(0x1E4A, 0x1E4B);
+ cv(0x1E4B, 0x1E4A);
+ cv(0x1E4C, 0x1E4D);
+ cv(0x1E4D, 0x1E4C);
+ cv(0x1E4E, 0x1E4F);
+ cv(0x1E4F, 0x1E4E);
+ cv(0x1E50, 0x1E51);
+ cv(0x1E51, 0x1E50);
+ cv(0x1E52, 0x1E53);
+ cv(0x1E53, 0x1E52);
+ cv(0x1E54, 0x1E55);
+ cv(0x1E55, 0x1E54);
+ cv(0x1E56, 0x1E57);
+ cv(0x1E57, 0x1E56);
+ cv(0x1E58, 0x1E59);
+ cv(0x1E59, 0x1E58);
+ cv(0x1E5A, 0x1E5B);
+ cv(0x1E5B, 0x1E5A);
+ cv(0x1E5C, 0x1E5D);
+ cv(0x1E5D, 0x1E5C);
+ cv(0x1E5E, 0x1E5F);
+ cv(0x1E5F, 0x1E5E);
+ cv(0x1E60, 0x1E61, 0x1E9B);
+ cv(0x1E61, 0x1E60, 0x1E9B);
+ cv(0x1E62, 0x1E63);
+ cv(0x1E63, 0x1E62);
+ cv(0x1E64, 0x1E65);
+ cv(0x1E65, 0x1E64);
+ cv(0x1E66, 0x1E67);
+ cv(0x1E67, 0x1E66);
+ cv(0x1E68, 0x1E69);
+ cv(0x1E69, 0x1E68);
+ cv(0x1E6A, 0x1E6B);
+ cv(0x1E6B, 0x1E6A);
+ cv(0x1E6C, 0x1E6D);
+ cv(0x1E6D, 0x1E6C);
+ cv(0x1E6E, 0x1E6F);
+ cv(0x1E6F, 0x1E6E);
+ cv(0x1E70, 0x1E71);
+ cv(0x1E71, 0x1E70);
+ cv(0x1E72, 0x1E73);
+ cv(0x1E73, 0x1E72);
+ cv(0x1E74, 0x1E75);
+ cv(0x1E75, 0x1E74);
+ cv(0x1E76, 0x1E77);
+ cv(0x1E77, 0x1E76);
+ cv(0x1E78, 0x1E79);
+ cv(0x1E79, 0x1E78);
+ cv(0x1E7A, 0x1E7B);
+ cv(0x1E7B, 0x1E7A);
+ cv(0x1E7C, 0x1E7D);
+ cv(0x1E7D, 0x1E7C);
+ cv(0x1E7E, 0x1E7F);
+ cv(0x1E7F, 0x1E7E);
+ cv(0x1E80, 0x1E81);
+ cv(0x1E81, 0x1E80);
+ cv(0x1E82, 0x1E83);
+ cv(0x1E83, 0x1E82);
+ cv(0x1E84, 0x1E85);
+ cv(0x1E85, 0x1E84);
+ cv(0x1E86, 0x1E87);
+ cv(0x1E87, 0x1E86);
+ cv(0x1E88, 0x1E89);
+ cv(0x1E89, 0x1E88);
+ cv(0x1E8A, 0x1E8B);
+ cv(0x1E8B, 0x1E8A);
+ cv(0x1E8C, 0x1E8D);
+ cv(0x1E8D, 0x1E8C);
+ cv(0x1E8E, 0x1E8F);
+ cv(0x1E8F, 0x1E8E);
+ cv(0x1E90, 0x1E91);
+ cv(0x1E91, 0x1E90);
+ cv(0x1E92, 0x1E93);
+ cv(0x1E93, 0x1E92);
+ cv(0x1E94, 0x1E95);
+ cv(0x1E95, 0x1E94);
+ cv(0x1E9B, 0x1E60, 0x1E61);
+ cv(0x1EA0, 0x1EA1);
+ cv(0x1EA1, 0x1EA0);
+ cv(0x1EA2, 0x1EA3);
+ cv(0x1EA3, 0x1EA2);
+ cv(0x1EA4, 0x1EA5);
+ cv(0x1EA5, 0x1EA4);
+ cv(0x1EA6, 0x1EA7);
+ cv(0x1EA7, 0x1EA6);
+ cv(0x1EA8, 0x1EA9);
+ cv(0x1EA9, 0x1EA8);
+ cv(0x1EAA, 0x1EAB);
+ cv(0x1EAB, 0x1EAA);
+ cv(0x1EAC, 0x1EAD);
+ cv(0x1EAD, 0x1EAC);
+ cv(0x1EAE, 0x1EAF);
+ cv(0x1EAF, 0x1EAE);
+ cv(0x1EB0, 0x1EB1);
+ cv(0x1EB1, 0x1EB0);
+ cv(0x1EB2, 0x1EB3);
+ cv(0x1EB3, 0x1EB2);
+ cv(0x1EB4, 0x1EB5);
+ cv(0x1EB5, 0x1EB4);
+ cv(0x1EB6, 0x1EB7);
+ cv(0x1EB7, 0x1EB6);
+ cv(0x1EB8, 0x1EB9);
+ cv(0x1EB9, 0x1EB8);
+ cv(0x1EBA, 0x1EBB);
+ cv(0x1EBB, 0x1EBA);
+ cv(0x1EBC, 0x1EBD);
+ cv(0x1EBD, 0x1EBC);
+ cv(0x1EBE, 0x1EBF);
+ cv(0x1EBF, 0x1EBE);
+ cv(0x1EC0, 0x1EC1);
+ cv(0x1EC1, 0x1EC0);
+ cv(0x1EC2, 0x1EC3);
+ cv(0x1EC3, 0x1EC2);
+ cv(0x1EC4, 0x1EC5);
+ cv(0x1EC5, 0x1EC4);
+ cv(0x1EC6, 0x1EC7);
+ cv(0x1EC7, 0x1EC6);
+ cv(0x1EC8, 0x1EC9);
+ cv(0x1EC9, 0x1EC8);
+ cv(0x1ECA, 0x1ECB);
+ cv(0x1ECB, 0x1ECA);
+ cv(0x1ECC, 0x1ECD);
+ cv(0x1ECD, 0x1ECC);
+ cv(0x1ECE, 0x1ECF);
+ cv(0x1ECF, 0x1ECE);
+ cv(0x1ED0, 0x1ED1);
+ cv(0x1ED1, 0x1ED0);
+ cv(0x1ED2, 0x1ED3);
+ cv(0x1ED3, 0x1ED2);
+ cv(0x1ED4, 0x1ED5);
+ cv(0x1ED5, 0x1ED4);
+ cv(0x1ED6, 0x1ED7);
+ cv(0x1ED7, 0x1ED6);
+ cv(0x1ED8, 0x1ED9);
+ cv(0x1ED9, 0x1ED8);
+ cv(0x1EDA, 0x1EDB);
+ cv(0x1EDB, 0x1EDA);
+ cv(0x1EDC, 0x1EDD);
+ cv(0x1EDD, 0x1EDC);
+ cv(0x1EDE, 0x1EDF);
+ cv(0x1EDF, 0x1EDE);
+ cv(0x1EE0, 0x1EE1);
+ cv(0x1EE1, 0x1EE0);
+ cv(0x1EE2, 0x1EE3);
+ cv(0x1EE3, 0x1EE2);
+ cv(0x1EE4, 0x1EE5);
+ cv(0x1EE5, 0x1EE4);
+ cv(0x1EE6, 0x1EE7);
+ cv(0x1EE7, 0x1EE6);
+ cv(0x1EE8, 0x1EE9);
+ cv(0x1EE9, 0x1EE8);
+ cv(0x1EEA, 0x1EEB);
+ cv(0x1EEB, 0x1EEA);
+ cv(0x1EEC, 0x1EED);
+ cv(0x1EED, 0x1EEC);
+ cv(0x1EEE, 0x1EEF);
+ cv(0x1EEF, 0x1EEE);
+ cv(0x1EF0, 0x1EF1);
+ cv(0x1EF1, 0x1EF0);
+ cv(0x1EF2, 0x1EF3);
+ cv(0x1EF3, 0x1EF2);
+ cv(0x1EF4, 0x1EF5);
+ cv(0x1EF5, 0x1EF4);
+ cv(0x1EF6, 0x1EF7);
+ cv(0x1EF7, 0x1EF6);
+ cv(0x1EF8, 0x1EF9);
+ cv(0x1EF9, 0x1EF8);
+ cv(0x1F00, 0x1F08);
+ cv(0x1F01, 0x1F09);
+ cv(0x1F02, 0x1F0A);
+ cv(0x1F03, 0x1F0B);
+ cv(0x1F04, 0x1F0C);
+ cv(0x1F05, 0x1F0D);
+ cv(0x1F06, 0x1F0E);
+ cv(0x1F07, 0x1F0F);
+ cv(0x1F08, 0x1F00);
+ cv(0x1F09, 0x1F01);
+ cv(0x1F0A, 0x1F02);
+ cv(0x1F0B, 0x1F03);
+ cv(0x1F0C, 0x1F04);
+ cv(0x1F0D, 0x1F05);
+ cv(0x1F0E, 0x1F06);
+ cv(0x1F0F, 0x1F07);
+ cv(0x1F10, 0x1F18);
+ cv(0x1F11, 0x1F19);
+ cv(0x1F12, 0x1F1A);
+ cv(0x1F13, 0x1F1B);
+ cv(0x1F14, 0x1F1C);
+ cv(0x1F15, 0x1F1D);
+ cv(0x1F18, 0x1F10);
+ cv(0x1F19, 0x1F11);
+ cv(0x1F1A, 0x1F12);
+ cv(0x1F1B, 0x1F13);
+ cv(0x1F1C, 0x1F14);
+ cv(0x1F1D, 0x1F15);
+ cv(0x1F20, 0x1F28);
+ cv(0x1F21, 0x1F29);
+ cv(0x1F22, 0x1F2A);
+ cv(0x1F23, 0x1F2B);
+ cv(0x1F24, 0x1F2C);
+ cv(0x1F25, 0x1F2D);
+ cv(0x1F26, 0x1F2E);
+ cv(0x1F27, 0x1F2F);
+ cv(0x1F28, 0x1F20);
+ cv(0x1F29, 0x1F21);
+ cv(0x1F2A, 0x1F22);
+ cv(0x1F2B, 0x1F23);
+ cv(0x1F2C, 0x1F24);
+ cv(0x1F2D, 0x1F25);
+ cv(0x1F2E, 0x1F26);
+ cv(0x1F2F, 0x1F27);
+ cv(0x1F30, 0x1F38);
+ cv(0x1F31, 0x1F39);
+ cv(0x1F32, 0x1F3A);
+ cv(0x1F33, 0x1F3B);
+ cv(0x1F34, 0x1F3C);
+ cv(0x1F35, 0x1F3D);
+ cv(0x1F36, 0x1F3E);
+ cv(0x1F37, 0x1F3F);
+ cv(0x1F38, 0x1F30);
+ cv(0x1F39, 0x1F31);
+ cv(0x1F3A, 0x1F32);
+ cv(0x1F3B, 0x1F33);
+ cv(0x1F3C, 0x1F34);
+ cv(0x1F3D, 0x1F35);
+ cv(0x1F3E, 0x1F36);
+ cv(0x1F3F, 0x1F37);
+ cv(0x1F40, 0x1F48);
+ cv(0x1F41, 0x1F49);
+ cv(0x1F42, 0x1F4A);
+ cv(0x1F43, 0x1F4B);
+ cv(0x1F44, 0x1F4C);
+ cv(0x1F45, 0x1F4D);
+ cv(0x1F48, 0x1F40);
+ cv(0x1F49, 0x1F41);
+ cv(0x1F4A, 0x1F42);
+ cv(0x1F4B, 0x1F43);
+ cv(0x1F4C, 0x1F44);
+ cv(0x1F4D, 0x1F45);
+ cv(0x1F51, 0x1F59);
+ cv(0x1F53, 0x1F5B);
+ cv(0x1F55, 0x1F5D);
+ cv(0x1F57, 0x1F5F);
+ cv(0x1F59, 0x1F51);
+ cv(0x1F5B, 0x1F53);
+ cv(0x1F5D, 0x1F55);
+ cv(0x1F5F, 0x1F57);
+ cv(0x1F60, 0x1F68);
+ cv(0x1F61, 0x1F69);
+ cv(0x1F62, 0x1F6A);
+ cv(0x1F63, 0x1F6B);
+ cv(0x1F64, 0x1F6C);
+ cv(0x1F65, 0x1F6D);
+ cv(0x1F66, 0x1F6E);
+ cv(0x1F67, 0x1F6F);
+ cv(0x1F68, 0x1F60);
+ cv(0x1F69, 0x1F61);
+ cv(0x1F6A, 0x1F62);
+ cv(0x1F6B, 0x1F63);
+ cv(0x1F6C, 0x1F64);
+ cv(0x1F6D, 0x1F65);
+ cv(0x1F6E, 0x1F66);
+ cv(0x1F6F, 0x1F67);
+ cv(0x1F70, 0x1FBA);
+ cv(0x1F71, 0x1FBB);
+ cv(0x1F72, 0x1FC8);
+ cv(0x1F73, 0x1FC9);
+ cv(0x1F74, 0x1FCA);
+ cv(0x1F75, 0x1FCB);
+ cv(0x1F76, 0x1FDA);
+ cv(0x1F77, 0x1FDB);
+ cv(0x1F78, 0x1FF8);
+ cv(0x1F79, 0x1FF9);
+ cv(0x1F7A, 0x1FEA);
+ cv(0x1F7B, 0x1FEB);
+ cv(0x1F7C, 0x1FFA);
+ cv(0x1F7D, 0x1FFB);
+ cv(0x1F80, 0x1F88);
+ cv(0x1F81, 0x1F89);
+ cv(0x1F82, 0x1F8A);
+ cv(0x1F83, 0x1F8B);
+ cv(0x1F84, 0x1F8C);
+ cv(0x1F85, 0x1F8D);
+ cv(0x1F86, 0x1F8E);
+ cv(0x1F87, 0x1F8F);
+ cv(0x1F88, 0x1F80);
+ cv(0x1F89, 0x1F81);
+ cv(0x1F8A, 0x1F82);
+ cv(0x1F8B, 0x1F83);
+ cv(0x1F8C, 0x1F84);
+ cv(0x1F8D, 0x1F85);
+ cv(0x1F8E, 0x1F86);
+ cv(0x1F8F, 0x1F87);
+ cv(0x1F90, 0x1F98);
+ cv(0x1F91, 0x1F99);
+ cv(0x1F92, 0x1F9A);
+ cv(0x1F93, 0x1F9B);
+ cv(0x1F94, 0x1F9C);
+ cv(0x1F95, 0x1F9D);
+ cv(0x1F96, 0x1F9E);
+ cv(0x1F97, 0x1F9F);
+ cv(0x1F98, 0x1F90);
+ cv(0x1F99, 0x1F91);
+ cv(0x1F9A, 0x1F92);
+ cv(0x1F9B, 0x1F93);
+ cv(0x1F9C, 0x1F94);
+ cv(0x1F9D, 0x1F95);
+ cv(0x1F9E, 0x1F96);
+ cv(0x1F9F, 0x1F97);
+ cv(0x1FA0, 0x1FA8);
+ cv(0x1FA1, 0x1FA9);
+ cv(0x1FA2, 0x1FAA);
+ cv(0x1FA3, 0x1FAB);
+ cv(0x1FA4, 0x1FAC);
+ cv(0x1FA5, 0x1FAD);
+ cv(0x1FA6, 0x1FAE);
+ cv(0x1FA7, 0x1FAF);
+ cv(0x1FA8, 0x1FA0);
+ cv(0x1FA9, 0x1FA1);
+ cv(0x1FAA, 0x1FA2);
+ cv(0x1FAB, 0x1FA3);
+ cv(0x1FAC, 0x1FA4);
+ cv(0x1FAD, 0x1FA5);
+ cv(0x1FAE, 0x1FA6);
+ cv(0x1FAF, 0x1FA7);
+ cv(0x1FB0, 0x1FB8);
+ cv(0x1FB1, 0x1FB9);
+ cv(0x1FB3, 0x1FBC);
+ cv(0x1FB8, 0x1FB0);
+ cv(0x1FB9, 0x1FB1);
+ cv(0x1FBA, 0x1F70);
+ cv(0x1FBB, 0x1F71);
+ cv(0x1FBC, 0x1FB3);
+ cv(0x1FBE, 0x0345, 0x0399, 0x03B9);
+ cv(0x1FC3, 0x1FCC);
+ cv(0x1FC8, 0x1F72);
+ cv(0x1FC9, 0x1F73);
+ cv(0x1FCA, 0x1F74);
+ cv(0x1FCB, 0x1F75);
+ cv(0x1FCC, 0x1FC3);
+ cv(0x1FD0, 0x1FD8);
+ cv(0x1FD1, 0x1FD9);
+ cv(0x1FD8, 0x1FD0);
+ cv(0x1FD9, 0x1FD1);
+ cv(0x1FDA, 0x1F76);
+ cv(0x1FDB, 0x1F77);
+ cv(0x1FE0, 0x1FE8);
+ cv(0x1FE1, 0x1FE9);
+ cv(0x1FE5, 0x1FEC);
+ cv(0x1FE8, 0x1FE0);
+ cv(0x1FE9, 0x1FE1);
+ cv(0x1FEA, 0x1F7A);
+ cv(0x1FEB, 0x1F7B);
+ cv(0x1FEC, 0x1FE5);
+ cv(0x1FF3, 0x1FFC);
+ cv(0x1FF8, 0x1F78);
+ cv(0x1FF9, 0x1F79);
+ cv(0x1FFA, 0x1F7C);
+ cv(0x1FFB, 0x1F7D);
+ cv(0x1FFC, 0x1FF3);
+ cv(0x2126, 0x03A9, 0x03C9);
+ cv(0x212A, 0x004B, 0x006B);
+ cv(0x212B, 0x00C5, 0x00E5);
+ cv(0x2160, 0x2170);
+ cv(0x2161, 0x2171);
+ cv(0x2162, 0x2172);
+ cv(0x2163, 0x2173);
+ cv(0x2164, 0x2174);
+ cv(0x2165, 0x2175);
+ cv(0x2166, 0x2176);
+ cv(0x2167, 0x2177);
+ cv(0x2168, 0x2178);
+ cv(0x2169, 0x2179);
+ cv(0x216A, 0x217A);
+ cv(0x216B, 0x217B);
+ cv(0x216C, 0x217C);
+ cv(0x216D, 0x217D);
+ cv(0x216E, 0x217E);
+ cv(0x216F, 0x217F);
+ cv(0x2170, 0x2160);
+ cv(0x2171, 0x2161);
+ cv(0x2172, 0x2162);
+ cv(0x2173, 0x2163);
+ cv(0x2174, 0x2164);
+ cv(0x2175, 0x2165);
+ cv(0x2176, 0x2166);
+ cv(0x2177, 0x2167);
+ cv(0x2178, 0x2168);
+ cv(0x2179, 0x2169);
+ cv(0x217A, 0x216A);
+ cv(0x217B, 0x216B);
+ cv(0x217C, 0x216C);
+ cv(0x217D, 0x216D);
+ cv(0x217E, 0x216E);
+ cv(0x217F, 0x216F);
+ cv(0x24B6, 0x24D0);
+ cv(0x24B7, 0x24D1);
+ cv(0x24B8, 0x24D2);
+ cv(0x24B9, 0x24D3);
+ cv(0x24BA, 0x24D4);
+ cv(0x24BB, 0x24D5);
+ cv(0x24BC, 0x24D6);
+ cv(0x24BD, 0x24D7);
+ cv(0x24BE, 0x24D8);
+ cv(0x24BF, 0x24D9);
+ cv(0x24C0, 0x24DA);
+ cv(0x24C1, 0x24DB);
+ cv(0x24C2, 0x24DC);
+ cv(0x24C3, 0x24DD);
+ cv(0x24C4, 0x24DE);
+ cv(0x24C5, 0x24DF);
+ cv(0x24C6, 0x24E0);
+ cv(0x24C7, 0x24E1);
+ cv(0x24C8, 0x24E2);
+ cv(0x24C9, 0x24E3);
+ cv(0x24CA, 0x24E4);
+ cv(0x24CB, 0x24E5);
+ cv(0x24CC, 0x24E6);
+ cv(0x24CD, 0x24E7);
+ cv(0x24CE, 0x24E8);
+ cv(0x24CF, 0x24E9);
+ cv(0x24D0, 0x24B6);
+ cv(0x24D1, 0x24B7);
+ cv(0x24D2, 0x24B8);
+ cv(0x24D3, 0x24B9);
+ cv(0x24D4, 0x24BA);
+ cv(0x24D5, 0x24BB);
+ cv(0x24D6, 0x24BC);
+ cv(0x24D7, 0x24BD);
+ cv(0x24D8, 0x24BE);
+ cv(0x24D9, 0x24BF);
+ cv(0x24DA, 0x24C0);
+ cv(0x24DB, 0x24C1);
+ cv(0x24DC, 0x24C2);
+ cv(0x24DD, 0x24C3);
+ cv(0x24DE, 0x24C4);
+ cv(0x24DF, 0x24C5);
+ cv(0x24E0, 0x24C6);
+ cv(0x24E1, 0x24C7);
+ cv(0x24E2, 0x24C8);
+ cv(0x24E3, 0x24C9);
+ cv(0x24E4, 0x24CA);
+ cv(0x24E5, 0x24CB);
+ cv(0x24E6, 0x24CC);
+ cv(0x24E7, 0x24CD);
+ cv(0x24E8, 0x24CE);
+ cv(0x24E9, 0x24CF);
+ cv(0x2C00, 0x2C30);
+ cv(0x2C01, 0x2C31);
+ cv(0x2C02, 0x2C32);
+ cv(0x2C03, 0x2C33);
+ cv(0x2C04, 0x2C34);
+ cv(0x2C05, 0x2C35);
+ cv(0x2C06, 0x2C36);
+ cv(0x2C07, 0x2C37);
+ cv(0x2C08, 0x2C38);
+ cv(0x2C09, 0x2C39);
+ cv(0x2C0A, 0x2C3A);
+ cv(0x2C0B, 0x2C3B);
+ cv(0x2C0C, 0x2C3C);
+ cv(0x2C0D, 0x2C3D);
+ cv(0x2C0E, 0x2C3E);
+ cv(0x2C0F, 0x2C3F);
+ cv(0x2C10, 0x2C40);
+ cv(0x2C11, 0x2C41);
+ cv(0x2C12, 0x2C42);
+ cv(0x2C13, 0x2C43);
+ cv(0x2C14, 0x2C44);
+ cv(0x2C15, 0x2C45);
+ cv(0x2C16, 0x2C46);
+ cv(0x2C17, 0x2C47);
+ cv(0x2C18, 0x2C48);
+ cv(0x2C19, 0x2C49);
+ cv(0x2C1A, 0x2C4A);
+ cv(0x2C1B, 0x2C4B);
+ cv(0x2C1C, 0x2C4C);
+ cv(0x2C1D, 0x2C4D);
+ cv(0x2C1E, 0x2C4E);
+ cv(0x2C1F, 0x2C4F);
+ cv(0x2C20, 0x2C50);
+ cv(0x2C21, 0x2C51);
+ cv(0x2C22, 0x2C52);
+ cv(0x2C23, 0x2C53);
+ cv(0x2C24, 0x2C54);
+ cv(0x2C25, 0x2C55);
+ cv(0x2C26, 0x2C56);
+ cv(0x2C27, 0x2C57);
+ cv(0x2C28, 0x2C58);
+ cv(0x2C29, 0x2C59);
+ cv(0x2C2A, 0x2C5A);
+ cv(0x2C2B, 0x2C5B);
+ cv(0x2C2C, 0x2C5C);
+ cv(0x2C2D, 0x2C5D);
+ cv(0x2C2E, 0x2C5E);
+ cv(0x2C30, 0x2C00);
+ cv(0x2C31, 0x2C01);
+ cv(0x2C32, 0x2C02);
+ cv(0x2C33, 0x2C03);
+ cv(0x2C34, 0x2C04);
+ cv(0x2C35, 0x2C05);
+ cv(0x2C36, 0x2C06);
+ cv(0x2C37, 0x2C07);
+ cv(0x2C38, 0x2C08);
+ cv(0x2C39, 0x2C09);
+ cv(0x2C3A, 0x2C0A);
+ cv(0x2C3B, 0x2C0B);
+ cv(0x2C3C, 0x2C0C);
+ cv(0x2C3D, 0x2C0D);
+ cv(0x2C3E, 0x2C0E);
+ cv(0x2C3F, 0x2C0F);
+ cv(0x2C40, 0x2C10);
+ cv(0x2C41, 0x2C11);
+ cv(0x2C42, 0x2C12);
+ cv(0x2C43, 0x2C13);
+ cv(0x2C44, 0x2C14);
+ cv(0x2C45, 0x2C15);
+ cv(0x2C46, 0x2C16);
+ cv(0x2C47, 0x2C17);
+ cv(0x2C48, 0x2C18);
+ cv(0x2C49, 0x2C19);
+ cv(0x2C4A, 0x2C1A);
+ cv(0x2C4B, 0x2C1B);
+ cv(0x2C4C, 0x2C1C);
+ cv(0x2C4D, 0x2C1D);
+ cv(0x2C4E, 0x2C1E);
+ cv(0x2C4F, 0x2C1F);
+ cv(0x2C50, 0x2C20);
+ cv(0x2C51, 0x2C21);
+ cv(0x2C52, 0x2C22);
+ cv(0x2C53, 0x2C23);
+ cv(0x2C54, 0x2C24);
+ cv(0x2C55, 0x2C25);
+ cv(0x2C56, 0x2C26);
+ cv(0x2C57, 0x2C27);
+ cv(0x2C58, 0x2C28);
+ cv(0x2C59, 0x2C29);
+ cv(0x2C5A, 0x2C2A);
+ cv(0x2C5B, 0x2C2B);
+ cv(0x2C5C, 0x2C2C);
+ cv(0x2C5D, 0x2C2D);
+ cv(0x2C5E, 0x2C2E);
+ cv(0x2C80, 0x2C81);
+ cv(0x2C81, 0x2C80);
+ cv(0x2C82, 0x2C83);
+ cv(0x2C83, 0x2C82);
+ cv(0x2C84, 0x2C85);
+ cv(0x2C85, 0x2C84);
+ cv(0x2C86, 0x2C87);
+ cv(0x2C87, 0x2C86);
+ cv(0x2C88, 0x2C89);
+ cv(0x2C89, 0x2C88);
+ cv(0x2C8A, 0x2C8B);
+ cv(0x2C8B, 0x2C8A);
+ cv(0x2C8C, 0x2C8D);
+ cv(0x2C8D, 0x2C8C);
+ cv(0x2C8E, 0x2C8F);
+ cv(0x2C8F, 0x2C8E);
+ cv(0x2C90, 0x2C91);
+ cv(0x2C91, 0x2C90);
+ cv(0x2C92, 0x2C93);
+ cv(0x2C93, 0x2C92);
+ cv(0x2C94, 0x2C95);
+ cv(0x2C95, 0x2C94);
+ cv(0x2C96, 0x2C97);
+ cv(0x2C97, 0x2C96);
+ cv(0x2C98, 0x2C99);
+ cv(0x2C99, 0x2C98);
+ cv(0x2C9A, 0x2C9B);
+ cv(0x2C9B, 0x2C9A);
+ cv(0x2C9C, 0x2C9D);
+ cv(0x2C9D, 0x2C9C);
+ cv(0x2C9E, 0x2C9F);
+ cv(0x2C9F, 0x2C9E);
+ cv(0x2CA0, 0x2CA1);
+ cv(0x2CA1, 0x2CA0);
+ cv(0x2CA2, 0x2CA3);
+ cv(0x2CA3, 0x2CA2);
+ cv(0x2CA4, 0x2CA5);
+ cv(0x2CA5, 0x2CA4);
+ cv(0x2CA6, 0x2CA7);
+ cv(0x2CA7, 0x2CA6);
+ cv(0x2CA8, 0x2CA9);
+ cv(0x2CA9, 0x2CA8);
+ cv(0x2CAA, 0x2CAB);
+ cv(0x2CAB, 0x2CAA);
+ cv(0x2CAC, 0x2CAD);
+ cv(0x2CAD, 0x2CAC);
+ cv(0x2CAE, 0x2CAF);
+ cv(0x2CAF, 0x2CAE);
+ cv(0x2CB0, 0x2CB1);
+ cv(0x2CB1, 0x2CB0);
+ cv(0x2CB2, 0x2CB3);
+ cv(0x2CB3, 0x2CB2);
+ cv(0x2CB4, 0x2CB5);
+ cv(0x2CB5, 0x2CB4);
+ cv(0x2CB6, 0x2CB7);
+ cv(0x2CB7, 0x2CB6);
+ cv(0x2CB8, 0x2CB9);
+ cv(0x2CB9, 0x2CB8);
+ cv(0x2CBA, 0x2CBB);
+ cv(0x2CBB, 0x2CBA);
+ cv(0x2CBC, 0x2CBD);
+ cv(0x2CBD, 0x2CBC);
+ cv(0x2CBE, 0x2CBF);
+ cv(0x2CBF, 0x2CBE);
+ cv(0x2CC0, 0x2CC1);
+ cv(0x2CC1, 0x2CC0);
+ cv(0x2CC2, 0x2CC3);
+ cv(0x2CC3, 0x2CC2);
+ cv(0x2CC4, 0x2CC5);
+ cv(0x2CC5, 0x2CC4);
+ cv(0x2CC6, 0x2CC7);
+ cv(0x2CC7, 0x2CC6);
+ cv(0x2CC8, 0x2CC9);
+ cv(0x2CC9, 0x2CC8);
+ cv(0x2CCA, 0x2CCB);
+ cv(0x2CCB, 0x2CCA);
+ cv(0x2CCC, 0x2CCD);
+ cv(0x2CCD, 0x2CCC);
+ cv(0x2CCE, 0x2CCF);
+ cv(0x2CCF, 0x2CCE);
+ cv(0x2CD0, 0x2CD1);
+ cv(0x2CD1, 0x2CD0);
+ cv(0x2CD2, 0x2CD3);
+ cv(0x2CD3, 0x2CD2);
+ cv(0x2CD4, 0x2CD5);
+ cv(0x2CD5, 0x2CD4);
+ cv(0x2CD6, 0x2CD7);
+ cv(0x2CD7, 0x2CD6);
+ cv(0x2CD8, 0x2CD9);
+ cv(0x2CD9, 0x2CD8);
+ cv(0x2CDA, 0x2CDB);
+ cv(0x2CDB, 0x2CDA);
+ cv(0x2CDC, 0x2CDD);
+ cv(0x2CDD, 0x2CDC);
+ cv(0x2CDE, 0x2CDF);
+ cv(0x2CDF, 0x2CDE);
+ cv(0x2CE0, 0x2CE1);
+ cv(0x2CE1, 0x2CE0);
+ cv(0x2CE2, 0x2CE3);
+ cv(0x2CE3, 0x2CE2);
+ cv(0x2D00, 0x10A0);
+ cv(0x2D01, 0x10A1);
+ cv(0x2D02, 0x10A2);
+ cv(0x2D03, 0x10A3);
+ cv(0x2D04, 0x10A4);
+ cv(0x2D05, 0x10A5);
+ cv(0x2D06, 0x10A6);
+ cv(0x2D07, 0x10A7);
+ cv(0x2D08, 0x10A8);
+ cv(0x2D09, 0x10A9);
+ cv(0x2D0A, 0x10AA);
+ cv(0x2D0B, 0x10AB);
+ cv(0x2D0C, 0x10AC);
+ cv(0x2D0D, 0x10AD);
+ cv(0x2D0E, 0x10AE);
+ cv(0x2D0F, 0x10AF);
+ cv(0x2D10, 0x10B0);
+ cv(0x2D11, 0x10B1);
+ cv(0x2D12, 0x10B2);
+ cv(0x2D13, 0x10B3);
+ cv(0x2D14, 0x10B4);
+ cv(0x2D15, 0x10B5);
+ cv(0x2D16, 0x10B6);
+ cv(0x2D17, 0x10B7);
+ cv(0x2D18, 0x10B8);
+ cv(0x2D19, 0x10B9);
+ cv(0x2D1A, 0x10BA);
+ cv(0x2D1B, 0x10BB);
+ cv(0x2D1C, 0x10BC);
+ cv(0x2D1D, 0x10BD);
+ cv(0x2D1E, 0x10BE);
+ cv(0x2D1F, 0x10BF);
+ cv(0x2D20, 0x10C0);
+ cv(0x2D21, 0x10C1);
+ cv(0x2D22, 0x10C2);
+ cv(0x2D23, 0x10C3);
+ cv(0x2D24, 0x10C4);
+ cv(0x2D25, 0x10C5);
+ cv(0xFF21, 0xFF41);
+ cv(0xFF22, 0xFF42);
+ cv(0xFF23, 0xFF43);
+ cv(0xFF24, 0xFF44);
+ cv(0xFF25, 0xFF45);
+ cv(0xFF26, 0xFF46);
+ cv(0xFF27, 0xFF47);
+ cv(0xFF28, 0xFF48);
+ cv(0xFF29, 0xFF49);
+ cv(0xFF2A, 0xFF4A);
+ cv(0xFF2B, 0xFF4B);
+ cv(0xFF2C, 0xFF4C);
+ cv(0xFF2D, 0xFF4D);
+ cv(0xFF2E, 0xFF4E);
+ cv(0xFF2F, 0xFF4F);
+ cv(0xFF30, 0xFF50);
+ cv(0xFF31, 0xFF51);
+ cv(0xFF32, 0xFF52);
+ cv(0xFF33, 0xFF53);
+ cv(0xFF34, 0xFF54);
+ cv(0xFF35, 0xFF55);
+ cv(0xFF36, 0xFF56);
+ cv(0xFF37, 0xFF57);
+ cv(0xFF38, 0xFF58);
+ cv(0xFF39, 0xFF59);
+ cv(0xFF3A, 0xFF5A);
+ cv(0xFF41, 0xFF21);
+ cv(0xFF42, 0xFF22);
+ cv(0xFF43, 0xFF23);
+ cv(0xFF44, 0xFF24);
+ cv(0xFF45, 0xFF25);
+ cv(0xFF46, 0xFF26);
+ cv(0xFF47, 0xFF27);
+ cv(0xFF48, 0xFF28);
+ cv(0xFF49, 0xFF29);
+ cv(0xFF4A, 0xFF2A);
+ cv(0xFF4B, 0xFF2B);
+ cv(0xFF4C, 0xFF2C);
+ cv(0xFF4D, 0xFF2D);
+ cv(0xFF4E, 0xFF2E);
+ cv(0xFF4F, 0xFF2F);
+ cv(0xFF50, 0xFF30);
+ cv(0xFF51, 0xFF31);
+ cv(0xFF52, 0xFF32);
+ cv(0xFF53, 0xFF33);
+ cv(0xFF54, 0xFF34);
+ cv(0xFF55, 0xFF35);
+ cv(0xFF56, 0xFF36);
+ cv(0xFF57, 0xFF37);
+ cv(0xFF58, 0xFF38);
+ cv(0xFF59, 0xFF39);
+ cv(0xFF5A, 0xFF3A);
+ cv(0x10400, 0x10428);
+ cv(0x10401, 0x10429);
+ cv(0x10402, 0x1042A);
+ cv(0x10403, 0x1042B);
+ cv(0x10404, 0x1042C);
+ cv(0x10405, 0x1042D);
+ cv(0x10406, 0x1042E);
+ cv(0x10407, 0x1042F);
+ cv(0x10408, 0x10430);
+ cv(0x10409, 0x10431);
+ cv(0x1040A, 0x10432);
+ cv(0x1040B, 0x10433);
+ cv(0x1040C, 0x10434);
+ cv(0x1040D, 0x10435);
+ cv(0x1040E, 0x10436);
+ cv(0x1040F, 0x10437);
+ cv(0x10410, 0x10438);
+ cv(0x10411, 0x10439);
+ cv(0x10412, 0x1043A);
+ cv(0x10413, 0x1043B);
+ cv(0x10414, 0x1043C);
+ cv(0x10415, 0x1043D);
+ cv(0x10416, 0x1043E);
+ cv(0x10417, 0x1043F);
+ cv(0x10418, 0x10440);
+ cv(0x10419, 0x10441);
+ cv(0x1041A, 0x10442);
+ cv(0x1041B, 0x10443);
+ cv(0x1041C, 0x10444);
+ cv(0x1041D, 0x10445);
+ cv(0x1041E, 0x10446);
+ cv(0x1041F, 0x10447);
+ cv(0x10420, 0x10448);
+ cv(0x10421, 0x10449);
+ cv(0x10422, 0x1044A);
+ cv(0x10423, 0x1044B);
+ cv(0x10424, 0x1044C);
+ cv(0x10425, 0x1044D);
+ cv(0x10426, 0x1044E);
+ cv(0x10427, 0x1044F);
+ cv(0x10428, 0x10400);
+ cv(0x10429, 0x10401);
+ cv(0x1042A, 0x10402);
+ cv(0x1042B, 0x10403);
+ cv(0x1042C, 0x10404);
+ cv(0x1042D, 0x10405);
+ cv(0x1042E, 0x10406);
+ cv(0x1042F, 0x10407);
+ cv(0x10430, 0x10408);
+ cv(0x10431, 0x10409);
+ cv(0x10432, 0x1040A);
+ cv(0x10433, 0x1040B);
+ cv(0x10434, 0x1040C);
+ cv(0x10435, 0x1040D);
+ cv(0x10436, 0x1040E);
+ cv(0x10437, 0x1040F);
+ cv(0x10438, 0x10410);
+ cv(0x10439, 0x10411);
+ cv(0x1043A, 0x10412);
+ cv(0x1043B, 0x10413);
+ cv(0x1043C, 0x10414);
+ cv(0x1043D, 0x10415);
+ cv(0x1043E, 0x10416);
+ cv(0x1043F, 0x10417);
+ cv(0x10440, 0x10418);
+ cv(0x10441, 0x10419);
+ cv(0x10442, 0x1041A);
+ cv(0x10443, 0x1041B);
+ cv(0x10444, 0x1041C);
+ cv(0x10445, 0x1041D);
+ cv(0x10446, 0x1041E);
+ cv(0x10447, 0x1041F);
+ cv(0x10448, 0x10420);
+ cv(0x10449, 0x10421);
+ cv(0x1044A, 0x10422);
+ cv(0x1044B, 0x10423);
+ cv(0x1044C, 0x10424);
+ cv(0x1044D, 0x10425);
+ cv(0x1044E, 0x10426);
+ cv(0x1044F, 0x10427);
+ }
+}
+
diff --git a/sf/saxon/regex/Categories.java b/sf/saxon/regex/Categories.java
new file mode 100644
index 0000000..c8ae1d6
--- /dev/null
+++ b/sf/saxon/regex/Categories.java
@@ -0,0 +1,3435 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.serialize.charcode.XMLCharacterData;
+import net.sf.saxon.z.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Data for Regular expression character categories. This is derived from Unicode 6.0.0. Since Saxon 9.4,
+ * we no longer make use of Java's support for character categories since there are too many differences
+ * from Unicode.
+ */
+public class Categories {
+
+
+ private final static HashMap<String, IntPredicate> CATEGORIES = new HashMap<String, IntPredicate>(30);
+
+ static {
+
+
+ CATEGORIES.put("Cc", pred(new int[]{
+ 0x1, 0x1F,
+ 0x7F, 0x9F
+ }));
+
+
+ CATEGORIES.put("Cf", pred(new int[]{
+ 0xAD, 0xAD,
+ 0x600, 0x603,
+ 0x6DD, 0x6DD,
+ 0x70F, 0x70F,
+ 0x17B4, 0x17B5,
+ 0x200B, 0x200F,
+ 0x202A, 0x202E,
+ 0x2060, 0x2064,
+ 0x206A, 0x206F,
+ 0xFEFF, 0xFEFF,
+ 0xFFF9, 0xFFFB,
+ 0x110BD, 0x110BD,
+ 0x1D173, 0x1D17A,
+ 0xE0001, 0xE0001,
+ 0xE0020, 0xE007F
+ }));
+
+ CATEGORIES.put("Cn", pred(new int[]{
+ 0x0378, 0x0379,
+ 0x037F, 0x0383,
+ 0x038B, 0x038B,
+ 0x038D, 0x038D,
+ 0x03A2, 0x03A2,
+ 0x0528, 0x0530,
+ 0x0557, 0x0558,
+ 0x0560, 0x0560,
+ 0x0588, 0x0588,
+ 0x058B, 0x058F,
+ 0x0590, 0x0590,
+ 0x05C8, 0x05CF,
+ 0x05EB, 0x05EF,
+ 0x05F5, 0x05FF,
+ 0x0604, 0x0605,
+ 0x061C, 0x061D,
+ 0x070E, 0x070E,
+ 0x074B, 0x074C,
+ 0x07B2, 0x07BF,
+ 0x07FB, 0x07FF,
+ 0x082E, 0x082F,
+ 0x083F, 0x083F,
+ 0x085C, 0x085D,
+ 0x085F, 0x08FF,
+ 0x0978, 0x0978,
+ 0x0980, 0x0980,
+ 0x0984, 0x0984,
+ 0x098D, 0x098E,
+ 0x0991, 0x0992,
+ 0x09A9, 0x09A9,
+ 0x09B1, 0x09B1,
+ 0x09B3, 0x09B5,
+ 0x09BA, 0x09BB,
+ 0x09C5, 0x09C6,
+ 0x09C9, 0x09CA,
+ 0x09CF, 0x09D6,
+ 0x09D8, 0x09DB,
+ 0x09DE, 0x09DE,
+ 0x09E4, 0x09E5,
+ 0x09FC, 0x0A00,
+ 0x0A04, 0x0A04,
+ 0x0A0B, 0x0A0E,
+ 0x0A11, 0x0A12,
+ 0x0A29, 0x0A29,
+ 0x0A31, 0x0A31,
+ 0x0A34, 0x0A34,
+ 0x0A37, 0x0A37,
+ 0x0A3A, 0x0A3B,
+ 0x0A3D, 0x0A3D,
+ 0x0A43, 0x0A46,
+ 0x0A49, 0x0A4A,
+ 0x0A4E, 0x0A50,
+ 0x0A52, 0x0A58,
+ 0x0A5D, 0x0A5D,
+ 0x0A5F, 0x0A65,
+ 0x0A76, 0x0A80,
+ 0x0A84, 0x0A84,
+ 0x0A8E, 0x0A8E,
+ 0x0A92, 0x0A92,
+ 0x0AA9, 0x0AA9,
+ 0x0AB1, 0x0AB1,
+ 0x0AB4, 0x0AB4,
+ 0x0ABA, 0x0ABB,
+ 0x0AC6, 0x0AC6,
+ 0x0ACA, 0x0ACA,
+ 0x0ACE, 0x0ACF,
+ 0x0AD1, 0x0ADF,
+ 0x0AE4, 0x0AE5,
+ 0x0AF0, 0x0AF0,
+ 0x0AF2, 0x0B00,
+ 0x0B04, 0x0B04,
+ 0x0B0D, 0x0B0E,
+ 0x0B11, 0x0B12,
+ 0x0B29, 0x0B29,
+ 0x0B31, 0x0B31,
+ 0x0B34, 0x0B34,
+ 0x0B3A, 0x0B3B,
+ 0x0B45, 0x0B46,
+ 0x0B49, 0x0B4A,
+ 0x0B4E, 0x0B55,
+ 0x0B58, 0x0B5B,
+ 0x0B5E, 0x0B5E,
+ 0x0B64, 0x0B65,
+ 0x0B78, 0x0B81,
+ 0x0B84, 0x0B84,
+ 0x0B8B, 0x0B8D,
+ 0x0B91, 0x0B91,
+ 0x0B96, 0x0B98,
+ 0x0B9B, 0x0B9B,
+ 0x0B9D, 0x0B9D,
+ 0x0BA0, 0x0BA2,
+ 0x0BA5, 0x0BA7,
+ 0x0BAB, 0x0BAD,
+ 0x0BBA, 0x0BBD,
+ 0x0BC3, 0x0BC5,
+ 0x0BC9, 0x0BC9,
+ 0x0BCE, 0x0BCF,
+ 0x0BD1, 0x0BD6,
+ 0x0BD8, 0x0BE5,
+ 0x0BFB, 0x0C00,
+ 0x0C04, 0x0C04,
+ 0x0C0D, 0x0C0D,
+ 0x0C11, 0x0C11,
+ 0x0C29, 0x0C29,
+ 0x0C34, 0x0C34,
+ 0x0C3A, 0x0C3C,
+ 0x0C45, 0x0C45,
+ 0x0C49, 0x0C49,
+ 0x0C4E, 0x0C54,
+ 0x0C57, 0x0C57,
+ 0x0C5A, 0x0C5F,
+ 0x0C64, 0x0C65,
+ 0x0C70, 0x0C77,
+ 0x0C80, 0x0C81,
+ 0x0C84, 0x0C84,
+ 0x0C8D, 0x0C8D,
+ 0x0C91, 0x0C91,
+ 0x0CA9, 0x0CA9,
+ 0x0CB4, 0x0CB4,
+ 0x0CBA, 0x0CBB,
+ 0x0CC5, 0x0CC5,
+ 0x0CC9, 0x0CC9,
+ 0x0CCE, 0x0CD4,
+ 0x0CD7, 0x0CDD,
+ 0x0CDF, 0x0CDF,
+ 0x0CE4, 0x0CE5,
+ 0x0CF0, 0x0CF0,
+ 0x0CF3, 0x0D01,
+ 0x0D04, 0x0D04,
+ 0x0D0D, 0x0D0D,
+ 0x0D11, 0x0D11,
+ 0x0D3B, 0x0D3C,
+ 0x0D45, 0x0D45,
+ 0x0D49, 0x0D49,
+ 0x0D4F, 0x0D56,
+ 0x0D58, 0x0D5F,
+ 0x0D64, 0x0D65,
+ 0x0D76, 0x0D78,
+ 0x0D80, 0x0D81,
+ 0x0D84, 0x0D84,
+ 0x0D97, 0x0D99,
+ 0x0DB2, 0x0DB2,
+ 0x0DBC, 0x0DBC,
+ 0x0DBE, 0x0DBF,
+ 0x0DC7, 0x0DC9,
+ 0x0DCB, 0x0DCE,
+ 0x0DD5, 0x0DD5,
+ 0x0DD7, 0x0DD7,
+ 0x0DE0, 0x0DF1,
+ 0x0DF5, 0x0E00,
+ 0x0E3B, 0x0E3E,
+ 0x0E5C, 0x0E80,
+ 0x0E83, 0x0E83,
+ 0x0E85, 0x0E86,
+ 0x0E89, 0x0E89,
+ 0x0E8B, 0x0E8C,
+ 0x0E8E, 0x0E93,
+ 0x0E98, 0x0E98,
+ 0x0EA0, 0x0EA0,
+ 0x0EA4, 0x0EA4,
+ 0x0EA6, 0x0EA6,
+ 0x0EA8, 0x0EA9,
+ 0x0EAC, 0x0EAC,
+ 0x0EBA, 0x0EBA,
+ 0x0EBE, 0x0EBF,
+ 0x0EC5, 0x0EC5,
+ 0x0EC7, 0x0EC7,
+ 0x0ECE, 0x0ECF,
+ 0x0EDA, 0x0EDB,
+ 0x0EDE, 0x0EFF,
+ 0x0F48, 0x0F48,
+ 0x0F6D, 0x0F70,
+ 0x0F98, 0x0F98,
+ 0x0FBD, 0x0FBD,
+ 0x0FCD, 0x0FCD,
+ 0x0FDB, 0x0FFF,
+ 0x10C6, 0x10CF,
+ 0x10FD, 0x10FF,
+ 0x1249, 0x1249,
+ 0x124E, 0x124F,
+ 0x1257, 0x1257,
+ 0x1259, 0x1259,
+ 0x125E, 0x125F,
+ 0x1289, 0x1289,
+ 0x128E, 0x128F,
+ 0x12B1, 0x12B1,
+ 0x12B6, 0x12B7,
+ 0x12BF, 0x12BF,
+ 0x12C1, 0x12C1,
+ 0x12C6, 0x12C7,
+ 0x12D7, 0x12D7,
+ 0x1311, 0x1311,
+ 0x1316, 0x1317,
+ 0x135B, 0x135C,
+ 0x137D, 0x137F,
+ 0x139A, 0x139F,
+ 0x13F5, 0x13FF,
+ 0x169D, 0x169F,
+ 0x16F1, 0x16FF,
+ 0x170D, 0x170D,
+ 0x1715, 0x171F,
+ 0x1737, 0x173F,
+ 0x1754, 0x175F,
+ 0x176D, 0x176D,
+ 0x1771, 0x1771,
+ 0x1774, 0x177F,
+ 0x17DE, 0x17DF,
+ 0x17EA, 0x17EF,
+ 0x17FA, 0x17FF,
+ 0x180F, 0x180F,
+ 0x181A, 0x181F,
+ 0x1878, 0x187F,
+ 0x18AB, 0x18AF,
+ 0x18F6, 0x18FF,
+ 0x191D, 0x191F,
+ 0x192C, 0x192F,
+ 0x193C, 0x193F,
+ 0x1941, 0x1943,
+ 0x196E, 0x196F,
+ 0x1975, 0x197F,
+ 0x19AC, 0x19AF,
+ 0x19CA, 0x19CF,
+ 0x19DB, 0x19DD,
+ 0x1A1C, 0x1A1D,
+ 0x1A5F, 0x1A5F,
+ 0x1A7D, 0x1A7E,
+ 0x1A8A, 0x1A8F,
+ 0x1A9A, 0x1A9F,
+ 0x1AAE, 0x1AFF,
+ 0x1B4C, 0x1B4F,
+ 0x1B7D, 0x1B7F,
+ 0x1BAB, 0x1BAD,
+ 0x1BBA, 0x1BBF,
+ 0x1BF4, 0x1BFB,
+ 0x1C38, 0x1C3A,
+ 0x1C4A, 0x1C4C,
+ 0x1C80, 0x1CCF,
+ 0x1CF3, 0x1CFF,
+ 0x1DE7, 0x1DFB,
+ 0x1F16, 0x1F17,
+ 0x1F1E, 0x1F1F,
+ 0x1F46, 0x1F47,
+ 0x1F4E, 0x1F4F,
+ 0x1F58, 0x1F58,
+ 0x1F5A, 0x1F5A,
+ 0x1F5C, 0x1F5C,
+ 0x1F5E, 0x1F5E,
+ 0x1F7E, 0x1F7F,
+ 0x1FB5, 0x1FB5,
+ 0x1FC5, 0x1FC5,
+ 0x1FD4, 0x1FD5,
+ 0x1FDC, 0x1FDC,
+ 0x1FF0, 0x1FF1,
+ 0x1FF5, 0x1FF5,
+ 0x1FFF, 0x1FFF,
+ 0x2065, 0x2069,
+ 0x2072, 0x2073,
+ 0x208F, 0x208F,
+ 0x209D, 0x209F,
+ 0x20BA, 0x20CF,
+ 0x20F1, 0x20FF,
+ 0x218A, 0x218F,
+ 0x23F4, 0x23FF,
+ 0x2427, 0x243F,
+ 0x244B, 0x245F,
+ 0x2700, 0x2700,
+ 0x27CB, 0x27CB,
+ 0x27CD, 0x27CD,
+ 0x2B4D, 0x2B4F,
+ 0x2B5A, 0x2BFF,
+ 0x2C2F, 0x2C2F,
+ 0x2C5F, 0x2C5F,
+ 0x2CF2, 0x2CF8,
+ 0x2D26, 0x2D2F,
+ 0x2D66, 0x2D6E,
+ 0x2D71, 0x2D7E,
+ 0x2D97, 0x2D9F,
+ 0x2DA7, 0x2DA7,
+ 0x2DAF, 0x2DAF,
+ 0x2DB7, 0x2DB7,
+ 0x2DBF, 0x2DBF,
+ 0x2DC7, 0x2DC7,
+ 0x2DCF, 0x2DCF,
+ 0x2DD7, 0x2DD7,
+ 0x2DDF, 0x2DDF,
+ 0x2E32, 0x2E7F,
+ 0x2E9A, 0x2E9A,
+ 0x2EF4, 0x2EFF,
+ 0x2FD6, 0x2FEF,
+ 0x2FFC, 0x2FFF,
+ 0x3040, 0x3040,
+ 0x3097, 0x3098,
+ 0x3100, 0x3104,
+ 0x312E, 0x3130,
+ 0x318F, 0x318F,
+ 0x31BB, 0x31BF,
+ 0x31E4, 0x31EF,
+ 0x321F, 0x321F,
+ 0x32FF, 0x32FF,
+ 0x4DB6, 0x4DBF,
+ 0x9FCC, 0x9FFF,
+ 0xA48D, 0xA48F,
+ 0xA4C7, 0xA4CF,
+ 0xA62C, 0xA63F,
+ 0xA674, 0xA67B,
+ 0xA698, 0xA69F,
+ 0xA6F8, 0xA6FF,
+ 0xA78F, 0xA78F,
+ 0xA792, 0xA79F,
+ 0xA7AA, 0xA7F9,
+ 0xA82C, 0xA82F,
+ 0xA83A, 0xA83F,
+ 0xA878, 0xA87F,
+ 0xA8C5, 0xA8CD,
+ 0xA8DA, 0xA8DF,
+ 0xA8FC, 0xA8FF,
+ 0xA954, 0xA95E,
+ 0xA97D, 0xA97F,
+ 0xA9CE, 0xA9CE,
+ 0xA9DA, 0xA9DD,
+ 0xA9E0, 0xA9FF,
+ 0xAA37, 0xAA3F,
+ 0xAA4E, 0xAA4F,
+ 0xAA5A, 0xAA5B,
+ 0xAA7C, 0xAA7F,
+ 0xAAC3, 0xAADA,
+ 0xAAE0, 0xAB00,
+ 0xAB07, 0xAB08,
+ 0xAB0F, 0xAB10,
+ 0xAB17, 0xAB1F,
+ 0xAB27, 0xAB27,
+ 0xAB2F, 0xABBF,
+ 0xABEE, 0xABEF,
+ 0xABFA, 0xABFF,
+ 0xD7A4, 0xD7AF,
+ 0xD7C7, 0xD7CA,
+ 0xD7FC, 0xD7FF,
+ 0xFA2E, 0xFA2F,
+ 0xFA6E, 0xFA6F,
+ 0xFADA, 0xFAFF,
+ 0xFB07, 0xFB12,
+ 0xFB18, 0xFB1C,
+ 0xFB37, 0xFB37,
+ 0xFB3D, 0xFB3D,
+ 0xFB3F, 0xFB3F,
+ 0xFB42, 0xFB42,
+ 0xFB45, 0xFB45,
+ 0xFBC2, 0xFBD2,
+ 0xFD40, 0xFD4F,
+ 0xFD90, 0xFD91,
+ 0xFDC8, 0xFDCF,
+ 0xFDFE, 0xFDFF,
+ 0xFE1A, 0xFE1F,
+ 0xFE27, 0xFE2F,
+ 0xFE53, 0xFE53,
+ 0xFE67, 0xFE67,
+ 0xFE6C, 0xFE6F,
+ 0xFE75, 0xFE75,
+ 0xFEFD, 0xFEFE,
+ 0xFF00, 0xFF00,
+ 0xFFBF, 0xFFC1,
+ 0xFFC8, 0xFFC9,
+ 0xFFD0, 0xFFD1,
+ 0xFFD8, 0xFFD9,
+ 0xFFDD, 0xFFDF,
+ 0xFFE7, 0xFFE7,
+ 0xFFEF, 0xFFEF,
+ 0xFFF0, 0xFFF8,
+ 0x1000C, 0x1000C,
+ 0x10027, 0x10027,
+ 0x1003B, 0x1003B,
+ 0x1003E, 0x1003E,
+ 0x1004E, 0x1004F,
+ 0x1005E, 0x1007F,
+ 0x100FB, 0x100FF,
+ 0x10103, 0x10106,
+ 0x10134, 0x10136,
+ 0x1018B, 0x1018F,
+ 0x1019C, 0x101CF,
+ 0x101FE, 0x1027F,
+ 0x1029D, 0x1029F,
+ 0x102D1, 0x102FF,
+ 0x1031F, 0x1031F,
+ 0x10324, 0x1032F,
+ 0x1034B, 0x1037F,
+ 0x1039E, 0x1039E,
+ 0x103C4, 0x103C7,
+ 0x103D6, 0x103FF,
+ 0x1049E, 0x1049F,
+ 0x104AA, 0x107FF,
+ 0x10806, 0x10807,
+ 0x10809, 0x10809,
+ 0x10836, 0x10836,
+ 0x10839, 0x1083B,
+ 0x1083D, 0x1083E,
+ 0x10856, 0x10856,
+ 0x10860, 0x108FF,
+ 0x1091C, 0x1091E,
+ 0x1093A, 0x1093E,
+ 0x10940, 0x109FF,
+ 0x10A04, 0x10A04,
+ 0x10A07, 0x10A0B,
+ 0x10A14, 0x10A14,
+ 0x10A18, 0x10A18,
+ 0x10A34, 0x10A37,
+ 0x10A3B, 0x10A3E,
+ 0x10A48, 0x10A4F,
+ 0x10A59, 0x10A5F,
+ 0x10A80, 0x10AFF,
+ 0x10B36, 0x10B38,
+ 0x10B56, 0x10B57,
+ 0x10B73, 0x10B77,
+ 0x10B80, 0x10BFF,
+ 0x10C49, 0x10E5F,
+ 0x10E7F, 0x10FFF,
+ 0x1104E, 0x11051,
+ 0x11070, 0x1107F,
+ 0x110C2, 0x11FFF,
+ 0x1236F, 0x123FF,
+ 0x12463, 0x1246F,
+ 0x12474, 0x12FFF,
+ 0x1342F, 0x167FF,
+ 0x16A39, 0x1AFFF,
+ 0x1B002, 0x1CFFF,
+ 0x1D0F6, 0x1D0FF,
+ 0x1D127, 0x1D128,
+ 0x1D1DE, 0x1D1FF,
+ 0x1D246, 0x1D2FF,
+ 0x1D357, 0x1D35F,
+ 0x1D372, 0x1D3FF,
+ 0x1D455, 0x1D455,
+ 0x1D49D, 0x1D49D,
+ 0x1D4A0, 0x1D4A1,
+ 0x1D4A3, 0x1D4A4,
+ 0x1D4A7, 0x1D4A8,
+ 0x1D4AD, 0x1D4AD,
+ 0x1D4BA, 0x1D4BA,
+ 0x1D4BC, 0x1D4BC,
+ 0x1D4C4, 0x1D4C4,
+ 0x1D506, 0x1D506,
+ 0x1D50B, 0x1D50C,
+ 0x1D515, 0x1D515,
+ 0x1D51D, 0x1D51D,
+ 0x1D53A, 0x1D53A,
+ 0x1D53F, 0x1D53F,
+ 0x1D545, 0x1D545,
+ 0x1D547, 0x1D549,
+ 0x1D551, 0x1D551,
+ 0x1D6A6, 0x1D6A7,
+ 0x1D7CC, 0x1D7CD,
+ 0x1D800, 0x1E7FF,
+ 0x1E800, 0x1EFFF,
+ 0x1F02C, 0x1F02F,
+ 0x1F094, 0x1F09F,
+ 0x1F0AF, 0x1F0B0,
+ 0x1F0BF, 0x1F0C0,
+ 0x1F0D0, 0x1F0D0,
+ 0x1F0E0, 0x1F0FF,
+ 0x1F10B, 0x1F10F,
+ 0x1F12F, 0x1F12F,
+ 0x1F16A, 0x1F16F,
+ 0x1F19B, 0x1F1E5,
+ 0x1F203, 0x1F20F,
+ 0x1F23B, 0x1F23F,
+ 0x1F249, 0x1F24F,
+ 0x1F252, 0x1F2FF,
+ 0x1F321, 0x1F32F,
+ 0x1F336, 0x1F336,
+ 0x1F37D, 0x1F37F,
+ 0x1F394, 0x1F39F,
+ 0x1F3C5, 0x1F3C5,
+ 0x1F3CB, 0x1F3DF,
+ 0x1F3F1, 0x1F3FF,
+ 0x1F43F, 0x1F43F,
+ 0x1F441, 0x1F441,
+ 0x1F4F8, 0x1F4F8,
+ 0x1F4FD, 0x1F4FF,
+ 0x1F53E, 0x1F54F,
+ 0x1F568, 0x1F5FA,
+ 0x1F600, 0x1F600,
+ 0x1F611, 0x1F611,
+ 0x1F615, 0x1F615,
+ 0x1F617, 0x1F617,
+ 0x1F619, 0x1F619,
+ 0x1F61B, 0x1F61B,
+ 0x1F61F, 0x1F61F,
+ 0x1F626, 0x1F627,
+ 0x1F62C, 0x1F62C,
+ 0x1F62E, 0x1F62F,
+ 0x1F634, 0x1F634,
+ 0x1F641, 0x1F644,
+ 0x1F650, 0x1F67F,
+ 0x1F6C6, 0x1F6FF,
+ 0x1F774, 0x1FFFD,
+ 0x2A6D7, 0x2A6FF,
+ 0x2B735, 0x2B73F,
+ 0x2B81E, 0x2F7FF,
+ 0x2FA1E, 0x2FFFD,
+ 0x30000, 0x3FFFD,
+ 0x40000, 0x4FFFD,
+ 0x50000, 0x5FFFD,
+ 0x60000, 0x6FFFD,
+ 0x70000, 0x7FFFD,
+ 0x80000, 0x8FFFD,
+ 0x90000, 0x9FFFD,
+ 0xA0000, 0xAFFFD,
+ 0xB0000, 0xBFFFD,
+ 0xC0000, 0xCFFFD,
+ 0xD0000, 0xDFFFD,
+ 0xE0000, 0xE0000,
+ 0xE0002, 0xE001F,
+ 0xE0080, 0xE00FF,
+ 0xE01F0, 0xE0FFF,
+ 0xE1000, 0xEFFFD
+
+ }));
+
+ CATEGORIES.put("Co", pred(new int[]{ //added by hand
+ 0xE000, 0xF8FF,
+ 0xF0000, 0xFFFFD,
+ 0x100000, 0x10FFFD
+ }));
+
+
+ CATEGORIES.put("Ll", pred(new int[]{
+ 0x61, 0x7A,
+ 0xAA, 0xAA,
+ 0xB5, 0xB5,
+ 0xBA, 0xBA,
+ 0xDF, 0xF6,
+ 0xF8, 0xFF,
+ 0x101, 0x101,
+ 0x103, 0x103,
+ 0x105, 0x105,
+ 0x107, 0x107,
+ 0x109, 0x109,
+ 0x10B, 0x10B,
+ 0x10D, 0x10D,
+ 0x10F, 0x10F,
+ 0x111, 0x111,
+ 0x113, 0x113,
+ 0x115, 0x115,
+ 0x117, 0x117,
+ 0x119, 0x119,
+ 0x11B, 0x11B,
+ 0x11D, 0x11D,
+ 0x11F, 0x11F,
+ 0x121, 0x121,
+ 0x123, 0x123,
+ 0x125, 0x125,
+ 0x127, 0x127,
+ 0x129, 0x129,
+ 0x12B, 0x12B,
+ 0x12D, 0x12D,
+ 0x12F, 0x12F,
+ 0x131, 0x131,
+ 0x133, 0x133,
+ 0x135, 0x135,
+ 0x137, 0x138,
+ 0x13A, 0x13A,
+ 0x13C, 0x13C,
+ 0x13E, 0x13E,
+ 0x140, 0x140,
+ 0x142, 0x142,
+ 0x144, 0x144,
+ 0x146, 0x146,
+ 0x148, 0x149,
+ 0x14B, 0x14B,
+ 0x14D, 0x14D,
+ 0x14F, 0x14F,
+ 0x151, 0x151,
+ 0x153, 0x153,
+ 0x155, 0x155,
+ 0x157, 0x157,
+ 0x159, 0x159,
+ 0x15B, 0x15B,
+ 0x15D, 0x15D,
+ 0x15F, 0x15F,
+ 0x161, 0x161,
+ 0x163, 0x163,
+ 0x165, 0x165,
+ 0x167, 0x167,
+ 0x169, 0x169,
+ 0x16B, 0x16B,
+ 0x16D, 0x16D,
+ 0x16F, 0x16F,
+ 0x171, 0x171,
+ 0x173, 0x173,
+ 0x175, 0x175,
+ 0x177, 0x177,
+ 0x17A, 0x17A,
+ 0x17C, 0x17C,
+ 0x17E, 0x180,
+ 0x183, 0x183,
+ 0x185, 0x185,
+ 0x188, 0x188,
+ 0x18C, 0x18D,
+ 0x192, 0x192,
+ 0x195, 0x195,
+ 0x199, 0x19B,
+ 0x19E, 0x19E,
+ 0x1A1, 0x1A1,
+ 0x1A3, 0x1A3,
+ 0x1A5, 0x1A5,
+ 0x1A8, 0x1A8,
+ 0x1AA, 0x1AB,
+ 0x1AD, 0x1AD,
+ 0x1B0, 0x1B0,
+ 0x1B4, 0x1B4,
+ 0x1B6, 0x1B6,
+ 0x1B9, 0x1BA,
+ 0x1BD, 0x1BF,
+ 0x1C6, 0x1C6,
+ 0x1C9, 0x1C9,
+ 0x1CC, 0x1CC,
+ 0x1CE, 0x1CE,
+ 0x1D0, 0x1D0,
+ 0x1D2, 0x1D2,
+ 0x1D4, 0x1D4,
+ 0x1D6, 0x1D6,
+ 0x1D8, 0x1D8,
+ 0x1DA, 0x1DA,
+ 0x1DC, 0x1DD,
+ 0x1DF, 0x1DF,
+ 0x1E1, 0x1E1,
+ 0x1E3, 0x1E3,
+ 0x1E5, 0x1E5,
+ 0x1E7, 0x1E7,
+ 0x1E9, 0x1E9,
+ 0x1EB, 0x1EB,
+ 0x1ED, 0x1ED,
+ 0x1EF, 0x1F0,
+ 0x1F3, 0x1F3,
+ 0x1F5, 0x1F5,
+ 0x1F9, 0x1F9,
+ 0x1FB, 0x1FB,
+ 0x1FD, 0x1FD,
+ 0x1FF, 0x1FF,
+ 0x201, 0x201,
+ 0x203, 0x203,
+ 0x205, 0x205,
+ 0x207, 0x207,
+ 0x209, 0x209,
+ 0x20B, 0x20B,
+ 0x20D, 0x20D,
+ 0x20F, 0x20F,
+ 0x211, 0x211,
+ 0x213, 0x213,
+ 0x215, 0x215,
+ 0x217, 0x217,
+ 0x219, 0x219,
+ 0x21B, 0x21B,
+ 0x21D, 0x21D,
+ 0x21F, 0x21F,
+ 0x221, 0x221,
+ 0x223, 0x223,
+ 0x225, 0x225,
+ 0x227, 0x227,
+ 0x229, 0x229,
+ 0x22B, 0x22B,
+ 0x22D, 0x22D,
+ 0x22F, 0x22F,
+ 0x231, 0x231,
+ 0x233, 0x239,
+ 0x23C, 0x23C,
+ 0x23F, 0x240,
+ 0x242, 0x242,
+ 0x247, 0x247,
+ 0x249, 0x249,
+ 0x24B, 0x24B,
+ 0x24D, 0x24D,
+ 0x24F, 0x293,
+ 0x295, 0x2AF,
+ 0x371, 0x371,
+ 0x373, 0x373,
+ 0x377, 0x377,
+ 0x37B, 0x37D,
+ 0x390, 0x390,
+ 0x3AC, 0x3CE,
+ 0x3D0, 0x3D1,
+ 0x3D5, 0x3D7,
+ 0x3D9, 0x3D9,
+ 0x3DB, 0x3DB,
+ 0x3DD, 0x3DD,
+ 0x3DF, 0x3DF,
+ 0x3E1, 0x3E1,
+ 0x3E3, 0x3E3,
+ 0x3E5, 0x3E5,
+ 0x3E7, 0x3E7,
+ 0x3E9, 0x3E9,
+ 0x3EB, 0x3EB,
+ 0x3ED, 0x3ED,
+ 0x3EF, 0x3F3,
+ 0x3F5, 0x3F5,
+ 0x3F8, 0x3F8,
+ 0x3FB, 0x3FC,
+ 0x430, 0x45F,
+ 0x461, 0x461,
+ 0x463, 0x463,
+ 0x465, 0x465,
+ 0x467, 0x467,
+ 0x469, 0x469,
+ 0x46B, 0x46B,
+ 0x46D, 0x46D,
+ 0x46F, 0x46F,
+ 0x471, 0x471,
+ 0x473, 0x473,
+ 0x475, 0x475,
+ 0x477, 0x477,
+ 0x479, 0x479,
+ 0x47B, 0x47B,
+ 0x47D, 0x47D,
+ 0x47F, 0x47F,
+ 0x481, 0x481,
+ 0x48B, 0x48B,
+ 0x48D, 0x48D,
+ 0x48F, 0x48F,
+ 0x491, 0x491,
+ 0x493, 0x493,
+ 0x495, 0x495,
+ 0x497, 0x497,
+ 0x499, 0x499,
+ 0x49B, 0x49B,
+ 0x49D, 0x49D,
+ 0x49F, 0x49F,
+ 0x4A1, 0x4A1,
+ 0x4A3, 0x4A3,
+ 0x4A5, 0x4A5,
+ 0x4A7, 0x4A7,
+ 0x4A9, 0x4A9,
+ 0x4AB, 0x4AB,
+ 0x4AD, 0x4AD,
+ 0x4AF, 0x4AF,
+ 0x4B1, 0x4B1,
+ 0x4B3, 0x4B3,
+ 0x4B5, 0x4B5,
+ 0x4B7, 0x4B7,
+ 0x4B9, 0x4B9,
+ 0x4BB, 0x4BB,
+ 0x4BD, 0x4BD,
+ 0x4BF, 0x4BF,
+ 0x4C2, 0x4C2,
+ 0x4C4, 0x4C4,
+ 0x4C6, 0x4C6,
+ 0x4C8, 0x4C8,
+ 0x4CA, 0x4CA,
+ 0x4CC, 0x4CC,
+ 0x4CE, 0x4CF,
+ 0x4D1, 0x4D1,
+ 0x4D3, 0x4D3,
+ 0x4D5, 0x4D5,
+ 0x4D7, 0x4D7,
+ 0x4D9, 0x4D9,
+ 0x4DB, 0x4DB,
+ 0x4DD, 0x4DD,
+ 0x4DF, 0x4DF,
+ 0x4E1, 0x4E1,
+ 0x4E3, 0x4E3,
+ 0x4E5, 0x4E5,
+ 0x4E7, 0x4E7,
+ 0x4E9, 0x4E9,
+ 0x4EB, 0x4EB,
+ 0x4ED, 0x4ED,
+ 0x4EF, 0x4EF,
+ 0x4F1, 0x4F1,
+ 0x4F3, 0x4F3,
+ 0x4F5, 0x4F5,
+ 0x4F7, 0x4F7,
+ 0x4F9, 0x4F9,
+ 0x4FB, 0x4FB,
+ 0x4FD, 0x4FD,
+ 0x4FF, 0x4FF,
+ 0x501, 0x501,
+ 0x503, 0x503,
+ 0x505, 0x505,
+ 0x507, 0x507,
+ 0x509, 0x509,
+ 0x50B, 0x50B,
+ 0x50D, 0x50D,
+ 0x50F, 0x50F,
+ 0x511, 0x511,
+ 0x513, 0x513,
+ 0x515, 0x515,
+ 0x517, 0x517,
+ 0x519, 0x519,
+ 0x51B, 0x51B,
+ 0x51D, 0x51D,
+ 0x51F, 0x51F,
+ 0x521, 0x521,
+ 0x523, 0x523,
+ 0x525, 0x525,
+ 0x527, 0x527,
+ 0x561, 0x587,
+ 0x1D00, 0x1D2B,
+ 0x1D62, 0x1D77,
+ 0x1D79, 0x1D9A,
+ 0x1E01, 0x1E01,
+ 0x1E03, 0x1E03,
+ 0x1E05, 0x1E05,
+ 0x1E07, 0x1E07,
+ 0x1E09, 0x1E09,
+ 0x1E0B, 0x1E0B,
+ 0x1E0D, 0x1E0D,
+ 0x1E0F, 0x1E0F,
+ 0x1E11, 0x1E11,
+ 0x1E13, 0x1E13,
+ 0x1E15, 0x1E15,
+ 0x1E17, 0x1E17,
+ 0x1E19, 0x1E19,
+ 0x1E1B, 0x1E1B,
+ 0x1E1D, 0x1E1D,
+ 0x1E1F, 0x1E1F,
+ 0x1E21, 0x1E21,
+ 0x1E23, 0x1E23,
+ 0x1E25, 0x1E25,
+ 0x1E27, 0x1E27,
+ 0x1E29, 0x1E29,
+ 0x1E2B, 0x1E2B,
+ 0x1E2D, 0x1E2D,
+ 0x1E2F, 0x1E2F,
+ 0x1E31, 0x1E31,
+ 0x1E33, 0x1E33,
+ 0x1E35, 0x1E35,
+ 0x1E37, 0x1E37,
+ 0x1E39, 0x1E39,
+ 0x1E3B, 0x1E3B,
+ 0x1E3D, 0x1E3D,
+ 0x1E3F, 0x1E3F,
+ 0x1E41, 0x1E41,
+ 0x1E43, 0x1E43,
+ 0x1E45, 0x1E45,
+ 0x1E47, 0x1E47,
+ 0x1E49, 0x1E49,
+ 0x1E4B, 0x1E4B,
+ 0x1E4D, 0x1E4D,
+ 0x1E4F, 0x1E4F,
+ 0x1E51, 0x1E51,
+ 0x1E53, 0x1E53,
+ 0x1E55, 0x1E55,
+ 0x1E57, 0x1E57,
+ 0x1E59, 0x1E59,
+ 0x1E5B, 0x1E5B,
+ 0x1E5D, 0x1E5D,
+ 0x1E5F, 0x1E5F,
+ 0x1E61, 0x1E61,
+ 0x1E63, 0x1E63,
+ 0x1E65, 0x1E65,
+ 0x1E67, 0x1E67,
+ 0x1E69, 0x1E69,
+ 0x1E6B, 0x1E6B,
+ 0x1E6D, 0x1E6D,
+ 0x1E6F, 0x1E6F,
+ 0x1E71, 0x1E71,
+ 0x1E73, 0x1E73,
+ 0x1E75, 0x1E75,
+ 0x1E77, 0x1E77,
+ 0x1E79, 0x1E79,
+ 0x1E7B, 0x1E7B,
+ 0x1E7D, 0x1E7D,
+ 0x1E7F, 0x1E7F,
+ 0x1E81, 0x1E81,
+ 0x1E83, 0x1E83,
+ 0x1E85, 0x1E85,
+ 0x1E87, 0x1E87,
+ 0x1E89, 0x1E89,
+ 0x1E8B, 0x1E8B,
+ 0x1E8D, 0x1E8D,
+ 0x1E8F, 0x1E8F,
+ 0x1E91, 0x1E91,
+ 0x1E93, 0x1E93,
+ 0x1E95, 0x1E9D,
+ 0x1E9F, 0x1E9F,
+ 0x1EA1, 0x1EA1,
+ 0x1EA3, 0x1EA3,
+ 0x1EA5, 0x1EA5,
+ 0x1EA7, 0x1EA7,
+ 0x1EA9, 0x1EA9,
+ 0x1EAB, 0x1EAB,
+ 0x1EAD, 0x1EAD,
+ 0x1EAF, 0x1EAF,
+ 0x1EB1, 0x1EB1,
+ 0x1EB3, 0x1EB3,
+ 0x1EB5, 0x1EB5,
+ 0x1EB7, 0x1EB7,
+ 0x1EB9, 0x1EB9,
+ 0x1EBB, 0x1EBB,
+ 0x1EBD, 0x1EBD,
+ 0x1EBF, 0x1EBF,
+ 0x1EC1, 0x1EC1,
+ 0x1EC3, 0x1EC3,
+ 0x1EC5, 0x1EC5,
+ 0x1EC7, 0x1EC7,
+ 0x1EC9, 0x1EC9,
+ 0x1ECB, 0x1ECB,
+ 0x1ECD, 0x1ECD,
+ 0x1ECF, 0x1ECF,
+ 0x1ED1, 0x1ED1,
+ 0x1ED3, 0x1ED3,
+ 0x1ED5, 0x1ED5,
+ 0x1ED7, 0x1ED7,
+ 0x1ED9, 0x1ED9,
+ 0x1EDB, 0x1EDB,
+ 0x1EDD, 0x1EDD,
+ 0x1EDF, 0x1EDF,
+ 0x1EE1, 0x1EE1,
+ 0x1EE3, 0x1EE3,
+ 0x1EE5, 0x1EE5,
+ 0x1EE7, 0x1EE7,
+ 0x1EE9, 0x1EE9,
+ 0x1EEB, 0x1EEB,
+ 0x1EED, 0x1EED,
+ 0x1EEF, 0x1EEF,
+ 0x1EF1, 0x1EF1,
+ 0x1EF3, 0x1EF3,
+ 0x1EF5, 0x1EF5,
+ 0x1EF7, 0x1EF7,
+ 0x1EF9, 0x1EF9,
+ 0x1EFB, 0x1EFB,
+ 0x1EFD, 0x1EFD,
+ 0x1EFF, 0x1F07,
+ 0x1F10, 0x1F15,
+ 0x1F20, 0x1F27,
+ 0x1F30, 0x1F37,
+ 0x1F40, 0x1F45,
+ 0x1F50, 0x1F57,
+ 0x1F60, 0x1F67,
+ 0x1F70, 0x1F7D,
+ 0x1F80, 0x1F87,
+ 0x1F90, 0x1F97,
+ 0x1FA0, 0x1FA7,
+ 0x1FB0, 0x1FB4,
+ 0x1FB6, 0x1FB7,
+ 0x1FBE, 0x1FBE,
+ 0x1FC2, 0x1FC4,
+ 0x1FC6, 0x1FC7,
+ 0x1FD0, 0x1FD3,
+ 0x1FD6, 0x1FD7,
+ 0x1FE0, 0x1FE7,
+ 0x1FF2, 0x1FF4,
+ 0x1FF6, 0x1FF7,
+ 0x210A, 0x210A,
+ 0x210E, 0x210F,
+ 0x2113, 0x2113,
+ 0x212F, 0x212F,
+ 0x2134, 0x2134,
+ 0x2139, 0x2139,
+ 0x213C, 0x213D,
+ 0x2146, 0x2149,
+ 0x214E, 0x214E,
+ 0x2184, 0x2184,
+ 0x2C30, 0x2C5E,
+ 0x2C61, 0x2C61,
+ 0x2C65, 0x2C66,
+ 0x2C68, 0x2C68,
+ 0x2C6A, 0x2C6A,
+ 0x2C6C, 0x2C6C,
+ 0x2C71, 0x2C71,
+ 0x2C73, 0x2C74,
+ 0x2C76, 0x2C7C,
+ 0x2C81, 0x2C81,
+ 0x2C83, 0x2C83,
+ 0x2C85, 0x2C85,
+ 0x2C87, 0x2C87,
+ 0x2C89, 0x2C89,
+ 0x2C8B, 0x2C8B,
+ 0x2C8D, 0x2C8D,
+ 0x2C8F, 0x2C8F,
+ 0x2C91, 0x2C91,
+ 0x2C93, 0x2C93,
+ 0x2C95, 0x2C95,
+ 0x2C97, 0x2C97,
+ 0x2C99, 0x2C99,
+ 0x2C9B, 0x2C9B,
+ 0x2C9D, 0x2C9D,
+ 0x2C9F, 0x2C9F,
+ 0x2CA1, 0x2CA1,
+ 0x2CA3, 0x2CA3,
+ 0x2CA5, 0x2CA5,
+ 0x2CA7, 0x2CA7,
+ 0x2CA9, 0x2CA9,
+ 0x2CAB, 0x2CAB,
+ 0x2CAD, 0x2CAD,
+ 0x2CAF, 0x2CAF,
+ 0x2CB1, 0x2CB1,
+ 0x2CB3, 0x2CB3,
+ 0x2CB5, 0x2CB5,
+ 0x2CB7, 0x2CB7,
+ 0x2CB9, 0x2CB9,
+ 0x2CBB, 0x2CBB,
+ 0x2CBD, 0x2CBD,
+ 0x2CBF, 0x2CBF,
+ 0x2CC1, 0x2CC1,
+ 0x2CC3, 0x2CC3,
+ 0x2CC5, 0x2CC5,
+ 0x2CC7, 0x2CC7,
+ 0x2CC9, 0x2CC9,
+ 0x2CCB, 0x2CCB,
+ 0x2CCD, 0x2CCD,
+ 0x2CCF, 0x2CCF,
+ 0x2CD1, 0x2CD1,
+ 0x2CD3, 0x2CD3,
+ 0x2CD5, 0x2CD5,
+ 0x2CD7, 0x2CD7,
+ 0x2CD9, 0x2CD9,
+ 0x2CDB, 0x2CDB,
+ 0x2CDD, 0x2CDD,
+ 0x2CDF, 0x2CDF,
+ 0x2CE1, 0x2CE1,
+ 0x2CE3, 0x2CE4,
+ 0x2CEC, 0x2CEC,
+ 0x2CEE, 0x2CEE,
+ 0x2D00, 0x2D25,
+ 0xA641, 0xA641,
+ 0xA643, 0xA643,
+ 0xA645, 0xA645,
+ 0xA647, 0xA647,
+ 0xA649, 0xA649,
+ 0xA64B, 0xA64B,
+ 0xA64D, 0xA64D,
+ 0xA64F, 0xA64F,
+ 0xA651, 0xA651,
+ 0xA653, 0xA653,
+ 0xA655, 0xA655,
+ 0xA657, 0xA657,
+ 0xA659, 0xA659,
+ 0xA65B, 0xA65B,
+ 0xA65D, 0xA65D,
+ 0xA65F, 0xA65F,
+ 0xA661, 0xA661,
+ 0xA663, 0xA663,
+ 0xA665, 0xA665,
+ 0xA667, 0xA667,
+ 0xA669, 0xA669,
+ 0xA66B, 0xA66B,
+ 0xA66D, 0xA66D,
+ 0xA681, 0xA681,
+ 0xA683, 0xA683,
+ 0xA685, 0xA685,
+ 0xA687, 0xA687,
+ 0xA689, 0xA689,
+ 0xA68B, 0xA68B,
+ 0xA68D, 0xA68D,
+ 0xA68F, 0xA68F,
+ 0xA691, 0xA691,
+ 0xA693, 0xA693,
+ 0xA695, 0xA695,
+ 0xA697, 0xA697,
+ 0xA723, 0xA723,
+ 0xA725, 0xA725,
+ 0xA727, 0xA727,
+ 0xA729, 0xA729,
+ 0xA72B, 0xA72B,
+ 0xA72D, 0xA72D,
+ 0xA72F, 0xA731,
+ 0xA733, 0xA733,
+ 0xA735, 0xA735,
+ 0xA737, 0xA737,
+ 0xA739, 0xA739,
+ 0xA73B, 0xA73B,
+ 0xA73D, 0xA73D,
+ 0xA73F, 0xA73F,
+ 0xA741, 0xA741,
+ 0xA743, 0xA743,
+ 0xA745, 0xA745,
+ 0xA747, 0xA747,
+ 0xA749, 0xA749,
+ 0xA74B, 0xA74B,
+ 0xA74D, 0xA74D,
+ 0xA74F, 0xA74F,
+ 0xA751, 0xA751,
+ 0xA753, 0xA753,
+ 0xA755, 0xA755,
+ 0xA757, 0xA757,
+ 0xA759, 0xA759,
+ 0xA75B, 0xA75B,
+ 0xA75D, 0xA75D,
+ 0xA75F, 0xA75F,
+ 0xA761, 0xA761,
+ 0xA763, 0xA763,
+ 0xA765, 0xA765,
+ 0xA767, 0xA767,
+ 0xA769, 0xA769,
+ 0xA76B, 0xA76B,
+ 0xA76D, 0xA76D,
+ 0xA76F, 0xA76F,
+ 0xA771, 0xA778,
+ 0xA77A, 0xA77A,
+ 0xA77C, 0xA77C,
+ 0xA77F, 0xA77F,
+ 0xA781, 0xA781,
+ 0xA783, 0xA783,
+ 0xA785, 0xA785,
+ 0xA787, 0xA787,
+ 0xA78C, 0xA78C,
+ 0xA78E, 0xA78E,
+ 0xA791, 0xA791,
+ 0xA7A1, 0xA7A1,
+ 0xA7A3, 0xA7A3,
+ 0xA7A5, 0xA7A5,
+ 0xA7A7, 0xA7A7,
+ 0xA7A9, 0xA7A9,
+ 0xA7FA, 0xA7FA,
+ 0xFB00, 0xFB06,
+ 0xFB13, 0xFB17,
+ 0xFF41, 0xFF5A,
+ 0x10428, 0x1044F,
+ 0x1D41A, 0x1D433,
+ 0x1D44E, 0x1D454,
+ 0x1D456, 0x1D467,
+ 0x1D482, 0x1D49B,
+ 0x1D4B6, 0x1D4B9,
+ 0x1D4BB, 0x1D4BB,
+ 0x1D4BD, 0x1D4C3,
+ 0x1D4C5, 0x1D4CF,
+ 0x1D4EA, 0x1D503,
+ 0x1D51E, 0x1D537,
+ 0x1D552, 0x1D56B,
+ 0x1D586, 0x1D59F,
+ 0x1D5BA, 0x1D5D3,
+ 0x1D5EE, 0x1D607,
+ 0x1D622, 0x1D63B,
+ 0x1D656, 0x1D66F,
+ 0x1D68A, 0x1D6A5,
+ 0x1D6C2, 0x1D6DA,
+ 0x1D6DC, 0x1D6E1,
+ 0x1D6FC, 0x1D714,
+ 0x1D716, 0x1D71B,
+ 0x1D736, 0x1D74E,
+ 0x1D750, 0x1D755,
+ 0x1D770, 0x1D788,
+ 0x1D78A, 0x1D78F,
+ 0x1D7AA, 0x1D7C2,
+ 0x1D7C4, 0x1D7C9,
+ 0x1D7CB, 0x1D7CB
+ }));
+
+
+ CATEGORIES.put("Lm", pred(new int[]{
+ 0x2B0, 0x2C1,
+ 0x2C6, 0x2D1,
+ 0x2E0, 0x2E4,
+ 0x2EC, 0x2EC,
+ 0x2EE, 0x2EE,
+ 0x374, 0x374,
+ 0x37A, 0x37A,
+ 0x559, 0x559,
+ 0x640, 0x640,
+ 0x6E5, 0x6E6,
+ 0x7F4, 0x7F5,
+ 0x7FA, 0x7FA,
+ 0x81A, 0x81A,
+ 0x824, 0x824,
+ 0x828, 0x828,
+ 0x971, 0x971,
+ 0xE46, 0xE46,
+ 0xEC6, 0xEC6,
+ 0x10FC, 0x10FC,
+ 0x17D7, 0x17D7,
+ 0x1843, 0x1843,
+ 0x1AA7, 0x1AA7,
+ 0x1C78, 0x1C7D,
+ 0x1D2C, 0x1D61,
+ 0x1D78, 0x1D78,
+ 0x1D9B, 0x1DBF,
+ 0x2071, 0x2071,
+ 0x207F, 0x207F,
+ 0x2090, 0x209C,
+ 0x2C7D, 0x2C7D,
+ 0x2D6F, 0x2D6F,
+ 0x2E2F, 0x2E2F,
+ 0x3005, 0x3005,
+ 0x3031, 0x3035,
+ 0x303B, 0x303B,
+ 0x309D, 0x309E,
+ 0x30FC, 0x30FE,
+ 0xA015, 0xA015,
+ 0xA4F8, 0xA4FD,
+ 0xA60C, 0xA60C,
+ 0xA67F, 0xA67F,
+ 0xA717, 0xA71F,
+ 0xA770, 0xA770,
+ 0xA788, 0xA788,
+ 0xA9CF, 0xA9CF,
+ 0xAA70, 0xAA70,
+ 0xAADD, 0xAADD,
+ 0xFF70, 0xFF70,
+ 0xFF9E, 0xFF9F
+ }));
+
+
+ CATEGORIES.put("Lo", pred(new int[]{
+ 0x1BB, 0x1BB,
+ 0x1C0, 0x1C3,
+ 0x294, 0x294,
+ 0x5D0, 0x5EA,
+ 0x5F0, 0x5F2,
+ 0x620, 0x63F,
+ 0x641, 0x64A,
+ 0x66E, 0x66F,
+ 0x671, 0x6D3,
+ 0x6D5, 0x6D5,
+ 0x6EE, 0x6EF,
+ 0x6FA, 0x6FC,
+ 0x6FF, 0x6FF,
+ 0x710, 0x710,
+ 0x712, 0x72F,
+ 0x74D, 0x7A5,
+ 0x7B1, 0x7B1,
+ 0x7CA, 0x7EA,
+ 0x800, 0x815,
+ 0x840, 0x858,
+ 0x904, 0x939,
+ 0x93D, 0x93D,
+ 0x950, 0x950,
+ 0x958, 0x961,
+ 0x972, 0x977,
+ 0x979, 0x97F,
+ 0x985, 0x98C,
+ 0x98F, 0x990,
+ 0x993, 0x9A8,
+ 0x9AA, 0x9B0,
+ 0x9B2, 0x9B2,
+ 0x9B6, 0x9B9,
+ 0x9BD, 0x9BD,
+ 0x9CE, 0x9CE,
+ 0x9DC, 0x9DD,
+ 0x9DF, 0x9E1,
+ 0x9F0, 0x9F1,
+ 0xA05, 0xA0A,
+ 0xA0F, 0xA10,
+ 0xA13, 0xA28,
+ 0xA2A, 0xA30,
+ 0xA32, 0xA33,
+ 0xA35, 0xA36,
+ 0xA38, 0xA39,
+ 0xA59, 0xA5C,
+ 0xA5E, 0xA5E,
+ 0xA72, 0xA74,
+ 0xA85, 0xA8D,
+ 0xA8F, 0xA91,
+ 0xA93, 0xAA8,
+ 0xAAA, 0xAB0,
+ 0xAB2, 0xAB3,
+ 0xAB5, 0xAB9,
+ 0xABD, 0xABD,
+ 0xAD0, 0xAD0,
+ 0xAE0, 0xAE1,
+ 0xB05, 0xB0C,
+ 0xB0F, 0xB10,
+ 0xB13, 0xB28,
+ 0xB2A, 0xB30,
+ 0xB32, 0xB33,
+ 0xB35, 0xB39,
+ 0xB3D, 0xB3D,
+ 0xB5C, 0xB5D,
+ 0xB5F, 0xB61,
+ 0xB71, 0xB71,
+ 0xB83, 0xB83,
+ 0xB85, 0xB8A,
+ 0xB8E, 0xB90,
+ 0xB92, 0xB95,
+ 0xB99, 0xB9A,
+ 0xB9C, 0xB9C,
+ 0xB9E, 0xB9F,
+ 0xBA3, 0xBA4,
+ 0xBA8, 0xBAA,
+ 0xBAE, 0xBB9,
+ 0xBD0, 0xBD0,
+ 0xC05, 0xC0C,
+ 0xC0E, 0xC10,
+ 0xC12, 0xC28,
+ 0xC2A, 0xC33,
+ 0xC35, 0xC39,
+ 0xC3D, 0xC3D,
+ 0xC58, 0xC59,
+ 0xC60, 0xC61,
+ 0xC85, 0xC8C,
+ 0xC8E, 0xC90,
+ 0xC92, 0xCA8,
+ 0xCAA, 0xCB3,
+ 0xCB5, 0xCB9,
+ 0xCBD, 0xCBD,
+ 0xCDE, 0xCDE,
+ 0xCE0, 0xCE1,
+ 0xCF1, 0xCF2,
+ 0xD05, 0xD0C,
+ 0xD0E, 0xD10,
+ 0xD12, 0xD3A,
+ 0xD3D, 0xD3D,
+ 0xD4E, 0xD4E,
+ 0xD60, 0xD61,
+ 0xD7A, 0xD7F,
+ 0xD85, 0xD96,
+ 0xD9A, 0xDB1,
+ 0xDB3, 0xDBB,
+ 0xDBD, 0xDBD,
+ 0xDC0, 0xDC6,
+ 0xE01, 0xE30,
+ 0xE32, 0xE33,
+ 0xE40, 0xE45,
+ 0xE81, 0xE82,
+ 0xE84, 0xE84,
+ 0xE87, 0xE88,
+ 0xE8A, 0xE8A,
+ 0xE8D, 0xE8D,
+ 0xE94, 0xE97,
+ 0xE99, 0xE9F,
+ 0xEA1, 0xEA3,
+ 0xEA5, 0xEA5,
+ 0xEA7, 0xEA7,
+ 0xEAA, 0xEAB,
+ 0xEAD, 0xEB0,
+ 0xEB2, 0xEB3,
+ 0xEBD, 0xEBD,
+ 0xEC0, 0xEC4,
+ 0xEDC, 0xEDD,
+ 0xF00, 0xF00,
+ 0xF40, 0xF47,
+ 0xF49, 0xF6C,
+ 0xF88, 0xF8C,
+ 0x1000, 0x102A,
+ 0x103F, 0x103F,
+ 0x1050, 0x1055,
+ 0x105A, 0x105D,
+ 0x1061, 0x1061,
+ 0x1065, 0x1066,
+ 0x106E, 0x1070,
+ 0x1075, 0x1081,
+ 0x108E, 0x108E,
+ 0x10D0, 0x10FA,
+ 0x1100, 0x1248,
+ 0x124A, 0x124D,
+ 0x1250, 0x1256,
+ 0x1258, 0x1258,
+ 0x125A, 0x125D,
+ 0x1260, 0x1288,
+ 0x128A, 0x128D,
+ 0x1290, 0x12B0,
+ 0x12B2, 0x12B5,
+ 0x12B8, 0x12BE,
+ 0x12C0, 0x12C0,
+ 0x12C2, 0x12C5,
+ 0x12C8, 0x12D6,
+ 0x12D8, 0x1310,
+ 0x1312, 0x1315,
+ 0x1318, 0x135A,
+ 0x1380, 0x138F,
+ 0x13A0, 0x13F4,
+ 0x1401, 0x166C,
+ 0x166F, 0x167F,
+ 0x1681, 0x169A,
+ 0x16A0, 0x16EA,
+ 0x1700, 0x170C,
+ 0x170E, 0x1711,
+ 0x1720, 0x1731,
+ 0x1740, 0x1751,
+ 0x1760, 0x176C,
+ 0x176E, 0x1770,
+ 0x1780, 0x17B3,
+ 0x17DC, 0x17DC,
+ 0x1820, 0x1842,
+ 0x1844, 0x1877,
+ 0x1880, 0x18A8,
+ 0x18AA, 0x18AA,
+ 0x18B0, 0x18F5,
+ 0x1900, 0x191C,
+ 0x1950, 0x196D,
+ 0x1970, 0x1974,
+ 0x1980, 0x19AB,
+ 0x19C1, 0x19C7,
+ 0x1A00, 0x1A16,
+ 0x1A20, 0x1A54,
+ 0x1B05, 0x1B33,
+ 0x1B45, 0x1B4B,
+ 0x1B83, 0x1BA0,
+ 0x1BAE, 0x1BAF,
+ 0x1BC0, 0x1BE5,
+ 0x1C00, 0x1C23,
+ 0x1C4D, 0x1C4F,
+ 0x1C5A, 0x1C77,
+ 0x1CE9, 0x1CEC,
+ 0x1CEE, 0x1CF1,
+ 0x2135, 0x2138,
+ 0x2D30, 0x2D65,
+ 0x2D80, 0x2D96,
+ 0x2DA0, 0x2DA6,
+ 0x2DA8, 0x2DAE,
+ 0x2DB0, 0x2DB6,
+ 0x2DB8, 0x2DBE,
+ 0x2DC0, 0x2DC6,
+ 0x2DC8, 0x2DCE,
+ 0x2DD0, 0x2DD6,
+ 0x2DD8, 0x2DDE,
+ 0x3006, 0x3006,
+ 0x303C, 0x303C,
+ 0x3041, 0x3096,
+ 0x309F, 0x309F,
+ 0x30A1, 0x30FA,
+ 0x30FF, 0x30FF,
+ 0x3105, 0x312D,
+ 0x3131, 0x318E,
+ 0x31A0, 0x31BA,
+ 0x31F0, 0x31FF,
+ 0x3400, 0x4DB5,
+ 0x4E00, 0x9FCB,
+ 0xA000, 0xA014,
+ 0xA016, 0xA48C,
+ 0xA4D0, 0xA4F7,
+ 0xA500, 0xA60B,
+ 0xA610, 0xA61F,
+ 0xA62A, 0xA62B,
+ 0xA66E, 0xA66E,
+ 0xA6A0, 0xA6E5,
+ 0xA7FB, 0xA801,
+ 0xA803, 0xA805,
+ 0xA807, 0xA80A,
+ 0xA80C, 0xA822,
+ 0xA840, 0xA873,
+ 0xA882, 0xA8B3,
+ 0xA8F2, 0xA8F7,
+ 0xA8FB, 0xA8FB,
+ 0xA90A, 0xA925,
+ 0xA930, 0xA946,
+ 0xA960, 0xA97C,
+ 0xA984, 0xA9B2,
+ 0xAA00, 0xAA28,
+ 0xAA40, 0xAA42,
+ 0xAA44, 0xAA4B,
+ 0xAA60, 0xAA6F,
+ 0xAA71, 0xAA76,
+ 0xAA7A, 0xAA7A,
+ 0xAA80, 0xAAAF,
+ 0xAAB1, 0xAAB1,
+ 0xAAB5, 0xAAB6,
+ 0xAAB9, 0xAABD,
+ 0xAAC0, 0xAAC0,
+ 0xAAC2, 0xAAC2,
+ 0xAADB, 0xAADC,
+ 0xAB01, 0xAB06,
+ 0xAB09, 0xAB0E,
+ 0xAB11, 0xAB16,
+ 0xAB20, 0xAB26,
+ 0xAB28, 0xAB2E,
+ 0xABC0, 0xABE2,
+ 0xAC00, 0xD7A3,
+ 0xD7B0, 0xD7C6,
+ 0xD7CB, 0xD7FB,
+ 0xF900, 0xFA2D,
+ 0xFA30, 0xFA6D,
+ 0xFA70, 0xFAD9,
+ 0xFB1D, 0xFB1D,
+ 0xFB1F, 0xFB28,
+ 0xFB2A, 0xFB36,
+ 0xFB38, 0xFB3C,
+ 0xFB3E, 0xFB3E,
+ 0xFB40, 0xFB41,
+ 0xFB43, 0xFB44,
+ 0xFB46, 0xFBB1,
+ 0xFBD3, 0xFD3D,
+ 0xFD50, 0xFD8F,
+ 0xFD92, 0xFDC7,
+ 0xFDF0, 0xFDFB,
+ 0xFE70, 0xFE74,
+ 0xFE76, 0xFEFC,
+ 0xFF66, 0xFF6F,
+ 0xFF71, 0xFF9D,
+ 0xFFA0, 0xFFBE,
+ 0xFFC2, 0xFFC7,
+ 0xFFCA, 0xFFCF,
+ 0xFFD2, 0xFFD7,
+ 0xFFDA, 0xFFDC,
+ 0x10000, 0x1000B,
+ 0x1000D, 0x10026,
+ 0x10028, 0x1003A,
+ 0x1003C, 0x1003D,
+ 0x1003F, 0x1004D,
+ 0x10050, 0x1005D,
+ 0x10080, 0x100FA,
+ 0x10280, 0x1029C,
+ 0x102A0, 0x102D0,
+ 0x10300, 0x1031E,
+ 0x10330, 0x10340,
+ 0x10342, 0x10349,
+ 0x10380, 0x1039D,
+ 0x103A0, 0x103C3,
+ 0x103C8, 0x103CF,
+ 0x10450, 0x1049D,
+ 0x10800, 0x10805,
+ 0x10808, 0x10808,
+ 0x1080A, 0x10835,
+ 0x10837, 0x10838,
+ 0x1083C, 0x1083C,
+ 0x1083F, 0x10855,
+ 0x10900, 0x10915,
+ 0x10920, 0x10939,
+ 0x10A00, 0x10A00,
+ 0x10A10, 0x10A13,
+ 0x10A15, 0x10A17,
+ 0x10A19, 0x10A33,
+ 0x10A60, 0x10A7C,
+ 0x10B00, 0x10B35,
+ 0x10B40, 0x10B55,
+ 0x10B60, 0x10B72,
+ 0x10C00, 0x10C48,
+ 0x11003, 0x11037,
+ 0x11083, 0x110AF,
+ 0x12000, 0x1236E,
+ 0x13000, 0x1342E,
+ 0x16800, 0x16A38,
+ 0x1B000, 0x1B001,
+ 0x20000, 0x2A6D6,
+ 0x2A700, 0x2B734,
+ 0x2B740, 0x2B81D,
+ 0x2F800, 0x2FA1D
+ }));
+
+
+ CATEGORIES.put("Lt", pred(new int[]{
+ 0x1C5, 0x1C5,
+ 0x1C8, 0x1C8,
+ 0x1CB, 0x1CB,
+ 0x1F2, 0x1F2,
+ 0x1F88, 0x1F8F,
+ 0x1F98, 0x1F9F,
+ 0x1FA8, 0x1FAF,
+ 0x1FBC, 0x1FBC,
+ 0x1FCC, 0x1FCC,
+ 0x1FFC, 0x1FFC
+ }));
+
+
+ CATEGORIES.put("Lu", pred(new int[]{
+ 0x41, 0x5A,
+ 0xC0, 0xD6,
+ 0xD8, 0xDE,
+ 0x100, 0x100,
+ 0x102, 0x102,
+ 0x104, 0x104,
+ 0x106, 0x106,
+ 0x108, 0x108,
+ 0x10A, 0x10A,
+ 0x10C, 0x10C,
+ 0x10E, 0x10E,
+ 0x110, 0x110,
+ 0x112, 0x112,
+ 0x114, 0x114,
+ 0x116, 0x116,
+ 0x118, 0x118,
+ 0x11A, 0x11A,
+ 0x11C, 0x11C,
+ 0x11E, 0x11E,
+ 0x120, 0x120,
+ 0x122, 0x122,
+ 0x124, 0x124,
+ 0x126, 0x126,
+ 0x128, 0x128,
+ 0x12A, 0x12A,
+ 0x12C, 0x12C,
+ 0x12E, 0x12E,
+ 0x130, 0x130,
+ 0x132, 0x132,
+ 0x134, 0x134,
+ 0x136, 0x136,
+ 0x139, 0x139,
+ 0x13B, 0x13B,
+ 0x13D, 0x13D,
+ 0x13F, 0x13F,
+ 0x141, 0x141,
+ 0x143, 0x143,
+ 0x145, 0x145,
+ 0x147, 0x147,
+ 0x14A, 0x14A,
+ 0x14C, 0x14C,
+ 0x14E, 0x14E,
+ 0x150, 0x150,
+ 0x152, 0x152,
+ 0x154, 0x154,
+ 0x156, 0x156,
+ 0x158, 0x158,
+ 0x15A, 0x15A,
+ 0x15C, 0x15C,
+ 0x15E, 0x15E,
+ 0x160, 0x160,
+ 0x162, 0x162,
+ 0x164, 0x164,
+ 0x166, 0x166,
+ 0x168, 0x168,
+ 0x16A, 0x16A,
+ 0x16C, 0x16C,
+ 0x16E, 0x16E,
+ 0x170, 0x170,
+ 0x172, 0x172,
+ 0x174, 0x174,
+ 0x176, 0x176,
+ 0x178, 0x179,
+ 0x17B, 0x17B,
+ 0x17D, 0x17D,
+ 0x181, 0x182,
+ 0x184, 0x184,
+ 0x186, 0x187,
+ 0x189, 0x18B,
+ 0x18E, 0x191,
+ 0x193, 0x194,
+ 0x196, 0x198,
+ 0x19C, 0x19D,
+ 0x19F, 0x1A0,
+ 0x1A2, 0x1A2,
+ 0x1A4, 0x1A4,
+ 0x1A6, 0x1A7,
+ 0x1A9, 0x1A9,
+ 0x1AC, 0x1AC,
+ 0x1AE, 0x1AF,
+ 0x1B1, 0x1B3,
+ 0x1B5, 0x1B5,
+ 0x1B7, 0x1B8,
+ 0x1BC, 0x1BC,
+ 0x1C4, 0x1C4,
+ 0x1C7, 0x1C7,
+ 0x1CA, 0x1CA,
+ 0x1CD, 0x1CD,
+ 0x1CF, 0x1CF,
+ 0x1D1, 0x1D1,
+ 0x1D3, 0x1D3,
+ 0x1D5, 0x1D5,
+ 0x1D7, 0x1D7,
+ 0x1D9, 0x1D9,
+ 0x1DB, 0x1DB,
+ 0x1DE, 0x1DE,
+ 0x1E0, 0x1E0,
+ 0x1E2, 0x1E2,
+ 0x1E4, 0x1E4,
+ 0x1E6, 0x1E6,
+ 0x1E8, 0x1E8,
+ 0x1EA, 0x1EA,
+ 0x1EC, 0x1EC,
+ 0x1EE, 0x1EE,
+ 0x1F1, 0x1F1,
+ 0x1F4, 0x1F4,
+ 0x1F6, 0x1F8,
+ 0x1FA, 0x1FA,
+ 0x1FC, 0x1FC,
+ 0x1FE, 0x1FE,
+ 0x200, 0x200,
+ 0x202, 0x202,
+ 0x204, 0x204,
+ 0x206, 0x206,
+ 0x208, 0x208,
+ 0x20A, 0x20A,
+ 0x20C, 0x20C,
+ 0x20E, 0x20E,
+ 0x210, 0x210,
+ 0x212, 0x212,
+ 0x214, 0x214,
+ 0x216, 0x216,
+ 0x218, 0x218,
+ 0x21A, 0x21A,
+ 0x21C, 0x21C,
+ 0x21E, 0x21E,
+ 0x220, 0x220,
+ 0x222, 0x222,
+ 0x224, 0x224,
+ 0x226, 0x226,
+ 0x228, 0x228,
+ 0x22A, 0x22A,
+ 0x22C, 0x22C,
+ 0x22E, 0x22E,
+ 0x230, 0x230,
+ 0x232, 0x232,
+ 0x23A, 0x23B,
+ 0x23D, 0x23E,
+ 0x241, 0x241,
+ 0x243, 0x246,
+ 0x248, 0x248,
+ 0x24A, 0x24A,
+ 0x24C, 0x24C,
+ 0x24E, 0x24E,
+ 0x370, 0x370,
+ 0x372, 0x372,
+ 0x376, 0x376,
+ 0x386, 0x386,
+ 0x388, 0x38A,
+ 0x38C, 0x38C,
+ 0x38E, 0x38F,
+ 0x391, 0x3A1,
+ 0x3A3, 0x3AB,
+ 0x3CF, 0x3CF,
+ 0x3D2, 0x3D4,
+ 0x3D8, 0x3D8,
+ 0x3DA, 0x3DA,
+ 0x3DC, 0x3DC,
+ 0x3DE, 0x3DE,
+ 0x3E0, 0x3E0,
+ 0x3E2, 0x3E2,
+ 0x3E4, 0x3E4,
+ 0x3E6, 0x3E6,
+ 0x3E8, 0x3E8,
+ 0x3EA, 0x3EA,
+ 0x3EC, 0x3EC,
+ 0x3EE, 0x3EE,
+ 0x3F4, 0x3F4,
+ 0x3F7, 0x3F7,
+ 0x3F9, 0x3FA,
+ 0x3FD, 0x42F,
+ 0x460, 0x460,
+ 0x462, 0x462,
+ 0x464, 0x464,
+ 0x466, 0x466,
+ 0x468, 0x468,
+ 0x46A, 0x46A,
+ 0x46C, 0x46C,
+ 0x46E, 0x46E,
+ 0x470, 0x470,
+ 0x472, 0x472,
+ 0x474, 0x474,
+ 0x476, 0x476,
+ 0x478, 0x478,
+ 0x47A, 0x47A,
+ 0x47C, 0x47C,
+ 0x47E, 0x47E,
+ 0x480, 0x480,
+ 0x48A, 0x48A,
+ 0x48C, 0x48C,
+ 0x48E, 0x48E,
+ 0x490, 0x490,
+ 0x492, 0x492,
+ 0x494, 0x494,
+ 0x496, 0x496,
+ 0x498, 0x498,
+ 0x49A, 0x49A,
+ 0x49C, 0x49C,
+ 0x49E, 0x49E,
+ 0x4A0, 0x4A0,
+ 0x4A2, 0x4A2,
+ 0x4A4, 0x4A4,
+ 0x4A6, 0x4A6,
+ 0x4A8, 0x4A8,
+ 0x4AA, 0x4AA,
+ 0x4AC, 0x4AC,
+ 0x4AE, 0x4AE,
+ 0x4B0, 0x4B0,
+ 0x4B2, 0x4B2,
+ 0x4B4, 0x4B4,
+ 0x4B6, 0x4B6,
+ 0x4B8, 0x4B8,
+ 0x4BA, 0x4BA,
+ 0x4BC, 0x4BC,
+ 0x4BE, 0x4BE,
+ 0x4C0, 0x4C1,
+ 0x4C3, 0x4C3,
+ 0x4C5, 0x4C5,
+ 0x4C7, 0x4C7,
+ 0x4C9, 0x4C9,
+ 0x4CB, 0x4CB,
+ 0x4CD, 0x4CD,
+ 0x4D0, 0x4D0,
+ 0x4D2, 0x4D2,
+ 0x4D4, 0x4D4,
+ 0x4D6, 0x4D6,
+ 0x4D8, 0x4D8,
+ 0x4DA, 0x4DA,
+ 0x4DC, 0x4DC,
+ 0x4DE, 0x4DE,
+ 0x4E0, 0x4E0,
+ 0x4E2, 0x4E2,
+ 0x4E4, 0x4E4,
+ 0x4E6, 0x4E6,
+ 0x4E8, 0x4E8,
+ 0x4EA, 0x4EA,
+ 0x4EC, 0x4EC,
+ 0x4EE, 0x4EE,
+ 0x4F0, 0x4F0,
+ 0x4F2, 0x4F2,
+ 0x4F4, 0x4F4,
+ 0x4F6, 0x4F6,
+ 0x4F8, 0x4F8,
+ 0x4FA, 0x4FA,
+ 0x4FC, 0x4FC,
+ 0x4FE, 0x4FE,
+ 0x500, 0x500,
+ 0x502, 0x502,
+ 0x504, 0x504,
+ 0x506, 0x506,
+ 0x508, 0x508,
+ 0x50A, 0x50A,
+ 0x50C, 0x50C,
+ 0x50E, 0x50E,
+ 0x510, 0x510,
+ 0x512, 0x512,
+ 0x514, 0x514,
+ 0x516, 0x516,
+ 0x518, 0x518,
+ 0x51A, 0x51A,
+ 0x51C, 0x51C,
+ 0x51E, 0x51E,
+ 0x520, 0x520,
+ 0x522, 0x522,
+ 0x524, 0x524,
+ 0x526, 0x526,
+ 0x531, 0x556,
+ 0x10A0, 0x10C5,
+ 0x1E00, 0x1E00,
+ 0x1E02, 0x1E02,
+ 0x1E04, 0x1E04,
+ 0x1E06, 0x1E06,
+ 0x1E08, 0x1E08,
+ 0x1E0A, 0x1E0A,
+ 0x1E0C, 0x1E0C,
+ 0x1E0E, 0x1E0E,
+ 0x1E10, 0x1E10,
+ 0x1E12, 0x1E12,
+ 0x1E14, 0x1E14,
+ 0x1E16, 0x1E16,
+ 0x1E18, 0x1E18,
+ 0x1E1A, 0x1E1A,
+ 0x1E1C, 0x1E1C,
+ 0x1E1E, 0x1E1E,
+ 0x1E20, 0x1E20,
+ 0x1E22, 0x1E22,
+ 0x1E24, 0x1E24,
+ 0x1E26, 0x1E26,
+ 0x1E28, 0x1E28,
+ 0x1E2A, 0x1E2A,
+ 0x1E2C, 0x1E2C,
+ 0x1E2E, 0x1E2E,
+ 0x1E30, 0x1E30,
+ 0x1E32, 0x1E32,
+ 0x1E34, 0x1E34,
+ 0x1E36, 0x1E36,
+ 0x1E38, 0x1E38,
+ 0x1E3A, 0x1E3A,
+ 0x1E3C, 0x1E3C,
+ 0x1E3E, 0x1E3E,
+ 0x1E40, 0x1E40,
+ 0x1E42, 0x1E42,
+ 0x1E44, 0x1E44,
+ 0x1E46, 0x1E46,
+ 0x1E48, 0x1E48,
+ 0x1E4A, 0x1E4A,
+ 0x1E4C, 0x1E4C,
+ 0x1E4E, 0x1E4E,
+ 0x1E50, 0x1E50,
+ 0x1E52, 0x1E52,
+ 0x1E54, 0x1E54,
+ 0x1E56, 0x1E56,
+ 0x1E58, 0x1E58,
+ 0x1E5A, 0x1E5A,
+ 0x1E5C, 0x1E5C,
+ 0x1E5E, 0x1E5E,
+ 0x1E60, 0x1E60,
+ 0x1E62, 0x1E62,
+ 0x1E64, 0x1E64,
+ 0x1E66, 0x1E66,
+ 0x1E68, 0x1E68,
+ 0x1E6A, 0x1E6A,
+ 0x1E6C, 0x1E6C,
+ 0x1E6E, 0x1E6E,
+ 0x1E70, 0x1E70,
+ 0x1E72, 0x1E72,
+ 0x1E74, 0x1E74,
+ 0x1E76, 0x1E76,
+ 0x1E78, 0x1E78,
+ 0x1E7A, 0x1E7A,
+ 0x1E7C, 0x1E7C,
+ 0x1E7E, 0x1E7E,
+ 0x1E80, 0x1E80,
+ 0x1E82, 0x1E82,
+ 0x1E84, 0x1E84,
+ 0x1E86, 0x1E86,
+ 0x1E88, 0x1E88,
+ 0x1E8A, 0x1E8A,
+ 0x1E8C, 0x1E8C,
+ 0x1E8E, 0x1E8E,
+ 0x1E90, 0x1E90,
+ 0x1E92, 0x1E92,
+ 0x1E94, 0x1E94,
+ 0x1E9E, 0x1E9E,
+ 0x1EA0, 0x1EA0,
+ 0x1EA2, 0x1EA2,
+ 0x1EA4, 0x1EA4,
+ 0x1EA6, 0x1EA6,
+ 0x1EA8, 0x1EA8,
+ 0x1EAA, 0x1EAA,
+ 0x1EAC, 0x1EAC,
+ 0x1EAE, 0x1EAE,
+ 0x1EB0, 0x1EB0,
+ 0x1EB2, 0x1EB2,
+ 0x1EB4, 0x1EB4,
+ 0x1EB6, 0x1EB6,
+ 0x1EB8, 0x1EB8,
+ 0x1EBA, 0x1EBA,
+ 0x1EBC, 0x1EBC,
+ 0x1EBE, 0x1EBE,
+ 0x1EC0, 0x1EC0,
+ 0x1EC2, 0x1EC2,
+ 0x1EC4, 0x1EC4,
+ 0x1EC6, 0x1EC6,
+ 0x1EC8, 0x1EC8,
+ 0x1ECA, 0x1ECA,
+ 0x1ECC, 0x1ECC,
+ 0x1ECE, 0x1ECE,
+ 0x1ED0, 0x1ED0,
+ 0x1ED2, 0x1ED2,
+ 0x1ED4, 0x1ED4,
+ 0x1ED6, 0x1ED6,
+ 0x1ED8, 0x1ED8,
+ 0x1EDA, 0x1EDA,
+ 0x1EDC, 0x1EDC,
+ 0x1EDE, 0x1EDE,
+ 0x1EE0, 0x1EE0,
+ 0x1EE2, 0x1EE2,
+ 0x1EE4, 0x1EE4,
+ 0x1EE6, 0x1EE6,
+ 0x1EE8, 0x1EE8,
+ 0x1EEA, 0x1EEA,
+ 0x1EEC, 0x1EEC,
+ 0x1EEE, 0x1EEE,
+ 0x1EF0, 0x1EF0,
+ 0x1EF2, 0x1EF2,
+ 0x1EF4, 0x1EF4,
+ 0x1EF6, 0x1EF6,
+ 0x1EF8, 0x1EF8,
+ 0x1EFA, 0x1EFA,
+ 0x1EFC, 0x1EFC,
+ 0x1EFE, 0x1EFE,
+ 0x1F08, 0x1F0F,
+ 0x1F18, 0x1F1D,
+ 0x1F28, 0x1F2F,
+ 0x1F38, 0x1F3F,
+ 0x1F48, 0x1F4D,
+ 0x1F59, 0x1F59,
+ 0x1F5B, 0x1F5B,
+ 0x1F5D, 0x1F5D,
+ 0x1F5F, 0x1F5F,
+ 0x1F68, 0x1F6F,
+ 0x1FB8, 0x1FBB,
+ 0x1FC8, 0x1FCB,
+ 0x1FD8, 0x1FDB,
+ 0x1FE8, 0x1FEC,
+ 0x1FF8, 0x1FFB,
+ 0x2102, 0x2102,
+ 0x2107, 0x2107,
+ 0x210B, 0x210D,
+ 0x2110, 0x2112,
+ 0x2115, 0x2115,
+ 0x2119, 0x211D,
+ 0x2124, 0x2124,
+ 0x2126, 0x2126,
+ 0x2128, 0x2128,
+ 0x212A, 0x212D,
+ 0x2130, 0x2133,
+ 0x213E, 0x213F,
+ 0x2145, 0x2145,
+ 0x2183, 0x2183,
+ 0x2C00, 0x2C2E,
+ 0x2C60, 0x2C60,
+ 0x2C62, 0x2C64,
+ 0x2C67, 0x2C67,
+ 0x2C69, 0x2C69,
+ 0x2C6B, 0x2C6B,
+ 0x2C6D, 0x2C70,
+ 0x2C72, 0x2C72,
+ 0x2C75, 0x2C75,
+ 0x2C7E, 0x2C80,
+ 0x2C82, 0x2C82,
+ 0x2C84, 0x2C84,
+ 0x2C86, 0x2C86,
+ 0x2C88, 0x2C88,
+ 0x2C8A, 0x2C8A,
+ 0x2C8C, 0x2C8C,
+ 0x2C8E, 0x2C8E,
+ 0x2C90, 0x2C90,
+ 0x2C92, 0x2C92,
+ 0x2C94, 0x2C94,
+ 0x2C96, 0x2C96,
+ 0x2C98, 0x2C98,
+ 0x2C9A, 0x2C9A,
+ 0x2C9C, 0x2C9C,
+ 0x2C9E, 0x2C9E,
+ 0x2CA0, 0x2CA0,
+ 0x2CA2, 0x2CA2,
+ 0x2CA4, 0x2CA4,
+ 0x2CA6, 0x2CA6,
+ 0x2CA8, 0x2CA8,
+ 0x2CAA, 0x2CAA,
+ 0x2CAC, 0x2CAC,
+ 0x2CAE, 0x2CAE,
+ 0x2CB0, 0x2CB0,
+ 0x2CB2, 0x2CB2,
+ 0x2CB4, 0x2CB4,
+ 0x2CB6, 0x2CB6,
+ 0x2CB8, 0x2CB8,
+ 0x2CBA, 0x2CBA,
+ 0x2CBC, 0x2CBC,
+ 0x2CBE, 0x2CBE,
+ 0x2CC0, 0x2CC0,
+ 0x2CC2, 0x2CC2,
+ 0x2CC4, 0x2CC4,
+ 0x2CC6, 0x2CC6,
+ 0x2CC8, 0x2CC8,
+ 0x2CCA, 0x2CCA,
+ 0x2CCC, 0x2CCC,
+ 0x2CCE, 0x2CCE,
+ 0x2CD0, 0x2CD0,
+ 0x2CD2, 0x2CD2,
+ 0x2CD4, 0x2CD4,
+ 0x2CD6, 0x2CD6,
+ 0x2CD8, 0x2CD8,
+ 0x2CDA, 0x2CDA,
+ 0x2CDC, 0x2CDC,
+ 0x2CDE, 0x2CDE,
+ 0x2CE0, 0x2CE0,
+ 0x2CE2, 0x2CE2,
+ 0x2CEB, 0x2CEB,
+ 0x2CED, 0x2CED,
+ 0xA640, 0xA640,
+ 0xA642, 0xA642,
+ 0xA644, 0xA644,
+ 0xA646, 0xA646,
+ 0xA648, 0xA648,
+ 0xA64A, 0xA64A,
+ 0xA64C, 0xA64C,
+ 0xA64E, 0xA64E,
+ 0xA650, 0xA650,
+ 0xA652, 0xA652,
+ 0xA654, 0xA654,
+ 0xA656, 0xA656,
+ 0xA658, 0xA658,
+ 0xA65A, 0xA65A,
+ 0xA65C, 0xA65C,
+ 0xA65E, 0xA65E,
+ 0xA660, 0xA660,
+ 0xA662, 0xA662,
+ 0xA664, 0xA664,
+ 0xA666, 0xA666,
+ 0xA668, 0xA668,
+ 0xA66A, 0xA66A,
+ 0xA66C, 0xA66C,
+ 0xA680, 0xA680,
+ 0xA682, 0xA682,
+ 0xA684, 0xA684,
+ 0xA686, 0xA686,
+ 0xA688, 0xA688,
+ 0xA68A, 0xA68A,
+ 0xA68C, 0xA68C,
+ 0xA68E, 0xA68E,
+ 0xA690, 0xA690,
+ 0xA692, 0xA692,
+ 0xA694, 0xA694,
+ 0xA696, 0xA696,
+ 0xA722, 0xA722,
+ 0xA724, 0xA724,
+ 0xA726, 0xA726,
+ 0xA728, 0xA728,
+ 0xA72A, 0xA72A,
+ 0xA72C, 0xA72C,
+ 0xA72E, 0xA72E,
+ 0xA732, 0xA732,
+ 0xA734, 0xA734,
+ 0xA736, 0xA736,
+ 0xA738, 0xA738,
+ 0xA73A, 0xA73A,
+ 0xA73C, 0xA73C,
+ 0xA73E, 0xA73E,
+ 0xA740, 0xA740,
+ 0xA742, 0xA742,
+ 0xA744, 0xA744,
+ 0xA746, 0xA746,
+ 0xA748, 0xA748,
+ 0xA74A, 0xA74A,
+ 0xA74C, 0xA74C,
+ 0xA74E, 0xA74E,
+ 0xA750, 0xA750,
+ 0xA752, 0xA752,
+ 0xA754, 0xA754,
+ 0xA756, 0xA756,
+ 0xA758, 0xA758,
+ 0xA75A, 0xA75A,
+ 0xA75C, 0xA75C,
+ 0xA75E, 0xA75E,
+ 0xA760, 0xA760,
+ 0xA762, 0xA762,
+ 0xA764, 0xA764,
+ 0xA766, 0xA766,
+ 0xA768, 0xA768,
+ 0xA76A, 0xA76A,
+ 0xA76C, 0xA76C,
+ 0xA76E, 0xA76E,
+ 0xA779, 0xA779,
+ 0xA77B, 0xA77B,
+ 0xA77D, 0xA77E,
+ 0xA780, 0xA780,
+ 0xA782, 0xA782,
+ 0xA784, 0xA784,
+ 0xA786, 0xA786,
+ 0xA78B, 0xA78B,
+ 0xA78D, 0xA78D,
+ 0xA790, 0xA790,
+ 0xA7A0, 0xA7A0,
+ 0xA7A2, 0xA7A2,
+ 0xA7A4, 0xA7A4,
+ 0xA7A6, 0xA7A6,
+ 0xA7A8, 0xA7A8,
+ 0xFF21, 0xFF3A,
+ 0x10400, 0x10427,
+ 0x1D400, 0x1D419,
+ 0x1D434, 0x1D44D,
+ 0x1D468, 0x1D481,
+ 0x1D49C, 0x1D49C,
+ 0x1D49E, 0x1D49F,
+ 0x1D4A2, 0x1D4A2,
+ 0x1D4A5, 0x1D4A6,
+ 0x1D4A9, 0x1D4AC,
+ 0x1D4AE, 0x1D4B5,
+ 0x1D4D0, 0x1D4E9,
+ 0x1D504, 0x1D505,
+ 0x1D507, 0x1D50A,
+ 0x1D50D, 0x1D514,
+ 0x1D516, 0x1D51C,
+ 0x1D538, 0x1D539,
+ 0x1D53B, 0x1D53E,
+ 0x1D540, 0x1D544,
+ 0x1D546, 0x1D546,
+ 0x1D54A, 0x1D550,
+ 0x1D56C, 0x1D585,
+ 0x1D5A0, 0x1D5B9,
+ 0x1D5D4, 0x1D5ED,
+ 0x1D608, 0x1D621,
+ 0x1D63C, 0x1D655,
+ 0x1D670, 0x1D689,
+ 0x1D6A8, 0x1D6C0,
+ 0x1D6E2, 0x1D6FA,
+ 0x1D71C, 0x1D734,
+ 0x1D756, 0x1D76E,
+ 0x1D790, 0x1D7A8,
+ 0x1D7CA, 0x1D7CA
+ }));
+
+
+ CATEGORIES.put("Mc", pred(new int[]{
+ 0x903, 0x903,
+ 0x93B, 0x93B,
+ 0x93E, 0x940,
+ 0x949, 0x94C,
+ 0x94E, 0x94F,
+ 0x982, 0x983,
+ 0x9BE, 0x9C0,
+ 0x9C7, 0x9C8,
+ 0x9CB, 0x9CC,
+ 0x9D7, 0x9D7,
+ 0xA03, 0xA03,
+ 0xA3E, 0xA40,
+ 0xA83, 0xA83,
+ 0xABE, 0xAC0,
+ 0xAC9, 0xAC9,
+ 0xACB, 0xACC,
+ 0xB02, 0xB03,
+ 0xB3E, 0xB3E,
+ 0xB40, 0xB40,
+ 0xB47, 0xB48,
+ 0xB4B, 0xB4C,
+ 0xB57, 0xB57,
+ 0xBBE, 0xBBF,
+ 0xBC1, 0xBC2,
+ 0xBC6, 0xBC8,
+ 0xBCA, 0xBCC,
+ 0xBD7, 0xBD7,
+ 0xC01, 0xC03,
+ 0xC41, 0xC44,
+ 0xC82, 0xC83,
+ 0xCBE, 0xCBE,
+ 0xCC0, 0xCC4,
+ 0xCC7, 0xCC8,
+ 0xCCA, 0xCCB,
+ 0xCD5, 0xCD6,
+ 0xD02, 0xD03,
+ 0xD3E, 0xD40,
+ 0xD46, 0xD48,
+ 0xD4A, 0xD4C,
+ 0xD57, 0xD57,
+ 0xD82, 0xD83,
+ 0xDCF, 0xDD1,
+ 0xDD8, 0xDDF,
+ 0xDF2, 0xDF3,
+ 0xF3E, 0xF3F,
+ 0xF7F, 0xF7F,
+ 0x102B, 0x102C,
+ 0x1031, 0x1031,
+ 0x1038, 0x1038,
+ 0x103B, 0x103C,
+ 0x1056, 0x1057,
+ 0x1062, 0x1064,
+ 0x1067, 0x106D,
+ 0x1083, 0x1084,
+ 0x1087, 0x108C,
+ 0x108F, 0x108F,
+ 0x109A, 0x109C,
+ 0x17B6, 0x17B6,
+ 0x17BE, 0x17C5,
+ 0x17C7, 0x17C8,
+ 0x1923, 0x1926,
+ 0x1929, 0x192B,
+ 0x1930, 0x1931,
+ 0x1933, 0x1938,
+ 0x19B0, 0x19C0,
+ 0x19C8, 0x19C9,
+ 0x1A19, 0x1A1B,
+ 0x1A55, 0x1A55,
+ 0x1A57, 0x1A57,
+ 0x1A61, 0x1A61,
+ 0x1A63, 0x1A64,
+ 0x1A6D, 0x1A72,
+ 0x1B04, 0x1B04,
+ 0x1B35, 0x1B35,
+ 0x1B3B, 0x1B3B,
+ 0x1B3D, 0x1B41,
+ 0x1B43, 0x1B44,
+ 0x1B82, 0x1B82,
+ 0x1BA1, 0x1BA1,
+ 0x1BA6, 0x1BA7,
+ 0x1BAA, 0x1BAA,
+ 0x1BE7, 0x1BE7,
+ 0x1BEA, 0x1BEC,
+ 0x1BEE, 0x1BEE,
+ 0x1BF2, 0x1BF3,
+ 0x1C24, 0x1C2B,
+ 0x1C34, 0x1C35,
+ 0x1CE1, 0x1CE1,
+ 0x1CF2, 0x1CF2,
+ 0xA823, 0xA824,
+ 0xA827, 0xA827,
+ 0xA880, 0xA881,
+ 0xA8B4, 0xA8C3,
+ 0xA952, 0xA953,
+ 0xA983, 0xA983,
+ 0xA9B4, 0xA9B5,
+ 0xA9BA, 0xA9BB,
+ 0xA9BD, 0xA9C0,
+ 0xAA2F, 0xAA30,
+ 0xAA33, 0xAA34,
+ 0xAA4D, 0xAA4D,
+ 0xAA7B, 0xAA7B,
+ 0xABE3, 0xABE4,
+ 0xABE6, 0xABE7,
+ 0xABE9, 0xABEA,
+ 0xABEC, 0xABEC,
+ 0x11000, 0x11000,
+ 0x11002, 0x11002,
+ 0x11082, 0x11082,
+ 0x110B0, 0x110B2,
+ 0x110B7, 0x110B8,
+ 0x1D165, 0x1D166,
+ 0x1D16D, 0x1D172
+ }));
+
+
+ CATEGORIES.put("Me", pred(new int[]{
+ 0x488, 0x489,
+ 0x20DD, 0x20E0,
+ 0x20E2, 0x20E4,
+ 0xA670, 0xA672
+ }));
+
+
+ CATEGORIES.put("Mn", pred(new int[]{
+ 0x300, 0x36F,
+ 0x483, 0x487,
+ 0x591, 0x5BD,
+ 0x5BF, 0x5BF,
+ 0x5C1, 0x5C2,
+ 0x5C4, 0x5C5,
+ 0x5C7, 0x5C7,
+ 0x610, 0x61A,
+ 0x64B, 0x65F,
+ 0x670, 0x670,
+ 0x6D6, 0x6DC,
+ 0x6DF, 0x6E4,
+ 0x6E7, 0x6E8,
+ 0x6EA, 0x6ED,
+ 0x711, 0x711,
+ 0x730, 0x74A,
+ 0x7A6, 0x7B0,
+ 0x7EB, 0x7F3,
+ 0x816, 0x819,
+ 0x81B, 0x823,
+ 0x825, 0x827,
+ 0x829, 0x82D,
+ 0x859, 0x85B,
+ 0x900, 0x902,
+ 0x93A, 0x93A,
+ 0x93C, 0x93C,
+ 0x941, 0x948,
+ 0x94D, 0x94D,
+ 0x951, 0x957,
+ 0x962, 0x963,
+ 0x981, 0x981,
+ 0x9BC, 0x9BC,
+ 0x9C1, 0x9C4,
+ 0x9CD, 0x9CD,
+ 0x9E2, 0x9E3,
+ 0xA01, 0xA02,
+ 0xA3C, 0xA3C,
+ 0xA41, 0xA42,
+ 0xA47, 0xA48,
+ 0xA4B, 0xA4D,
+ 0xA51, 0xA51,
+ 0xA70, 0xA71,
+ 0xA75, 0xA75,
+ 0xA81, 0xA82,
+ 0xABC, 0xABC,
+ 0xAC1, 0xAC5,
+ 0xAC7, 0xAC8,
+ 0xACD, 0xACD,
+ 0xAE2, 0xAE3,
+ 0xB01, 0xB01,
+ 0xB3C, 0xB3C,
+ 0xB3F, 0xB3F,
+ 0xB41, 0xB44,
+ 0xB4D, 0xB4D,
+ 0xB56, 0xB56,
+ 0xB62, 0xB63,
+ 0xB82, 0xB82,
+ 0xBC0, 0xBC0,
+ 0xBCD, 0xBCD,
+ 0xC3E, 0xC40,
+ 0xC46, 0xC48,
+ 0xC4A, 0xC4D,
+ 0xC55, 0xC56,
+ 0xC62, 0xC63,
+ 0xCBC, 0xCBC,
+ 0xCBF, 0xCBF,
+ 0xCC6, 0xCC6,
+ 0xCCC, 0xCCD,
+ 0xCE2, 0xCE3,
+ 0xD41, 0xD44,
+ 0xD4D, 0xD4D,
+ 0xD62, 0xD63,
+ 0xDCA, 0xDCA,
+ 0xDD2, 0xDD4,
+ 0xDD6, 0xDD6,
+ 0xE31, 0xE31,
+ 0xE34, 0xE3A,
+ 0xE47, 0xE4E,
+ 0xEB1, 0xEB1,
+ 0xEB4, 0xEB9,
+ 0xEBB, 0xEBC,
+ 0xEC8, 0xECD,
+ 0xF18, 0xF19,
+ 0xF35, 0xF35,
+ 0xF37, 0xF37,
+ 0xF39, 0xF39,
+ 0xF71, 0xF7E,
+ 0xF80, 0xF84,
+ 0xF86, 0xF87,
+ 0xF8D, 0xF97,
+ 0xF99, 0xFBC,
+ 0xFC6, 0xFC6,
+ 0x102D, 0x1030,
+ 0x1032, 0x1037,
+ 0x1039, 0x103A,
+ 0x103D, 0x103E,
+ 0x1058, 0x1059,
+ 0x105E, 0x1060,
+ 0x1071, 0x1074,
+ 0x1082, 0x1082,
+ 0x1085, 0x1086,
+ 0x108D, 0x108D,
+ 0x109D, 0x109D,
+ 0x135D, 0x135F,
+ 0x1712, 0x1714,
+ 0x1732, 0x1734,
+ 0x1752, 0x1753,
+ 0x1772, 0x1773,
+ 0x17B7, 0x17BD,
+ 0x17C6, 0x17C6,
+ 0x17C9, 0x17D3,
+ 0x17DD, 0x17DD,
+ 0x180B, 0x180D,
+ 0x18A9, 0x18A9,
+ 0x1920, 0x1922,
+ 0x1927, 0x1928,
+ 0x1932, 0x1932,
+ 0x1939, 0x193B,
+ 0x1A17, 0x1A18,
+ 0x1A56, 0x1A56,
+ 0x1A58, 0x1A5E,
+ 0x1A60, 0x1A60,
+ 0x1A62, 0x1A62,
+ 0x1A65, 0x1A6C,
+ 0x1A73, 0x1A7C,
+ 0x1A7F, 0x1A7F,
+ 0x1B00, 0x1B03,
+ 0x1B34, 0x1B34,
+ 0x1B36, 0x1B3A,
+ 0x1B3C, 0x1B3C,
+ 0x1B42, 0x1B42,
+ 0x1B6B, 0x1B73,
+ 0x1B80, 0x1B81,
+ 0x1BA2, 0x1BA5,
+ 0x1BA8, 0x1BA9,
+ 0x1BE6, 0x1BE6,
+ 0x1BE8, 0x1BE9,
+ 0x1BED, 0x1BED,
+ 0x1BEF, 0x1BF1,
+ 0x1C2C, 0x1C33,
+ 0x1C36, 0x1C37,
+ 0x1CD0, 0x1CD2,
+ 0x1CD4, 0x1CE0,
+ 0x1CE2, 0x1CE8,
+ 0x1CED, 0x1CED,
+ 0x1DC0, 0x1DE6,
+ 0x1DFC, 0x1DFF,
+ 0x20D0, 0x20DC,
+ 0x20E1, 0x20E1,
+ 0x20E5, 0x20F0,
+ 0x2CEF, 0x2CF1,
+ 0x2D7F, 0x2D7F,
+ 0x2DE0, 0x2DFF,
+ 0x302A, 0x302F,
+ 0x3099, 0x309A,
+ 0xA66F, 0xA66F,
+ 0xA67C, 0xA67D,
+ 0xA6F0, 0xA6F1,
+ 0xA802, 0xA802,
+ 0xA806, 0xA806,
+ 0xA80B, 0xA80B,
+ 0xA825, 0xA826,
+ 0xA8C4, 0xA8C4,
+ 0xA8E0, 0xA8F1,
+ 0xA926, 0xA92D,
+ 0xA947, 0xA951,
+ 0xA980, 0xA982,
+ 0xA9B3, 0xA9B3,
+ 0xA9B6, 0xA9B9,
+ 0xA9BC, 0xA9BC,
+ 0xAA29, 0xAA2E,
+ 0xAA31, 0xAA32,
+ 0xAA35, 0xAA36,
+ 0xAA43, 0xAA43,
+ 0xAA4C, 0xAA4C,
+ 0xAAB0, 0xAAB0,
+ 0xAAB2, 0xAAB4,
+ 0xAAB7, 0xAAB8,
+ 0xAABE, 0xAABF,
+ 0xAAC1, 0xAAC1,
+ 0xABE5, 0xABE5,
+ 0xABE8, 0xABE8,
+ 0xABED, 0xABED,
+ 0xFB1E, 0xFB1E,
+ 0xFE00, 0xFE0F,
+ 0xFE20, 0xFE26,
+ 0x101FD, 0x101FD,
+ 0x10A01, 0x10A03,
+ 0x10A05, 0x10A06,
+ 0x10A0C, 0x10A0F,
+ 0x10A38, 0x10A3A,
+ 0x10A3F, 0x10A3F,
+ 0x11001, 0x11001,
+ 0x11038, 0x11046,
+ 0x11080, 0x11081,
+ 0x110B3, 0x110B6,
+ 0x110B9, 0x110BA,
+ 0x1D167, 0x1D169,
+ 0x1D17B, 0x1D182,
+ 0x1D185, 0x1D18B,
+ 0x1D1AA, 0x1D1AD,
+ 0x1D242, 0x1D244,
+ 0xE0100, 0xE01EF
+ }));
+
+
+ CATEGORIES.put("Nd", pred(new int[]{
+ 0x30, 0x39,
+ 0x660, 0x669,
+ 0x6F0, 0x6F9,
+ 0x7C0, 0x7C9,
+ 0x966, 0x96F,
+ 0x9E6, 0x9EF,
+ 0xA66, 0xA6F,
+ 0xAE6, 0xAEF,
+ 0xB66, 0xB6F,
+ 0xBE6, 0xBEF,
+ 0xC66, 0xC6F,
+ 0xCE6, 0xCEF,
+ 0xD66, 0xD6F,
+ 0xE50, 0xE59,
+ 0xED0, 0xED9,
+ 0xF20, 0xF29,
+ 0x1040, 0x1049,
+ 0x1090, 0x1099,
+ 0x17E0, 0x17E9,
+ 0x1810, 0x1819,
+ 0x1946, 0x194F,
+ 0x19D0, 0x19D9,
+ 0x1A80, 0x1A89,
+ 0x1A90, 0x1A99,
+ 0x1B50, 0x1B59,
+ 0x1BB0, 0x1BB9,
+ 0x1C40, 0x1C49,
+ 0x1C50, 0x1C59,
+ 0xA620, 0xA629,
+ 0xA8D0, 0xA8D9,
+ 0xA900, 0xA909,
+ 0xA9D0, 0xA9D9,
+ 0xAA50, 0xAA59,
+ 0xABF0, 0xABF9,
+ 0xFF10, 0xFF19,
+ 0x104A0, 0x104A9,
+ 0x11066, 0x1106F,
+ 0x1D7CE, 0x1D7FF
+ }));
+
+
+ CATEGORIES.put("Nl", pred(new int[]{
+ 0x16EE, 0x16F0,
+ 0x2160, 0x2182,
+ 0x2185, 0x2188,
+ 0x3007, 0x3007,
+ 0x3021, 0x3029,
+ 0x3038, 0x303A,
+ 0xA6E6, 0xA6EF,
+ 0x10140, 0x10174,
+ 0x10341, 0x10341,
+ 0x1034A, 0x1034A,
+ 0x103D1, 0x103D5,
+ 0x12400, 0x12462
+ }));
+
+
+ CATEGORIES.put("No", pred(new int[]{
+ 0xB2, 0xB3,
+ 0xB9, 0xB9,
+ 0xBC, 0xBE,
+ 0x9F4, 0x9F9,
+ 0xB72, 0xB77,
+ 0xBF0, 0xBF2,
+ 0xC78, 0xC7E,
+ 0xD70, 0xD75,
+ 0xF2A, 0xF33,
+ 0x1369, 0x137C,
+ 0x17F0, 0x17F9,
+ 0x19DA, 0x19DA,
+ 0x2070, 0x2070,
+ 0x2074, 0x2079,
+ 0x2080, 0x2089,
+ 0x2150, 0x215F,
+ 0x2189, 0x2189,
+ 0x2460, 0x249B,
+ 0x24EA, 0x24FF,
+ 0x2776, 0x2793,
+ 0x2CFD, 0x2CFD,
+ 0x3192, 0x3195,
+ 0x3220, 0x3229,
+ 0x3251, 0x325F,
+ 0x3280, 0x3289,
+ 0x32B1, 0x32BF,
+ 0xA830, 0xA835,
+ 0x10107, 0x10133,
+ 0x10175, 0x10178,
+ 0x1018A, 0x1018A,
+ 0x10320, 0x10323,
+ 0x10858, 0x1085F,
+ 0x10916, 0x1091B,
+ 0x10A40, 0x10A47,
+ 0x10A7D, 0x10A7E,
+ 0x10B58, 0x10B5F,
+ 0x10B78, 0x10B7F,
+ 0x10E60, 0x10E7E,
+ 0x11052, 0x11065,
+ 0x1D360, 0x1D371,
+ 0x1F100, 0x1F10A
+ }));
+
+
+ CATEGORIES.put("Pc", pred(new int[]{
+ 0x5F, 0x5F,
+ 0x203F, 0x2040,
+ 0x2054, 0x2054,
+ 0xFE33, 0xFE34,
+ 0xFE4D, 0xFE4F,
+ 0xFF3F, 0xFF3F
+ }));
+
+
+ CATEGORIES.put("Pd", pred(new int[]{
+ 0x2D, 0x2D,
+ 0x58A, 0x58A,
+ 0x5BE, 0x5BE,
+ 0x1400, 0x1400,
+ 0x1806, 0x1806,
+ 0x2010, 0x2015,
+ 0x2E17, 0x2E17,
+ 0x2E1A, 0x2E1A,
+ 0x301C, 0x301C,
+ 0x3030, 0x3030,
+ 0x30A0, 0x30A0,
+ 0xFE31, 0xFE32,
+ 0xFE58, 0xFE58,
+ 0xFE63, 0xFE63,
+ 0xFF0D, 0xFF0D
+ }));
+
+
+ CATEGORIES.put("Pe", pred(new int[]{
+ 0x29, 0x29,
+ 0x5D, 0x5D,
+ 0x7D, 0x7D,
+ 0xF3B, 0xF3B,
+ 0xF3D, 0xF3D,
+ 0x169C, 0x169C,
+ 0x2046, 0x2046,
+ 0x207E, 0x207E,
+ 0x208E, 0x208E,
+ 0x232A, 0x232A,
+ 0x2769, 0x2769,
+ 0x276B, 0x276B,
+ 0x276D, 0x276D,
+ 0x276F, 0x276F,
+ 0x2771, 0x2771,
+ 0x2773, 0x2773,
+ 0x2775, 0x2775,
+ 0x27C6, 0x27C6,
+ 0x27E7, 0x27E7,
+ 0x27E9, 0x27E9,
+ 0x27EB, 0x27EB,
+ 0x27ED, 0x27ED,
+ 0x27EF, 0x27EF,
+ 0x2984, 0x2984,
+ 0x2986, 0x2986,
+ 0x2988, 0x2988,
+ 0x298A, 0x298A,
+ 0x298C, 0x298C,
+ 0x298E, 0x298E,
+ 0x2990, 0x2990,
+ 0x2992, 0x2992,
+ 0x2994, 0x2994,
+ 0x2996, 0x2996,
+ 0x2998, 0x2998,
+ 0x29D9, 0x29D9,
+ 0x29DB, 0x29DB,
+ 0x29FD, 0x29FD,
+ 0x2E23, 0x2E23,
+ 0x2E25, 0x2E25,
+ 0x2E27, 0x2E27,
+ 0x2E29, 0x2E29,
+ 0x3009, 0x3009,
+ 0x300B, 0x300B,
+ 0x300D, 0x300D,
+ 0x300F, 0x300F,
+ 0x3011, 0x3011,
+ 0x3015, 0x3015,
+ 0x3017, 0x3017,
+ 0x3019, 0x3019,
+ 0x301B, 0x301B,
+ 0x301E, 0x301F,
+ 0xFD3F, 0xFD3F,
+ 0xFE18, 0xFE18,
+ 0xFE36, 0xFE36,
+ 0xFE38, 0xFE38,
+ 0xFE3A, 0xFE3A,
+ 0xFE3C, 0xFE3C,
+ 0xFE3E, 0xFE3E,
+ 0xFE40, 0xFE40,
+ 0xFE42, 0xFE42,
+ 0xFE44, 0xFE44,
+ 0xFE48, 0xFE48,
+ 0xFE5A, 0xFE5A,
+ 0xFE5C, 0xFE5C,
+ 0xFE5E, 0xFE5E,
+ 0xFF09, 0xFF09,
+ 0xFF3D, 0xFF3D,
+ 0xFF5D, 0xFF5D,
+ 0xFF60, 0xFF60,
+ 0xFF63, 0xFF63
+ }));
+
+
+ CATEGORIES.put("Pf", pred(new int[]{
+ 0xBB, 0xBB,
+ 0x2019, 0x2019,
+ 0x201D, 0x201D,
+ 0x203A, 0x203A,
+ 0x2E03, 0x2E03,
+ 0x2E05, 0x2E05,
+ 0x2E0A, 0x2E0A,
+ 0x2E0D, 0x2E0D,
+ 0x2E1D, 0x2E1D,
+ 0x2E21, 0x2E21
+ }));
+
+
+ CATEGORIES.put("Pi", pred(new int[]{
+ 0xAB, 0xAB,
+ 0x2018, 0x2018,
+ 0x201B, 0x201C,
+ 0x201F, 0x201F,
+ 0x2039, 0x2039,
+ 0x2E02, 0x2E02,
+ 0x2E04, 0x2E04,
+ 0x2E09, 0x2E09,
+ 0x2E0C, 0x2E0C,
+ 0x2E1C, 0x2E1C,
+ 0x2E20, 0x2E20
+ }));
+
+
+ CATEGORIES.put("Po", pred(new int[]{
+ 0x21, 0x23,
+ 0x25, 0x27,
+ 0x2A, 0x2A,
+ 0x2C, 0x2C,
+ 0x2E, 0x2F,
+ 0x3A, 0x3B,
+ 0x3F, 0x40,
+ 0x5C, 0x5C,
+ 0xA1, 0xA1,
+ 0xB7, 0xB7,
+ 0xBF, 0xBF,
+ 0x37E, 0x37E,
+ 0x387, 0x387,
+ 0x55A, 0x55F,
+ 0x589, 0x589,
+ 0x5C0, 0x5C0,
+ 0x5C3, 0x5C3,
+ 0x5C6, 0x5C6,
+ 0x5F3, 0x5F4,
+ 0x609, 0x60A,
+ 0x60C, 0x60D,
+ 0x61B, 0x61B,
+ 0x61E, 0x61F,
+ 0x66A, 0x66D,
+ 0x6D4, 0x6D4,
+ 0x700, 0x70D,
+ 0x7F7, 0x7F9,
+ 0x830, 0x83E,
+ 0x85E, 0x85E,
+ 0x964, 0x965,
+ 0x970, 0x970,
+ 0xDF4, 0xDF4,
+ 0xE4F, 0xE4F,
+ 0xE5A, 0xE5B,
+ 0xF04, 0xF12,
+ 0xF85, 0xF85,
+ 0xFD0, 0xFD4,
+ 0xFD9, 0xFDA,
+ 0x104A, 0x104F,
+ 0x10FB, 0x10FB,
+ 0x1361, 0x1368,
+ 0x166D, 0x166E,
+ 0x16EB, 0x16ED,
+ 0x1735, 0x1736,
+ 0x17D4, 0x17D6,
+ 0x17D8, 0x17DA,
+ 0x1800, 0x1805,
+ 0x1807, 0x180A,
+ 0x1944, 0x1945,
+ 0x1A1E, 0x1A1F,
+ 0x1AA0, 0x1AA6,
+ 0x1AA8, 0x1AAD,
+ 0x1B5A, 0x1B60,
+ 0x1BFC, 0x1BFF,
+ 0x1C3B, 0x1C3F,
+ 0x1C7E, 0x1C7F,
+ 0x1CD3, 0x1CD3,
+ 0x2016, 0x2017,
+ 0x2020, 0x2027,
+ 0x2030, 0x2038,
+ 0x203B, 0x203E,
+ 0x2041, 0x2043,
+ 0x2047, 0x2051,
+ 0x2053, 0x2053,
+ 0x2055, 0x205E,
+ 0x2CF9, 0x2CFC,
+ 0x2CFE, 0x2CFF,
+ 0x2D70, 0x2D70,
+ 0x2E00, 0x2E01,
+ 0x2E06, 0x2E08,
+ 0x2E0B, 0x2E0B,
+ 0x2E0E, 0x2E16,
+ 0x2E18, 0x2E19,
+ 0x2E1B, 0x2E1B,
+ 0x2E1E, 0x2E1F,
+ 0x2E2A, 0x2E2E,
+ 0x2E30, 0x2E31,
+ 0x3001, 0x3003,
+ 0x303D, 0x303D,
+ 0x30FB, 0x30FB,
+ 0xA4FE, 0xA4FF,
+ 0xA60D, 0xA60F,
+ 0xA673, 0xA673,
+ 0xA67E, 0xA67E,
+ 0xA6F2, 0xA6F7,
+ 0xA874, 0xA877,
+ 0xA8CE, 0xA8CF,
+ 0xA8F8, 0xA8FA,
+ 0xA92E, 0xA92F,
+ 0xA95F, 0xA95F,
+ 0xA9C1, 0xA9CD,
+ 0xA9DE, 0xA9DF,
+ 0xAA5C, 0xAA5F,
+ 0xAADE, 0xAADF,
+ 0xABEB, 0xABEB,
+ 0xFE10, 0xFE16,
+ 0xFE19, 0xFE19,
+ 0xFE30, 0xFE30,
+ 0xFE45, 0xFE46,
+ 0xFE49, 0xFE4C,
+ 0xFE50, 0xFE52,
+ 0xFE54, 0xFE57,
+ 0xFE5F, 0xFE61,
+ 0xFE68, 0xFE68,
+ 0xFE6A, 0xFE6B,
+ 0xFF01, 0xFF03,
+ 0xFF05, 0xFF07,
+ 0xFF0A, 0xFF0A,
+ 0xFF0C, 0xFF0C,
+ 0xFF0E, 0xFF0F,
+ 0xFF1A, 0xFF1B,
+ 0xFF1F, 0xFF20,
+ 0xFF3C, 0xFF3C,
+ 0xFF61, 0xFF61,
+ 0xFF64, 0xFF65,
+ 0x10100, 0x10101,
+ 0x1039F, 0x1039F,
+ 0x103D0, 0x103D0,
+ 0x10857, 0x10857,
+ 0x1091F, 0x1091F,
+ 0x1093F, 0x1093F,
+ 0x10A50, 0x10A58,
+ 0x10A7F, 0x10A7F,
+ 0x10B39, 0x10B3F,
+ 0x11047, 0x1104D,
+ 0x110BB, 0x110BC,
+ 0x110BE, 0x110C1,
+ 0x12470, 0x12473
+ }));
+
+
+ CATEGORIES.put("Ps", pred(new int[]{
+ 0x28, 0x28,
+ 0x5B, 0x5B,
+ 0x7B, 0x7B,
+ 0xF3A, 0xF3A,
+ 0xF3C, 0xF3C,
+ 0x169B, 0x169B,
+ 0x201A, 0x201A,
+ 0x201E, 0x201E,
+ 0x2045, 0x2045,
+ 0x207D, 0x207D,
+ 0x208D, 0x208D,
+ 0x2329, 0x2329,
+ 0x2768, 0x2768,
+ 0x276A, 0x276A,
+ 0x276C, 0x276C,
+ 0x276E, 0x276E,
+ 0x2770, 0x2770,
+ 0x2772, 0x2772,
+ 0x2774, 0x2774,
+ 0x27C5, 0x27C5,
+ 0x27E6, 0x27E6,
+ 0x27E8, 0x27E8,
+ 0x27EA, 0x27EA,
+ 0x27EC, 0x27EC,
+ 0x27EE, 0x27EE,
+ 0x2983, 0x2983,
+ 0x2985, 0x2985,
+ 0x2987, 0x2987,
+ 0x2989, 0x2989,
+ 0x298B, 0x298B,
+ 0x298D, 0x298D,
+ 0x298F, 0x298F,
+ 0x2991, 0x2991,
+ 0x2993, 0x2993,
+ 0x2995, 0x2995,
+ 0x2997, 0x2997,
+ 0x29D8, 0x29D8,
+ 0x29DA, 0x29DA,
+ 0x29FC, 0x29FC,
+ 0x2E22, 0x2E22,
+ 0x2E24, 0x2E24,
+ 0x2E26, 0x2E26,
+ 0x2E28, 0x2E28,
+ 0x3008, 0x3008,
+ 0x300A, 0x300A,
+ 0x300C, 0x300C,
+ 0x300E, 0x300E,
+ 0x3010, 0x3010,
+ 0x3014, 0x3014,
+ 0x3016, 0x3016,
+ 0x3018, 0x3018,
+ 0x301A, 0x301A,
+ 0x301D, 0x301D,
+ 0xFD3E, 0xFD3E,
+ 0xFE17, 0xFE17,
+ 0xFE35, 0xFE35,
+ 0xFE37, 0xFE37,
+ 0xFE39, 0xFE39,
+ 0xFE3B, 0xFE3B,
+ 0xFE3D, 0xFE3D,
+ 0xFE3F, 0xFE3F,
+ 0xFE41, 0xFE41,
+ 0xFE43, 0xFE43,
+ 0xFE47, 0xFE47,
+ 0xFE59, 0xFE59,
+ 0xFE5B, 0xFE5B,
+ 0xFE5D, 0xFE5D,
+ 0xFF08, 0xFF08,
+ 0xFF3B, 0xFF3B,
+ 0xFF5B, 0xFF5B,
+ 0xFF5F, 0xFF5F,
+ 0xFF62, 0xFF62
+ }));
+
+
+ CATEGORIES.put("Sc", pred(new int[]{
+ 0x24, 0x24,
+ 0xA2, 0xA5,
+ 0x60B, 0x60B,
+ 0x9F2, 0x9F3,
+ 0x9FB, 0x9FB,
+ 0xAF1, 0xAF1,
+ 0xBF9, 0xBF9,
+ 0xE3F, 0xE3F,
+ 0x17DB, 0x17DB,
+ 0x20A0, 0x20B9,
+ 0xA838, 0xA838,
+ 0xFDFC, 0xFDFC,
+ 0xFE69, 0xFE69,
+ 0xFF04, 0xFF04,
+ 0xFFE0, 0xFFE1,
+ 0xFFE5, 0xFFE6
+ }));
+
+
+ CATEGORIES.put("Sk", pred(new int[]{
+ 0x5E, 0x5E,
+ 0x60, 0x60,
+ 0xA8, 0xA8,
+ 0xAF, 0xAF,
+ 0xB4, 0xB4,
+ 0xB8, 0xB8,
+ 0x2C2, 0x2C5,
+ 0x2D2, 0x2DF,
+ 0x2E5, 0x2EB,
+ 0x2ED, 0x2ED,
+ 0x2EF, 0x2FF,
+ 0x375, 0x375,
+ 0x384, 0x385,
+ 0x1FBD, 0x1FBD,
+ 0x1FBF, 0x1FC1,
+ 0x1FCD, 0x1FCF,
+ 0x1FDD, 0x1FDF,
+ 0x1FED, 0x1FEF,
+ 0x1FFD, 0x1FFE,
+ 0x309B, 0x309C,
+ 0xA700, 0xA716,
+ 0xA720, 0xA721,
+ 0xA789, 0xA78A,
+ 0xFBB2, 0xFBC1,
+ 0xFF3E, 0xFF3E,
+ 0xFF40, 0xFF40,
+ 0xFFE3, 0xFFE3
+ }));
+
+
+ CATEGORIES.put("Sm", pred(new int[]{
+ 0x2B, 0x2B,
+ 0x3C, 0x3E,
+ 0x7C, 0x7C,
+ 0x7E, 0x7E,
+ 0xAC, 0xAC,
+ 0xB1, 0xB1,
+ 0xD7, 0xD7,
+ 0xF7, 0xF7,
+ 0x3F6, 0x3F6,
+ 0x606, 0x608,
+ 0x2044, 0x2044,
+ 0x2052, 0x2052,
+ 0x207A, 0x207C,
+ 0x208A, 0x208C,
+ 0x2118, 0x2118,
+ 0x2140, 0x2144,
+ 0x214B, 0x214B,
+ 0x2190, 0x2194,
+ 0x219A, 0x219B,
+ 0x21A0, 0x21A0,
+ 0x21A3, 0x21A3,
+ 0x21A6, 0x21A6,
+ 0x21AE, 0x21AE,
+ 0x21CE, 0x21CF,
+ 0x21D2, 0x21D2,
+ 0x21D4, 0x21D4,
+ 0x21F4, 0x22FF,
+ 0x2308, 0x230B,
+ 0x2320, 0x2321,
+ 0x237C, 0x237C,
+ 0x239B, 0x23B3,
+ 0x23DC, 0x23E1,
+ 0x25B7, 0x25B7,
+ 0x25C1, 0x25C1,
+ 0x25F8, 0x25FF,
+ 0x266F, 0x266F,
+ 0x27C0, 0x27C4,
+ 0x27C7, 0x27CA,
+ 0x27CC, 0x27CC,
+ 0x27CE, 0x27E5,
+ 0x27F0, 0x27FF,
+ 0x2900, 0x2982,
+ 0x2999, 0x29D7,
+ 0x29DC, 0x29FB,
+ 0x29FE, 0x2AFF,
+ 0x2B30, 0x2B44,
+ 0x2B47, 0x2B4C,
+ 0xFB29, 0xFB29,
+ 0xFE62, 0xFE62,
+ 0xFE64, 0xFE66,
+ 0xFF0B, 0xFF0B,
+ 0xFF1C, 0xFF1E,
+ 0xFF5C, 0xFF5C,
+ 0xFF5E, 0xFF5E,
+ 0xFFE2, 0xFFE2,
+ 0xFFE9, 0xFFEC,
+ 0x1D6C1, 0x1D6C1,
+ 0x1D6DB, 0x1D6DB,
+ 0x1D6FB, 0x1D6FB,
+ 0x1D715, 0x1D715,
+ 0x1D735, 0x1D735,
+ 0x1D74F, 0x1D74F,
+ 0x1D76F, 0x1D76F,
+ 0x1D789, 0x1D789,
+ 0x1D7A9, 0x1D7A9,
+ 0x1D7C3, 0x1D7C3
+ }));
+
+
+ CATEGORIES.put("So", pred(new int[]{
+ 0xA6, 0xA7,
+ 0xA9, 0xA9,
+ 0xAE, 0xAE,
+ 0xB0, 0xB0,
+ 0xB6, 0xB6,
+ 0x482, 0x482,
+ 0x60E, 0x60F,
+ 0x6DE, 0x6DE,
+ 0x6E9, 0x6E9,
+ 0x6FD, 0x6FE,
+ 0x7F6, 0x7F6,
+ 0x9FA, 0x9FA,
+ 0xB70, 0xB70,
+ 0xBF3, 0xBF8,
+ 0xBFA, 0xBFA,
+ 0xC7F, 0xC7F,
+ 0xD79, 0xD79,
+ 0xF01, 0xF03,
+ 0xF13, 0xF17,
+ 0xF1A, 0xF1F,
+ 0xF34, 0xF34,
+ 0xF36, 0xF36,
+ 0xF38, 0xF38,
+ 0xFBE, 0xFC5,
+ 0xFC7, 0xFCC,
+ 0xFCE, 0xFCF,
+ 0xFD5, 0xFD8,
+ 0x109E, 0x109F,
+ 0x1360, 0x1360,
+ 0x1390, 0x1399,
+ 0x1940, 0x1940,
+ 0x19DE, 0x19FF,
+ 0x1B61, 0x1B6A,
+ 0x1B74, 0x1B7C,
+ 0x2100, 0x2101,
+ 0x2103, 0x2106,
+ 0x2108, 0x2109,
+ 0x2114, 0x2114,
+ 0x2116, 0x2117,
+ 0x211E, 0x2123,
+ 0x2125, 0x2125,
+ 0x2127, 0x2127,
+ 0x2129, 0x2129,
+ 0x212E, 0x212E,
+ 0x213A, 0x213B,
+ 0x214A, 0x214A,
+ 0x214C, 0x214D,
+ 0x214F, 0x214F,
+ 0x2195, 0x2199,
+ 0x219C, 0x219F,
+ 0x21A1, 0x21A2,
+ 0x21A4, 0x21A5,
+ 0x21A7, 0x21AD,
+ 0x21AF, 0x21CD,
+ 0x21D0, 0x21D1,
+ 0x21D3, 0x21D3,
+ 0x21D5, 0x21F3,
+ 0x2300, 0x2307,
+ 0x230C, 0x231F,
+ 0x2322, 0x2328,
+ 0x232B, 0x237B,
+ 0x237D, 0x239A,
+ 0x23B4, 0x23DB,
+ 0x23E2, 0x23F3,
+ 0x2400, 0x2426,
+ 0x2440, 0x244A,
+ 0x249C, 0x24E9,
+ 0x2500, 0x25B6,
+ 0x25B8, 0x25C0,
+ 0x25C2, 0x25F7,
+ 0x2600, 0x266E,
+ 0x2670, 0x26FF,
+ 0x2701, 0x2767,
+ 0x2794, 0x27BF,
+ 0x2800, 0x28FF,
+ 0x2B00, 0x2B2F,
+ 0x2B45, 0x2B46,
+ 0x2B50, 0x2B59,
+ 0x2CE5, 0x2CEA,
+ 0x2E80, 0x2E99,
+ 0x2E9B, 0x2EF3,
+ 0x2F00, 0x2FD5,
+ 0x2FF0, 0x2FFB,
+ 0x3004, 0x3004,
+ 0x3012, 0x3013,
+ 0x3020, 0x3020,
+ 0x3036, 0x3037,
+ 0x303E, 0x303F,
+ 0x3190, 0x3191,
+ 0x3196, 0x319F,
+ 0x31C0, 0x31E3,
+ 0x3200, 0x321E,
+ 0x322A, 0x3250,
+ 0x3260, 0x327F,
+ 0x328A, 0x32B0,
+ 0x32C0, 0x32FE,
+ 0x3300, 0x33FF,
+ 0x4DC0, 0x4DFF,
+ 0xA490, 0xA4C6,
+ 0xA828, 0xA82B,
+ 0xA836, 0xA837,
+ 0xA839, 0xA839,
+ 0xAA77, 0xAA79,
+ 0xFDFD, 0xFDFD,
+ 0xFFE4, 0xFFE4,
+ 0xFFE8, 0xFFE8,
+ 0xFFED, 0xFFEE,
+ 0xFFFC, 0xFFFD,
+ 0x10102, 0x10102,
+ 0x10137, 0x1013F,
+ 0x10179, 0x10189,
+ 0x10190, 0x1019B,
+ 0x101D0, 0x101FC,
+ 0x1D000, 0x1D0F5,
+ 0x1D100, 0x1D126,
+ 0x1D129, 0x1D164,
+ 0x1D16A, 0x1D16C,
+ 0x1D183, 0x1D184,
+ 0x1D18C, 0x1D1A9,
+ 0x1D1AE, 0x1D1DD,
+ 0x1D200, 0x1D241,
+ 0x1D245, 0x1D245,
+ 0x1D300, 0x1D356,
+ 0x1F000, 0x1F02B,
+ 0x1F030, 0x1F093,
+ 0x1F0A0, 0x1F0AE,
+ 0x1F0B1, 0x1F0BE,
+ 0x1F0C1, 0x1F0CF,
+ 0x1F0D1, 0x1F0DF,
+ 0x1F110, 0x1F12E,
+ 0x1F130, 0x1F169,
+ 0x1F170, 0x1F19A,
+ 0x1F1E6, 0x1F202,
+ 0x1F210, 0x1F23A,
+ 0x1F240, 0x1F248,
+ 0x1F250, 0x1F251,
+ 0x1F300, 0x1F320,
+ 0x1F330, 0x1F335,
+ 0x1F337, 0x1F37C,
+ 0x1F380, 0x1F393,
+ 0x1F3A0, 0x1F3C4,
+ 0x1F3C6, 0x1F3CA,
+ 0x1F3E0, 0x1F3F0,
+ 0x1F400, 0x1F43E,
+ 0x1F440, 0x1F440,
+ 0x1F442, 0x1F4F7,
+ 0x1F4F9, 0x1F4FC,
+ 0x1F500, 0x1F53D,
+ 0x1F550, 0x1F567,
+ 0x1F5FB, 0x1F5FF,
+ 0x1F601, 0x1F610,
+ 0x1F612, 0x1F614,
+ 0x1F616, 0x1F616,
+ 0x1F618, 0x1F618,
+ 0x1F61A, 0x1F61A,
+ 0x1F61C, 0x1F61E,
+ 0x1F620, 0x1F625,
+ 0x1F628, 0x1F62B,
+ 0x1F62D, 0x1F62D,
+ 0x1F630, 0x1F633,
+ 0x1F635, 0x1F640,
+ 0x1F645, 0x1F64F,
+ 0x1F680, 0x1F6C5,
+ 0x1F700, 0x1F773
+ }));
+
+
+ CATEGORIES.put("Zl", pred(new int[]{
+ 0x2028, 0x2028
+ }));
+
+
+ CATEGORIES.put("Zp", pred(new int[]{
+ 0x2029, 0x2029
+ }));
+
+
+ CATEGORIES.put("Zs", pred(new int[]{
+ 0x20, 0x20,
+ 0xA0, 0xA0,
+ 0x1680, 0x1680,
+ 0x180E, 0x180E,
+ 0x2000, 0x200A,
+ 0x202F, 0x202F,
+ 0x205F, 0x205F,
+ 0x3000, 0x3000
+ }));
+
+ String c = "CLMNPSZ";
+ for (int i=0; i<c.length(); i++) {
+ char ch = c.charAt(i);
+ IntPredicate ip = null;
+ for (Map.Entry<String, IntPredicate> entry : CATEGORIES.entrySet()) {
+ if (entry.getKey().charAt(0) == ch) {
+ ip = (ip == null ? entry.getValue() : new IntUnionPredicate(ip, entry.getValue()));
+ }
+ }
+ CATEGORIES.put(ch+"", ip);
+ }
+
+
+ }
+
+ public final static IntPredicate ESCAPE_s =
+ new IntSetPredicate(IntArraySet.make(new int[]{9, 10, 13, 32}, 4));
+
+ public final static IntPredicate ESCAPE_S = new IntComplementPredicate(ESCAPE_s);
+
+ public final static IntPredicate ESCAPE_i = new IntPredicate() {
+ public boolean matches(int value) {
+ return XMLCharacterData.isNCNameStart11(value) || value==':';
+ }
+ };
+
+ public final static IntPredicate ESCAPE_I = new IntPredicate() {
+ public boolean matches(int value) {
+ return !(XMLCharacterData.isNCNameStart11(value) || value==':');
+ }
+ };
+
+ public final static IntPredicate ESCAPE_c = new IntPredicate() {
+ public boolean matches(int value) {
+ return XMLCharacterData.isNCName11(value) || value==':';
+ }
+ };
+
+ public final static IntPredicate ESCAPE_C = new IntPredicate() {
+ public boolean matches(int value) {
+ return !(XMLCharacterData.isNCName11(value) || value==':');
+ }
+ };
+
+ public final static IntPredicate ESCAPE_d = getCategory("Nd");
+
+ public final static IntPredicate ESCAPE_D = new IntComplementPredicate(ESCAPE_d);
+
+ static IntPredicate CATEGORY_P = getCategory("P");
+ static IntPredicate CATEGORY_Z = getCategory("Z");
+ static IntPredicate CATEGORY_C = getCategory("C");
+
+ public final static IntPredicate ESCAPE_w = new IntPredicate () {
+ public boolean matches(int value) {
+ return !(CATEGORY_P.matches(value) || CATEGORY_Z.matches(value) || CATEGORY_C.matches(value));
+ }
+ };
+
+ public final static IntPredicate ESCAPE_W = new IntComplementPredicate(ESCAPE_w);
+
+ /**
+ * Construct a predicate from a set of character ranges
+ * @param ranges the ranges of characters
+ * @return a predicate that is true for characters present in one of these ranges
+ */
+
+ private static IntPredicate pred(int[] ranges) {
+ int[] startPoints = new int[ranges.length/2];
+ int[] endPoints = new int[ranges.length/2];
+ for (int i=0; i<ranges.length; i+=2) {
+ startPoints[i/2] = ranges[i];
+ endPoints[i/2] = ranges[i+1];
+ }
+ return new IntSetPredicate(new IntRangeSet(startPoints, endPoints));
+ }
+
+ /**
+ * Get a predicate to test characters for membership of one of the Unicode
+ * character categories
+ * @param cat a one-character or two-character category name, for example L or Lu
+ * @return a predicate that tests whether a given character belongs to the category
+ */
+
+ public static IntPredicate getCategory(String cat) {
+ return CATEGORIES.get(cat);
+ }
+
+
+}
+
+// The following stylesheet was used to generate this module, taking data from the XML form of the
+// Unicode 6.0.0 database. The ranges not listed as individual characters (Co, Cn) were added by hand.
+
+//<?xml version="1.0" encoding="UTF-8"?>
+//<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+// xmlns:xs="http://www.w3.org/2001/XMLSchema"
+// xmlns:f="http://local-functions/"
+// exclude-result-prefixes="xs f"
+// version="2.0">
+//
+// <!-- Output Java representation of character categories -->
+//
+// <xsl:output method="text"/>
+//
+// <xsl:param name="v6" select="doc('ucd.all.flat.xml')"/>
+//
+// <xsl:key name="cat-key" match="char" use="@gc"/>
+//
+// <xsl:template name="main">
+//
+// public final static HashMap<String, int[]> CATEGORIES = new HashMap<String, int[]>(30);
+//
+// static {
+//
+// <xsl:variable name="categories" select="distinct-values($v6/ucd/repertoire/char/@gc)"/>
+// <xsl:for-each select="$categories">
+// <xsl:sort select="."/>
+// <xsl:variable name="chars" select="key('cat-key', ., $v6)/@cp"/>
+// <xsl:variable name="codes" select="for $c in $chars return f:hexToInt(0,$c)"/>
+//
+// CATEGORIES.put("<xsl:value-of select="."/>", new int[]{
+// <xsl:for-each-group select="$codes" group-adjacent=". - position()">
+// <xsl:if test="position() ne 1">,
</xsl:if>
+// <xsl:value-of select="'0x', f:intToHex(current-group()[1]), ', 0x', f:intToHex(current-group()[1] + count(current-group()) - 1)" separator=""/>
+// </xsl:for-each-group>
+// });
+// </xsl:for-each>
+// }
+// </xsl:template>
+//
+//
+// <xsl:function name="f:hexToInt" as="xs:integer">
+// <xsl:param name="acc" as="xs:integer"/>
+// <xsl:param name="in" as="xs:string"/>
+// <xsl:choose>
+// <xsl:when test="$in eq ''">
+// <xsl:sequence select="$acc"/>
+// </xsl:when>
+// <xsl:otherwise>
+// <xsl:variable name="first" select="string-length(substring-before('0123456789ABCDEF', substring($in, 1, 1)))"/>
+// <xsl:sequence select="f:hexToInt($acc * 16 + $first, substring($in, 2))"/>
+// </xsl:otherwise>
+// </xsl:choose>
+// </xsl:function>
+//
+// <xsl:function name="f:intToHex" as="xs:string">
+// <xsl:param name="in" as="xs:integer"/>
+// <xsl:choose>
+// <xsl:when test="$in eq 0">
+// <xsl:sequence select="''"/>
+// </xsl:when>
+// <xsl:otherwise>
+// <xsl:variable name="last" select="substring('0123456789ABCDEF', $in mod 16 + 1, 1)"/>
+// <xsl:sequence select="concat(f:intToHex($in idiv 16), $last)"/>
+// </xsl:otherwise>
+// </xsl:choose>
+// </xsl:function>
+//
+//</xsl:stylesheet>
\ No newline at end of file
diff --git a/sf/saxon/regex/GeneralUnicodeString.java b/sf/saxon/regex/GeneralUnicodeString.java
new file mode 100644
index 0000000..c4b6253
--- /dev/null
+++ b/sf/saxon/regex/GeneralUnicodeString.java
@@ -0,0 +1,85 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.value.StringValue;
+
+/**
+ * A Unicode string which, in general, may contain non-BMP characters (that is, codepoints
+ * outside the range 0-65535)
+ */
+
+public final class GeneralUnicodeString extends UnicodeString {
+
+ private int[] chars;
+ private int start;
+ private int end;
+
+ public GeneralUnicodeString(CharSequence in) {
+ chars = net.sf.saxon.value.StringValue.expand(in);
+ start = 0;
+ end = chars.length;
+ }
+
+ GeneralUnicodeString(int[] chars, int start, int end) {
+ this.chars = chars;
+ this.start = start;
+ this.end = end;
+ }
+
+ public UnicodeString substring(int beginIndex, int endIndex) {
+ if (endIndex > chars.length) {
+ throw new IndexOutOfBoundsException("endIndex=" + endIndex
+ + "; sequence size=" + chars.length);
+ }
+ if (beginIndex < 0 || beginIndex > endIndex) {
+ throw new IndexOutOfBoundsException("beginIndex=" + beginIndex
+ + "; endIndex=" + endIndex);
+ }
+ return new GeneralUnicodeString(chars, start + beginIndex, start + endIndex);
+ }
+
+ public int charAt(int pos) {
+ return chars[start + pos];
+ }
+
+ public int indexOf(int search, int pos) {
+ for (int i=pos; i<length(); i++) {
+ if (chars[start+i] == search) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public int length() {
+ return end - start;
+ }
+
+ public boolean isEnd(int pos) {
+ return (pos >= (end - start));
+ }
+
+ public String toString() {
+ int[] c = chars;
+ if (start != 0) {
+ c = new int[end - start];
+ System.arraycopy(chars, start, c, 0, end - start);
+ }
+ return StringValue.contract(c, end - start).toString();
+ }
+
+ public CharSequence getCharSequence() {
+ int[] c = chars;
+ if (start != 0) {
+ c = new int[end - start];
+ System.arraycopy(chars, start, c, 0, end - start);
+ }
+ return StringValue.contract(c, end - start);
+ }
+}
diff --git a/sf/saxon/regex/JRegexIterator.java b/sf/saxon/regex/JRegexIterator.java
new file mode 100644
index 0000000..a423774
--- /dev/null
+++ b/sf/saxon/regex/JRegexIterator.java
@@ -0,0 +1,343 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntToIntHashMap;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ArrayIterator;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class JRegexIterator - provides an iterator over matched and unmatched substrings.
+ * This implementation of RegexIterator uses the JDK regular expression engine.
+*/
+
+public class JRegexIterator implements RegexIterator {
+
+ private String theString; // the input string being matched
+ private Pattern pattern; // the regex against which the string is matched
+ private Matcher matcher; // the Matcher object that does the matching, and holds the state
+ /*@Nullable*/ private String current; // the string most recently returned by the iterator
+ private String next; // if the last string was a matching string, null; otherwise the next substring
+ // matched by the regex
+ private int position = 0; // the value of XPath position()
+ private int prevEnd = 0; // the position in the input string of the end of the last match or non-match
+ private IntToIntHashMap nestingTable = null;
+ // evaluated on demand: a table that indicates for each captured group,
+ // what its immediately-containing captured group is.
+
+ /**
+ * Construct a RegexIterator. Note that the underlying matcher.find() method is called once
+ * to obtain each matching substring. But the iterator also returns non-matching substrings
+ * if these appear between the matching substrings.
+ * @param string the string to be analysed
+ * @param pattern the regular expression
+ */
+
+ public JRegexIterator (String string, Pattern pattern) {
+ theString = string;
+ this.pattern = pattern;
+ matcher = pattern.matcher(string);
+ next = null;
+ }
+
+ /**
+ * Get the next item in the sequence
+ * @return the next item in the sequence
+ */
+
+ public Item next() {
+ if (next == null && prevEnd >= 0) {
+ // we've returned a match (or we're at the start), so find the next match
+ if (matcher.find()) {
+ int start = matcher.start();
+ int end = matcher.end();
+ if (prevEnd == start) {
+ // there's no intervening non-matching string to return
+ next = null;
+ current = theString.substring(start, end);
+ prevEnd = end;
+ } else {
+ // return the non-matching substring first
+ current = theString.substring(prevEnd, start);
+ next = theString.substring(start, end);
+ }
+ } else {
+ // there are no more regex matches, we must return the final non-matching text if any
+ if (prevEnd < theString.length()) {
+ current = theString.substring(prevEnd);
+ next = null;
+ } else {
+ // this really is the end...
+ current = null;
+ position = -1;
+ prevEnd = -1;
+ return null;
+ }
+ prevEnd = -1;
+ }
+ } else {
+ // we've returned a non-match, so now return the match that follows it, if there is one
+ if (prevEnd >= 0) {
+ current = next;
+ next = null;
+ prevEnd = matcher.end();
+ } else {
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+ position++;
+ return StringValue.makeStringValue(current);
+ }
+
+ /**
+ * Get the current item in the sequence
+ * @return the item most recently returned by next()
+ */
+
+ public Item current() {
+ return StringValue.makeStringValue(current);
+ }
+
+ /**
+ * Get the position of the current item in the sequence
+ * @return the position of the item most recently returned by next(), starting at 1
+ */
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /**
+ * Get another iterator over the same items
+ * @return a new iterator, positioned before the first item
+ */
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() {
+ return new JRegexIterator(theString, pattern);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+ /**
+ * Determine whether the current item is a matching item or a non-matching item
+ * @return true if the current item (the one most recently returned by next()) is
+ * an item that matches the regular expression, or false if it is an item that
+ * does not match
+ */
+
+ public boolean isMatching() {
+ return next == null && prevEnd >= 0;
+ }
+
+ /**
+ * Get a substring that matches a parenthesised group within the regular expression
+ * @param number the number of the group to be obtained
+ * @return the substring of the current item that matches the n'th parenthesized group
+ * within the regular expression
+ */
+
+ public String getRegexGroup(int number) {
+ if (!isMatching()) return null;
+ if (number > matcher.groupCount() || number < 0) return "";
+ String s = matcher.group(number);
+ if (s==null) return "";
+ return s;
+ }
+
+ /**
+ * Get a sequence containing all the regex groups (except group 0, because we want to use indexing from 1).
+ * This is used by the saxon:analyze-string() higher-order extension function.
+ */
+
+ public SequenceIterator getRegexGroupIterator() {
+ int c = matcher.groupCount();
+ if (c == 0) {
+ return EmptyIterator.getInstance();
+ } else {
+ StringValue[] groups = new StringValue[c];
+ for (int i=1; i<=groups.length; i++) {
+ groups[i-1] = StringValue.makeStringValue(matcher.group(i));
+ }
+ return new ArrayIterator(groups);
+ }
+ }
+
+ /**
+ * Process a matching substring, performing specified actions at the start and end of each captured
+ * subgroup. This method will always be called when operating in "push" mode; it writes its
+ * result to context.getReceiver(). The matching substring text is all written to the receiver,
+ * interspersed with calls to the {@link net.sf.saxon.regexp.RegexIterator.OnGroup} methods onGroupStart() and onGroupEnd().
+ * @param context the dynamic evaluation context
+ * @param action defines the processing to be performed at the start and end of a group
+ */
+
+ public void processMatchingSubstring(XPathContext context, OnGroup action) throws XPathException {
+ Receiver out = context.getReceiver();
+ int c = matcher.groupCount();
+ if (c == 0) {
+ out.characters(current, 0, 0);
+ } else {
+ // Create a map from positions in the string to lists of actions.
+ // The "actions" in each list are: +N: start group N; -N: end group N.
+ IntHashMap<List<Integer>> actions = new IntHashMap<List<Integer>>(c);
+ for (int i=1; i<=c; i++) {
+ int start = matcher.start(i) - matcher.start();
+ if (start != -1) {
+ int end = matcher.end(i) - matcher.start();
+ if (start < end) {
+ // Add the start action after all other actions on the list for the same position
+ List<Integer> s = actions.get(start);
+ if (s == null) {
+ s = new ArrayList<Integer>(4);
+ actions.put(start, s);
+ }
+ s.add(i);
+ // Add the end action before all other actions on the list for the same position
+ List<Integer> e = actions.get(end);
+ if (e == null) {
+ e = new ArrayList<Integer>(4);
+ actions.put(end, e);
+ }
+ e.add(0, -i);
+ } else {
+ // zero-length group (start==end). The problem here is that the information available
+ // from Java isn't sufficient to determine the nesting of groups: match("a", "(a(b?))")
+ // and match("a", "(a)(b?)") will both give the same result for group 2 (start=1, end=1).
+ // So we need to go back to the original regex to determine the group nesting
+ if (nestingTable == null) {
+ computeNestingTable();
+ }
+ int parentGroup = nestingTable.get(i);
+ // insert the start and end events immediately before the end event for the parent group,
+ // if present; otherwise after all existing events for this position
+ List<Integer> s = actions.get(start);
+ if (s == null) {
+ s = new ArrayList<Integer>(4);
+ actions.put(start, s);
+ s.add(i);
+ s.add(-i);
+ } else {
+ int pos = s.size();
+ for (int e=0; e<s.size(); e++) {
+ if (s.get(e) == -parentGroup) {
+ pos = e;
+ break;
+ }
+ }
+ s.add(pos, -i);
+ s.add(pos, i);
+ }
+
+ }
+ }
+
+ }
+ FastStringBuffer buff = new FastStringBuffer(current.length());
+ for (int i=0; i < current.length()+1; i++) {
+ List<Integer> events = actions.get(i);
+ if (events != null) {
+ if (buff.length() > 0) {
+ out.characters(buff, 0, 0);
+ buff.setLength(0);
+ }
+ for (Iterator<Integer> ii = events.iterator(); ii.hasNext();) {
+ int group = ii.next();
+ if (group > 0) {
+ action.onGroupStart(context, group);
+ } else {
+ action.onGroupEnd(context, -group);
+ }
+ }
+ }
+ if (i < current.length()) {
+ buff.append(current.charAt(i));
+ }
+ }
+ if (buff.length() > 0) {
+ out.characters(buff, 0, 0);
+ }
+ }
+
+ }
+
+ public RegexIterator getSnapShot(XPathContext context) throws XPathException {
+ JRegexIterator regexItr = new JRegexIterator (theString, pattern);
+ regexItr.position = this.position;
+ regexItr.current = this.current;
+ regexItr.nestingTable = this.nestingTable;
+ regexItr.prevEnd = this.prevEnd;
+ return regexItr;
+ }
+
+ /**
+ * Compute a table showing for each captured group number (opening paren in the regex),
+ * the number of its parent group. This is done by reparsing the source of the regular
+ * expression. This is needed when the result of a match includes an empty group, to determine
+ * its position relative to other groups finishing at the same character position.
+ */
+
+ private void computeNestingTable() {
+ nestingTable = new IntToIntHashMap(16);
+ String s = pattern.pattern();
+ int[] stack = new int[s.length()];
+ int tos = 0;
+ int group = 1;
+ int inBrackets = 0;
+ stack[tos++] = 0;
+ for (int i=0; i<s.length(); i++) {
+ char ch = s.charAt(i);
+ if (ch == '\'') {
+ i++;
+ } else if (ch == '[') {
+ inBrackets++;
+ } else if (ch == ']') {
+ inBrackets--;
+ } else if (ch == '(' && s.charAt(i+1) != '?' && inBrackets == 0) {
+ nestingTable.put(group, stack[tos-1]);
+ stack[tos++] = group++;
+ } else if (ch == ')' && inBrackets == 0) {
+ tos--;
+ }
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/regex/JTokenIterator.java b/sf/saxon/regex/JTokenIterator.java
new file mode 100644
index 0000000..06cc637
--- /dev/null
+++ b/sf/saxon/regex/JTokenIterator.java
@@ -0,0 +1,92 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.value.StringValue;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A JTokenIterator is an iterator over the strings that result from tokenizing a string using
+ * a regular expression, in this case a regular expression evaluated using the JDK regex engine
+*/
+
+public class JTokenIterator implements SequenceIterator {
+
+ private CharSequence input;
+ private Pattern pattern;
+ private Matcher matcher;
+ /*@Nullable*/ private CharSequence current;
+ private int position = 0;
+ private int prevEnd = 0;
+
+
+ /**
+ * Construct a JTokenIterator.
+ */
+
+ public JTokenIterator (CharSequence input, Pattern pattern) {
+ this.input = input;
+ this.pattern = pattern;
+ matcher = pattern.matcher(input);
+ prevEnd = 0;
+ }
+
+ public Item next() {
+ if (prevEnd < 0) {
+ current = null;
+ position = -1;
+ return null;
+ }
+
+ if (matcher.find()) {
+ current = input.subSequence(prevEnd, matcher.start());
+ prevEnd = matcher.end();
+ } else {
+ current = input.subSequence(prevEnd, input.length());
+ prevEnd = -1;
+ }
+ position++;
+ return StringValue.makeStringValue(current);
+ }
+
+ public Item current() {
+ return (current==null ? null : StringValue.makeStringValue(current));
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() {
+ return new JTokenIterator(input, pattern);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/regex/JavaRegularExpression.java b/sf/saxon/regex/JavaRegularExpression.java
new file mode 100644
index 0000000..43e3a85
--- /dev/null
+++ b/sf/saxon/regex/JavaRegularExpression.java
@@ -0,0 +1,181 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * An implementation of RegularExpression that calls the JDK regular expression library directly.
+ * This can be invoked by appending ";j" to the flags attribute/argument
+ */
+public class JavaRegularExpression implements RegularExpression {
+
+ Pattern pattern;
+ String javaRegex;
+ int flagBits;
+
+ /**
+ * Create a regular expression, starting with an already-translated Java regex.
+ * NOTE: this constructor is called from compiled XQuery code
+ * @param javaRegex the regular expression after translation to Java notation
+ * @param flags the user-specified flags (prior to any semicolon)
+ */
+
+ public JavaRegularExpression(CharSequence javaRegex, String flags) throws XPathException {
+ this.flagBits = setFlags(flags);
+ this.javaRegex = javaRegex.toString();
+ pattern = Pattern.compile(this.javaRegex, flagBits & (~(Pattern.COMMENTS)));
+ }
+
+ /**
+ * Get the Java regular expression (after translation from an XPath regex, but before compilation)
+ * @return the regular expression in Java notation
+ */
+
+ public String getJavaRegularExpression() {
+ return javaRegex;
+ }
+
+ /**
+ * Get the flag bits as used by the Java regular expression engine
+ * @return the flag bits
+ */
+
+ public int getFlagBits() {
+ return flagBits;
+ }
+
+ /**
+ * Use this regular expression to analyze an input string, in support of the XSLT
+ * analyze-string instruction. The resulting RegexIterator provides both the matching and
+ * non-matching substrings, and allows them to be distinguished. It also provides access
+ * to matched subgroups.
+ */
+
+ public RegexIterator analyze(CharSequence input) {
+ return new JRegexIterator(input.toString(), pattern);
+ }
+
+ /**
+ * Determine whether the regular expression contains a match for a given string
+ *
+ * @param input the string to match
+ * @return true if the string matches, false otherwise
+ */
+
+ public boolean containsMatch(CharSequence input) {
+ return pattern.matcher(input).find();
+ }
+
+ /**
+ * Determine whether the regular expression matches a given string in its entirety
+ *
+ * @param input the string to match
+ * @return true if the string matches, false otherwise
+ */
+
+ public boolean matches(CharSequence input) {
+ return pattern.matcher(input).matches();
+ }
+
+ /**
+ * Replace all substrings of a supplied input string that match the regular expression
+ * with a replacement string.
+ *
+ * @param input the input string on which replacements are to be performed
+ * @param replacement the replacement string in the format of the XPath replace() function
+ * @return the result of performing the replacement
+ * @throws net.sf.saxon.trans.XPathException
+ * if the replacement string is invalid
+ */
+
+ public CharSequence replace(CharSequence input, CharSequence replacement) throws XPathException {
+ Matcher matcher = pattern.matcher(input);
+ try {
+ return matcher.replaceAll(replacement.toString());
+ } catch (IndexOutOfBoundsException e) {
+ throw new XPathException(e.getMessage(), "FORX0004");
+ }
+
+ }
+
+ /**
+ * Use this regular expression to tokenize an input string.
+ *
+ * @param input the string to be tokenized
+ * @return a SequenceIterator containing the resulting tokens, as objects of type StringValue
+ */
+
+ public SequenceIterator tokenize(CharSequence input) {
+ if (input.length() == 0) {
+ return EmptyIterator.getInstance();
+ }
+ return new JTokenIterator(input, pattern);
+ }
+
+ /**
+ * Set the Java flags from the supplied XPath flags. The flags recognized have their
+ * Java-defined meanings rather than their XPath-defined meanings. The available flags are:
+ * <p>d - UNIX_LINES</p>
+ * <p>m - MULTILINE</p>
+ * <p>i - CASE_INSENSITIVE</p>
+ * <p>s - DOTALL</p>
+ * <p>x - COMMENTS</p>
+ * <p>u - UNICODE_CASE</p>
+ * <p>q - LITERAL</p>
+ * <p>c - CANON_EQ</p>
+ * @param inFlags the flags as a string, e.g. "im"
+ * @return the flags as a bit-significant integer
+ * @throws XPathException if the supplied value contains an unrecognized flag character
+ * @see java.util.regex.Pattern
+ */
+
+ public static int setFlags(/*@NotNull*/ CharSequence inFlags) throws XPathException {
+ int flags = Pattern.UNIX_LINES;
+ for (int i=0; i<inFlags.length(); i++) {
+ char c = inFlags.charAt(i);
+ switch (c) {
+ case 'd':
+ flags |= Pattern.UNIX_LINES;
+ break;
+ case 'm':
+ flags |= Pattern.MULTILINE;
+ break;
+ case 'i':
+ flags |= Pattern.CASE_INSENSITIVE;
+ break;
+ case 's':
+ flags |= Pattern.DOTALL;
+ break;
+ case 'x':
+ flags |= Pattern.COMMENTS; // note, this enables comments as well as whitespace
+ break;
+ case 'u':
+ flags |= Pattern.UNICODE_CASE;
+ break;
+ case 'q':
+ flags |= Pattern.LITERAL;
+ break;
+ case 'c':
+ flags |= Pattern.CANON_EQ;
+ break;
+ default:
+ XPathException err = new XPathException("Invalid character '" + c + "' in regular expression flags");
+ err.setErrorCode("FORX0001");
+ throw err;
+ }
+ }
+ return flags;
+ }
+}
+
diff --git a/sf/saxon/regex/Operation.java b/sf/saxon/regex/Operation.java
new file mode 100644
index 0000000..1e666f6
--- /dev/null
+++ b/sf/saxon/regex/Operation.java
@@ -0,0 +1,741 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+
+import net.sf.saxon.z.IntPredicate;
+
+/**
+ * Represents an operation or instruction in the regular expression program. The class Operation
+ * is abstract, and has concrete subclasses for each kind of operation/instruction
+ */
+public abstract class Operation {
+
+ // Offset of the next instruction in the program (if branching). During code generation
+ // this is a relative offset; when the array of operations is passed to the REProgram object
+ // it is converted to an absolute offset.
+ public int next;
+
+ // Actions available after calling the exec() method
+ public static final int ACTION_ADVANCE_TO_NEXT = 1; // advance to next instruction
+ public static final int ACTION_RETURN = 2; // return to caller
+ public static final int ACTION_ADVANCE_TO_FOLLOWING = 3; // proceed to collowing instruction
+ public static final int ACTION_ADVANCE_TO_NEXT_NEXT = 4; // advance to next instruction of the next instruction
+
+ /**
+ * Execute the operation
+ * @param matcher the REMatcher
+ * @param node the program node containing this operation
+ * @param idx the current position in the input string
+ * @return >=0: matching succeeded, returns new position in input string.
+ * -1: matching failed: return to caller.
+ */
+
+ abstract int exec(REMatcher matcher, int node, int idx);
+
+ /**
+ * Determine the action to take after calling exec()
+ * @param idx the value returned by exec()
+ * @return one of the values ACTION_RETURN, ACTION_ADVANCE_TO_NEXT, ...
+ */
+
+ public int nextAction(int idx) {
+ // Default action: return -1 on failure, continue on success
+ if (idx == -1) {
+ return ACTION_RETURN;
+ } else {
+ return ACTION_ADVANCE_TO_NEXT;
+ }
+ }
+
+ /**
+ * End of program
+ */
+
+ public static class OpEndProgram extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // An anchored match is successful only if we are at the end of the string.
+ // Otherwise, match has succeeded unconditionally
+ if (matcher.anchoredMatch) {
+ return (matcher.search.isEnd(idx) ? idx : -1);
+ } else {
+ matcher.setParenEnd(0, idx);
+ return idx;
+ }
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_RETURN;
+ }
+
+ public String toString() {
+ return "END";
+ }
+
+ }
+
+ /**
+ * Beginning of Line (^)
+ */
+
+ public static class OpBOL extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Fail if we're not at the start of the string
+ if (idx != 0) {
+ // If we're multiline matching, we could still be at the start of a line
+ if (matcher.program.flags.isMultiLine()) {
+ // Continue if at the start of a line
+ if (matcher.isNewline(idx - 1) && !matcher.search.isEnd(idx)) {
+ return idx;
+ }
+ }
+ return -1;
+ }
+ return idx;
+ }
+
+ public String toString() {
+ return "BOL";
+ }
+
+ }
+
+ /**
+ * End of Line ($)
+ */
+
+ public static class OpEOL extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // If we're not at the end of string
+
+ UnicodeString search = matcher.search;
+ if (matcher.program.flags.isMultiLine()) {
+ if (search.isEnd(0) || search.isEnd(idx) || matcher.isNewline(idx)) {
+ return idx; //match successful
+ } else {
+ return -1;
+ }
+ } else {
+ // TODO: Spec issue. De facto rule (and XSLT test regex02) assume $ matches a final \n
+ if (search.isEnd(0) || search.isEnd(idx) /*|| (matcher.isNewline(idx) && search.isEnd(idx+1))*/) {
+ return idx;
+ } else {
+ return -1;
+ }
+ }
+
+ }
+
+ public String toString() {
+ return "EOL";
+ }
+
+ }
+
+ /**
+ * Choice (|)
+ */
+
+ public static class OpBranch extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Try all available branches
+ int idxNew;
+ do {
+ // Try matching the branch against the string
+ if ((idxNew = matcher.matchNodes(node + 1, idx)) != -1) {
+ return idxNew;
+ }
+
+ // Go to next branch (if any)
+ node = matcher.instructions[node].next;
+ }
+ while (node != -1 && (matcher.program.instructions[node] instanceof Operation.OpBranch));
+
+ // Failed to match any branch!
+ return -1;
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_RETURN;
+ }
+
+ public String toString() {
+ return "BRANCH";
+ }
+
+ }
+
+ /**
+ * Atom
+ */
+
+ public static class OpAtom extends Operation {
+ public UnicodeString atom;
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Match an atom value
+ UnicodeString search = matcher.search;
+ if (search.isEnd(idx)) {
+ return -1;
+ }
+
+
+ // Give up if not enough input remains to have a match
+ if (search.isEnd(atom.length() + idx - 1)) {
+ return -1;
+ }
+
+ // Match atom differently depending on casefolding flag
+ if (matcher.program.flags.isCaseIndependent()) {
+ for (int i = 0; i < atom.length(); i++) {
+ if (!matcher.equalCaseBlind(search.charAt(idx++), atom.charAt(i))) {
+ return -1;
+ }
+ }
+ } else {
+ for (int i = 0; i < atom.length(); i++) {
+ if (search.charAt(idx++) != atom.charAt(i)) {
+ return -1;
+ }
+ }
+ }
+ return idx;
+ }
+
+ public String toString() {
+ return "ATOM \"" + atom.toString() + "\"";
+ }
+ }
+
+ /**
+ * Star quantifier
+ */
+
+ public static class OpStar extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Note: same as OpMaybe
+ // If we've been here before, then don't try again; we won't make any progress.
+ if (matcher.beenHereBefore(idx, node)) {
+ return -1;
+ }
+
+ // Try to match the following subexpr. If it matches:
+ // MAYBE: Continues matching rest of the expression
+ // STAR: Points back here to repeat subexpr matching
+ return matcher.matchNodes(node + 1, idx);
+ }
+
+ public int nextAction(int idx) {
+ if (idx == -1) {
+ return ACTION_ADVANCE_TO_NEXT;
+ } else {
+ return ACTION_RETURN;
+ }
+ }
+
+ public String toString() {
+ return "STAR";
+ }
+
+ }
+
+ /**
+ * "Confident Star" quantifier: used when there is no ambiguity about the ending condition,
+ * and therefore no need to backtrack. This means we can use iteration rather than recursion,
+ * eliminating the risk of stack overflow.
+ */
+
+ public static class OpConfidentStar extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+
+ // If we've been here before, then don't try again; we won't make any progress.
+ if (matcher.beenHereBefore(idx, node)) {
+ return -1;
+ }
+
+ int newIdx;
+ Operation term = matcher.instructions[node+1];
+ while (true) {
+ newIdx = term.exec(matcher, node+1, idx);
+ if (newIdx == -1) {
+ return idx;
+ } else {
+ idx = newIdx;
+ }
+ }
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_ADVANCE_TO_NEXT;
+ }
+
+ public String toString() {
+ return "CONFIDENT_STAR";
+ }
+
+ }
+
+
+ /**
+ * Plus quantifier
+ */
+
+ public static class OpPlus extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ return matcher.matchNodes(next, idx);
+ }
+
+ public int nextAction(int idx) {
+ if (idx == -1) {
+ return ACTION_ADVANCE_TO_NEXT_NEXT;
+ } else {
+ return ACTION_RETURN;
+ }
+ }
+
+ public String toString() {
+ return "PLUS";
+ }
+
+ }
+
+ /**
+ * "Confident Plus" quantifier: used when there is no ambiguity about the ending condition,
+ * and therefore no need to backtrack. This means we can use iteration rather than recursion,
+ * eliminating the risk of stack overflow.
+ */
+
+ public static class OpConfidentPlus extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+
+ // If we've been here before, then don't try again; we won't make any progress.
+ if (matcher.beenHereBefore(idx, node)) {
+ return -1;
+ }
+
+ int newIdx;
+ Operation term = matcher.instructions[node-1];
+ while (true) {
+ newIdx = term.exec(matcher, node-1, idx);
+ if (newIdx == -1) {
+ return idx;
+ } else {
+ idx = newIdx;
+ }
+ }
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_ADVANCE_TO_NEXT;
+ }
+
+ public String toString() {
+ return "CONFIDENT_PLUS";
+ }
+
+ }
+
+
+ /**
+ * Maybe (question-mark) quantifier
+ */
+
+ public static class OpMaybe extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Note: same as OpStar
+ // If we've been here before, then don't try again; we won't make any progress.
+ if (matcher.beenHereBefore(idx, node)) {
+ return -1;
+ }
+
+ // Try to match the following subexpr. If it matches:
+ // MAYBE: Continues matching rest of the expression
+ // STAR: Points back here to repeat subexpr matching
+ return matcher.matchNodes(node + 1, idx);
+ }
+
+ public int nextAction(int idx) {
+ if (idx == -1) {
+ return ACTION_ADVANCE_TO_NEXT;
+ } else {
+ return ACTION_RETURN;
+ }
+ }
+
+ public String toString() {
+ return "MAYBE";
+ }
+
+ }
+
+ /**
+ * Open paren (captured group)
+ */
+
+ public static class OpOpen extends Operation {
+
+ public int groupNr;
+
+ public OpOpen(int group) {
+ this.groupNr = group;
+ }
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ if ((matcher.program.optimizationFlags & REProgram.OPT_HASBACKREFS) != 0) {
+ matcher.startBackref[groupNr] = idx;
+ }
+ int idxNew = matcher.matchNodes(next, idx);
+ if (idxNew != -1) {
+ // Increase valid paren count
+ if (groupNr >= matcher.parenCount) {
+ matcher.parenCount = groupNr + 1;
+ }
+
+ // Don't set paren if already set later on
+ if (matcher.getParenStart(groupNr) == -1) {
+ matcher.setParenStart(groupNr, idx);
+ }
+ }
+ return idxNew;
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_RETURN;
+ }
+
+ public String toString() {
+ return "OPEN_GROUP " + groupNr;
+ }
+
+ }
+
+ /**
+ * Open non-capturing paren
+ */
+
+ public static class OpOpenCluster extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ return idx;
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_ADVANCE_TO_NEXT;
+ }
+
+ public String toString() {
+ return "OPEN_CLUSTER";
+ }
+
+ }
+
+ /**
+ * Close paren (captured group)
+ */
+
+ public static class OpClose extends Operation {
+
+ public int groupNr;
+
+ public OpClose(int groupNr) {
+ this.groupNr = groupNr;
+ }
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Done matching subexpression
+ if ((matcher.program.optimizationFlags & REProgram.OPT_HASBACKREFS) != 0) {
+ matcher.endBackref[groupNr] = idx;
+ }
+ int idxNew = matcher.matchNodes(next, idx);
+ if (idxNew != -1) {
+ // Increase valid paren count
+ if (groupNr >= matcher.parenCount) {
+ matcher.parenCount = groupNr + 1;
+ }
+
+ // Don't set paren if already set later on
+ if (matcher.getParenEnd(groupNr) == -1) {
+ matcher.setParenEnd(groupNr, idx);
+ }
+ }
+ return idxNew;
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_RETURN;
+ }
+
+ public String toString() {
+ return "CLOSE_GROUP " + groupNr;
+ }
+ }
+
+ /**
+ * Close non-capturing group
+ */
+
+ public static class OpCloseCluster extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ return idx;
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_ADVANCE_TO_NEXT;
+ }
+
+ public String toString() {
+ return "CLOSE_CLUSTER";
+ }
+ }
+
+ /**
+ * Back-reference
+ */
+
+ public static class OpBackReference extends Operation {
+
+ public int groupNr;
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Get the start and end of the backref
+ int s = matcher.startBackref[groupNr];
+ int e = matcher.endBackref[groupNr];
+
+ // We don't know the backref yet
+ if (s == -1 || e == -1) {
+ return -1;
+ }
+
+ // The backref is empty size
+ if (s == e) {
+ return idx;
+ }
+
+ // Get the length of the backref
+ int l = e - s;
+
+ // If there's not enough input left, give up.
+ UnicodeString search = matcher.search;
+ if (search.isEnd(idx + l - 1)) {
+ return -1;
+ }
+
+ // Case fold the backref?
+ if (matcher.program.flags.isCaseIndependent()) {
+ // Compare backref to input
+ for (int i = 0; i < l; i++) {
+ if (!matcher.equalCaseBlind(search.charAt(idx++), search.charAt(s + i))) {
+ return -1;
+ }
+ }
+ } else {
+ // Compare backref to input
+ for (int i = 0; i < l; i++) {
+ if (search.charAt(idx++) != search.charAt(s + i)) {
+ return -1;
+ }
+ }
+ }
+ return idx;
+ }
+
+ public String toString() {
+ return "BACKREF " + groupNr;
+ }
+ }
+
+ /**
+ * Goto specified instruction
+ */
+
+ public static class OpGoTo extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ return idx;
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_ADVANCE_TO_NEXT;
+ }
+
+ public String toString() {
+ return "GOTO";
+ }
+
+ }
+
+ /**
+ * Match empty string
+ */
+
+ public static class OpNothing extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ return idx;
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_ADVANCE_TO_NEXT;
+ }
+
+ public String toString() {
+ return "NOTHING";
+ }
+ }
+
+ /**
+ * Continue to the following instruction (ignore 'next')
+ */
+
+ public static class OpContinue extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ return idx;
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_ADVANCE_TO_FOLLOWING;
+ }
+
+ public String toString() {
+ return "CONTINUE";
+ }
+ }
+
+ /**
+ * Reluctant star operator
+ */
+
+ public static class OpReluctantStar extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Don't go round in circles...
+ if (matcher.beenHereBefore(idx, node)) {
+ return -1;
+ }
+ // Try to match the rest without using the reluctant subexpr
+
+ int idxNew = matcher.matchNodes(next, idx);
+ if (idxNew != -1) {
+ return idxNew;
+ }
+
+ // Try reluctant subexpr. If it matches:
+ // RELUCTANTMAYBE: Continues matching rest of the expression
+ // RELUCTANTSTAR: Points back here to repeat reluctant star matching
+ return matcher.matchNodes(node + 1, next, idx);
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_RETURN;
+ }
+
+ public String toString() {
+ return "RELUCTANT_STAR";
+ }
+ }
+
+ /**
+ * Reluctant plus operator
+ */
+
+ public static class OpReluctantPlus extends Operation {
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ return matcher.matchNodes(matcher.instructions[next].next, idx);
+ }
+
+ public int nextAction(int idx) {
+ if (idx == -1) {
+ return ACTION_ADVANCE_TO_NEXT;
+ } else {
+ return ACTION_RETURN;
+ }
+ }
+
+ public String toString() {
+ return "RELUCTANT_PLUS";
+ }
+ }
+
+ /**
+ * Reluctant maybe operator
+ */
+
+ public static class OpReluctantMaybe extends Operation {
+
+ // Note: same as ReluctantStar
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Don't go round in circles...
+ if (matcher.beenHereBefore(idx, node)) {
+ return -1;
+ }
+ // Try to match the rest without using the reluctant subexpr
+
+ int idxNew = matcher.matchNodes(next, idx);
+ if (idxNew != -1) {
+ return idxNew;
+ }
+
+ // Try reluctant subexpr. If it matches:
+ // RELUCTANTMAYBE: Continues matching rest of the expression
+ // RELUCTANTSTAR: Points back here to repeat reluctant star matching
+ return matcher.matchNodes(node + 1, next, idx);
+ }
+
+ public int nextAction(int idx) {
+ return ACTION_RETURN;
+ }
+
+ public String toString() {
+ return "RELUCTANT_MAYBE";
+ }
+ }
+
+ /**
+ * Character class: match any one of a set of characters
+ */
+
+ public static class OpCharClass extends Operation {
+ IntPredicate predicate;
+
+ public int exec(REMatcher matcher, int node, int idx) {
+ // Out of input?
+ UnicodeString search = matcher.search;
+ if (search.isEnd(idx)) {
+ return -1;
+ }
+
+ if (!predicate.matches(search.charAt(idx))) {
+ return -1;
+ }
+
+ // Matched.
+ return idx+1;
+ }
+
+ public String toString() {
+ return "CHAR_CLASS (" + predicate.getClass() + ") ";
+ }
+ }
+
+
+
+}
+
diff --git a/sf/saxon/regex/RECompiler.java b/sf/saxon/regex/RECompiler.java
new file mode 100644
index 0000000..d2bbaef
--- /dev/null
+++ b/sf/saxon/regex/RECompiler.java
@@ -0,0 +1,1513 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Originally part of Apache's Jakarta project (downloaded January 2012),
+ * this file has been extensively modified for integration into Saxon by
+ * Michael Kay, Saxonica.
+ */
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.Whitespace;
+import net.sf.saxon.z.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A regular expression compiler class. This class compiles a pattern string into a
+ * regular expression program interpretable by the RE evaluator class. The 'recompile'
+ * command line tool uses this compiler to pre-compile regular expressions for use
+ * with RE. For a description of the syntax accepted by RECompiler and what you can
+ * do with regular expressions, see the documentation for the RE matcher class.
+ *
+ * @author <a href="mailto:jonl at muppetlabs.com">Jonathan Locke</a>
+ * @author <a href="mailto:gholam at xtra.co.nz">Michael McCallum</a>
+ * @version $Id: RECompiler.java 518156 2007-03-14 14:31:26Z vgritsenko $
+ * @see net.sf.saxon.regex.REMatcher
+ */
+
+/*
+ * Changes made for Saxon:
+ *
+ * - handle full Unicode repertoire (esp non-BMP characters) using UnicodeString class for
+ * both the source string and the regular expression
+ * - added support for subtraction in a character class
+ * - in a character range, changed the condition start < end to start <= end
+ * - removed support for [:POSIX:] construct
+ * - added support for \p{} and \P{} classes
+ * - removed support for unsupported escapes: f, x, u, b, octal characters; added i and c
+ * - changed the handling of hyphens within square brackets, and ^ appearing other than at the start
+ * - changed the data structure used for the executable so that terms that match a character class
+ * now reference an IntPredicate that tests for membership of the character in a set
+ * - added support for reluctant {n,m}? quantifiers
+ * - allow a quantifier on a nullable expression
+ * - allow a quantifier on '$' or '^'
+ * - some constructs (back-references, non-capturing groups, etc) are conditional on which XPath/XSD version
+ * is in use
+ * - regular expression flags are now fixed at the time the RE is compiled, this can no longer be deferred
+ * until the RE is evaluated
+ * - split() function includes a zero-length string at the end of the returned sequence if the last
+ * separator is at the end of the string
+ * - added support for the 'q' and 'x' flags; improved support for the 'i' flag
+ * - added a method to determine whether there is an anchored match (for XSD use)
+ * - tests for newline (e.g in multiline mode) now match \n only, as required by the XPath specification
+ * - reorganised the executable program to use Operation objects rather than integer opcodes
+ * - introduced optimization for non-backtracking + and * operators (with simple operands)
+ */
+public class RECompiler {
+ // The compiled program
+ ArrayList<Operation> instructions = new ArrayList<Operation>(20);
+
+ // Input state for compiling regular expression
+ UnicodeString pattern; // Input string
+ int len; // Length of the pattern string
+ int idx; // Current input index into ac
+ int parens; // Total number of paren pairs
+
+ // Node flags
+ static final int NODE_NORMAL = 0; // No flags (nothing special)
+ static final int NODE_NULLABLE = 1; // True if node is potentially null
+ static final int NODE_TOPLEVEL = 2; // True if top level expr
+
+ // {m,n} stacks
+ static final int bracketUnbounded = -1; // Unbounded value
+ int bracketMin; // Minimum number of matches
+ int bracketOpt; // Additional optional matches
+
+ boolean isXPath = true;
+ boolean isXPath30 = true;
+ boolean isXSD11 = false;
+ IntHashSet captures = new IntHashSet();
+
+ REFlags reFlags;
+
+ List<String> warnings;
+
+ /**
+ * Constructor. Creates (initially empty) storage for a regular expression program.
+ */
+ public RECompiler() {
+
+ }
+
+ /**
+ * Set the regular expression flags to be used
+ * @param flags the regular expression flags
+ */
+
+ public void setFlags(REFlags flags) {
+ this.reFlags = flags;
+ isXPath = flags.isAllowsXPath20Extensions();
+ isXPath30 = flags.isAllowsXPath30Extensions();
+ isXSD11 = flags.isAllowsXSD11Syntax();
+ }
+
+
+ private void insertNode(Operation node, int insertAt) {
+ instructions.add(insertAt, node);
+ }
+
+ private void warning(String s) {
+ if (warnings == null) {
+ warnings = new ArrayList<String>(4);
+ }
+ warnings.add(s);
+ }
+
+ /**
+ * On completion of compilation, get any warnings that were generated
+ * @return the list of warning messages
+ */
+
+ public List<String> getWarnings() {
+ if (warnings == null) {
+ return Collections.emptyList();
+ } else {
+ return warnings;
+ }
+ }
+
+ /**
+ * Appends a node to the end of a node chain
+ *
+ * @param node Start of node chain to traverse
+ * @param pointTo Node to have the tail of the chain point to
+ */
+ void setNextOfEnd(int node, int pointTo) {
+ //System.err.println("NEW nextOfEnd " + node + " " + pointTo);
+ // Traverse the chain until the next offset is 0
+ int next = instructions.get(node).next;
+ // while the 'node' is not the last in the chain
+ // and the 'node' is not the last in the program.
+ while (next != 0 && node < instructions.size()) {
+ // if the node we are supposed to point to is in the chain then
+ // point to the end of the program instead.
+ // Michael McCallum <gholam at xtra.co.nz>
+ // FIXME: This is a _hack_ to stop infinite programs.
+ // I believe that the implementation of the reluctant matches is wrong but
+ // have not worked out a better way yet.
+ if (node == pointTo) {
+ pointTo = instructions.size();
+ }
+ node += next;
+ next = instructions.get(node).next;
+ }
+
+ // if we have reached the end of the program then dont set the pointTo.
+ // im not sure if this will break any thing but passes all the tests.
+ if (node < instructions.size()) {
+ int offset = pointTo - node;
+
+ // Point the last node in the chain to pointTo.
+ instructions.get(node).next = offset;
+ }
+ }
+
+ /**
+ * Throws a new internal error exception
+ *
+ * @throws Error Thrown in the event of an internal error.
+ */
+ void internalError() throws Error {
+ throw new Error("Internal error!");
+ }
+
+ /**
+ * Throws a new syntax error exception
+ * @param s the error message
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ */
+ void syntaxError(String s) throws RESyntaxException {
+ throw new RESyntaxException(s, idx);
+ }
+
+ /**
+ * Match bracket {m,n} expression put results in bracket member variables
+ *
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ */
+ void bracket() throws RESyntaxException {
+ // Current character must be a '{'
+ if (idx >= len || pattern.charAt(idx++) != '{') {
+ internalError();
+ }
+
+ // Next char must be a digit
+ if (idx >= len || !isAsciiDigit(pattern.charAt(idx))) {
+ syntaxError("Expected digit");
+ }
+
+ // Get min ('m' of {m,n}) number
+ StringBuffer number = new StringBuffer();
+ while (idx < len && isAsciiDigit(pattern.charAt(idx))) {
+ number.append((char)pattern.charAt(idx++));
+ }
+ try {
+ bracketMin = Integer.parseInt(number.toString());
+ } catch (NumberFormatException e) {
+ syntaxError("Expected valid number");
+ }
+
+ // If out of input, fail
+ if (idx >= len) {
+ syntaxError("Expected comma or right bracket");
+ }
+
+ // If end of expr, optional limit is 0
+ if (pattern.charAt(idx) == '}') {
+ idx++;
+ bracketOpt = 0;
+ return;
+ }
+
+ // Must have at least {m,} and maybe {m,n}.
+ if (idx >= len || pattern.charAt(idx++) != ',') {
+ syntaxError("Expected comma");
+ }
+
+ // If out of input, fail
+ if (idx >= len) {
+ syntaxError("Expected comma or right bracket");
+ }
+
+ // If {m,} max is unlimited
+ if (pattern.charAt(idx) == '}') {
+ idx++;
+ bracketOpt = bracketUnbounded;
+ return;
+ }
+
+ // Next char must be a digit
+ if (idx >= len || !isAsciiDigit(pattern.charAt(idx))) {
+ syntaxError("Expected digit");
+ }
+
+ // Get max number
+ number.setLength(0);
+ while (idx < len && isAsciiDigit(pattern.charAt(idx))) {
+ number.append((char)pattern.charAt(idx++));
+ }
+ try {
+ bracketOpt = Integer.parseInt(number.toString()) - bracketMin;
+ } catch (NumberFormatException e) {
+ syntaxError("Expected valid number");
+ }
+
+ // Optional repetitions must be >= 0
+ if (bracketOpt < 0) {
+ syntaxError("Bad range");
+ }
+
+ // Must have close brace
+ if (idx >= len || pattern.charAt(idx++) != '}') {
+ syntaxError("Missing close brace");
+ }
+ }
+
+ /**
+ * Test whether a character is an ASCII decimal digit
+ * @param ch the character to be matched
+ * @return true if the character is an ASCII digit (0-9)
+ */
+
+ private static boolean isAsciiDigit(int ch) {
+ return ch >= '0' && ch <= '9';
+ }
+
+ /**
+ * Match an escape sequence. Handles quoted chars and octal escapes as well
+ * as normal escape characters. Always advances the input stream by the
+ * right amount. This code "understands" the subtle difference between an
+ * octal escape and a backref. You can access the type of ESC_CLASS or
+ * ESC_COMPLEX or ESC_BACKREF by looking at pattern[idx - 1].
+ *
+ * @return an IntPredicate that matches the character or characters represented
+ * by this escape sequence. For a single-character escape this must be an IntValuePredicate
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ */
+ IntPredicate escape(boolean inSquareBrackets) throws RESyntaxException {
+ // "Shouldn't" happen
+ if (pattern.charAt(idx) != '\\') {
+ internalError();
+ }
+
+ // Escape shouldn't occur as last character in string!
+ if (idx + 1 == len) {
+ syntaxError("Escape terminates string");
+ }
+
+ // Switch on character after backslash
+ idx += 2;
+ int escapeChar = pattern.charAt(idx - 1);
+ switch (escapeChar) {
+
+ case 'n':
+ return new IntValuePredicate('\n');
+ case 'r':
+ return new IntValuePredicate('\r');
+ case 't':
+ return new IntValuePredicate('\t');
+
+ case '\\':
+ case '|':
+ case '.':
+ case '-':
+ case '^':
+ case '?':
+ case '*':
+ case '+':
+ case '{':
+ case '}':
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ return new IntValuePredicate(escapeChar);
+
+ case '$':
+ if (isXPath) {
+ return new IntValuePredicate(escapeChar);
+ } else {
+ syntaxError("In XSD, '$' must not be escaped");
+ }
+
+ case 's':
+ return Categories.ESCAPE_s;
+
+ case 'S':
+ return Categories.ESCAPE_S;
+
+ case 'i':
+ return Categories.ESCAPE_i;
+
+ case 'I':
+ return Categories.ESCAPE_I;
+
+ case 'c':
+ return Categories.ESCAPE_c;
+
+ case 'C':
+ return Categories.ESCAPE_C;
+
+ case 'd':
+ return Categories.ESCAPE_d;
+
+ case 'D':
+ return Categories.ESCAPE_D;
+
+ case 'w':
+ return Categories.ESCAPE_w;
+
+ case 'W':
+ return Categories.ESCAPE_W;
+
+
+ case 'p':
+ case 'P':
+
+ if (idx == len) {
+ syntaxError("Expected '{' after \\" + escapeChar);
+ }
+ if (pattern.charAt(idx) != '{') {
+ syntaxError("Expected '{' after \\" + escapeChar);
+ }
+ int close = pattern.indexOf('}', idx++);
+ if (close == -1) {
+ syntaxError("No closing '}' after \\" + escapeChar);
+ }
+ UnicodeString block = pattern.substring(idx, close);
+ if (block.length() == 1 || block.length() == 2) {
+ IntPredicate primary = Categories.getCategory(block.toString());
+ if (primary == null) {
+ syntaxError("Unknown character category " + block.toString());
+ }
+ idx = close+1;
+ if (escapeChar == 'p') {
+ return primary;
+ } else {
+ return makeComplement(primary);
+ }
+ } else if (block.toString().startsWith("Is")) {
+ String blockName = block.toString().substring(2);
+ IntSet uniBlock = UnicodeBlocks.getBlock(blockName);
+ if (uniBlock == null) {
+ // XSD 1.1 says this is not an error, but by default we reject it
+ if (reFlags.isAllowUnknownBlockNames()) {
+ warning("Unknown Unicode block: " + blockName);
+ idx = close+1;
+ return new IntSetPredicate(IntUniversalSet.getInstance());
+ } else {
+ syntaxError("Unknown Unicode block: " + blockName);
+ }
+ }
+ idx = close+1;
+ IntPredicate primary = new IntSetPredicate(uniBlock);
+ if (escapeChar == 'p') {
+ return primary;
+ } else {
+ return makeComplement(primary);
+ }
+ } else {
+ syntaxError("Unknown character category: " + block);
+ }
+
+ case '0':
+ syntaxError("Octal escapes not allowed");
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+
+ if (inSquareBrackets) {
+ syntaxError("Backreference not allowed within character class");
+ } else if (isXPath) {
+ int backRef = (escapeChar - '0');
+ while (idx < len) {
+ int c1 = "0123456789".indexOf(pattern.charAt(idx));
+ if (c1 < 0) {
+ break;
+ } else {
+ int backRef2 = backRef * 10 + c1;
+ if (backRef2 > (parens - 1)) {
+ break;
+ } else {
+ backRef = backRef2;
+ idx++;
+ }
+ }
+
+ }
+ if (!captures.contains(backRef)) {
+ String explanation = (backRef > (parens - 1) ? "(no such group)" : "(group not yet closed)");
+ syntaxError("invalid backreference \\" + backRef + " " + explanation);
+ }
+ return new BackReference(backRef);
+ } else {
+ syntaxError("digit not allowed after \\");
+ }
+
+ default:
+
+ // Other characters not allowed in XSD regexes
+ syntaxError("Escape character '" + (char)escapeChar + "' not allowed");
+ }
+ return null;
+ }
+
+ /**
+ * For convenience a back-reference is treated as an IntPredicate, although this a fiction
+ */
+
+ class BackReference extends IntValuePredicate {
+ public BackReference(int number) {
+ super(number);
+ }
+ }
+
+
+ /**
+ * Compile a character class (in square brackets)
+ *
+ * @return an IntPredicate that tests whether a character matches this character class
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ */
+ IntPredicate parseCharacterClass() throws RESyntaxException {
+ // Check for bad calling or empty class
+ if (pattern.charAt(idx) != '[') {
+ internalError();
+ }
+
+ // Check for unterminated or empty class
+ if ((idx + 1) >= len || pattern.charAt(++idx) == ']') {
+ syntaxError("Missing ']'");
+ }
+
+ // Parse class declaration
+ int simpleChar;
+ boolean positive = true;
+ boolean definingRange = false;
+ int rangeStart = -1;
+ int rangeEnd;
+ IntRangeSet range = new IntRangeSet();
+ IntPredicate addend = null;
+ IntPredicate subtrahend = null;
+ if (thereFollows("^")) {
+ if (thereFollows("^-[")) {
+ syntaxError("Nothing before subtraction operator");
+ } else if (thereFollows("^]")) {
+ syntaxError("Empty negative character group");
+ } else {
+ positive = false;
+ idx++;
+ }
+ } else if (thereFollows("-[")) {
+ syntaxError("Nothing before subtraction operator");
+ }
+ while (idx < len && pattern.charAt(idx) != ']') {
+ int ch = pattern.charAt(idx);
+ simpleChar = -1;
+ switch (ch) {
+ case '[':
+ syntaxError("Unescaped '[' within square brackets");
+ break;
+ case '\\': {
+ // Escape always advances the stream
+ IntPredicate cc = escape(true);
+ if (cc instanceof IntValuePredicate) {
+ simpleChar = ((IntValuePredicate) cc).getTarget();
+ break;
+ } else {
+ if (definingRange) {
+ syntaxError("Multi-character escape cannot follow '-'");
+ } else if (addend == null) {
+ addend = cc;
+ } else {
+ addend = makeUnion(addend, cc);
+ }
+ continue;
+ }
+ }
+ case '-':
+ if (thereFollows("-[")) {
+ idx++;
+ subtrahend = parseCharacterClass();
+ if (!thereFollows("]")) {
+ syntaxError("Expected closing ']' after subtraction");
+ }
+ } else if (thereFollows("-]")) {
+ simpleChar = '-';
+ idx++;
+ } else if (rangeStart >= 0) {
+ definingRange = true;
+ idx++;
+ continue;
+ } else if (definingRange) {
+ syntaxError("Bad range");
+ } else if (thereFollows("--") && !thereFollows("--[")) {
+ syntaxError("Unescaped hyphen as start of range");
+ } else if (!isXSD11 && pattern.charAt(idx-1) != '[' && pattern.charAt(idx-1) != '^' && !thereFollows("]") && !thereFollows("-[")) {
+ syntaxError("In XSD 1.0, hyphen is allowed only at the beginning or end of a positive character group");
+ } else {
+ simpleChar = '-';
+ idx++;
+ }
+ break;
+
+ default:
+ simpleChar = ch;
+ idx++;
+ break;
+ }
+
+ // Handle simple character simpleChar
+ if (definingRange) {
+ // if we are defining a range make it now
+ rangeEnd = simpleChar;
+
+ // Actually create a range if the range is ok
+ if (rangeStart > rangeEnd) {
+ syntaxError("Bad character range: start > end");
+ // TODO: not an error in XSD, merely a no-op?
+ }
+ range.addRange(rangeStart, rangeEnd);
+ if (reFlags.isCaseIndependent()) {
+ // Special-case A-Z and a-z
+ if (rangeStart == 'a' && rangeEnd == 'z') {
+ range.addRange('A', 'Z');
+ for (int v=0; v<CaseVariants.ROMAN_VARIANTS.length; v++) {
+ range.add(CaseVariants.ROMAN_VARIANTS[v]);
+ }
+ } else if (rangeStart == 'A' && rangeEnd == 'Z') {
+ range.addRange('a', 'z');
+ for (int v=0; v<CaseVariants.ROMAN_VARIANTS.length; v++) {
+ range.add(CaseVariants.ROMAN_VARIANTS[v]);
+ }
+ } else {
+ for (int k = rangeStart; k <= rangeEnd; k++) {
+ int[] variants = CaseVariants.getCaseVariants(k);
+ for (int variant : variants) {
+ range.add(variant);
+ }
+ }
+ }
+ }
+
+ // We are done defining the range
+ definingRange = false;
+ rangeStart = -1;
+ } else {
+ // If simple character and not start of range, include it (see XSD 1.1 rules)
+ if (thereFollows("-")) {
+ if (thereFollows("-[")) {
+ range.add(simpleChar);
+ } else if (thereFollows("-]")) {
+ range.add(simpleChar);
+ } else if (thereFollows("--[")) {
+ range.add(simpleChar);
+ } else if (thereFollows("--")) {
+ syntaxError("Unescaped hyphen cannot act as end of range");
+ } else {
+ rangeStart = simpleChar;
+ }
+ } else {
+ range.add(simpleChar);
+ if (reFlags.isCaseIndependent()) {
+ int[] variants = CaseVariants.getCaseVariants(simpleChar);
+ for (int variant : variants) {
+ range.add(variant);
+ }
+ }
+ }
+ }
+ }
+
+ // Shouldn't be out of input
+ if (idx == len) {
+ syntaxError("Unterminated character class");
+ }
+
+ // Absorb the ']' end of class marker
+ idx++;
+ IntPredicate result = new IntSetPredicate(range);
+ if (addend != null) {
+ result = makeUnion(result, addend);
+ }
+ if (!positive) {
+ result = makeComplement(result);
+ }
+ if (subtrahend != null) {
+ result = makeDifference(result, subtrahend);
+ }
+ return result;
+ }
+
+ /**
+ * Test whether the string starting at the current position is equal to some specified string
+ * @param s the string being tested
+ * @return true if the specified string is present
+ */
+
+ private boolean thereFollows(String s) {
+ return idx + s.length() <= len &&
+ (pattern.substring(idx, idx + s.length()).toString().equals(s));
+ }
+
+ /**
+ * Make the union of two IntPredicates (matches if p1 matches or p2 matches)
+ * @param p1 the first
+ * @param p2 the second
+ * @return the result
+ */
+
+ private IntPredicate makeUnion(IntPredicate p1, IntPredicate p2) {
+ if (p1 instanceof IntSetPredicate && ((IntSetPredicate)p1).getIntSet(). isEmpty()) {
+ return p2;
+ }
+ if (p2 instanceof IntSetPredicate && ((IntSetPredicate)p2).getIntSet(). isEmpty()) {
+ return p1;
+ }
+ return new IntUnionPredicate(p1, p2);
+ }
+
+ /**
+ * Make the difference of two IntPredicates (matches if p1 matches and p2 does not match)
+ * @param p1 the first
+ * @param p2 the second
+ * @return the result
+ */
+
+ private IntPredicate makeDifference(IntPredicate p1, IntPredicate p2) {
+ return new IntExceptPredicate(p1, p2);
+ }
+
+ /**
+ * Make the complement of an IntPredicate (matches if p1 does not match)
+ * @param p1 the operand
+ * @return the result
+ */
+
+ private IntPredicate makeComplement(IntPredicate p1) {
+ if (p1 instanceof IntComplementPredicate) {
+ return ((IntComplementPredicate)p1).getOperand();
+ } else {
+ return new IntComplementPredicate(p1);
+ }
+ }
+
+ private int emitCharacterClass(IntPredicate range) {
+ Operation.OpCharClass node = new Operation.OpCharClass();
+ node.predicate = range;
+ return appendNode(node);
+ }
+
+ /**
+ * Absorb an atomic character string. This method is a little tricky because
+ * it can un-include the last character of string if a quantifier operator follows.
+ * This is correct because *+? have higher precedence than concatentation (thus
+ * ABC* means AB(C*) and NOT (ABC)*).
+ *
+ * @return Index of new atom node
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ */
+ int atom() throws RESyntaxException {
+ // Create a string node
+ Operation.OpAtom node = new Operation.OpAtom();
+
+ // Length of atom
+ int lenAtom = 0;
+
+ // Loop while we've got input
+
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+
+ atomLoop:
+
+ while (idx < len) {
+ // Is there a next char?
+ if ((idx + 1) < len) {
+ int c = pattern.charAt(idx + 1);
+
+ // If the next 'char' is an escape, look past the whole escape
+ if (pattern.charAt(idx) == '\\') {
+ int idxEscape = idx;
+ escape(false);
+ if (idx < len) {
+ c = pattern.charAt(idx);
+ }
+ idx = idxEscape;
+ }
+
+ // Switch on next char
+ switch (c) {
+ case '{':
+ case '?':
+ case '*':
+ case '+':
+
+ // If the next character is a quantifier operator and our atom is non-empty, the
+ // current character should bind to the quantifier operator rather than the atom
+ if (lenAtom != 0) {
+ break atomLoop;
+ }
+ }
+ }
+
+ // Switch on current char
+ switch (pattern.charAt(idx)) {
+ case ']':
+ case '.':
+ case '[':
+ case '(':
+ case ')':
+ case '|':
+ break atomLoop;
+
+ case '{':
+ case '?':
+ case '*':
+ case '+':
+
+ // We should have an atom by now
+ if (lenAtom == 0) {
+ // No atom before quantifier
+ syntaxError("No expression before quantifier");
+ }
+ break atomLoop;
+
+ case '\\': {
+ // Get the escaped character (advances input automatically)
+ int idxBeforeEscape = idx;
+ IntPredicate charClass = escape(false);
+
+ // Check if it's a simple escape (as opposed to, say, a backreference)
+ if (charClass instanceof BackReference || !(charClass instanceof IntValuePredicate)) {
+ // Not a simple escape, so backup to where we were before the escape.
+ idx = idxBeforeEscape;
+ break atomLoop;
+ }
+
+ // Add escaped char to atom
+ fsb.appendWideChar(((IntValuePredicate) charClass).getTarget());
+ lenAtom++;
+ break;
+ }
+
+ case '^':
+ case '$':
+ if (isXPath) {
+ break atomLoop;
+ }
+ // else fall through ($ is not a metacharacter in XSD)
+
+ default:
+
+ // Add normal character to atom
+ fsb.appendWideChar(pattern.charAt(idx++));
+ lenAtom++;
+ break;
+ }
+ }
+
+ // This shouldn't happen
+ if (fsb.length() == 0) {
+ internalError();
+ }
+
+ // Emit the instruction into the program
+ node.atom = UnicodeString.makeUnicodeString(fsb.condense());
+ return appendNode(node);
+ }
+
+ private int appendNode(Operation node) {
+ instructions.add(node);
+ return instructions.size()-1;
+ }
+
+
+ /**
+ * Match a terminal node.
+ *
+ * @param flags Flags
+ * @return Index of terminal node (closeable)
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ */
+ int terminal(int[] flags) throws RESyntaxException {
+ switch (pattern.charAt(idx)) {
+ case '$':
+ if (isXPath) {
+ flags[0] |= NODE_NULLABLE;
+ idx++;
+ Operation.OpEOL eol = new Operation.OpEOL();
+ return appendNode(eol);
+ }
+ break;
+
+ case '^':
+ if (isXPath) {
+ flags[0] |= NODE_NULLABLE;
+ idx++;
+ Operation.OpBOL bol = new Operation.OpBOL();
+ return appendNode(bol);
+ }
+ break;
+
+ case '.':
+ idx++;
+ IntPredicate predicate;
+ if (reFlags.isSingleLine()) {
+ // in XPath with the 's' flag, '.' matches everything
+ predicate = new IntPredicate() {
+ public boolean matches(int value) {
+ return true;
+ }
+ };
+ } else {
+ // in XSD, "." matches everything except \n and \r. See also bug 15594.
+ predicate = new IntPredicate() {
+ public boolean matches(int value) {
+ return (value != '\n' && value != '\r');
+ }
+ };
+ }
+ Operation.OpCharClass dot = new Operation.OpCharClass();
+ dot.predicate = predicate;
+ return appendNode(dot);
+
+ case '[':
+ IntPredicate range = parseCharacterClass();
+ Operation.OpCharClass cc = new Operation.OpCharClass();
+ cc.predicate = range;
+ return appendNode(cc);
+
+ case '(':
+ return expr(flags);
+
+ case ')':
+ syntaxError("Unexpected close paren");
+
+ case '|':
+ internalError();
+
+ case ']':
+ syntaxError("Mismatched class");
+
+ case 0:
+ syntaxError("Unexpected end of input");
+
+ case '?':
+ case '+':
+ case '{':
+ case '*':
+ syntaxError("No expression before quantifier");
+
+ case '\\': {
+ // Don't forget, escape() advances the input stream!
+ int idxBeforeEscape = idx;
+
+ IntPredicate esc = escape(false);
+
+ if (esc instanceof BackReference) {
+ int backreference = ((BackReference)esc).getTarget();
+ if (parens <= backreference) {
+ syntaxError("Bad backreference");
+ }
+ flags[0] |= NODE_NULLABLE;
+ Operation.OpBackReference back = new Operation.OpBackReference();
+ back.groupNr = backreference;
+ return appendNode(back);
+
+ } else if (esc instanceof IntSingletonSet) {
+ // We had a simple escape and we want to have it end up in
+ // an atom, so we back up and fall though to the default handling
+ idx = idxBeforeEscape;
+ flags[0] &= ~NODE_NULLABLE;
+
+ } else {
+
+ flags[0] &= ~NODE_NULLABLE;
+ return emitCharacterClass(esc);
+ //return node(RE.OP_ESCAPE, pattern.charAt(idx - 1));
+ }
+
+ }
+ }
+
+ // Everything above either fails or returns.
+ // If it wasn't one of the above, it must be the start of an atom.
+ flags[0] &= ~NODE_NULLABLE;
+ return atom();
+ }
+
+ /**
+ * Compile a piece consisting of an atom and optional quantifier
+ *
+ * @param flags Flags passed by reference
+ * @return Index of resulting instruction
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ */
+ int piece(int[] flags) throws RESyntaxException {
+ // Before terminal
+ int idxBeforeTerminal = idx;
+
+ // Values to pass by reference to terminal()
+ int[] terminalFlags = {NODE_NORMAL};
+
+ // Get terminal symbol
+ int ret = terminal(terminalFlags);
+
+ // Or in flags from terminal symbol
+ flags[0] |= terminalFlags[0];
+
+ // Advance input, set NODE_NULLABLE flag and do sanity checks
+ if (idx >= len) {
+ return ret;
+ }
+
+ boolean greedy = true;
+ int quantifierType = pattern.charAt(idx);
+ switch (quantifierType) {
+ case '?':
+ case '*':
+
+ // The current node can be null
+ flags[0] |= NODE_NULLABLE;
+
+ // Drop through
+
+ case '+':
+
+ // Eat quantifier character
+ idx++;
+
+ // Drop through
+
+ case '{':
+
+ if (quantifierType == '{') {
+ bracket();
+ if (bracketMin == 0) {
+ flags[0] |= NODE_NULLABLE;
+ }
+ }
+
+
+
+ Operation op = instructions.get(ret);
+ if (op instanceof Operation.OpBOL || op instanceof Operation.OpEOL) {
+ // Pretty meaningless, but legal. If the quantifier allows zero occurrences, ignore the instruction.
+ // Otherwise, ignore the quantifier
+ if (quantifierType == '?' || quantifierType == '*' ||
+ (quantifierType == '{' && bracketMin == 0)) {
+ instructions.set(ret, new Operation.OpNothing());
+ } else {
+ quantifierType = 0;
+ }
+ }
+ if ((terminalFlags[0] & NODE_NULLABLE) != 0) {
+ if (quantifierType == '?') {
+ // can ignore the quantifier
+ quantifierType = 0;
+ } else if (quantifierType == '+') {
+ // '*' and '+' are equivalent
+ quantifierType = '*';
+ } else if (quantifierType == '{') {
+ // bounds are meaningless
+ quantifierType = '*';
+ }
+ }
+
+ }
+
+ // If the next character is a '?', make the quantifier non-greedy (reluctant)
+ if (idx < len && pattern.charAt(idx) == '?') {
+ if (!isXPath) {
+ syntaxError("Reluctant quantifiers are not allowed in XSD");
+ }
+ idx++;
+ greedy = false;
+ }
+
+ if (greedy) {
+ // Actually do the quantifier now
+ switch (quantifierType) {
+ case '{': {
+ //bracket();
+ int bracketEnd = idx;
+ int bracketMin = this.bracketMin;
+ int bracketOpt = this.bracketOpt;
+
+ // Pointer to the last terminal
+ int pos = ret;
+
+ // Process min first
+ for (int c = 0; c < bracketMin; c++) {
+ // Rewind stream and run it through again - more matchers coming
+ idx = idxBeforeTerminal;
+ setNextOfEnd(pos, pos = terminal(terminalFlags));
+ }
+
+ // Do the right thing for maximum ({m,})
+ if (bracketOpt == bracketUnbounded) {
+ // Drop through now and quantifier expression.
+ // We are done with the {m,} expr, so skip rest
+ idx = bracketEnd;
+ Operation.OpStar op = new Operation.OpStar();
+ insertNode(op, pos);
+ setNextOfEnd(pos + 1, pos);
+ break;
+ } else if (bracketOpt > 0) {
+ int opt[] = new int[bracketOpt + 1];
+ // Surround first optional terminal with MAYBE
+ Operation.OpMaybe op = new Operation.OpMaybe();
+ insertNode(op, pos);
+ opt[0] = pos;
+
+ // Add all the rest optional terminals with preceding MAYBEs
+ for (int c = 1; c < bracketOpt; c++) {
+ op = new Operation.OpMaybe();
+ opt[c] = appendNode(op);
+ // Rewind stream and run it through again - more matchers coming
+ idx = idxBeforeTerminal;
+ terminal(terminalFlags);
+ }
+
+ // Tie ends together
+ int end = opt[bracketOpt] = appendNode(new Operation.OpNothing());
+ for (int c = 0; c < bracketOpt; c++) {
+ setNextOfEnd(opt[c], end);
+ setNextOfEnd(opt[c] + 1, opt[c + 1]);
+ }
+ } else {
+ // Rollback terminal - no opt matchers present
+ //lenInstruction = pos;
+ while (instructions.size() > pos) {
+ instructions.remove(instructions.size()-1);
+ }
+ Operation.OpNothing nothing = new Operation.OpNothing();
+ appendNode(nothing);
+ }
+
+ // We are done. skip the reminder of {m,n} expr
+ idx = bracketEnd;
+ break;
+ }
+
+ case '?': {
+ Operation.OpMaybe maybe = new Operation.OpMaybe();
+ insertNode(maybe, ret);
+ Operation.OpNothing nothing = new Operation.OpNothing();
+ int n = appendNode(nothing);
+ setNextOfEnd(ret, n);
+ setNextOfEnd(ret + 1, n);
+ break;
+ }
+
+ case '*': {
+ Operation.OpStar star = new Operation.OpStar();
+ insertNode(star, ret);
+ setNextOfEnd(ret + 1, ret);
+ break;
+ }
+
+ case '+': {
+ Operation.OpContinue continu = new Operation.OpContinue();
+ insertNode(continu, ret);
+ Operation.OpPlus plus = new Operation.OpPlus();
+ int n = appendNode(plus);
+ setNextOfEnd(ret + 1, n);
+ setNextOfEnd(n, ret);
+ break;
+ }
+ }
+ } else {
+ // Not greedy (reluctant): Actually do the quantifier now
+ switch (quantifierType) {
+ case '?': {
+ Operation.OpReluctantMaybe reluctantMaybe = new Operation.OpReluctantMaybe();
+ insertNode(reluctantMaybe, ret);
+ //nodeInsert(RE.OP_RELUCTANTMAYBE, 0, ret);
+ int n = appendNode(new Operation.OpNothing());
+ //int n = node(RE.OP_NOTHING, 0);
+ setNextOfEnd(ret, n);
+ setNextOfEnd(ret + 1, n);
+ break;
+ }
+
+ case '*': {
+ Operation.OpReluctantStar reluctantStar = new Operation.OpReluctantStar();
+ insertNode(reluctantStar, ret);
+ setNextOfEnd(ret + 1, ret);
+ break;
+ }
+
+ case '+': {
+ insertNode(new Operation.OpContinue(), ret);
+ //nodeInsert(RE.OP_CONTINUE, 0, ret);
+ int n = appendNode(new Operation.OpReluctantPlus());
+ //int n = node(RE.OP_RELUCTANTPLUS, 0);
+ setNextOfEnd(n, ret);
+ setNextOfEnd(ret + 1, n);
+ break;
+ }
+
+ case '{': {
+ // reluctant {..}? - added by MHK
+ //bracket();
+ int bracketEnd = idx;
+ int bracketMin = this.bracketMin;
+ int bracketOpt = this.bracketOpt;
+
+ // Pointer to the last terminal
+ int pos = ret;
+
+ // Process min first
+ for (int c = 0; c < bracketMin; c++) {
+ // Rewind stream and run it through again - more matchers coming
+ idx = idxBeforeTerminal;
+ setNextOfEnd(pos, pos = terminal(terminalFlags));
+ }
+
+ // Do the right thing for maximum ({m,})
+ if (bracketOpt == bracketUnbounded) {
+ // Drop through now and quantifier expression.
+ // We are done with the {m,} expr, so skip rest
+ idx = bracketEnd;
+ insertNode(new Operation.OpReluctantStar(), pos);
+ //nodeInsert(RE.OP_RELUCTANTSTAR, 0, pos);
+ setNextOfEnd(pos + 1, pos);
+ break;
+ } else if (bracketOpt > 0) {
+ int opt[] = new int[bracketOpt + 1];
+ // Surround first optional terminal with MAYBE
+ insertNode(new Operation.OpReluctantMaybe(), pos);
+ //nodeInsert(RE.OP_RELUCTANTMAYBE, 0, pos);
+ opt[0] = pos;
+
+ // Add all the rest optional terminals with preceeding MAYBEs
+ for (int c = 1; c < bracketOpt; c++) {
+ opt[c] = appendNode(new Operation.OpReluctantMaybe());
+ //opt[c] = node(RE.OP_RELUCTANTMAYBE, 0);
+ // Rewind stream and run it through again - more matchers coming
+ idx = idxBeforeTerminal;
+ terminal(terminalFlags);
+ }
+
+ // Tie ends together
+ int end = opt[bracketOpt] = appendNode(new Operation.OpNothing());
+ for (int c = 0; c < bracketOpt; c++) {
+ setNextOfEnd(opt[c], end);
+ setNextOfEnd(opt[c] + 1, opt[c + 1]);
+ }
+ } else {
+ // Rollback terminal - no opt matchers present
+ while (instructions.size() > pos) {
+ instructions.remove(instructions.size() - 1);
+ }
+ appendNode(new Operation.OpNothing());
+ }
+
+ // We are done. skip the reminder of {m,n} expr
+ idx = bracketEnd;
+ break;
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Compile body of one branch of an or operator (implements concatenation)
+ *
+ * @param compilerFlags Flags passed by reference
+ * @return Pointer to first node in the branch
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ */
+ int branch(int[] compilerFlags) throws RESyntaxException {
+ // Get each possibly qnatified piece and concat
+ int node;
+ int ret = -1;
+ int chain = -1;
+ int[] quantifierFlags = new int[1];
+ boolean nullable = true;
+ while (idx < len && pattern.charAt(idx) != '|' && pattern.charAt(idx) != ')') {
+ // Get new node
+ quantifierFlags[0] = NODE_NORMAL;
+ node = piece(quantifierFlags);
+ if (quantifierFlags[0] == NODE_NORMAL) {
+ nullable = false;
+ }
+
+ // If there's a chain, append to the end
+ if (chain != -1) {
+ setNextOfEnd(chain, node);
+ }
+
+ // Chain starts at current
+ chain = node;
+ if (ret == -1) {
+ ret = node;
+ }
+ }
+
+ // If we don't run loop, make a nothing node
+ if (ret == -1) {
+ Operation nothing = new Operation.OpNothing();
+ ret = appendNode(nothing);
+ }
+
+ // Set nullable flag for this branch
+ if (nullable) {
+ compilerFlags[0] |= NODE_NULLABLE;
+ }
+
+ return ret;
+ }
+
+ /**
+ * Compile an expression with possible parens around it. Paren matching
+ * is done at this level so we can tie the branch tails together.
+ *
+ * @param compilerFlags Flag value passed by reference
+ * @return Node index of expression in instruction array
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ */
+ int expr(int[] compilerFlags) throws RESyntaxException {
+ // Create open paren node unless we were called from the top level (which has no parens)
+ int paren = -1;
+ int ret = -1;
+ int closeParens = parens;
+ if ((compilerFlags[0] & NODE_TOPLEVEL) == 0 && pattern.charAt(idx) == '(') {
+ // if its a cluster ( rather than a proper subexpression ie with backrefs )
+ if (idx + 2 < len && pattern.charAt(idx + 1) == '?' && pattern.charAt(idx + 2) == ':') {
+ if (!isXPath30) {
+ syntaxError("Non-capturing groups allowed only in XPath3.0");
+ }
+ paren = 2;
+ idx += 3;
+ ret = appendNode(new Operation.OpOpenCluster());
+ } else {
+ paren = 1;
+ idx++;
+ ret = appendNode(new Operation.OpOpen(parens++));
+ }
+ }
+ compilerFlags[0] &= ~NODE_TOPLEVEL;
+
+ // Process contents of first branch node
+ boolean open = false;
+ int branch = branch(compilerFlags);
+ if (ret == -1) {
+ ret = branch;
+ } else {
+ setNextOfEnd(ret, branch);
+ }
+
+ // Loop through branches
+ while (idx < len && pattern.charAt(idx) == '|') {
+ // Now open the first branch since there are more than one
+ if (!open) {
+ Operation.OpBranch op = new Operation.OpBranch();
+ insertNode(op, branch);
+ open = true;
+ }
+
+ idx++;
+ setNextOfEnd(branch, branch = appendNode(new Operation.OpBranch()));
+ branch(compilerFlags);
+ }
+
+ // Create an ending node (either a close paren or an OP_END)
+ int end;
+ if (paren > 0) {
+ if (idx < len && pattern.charAt(idx) == ')') {
+ idx++;
+ } else {
+ syntaxError("Missing close paren");
+ }
+ if (paren == 1) {
+ end = appendNode(new Operation.OpClose(closeParens));
+ captures.add(closeParens);
+ } else {
+ end = appendNode(new Operation.OpCloseCluster());
+ }
+ } else {
+ end = appendNode(new Operation.OpEndProgram());
+ }
+
+ // Append the ending node to the ret nodelist
+ setNextOfEnd(ret, end);
+
+ // Hook the ends of each branch to the end node
+ int currentNode = ret;
+ int nextNodeOffset = instructions.get(currentNode).next;
+ // while the next node o
+ while (nextNodeOffset != 0 && currentNode < instructions.size()) {
+ // If branch, make the end of the branch's operand chain point to the end node.
+ if (instructions.get(currentNode) instanceof Operation.OpBranch) {
+ setNextOfEnd(currentNode + 1, end);
+ }
+ nextNodeOffset = instructions.get(currentNode).next;
+ currentNode += nextNodeOffset;
+ }
+
+ // Return the node list
+ return ret;
+ }
+
+ /**
+ * Compiles a regular expression pattern into a program runnable by the pattern
+ * matcher class 'RE'.
+ *
+ * @param pattern Regular expression pattern to compile (see RECompiler class
+ * for details).
+ * @return A compiled regular expression program.
+ * @throws net.sf.saxon.regex.RESyntaxException Thrown if the regular expression has invalid syntax.
+ * @see RECompiler
+ * @see net.sf.saxon.regex.REMatcher
+ */
+ public REProgram compile(UnicodeString pattern) throws RESyntaxException {
+ // Initialize variables for compilation
+ this.pattern = pattern; // Save pattern in instance variable
+ len = pattern.length(); // Precompute pattern length for speed
+ idx = 0; // Set parsing index to the first character
+ parens = 1; // Set paren level to 1 (the implicit outer parens)
+ boolean nullable = false;
+
+ if (reFlags.isLiteral()) {
+
+ // 'q' flag is set
+ int ret = literalAtom();
+ Operation.OpEndProgram endNode = new Operation.OpEndProgram();
+ int end = appendNode(endNode);
+ setNextOfEnd(ret, end);
+
+ } else {
+
+ if (reFlags.isAllowWhitespace()) {
+ // 'x' flag is set. Preprocess the expression to strip whitespace, other than between
+ // square brackets
+ FastStringBuffer sb = new FastStringBuffer(pattern.length());
+ int nesting = 0;
+ boolean astral = false;
+ boolean escaped = false;
+ for (int i=0; i<pattern.length(); i++) {
+ int ch = pattern.charAt(i);
+ if (ch > 65535) {
+ astral = true;
+ }
+ if (ch == '\\' && !escaped) {
+ escaped = true;
+ sb.appendWideChar(ch);
+ } else if (ch == '[' && !escaped) {
+ nesting++;
+ escaped = false;
+ sb.appendWideChar(ch);
+ } else if (ch == ']' && !escaped) {
+ nesting--;
+ escaped = false;
+ sb.appendWideChar(ch);
+ } else if (nesting==0 && Whitespace.isWhitespace(ch)) {
+ // no action
+ } else {
+ escaped = false;
+ sb.appendWideChar(ch);
+ }
+ }
+ if (astral) {
+ pattern = new GeneralUnicodeString(sb);
+ } else {
+ pattern = new BMPString(sb);
+ }
+ this.pattern = pattern;
+ this.len = pattern.length();
+ }
+
+ // Initialize pass by reference flags value
+ int[] compilerFlags = {NODE_TOPLEVEL};
+
+ // Parse expression
+ expr(compilerFlags);
+
+ nullable = (compilerFlags[0] & NODE_NULLABLE) != 0;
+
+ // Should be at end of input
+ if (idx != len) {
+ if (pattern.charAt(idx) == ')') {
+ syntaxError("Unmatched close paren");
+ }
+ syntaxError("Unexpected input remains");
+ }
+
+ }
+
+ // Return the result
+ Operation[] ops = new Operation[instructions.size()];
+ for (int i=0; i<instructions.size(); i++) {
+ // convert relative offsets in "next" pointer to absolute offsets (with -1 meaning null)
+ Operation op = instructions.get(i);
+ if (op.next == 0) {
+ op.next = -1;
+ } else {
+ op.next += i;
+ }
+ ops[i] = op;
+ }
+ REProgram program = new REProgram(ops, parens, reFlags);
+
+ if (reFlags.isDebug()) {
+ program.display(System.err);
+ //throw new AssertionError("terminated by request");
+ }
+
+ program.setNullable(nullable);
+
+ return program;
+ }
+
+ /**
+ * Process a "regular expression" with the q flag set. This is simply handled as an atom, where
+ * no characters are treated as special (i.e. all are treated as if escaped)
+ *
+ * @return Index of new atom node
+ */
+ int literalAtom() {
+ // Create a string node
+ Operation.OpAtom node = new Operation.OpAtom();
+ node.atom = pattern;
+ return appendNode(node);
+ }
+
+
+}
diff --git a/sf/saxon/regex/REFlags.java b/sf/saxon/regex/REFlags.java
new file mode 100644
index 0000000..e1bdbd2
--- /dev/null
+++ b/sf/saxon/regex/REFlags.java
@@ -0,0 +1,142 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+/**
+ * Class representing a set of regular expression flags (some combination of i, m, s, x, q).
+ * Also contains options affecting the regular expression dialect: whether or not XPath 2.0
+ * and XPath 3.0 extensions to XSD regex syntax are accepted.
+ */
+
+public class REFlags {
+
+ private boolean caseIndependent;
+ private boolean multiLine;
+ private boolean singleLine;
+ private boolean allowWhitespace;
+ private boolean literal;
+ private boolean xpath20;
+ private boolean xpath30;
+ private boolean xsd11;
+ private boolean debug; // flags = ";g"
+ private boolean allowUnknownBlockNames = false; //flags = ";k"
+
+ /**
+ * Create the regular expression flags
+ * @param flags a string containing zero or more of 'i', 'x', 'm', 's'
+ * @param language one of "XSD10", "XSD11", "XP20", or "XP30" indicating the regular expression dialect.
+ * Also allow combinations, e.g. "XP20/XSD11".
+ */
+ public REFlags(String flags, String language) {
+
+ if (language.equals("XSD10")) {
+ // no action
+ } else if (language.contains("XSD11")) {
+ allowUnknownBlockNames = !language.contains("XP");
+ xsd11 = true;
+ }
+ if (language.contains("XP20")) {
+ xpath20 = true;
+ } else if (language.contains("XP30")) {
+ xpath20 = true;
+ xpath30 = true;
+ }
+
+ int semi = flags.indexOf(';');
+ int endStd = (semi >= 0 ? semi : flags.length());
+ for (int i=0; i<endStd; i++) {
+ char c = flags.charAt(i);
+ switch (c) {
+ case 'i':
+ caseIndependent = true;
+ break;
+ case 'm':
+ multiLine = true;
+ break;
+ case 's':
+ singleLine = true;
+ break;
+ case 'q':
+ literal = true;
+ if (!xpath30) {
+ throw new RESyntaxException("'q' flag requires XPath 3.0 to be enabled");
+ }
+ break;
+ case 'x':
+ allowWhitespace = true;
+ break;
+ default:
+ throw new RESyntaxException("unrecognized flag '" + c + "'");
+ }
+ }
+ for (int i=semi+1; i<flags.length(); i++) {
+ char c = flags.charAt(i);
+ switch (c) {
+ case 'g':
+ debug = true;
+ break;
+ case 'k':
+ allowUnknownBlockNames = true;
+ break;
+ case 'K':
+ allowUnknownBlockNames = false;
+ break;
+ }
+ }
+ }
+
+ public boolean isCaseIndependent() {
+ return caseIndependent;
+ }
+
+ public boolean isMultiLine() {
+ return multiLine;
+ }
+
+ public boolean isSingleLine() {
+ return singleLine;
+ }
+
+ public boolean isAllowWhitespace() {
+ return allowWhitespace;
+ }
+
+ public boolean isLiteral() {
+ return literal;
+ }
+
+ public boolean isAllowsXPath20Extensions() {
+ return xpath20;
+ }
+
+ public boolean isAllowsXPath30Extensions() {
+ return xpath30;
+ }
+
+ public boolean isAllowsXSD11Syntax() {
+ return xsd11;
+ }
+
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ public boolean isDebug() {
+ return debug;
+ }
+
+ public void setAllowUnknownBlockNames(boolean allow) {
+ this.allowUnknownBlockNames = allow;
+ }
+
+ public boolean isAllowUnknownBlockNames() {
+ return allowUnknownBlockNames;
+ }
+
+
+}
diff --git a/sf/saxon/regex/REMatcher.java b/sf/saxon/regex/REMatcher.java
new file mode 100644
index 0000000..153138a
--- /dev/null
+++ b/sf/saxon/regex/REMatcher.java
@@ -0,0 +1,837 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Originally part of Apache's Jakarta project (downloaded January 2012),
+ * this file has been extensively modified for integration into Saxon by
+ * Michael Kay, Saxonica.
+ */
+
+package net.sf.saxon.regex;
+
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntHashSet;
+import net.sf.saxon.z.IntPredicate;
+import net.sf.saxon.z.IntSet;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+/**
+ * RE is an efficient, lightweight regular expression evaluator/matcher
+ * class. Regular expressions are pattern descriptions which enable
+ * sophisticated matching of strings. In addition to being able to
+ * match a string against a pattern, you can also extract parts of the
+ * match. This is especially useful in text parsing! Details on the
+ * syntax of regular expression patterns are given below.
+ * <p/>
+ * <p/>
+ * To compile a regular expression (RE), you can simply construct an RE
+ * matcher object from the string specification of the pattern, like this:
+ * <p/>
+ * <pre>
+ * RE r = new RE("a*b");
+ * </pre>
+ * <p/>
+ * <p/>
+ * Once you have done this, you can call either of the RE.match methods to
+ * perform matching on a String. For example:
+ * <p/>
+ * <pre>
+ * boolean matched = r.match("aaaab");
+ * </pre>
+ * <p/>
+ * will cause the boolean matched to be set to true because the
+ * pattern "a*b" matches the string "aaaab".
+ * <p/>
+ * <p/>
+ * If you were interested in the <i>number</i> of a's which matched the
+ * first part of our example expression, you could change the expression to
+ * "(a*)b". Then when you compiled the expression and matched it against
+ * something like "xaaaab", you would get results like this:
+ * <p/>
+ * <pre>
+ * RE r = new RE("(a*)b"); // Compile expression
+ * boolean matched = r.match("xaaaab"); // Match against "xaaaab"
+ *
+ * String wholeExpr = r.getParen(0); // wholeExpr will be 'aaaab'
+ * String insideParens = r.getParen(1); // insideParens will be 'aaaa'
+ *
+ * int startWholeExpr = r.getParenStart(0); // startWholeExpr will be index 1
+ * int endWholeExpr = r.getParenEnd(0); // endWholeExpr will be index 6
+ * int lenWholeExpr = r.getParenLength(0); // lenWholeExpr will be 5
+ *
+ * int startInside = r.getParenStart(1); // startInside will be index 1
+ * int endInside = r.getParenEnd(1); // endInside will be index 5
+ * int lenInside = r.getParenLength(1); // lenInside will be 4
+ * </pre>
+ * <p/>
+ * You can also refer to the contents of a parenthesized expression
+ * within a regular expression itself. This is called a
+ * 'backreference'. The first backreference in a regular expression is
+ * denoted by \1, the second by \2 and so on. So the expression:
+ * <p/>
+ * <pre>
+ * ([0-9]+)=\1
+ * </pre>
+ * <p/>
+ * will match any string of the form n=n (like 0=0 or 2=2).
+ * <p/>
+ * <p/>
+ * The full regular expression syntax accepted by RE is as defined in the XSD 1.1
+ * specification, modified by the XPath 2.0 or 3.0 specifications.
+ * <p/>
+ * <p/>
+ * <b><font face="times roman">Line terminators</font></b>
+ * <br>
+ * A line terminator is a one- or two-character sequence that marks
+ * the end of a line of the input character sequence. The following
+ * are recognized as line terminators:
+ * <ul>
+ * <li>A newline (line feed) character ('\n'),</li>
+ * <li>A carriage-return character followed immediately by a newline character ("\r\n"),</li>
+ * <li>A standalone carriage-return character ('\r'),</li>
+ * <li>A next-line character ('\u0085'),</li>
+ * <li>A line-separator character ('\u2028'), or</li>
+ * <li>A paragraph-separator character ('\u2029).</li>
+ * </ul>
+ * <p/>
+ * <p/>
+ * RE runs programs compiled by the RECompiler class. But the RE
+ * matcher class does not include the actual regular expression compiler
+ * for reasons of efficiency. In fact, if you want to pre-compile one
+ * or more regular expressions, the 'recompile' class can be invoked
+ * from the command line to produce compiled output like this:
+ * <p/>
+ * <pre>
+ * // Pre-compiled regular expression "a*b"
+ * char[] re1Instructions =
+ * {
+ * 0x007c, 0x0000, 0x001a, 0x007c, 0x0000, 0x000d, 0x0041,
+ * 0x0001, 0x0004, 0x0061, 0x007c, 0x0000, 0x0003, 0x0047,
+ * 0x0000, 0xfff6, 0x007c, 0x0000, 0x0003, 0x004e, 0x0000,
+ * 0x0003, 0x0041, 0x0001, 0x0004, 0x0062, 0x0045, 0x0000,
+ * 0x0000,
+ * };
+ *
+ *
+ * REProgram re1 = new REProgram(re1Instructions);
+ * </pre>
+ * <p/>
+ * You can then construct a regular expression matcher (RE) object from
+ * the pre-compiled expression re1 and thus avoid the overhead of
+ * compiling the expression at runtime. If you require more dynamic
+ * regular expressions, you can construct a single RECompiler object and
+ * re-use it to compile each expression. Similarly, you can change the
+ * program run by a given matcher object at any time. However, RE and
+ * RECompiler are not threadsafe (for efficiency reasons, and because
+ * requiring thread safety in this class is deemed to be a rare
+ * requirement), so you will need to construct a separate compiler or
+ * matcher object for each thread (unless you do thread synchronization
+ * yourself). Once expression compiled into the REProgram object, REProgram
+ * can be safely shared across multiple threads and RE objects.
+ * <p/>
+ * <br><p><br>
+ * <p/>
+ * <font color="red">
+ * <i>ISSUES:</i>
+ * <p/>
+ * <li>Not *all* possibilities are considered for greediness when backreferences
+ * are involved (as POSIX suggests should be the case). The POSIX RE
+ * "(ac*)c*d[ac]*\1", when matched against "acdacaa" should yield a match
+ * of acdacaa where \1 is "a". This is not the case in this RE package,
+ * and actually Perl doesn't go to this extent either! Until someone
+ * actually complains about this, I'm not sure it's worth "fixing".
+ * If it ever is fixed, test #137 in RETest.txt should be updated.</li>
+ * </ul>
+ * <p/>
+ * <p>This library is based on the Apache Jakarta regex library as downloaded
+ * on 3 January 2012. Changes have been made to make the grammar and semantics conform to XSD
+ * and XPath rules; these changes are listed in source code comments in the
+ * RECompiler source code module.</p>
+ * </font>
+ *
+ * @author <a href="mailto:jonl at muppetlabs.com">Jonathan Locke</a>
+ * @author <a href="mailto:ts at sch-fer.de">Tobias Schäfer</a>
+ * @author <a href="mailto:mike at saxonica.com">Michael Kay</a>
+ * @see RECompiler
+ */
+public class REMatcher {
+
+ // Limits
+ static final int MAX_PAREN = 16; // Number of paren pairs
+
+ // State of current program
+ REProgram program; // Compiled regular expression 'program'
+ UnicodeString search; // The string being matched against
+ int matchFlags; // Match behaviour flags
+ int maxParen = MAX_PAREN;
+
+ // Parenthesized subexpressions
+ int parenCount; // Number of subexpressions matched (num open parens + 1)
+ int[] startn; // Lazy-alloced array of sub-expression starts
+ int[] endn; // Lazy-alloced array of sub-expression ends
+
+ // Backreferences
+ int[] startBackref; // Lazy-alloced array of backref starts
+ int[] endBackref; // Lazy-alloced array of backref ends
+
+ IntHashMap<IntSet> history; // Tracks progress of a match operation
+ // Key is an integer offset in the source string
+ // Value is a set of instructions that have visited this offset
+ Operation[] instructions;
+ boolean anchoredMatch;
+
+
+ /**
+ * Construct a matcher for a pre-compiled regular expression from program
+ * (bytecode) data.
+ *
+ * @param program Compiled regular expression program
+ * @see RECompiler
+ */
+ public REMatcher(REProgram program) {
+ setProgram(program);
+ }
+
+ /**
+ * Sets the current regular expression program used by this matcher object.
+ *
+ * @param program Regular expression program compiled by RECompiler.
+ * @see RECompiler
+ * @see REProgram
+ */
+ public void setProgram(REProgram program) {
+ this.program = program;
+ if (program != null && program.maxParens != -1) {
+ this.instructions = program.instructions;
+ this.maxParen = program.maxParens;
+ } else {
+ this.maxParen = MAX_PAREN;
+ }
+ }
+
+ /**
+ * Returns the current regular expression program in use by this matcher object.
+ *
+ * @return Regular expression program
+ * @see #setProgram
+ */
+ public REProgram getProgram() {
+ return program;
+ }
+
+ /**
+ * Returns the number of parenthesized subexpressions available after a successful match.
+ *
+ * @return Number of available parenthesized subexpressions
+ */
+ public int getParenCount() {
+ return parenCount;
+ }
+
+ /**
+ * Gets the contents of a parenthesized subexpression after a successful match.
+ *
+ * @param which Nesting level of subexpression
+ * @return String
+ */
+ public UnicodeString getParen(int which) {
+ int start;
+ if (which < parenCount && (start = getParenStart(which)) >= 0) {
+ return search.substring(start, getParenEnd(which));
+ }
+ return null;
+ }
+
+ /**
+ * Returns the start index of a given paren level.
+ *
+ * @param which Nesting level of subexpression
+ * @return String index
+ */
+ public final int getParenStart(int which) {
+ if (which < startn.length) {
+ return startn[which];
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the end index of a given paren level.
+ *
+ * @param which Nesting level of subexpression
+ * @return String index
+ */
+ public final int getParenEnd(int which) {
+ if (which < endn.length) {
+ return endn[which];
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the length of a given paren level.
+ *
+ * @param which Nesting level of subexpression
+ * @return Number of characters in the parenthesized subexpression
+ */
+ public final int getParenLength(int which) {
+ if (which < startn.length) {
+ return getParenEnd(which) - getParenStart(which);
+ }
+ return -1;
+ }
+
+ /**
+ * Sets the start of a paren level
+ *
+ * @param which Which paren level
+ * @param i Index in input array
+ */
+ protected final void setParenStart(int which, int i) {
+ while (which > startn.length - 1) {
+ int[] s2 = new int[startn.length*2];
+ System.arraycopy(startn, 0, s2, 0, startn.length);
+ Arrays.fill(s2, startn.length, s2.length, -1);
+ startn = s2;
+ }
+ startn[which] = i;
+ }
+
+ /**
+ * Sets the end of a paren level
+ *
+ * @param which Which paren level
+ * @param i Index in input array
+ */
+ protected final void setParenEnd(int which, int i) {
+ while (which > endn.length - 1) {
+ int[] e2 = new int[endn.length*2];
+ System.arraycopy(endn, 0, e2, 0, endn.length);
+ Arrays.fill(e2, endn.length, e2.length, -1);
+ endn = e2;
+ }
+ endn[which] = i;
+ }
+
+ /**
+ * Throws an Error representing an internal error condition probably resulting
+ * from a bug in the regular expression compiler (or possibly data corruption).
+ * In practice, this should be very rare.
+ *
+ * @param s Error description
+ */
+ protected void internalError(String s) throws Error {
+ throw new Error("RE internal error: " + s);
+ }
+
+ /**
+ * Try to match a string against a subset of nodes in the program
+ *
+ * @param firstNode Node to start at in program
+ * @param lastNode Last valid node (used for matching a subexpression without
+ * matching the rest of the program as well).
+ * @param idx Starting position in character array
+ * @return Final input array index if match succeeded. -1 if not.
+ */
+ int matchNodes(int firstNode, int lastNode, int idx) {
+ // TODO: all the tests seem to pass with the two-arg version of the method, and the 3-arg version seems
+ // illogical: what is supposed to happen when node>=lastNode other than a program crash?
+ return matchNodes(firstNode, idx);
+
+// // Loop while node is valid
+// int idxNew;
+// for (int node = firstNode; node < lastNode; ) {
+// Operation op = instructions[node];
+// idxNew = op.exec(this, node, idx);
+// if (idxNew != -1) {
+// idx = idxNew;
+// }
+// switch (op.nextAction(idxNew)) {
+// case Operation.ACTION_RETURN:
+// return idxNew;
+// case Operation.ACTION_ADVANCE_TO_NEXT:
+// node = op.next;
+// continue;
+// case Operation.ACTION_ADVANCE_TO_FOLLOWING:
+// node++;
+// continue;
+// case Operation.ACTION_ADVANCE_TO_NEXT_NEXT:
+// node = instructions[op.next].next;
+// continue;
+// default:
+// internalError("Unknown action");
+// }
+// break;
+// }
+//
+// // We should never end up here
+// internalError("Corrupt program");
+// return -1;
+ }
+
+ /**
+ * Try to match a string against a subset of nodes in the program. This version
+ * has no lastNode argument (which hopefully saves a bit of stack space in the deep recursion)
+ *
+ * @param node Node to start at in program
+ * @param idx Starting position in character array
+ * @return Final input array index if match succeeded. -1 if not.
+ */
+ int matchNodes(int node, int idx) {
+
+ // Loop while node is valid
+ int idxNew;
+ while (true) {
+ Operation op = instructions[node];
+ idxNew = op.exec(this, node, idx);
+ if (idxNew != -1) {
+ idx = idxNew;
+ }
+ //System.err.println("At char " + idxNew + " with instruction " + node);
+ switch (op.nextAction(idxNew)) {
+ case Operation.ACTION_RETURN:
+ return idxNew;
+ case Operation.ACTION_ADVANCE_TO_NEXT:
+ node = op.next;
+ continue;
+ case Operation.ACTION_ADVANCE_TO_FOLLOWING:
+ node++;
+ continue;
+ case Operation.ACTION_ADVANCE_TO_NEXT_NEXT:
+ node = instructions[op.next].next;
+ continue;
+ default:
+ internalError("Unknown action");
+ }
+ break;
+ }
+
+ // We should never end up here
+ internalError("Corrupt program");
+ return -1;
+ }
+
+
+ /**
+ * Ask whether a particular node has previously visited a particular position
+ * in the input string
+ *
+ * @param idx the position in the input string
+ * @param node the instruction node
+ * @return true if this is not the first visit by this instruction to this node
+ */
+
+ boolean beenHereBefore(int idx, int node) {
+ // TODO: this mechanism succeeds in its purpose of preventing an infinite number of matches
+ // of zero-length strings, but it is incorrect: the state of the machine does not only depend
+ // on the current instruction and the position in the input string, but also on the state of the stack.
+ if (history == null) {
+ history = new IntHashMap<IntSet>(Math.max(search.length(), 128));
+ }
+ IntSet previousVisitors = history.get(idx);
+ if (previousVisitors != null && previousVisitors.contains(node)) {
+ return true;
+ } else {
+ if (previousVisitors == null) {
+ previousVisitors = new IntHashSet(4);
+ history.put(idx, previousVisitors);
+ }
+ previousVisitors.add(node);
+ return false;
+ }
+ }
+
+ /**
+ * Match the current regular expression program against the current
+ * input string, starting at index i of the input string. This method
+ * is only meant for internal use.
+ *
+ * @param i The input string index to start matching at
+ * @param anchored true if the regex must match all characters up to the end of the string
+ * @return True if the input matched the expression
+ */
+ protected boolean matchAt(int i, boolean anchored) {
+ // Initialize start pointer, paren cache and paren count
+ startn = new int[3];
+ startn[0] = startn[1] = startn[2] = -1;
+ endn = new int[3];
+ endn[0] = endn[1] = endn[2] = -1;
+ parenCount = 1;
+ //history = new IntHashMap<IntSet>(search.length());
+ anchoredMatch = anchored;
+ setParenStart(0, i);
+
+ // Allocate backref arrays (unless optimizations indicate otherwise)
+ if ((program.optimizationFlags & REProgram.OPT_HASBACKREFS) != 0) {
+ startBackref = new int[maxParen];
+ endBackref = new int[maxParen];
+ }
+
+ // Match against string
+ int idx;
+ if ((idx = matchNodes(0, i)) != -1) {
+ setParenEnd(0, idx);
+ return true;
+ }
+
+ // Didn't match
+ parenCount = 0;
+ return false;
+ }
+
+ /**
+ * Tests whether the regex matches a string in its entirety, anchored
+ * at both ends
+ * @param search the string to be matched
+ * @return true if the regex matches the whols string
+ */
+
+ public boolean anchoredMatch(UnicodeString search) {
+ this.search = search;
+ return matchAt(0, true);
+ }
+
+ /**
+ * Matches the current regular expression program against a character array,
+ * starting at a given index.
+ *
+ * @param search String to match against
+ * @param i Index to start searching at
+ * @return True if string matched
+ */
+ public boolean match(UnicodeString search, int i) {
+ //System.err.println("Matching '" + search + "'");
+ // There is no compiled program to search with!
+ if (program == null) {
+ // This should be uncommon enough to be an error case rather
+ // than an exception (which would have to be handled everywhere)
+ internalError("No RE program to run!");
+ }
+
+ // Save string to search
+ this.search = search;
+
+ // Can we optimize the search by looking for new lines?
+ if ((program.optimizationFlags & REProgram.OPT_HASBOL) == REProgram.OPT_HASBOL) {
+ // Non multi-line matching with BOL: Must match at '0' index
+ if (!program.flags.isMultiLine()) {
+ return i == 0 && matchAt(i, false);
+ }
+
+ // Multi-line matching with BOL: Seek to next line
+ for (; !search.isEnd(i); i++) {
+ // Skip if we are at the beginning of the line
+ if (isNewline(i)) {
+ continue;
+ }
+
+ // Match at the beginning of the line
+ if (matchAt(i, false)) {
+ return true;
+ }
+
+ // Skip to the end of line
+ for (; !search.isEnd(i); i++) {
+ if (isNewline(i)) {
+ break;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Can we optimize the search by looking for a prefix string?
+ if (program.prefix == null) {
+ if (program.initialCharClass != null) {
+ // no prefix known; but the first character must match a predicate
+ IntPredicate pred = program.initialCharClass;
+ for (; !search.isEnd(i); i++) {
+ if (pred.matches(search.charAt(i))) {
+ if (matchAt(i, false)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ // Unprefixed matching must try for a match at each character
+ for (; !search.isEnd(i - 1); i++) {
+ // Try a match at index i
+ if (matchAt(i, false)) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ // Prefix-anchored matching is possible
+ UnicodeString prefix = program.prefix;
+ for (; !search.isEnd(i + prefix.length() - 1); i++) {
+ int j = i;
+ int k = 0;
+
+ if (program.flags.isCaseIndependent()) {
+ do {
+ // If there's a mismatch of any character in the prefix, give up
+ } while (equalCaseBlind(search.charAt(j++), prefix.charAt(k++)) && k < prefix.length());
+ } else {
+ do {
+ // If there's a mismatch of any character in the prefix, give up
+ } while ((search.charAt(j++) == prefix.charAt(k++)) && k < prefix.length());
+ }
+
+ // See if the whole prefix string matched
+ if (k == prefix.length()) {
+ // We matched the full prefix at firstChar, so try it
+ if (matchAt(i, false)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Matches the current regular expression program against a String.
+ *
+ * @param search String to match against
+ * @return True if string matched
+ */
+ public boolean match(String search) {
+ return match(UnicodeString.makeUnicodeString(search), 0);
+ }
+
+ /**
+ * Splits a string into an array of strings on regular expression boundaries.
+ * This function works the same way as the Perl function of the same name.
+ * Given a regular expression of "[ab]+" and a string to split of
+ * "xyzzyababbayyzabbbab123", the result would be the array of Strings
+ * "[xyzzy, yyz, 123]".
+ * <p/>
+ * <p>Please note that the first string in the resulting array may be an empty
+ * string. This happens when the very first character of input string is
+ * matched by the pattern.
+ *
+ * @param s String to split on this regular exression
+ * @return Array of strings
+ */
+ public List<UnicodeString> split(UnicodeString s) {
+ // Create new vector
+ List<UnicodeString> v = new ArrayList<UnicodeString>();
+
+ // Start at position 0 and search the whole string
+ int pos = 0;
+ int len = s.length();
+
+ // Try a match at each position
+ while (pos < len && match(s, pos)) {
+ // Get start of match
+ int start = getParenStart(0);
+
+ // Get end of match
+ int newpos = getParenEnd(0);
+
+ // Check if no progress was made
+ if (newpos == pos) {
+ v.add(s.substring(pos, start + 1));
+ newpos++;
+ } else {
+ v.add(s.substring(pos, start));
+ }
+
+ // Move to new position
+ pos = newpos;
+ }
+
+ // Push remainder even if it's empty
+ UnicodeString remainder = s.substring(pos, len);
+ v.add(remainder);
+
+ // Return the list
+ return v;
+ }
+
+ /**
+ * Substitutes a string for this regular expression in another string.
+ * This method works like the Perl function of the same name.
+ * Given a regular expression of "a*b", a String to substituteIn of
+ * "aaaabfooaaabgarplyaaabwackyb" and the substitution String "-", the
+ * resulting String returned by subst would be "-foo-garply-wacky-".
+ * <p/>
+ * It is also possible to reference the contents of a parenthesized expression
+ * with $0, $1, ... $9. A regular expression of "http://[\\.\\w\\-\\?/~_@&=%]+",
+ * a String to substituteIn of "visit us: http://www.apache.org!" and the
+ * substitution String "<a href=\"$0\">$0</a>", the resulting String
+ * returned by subst would be
+ * "visit us: <a href=\"http://www.apache.org\">http://www.apache.org</a>!".
+ * <p/>
+ * <i>Note:</i> $0 represents the whole match.
+ *
+ * @param in String to substitute within
+ * @param replacement String to substitute for matches of this regular expression
+ * @return The string substituteIn with zero or more occurrences of the current
+ * regular expression replaced with the substitution String (if this regular
+ * expression object doesn't match at any position, the original String is returned
+ * unchanged).
+ */
+ public CharSequence subst(UnicodeString in, UnicodeString replacement) {
+ // String to return
+ FastStringBuffer sb = new FastStringBuffer(in.length() * 2);
+
+ // Start at position 0 and search the whole string
+ int pos = 0;
+ int len = in.length();
+
+ // Try a match at each position
+ while (pos < len && match(in, pos)) {
+ // Append chars from input string before match
+ for (int i = pos; i < getParenStart(0); i++) {
+ sb.appendWideChar(in.charAt(i));
+ }
+
+ if (!program.flags.isLiteral()) {
+ // Process references to captured substrings
+ int maxCapture = getParenCount() - 1;
+
+ for (int i = 0; i < replacement.length(); i++) {
+ int ch = replacement.charAt(i);
+ if (ch == '\\') {
+ ch = replacement.charAt(++i);
+ if (ch == '\\' || ch == '$') {
+ sb.append((char) ch);
+ } else {
+ throw new RESyntaxException("Invalid escape in replacement string");
+ }
+ } else if (ch == '$') {
+ ch = replacement.charAt(++i);
+ if (!(ch >= '0' && ch <= '9')) {
+ throw new RESyntaxException("$ in replacement must be followed by a digit");
+ }
+ int n = (ch - '0');
+ if (maxCapture <= 9) {
+ if (maxCapture >= n) {
+ UnicodeString captured = getParen(n);
+ if (captured != null) {
+ for (int j = 0; j < captured.length(); j++) {
+ sb.appendWideChar(captured.charAt(j));
+ }
+ }
+ } else {
+ // append a zero-length string (no-op)
+ }
+ } else {
+ while (true) {
+ if (i >= replacement.length()) {
+ break;
+ }
+ ch = replacement.charAt(++i);
+ if (ch >= '0' && ch <= '9') {
+ int m = n * 10 + (ch - '0');
+ if (m > maxCapture) {
+ i--;
+ break;
+ } else {
+ n = m;
+ }
+ } else {
+ i--;
+ break;
+ }
+ }
+ UnicodeString captured = getParen(n);
+ for (int j = 0; j < captured.length(); j++) {
+ sb.appendWideChar(captured.charAt(j));
+ }
+ }
+ } else {
+ sb.appendWideChar(ch);
+ }
+ }
+
+ } else {
+ // Append substitution without processing backreferences
+ for (int i = 0; i < replacement.length(); i++) {
+ sb.appendWideChar(replacement.charAt(i));
+ }
+ }
+
+ // Move forward, skipping past match
+ int newpos = getParenEnd(0);
+
+ // We always want to make progress!
+ if (newpos == pos) {
+ newpos++;
+ }
+
+ // Try new position
+ pos = newpos;
+
+ }
+
+ // If there's remaining input, append it
+ for (int i = pos; i < len; i++) {
+ sb.appendWideChar(in.charAt(i));
+ }
+
+ // Return string buffer
+ return sb.condense();
+ }
+
+
+ /**
+ * Test whether the character at a given position is a newline
+ *
+ * @param i the position of the character to be tested
+ * @return true if character at i-th position in the <code>search</code> string is a newline
+ */
+ boolean isNewline(int i) {
+ return search.charAt(i) == '\n';
+ }
+
+ /**
+ * Compares two characters ignoring case.
+ *
+ * @param c1 first character to compare.
+ * @param c2 second character to compare.
+ * @return true the first character is equal to the second ignoring case.
+ */
+ boolean equalCaseBlind(int c1, int c2) {
+ if (c1 == c2) {
+ return true;
+ }
+ for (int v : CaseVariants.getCaseVariants(c2)) {
+ if (c1 == v) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/sf/saxon/regex/REProgram.java b/sf/saxon/regex/REProgram.java
new file mode 100644
index 0000000..d0e7b06
--- /dev/null
+++ b/sf/saxon/regex/REProgram.java
@@ -0,0 +1,276 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Originally part of Apache's Jakarta project (downloaded January 2012),
+ * this file has been extensively modified for integration into Saxon by
+ * Michael Kay, Saxonica.
+ */
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.z.*;
+
+import java.io.PrintStream;
+import java.io.Serializable;
+
+/**
+ * A class that holds compiled regular expressions.
+ *
+ * @see net.sf.saxon.regex.REMatcher
+ * @see RECompiler
+ *
+ * @author <a href="mailto:jonl at muppetlabs.com">Jonathan Locke</a>
+ * @version $Id: REProgram.java 518156 2007-03-14 14:31:26Z vgritsenko $
+ */
+public class REProgram implements Serializable
+{
+ static final int OPT_HASBACKREFS = 1;
+ static final int OPT_HASBOL = 2;
+
+ Operation[] instructions;
+ REFlags flags;
+ UnicodeString prefix; // Prefix string optimization
+ IntPredicate initialCharClass;
+ int optimizationFlags; // Optimization flags (REProgram.OPT_*)
+ int maxParens = -1;
+ boolean nullable = false;
+
+ /**
+ * Constructs a program object from a character array
+ * @param parens Count of parens in the program
+ * @param instructions Array with RE opcode instructions in it. The "next"
+ * pointers within the operations must already have been converted to absolute
+ * offsets.
+ */
+ public REProgram(Operation[] instructions, int parens, REFlags flags) {
+ this.flags = flags;
+ setInstructions(instructions);
+ this.maxParens = parens;
+ }
+
+
+
+ /**
+ * Sets a new regular expression program to run. It is this method which
+ * performs any special compile-time search optimizations. Currently only
+ * two optimizations are in place - one which checks for backreferences
+ * (so that they can be lazily allocated) and another which attempts to
+ * find an prefix anchor string so that substantial amounts of input can
+ * potentially be skipped without running the actual program.
+ * @param instructions Program instruction buffer
+ */
+ private void setInstructions(Operation[] instructions) {
+ // Save reference to instruction array
+ this.instructions = instructions;
+
+ // Initialize other program-related variables
+ this.optimizationFlags = 0;
+ this.prefix = null;
+
+ // Try various compile-time optimizations if there's a program
+
+ if (instructions != null && instructions.length != 0) {
+ int first = 0;
+ while (instructions[first] instanceof Operation.OpContinue) {
+ first++;
+ }
+ if (instructions[first] instanceof Operation.OpAtom) {
+ prefix = ((Operation.OpAtom)instructions[first]).atom;
+ }
+ if (instructions[first] instanceof Operation.OpCharClass) {
+ initialCharClass = ((Operation.OpCharClass)instructions[first]).predicate;
+ }
+ // If the first node is a branch
+ if (instructions[first] instanceof Operation.OpBranch) {
+ // to the end node
+ int next = instructions[first].next;
+ if (instructions[next] instanceof Operation.OpEndProgram) {
+ final Operation nextOp = instructions[first+1];
+ // the branch starts with an atom
+ if (nextOp instanceof Operation.OpAtom) {
+ // then get that atom as an prefix because there's no other choice
+ this.prefix = ((Operation.OpAtom)nextOp).atom;
+ }
+ // the branch starts with a BOL
+ else if (nextOp instanceof Operation.OpBOL) {
+ // then set the flag indicating that BOL is present
+ this.optimizationFlags |= OPT_HASBOL;
+ }
+ }
+ }
+
+ // Check for backreferences
+ for (Operation op : instructions) {
+ if (op instanceof Operation.OpBackReference) {
+ optimizationFlags |= OPT_HASBACKREFS;
+ break;
+ }
+ }
+
+ // Check for deterministic quantifiers; the optimization causes constructs such as A* or [0-9]+ to
+ // be evaluated using iteration rather than recursion if there is no ambiguity about the ending condition,
+ // which means there will never be any need to backtrack.
+ boolean caseBlind = flags.isCaseIndependent();
+ for (int i=0; i<instructions.length; i++) {
+ Operation op = instructions[i];
+ if (op instanceof Operation.OpStar &&
+ op.next == i+2 &&
+ (instructions[i+1] instanceof Operation.OpAtom || instructions[i+1] instanceof Operation.OpCharClass)) {
+ if (noAmbiguity(instructions[i+1], instructions[op.next], caseBlind)) {
+ //System.err.println("Optimizing *");
+ instructions[i] = new Operation.OpConfidentStar();
+ instructions[i].next = op.next;
+ }
+ } else if (op instanceof Operation.OpPlus &&
+ op.next == i-2 &&
+ (instructions[i-1] instanceof Operation.OpAtom || instructions[i-1] instanceof Operation.OpCharClass) &&
+ (instructions[i-2].next == i+1)) {
+ if (noAmbiguity(instructions[i-1], instructions[i+1], caseBlind)) {
+ //System.err.println("Optimizing +");
+ instructions[i] = new Operation.OpConfidentPlus();
+ instructions[i].next = i+1;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Ask whether the regular expression matches a zero length string
+ * @return true if the regex matches a zero length string
+ */
+
+ public boolean isNullable() {
+ return nullable;
+ }
+
+ /**
+ * Say whether the regular expression matches a zero length string
+ * @param nullable true if the regex matches a zero length string
+ */
+
+ public void setNullable(boolean nullable) {
+ this.nullable = nullable;
+ }
+
+
+ /**
+ * Returns a copy of the prefix of current regular expression program
+ * in a character array. If there is no prefix, or there is no program
+ * compiled yet, <code>getPrefix</code> will return null.
+ * @return A copy of the prefix of current compiled RE program
+ */
+ public UnicodeString getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Output a human-readable printout of the program
+ */
+
+ public void display(PrintStream out) {
+ for (int i=0; i<instructions.length; i++) {
+ int nextOffset = instructions[i].next;
+ out.println(i + ". " + instructions[i].toString() +
+ (nextOffset==-1 ? "" : ", next = " + (nextOffset)));
+ }
+ }
+
+ /**
+ * Determine that there is no ambiguity between two branches, that is, if one of them matches then the
+ * other cannot possibly match. (This is for optimization, so it does not have to detect all cases; but
+ * if it returns true, then the result must be dependable.)
+ * @return true if it can be established that there is no input sequence that will match both instructions
+ */
+
+ boolean noAmbiguity(Operation op0, Operation op1, boolean caseBlind) {
+ // op0 will always be either an Atom or a CharClass. op1 may be anything.
+ if (op1 instanceof Operation.OpClose || op1 instanceof Operation.OpCloseCluster) {
+ op1 = instructions[op1.next];
+ }
+ if (op1 instanceof Operation.OpEndProgram || op1 instanceof Operation.OpBOL || op1 instanceof Operation.OpEOL) {
+ return true;
+ }
+ IntSet set0;
+ if (op0 instanceof Operation.OpAtom) {
+ set0 = getInitialChars((Operation.OpAtom) op0, caseBlind);
+ } else {
+ IntPredicate ip0 = ((Operation.OpCharClass)op0).predicate;
+ if (ip0 instanceof IntSetPredicate) {
+ set0 = ((IntSetPredicate)ip0).getIntSet();
+ } else if (ip0 instanceof IntValuePredicate) {
+ set0 = new IntSingletonSet(((IntValuePredicate)ip0).getTarget());
+ } else {
+ return false;
+ }
+ }
+
+ IntSet set1;
+ if (op1 instanceof Operation.OpAtom) {
+ set1 = getInitialChars((Operation.OpAtom) op1, caseBlind);
+ } else if (op1 instanceof Operation.OpCharClass) {
+ IntPredicate ip1 = ((Operation.OpCharClass)op1).predicate;
+ if (ip1 instanceof IntSetPredicate) {
+ set1 = ((IntSetPredicate)ip1).getIntSet();
+ } else if (ip1 instanceof IntValuePredicate) {
+ set1 = new IntSingletonSet(((IntValuePredicate)ip1).getTarget());
+ } else {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return isDisjoint(set0, set1);
+ }
+
+ private IntSet getInitialChars(Operation.OpAtom op, boolean caseBlind) {
+ IntSet set;
+ int ch = op.atom.charAt(0);
+ set = new IntSingletonSet(ch);
+ if (caseBlind) {
+ set = new IntHashSet(10);
+ set.add(ch);
+ for (int v : CaseVariants.getCaseVariants(ch)) {
+ set.add(v);
+ }
+ }
+ return set;
+ }
+
+ boolean isDisjoint(IntSet set0, IntSet set1) {
+ try {
+ IntSet intersection = set0.intersect(set1);
+ return intersection.isEmpty();
+ } catch (Throwable e) {
+ return false;
+ }
+// int size0 = set0.size();
+// int size1 = set1.size();
+// IntSet smaller = (size0 < size1 ? set0 : set1);
+// IntSet larger = (smaller == set0 ? set1 : set0);
+// if (smaller.size() < 100)
+ }
+}
diff --git a/sf/saxon/regex/RESyntaxException.java b/sf/saxon/regex/RESyntaxException.java
new file mode 100644
index 0000000..3a1561f
--- /dev/null
+++ b/sf/saxon/regex/RESyntaxException.java
@@ -0,0 +1,58 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.sf.saxon.regex;
+
+/**
+ * Exception thrown to indicate a syntax error in a regular expression.
+ * This is a non-checked exception because you should only have problems compiling
+ * a regular expression during development.
+ * If you are making regular expresion programs dynamically then you can catch it
+ * if you wish. But should not be forced to.
+ *
+ * @author <a href="mailto:jonl at muppetlabs.com">Jonathan Locke</a>
+ * @author <a href="mailto:gholam at xtra.co.nz>Michael McCallum</a>
+ * @version $Id: RESyntaxException.java 518156 2007-03-14 14:31:26Z vgritsenko $
+ */
+public class RESyntaxException extends RuntimeException
+{
+ /**
+ * Constructor.
+ * @param s Further description of the syntax error
+ */
+ public RESyntaxException(String s)
+ {
+ super("Syntax error in regular expression: " + s);
+ }
+
+ /**
+ * Constructor.
+ * @param s Further description of the syntax error
+ * @param offset character position within regex where the error was detected
+ */
+ public RESyntaxException(String s, int offset)
+ {
+ super("Syntax error at char " + offset + " in regular expression: " + s);
+ }
+}
diff --git a/sf/saxon/regex/RegexIterator.java b/sf/saxon/regex/RegexIterator.java
new file mode 100644
index 0000000..e435ae5
--- /dev/null
+++ b/sf/saxon/regex/RegexIterator.java
@@ -0,0 +1,83 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+
+/**
+ * This interface defines an iterator that supports the evaluation of xsl:analyze-string.
+ * It returns all the matching and non-matching substrings in an input string, and
+ * provides access to their captured groups
+ */
+public interface RegexIterator extends SequenceIterator {
+
+ /**
+ * Determine whether the current item in the sequence is a matching item or a non-matching item
+ * @return true if the current item is a matching item
+ */
+
+ public boolean isMatching();
+
+ /**
+ * Get a substring that matches a parenthesised group within the regular expression
+ * @param number the number of the group to be obtained
+ * @return the substring of the current item that matches the n'th parenthesized group
+ * within the regular expression
+ */
+
+ /*@Nullable*/ public String getRegexGroup(int number);
+
+ /**
+ * Get a sequence containing all the regex captured groups relating to the current matching item
+ * (except group 0, because we want to use indexing from 1).
+ * This is used by the saxon:analyze-string() higher-order extension function.
+ */
+
+ public SequenceIterator getRegexGroupIterator();
+
+ /**
+ * Process a matching substring, performing specified actions at the start and end of each matching
+ * group
+ */
+
+ public void processMatchingSubstring(XPathContext context, OnGroup action) throws XPathException;
+
+
+ /**
+ * Get a Regex sequence which is a snapshot of this sequence at the current position
+ */
+ public RegexIterator getSnapShot(XPathContext context) throws XPathException;
+
+ /**
+ * Interface defining a call-back action for processing captured groups
+ */
+
+ public static interface OnGroup {
+
+ /**
+ * Method to be called when the start of a captured group is encountered
+ * @param c the dynamic evaluation context
+ * @param groupNumber the group number of the captured group
+ */
+
+ public void onGroupStart(XPathContext c, int groupNumber) throws XPathException;
+
+ /**
+ * Method to be called when the end of a captured group is encountered
+ * @param c the dynamic evaluation context
+ * @param groupNumber the group number of the captured group
+ */
+
+ public void onGroupEnd(XPathContext c, int groupNumber) throws XPathException;
+ }
+
+}
+
diff --git a/sf/saxon/regex/RegularExpression.java b/sf/saxon/regex/RegularExpression.java
new file mode 100644
index 0000000..40a0b01
--- /dev/null
+++ b/sf/saxon/regex/RegularExpression.java
@@ -0,0 +1,70 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Serializable;
+
+/**
+ * This interface represents a compiled regular expression. There are different
+ * implementations for different regex engines
+ */
+public interface RegularExpression extends Serializable {
+
+ /**
+ * Determine whether the regular expression matches a given string in its entirety
+ * @param input the string to match
+ * @return true if the string matches, false otherwise
+ */
+
+ public boolean matches(CharSequence input);
+
+ /**
+ * Determine whether the regular expression contains a match of a given string
+ * @param input the string to match
+ * @return true if the string matches, false otherwise
+ */
+
+ public boolean containsMatch(CharSequence input);
+
+ /**
+ * Use this regular expression to tokenize an input string.
+ *
+ * @param input the string to be tokenized
+ * @return a SequenceIterator containing the resulting tokens, as objects of type StringValue
+ */
+
+ public SequenceIterator<net.sf.saxon.value.StringValue> tokenize(CharSequence input);
+
+ /**
+ * Use this regular expression to analyze an input string, in support of the XSLT
+ * analyze-string instruction. The resulting RegexIterator provides both the matching and
+ * non-matching substrings, and allows them to be distinguished. It also provides access
+ * to matched subgroups.
+ * @param input the character string to be analyzed using the regular expression
+ * @return an iterator over matched and unmatched substrings
+ */
+
+ /*@NotNull*/ public RegexIterator analyze(CharSequence input);
+
+ /**
+ * Replace all substrings of a supplied input string that match the regular expression
+ * with a replacement string.
+ * @param input the input string on which replacements are to be performed
+ * @param replacement the replacement string in the format of the XPath replace() function
+ * @return the result of performing the replacement
+ * @throws XPathException if the replacement string is invalid
+ */
+
+ public CharSequence replace(CharSequence input, CharSequence replacement) throws XPathException;
+
+
+}
+
diff --git a/sf/saxon/regex/UnicodeBlocks.java b/sf/saxon/regex/UnicodeBlocks.java
new file mode 100644
index 0000000..55f2a38
--- /dev/null
+++ b/sf/saxon/regex/UnicodeBlocks.java
@@ -0,0 +1,115 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.z.IntBlockSet;
+import net.sf.saxon.z.IntSet;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.stream.StreamSource;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class provides knowledge of the names and contents of Unicode character blocks,
+ * as referenced using the \p{IsXXXXX} construct in a regular expression. The underlying
+ * data is in an XML resource file UnicodeBlocks.xml
+ */
+public class UnicodeBlocks {
+
+ private static Map<String, IntSet> blocks = null;
+
+ public static IntSet getBlock(String name) throws RESyntaxException {
+ if (blocks == null) {
+ readBlocks(new Configuration());
+ }
+ IntSet cc = blocks.get(name);
+ if (cc != null) {
+ return cc;
+ }
+ cc = blocks.get(normalizeBlockName(name));
+ return cc;
+ }
+
+ private static String normalizeBlockName(String name) {
+ FastStringBuffer fsb = new FastStringBuffer(name.length());
+ for (int i=0; i<name.length(); i++) {
+ final char c = name.charAt(i);
+ switch (c) {
+ case ' ': case '\t': case '\r': case '\n': case '_':
+ // no action
+ break;
+ default:
+ fsb.append(c);
+ }
+ }
+ return fsb.toString();
+ }
+
+ private synchronized static void readBlocks(Configuration config) throws RESyntaxException {
+ blocks = new HashMap<String, IntSet>(250);
+ InputStream in = Configuration.locateResource("unicodeBlocks.xml", new ArrayList<String>(), new ArrayList<ClassLoader>());
+ if (in == null) {
+ throw new RESyntaxException("Unable to read unicodeBlocks.xml file");
+ }
+
+ ParseOptions options = new ParseOptions();
+ options.setSchemaValidationMode(Validation.SKIP);
+ options.setStripSpace(Whitespace.ALL);
+ DocumentInfo doc;
+ try {
+ doc = config.buildDocument(new StreamSource(in, "unicodeBlocks.xml"), options);
+ } catch (XPathException e) {
+ throw new RESyntaxException("Failed to process unicodeBlocks.xml: " + e.getMessage());
+ }
+
+ AxisIterator iter = doc.iterateAxis(AxisInfo.DESCENDANT, new NameTest(Type.ELEMENT, "", "block", config.getNamePool()));
+ while (true) {
+ NodeInfo item = iter.next();
+ if (item == null) {
+ break;
+ }
+ String blockName = normalizeBlockName(item.getAttributeValue("", "name"));
+ IntSet range = null;
+ AxisIterator ranges = item.iterateAxis(AxisInfo.CHILD, NodeKindTest.ELEMENT);
+ while (true) {
+ NodeInfo rangeElement = ranges.next();
+ if (rangeElement == null) {
+ break;
+ }
+ int from = Integer.parseInt(rangeElement.getAttributeValue("", "from").substring(2), 16);
+ int to = Integer.parseInt(rangeElement.getAttributeValue("", "to").substring(2), 16);
+ IntSet cr = new IntBlockSet(from, to);
+ if (range == null) {
+ range = cr;
+ } else if (range instanceof IntBlockSet) {
+ range = range.mutableCopy().union(cr);
+ } else {
+ range = range.union(cr);
+ }
+ }
+ blocks.put(blockName, range);
+ }
+
+ }
+}
+
diff --git a/sf/saxon/regex/UnicodeString.java b/sf/saxon/regex/UnicodeString.java
new file mode 100644
index 0000000..b88e207
--- /dev/null
+++ b/sf/saxon/regex/UnicodeString.java
@@ -0,0 +1,123 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.regex;
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+/**
+ * An abstract class that efficiently handles Unicode strings including
+ * non-BMP characters; it has two subclasses, one optimized for strings
+ * whose characters are all in the BMP, the other handling the general case.
+ */
+
+public abstract class UnicodeString {
+
+ public static UnicodeString EMPTY_STRING = new GeneralUnicodeString("");
+
+ /**
+ * Make a UnicodeString for a given CharSequence
+ * @param in the input CharSequence
+ * @return a UnicodeString using an appropriate implementation class
+ */
+
+ public static UnicodeString makeUnicodeString(CharSequence in) {
+ if (containsSurrogatePairs(in)) {
+ return new GeneralUnicodeString(in);
+ } else {
+ return new BMPString(in);
+ }
+ }
+
+ /**
+ * Make a UnicodeString for a given array of codepoints
+ * @param in the input CharSequence
+ * @return a UnicodeString using an appropriate implementation class
+ */
+
+ public static UnicodeString makeUnicodeString(int[] in) {
+ for (int ch : in) {
+ if (ch > 65535) {
+ return new GeneralUnicodeString(in, 0, in.length);
+ }
+ }
+ FastStringBuffer fsb = new FastStringBuffer(in.length);
+ for (int ch : in) {
+ fsb.append((char) ch);
+ }
+ return new BMPString(fsb);
+ }
+
+ /**
+ * Test whether a CharSequence contains Unicode codepoints outside the BMP range
+ * @param value the string to be tested
+ * @return true if the string contains non-BMP codepoints
+ */
+
+ public static boolean containsSurrogatePairs(CharSequence value) {
+ for (int i = 0; i < value.length(); i++) {
+ int c = (int) value.charAt(i);
+ if (c >= 55296 && c <= 56319) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get a substring of this string
+ * @param beginIndex the index of the first character to be included (counting
+ * codepoints, not 16-bit characters)
+ * @param endIndex the index of the first character to be NOT included (counting
+ * codepoints, not 16-bit characters)
+ * @return a substring
+ * @throws IndexOutOfBoundsException if the selection goes off the start or end of the string
+ * (this function follows the semantics of String.substring(), not the XPath semantics
+ */
+ public abstract UnicodeString substring(int beginIndex, int endIndex);
+
+ /**
+ * Get the first match for a given character
+ * @param search the character to look for
+ * @param start the first position to look
+ * @return the position of the first occurrence of the sought character, or -1 if not found
+ */
+
+ public abstract int indexOf(int search, int start);
+
+ /**
+ * Get the character at a specified position
+ * @param pos the index of the required character (counting
+ * codepoints, not 16-bit characters)
+ * @return a character (Unicode codepoint) at the specified position.
+ */
+
+ public abstract int charAt(int pos);
+
+ /**
+ * Get the length of the string, in Unicode codepoints
+ * @return the number of codepoints in the string
+ */
+
+ public abstract int length();
+
+ /**
+ * Ask whether a given position is at (or beyond) the end of the string
+ * @param pos the index of the required character (counting
+ * codepoints, not 16-bit characters)
+ * @return <tt>true</tt> iff if the specified index is after the end of the character stream
+ */
+
+ public abstract boolean isEnd(int pos);
+
+ /**
+ * Get the value of this UnicodeString as a CharSequence
+ * @return a CharSequence representing the same characters as this UnicodeString
+ */
+
+ public abstract CharSequence getCharSequence();
+}
diff --git a/sf/saxon/regex/package.html b/sf/saxon/regex/package.html
new file mode 100644
index 0000000..cc2dc0d
--- /dev/null
+++ b/sf/saxon/regex/package.html
@@ -0,0 +1,35 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.regexp</title>
+
+</head>
+ <body>
+ <p>This package contains the code to map XML Schema and XPath regular expressions
+ to a regular expression engine of the underlying Java platform.</p>
+
+ <p>Most of the classes implement a regular expression derived from Apache's Jakarta
+ project. The code of Jakarta has been modified so that the regular expressions implement
+ the syntax of XSD/XPath regular expressions.</p>
+
+ <p>In addition, there are classes to provide direct access to the native JDK
+ regular expression engine. The flags value ";j" may be used to select this
+ engine. The resulting syntax/semantics will not be an exact match to the XPath
+ definition.</p>
+
+ <p>Users should not normally need to use these classes directly.</p>
+
+ <p>Earlier versions of Saxon included a translator from XPath regular expressions
+ to Java regular expressions, based on that produced by James Clark. This
+ mechanism is no longer used.</p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/s9api/Axis.java b/sf/saxon/s9api/Axis.java
new file mode 100644
index 0000000..eaa526d
--- /dev/null
+++ b/sf/saxon/s9api/Axis.java
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.om.AxisInfo;
+
+/**
+ * This is an enumeration class containing constants representing the thirteen XPath axes
+ */
+public enum Axis {
+ ANCESTOR (AxisInfo.ANCESTOR),
+ ANCESTOR_OR_SELF (AxisInfo.ANCESTOR_OR_SELF),
+ ATTRIBUTE (AxisInfo.ATTRIBUTE),
+ CHILD (AxisInfo.CHILD),
+ DESCENDANT (AxisInfo.DESCENDANT),
+ DESCENDANT_OR_SELF (AxisInfo.DESCENDANT_OR_SELF),
+ FOLLOWING (AxisInfo.FOLLOWING),
+ FOLLOWING_SIBLING (AxisInfo.FOLLOWING_SIBLING),
+ PARENT (AxisInfo.PARENT),
+ PRECEDING (AxisInfo.PRECEDING),
+ PRECEDING_SIBLING (AxisInfo.PRECEDING_SIBLING),
+ SELF (AxisInfo.SELF),
+ NAMESPACE (AxisInfo.NAMESPACE);
+
+ private byte number;
+
+ /**
+ * Create an Axis
+ * @param number the internal axis number as defined in class {@link net.sf.saxon.om.AxisInfo}
+ */
+
+ private Axis(byte number) {
+ this.number = number;
+ }
+
+ /**
+ * Get the axis number, as defined in class {@link net.sf.saxon.om.AxisInfo}
+ * @return the axis number
+ */
+ public byte getAxisNumber() {
+ return number;
+ }
+}
+
diff --git a/sf/saxon/s9api/BuildingContentHandler.java b/sf/saxon/s9api/BuildingContentHandler.java
new file mode 100644
index 0000000..23bc455
--- /dev/null
+++ b/sf/saxon/s9api/BuildingContentHandler.java
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import org.xml.sax.ContentHandler;
+
+/**
+ * A SAX {@link ContentHandler} that builds a Saxon tree, and allows the node at the root of the tree
+ * to be retrieved on completion.
+ *
+ * <p>To create a <code>BuildingContentHandler</code> for a particular tree model, use the method
+ * {@link net.sf.saxon.s9api.DocumentBuilder#newBuildingContentHandler()}.</p>
+ */
+public interface BuildingContentHandler extends ContentHandler {
+
+ /**
+ * After building the document by writing a sequence of events, retrieve the root node
+ * of the constructed document tree
+ * @return the root node of the constructed tree. The result is undefined (maybe null, maybe an exception)
+ * if the method is called before successfully completing the sequence of events (of which the last should be
+ * {@link #endDocument}) that constructs the tree.
+ * @throws SaxonApiException if any failure occurs
+ */
+
+ public XdmNode getDocumentNode() throws SaxonApiException;
+
+}
+
diff --git a/sf/saxon/s9api/BuildingStreamWriter.java b/sf/saxon/s9api/BuildingStreamWriter.java
new file mode 100644
index 0000000..97d8dd6
--- /dev/null
+++ b/sf/saxon/s9api/BuildingStreamWriter.java
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import javax.xml.stream.XMLStreamWriter;
+
+/**
+ * A BuildingStreamWriter allows a document to be constructed by calling the methods defined in the
+ * {@link javax.xml.stream.XMLStreamWriter} interface; after the document has been constructed, its root
+ * node may be retrieved by calling the <code>getDocumentNode()</code> method.
+ *
+ * <p>The class will attempt to generate namespace prefixes where none have been supplied, unless the
+ * <code>inventPrefixes</code> option is set to false. The preferred mode of use is to call the versions
+ * of <code>writeStartElement</code> and <code>writeAttribute</code> that supply the prefix, URI, and
+ * local name in full. If the prefix is omitted, the class attempts to invent a prefix. If the URI is
+ * omitted, the name is assumed to be in no namespace. The <code>writeNamespace</p> method should be
+ * called only if there is a need to declare a namespace prefix that is not used on any element or
+ * attribute name.</p>
+ *
+ * <p>The class will check all names, URIs, and character content for conformance against XML well-formedness
+ * rules unless the <code>checkValues</code> option is set to false.</p>
+ *
+ * <p>A <code>BuildingStreamWriter</code> for a particular object model can be obtained by calling
+ * {@link net.sf.saxon.s9api.DocumentBuilder#newBuildingStreamWriter()}.</p>
+ */
+
+public interface BuildingStreamWriter extends XMLStreamWriter {
+
+ /**
+ * After building the document by writing a sequence of events, retrieve the root node
+ * of the constructed document tree
+ * @return the root node of the constructed tree. The result is undefined (maybe null, maybe an exception)
+ * if the method is called before successfully completing the sequence of events (of which the last should be
+ * {@link #writeEndDocument}) that constructs the tree.
+ * @throws SaxonApiException if any failure occurs
+ */
+
+ public XdmNode getDocumentNode() throws SaxonApiException;
+
+ /**
+ * Say whether prefixes are to be invented when none is specified by the user
+ * @param invent true if prefixes are to be invented. Default is true.
+ */
+
+ public void setInventPrefixes(boolean invent);
+
+ /**
+ * Ask whether prefixes are to be invented when none is specified by the user
+ * @return true if prefixes are to be invented. Default is true.
+ */
+
+ public boolean isInventPrefixes();
+
+ /**
+ * Say whether names and values are to be checked for conformance with XML rules
+ * @param check true if names and values are to be checked. Default is true.
+ */
+
+ public void setCheckValues(boolean check);
+
+ /**
+ * Ask whether names and values are to be checked for conformance with XML rules
+ * @return true if names and values are to be checked. Default is true.
+ */
+
+ public boolean isCheckValues();
+
+
+}
+
diff --git a/sf/saxon/s9api/BuildingStreamWriterImpl.java b/sf/saxon/s9api/BuildingStreamWriterImpl.java
new file mode 100644
index 0000000..caff5b5
--- /dev/null
+++ b/sf/saxon/s9api/BuildingStreamWriterImpl.java
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.StreamWriterToReceiver;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class is an implementation of {@link javax.xml.stream.XMLStreamWriter}, allowing
+ * a document to be constructed by means of a series of XMLStreamWriter method calls such
+ * as writeStartElement(), writeAttribute(), writeCharacters(), and writeEndElement().
+ *
+ * <p>The detailed way in which this class is packaged is carefully designed to ensure that
+ * if the functionality is not used, the <code>DocumentBuilder</code> is still usable under
+ * JDK 1.5 (which does not include javax.xml.stream interfaces).</p>
+*/
+
+public class BuildingStreamWriterImpl extends StreamWriterToReceiver implements BuildingStreamWriter {
+
+ Builder builder;
+
+ public BuildingStreamWriterImpl(Receiver receiver, Builder builder) {
+ super(receiver);
+ this.builder = builder;
+ builder.open();
+ }
+
+ /*@Nullable*/ public XdmNode getDocumentNode() throws SaxonApiException {
+ try {
+ builder.close();
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ return new XdmNode(builder.getCurrentRoot());
+ }
+}
diff --git a/sf/saxon/s9api/ConstructedItemType.java b/sf/saxon/s9api/ConstructedItemType.java
new file mode 100644
index 0000000..67c114c
--- /dev/null
+++ b/sf/saxon/s9api/ConstructedItemType.java
@@ -0,0 +1,101 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.type.TypeHierarchy;
+
+/**
+ * An item type constructed by the ItemTypeFactory (as distinct from one that is predefined)
+ *
+ * This class is not user-visible.
+ */
+
+class ConstructedItemType extends ItemType {
+
+ private net.sf.saxon.type.ItemType underlyingType;
+ private Processor processor;
+
+ /**
+ * Protected constructor
+ * @param underlyingType the Saxon internal item type. Must not be null.
+ * @param processor The s9api processor Must not be null
+ */
+
+ protected ConstructedItemType(/*@Nullable*/ net.sf.saxon.type.ItemType underlyingType, Processor processor) {
+ if (processor == null) {
+ throw new NullPointerException("processor");
+ }
+ if (underlyingType == null) {
+ throw new NullPointerException("underlyingType");
+ }
+ this.processor = processor;
+ this.underlyingType = underlyingType;
+ }
+
+ /**
+ * Get the conversion rules implemented by this type. The conversion rules reflect variations
+ * between different versions of the W3C specifications, for example XSD 1.1 allows "+INF" as
+ * a lexical representation of xs:double, while XSD 1.0 does not.
+ * @return the conversion rules
+ */
+
+ public ConversionRules getConversionRules() {
+ return processor.getUnderlyingConfiguration().getConversionRules();
+ }
+
+ /**
+ * Determine whether this item type matches a given item.
+ *
+ * @param item the item to be tested against this item type
+ * @return true if the item matches this item type, false if it does not match.
+ */
+
+ public boolean matches(XdmItem item) {
+ return underlyingType.matchesItem(
+ (Item)item.getUnderlyingValue(), false, processor.getUnderlyingConfiguration());
+ }
+
+ /**
+ * Determine whether this ItemType subsumes another ItemType. Specifically,
+ * <code>A.subsumes(B) is true if every value that matches the ItemType B also matches
+ * the ItemType A.
+ * @param other the other ItemType
+ * @return true if this ItemType subsumes the other ItemType. This includes the case where A and B
+ * represent the same ItemType.
+ * @since 9.1
+ */
+
+ public boolean subsumes(ItemType other) {
+ TypeHierarchy th = processor.getUnderlyingConfiguration().getTypeHierarchy();
+ return th.isSubType(other.getUnderlyingItemType(), underlyingType);
+ }
+
+ /**
+ * Method to get the underlying Saxon implementation object
+ *
+ * <p>This gives access to Saxon methods that may change from one release to another.</p>
+ * @return the underlying Saxon implementation object
+ */
+
+ public net.sf.saxon.type.ItemType getUnderlyingItemType() {
+ return underlyingType;
+ }
+
+ /**
+ * Get the underlying Processor
+ * @return the processor used to create this ItemType. This will be null if the ItemType is one of the three
+ * static constant item types {@link #ANY_ITEM}, {@link #ANY_NODE}, or {@link #ANY_ATOMIC_VALUE}
+ */
+
+ protected Processor getProcessor() {
+ return processor;
+ }
+}
+
diff --git a/sf/saxon/s9api/DOMDestination.java b/sf/saxon/s9api/DOMDestination.java
new file mode 100644
index 0000000..47162cb
--- /dev/null
+++ b/sf/saxon/s9api/DOMDestination.java
@@ -0,0 +1,60 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.dom.DOMWriter;
+import net.sf.saxon.event.Receiver;
+
+/**
+ * This class represents a Destination (for example, the destination of the output of a transformation)
+ * in which the results are written to a newly constructed DOM tree in memory. The caller must supply
+ * a Document node, which will be used as the root of the constructed tree
+ */
+
+public class DOMDestination implements Destination {
+
+ private DOMWriter domWriter;
+
+ /**
+ * Create a DOMDestination, supplying the root of a DOM document to which the
+ * content of the result tree will be appended.
+ * @param root the root node for the new tree.
+ */
+
+ public DOMDestination(org.w3c.dom.Document root) {
+ domWriter = new DOMWriter();
+ domWriter.setNode(root);
+ }
+
+ /**
+ * Return a Receiver. Saxon calls this method to obtain a Receiver, to which it then sends
+ * a sequence of events representing the content of an XML document.
+ *
+ * @param config The Saxon configuration. This is supplied so that the destination can
+ * use information from the configuration (for example, a reference to the name pool)
+ * to construct or configure the returned Receiver.
+ * @return the Receiver to which events are to be sent.
+ * @throws SaxonApiException
+ * if the Receiver cannot be created
+ */
+
+ public Receiver getReceiver(/*@NotNull*/ Configuration config) throws SaxonApiException {
+ domWriter.setPipelineConfiguration(config.makePipelineConfiguration());
+ return domWriter;
+ }
+
+ /**
+ * Close the destination, allowing resources to be released. Saxon calls this method when
+ * it has finished writing to the destination.
+ */
+
+ public void close() throws SaxonApiException {
+ // no action
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/s9api/Destination.java b/sf/saxon/s9api/Destination.java
new file mode 100644
index 0000000..078d35c
--- /dev/null
+++ b/sf/saxon/s9api/Destination.java
@@ -0,0 +1,74 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+
+/**
+ * A Destination represents a place where XML can be sent. It is used, for example,
+ * to define the output of a transformation or query.
+ *
+ * <p>In general a <code>Destination</code> is designed to hold a single document.
+ * It should therefore not be used as the destination of a query that produces multiple
+ * documents. The effect of sending multiple documents to a <code>Destination</code>
+ * depends on the kind of <code>Destination</code>.</p>
+ *
+ * <p>The interface <code>Destination</code> has some similarities with the JAXP
+ * {@link javax.xml.transform.Result} class. It differs, however, in that implementations
+ * of this interface can be written by users or third parties to define new kinds of
+ * destination, and any such implementation can be supplied to the Saxon methods that
+ * take a <code>Destination</code> as an argument.</p>
+ *
+ * <p>Implementing a new <code>Destination</code>, however, will generally require access
+ * to implementation-level classes and methods within the Saxon product. The only method that
+ * needs to be supplied is {@link #getReceiver}, which returns an instance of
+ * {@link net.sf.saxon.event.Receiver}, and unless you use an existing implementation of
+ * <code>Receiver</code>, you will need to handle internal Saxon concepts such as name codes
+ * and name pools.</p>
+ *
+ * <p>In general a Destination is not thread-safe (cannot be used from more than one thread),
+ * and is not serially reusable. The {@link #close} method is called by the system when
+ * it finishes writing the document, and this causes all resources held by the Destination
+ * to be released.</p>
+ */
+public interface Destination {
+
+ /**
+ * Return a Receiver. Saxon calls this method to obtain a Receiver, to which it then sends
+ * a sequence of events representing the content of an XML document. The method is intended
+ * primarily for internal use, and may give poor diagnostics if used incorrectly.
+ * @param config The Saxon configuration. This is supplied so that the destination can
+ * use information from the configuration (for example, a reference to the name pool)
+ * to construct or configure the returned Receiver.
+ * @return the Receiver to which events are to be sent. It is the caller's responsibility to
+ * initialize this Receiver with a {@link net.sf.saxon.event.PipelineConfiguration} before calling
+ * its <code>open()</code> method. The caller is also responsible for ensuring that the sequence
+ * of events sent to the Receiver represents a well-formed document: in particular the event
+ * stream must include namespace events corresponding exactly to the namespace declarations
+ * that are required. If the calling code cannot guarantee this, it should insert a
+ * {@link net.sf.saxon.event.NamespaceReducer} into the pipeline in front of the returned
+ * Receiver.
+ * @throws SaxonApiException if the Receiver cannot be created
+ */
+
+ public Receiver getReceiver(Configuration config) throws SaxonApiException;
+
+ /**
+ * Close the destination, allowing resources to be released. Saxon calls this method when
+ * it has finished writing to the destination.
+ *
+ * <p>The close() method should not cause any adverse effects if it is called more than
+ * once. If any other method is called after the close() call, the results are undefined.
+ * This means that a Destination is not, in general, serially reusable.</p>
+ * @throws SaxonApiException if any failure occurs
+ */
+
+ public void close() throws SaxonApiException;
+}
+
diff --git a/sf/saxon/s9api/DocumentBuilder.java b/sf/saxon/s9api/DocumentBuilder.java
new file mode 100644
index 0000000..2ac5b6f
--- /dev/null
+++ b/sf/saxon/s9api/DocumentBuilder.java
@@ -0,0 +1,524 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.*;
+import net.sf.saxon.expr.EarlyEvaluationContext;
+import net.sf.saxon.expr.JPConverter;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.lib.AugmentedSource;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.FingerprintedQName;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.TreeModel;
+import net.sf.saxon.query.XQueryExpression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import java.io.File;
+import java.net.URI;
+
+/**
+ * A document builder holds properties controlling how a Saxon document tree should be built, and
+ * provides methods to invoke the tree construction.
+ * <p/>
+ * <p>This class has no public constructor. Users should construct a DocumentBuilder
+ * by calling the factory method {@link net.sf.saxon.s9api.Processor#newDocumentBuilder()}.</p>
+ * <p/>
+ * <p>All documents used in a single Saxon query, transformation, or validation episode must
+ * be built with the same {@link Configuration}. However, there is no requirement that they
+ * should use the same <code>DocumentBuilder</code>.</p>
+ * <p/>
+ * <p>Sharing of a <code>DocumentBuilder</code> across multiple threads is not recommended. However,
+ * in the current implementation sharing a <code>DocumentBuilder</code> (once initialized) will only
+ * cause problems if a <code>SchemaValidator</code> is used.</p>
+ *
+ * @since 9.0
+ */
+
+public class DocumentBuilder {
+
+ private Configuration config;
+ private SchemaValidator schemaValidator;
+ private boolean dtdValidation;
+ private boolean lineNumbering;
+ private TreeModel treeModel = TreeModel.TINY_TREE;
+ private WhitespaceStrippingPolicy whitespacePolicy;
+ private URI baseURI;
+ private XQueryExecutable projectionQuery;
+
+ // TODO: combine the functionality of this class with that of XdmDestination
+
+ /**
+ * Create a DocumentBuilder. This is a protected constructor. Users should construct a DocumentBuilder
+ * by calling the factory method {@link net.sf.saxon.s9api.Processor#newDocumentBuilder()}.
+ *
+ * @param config the Saxon configuration
+ */
+
+ protected DocumentBuilder(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Set the tree model to be used for documents constructed using this DocumentBuilder.
+ * By default, the TinyTree is used (irrespective of the TreeModel set in the underlying
+ * Configuration).
+ *
+ * @param model typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link TreeModel#TINY_TREE_CONDENSED}, or {@link TreeModel#LINKED_TREE}. It can also be
+ * an external object model such as {@link net.sf.saxon.option.xom.XOMObjectModel}
+ * @since 9.2
+ */
+
+ public void setTreeModel(TreeModel model) {
+ this.treeModel = model;
+ }
+
+ /**
+ * Get the tree model to be used for documents constructed using this DocumentBuilder.
+ * By default, the TinyTree is used (irrespective of the TreeModel set in the underlying
+ * Configuration).
+ *
+ * @return the tree model in use: typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link net.sf.saxon.om.TreeModel#TINY_TREE_CONDENSED}, or {@link TreeModel#LINKED_TREE}. However, in principle
+ * a user-defined tree model can be used.
+ * @since 9.2
+ */
+
+ public TreeModel getTreeModel() {
+ return treeModel;
+ }
+
+ /**
+ * Say whether line numbering is to be enabled for documents constructed using this DocumentBuilder.
+ * This has the effect that the line number in the original source document is maintained in the constructed
+ * tree, for each element node (and only for elements). The line number in question is generally the line number
+ * on which the closing ">" of the element start tag appears.
+ * <p/>
+ * <p>By default, line numbers are not maintained.</p>
+ * <p/>
+ * <p>Errors relating to document parsing and validation will generally contain line numbers whether or not
+ * this option is set, because such errors are detected during document construction.</p>
+ * <p/>
+ * <p>Line numbering is not available for all kinds of source: for example,
+ * it is not available when loading from an existing DOM Document.</p>
+ * <p/>
+ * <p>The resulting line numbers are accessible to applications using the
+ * XPath extension function saxon:line-number() applied to a node, or using the
+ * Java method {@link net.sf.saxon.om.NodeInfo#getLineNumber()} </p>
+ * <p/>
+ * <p>Line numbers are maintained only for element nodes; the line number
+ * returned for any other node will be that of the most recent element. For an element node, the
+ * line number is generally that of the closing angle bracket at the end of the start tag
+ * (this is what a SAX parser notifies)</p>
+ *
+ * @param option true if line numbers are to be maintained, false otherwise.
+ */
+
+ public void setLineNumbering(boolean option) {
+ lineNumbering = option;
+ }
+
+ /**
+ * Ask whether line numbering is enabled for documents loaded using this
+ * <code>DocumentBuilder</code>.
+ * <p/>
+ * <p>By default, line numbering is disabled.</p>
+ * <p/>
+ * <p>Line numbering is not available for all kinds of source: in particular,
+ * it is not available when loading from an existing XmlDocument.</p>
+ * <p/>
+ * <p>The resulting line numbers are accessible to applications using the
+ * extension function saxon:line-number() applied to a node, or using the
+ * Java method {@link net.sf.saxon.om.NodeInfo#getLineNumber()}</p>
+ * <p/>
+ * <p>Line numbers are maintained only for element nodes; the line number
+ * returned for any other node will be that of the most recent element. For an element node, the
+ * line number is generally that of the closing angle bracket at the end of the start tag
+ * (this is what a SAX parser notifies)</p>
+ *
+ * @return true if line numbering is enabled
+ */
+
+ public boolean isLineNumbering() {
+ return lineNumbering;
+ }
+
+ /**
+ * Set the schemaValidator to be used. This determines whether schema validation is applied to an input
+ * document and whether type annotations in a supplied document are retained. If no schemaValidator
+ * is supplied, then schema validation does not take place.
+ * <p/>
+ * <p>This option requires the schema-aware version of the Saxon product (Saxon-EE).</p>
+ * <p/>
+ * <p>Since a <code>SchemaValidator</code> is serially reusable but not thread-safe, using this
+ * method is not appropriate when the <code>DocumentBuilder</code> is shared between threads.</p>
+ *
+ * @param validator the SchemaValidator to be used
+ */
+
+ public void setSchemaValidator(SchemaValidator validator) {
+ schemaValidator = validator;
+ }
+
+ /**
+ * Get the SchemaValidator used to validate documents loaded using this
+ * <code>DocumentBuilder</code>.
+ *
+ * @return the SchemaValidator if one has been set; otherwise null.
+ */
+ public SchemaValidator getSchemaValidator() {
+ return schemaValidator;
+ }
+
+ /**
+ * Set whether DTD validation should be applied to documents loaded using this
+ * <code>DocumentBuilder</code>.
+ * <p/>
+ * <p>By default, no DTD validation takes place.</p>
+ *
+ * @param option true if DTD validation is to be applied to the document
+ */
+
+ public void setDTDValidation(boolean option) {
+ dtdValidation = option;
+ }
+
+ /**
+ * Ask whether DTD validation is to be applied to documents loaded using this <code>DocumentBuilder</code>
+ *
+ * @return true if DTD validation is to be applied
+ */
+
+ public boolean isDTDValidation() {
+ return dtdValidation;
+ }
+
+ /**
+ * Set the whitespace stripping policy applied when loading a document
+ * using this <code>DocumentBuilder</code>.
+ * <p/>
+ * <p>By default, whitespace text nodes appearing in element-only content
+ * are stripped, and all other whitespace text nodes are retained.</p>
+ *
+ * @param policy the policy for stripping whitespace-only text nodes from
+ * source documents
+ */
+
+ public void setWhitespaceStrippingPolicy(WhitespaceStrippingPolicy policy) {
+ whitespacePolicy = policy;
+ }
+
+ /**
+ * Get the white whitespace stripping policy applied when loading a document
+ * using this <code>DocumentBuilder</code>.
+ *
+ * @return the policy for stripping whitespace-only text nodes
+ */
+
+ public WhitespaceStrippingPolicy getWhitespaceStrippingPolicy() {
+ return whitespacePolicy;
+ }
+
+ /**
+ * Set the base URI of a document loaded using this <code>DocumentBuilder</code>.
+ * <p/>
+ * <p>This is used for resolving any relative URIs appearing
+ * within the document, for example in references to DTDs and external entities.</p>
+ * <p/>
+ * <p>This information is required when the document is loaded from a source that does not
+ * provide an intrinsic URI, notably when loading from a Stream or a DOMSource. The value is
+ * ignored when loading from a source that does have an intrinsic base URI.</p>
+ *
+ * @param uri the base URI of documents loaded using this <code>DocumentBuilder</code>. This
+ * must be an absolute URI.
+ * @throws IllegalArgumentException if the baseURI supplied is not an absolute URI
+ */
+
+ public void setBaseURI(URI uri) {
+ if (!uri.isAbsolute()) {
+ throw new IllegalArgumentException("Supplied base URI must be absolute");
+ }
+ baseURI = uri;
+ }
+
+ /**
+ * Get the base URI of documents loaded using this DocumentBuilder when no other URI is available.
+ *
+ * @return the base URI to be used, or null if no value has been set.
+ */
+
+ public URI getBaseURI() {
+ return baseURI;
+ }
+
+ /**
+ * Set a compiled query to be used for implementing document projection. The effect of using
+ * this option is that the tree constructed by the DocumentBuilder contains only those parts
+ * of the source document that are needed to answer this query. Running this query against
+ * the projected document should give the same results as against the raw document, but the
+ * projected document typically occupies significantly less memory. It is permissible to run
+ * other queries against the projected document, but unless they are carefully chosen, they
+ * will give the wrong answer, because the document being used is different from the original.
+ * <p>The query should be written to use the projected document as its initial context item.
+ * For example, if the query is <code>//ITEM[COLOR='blue')</code>, then only <code>ITEM</code>
+ * elements and their <code>COLOR</code> children will be retained in the projected document.</p>
+ * <p>This facility is only available in Saxon-EE; if the facility is not available,
+ * calling this method has no effect.</p>
+ * @param query the compiled query used to control document projection
+ * @since 9.3
+ */
+
+ public void setDocumentProjectionQuery(XQueryExecutable query) {
+ this.projectionQuery = query;
+ }
+
+ /**
+ * Get the compiled query to be used for implementing document projection.
+ * @return the query set using {@link #setDocumentProjectionQuery} if this
+ * has been called, or null otherwise
+ * @since 9.3. In 9.4 the unused and undocumented first argument is removed.
+ */
+
+ public XQueryExecutable getDocumentProjectionQuery() {
+ return this.projectionQuery;
+ }
+
+ /**
+ * Load an XML document, to create a tree representation of the document in memory.
+ *
+ * @param source A JAXP Source object identifying the source of the document. This can always be
+ * a {@link javax.xml.transform.stream.StreamSource} or a {@link javax.xml.transform.sax.SAXSource}.
+ * Some kinds of Source are consumed by this method, and should only be used once.
+ * <p/>
+ * <p>If a SAXSource is supplied, the XMLReader held within the SAXSource may be modified (by setting
+ * features and properties) to reflect the options selected on this DocumentBuilder.</p>
+ * <p>An instance of {@link javax.xml.transform.dom.DOMSource} is accepted provided that the Saxon support
+ * code for DOM (in saxon9-dom.jar) is on the classpath.</p>
+ * <p/>
+ * <p>If the source is an instance of {@link net.sf.saxon.om.NodeInfo} then the subtree rooted at this node
+ * will be copied (applying schema validation if requested) to create a new tree.</p>
+ * <p/>
+ * <p>Saxon also accepts an instance of {@link javax.xml.transform.stax.StAXSource} or
+ * {@link net.sf.saxon.pull.PullSource}, which can be used to supply a document that is to be parsed
+ * using a StAX parser.</p>
+ * <p>(9.2) This method no longer accepts an instance of {@link net.sf.saxon.lib.AugmentedSource}, because of
+ * confusion over interactions between the properties of the AugmentedSource and the properties
+ * of this DocumentBuilder.</p>
+ * @return An <code>XdmNode</code>. This will be
+ * the document node at the root of the tree of the resulting in-memory document.
+ * @throws NullPointerException if the source argument is null
+ * @throws IllegalArgumentException if the kind of source is not recognized
+ * @throws SaxonApiException if any other failure occurs building the document, for example
+ * a parsing error
+ */
+
+ public XdmNode build(/*@Nullable*/ Source source) throws SaxonApiException {
+ if (source == null) {
+ throw new NullPointerException("source");
+ }
+ if (source instanceof AugmentedSource) {
+ throw new IllegalArgumentException("AugmentedSource not accepted");
+ }
+ ParseOptions options = new ParseOptions(config.getParseOptions());
+ options.setDTDValidationMode(dtdValidation ? Validation.STRICT : Validation.STRIP);
+ if (schemaValidator != null) {
+ options.setSchemaValidationMode(schemaValidator.isLax() ? Validation.LAX : Validation.STRICT);
+ if (schemaValidator.getDocumentElementName() != null) {
+ QName qn = schemaValidator.getDocumentElementName();
+ options.setTopLevelElement(new FingerprintedQName(
+ qn.getPrefix(), qn.getNamespaceURI(), qn.getLocalName()));
+ }
+ if (schemaValidator.getDocumentElementType() != null) {
+ options.setTopLevelType(schemaValidator.getDocumentElementType());
+ }
+ }
+ if (treeModel != null) {
+ options.setModel(treeModel);
+ }
+ if (whitespacePolicy != null) {
+ int option = whitespacePolicy.ordinal();
+ if (option == Whitespace.XSLT) {
+ options.setStripSpace(Whitespace.NONE);
+ options.addFilter(whitespacePolicy.makeStripper());
+ } else {
+ options.setStripSpace(option);
+ }
+ }
+ options.setLineNumbering(lineNumbering);
+ if (source.getSystemId() == null && baseURI != null) {
+ source.setSystemId(baseURI.toString());
+ }
+ if (projectionQuery != null) {
+ XQueryExpression exp = projectionQuery.getUnderlyingCompiledQuery();
+ PathMap map = exp.getPathMap();
+ PathMap.PathMapRoot contextRoot = map.getContextDocumentRoot();
+ if (contextRoot != null) {
+ if (contextRoot.hasUnknownDependencies()) {
+ // No action (no document projection takes place)
+ } else {
+ options.addFilter(config.makeDocumentProjector(contextRoot));
+ }
+ } else {
+ // No action, query does not access the context item
+ }
+ }
+ try {
+ NodeInfo doc = config.buildDocument(source, options);
+ return new XdmNode(doc);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Build a document from a supplied XML file
+ * @param file the supplied file
+ * @return the XdmNode representing the root of the document tree
+ * @throws SaxonApiException if any failure occurs retrieving or parsing the document
+ */
+
+ public XdmNode build(File file) throws SaxonApiException {
+ return build(new StreamSource(file));
+ }
+
+ /**
+ * Get an {@link org.xml.sax.ContentHandler} that may be used to build the document programmatically.
+ * @return a newly constructed {@link BuildingContentHandler}, which implements the <code>ContentHandler</code>
+ * interface. If schema validation has been requested for this <code>DocumentBuilder</code>, then the document constructed
+ * using the <code>ContentHandler</code> will be validated as it is written.
+ * <p>Note that the returned <code>ContentHandler</code> expects namespace scopes to be indicated
+ * explicitly by calls to {@link org.xml.sax.ContentHandler#startPrefixMapping} and
+ * {@link org.xml.sax.ContentHandler#endPrefixMapping}.</p>
+ * <p>If the stream of events supplied to the <code>ContentHandler</code> does not constitute
+ * a well formed (and namespace-well-formed) document, the effect is undefined; Saxon may fail
+ * to detect the error, and construct an unusable tree. </p>
+ * @throws SaxonApiException if any failure occurs
+ * @since 9.3
+ */
+
+ public BuildingContentHandler newBuildingContentHandler() throws SaxonApiException {
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ Builder builder = treeModel.makeBuilder(pipe);
+ builder.setLineNumbering(lineNumbering);
+ Receiver r = builder;
+ r = new NamespaceReducer(r);
+ if (schemaValidator != null) {
+ r = schemaValidator.getReceiver(config);
+ r.setPipelineConfiguration(pipe);
+ if (r instanceof ProxyReceiver) {
+ ((ProxyReceiver)r).setUnderlyingReceiver(builder);
+ }
+ }
+ return new BuildingContentHandlerImpl(r, builder);
+ }
+
+ /**
+ * Private implementation of BuildingContentHandler
+ */
+
+ private static class BuildingContentHandlerImpl extends ReceivingContentHandler
+ implements BuildingContentHandler {
+
+ private Builder builder;
+
+ public BuildingContentHandlerImpl(Receiver r, Builder b) {
+ setReceiver(r);
+ setPipelineConfiguration(r.getPipelineConfiguration());
+ this.builder = b;
+ }
+
+ public XdmNode getDocumentNode() throws SaxonApiException {
+ return new XdmNode(builder.getCurrentRoot());
+ }
+ }
+
+ /**
+ * Get an {@link javax.xml.stream.XMLStreamWriter} that may be used to build the document programmatically.
+ * @return a newly constructed {@link BuildingStreamWriter}, which implements the <code>XMLStreamWriter</code>
+ * interface. If schema validation has been requested for this <code>DocumentBuilder</code>, then the document constructed
+ * using the <code>XMLStreamWriter</code> will be validated as it is written.
+ * <p>If the stream of events supplied to the <code>XMLStreamWriter</code> does not constitute
+ * a well formed (and namespace-well-formed) document, the effect is undefined; Saxon may fail
+ * to detect the error, and construct an unusable tree. </p>
+ * @throws SaxonApiException if any failure occurs
+ * @since 9.3
+ */
+
+ public BuildingStreamWriterImpl newBuildingStreamWriter() throws SaxonApiException {
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ Builder builder = treeModel.makeBuilder(pipe);
+ builder.setLineNumbering(lineNumbering);
+ Receiver r = builder;
+ r = new NamespaceReducer(r);
+ if (schemaValidator != null) {
+ r = schemaValidator.getReceiver(config);
+ r.setPipelineConfiguration(pipe);
+ if (r instanceof ProxyReceiver) {
+ ((ProxyReceiver)r).setUnderlyingReceiver(builder);
+ }
+ }
+ return new BuildingStreamWriterImpl(r, builder);
+ }
+
+ /**
+ * Create a node by wrapping a recognized external node from a supported object model.
+ *
+ * <p>If the supplied object implements the {@link net.sf.saxon.om.NodeInfo} interface then it
+ * will be wrapped as an <code>XdmNode</code> without copying and without change. The <code>NodeInfo</code>
+ * must have been created using a {@link net.sf.saxon.Configuration} compatible
+ * with the one used by this <code>Processor</code> (specifically, one that uses the same
+ * {@link net.sf.saxon.om.NamePool})</p>
+ *
+ * <p>To wrap nodes from other object models, such as DOM, the support module for the external object
+ * model must be on the class path and registered with the Saxon configuration. The support modules
+ * for DOM, JDOM, DOM4J and XOM are registered automatically if they can be found on the classpath.</p>
+ *
+ * <p>It is best to avoid calling this method repeatedly to wrap different nodes in the same document.
+ * Each such wrapper conceptually creates a new XDM tree instance with its own identity. Although the
+ * memory is shared, operations that rely on node identity might not have the expected result. It is
+ * best to create a single wrapper for the document node, and then to navigate to the other nodes in the
+ * tree using S9API interfaces.</p>
+ *
+ * @param node the node in the external tree representation. Either an instance of
+ * {@link net.sf.saxon.om.NodeInfo}, or an instances of a node in an external object model.
+ * Nodes in other object models (such as DOM, JDOM, etc) are recognized only if
+ * the support module for the external object model is known to the Configuration.
+ * @return the supplied node wrapped as an XdmNode
+ * @throws IllegalArgumentException if the type of object supplied is not recognized. This may be because
+ * node was created using a different Saxon Processor, or because the required code for the external
+ * object model is not on the class path
+ */
+
+ public XdmNode wrap(Object node) throws IllegalArgumentException {
+ if (node instanceof NodeInfo) {
+ NodeInfo nodeInfo = (NodeInfo)node;
+ if (nodeInfo.getConfiguration().isCompatible(config)) {
+ return new XdmNode((NodeInfo)node);
+ } else {
+ throw new IllegalArgumentException("Supplied NodeInfo was created using a different Configuration");
+ }
+ } else {
+ try {
+ JPConverter converter = JPConverter.allocate(node.getClass(), config);
+ NodeInfo nodeInfo = (NodeInfo)converter.convert(node, new EarlyEvaluationContext(config, null));
+ return (XdmNode)XdmItem.wrapItem(nodeInfo);
+ } catch (XPathException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/s9api/ExtensionFunction.java b/sf/saxon/s9api/ExtensionFunction.java
new file mode 100644
index 0000000..8d92fb7
--- /dev/null
+++ b/sf/saxon/s9api/ExtensionFunction.java
@@ -0,0 +1,55 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+/**
+ * This is an interface for simple external/extension functions. Users can implement this
+ * interface and register the implementation with the {@link Processor}; the function will
+ * then be available for calling from all queries, stylesheets, and XPath expressions compiled
+ * under this Processor.
+ *
+ * <p>Extension functions implemented using this interface are expected to be free of side-effects,
+ * and to have no dependencies on the static or dynamic context. A richer interface for extension
+ * functions is provided via the {@link net.sf.saxon.lib.ExtensionFunctionDefinition} class.</p>
+ */
+public interface ExtensionFunction {
+
+ /**
+ * Return the name of the external function
+ * @return the name of the function, as a QName.
+ */
+ public QName getName();
+
+ /**
+ * Declare the result type of the external function
+ * @return the result type of the external function
+ */
+
+ public SequenceType getResultType();
+
+ /**
+ * Declare the types of the arguments
+ * @return an array of SequenceType objects, one for each argument to the function,
+ * representing the expected types of the arguments
+ */
+
+ public SequenceType[] getArgumentTypes();
+
+ /**
+ * Call the function. The implementation of this method represents the body of the external function.
+ * @param arguments the arguments, as supplied in the XPath function call. These will always be of
+ * the declared types. Arguments are converted to the required types according to the standard XPath
+ * function conversion rules - for example, if the expected type is atomic and a node is supplied in the
+ * call, the node will be atomized
+ * @return the result of the function. This must be an instance of the declared return type; if it is not,
+ * a dynamic error will be reported
+ * @throws SaxonApiException can be thrown if the implementation of the function detects a dynamic error
+ */
+
+ /*@NotNull*/ public XdmValue call(XdmValue[] arguments) throws SaxonApiException;
+}
diff --git a/sf/saxon/s9api/ItemType.java b/sf/saxon/s9api/ItemType.java
new file mode 100644
index 0000000..15e3d47
--- /dev/null
+++ b/sf/saxon/s9api/ItemType.java
@@ -0,0 +1,509 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.StandardURIChecker;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Name10Checker;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * An item type, as defined in the XPath/XQuery specifications.
+ *
+ * <p>This class contains a number of static constant fields
+ * referring to instances that represent simple item types, such as
+ * <code>item()</code>, <code>node()</code>, and <code>xs:anyAtomicType</code>. These named types are currently
+ * based on the definitions in XSD 1.0 and XML 1.0. They may be changed in a future version to be based
+ * on a later version.</p>
+ *
+ * <p>More complicated item types, especially those that are dependent on information in a schema,
+ * are available using factory methods on the {@link ItemTypeFactory} object. The factory methods can
+ * also be used to create variants of the types that use the rules given in the XML 1.1 and/or XSD 1.1 specifications.</p>
+ */
+
+public abstract class ItemType {
+
+ private static ConversionRules defaultConversionRules = new ConversionRules();
+
+ static {
+ defaultConversionRules.setNameChecker(Name10Checker.getInstance());
+ defaultConversionRules.setStringToDoubleConverter(StringToDouble.getInstance());
+ defaultConversionRules.setNotationSet(null);
+ defaultConversionRules.setURIChecker(StandardURIChecker.getInstance());
+ }
+
+ /**
+ * ItemType representing the type item(), that is, any item at all
+ */
+
+ public static ItemType ANY_ITEM = new ItemType() {
+
+ public ConversionRules getConversionRules() {
+ return defaultConversionRules;
+ }
+
+ public boolean matches(XdmItem item) {
+ return true;
+ }
+
+ public boolean subsumes(ItemType other) {
+ return true;
+ }
+
+ public net.sf.saxon.type.ItemType getUnderlyingItemType() {
+ return AnyItemType.getInstance();
+ }
+ };
+
+ /**
+ * ItemType representing the type node(), that is, any node
+ */
+
+ public static final ItemType ANY_NODE = new ItemType() {
+
+ public ConversionRules getConversionRules() {
+ return defaultConversionRules;
+ }
+
+ public boolean matches(XdmItem item) {
+ return item.getUnderlyingValue() instanceof NodeInfo;
+ }
+
+ public boolean subsumes(ItemType other) {
+ return other.getUnderlyingItemType() instanceof NodeTest;
+ }
+
+ public net.sf.saxon.type.ItemType getUnderlyingItemType() {
+ return AnyNodeTest.getInstance();
+ }
+ };
+
+ /**
+ * ItemType representing the type xs:anyAtomicType, that is, any atomic value
+ */
+
+ public static final ItemType ANY_ATOMIC_VALUE = new ItemType() {
+
+ public ConversionRules getConversionRules() {
+ return defaultConversionRules;
+ }
+
+ public boolean matches(XdmItem item) {
+ return item.getUnderlyingValue() instanceof AtomicValue;
+ }
+
+ public boolean subsumes(ItemType other) {
+ return other.getUnderlyingItemType() instanceof AtomicType;
+ }
+
+ public net.sf.saxon.type.ItemType getUnderlyingItemType() {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+ };
+
+ static class BuiltInAtomicItemType extends ItemType {
+
+ private BuiltInAtomicType underlyingType;
+ private ConversionRules conversionRules;
+
+ public BuiltInAtomicItemType(BuiltInAtomicType underlyingType, ConversionRules conversionRules) {
+ this.underlyingType = underlyingType;
+ this.conversionRules = conversionRules;
+ }
+
+ public static BuiltInAtomicItemType makeVariant(BuiltInAtomicItemType type, ConversionRules conversionRules) {
+ // TODO: allocate from a pool
+ return new BuiltInAtomicItemType(type.underlyingType, conversionRules);
+ }
+
+ public ConversionRules getConversionRules() {
+ return conversionRules;
+ }
+
+ public boolean matches(XdmItem item) {
+ Item value = (Item)item.getUnderlyingValue();
+ if (!(value instanceof AtomicValue)) {
+ return false;
+ }
+ AtomicType type = ((AtomicValue) value).getItemType();
+ return subsumesUnderlyingType(type);
+ }
+
+ public boolean subsumes(ItemType other) {
+ net.sf.saxon.type.ItemType otherType = other.getUnderlyingItemType();
+ if (!(otherType.isPlainType())) {
+ return false;
+ }
+ AtomicType type = (AtomicType)otherType;
+ return subsumesUnderlyingType(type);
+ }
+
+ private boolean subsumesUnderlyingType(AtomicType type) {
+ BuiltInAtomicType builtIn =
+ (type instanceof BuiltInAtomicType ? ((BuiltInAtomicType)type) : (BuiltInAtomicType)type.getBuiltInBaseType());
+ while (true) {
+ if (builtIn.isSameType(underlyingType)) {
+ return true;
+ }
+ SchemaType base = builtIn.getBaseType();
+ if (!(base instanceof BuiltInAtomicType)) {
+ return false;
+ }
+ builtIn = (BuiltInAtomicType)base;
+ }
+ }
+
+ public net.sf.saxon.type.ItemType getUnderlyingItemType() {
+ return underlyingType;
+ }
+ }
+
+ /**
+ * A Saxon-specific item type representing the base type of double, float, and decimal
+ */
+
+ public static final ItemType NUMERIC = new BuiltInAtomicItemType(BuiltInAtomicType.NUMERIC, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:string
+ */
+
+ public static final ItemType STRING = new BuiltInAtomicItemType(BuiltInAtomicType.STRING, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:boolean
+ */
+
+ public static final ItemType BOOLEAN = new BuiltInAtomicItemType(BuiltInAtomicType.BOOLEAN, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:duration
+ */
+
+ public static final ItemType DURATION = new BuiltInAtomicItemType(BuiltInAtomicType.DURATION, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:dateTime
+ */
+
+ public static final ItemType DATE_TIME = new BuiltInAtomicItemType(BuiltInAtomicType.DATE_TIME, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:date
+ */
+
+ public static final ItemType DATE = new BuiltInAtomicItemType(BuiltInAtomicType.DATE, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:time
+ */
+
+ public static final ItemType TIME = new BuiltInAtomicItemType(BuiltInAtomicType.TIME, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:gYearMonth
+ */
+
+ public static final ItemType G_YEAR_MONTH = new BuiltInAtomicItemType(BuiltInAtomicType.G_YEAR_MONTH, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:gMonth
+ */
+
+ public static final ItemType G_MONTH = new BuiltInAtomicItemType(BuiltInAtomicType.G_MONTH, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:gMonthDay
+ */
+
+ public static final ItemType G_MONTH_DAY = new BuiltInAtomicItemType(BuiltInAtomicType.G_MONTH_DAY, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:gYear
+ */
+
+ public static final ItemType G_YEAR = new BuiltInAtomicItemType(BuiltInAtomicType.G_YEAR, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:gDay
+ */
+
+ public static final ItemType G_DAY = new BuiltInAtomicItemType(BuiltInAtomicType.G_DAY, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:hexBinary
+ */
+
+ public static final ItemType HEX_BINARY = new BuiltInAtomicItemType(BuiltInAtomicType.HEX_BINARY, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:base64Binary
+ */
+
+ public static final ItemType BASE64_BINARY = new BuiltInAtomicItemType(BuiltInAtomicType.BASE64_BINARY, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:anyURI
+ */
+
+ public static final ItemType ANY_URI = new BuiltInAtomicItemType(BuiltInAtomicType.ANY_URI, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:QName
+ */
+
+ public static final ItemType QNAME = new BuiltInAtomicItemType(BuiltInAtomicType.QNAME, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:NOTATION
+ */
+
+ public static final ItemType NOTATION = new BuiltInAtomicItemType(BuiltInAtomicType.NOTATION, defaultConversionRules);
+
+ /**
+ * ItemType representing the XPath-defined type xs:untypedAtomic
+ */
+
+ public static final ItemType UNTYPED_ATOMIC = new BuiltInAtomicItemType(BuiltInAtomicType.UNTYPED_ATOMIC, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:decimal
+ */
+
+ public static final ItemType DECIMAL = new BuiltInAtomicItemType(BuiltInAtomicType.DECIMAL, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:float
+ */
+
+ public static final ItemType FLOAT = new BuiltInAtomicItemType(BuiltInAtomicType.FLOAT, defaultConversionRules);
+
+ /**
+ * ItemType representing the primitive type xs:double
+ */
+
+ public static final ItemType DOUBLE = new BuiltInAtomicItemType(BuiltInAtomicType.DOUBLE, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:integer
+ */
+
+ public static final ItemType INTEGER = new BuiltInAtomicItemType(BuiltInAtomicType.INTEGER, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:nonPositiveInteger
+ */
+
+ public static final ItemType NON_POSITIVE_INTEGER = new BuiltInAtomicItemType(BuiltInAtomicType.NON_POSITIVE_INTEGER, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:negativeInteger
+ */
+
+ public static final ItemType NEGATIVE_INTEGER = new BuiltInAtomicItemType(BuiltInAtomicType.NEGATIVE_INTEGER, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:long
+ */
+
+ public static final ItemType LONG = new BuiltInAtomicItemType(BuiltInAtomicType.LONG, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:int
+ */
+
+ public static final ItemType INT = new BuiltInAtomicItemType(BuiltInAtomicType.INT, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:short
+ */
+
+ public static final ItemType SHORT = new BuiltInAtomicItemType(BuiltInAtomicType.SHORT, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:byte
+ */
+
+ public static final ItemType BYTE = new BuiltInAtomicItemType(BuiltInAtomicType.BYTE, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:nonNegativeInteger
+ */
+
+ public static final ItemType NON_NEGATIVE_INTEGER = new BuiltInAtomicItemType(BuiltInAtomicType.NON_NEGATIVE_INTEGER, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:positiveInteger
+ */
+
+ public static final ItemType POSITIVE_INTEGER = new BuiltInAtomicItemType(BuiltInAtomicType.POSITIVE_INTEGER, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:unsignedLong
+ */
+
+ public static final ItemType UNSIGNED_LONG = new BuiltInAtomicItemType(BuiltInAtomicType.UNSIGNED_LONG, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:unsignedInt
+ */
+
+ public static final ItemType UNSIGNED_INT = new BuiltInAtomicItemType(BuiltInAtomicType.UNSIGNED_INT, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:unsignedShort
+ */
+
+ public static final ItemType UNSIGNED_SHORT = new BuiltInAtomicItemType(BuiltInAtomicType.UNSIGNED_SHORT, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:unsignedByte
+ */
+
+ public static final ItemType UNSIGNED_BYTE = new BuiltInAtomicItemType(BuiltInAtomicType.UNSIGNED_BYTE, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:yearMonthDuration
+ */
+
+ public static final ItemType YEAR_MONTH_DURATION = new BuiltInAtomicItemType(BuiltInAtomicType.YEAR_MONTH_DURATION, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:dayTimeDuration
+ */
+
+ public static final ItemType DAY_TIME_DURATION = new BuiltInAtomicItemType(BuiltInAtomicType.DAY_TIME_DURATION, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:normalizedString
+ */
+
+ public static final ItemType NORMALIZED_STRING = new BuiltInAtomicItemType(BuiltInAtomicType.NORMALIZED_STRING, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:token
+ */
+
+ public static final ItemType TOKEN = new BuiltInAtomicItemType(BuiltInAtomicType.TOKEN, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:language
+ */
+
+ public static final ItemType LANGUAGE = new BuiltInAtomicItemType(BuiltInAtomicType.LANGUAGE, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:Name
+ */
+
+ public static final ItemType NAME = new BuiltInAtomicItemType(BuiltInAtomicType.NAME, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:NMTOKEN
+ */
+
+ public static final ItemType NMTOKEN = new BuiltInAtomicItemType(BuiltInAtomicType.NMTOKEN, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:NCName
+ */
+
+ public static final ItemType NCNAME = new BuiltInAtomicItemType(BuiltInAtomicType.NCNAME, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:ID
+ */
+
+ public static final ItemType ID = new BuiltInAtomicItemType(BuiltInAtomicType.ID, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:IDREF
+ */
+
+ public static final ItemType IDREF = new BuiltInAtomicItemType(BuiltInAtomicType.IDREF, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:ENTITY
+ */
+
+ public static final ItemType ENTITY = new BuiltInAtomicItemType(BuiltInAtomicType.ENTITY, defaultConversionRules);
+
+ /**
+ * ItemType representing the built-in (but non-primitive) type xs:dateTimeStamp
+ * (introduced in XSD 1.1)
+ */
+
+ public static final ItemType DATE_TIME_STAMP = new BuiltInAtomicItemType(BuiltInAtomicType.DATE_TIME_STAMP, defaultConversionRules);
+
+ /**
+ * Get the conversion rules implemented by this type. The conversion rules reflect variations
+ * between different versions of the W3C specifications, for example XSD 1.1 allows "+INF" as
+ * a lexical representation of xs:double, while XSD 1.0 does not.
+ * @return the conversion rules
+ */
+
+ /*@Nullable*/ public abstract ConversionRules getConversionRules();
+
+ /**
+ * Determine whether this item type matches a given item.
+ *
+ * @param item the item to be tested against this item type
+ * @return true if the item matches this item type, false if it does not match.
+ */
+
+ public abstract boolean matches(XdmItem item);
+
+ /**
+ * Determine whether this ItemType subsumes another ItemType. Specifically,
+ * <code>A.subsumes(B) is true if every value that matches the ItemType B also matches
+ * the ItemType A.
+ * @param other the other ItemType
+ * @return true if this ItemType subsumes the other ItemType. This includes the case where A and B
+ * represent the same ItemType.
+ * @since 9.1
+ */
+
+ public abstract boolean subsumes(ItemType other);
+
+ /**
+ * Method to get the underlying Saxon implementation object
+ *
+ * <p>This gives access to Saxon methods that may change from one release to another.</p>
+ * @return the underlying Saxon implementation object
+ */
+
+ public abstract net.sf.saxon.type.ItemType getUnderlyingItemType();
+
+ /**
+ * Test whether two ItemType objects represent the same type
+ * @param other the other ItemType object
+ * @return true if the other object is an ItemType representing the same type
+ * @since 9.5
+ */
+
+ public final boolean equals(Object other) {
+ return other instanceof ItemType && getUnderlyingItemType().equals(((ItemType) other).getUnderlyingItemType());
+ }
+
+ /**
+ * Get a hash code with semantics corresponding to the equals() method
+ * @return the hash code
+ * @since 9.5
+ */
+
+ public final int hashCode() {
+ return getUnderlyingItemType().hashCode();
+ }
+
+}
+
diff --git a/sf/saxon/s9api/ItemTypeFactory.java b/sf/saxon/s9api/ItemTypeFactory.java
new file mode 100644
index 0000000..faced72
--- /dev/null
+++ b/sf/saxon/s9api/ItemTypeFactory.java
@@ -0,0 +1,524 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.ObjectValue;
+
+/**
+ * This class is used for creating ItemType objects.
+ *
+ * <p>The <code>ItemTypeFactory</code> class is thread-safe.</p>
+ */
+public class ItemTypeFactory {
+
+ private Processor processor;
+
+ /**
+ * Create an ItemTypeFactory
+ * @param processor the processor used by this ItemTypeFactory. This must be supplied
+ * in the case of user-defined types or types that reference element or attribute names;
+ * for built-in types it can be omitted.
+ */
+
+ public ItemTypeFactory(Processor processor) {
+ this.processor = processor;
+ }
+
+ /**
+ * Get an item type representing an atomic type. This may be a built-in type in the
+ * XML Schema namespace, or a user-defined atomic type. The resulting type will use
+ * the conversion rules configured for the supplied <code>Processor</code>.
+ *
+ * <p>It is undefined whether two calls supplying the same QName will return the same ItemType
+ * object.</p>
+ * @param name the name of the built-in or user-defined atomic type required
+ * @return an ItemType object representing this built-in or user-defined atomic type
+ * @throws SaxonApiException if the type name is not known, or if the type identified by the
+ * name is not an atomic type.
+ */
+
+ public ItemType getAtomicType(QName name) throws SaxonApiException {
+ String uri = name.getNamespaceURI();
+ String local = name.getLocalName();
+ if (NamespaceConstant.SCHEMA.equals(uri)) {
+ int fp = StandardNames.getFingerprint(uri, local);
+ Configuration config = processor.getUnderlyingConfiguration();
+ if (config.getXsdVersion() == Configuration.XSD10 && config.getXMLVersion() == Configuration.XML10) {
+ return getBuiltInAtomicType(fp);
+ } else {
+ return ItemType.BuiltInAtomicItemType.makeVariant(
+ (ItemType.BuiltInAtomicItemType)getBuiltInAtomicType(fp), config.getConversionRules());
+ }
+ } else {
+ Configuration config = processor.getUnderlyingConfiguration();
+ int fp = config.getNamePool().allocate("", name.getNamespaceURI(), local) & NamePool.FP_MASK;
+ SchemaType type = config.getSchemaType(fp);
+ if (type == null || !type.isAtomicType()) {
+ throw new SaxonApiException("Unknown atomic type " + name.getClarkName());
+ }
+ return new ConstructedItemType((AtomicType)type, processor);
+ }
+ }
+
+
+ private ItemType getBuiltInAtomicType(int fp) throws SaxonApiException {
+ switch (fp) {
+ case StandardNames.XS_ANY_ATOMIC_TYPE:
+ return ItemType.ANY_ATOMIC_VALUE;
+
+ case StandardNames.XS_NUMERIC:
+ return ItemType.NUMERIC;
+
+ case StandardNames.XS_STRING:
+ return ItemType.STRING;
+
+ case StandardNames.XS_BOOLEAN:
+ return ItemType.BOOLEAN;
+
+ case StandardNames.XS_DURATION:
+ return ItemType.DURATION;
+
+ case StandardNames.XS_DATE_TIME:
+ return ItemType.DATE_TIME;
+
+ case StandardNames.XS_DATE:
+ return ItemType.DATE;
+
+ case StandardNames.XS_TIME:
+ return ItemType.TIME;
+
+ case StandardNames.XS_G_YEAR_MONTH:
+ return ItemType.G_YEAR_MONTH;
+
+ case StandardNames.XS_G_MONTH:
+ return ItemType.G_MONTH;
+
+ case StandardNames.XS_G_MONTH_DAY:
+ return ItemType.G_MONTH_DAY;
+
+ case StandardNames.XS_G_YEAR:
+ return ItemType.G_YEAR;
+
+ case StandardNames.XS_G_DAY:
+ return ItemType.G_DAY;
+
+ case StandardNames.XS_HEX_BINARY:
+ return ItemType.HEX_BINARY;
+
+ case StandardNames.XS_BASE64_BINARY:
+ return ItemType.BASE64_BINARY;
+
+ case StandardNames.XS_ANY_URI:
+ return ItemType.ANY_URI;
+
+ case StandardNames.XS_QNAME:
+ return ItemType.QNAME;
+
+ case StandardNames.XS_NOTATION:
+ return ItemType.NOTATION;
+
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ return ItemType.UNTYPED_ATOMIC;
+
+ case StandardNames.XS_DECIMAL:
+ return ItemType.DECIMAL;
+
+ case StandardNames.XS_FLOAT:
+ return ItemType.FLOAT;
+
+ case StandardNames.XS_DOUBLE:
+ return ItemType.DOUBLE;
+
+ case StandardNames.XS_INTEGER:
+ return ItemType.INTEGER;
+
+ case StandardNames.XS_NON_POSITIVE_INTEGER:
+ return ItemType.NON_POSITIVE_INTEGER;
+
+ case StandardNames.XS_NEGATIVE_INTEGER:
+ return ItemType.NEGATIVE_INTEGER;
+
+ case StandardNames.XS_LONG:
+ return ItemType.LONG;
+
+ case StandardNames.XS_INT:
+ return ItemType.INT;
+
+ case StandardNames.XS_SHORT:
+ return ItemType.SHORT;
+
+ case StandardNames.XS_BYTE:
+ return ItemType.BYTE;
+
+ case StandardNames.XS_NON_NEGATIVE_INTEGER:
+ return ItemType.NON_NEGATIVE_INTEGER;
+
+ case StandardNames.XS_POSITIVE_INTEGER:
+ return ItemType.POSITIVE_INTEGER;
+
+ case StandardNames.XS_UNSIGNED_LONG:
+ return ItemType.UNSIGNED_LONG;
+
+ case StandardNames.XS_UNSIGNED_INT:
+ return ItemType.UNSIGNED_INT;
+
+ case StandardNames.XS_UNSIGNED_SHORT:
+ return ItemType.UNSIGNED_SHORT;
+
+ case StandardNames.XS_UNSIGNED_BYTE:
+ return ItemType.UNSIGNED_BYTE;
+
+ case StandardNames.XS_YEAR_MONTH_DURATION:
+ return ItemType.YEAR_MONTH_DURATION;
+
+ case StandardNames.XS_DAY_TIME_DURATION:
+ return ItemType.DAY_TIME_DURATION;
+
+ case StandardNames.XS_NORMALIZED_STRING:
+ return ItemType.NORMALIZED_STRING;
+
+ case StandardNames.XS_TOKEN:
+ return ItemType.TOKEN;
+
+ case StandardNames.XS_LANGUAGE:
+ return ItemType.LANGUAGE;
+
+ case StandardNames.XS_NAME:
+ return ItemType.NAME;
+
+ case StandardNames.XS_NMTOKEN:
+ return ItemType.NMTOKEN;
+
+ case StandardNames.XS_NCNAME:
+ return ItemType.NCNAME;
+
+ case StandardNames.XS_ID:
+ return ItemType.ID;
+
+ case StandardNames.XS_IDREF:
+ return ItemType.IDREF;
+
+ case StandardNames.XS_ENTITY:
+ return ItemType.ENTITY;
+
+ case StandardNames.XS_DATE_TIME_STAMP:
+ return ItemType.DATE_TIME_STAMP;
+
+ default:
+ throw new SaxonApiException("Unknown atomic type " +
+ processor.getUnderlyingConfiguration().getNamePool().getClarkName(fp));
+ }
+ }
+
+ /**
+ * Get an item type that matches any node of a specified kind.
+ *
+ * <p>This corresponds to the XPath syntactic forms element(), attribute(),
+ * document-node(), text(), comment(), processing-instruction(). It also provides
+ * an option, not available in the XPath syntax, that matches namespace nodes.</p>
+ *
+ * <p>It is undefined whether two calls supplying the same argument value will
+ * return the same ItemType object.</p>
+ *
+ * @param kind the kind of node for which a NodeTest is required
+ * @return an item type corresponding to the specified kind of node
+ */
+
+ public ItemType getNodeKindTest(XdmNodeKind kind) {
+ switch (kind) {
+ case DOCUMENT:
+ return new ConstructedItemType(NodeKindTest.DOCUMENT, processor);
+ case ELEMENT:
+ return new ConstructedItemType(NodeKindTest.ELEMENT, processor);
+ case ATTRIBUTE:
+ return new ConstructedItemType(NodeKindTest.ATTRIBUTE, processor);
+ case TEXT:
+ return new ConstructedItemType(NodeKindTest.TEXT, processor);
+ case COMMENT:
+ return new ConstructedItemType(NodeKindTest.COMMENT, processor);
+ case PROCESSING_INSTRUCTION:
+ return new ConstructedItemType(NodeKindTest.PROCESSING_INSTRUCTION, processor);
+ case NAMESPACE:
+ return new ConstructedItemType(NodeKindTest.NAMESPACE, processor);
+ default:
+ throw new IllegalArgumentException("XdmNodeKind");
+ }
+ }
+
+ /**
+ * Get an item type that matches nodes of a specified kind with a specified name.
+ *
+ * <p>This corresponds to the XPath syntactic forms element(name), attribute(name),
+ * and processing-instruction(name). In the case of processing-instruction, the supplied
+ * QName must have no namespace.</p>
+ *
+ * <p>It is undefined whether two calls supplying the same argument values will
+ * return the same ItemType object.</p>
+ *
+ * @param kind the kind of nodes that match
+ * @param name the name of the nodes that match
+ * @return an ItemType that matches nodes of a given node kind with a given name
+ * @throws IllegalArgumentException if the node kind is other than element, attribute, or
+ * processing instruction, or if the node kind is processing instruction and the name is in a namespace.
+ */
+
+ public ItemType getItemType(XdmNodeKind kind, QName name) {
+ int k = kind.getNumber();
+ if (k == Type.ELEMENT || k == Type.ATTRIBUTE || k == Type.PROCESSING_INSTRUCTION) {
+ if (k == Type.PROCESSING_INSTRUCTION && name.getNamespaceURI().length()==0) {
+ throw new IllegalArgumentException("The name of a processing instruction must not be in a namespace");
+ }
+ NameTest type = new NameTest(k,
+ name.getNamespaceURI(), name.getLocalName(), processor.getUnderlyingConfiguration().getNamePool());
+ return new ConstructedItemType(type, processor);
+ } else {
+ throw new IllegalArgumentException("Node kind must be element, attribute, or processing-instruction");
+ }
+ }
+
+ /**
+ * Make an ItemType representing an element declaration in the schema. This is the
+ * equivalent of the XPath syntax <code>schema-element(element-name)</code>
+ *
+ * <p>It is undefined whether two calls supplying the same argument values will
+ * return the same ItemType object.</p>
+ *
+ * @param name the element name
+ * @return the ItemType
+ * @throws SaxonApiException if the schema does not contain a global element declaration
+ * for the given name
+ */
+
+ public ItemType getSchemaElementTest(QName name) throws SaxonApiException {
+ Configuration config = processor.getUnderlyingConfiguration();
+ int fingerprint = config.getNamePool().allocate("", name.getNamespaceURI(), name.getLocalName());
+ SchemaDeclaration decl = config.getElementDeclaration(fingerprint);
+ if (decl == null) {
+ throw new SaxonApiException("No global declaration found for element " + name.getClarkName());
+ }
+ NodeTest test = decl.makeSchemaNodeTest();
+ return new ConstructedItemType(test, processor);
+ }
+
+ /**
+ * Make an ItemType that tests an element name and/or schema type. This is the
+ * equivalent of the XPath syntax <code>element(element-name, type)</code>
+ *
+ * <p>It is undefined whether two calls supplying the same argument values will
+ * return the same ItemType object.</p>
+ *
+ * @param name the element name, or null if there is no constraint on the name (equivalent to
+ * specifying <code>element(*, type)</code>)
+ * @param schemaType the name of the required schema type, or null if there is no constraint
+ * on the type (equivalent to specifying <code>element(name)</code>)
+ * @param nillable if a nilled element is allowed to match the type (equivalent to specifying
+ * "?" after the type name). The value is ignored if schemaType is null.
+ * @return the constructed ItemType
+ * @throws SaxonApiException if the schema does not contain a global element declaration
+ * for the given name
+ */
+
+ public ItemType getElementTest(/*@Nullable*/ QName name, QName schemaType, boolean nillable) throws SaxonApiException {
+ Configuration config = processor.getUnderlyingConfiguration();
+ NameTest nameTest = null;
+ ContentTypeTest contentTest = null;
+ if (name != null) {
+ int elementFP = config.getNamePool().allocate("", name.getNamespaceURI(), name.getLocalName());
+ nameTest = new NameTest(Type.ELEMENT, elementFP, config.getNamePool());
+ }
+ if (schemaType != null) {
+ int typeFP = config.getNamePool().allocate("", schemaType.getNamespaceURI(), schemaType.getLocalName());
+ SchemaType type = config.getSchemaType(typeFP);
+ if (type == null) {
+ throw new SaxonApiException("Unknown schema type " + schemaType.getClarkName());
+ }
+ contentTest = new ContentTypeTest(Type.ELEMENT, type, config, nillable);
+ }
+ if (contentTest == null) {
+ if (nameTest == null) {
+ return getNodeKindTest(XdmNodeKind.ELEMENT);
+ } else {
+ return new ConstructedItemType(nameTest, processor);
+ }
+ } else {
+ if (nameTest == null) {
+ return new ConstructedItemType(contentTest, processor);
+ } else {
+ CombinedNodeTest combo = new CombinedNodeTest(
+ nameTest,
+ Token.INTERSECT,
+ contentTest);
+ return new ConstructedItemType(combo, processor);
+ }
+ }
+ }
+
+ /**
+ * Get an ItemType representing an attribute declaration in the schema. This is the
+ * equivalent of the XPath syntax <code>schema-attribute(attribute-name)</code>
+ *
+ * <p>It is undefined whether two calls supplying the same argument values will
+ * return the same ItemType object.</p>
+ *
+ * @param name the attribute name
+ * @return the ItemType
+ * @throws SaxonApiException if the schema does not contain a global attribute declaration
+ * for the given name
+ */
+
+ public ItemType getSchemaAttributeTest(QName name) throws SaxonApiException {
+ Configuration config = processor.getUnderlyingConfiguration();
+ int fingerprint = config.getNamePool().allocate("", name.getNamespaceURI(), name.getLocalName());
+ SchemaDeclaration decl = config.getAttributeDeclaration(fingerprint);
+ if (decl == null) {
+ throw new SaxonApiException("No global declaration found for attribute " + name.getClarkName());
+ }
+ NodeTest test = decl.makeSchemaNodeTest();
+ return new ConstructedItemType(test, processor);
+ }
+
+ /**
+ * Get an ItemType that tests an element name and/or schema type. This is the
+ * equivalent of the XPath syntax <code>element(element-name, type)</code>
+ *
+ * <p>It is undefined whether two calls supplying the same argument values will
+ * return the same ItemType object.</p>
+ *
+ * @param name the element name, or null if there is no constraint on the name (equivalent to
+ * specifying <code>element(*, type)</code>)
+ * @param schemaType the name of the required schema type, or null of there is no constraint
+ * on the type (equivalent to specifying <code>element(name)</code>)
+ * @return the constructed ItemType
+ * @throws SaxonApiException if the schema does not contain a global element declaration
+ * for the given name
+ */
+
+ public ItemType getAttributeTest(QName name, QName schemaType) throws SaxonApiException {
+ NameTest nameTest = null;
+ ContentTypeTest contentTest = null;
+ Configuration config = processor.getUnderlyingConfiguration();
+ if (name != null) {
+ int attributeFP = config.getNamePool().allocate("", name.getNamespaceURI(), name.getLocalName());
+ nameTest = new NameTest(Type.ATTRIBUTE, attributeFP, config.getNamePool());
+ }
+ if (schemaType != null) {
+ int typeFP = config.getNamePool().allocate("", schemaType.getNamespaceURI(), schemaType.getLocalName());
+ SchemaType type = config.getSchemaType(typeFP);
+ if (type == null) {
+ throw new SaxonApiException("Unknown schema type " + schemaType.getClarkName());
+ }
+ contentTest = new ContentTypeTest(Type.ATTRIBUTE, type, config, false);
+ }
+ if (contentTest == null) {
+ if (nameTest == null) {
+ return getNodeKindTest(XdmNodeKind.ATTRIBUTE);
+ } else {
+ return new ConstructedItemType(nameTest, processor);
+ }
+ } else {
+ if (nameTest == null) {
+ return new ConstructedItemType(contentTest, processor);
+ } else {
+ CombinedNodeTest combo = new CombinedNodeTest(
+ nameTest,
+ Token.INTERSECT,
+ contentTest);
+ return new ConstructedItemType(combo, processor);
+ }
+ }
+ }
+
+ /**
+ * Make an ItemType representing a document-node() test with an embedded element test.
+ * This reflects the XPath syntax <code>document-node(element(N, T))</code> or
+ * <code>document-node(schema-element(N))</code>.
+ *
+ * <p>It is undefined whether two calls supplying the same argument values will
+ * return the same ItemType object.</p>
+ *
+ * @param elementTest the elementTest. An IllegalArgumentException is thrown if the supplied
+ * ItemTest is not an elementTest or schemaElementTest.
+ * @return a new ItemType representing the document test
+ */
+
+ public ItemType getDocumentTest(ItemType elementTest) {
+ net.sf.saxon.type.ItemType test = elementTest.getUnderlyingItemType();
+ if (test.getPrimitiveType() != Type.ELEMENT) {
+ throw new IllegalArgumentException("Supplied itemType is not an element test");
+ }
+ DocumentNodeTest docTest = new DocumentNodeTest((NodeTest)test);
+ return new ConstructedItemType(docTest, processor);
+ }
+
+ /**
+ * Get an ItemType representing the type of a Java object when used as an external object
+ * for use in conjunction with calls on extension/external functions.
+ * @param externalClass a Java class
+ * @return the ItemType representing the type of external objects of this class
+ */
+
+ public ItemType getExternalObjectType(Class externalClass) {
+ ExternalObjectType type = new ExternalObjectType(externalClass, processor.getUnderlyingConfiguration());
+ return new ConstructedItemType(type, processor);
+ }
+
+ /**
+ * Factory method to construct an "external object". This is an XDM value that wraps a Java
+ * object. Such values can be passed as parameters to stylesheets or queries, for use in conjunction
+ * with external (extension) functions.
+ *
+ * <p>Changed in Saxon 9.5 to return XdmItem rather than XdmAtomicValue, since wrapped
+ * external objects are now modelled as a separate type of item, rather than as an atomic value.</p>
+ *
+ * @param object the value to be wrapped as an external object. Must not be null.
+ * @return the object, wrapped as an XdmAtomicValue
+ */
+
+ public XdmItem getExternalObject(Object object) {
+ return (XdmItem)XdmItem.wrap(new ObjectValue(object));
+ }
+
+ /**
+ * Get an ItemType representing the type of a supplied XdmItem. If the supplied item is
+ * an atomic value, the returned ItemType will reflect the most specific atomic type of the
+ * item. If the supplied item is a node, the returned item type will reflect the node kind,
+ * and if the node has a name, then its name. It will not reflect the type annotation.
+ * @param item the supplied item whose type is required
+ * @return the type of the supplied item
+ */
+
+ public ItemType getItemType(XdmItem item) {
+ Configuration config = processor.getUnderlyingConfiguration();
+ if (item.isAtomicValue()) {
+ AtomicValue value = (AtomicValue)item.getUnderlyingValue();
+
+ AtomicType type = (AtomicType)value.getItemType();
+ return new ConstructedItemType(type, processor);
+ } else {
+ NodeInfo node = (NodeInfo)item.getUnderlyingValue();
+ int kind = node.getNodeKind();
+ int fp = node.getFingerprint();
+ if (fp == -1) {
+ return new ConstructedItemType(NodeKindTest.makeNodeKindTest(kind), processor);
+ } else {
+ return new ConstructedItemType(new NameTest(kind, fp, config.getNamePool()), processor);
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/s9api/MessageListener.java b/sf/saxon/s9api/MessageListener.java
new file mode 100644
index 0000000..2077c10
--- /dev/null
+++ b/sf/saxon/s9api/MessageListener.java
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * A user-written implementation of the MessageListener interface may be registered with the XsltTransformer
+ * to receive notification of xsl:message output. Each xsl:message instruction that is evaluated results in
+ * a single call to the MessageListener
+ */
+public interface MessageListener {
+
+ /**
+ * Notify a message written using the <code>xsl:message</code> instruction
+ * @param content a document node representing the message content. Note that the output of
+ * <code>xsl:message</code> is always an XML document node. It can be flattened to obtain the
+ * string value if required by calling <code>getStringValue()</code>.
+ * @param terminate Set to true if <code>terminate="yes"</code> was specified or to false otherwise.
+ * The message listener does not need to take any special action based on this parameter, but the information
+ * is available if required. If <code>terminate="yes"</code> was specified, then the transformation will abort
+ * with an exception immediately on return from this callback.
+ * @param locator an object that contains the location of the <code>xsl:message</code> instruction in the
+ * stylesheet that caused this message to be output. This provides access to the URI of the stylesheet module
+ * and the line number of the <code>xsl:message</code> instruction.
+ */
+
+ public void message(XdmNode content, boolean terminate, SourceLocator locator);
+}
+
diff --git a/sf/saxon/s9api/MessageListenerProxy.java b/sf/saxon/s9api/MessageListenerProxy.java
new file mode 100644
index 0000000..3217135
--- /dev/null
+++ b/sf/saxon/s9api/MessageListenerProxy.java
@@ -0,0 +1,118 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.event.SequenceWriter;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+
+/**
+ * This class implements a Receiver that can receive xsl:message output and send it to a
+ * user-supplied MessageListener.
+ */
+
+class MessageListenerProxy extends SequenceWriter {
+
+ private MessageListener listener;
+ private boolean terminate;
+ private int locationId = -1;
+
+ protected MessageListenerProxy(MessageListener listener, PipelineConfiguration pipe) {
+ super(pipe);
+ this.listener = listener;
+ }
+
+ /**
+ * Get the wrapped MessageListener
+ * @return the wrapped MessageListener
+ */
+
+ public MessageListener getMessageListener() {
+ return listener;
+ }
+
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ terminate = (properties & ReceiverOptions.TERMINATE) != 0;
+ locationId = -1;
+ super.startDocument(properties);
+ }
+
+
+ /**
+ * Output an element start tag.
+ *
+ * @param nameCode The element name code - a code held in the Name Pool
+ * @param typeCode Integer code identifying the type of this element. Zero identifies the default
+ * type, that is xs:anyType
+ * @param properties bit-significant flags indicating any special information
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (this.locationId == -1) {
+ this.locationId = locationId;
+ }
+ super.startElement(nameCode, typeCode, locationId, properties);
+ }
+
+ /**
+ * Produce text content output. <BR>
+ *
+ * @param s The String to be output
+ * @param properties bit-significant flags for extra information, e.g. disable-output-escaping
+ * @throws net.sf.saxon.trans.XPathException
+ * for any failure
+ */
+
+ public void characters(CharSequence s, int locationId, int properties) throws XPathException {
+ if (this.locationId == -1) {
+ this.locationId = locationId;
+ }
+ super.characters(s, locationId, properties);
+ }
+
+
+ /**
+ * Append an item to the sequence, performing any necessary type-checking and conversion
+ */
+
+ public void append(Item item, int locationId, int copyNamespaces) throws XPathException {
+ if (this.locationId == -1) {
+ this.locationId = locationId;
+ }
+ super.append(item, locationId, copyNamespaces);
+ }
+
+ /**
+ * Abstract method to be supplied by subclasses: output one item in the sequence.
+ *
+ * @param item the item to be written to the sequence
+ */
+
+ public void write(Item item) throws XPathException {
+ ExpressionLocation loc = new ExpressionLocation();
+ if (locationId != -1) {
+ LocationProvider provider = getPipelineConfiguration().getLocationProvider();
+ loc.setSystemId(provider.getSystemId(locationId));
+ loc.setLineNumber(provider.getLineNumber(locationId));
+ }
+ listener.message(new XdmNode((NodeInfo)item), terminate, loc);
+ }
+}
+
diff --git a/sf/saxon/s9api/OccurrenceIndicator.java b/sf/saxon/s9api/OccurrenceIndicator.java
new file mode 100644
index 0000000..6a65a4a
--- /dev/null
+++ b/sf/saxon/s9api/OccurrenceIndicator.java
@@ -0,0 +1,91 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.value.Cardinality;
+
+/**
+ * Represents one of the possible occurrence indicators in a SequenceType. The four standard values are
+ * ONE (no occurrence indicator), ZERO_OR_ONE (?), ZERO_OR_MORE (*), ONE_OR_MORE (+). In addition the
+ * value ZERO is supported: this is used only in the type empty-sequence() which matches an empty sequence
+ * and nothing else.
+ */
+public enum OccurrenceIndicator {
+ ZERO, ZERO_OR_ONE, ZERO_OR_MORE, ONE, ONE_OR_MORE;
+
+ protected int getCardinality() {
+ switch(this) {
+ case ZERO:
+ return StaticProperty.EMPTY;
+ case ZERO_OR_ONE:
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ case ZERO_OR_MORE:
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ case ONE:
+ return StaticProperty.ALLOWS_ONE;
+ case ONE_OR_MORE:
+ return StaticProperty.ALLOWS_ONE_OR_MORE;
+ default:
+ return StaticProperty.EMPTY;
+ }
+ }
+
+ protected static OccurrenceIndicator getOccurrenceIndicator(int cardinality) {
+ switch (cardinality) {
+ case StaticProperty.EMPTY:
+ return ZERO;
+ case StaticProperty.ALLOWS_ZERO_OR_ONE:
+ return ZERO_OR_ONE;
+ case StaticProperty.ALLOWS_ZERO_OR_MORE:
+ return ZERO_OR_MORE;
+ case StaticProperty.ALLOWS_ONE:
+ return ONE;
+ case StaticProperty.ALLOWS_ONE_OR_MORE:
+ return ONE_OR_MORE;
+ default:
+ return ZERO_OR_MORE;
+ }
+ }
+
+ /**
+ * Ask whether this occurrence indicator permits an empty sequence.
+ * @return true if the occurrence indicator is one of {@link #ZERO}, {@link #ZERO_OR_ONE},
+ * or {@link #ZERO_OR_MORE}
+ * @since 9.2
+ */
+
+ public boolean allowsZero() {
+ return Cardinality.allowsZero(getCardinality());
+ }
+
+ /**
+ * Ask whether this occurrence indicator permits a sequence containing more than one item.
+ * @return true if the occurrence indicator is one of {@link #ZERO_OR_MORE} or {@link #ONE_OR_MORE}
+ * @since 9.2
+ */
+
+ public boolean allowsMany() {
+ return Cardinality.allowsMany(getCardinality());
+ }
+
+ /**
+ * Ask whether one occurrence indicator subsumes another. Specifically,
+ * <code>A.subsumes(B)</code> is true if every sequence that satisfies the occurrence
+ * indicator B also satisfies the occurrence indicator A.
+ * @param other The other occurrence indicator
+ * @return true if this occurrence indicator subsumes the other occurrence indicator
+ * @since 9.1
+ */
+
+ public boolean subsumes(/*@NotNull*/ OccurrenceIndicator other) {
+ return Cardinality.subsumes(getCardinality(), other.getCardinality());
+ }
+
+}
+
diff --git a/sf/saxon/s9api/Processor.java b/sf/saxon/s9api/Processor.java
new file mode 100644
index 0000000..d99bce4
--- /dev/null
+++ b/sf/saxon/s9api/Processor.java
@@ -0,0 +1,556 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Version;
+import net.sf.saxon.event.NamespaceReducer;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.TreeReceiver;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.ExtensionFunctionCall;
+import net.sf.saxon.lib.ExtensionFunctionDefinition;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceExtent;
+import net.sf.saxon.value.SequenceType;
+
+import javax.xml.transform.Source;
+import java.io.File;
+import java.io.OutputStream;
+import java.io.Writer;
+
+/**
+ * The <code>Processor</code> class serves three purposes: it allows global Saxon configuration options to be set;
+ * it acts as a factory for generating XQuery, XPath, and XSLT compilers; and it owns certain shared
+ * resources such as the Saxon NamePool and compiled schemas. This is the first object that a
+ * Saxon application should create. Once established, a Processor may be used in multiple threads.
+ * <p/>
+ * <p>It is possible to run more than one Saxon Processor concurrently, but only when running completely
+ * independent workloads. Nothing can be shared between Processor instances. Within a query or transformation,
+ * all source documents and schemas must be built using the same Processor, which must also be used to
+ * compile the query or stylesheet.</p>
+ */
+
+public class Processor {
+
+ private Configuration config;
+ private SchemaManager schemaManager;
+
+ /**
+ * Create a Processor
+ *
+ * @param licensedEdition indicates whether the Processor requires features of Saxon that need a license
+ * file (that is, features not available in Saxon HE (Home Edition). If true, the method will create
+ * a Configuration appropriate to the version of the software that is running: for example, if running
+ * Saxon-EE, it will create an EnterpriseConfiguration. The method does not at this stage check that a license
+ * is available, and in the absence of a license, it should run successfully provided no features that
+ * require licensing are actually used. If the argument is set to false, a plain Home Edition Configuration
+ * is created unconditionally.
+ */
+
+ public Processor(boolean licensedEdition) {
+ if (licensedEdition) {
+ config = Configuration.newConfiguration();
+ if (config.getEditionCode().equals("EE")) {
+ schemaManager = new SchemaManager(config);
+ }
+ } else {
+ config = new Configuration();
+ }
+ config.setProcessor(this);
+ }
+
+ /**
+ * Create a Processor based on an existing Configuration. This constructor is useful for transition,
+ * when new components of an application are to use s9api interfaces but existing components use lower-level
+ * interfaces.
+ *
+ * @param config the Configuration to be used by this processor
+ * @since 9.3
+ */
+
+ public Processor(/*@NotNull*/ Configuration config) {
+ this.config = config;
+ if (config.getEditionCode().equals("EE")) {
+ schemaManager = new SchemaManager(config);
+ }
+ }
+
+ /**
+ * Create a Processor configured according to the settings in a supplied configuration file.
+ *
+ * @param source the Source of the configuration file
+ * @throws SaxonApiException if the configuration file cannot be read, or its contents are invalid
+ * @since 9.2
+ */
+
+ public Processor(Source source) throws SaxonApiException {
+ try {
+ config = Configuration.readConfiguration(source);
+ schemaManager = new SchemaManager(config);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ config.setProcessor(this);
+ }
+
+ /**
+ * Create a DocumentBuilder. A DocumentBuilder is used to load source XML documents.
+ *
+ * @return a newly created DocumentBuilder
+ */
+
+ /*@NotNull*/
+ public DocumentBuilder newDocumentBuilder() {
+ return new DocumentBuilder(config);
+ }
+
+ /**
+ * Create an XPathCompiler. An XPathCompiler is used to compile XPath expressions.
+ *
+ * @return a newly created XPathCompiler
+ */
+
+ /*@NotNull*/
+ public XPathCompiler newXPathCompiler() {
+ return new XPathCompiler(this);
+ }
+
+ /**
+ * Create an XsltCompiler. An XsltCompiler is used to compile XSLT stylesheets.
+ *
+ * @return a newly created XsltCompiler
+ * @throws UnsupportedOperationException if this version of the Saxon product does not support XSLT processing
+ */
+
+ /*@NotNull*/
+ public XsltCompiler newXsltCompiler() {
+ return new XsltCompiler(this);
+ }
+
+ /**
+ * Create an XQueryCompiler. An XQueryCompiler is used to compile XQuery queries.
+ *
+ * @return a newly created XQueryCompiler
+ * @throws UnsupportedOperationException if this version of the Saxon product does not support XQuery processing
+ */
+
+ /*@NotNull*/
+ public XQueryCompiler newXQueryCompiler() {
+ return new XQueryCompiler(this);
+ }
+
+ /**
+ * Create a Serializer
+ *
+ * @return a new Serializer
+ * @since 9.3
+ */
+
+ /*@NotNull*/
+ public Serializer newSerializer() {
+ Serializer s = new Serializer();
+ s.setProcessor(this);
+ return s;
+ }
+
+ /**
+ * Create a Serializer initialized to write to a given OutputStream.
+ * <p>Closing the output stream after use is the responsibility of the caller.</p>
+ *
+ * @param stream The OutputStream to which the Serializer will write
+ * @return a new Serializer
+ * @since 9.3
+ */
+
+ /*@NotNull*/
+ public Serializer newSerializer(OutputStream stream) {
+ Serializer s = new Serializer();
+ s.setProcessor(this);
+ s.setOutputStream(stream);
+ return s;
+ }
+
+ /**
+ * Create a Serializer initialized to write to a given Writer.
+ * <p>Closing the writer after use is the responsibility of the caller.</p>
+ *
+ * @param writer The Writer to which the Serializer will write
+ * @return a new Serializer
+ * @since 9.3
+ */
+
+ /*@NotNull*/
+ public Serializer newSerializer(Writer writer) {
+ Serializer s = new Serializer();
+ s.setProcessor(this);
+ s.setOutputWriter(writer);
+ return s;
+ }
+
+ /**
+ * Create a Serializer initialized to write to a given File.
+ *
+ * @param file The File to which the Serializer will write
+ * @return a new Serializer
+ * @since 9.3
+ */
+
+ /*@NotNull*/
+ public Serializer newSerializer(File file) {
+ Serializer s = new Serializer();
+ s.setProcessor(this);
+ s.setOutputFile(file);
+ return s;
+ }
+
+ /**
+ * Register a simple external/extension function that is to be made available within any stylesheet, query,
+ * or XPath expression compiled under the control of this processor.
+ * <p/>
+ * <p>This interface provides only for simple extension functions that have no side-effects and no dependencies
+ * on the static or dynamic context.
+ *
+ * @param function the implementation of the extension function.</p>
+ * @since 9.4
+ */
+
+ public void registerExtensionFunction(ExtensionFunction function) {
+ ExtensionFunctionDefinitionWrapper wrapper = new ExtensionFunctionDefinitionWrapper(function);
+ registerExtensionFunction(wrapper);
+ }
+
+ /**
+ * Register an extension function that is to be made available within any stylesheet, query,
+ * or XPath expression compiled under the control of this processor. This method
+ * registers an extension function implemented as an instance of
+ * {@link net.sf.saxon.lib.ExtensionFunctionDefinition}, using an arbitrary name and namespace.
+ * <p/>
+ * <p>This interface allows extension functions that have dependencies on the static or dynamic
+ * context. It also allows an extension function to declare that it has side-effects, in which
+ * case calls to the function will be optimized less aggressively than usual, although the semantics
+ * are still to some degree unpredictable.</p>
+ *
+ * @param function the implementation of the extension function.
+ * @since 9.2
+ */
+
+ public void registerExtensionFunction(ExtensionFunctionDefinition function) {
+ try {
+ config.registerExtensionFunction(function);
+ } catch (Exception err) {
+ throw new IllegalArgumentException(err);
+ }
+ }
+
+ /**
+ * Get the associated SchemaManager. The SchemaManager provides capabilities to load and cache
+ * XML schema definitions. There is exactly one SchemaManager in a schema-aware Processor, and none
+ * in a Processor that is not schema-aware. The SchemaManager is created automatically by the system.
+ *
+ * @return the associated SchemaManager, or null if the Processor is not schema-aware.
+ */
+
+ public SchemaManager getSchemaManager() {
+ return schemaManager;
+ }
+
+ /**
+ * Test whether this processor is schema-aware
+ *
+ * @return true if this this processor is licensed for schema processing, false otherwise
+ */
+
+ public boolean isSchemaAware() {
+ return config.isLicensedFeature(Configuration.LicenseFeature.SCHEMA_VALIDATION);
+ }
+
+ /**
+ * Get the user-visible Saxon product version, for example "9.0.0.1"
+ *
+ * @return the Saxon product version, as a string
+ */
+
+ public String getSaxonProductVersion() {
+ return Version.getProductVersion();
+ }
+
+ /**
+ * Set the version of XML used by this Processor. If the value is set to "1.0", then
+ * output documents will be serialized as XML 1.0. This option also affects
+ * the characters permitted to appear in queries and stylesheets, and the characters that can appear
+ * in names (for example, in path expressions).
+ * <p/>
+ * <p>Note that source documents specifying xml version="1.0" or "1.1" are accepted
+ * regardless of this setting.</p>
+ *
+ * @param version must be one of the strings "1.0" or "1.1"
+ * @throws IllegalArgumentException if any string other than "1.0" or "1.1" is supplied
+ */
+
+ public void setXmlVersion(/*@NotNull*/ String version) {
+ if (version.equals("1.0")) {
+ config.setXMLVersion(Configuration.XML10);
+ } else if (version.equals("1.1")) {
+ config.setXMLVersion(Configuration.XML11);
+ } else {
+ throw new IllegalArgumentException("XmlVersion");
+ }
+ }
+
+ /**
+ * Get the version of XML used by this Processor. If the value is "1.0", then input documents
+ * must be XML 1.0 documents, and output documents will be serialized as XML 1.0. This option also affects
+ * the characters permitted to appear in queries and stylesheets, and the characters that can appear
+ * in names (for example, in path expressions).
+ *
+ * @return one of the strings "1.0" or "1.1"
+ */
+
+ /*@NotNull*/
+ public String getXmlVersion() {
+ if (config.getXMLVersion() == Configuration.XML10) {
+ return "1.0";
+ } else {
+ return "1.1";
+ }
+ }
+
+ /**
+ * Set a configuration property
+ *
+ * @param name the name of the option to be set. The names of the options available are listed
+ * as constants in class {@link net.sf.saxon.lib.FeatureKeys}.
+ * @param value the value of the option to be set.
+ * @throws IllegalArgumentException if the property name is not recognized
+ */
+
+ public void setConfigurationProperty(/*@NotNull*/ String name, /*@NotNull*/ Object value) {
+ config.setConfigurationProperty(name, value);
+ }
+
+ /**
+ * Get the value of a configuration property
+ *
+ * @param name the name of the option required. The names of the properties available are listed
+ * as constants in class {@link net.sf.saxon.lib.FeatureKeys}.
+ * @return the value of the property, if one is set; or null if the property is unset and there is
+ * no default.
+ * @throws IllegalArgumentException if the property name is not recognized
+ */
+
+
+ /*@Nullable*/ public Object getConfigurationProperty(/*@NotNull*/ String name) {
+ return config.getConfigurationProperty(name);
+ }
+
+ /**
+ * Get the underlying {@link Configuration} object that underpins this Processor. This method
+ * provides an escape hatch to internal Saxon implementation objects that offer a finer and lower-level
+ * degree of control than the s9api classes and methods. Some of these classes and methods may change
+ * from release to release.
+ *
+ * @return the underlying Configuration object
+ */
+
+ public Configuration getUnderlyingConfiguration() {
+ return config;
+ }
+
+ /**
+ * Write an XdmValue to a given destination. The sequence represented by the XdmValue is "normalized"
+ * as defined in the serialization specification (this is equivalent to constructing a document node
+ * in XSLT or XQuery with this sequence as the content expression), and the resulting document is
+ * then copied to the destination. If the destination is a serializer this has the effect of serializing
+ * the sequence as described in the W3C specifications.
+ *
+ * @param value the value to be written
+ * @param destination the destination to which the value is to be written
+ * @throws SaxonApiException if any failure occurs, for example a serialization error
+ */
+
+ public void writeXdmValue(/*@NotNull*/ XdmValue value, /*@NotNull*/ Destination destination) throws SaxonApiException {
+ try {
+ Receiver out = destination.getReceiver(config);
+ out = new NamespaceReducer(out);
+ TreeReceiver tree = new TreeReceiver(out);
+ tree.open();
+ tree.startDocument(0);
+ for (XdmItem item : value) {
+ tree.append((Item) item.getUnderlyingValue(), 0, NodeInfo.ALL_NAMESPACES);
+ }
+ tree.endDocument();
+ tree.close();
+ destination.close();
+ } catch (XPathException err) {
+ throw new SaxonApiException(err);
+ }
+ }
+
+
+ private static class ExtensionFunctionDefinitionWrapper extends ExtensionFunctionDefinition {
+
+ private ExtensionFunction function;
+
+ public ExtensionFunctionDefinitionWrapper(ExtensionFunction function) {
+ this.function = function;
+ }
+
+ /**
+ * Get the name of the function, as a QName.
+ * <p>This method must be implemented in all subclasses</p>
+ *
+ * @return the function name
+ */
+ @Override
+ public StructuredQName getFunctionQName() {
+ return function.getName().getStructuredQName();
+ }
+
+ /**
+ * Get the minimum number of arguments required by the function
+ * <p>The default implementation returns the number of items in the result of calling
+ * {@link #getArgumentTypes}</p>
+ *
+ * @return the minimum number of arguments that must be supplied in a call to this function
+ */
+ @Override
+ public int getMinimumNumberOfArguments() {
+ return function.getArgumentTypes().length;
+ }
+
+ /**
+ * Get the maximum number of arguments allowed by the function.
+ * <p>The default implementation returns the value of {@link #getMinimumNumberOfArguments}
+ *
+ * @return the maximum number of arguments that may be supplied in a call to this function
+ */
+ @Override
+ public int getMaximumNumberOfArguments() {
+ return function.getArgumentTypes().length;
+ }
+
+ /**
+ * Get the required types for the arguments of this function.
+ * <p>This method must be implemented in all subtypes.</p>
+ *
+ * @return the required types of the arguments, as defined by the function signature. Normally
+ * this should be an array of size {@link #getMaximumNumberOfArguments()}; however for functions
+ * that allow a variable number of arguments, the array can be smaller than this, with the last
+ * entry in the array providing the required type for all the remaining arguments.
+ */
+ /*@NotNull*/
+ @Override
+ public net.sf.saxon.value.SequenceType[] getArgumentTypes() {
+ net.sf.saxon.s9api.SequenceType[] declaredArgs = function.getArgumentTypes();
+ net.sf.saxon.value.SequenceType[] types = new net.sf.saxon.value.SequenceType[declaredArgs.length];
+ for (int i = 0; i < declaredArgs.length; i++) {
+ types[i] = net.sf.saxon.value.SequenceType.makeSequenceType(
+ declaredArgs[i].getItemType().getUnderlyingItemType(),
+ declaredArgs[i].getOccurrenceIndicator().getCardinality());
+ }
+ return types;
+ }
+
+ /**
+ * Get the type of the result of the function
+ * <p>This method must be implemented in all subtypes.</p>
+ *
+ * @param suppliedArgumentTypes the static types of the supplied arguments to the function.
+ * This is provided so that a more precise result type can be returned in the common
+ * case where the type of the result depends on the types of the arguments.
+ * @return the return type of the function, as defined by its function signature
+ */
+ @Override
+ public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) {
+ net.sf.saxon.s9api.SequenceType declaredResult = function.getResultType();
+ return net.sf.saxon.value.SequenceType.makeSequenceType(
+ declaredResult.getItemType().getUnderlyingItemType(),
+ declaredResult.getOccurrenceIndicator().getCardinality());
+ }
+
+ /**
+ * Ask whether the result actually returned by the function can be trusted,
+ * or whether it should be checked against the declared type.
+ *
+ * @return true if the function implementation warrants that the value it returns will
+ * be an instance of the declared result type. The default value is false, in which case
+ * the result will be checked at run-time to ensure that it conforms to the declared type.
+ * If the value true is returned, but the function returns a value of the wrong type, the
+ * consequences are unpredictable.
+ */
+ @Override
+ public boolean trustResultType() {
+ return false;
+ }
+
+ /**
+ * Ask whether the result of the function depends on the focus, or on other variable parts
+ * of the context.
+ *
+ * @return true if the result of the function depends on the context item, position, or size.
+ * Despite the method name, the method should also return true if the function depends on other
+ * parts of the context that vary from one part of the query/stylesheet to another, for example
+ * the XPath default namespace.
+ * <p>The default implementation returns false.</p>
+ * <p>The method must return true if the function
+ * makes use of any of these values from the dynamic context. Returning true inhibits certain
+ * optimizations, such as moving the function call out of the body of an xsl:for-each loop,
+ * or extracting it into a global variable.</p>
+ */
+ @Override
+ public boolean dependsOnFocus() {
+ return false;
+ }
+
+ /**
+ * Ask whether the function has side-effects. If the function does have side-effects, the optimizer
+ * will be less aggressive in moving or removing calls to the function. However, calls on functions
+ * with side-effects can never be guaranteed.
+ *
+ * @return true if the function has side-effects (including creation of new nodes, if the
+ * identity of those nodes is significant). The default implementation returns false.
+ */
+ @Override
+ public boolean hasSideEffects() {
+ return false;
+ }
+
+ /**
+ * Create a call on this function. This method is called by the compiler when it identifies
+ * a function call that calls this function.
+ */
+ /*@NotNull*/
+ @Override
+ public ExtensionFunctionCall makeCallExpression() {
+ return new ExtensionFunctionCall() {
+ @Override
+ public Sequence call(
+ /*@NotNull*/ XPathContext context, Sequence[] arguments) throws XPathException {
+ XdmValue[] args = new XdmValue[arguments.length];
+ for (int i = 0; i < args.length; i++) {
+ Sequence val = SequenceExtent.makeSequenceExtent(arguments[i].iterate());
+ args[i] = XdmValue.wrap(val);
+ }
+ try {
+ XdmValue result = function.call(args);
+ return result.getUnderlyingValue();
+ } catch (SaxonApiException e) {
+ throw new XPathException(e);
+ }
+ }
+ };
+ }
+
+
+ }
+
+
+}
+
diff --git a/sf/saxon/s9api/QName.java b/sf/saxon/s9api/QName.java
new file mode 100644
index 0000000..97576ec
--- /dev/null
+++ b/sf/saxon/s9api/QName.java
@@ -0,0 +1,471 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.InscopeNamespaceResolver;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * The QName class represents an instance of xs:QName, as defined in the XPath 2.0 data model.
+ * Internally, it has three components, a namespace URI, a local name, and a prefix. The prefix
+ * is intended to be used only when converting the value back to a string.
+ *
+ * <p>This class also defines a number of QName-valued constants relating to built-in types in
+ * XML Schema</p>
+ *
+ * <p>A QName is immutable.</p>
+ *
+ * <p>Note that a QName is not itself an {@link XdmItem} in this model; however it can be wrapped in an
+ * XdmItem.</p>
+ */
+public class QName {
+
+ private StructuredQName sqName;
+
+ /**
+ * Construct a QName using a namespace prefix, a namespace URI, and a local name (in that order).
+ * <p>This constructor does not check that the components of the QName are lexically valid.</p>
+ *
+ * @param prefix The prefix of the name. Use either the string "" or null for names that have
+ * no prefix (that is, they are in the default namespace)
+ * @param uri The namespace URI. Use either the string "" or null for names that are not in any namespace.
+ * @param localName The local part of the name
+ */
+
+ public QName(String prefix, String uri, String localName) {
+ sqName = new StructuredQName(prefix, uri, localName);
+ }
+
+ /**
+ * Construct a QName using a namespace URI and a lexical representation. The lexical representation
+ * may be a local name on its own, or it may be in the form prefix:local-name.
+ * <p>This constructor does not check that the components of the QName are lexically valid.</p>
+ *
+ * @param uri The namespace URI. Use either the string "" or null for names that are not in any namespace.
+ * @param lexical Either the local part of the name, or the prefix and local part in the format prefix:local
+ */
+
+ public QName(String uri, String lexical) {
+ uri = (uri == null ? "" : uri);
+ int colon = lexical.indexOf(':');
+ if (colon < 0) {
+ sqName = new StructuredQName("", uri, lexical);
+ } else {
+ String prefix = lexical.substring(0, colon);
+ String local = lexical.substring(colon + 1);
+ sqName = new StructuredQName(prefix, uri, local);
+ }
+ }
+
+ /**
+ * Construct a QName from a localName alone. The localName must not contain a colon.
+ * The resulting QName represents a name in no namespace (which therefore has no prefix)</p>
+ *
+ * @param localName The local name. This must be a valid NCName, in particular it must contain no colon
+ */
+
+ public QName(String localName) {
+ int colon = localName.indexOf(':');
+ if (colon < 0) {
+ sqName = new StructuredQName("", "", localName);
+ } else {
+ throw new IllegalArgumentException("Local name contains a colon");
+ }
+ }
+
+ /**
+ * Construct a QName from a lexical QName, supplying an element node whose
+ * in-scope namespaces are to be used to resolve any prefix contained in the QName.
+ *
+ * <p>This constructor checks that the components of the QName are
+ * lexically valid.</p>
+ * <p>If the lexical QName has no prefix, the name is considered to be in the
+ * default namespace, as defined by <code>xmlns="..."</code>.</p>
+ * <p>If the prefix of the lexical QName is not in scope, returns null.</p>
+ *
+ * @param lexicalQName The lexical QName, in the form <code>prefix:local</code>
+ * or simply <code>local</code>. The EQName syntax "Q{uri}local"
+ * and the ClarkName syntax "{uri}local" are also accepted.
+ * @param element The element node whose in-scope namespaces are to be used
+ * to resolve the prefix part of the lexical QName.
+ * @throws IllegalArgumentException If the prefix of the lexical QName is not in scope
+ * or if the lexical QName is invalid (for example, if it contains invalid characters)
+ */
+
+ public QName(String lexicalQName, XdmNode element) {
+ if (lexicalQName.startsWith("{")) {
+ lexicalQName = "Q" + lexicalQName;
+ }
+ try {
+ NodeInfo node = (NodeInfo) element.getUnderlyingValue();
+ sqName = StructuredQName.fromLexicalQName(lexicalQName, true,
+ true, node.getConfiguration().getNameChecker(), new InscopeNamespaceResolver(node));
+
+ } catch (XPathException err) {
+ throw new IllegalArgumentException(err);
+ }
+ }
+
+ /**
+ * Construct a QName from a JAXP QName object
+ *
+ * @param qName the JAXP QName object
+ */
+
+ public QName(javax.xml.namespace.QName qName) {
+ sqName = new StructuredQName(qName.getPrefix(), qName.getNamespaceURI(), qName.getLocalPart());
+ }
+
+ /**
+ * Protected constructor accepting a StructuredQName
+ * @param sqName the StructuredQName
+ */
+
+ protected QName(StructuredQName sqName) {
+ this.sqName = sqName;
+ }
+
+ /**
+ * Factory method to construct a QName from a string containing the expanded
+ * QName in Clark notation, that is, <code>{uri}local</code>
+ * <p/>
+ * The prefix part of the <code>QName</code> will be set to an empty string.
+ * </p>
+ *
+ * @param expandedName The URI in Clark notation: <code>{uri}local</code> if the
+ * name is in a namespace, or simply <code>local</code> if not.
+ * @return the QName corresponding to the supplied name in Clark notation. This will always
+ * have an empty prefix.
+ */
+
+ public static QName fromClarkName(String expandedName) {
+ String namespaceURI;
+ String localName;
+ if (expandedName.charAt(0) == '{') {
+ int closeBrace = expandedName.indexOf('}');
+ if (closeBrace < 0) {
+ throw new IllegalArgumentException("No closing '}' in Clark name");
+ }
+ namespaceURI = expandedName.substring(1, closeBrace);
+ if (closeBrace == expandedName.length()) {
+ throw new IllegalArgumentException("Missing local part in Clark name");
+ }
+ localName = expandedName.substring(closeBrace + 1);
+ } else {
+ namespaceURI = "";
+ localName = expandedName;
+ }
+
+ return new QName("", namespaceURI, localName);
+ }
+
+ /**
+ * Factory method to construct a QName from a string containing the expanded
+ * QName in EQName notation, that is, <code>Q{uri}local</code>
+ * <p/>
+ * The prefix part of the <code>QName</code> will be set to an empty string.
+ * </p>
+ *
+ * @param expandedName The URI in EQName notation: <code>{uri}local</code> if the
+ * name is in a namespace. For a name in no namespace, either of the
+ * forms <code>Q{}local</code> or simply <code>local</code> are accepted.
+ * @return the QName corresponding to the supplied name in EQName notation. This will always
+ * have an empty prefix.
+ */
+
+ public static QName fromEQName(String expandedName) {
+ String namespaceURI;
+ String localName;
+ if (expandedName.charAt(0) == 'Q' && expandedName.charAt(1) == '{') {
+ int closeBrace = expandedName.indexOf('}');
+ if (closeBrace < 0) {
+ throw new IllegalArgumentException("No closing '}' in EQName");
+ }
+ namespaceURI = expandedName.substring(2, closeBrace);
+ if (closeBrace == expandedName.length()) {
+ throw new IllegalArgumentException("Missing local part in EQName");
+ }
+ localName = expandedName.substring(closeBrace + 1);
+ } else {
+ namespaceURI = "";
+ localName = expandedName;
+ }
+
+ return new QName("", namespaceURI, localName);
+ }
+
+
+
+ /**
+ * Validate the QName against the XML 1.0 or XML 1.1 rules for valid names.
+ *
+ * @param processor The Processor in which the name is to be validated.
+ * This determines whether the XML 1.0 or XML 1.1 rules for forming names are used.
+ * @return true if the name is valid, false if not
+ */
+
+ public boolean isValid(Processor processor) {
+ NameChecker nc = processor.getUnderlyingConfiguration().getNameChecker();
+ String prefix = getPrefix();
+ if (prefix.length() > 0) {
+ if (!nc.isValidNCName(prefix)) {
+ return false;
+ }
+ }
+ return nc.isValidNCName(getLocalName());
+ }
+
+ /**
+ * Get the prefix of the QName. This plays no role in operations such as comparison
+ * of QNames for equality, but is retained (as specified in XPath) so that a string representation
+ * can be reconstructed.
+ * <p/>
+ * Returns the zero-length string in the case of a QName that has no prefix.
+ * </p>
+ * @return the prefix part of the QName, or "" if the name has no known prefix
+ */
+
+ public String getPrefix() {
+ return sqName.getPrefix();
+ }
+
+ /**
+ * The namespace URI of the QName. Returns "" (the zero-length string) if the
+ * QName is not in a namespace.
+ * @return the namespace part of the QName, or "" for a name in no namespace
+ */
+
+ public String getNamespaceURI() {
+ return sqName.getURI();
+ }
+
+ /**
+ * The local part of the QName
+ * @return the local part of the QName
+ */
+
+ public String getLocalName() {
+ return sqName.getLocalPart();
+ }
+
+ /**
+ * The expanded name, as a string using the notation devised by James Clark.
+ * If the name is in a namespace, the resulting string takes the form <code>{uri}local</code>.
+ * Otherwise, the value is the local part of the name.
+ * @return the name in Clark notation. If the name is not in a namespace, returns the
+ * local part of the name. Otherwise returns the concatenation of "{", the namespace part
+ * of the QName, "}", and the local part of the QName.
+ */
+
+ public String getClarkName() {
+ String uri = getNamespaceURI();
+ if (uri.length() == 0) {
+ return getLocalName();
+ } else {
+ return "{" + uri + "}" + getLocalName();
+ }
+ }
+
+ /**
+ * Convert the value to a string. The resulting string is the lexical form of the QName,
+ * using the original prefix if there was one.
+ */
+
+ public String toString() {
+ return sqName.getDisplayName();
+ }
+
+ /**
+ * Get a hash code for the QName, to support equality matching. This supports the
+ * semantics of equality, which considers only the namespace URI and local name, and
+ * not the prefix.
+ * @return a hashCode for the QName
+ */
+
+ public int hashCode() {
+ return sqName.hashCode();
+ }
+
+ /**
+ * Test whether two QNames are equal. This supports the
+ * semantics of equality, which considers only the namespace URI and local name, and
+ * not the prefix.
+ * @return true if the namespace URIs are equal and the local parts are equal, when
+ * compared character-by-character.
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof QName && sqName.equals(((QName)other).sqName);
+ }
+
+ /**
+ * Get the underlying StructuredQName
+ * @return the underlying StructuredQName
+ */
+
+ protected StructuredQName getStructuredQName() {
+ return sqName;
+ }
+
+ /** QName denoting the schema type xs:string **/
+ public static final QName XS_STRING =
+ new QName("xs", NamespaceConstant.SCHEMA, "string");
+ /** QName denoting the schema type xs:boolean **/
+ public static final QName XS_BOOLEAN =
+ new QName("xs", NamespaceConstant.SCHEMA, "boolean");
+ /** QName denoting the schema type xs:decimal **/
+ public static final QName XS_DECIMAL =
+ new QName("xs", NamespaceConstant.SCHEMA, "decimal");
+ /** QName denoting the schema type xs:float **/
+ public static final QName XS_FLOAT =
+ new QName("xs", NamespaceConstant.SCHEMA, "float");
+ /** QName denoting the schema type xs:double **/
+ public static final QName XS_DOUBLE =
+ new QName("xs", NamespaceConstant.SCHEMA, "double");
+ /** QName denoting the schema type xs:duration **/
+ public static final QName XS_DURATION =
+ new QName("xs", NamespaceConstant.SCHEMA, "duration");
+ /** QName denoting the schema type xs:dateTime **/
+ public static final QName XS_DATE_TIME =
+ new QName("xs", NamespaceConstant.SCHEMA, "dateTime");
+ /** QName denoting the schema type xs:time **/
+ public static final QName XS_TIME =
+ new QName("xs", NamespaceConstant.SCHEMA, "time");
+ /** QName denoting the schema type xs:date **/
+ public static final QName XS_DATE =
+ new QName("xs", NamespaceConstant.SCHEMA, "date");
+ /** QName denoting the schema type xs:gYearMonth **/
+ public static final QName XS_G_YEAR_MONTH =
+ new QName("xs", NamespaceConstant.SCHEMA, "gYearMonth");
+ /** QName denoting the schema type xs:gYear **/
+ public static final QName XS_G_YEAR =
+ new QName("xs", NamespaceConstant.SCHEMA, "gYear");
+ /** QName denoting the schema type xs:gMonthDay **/
+ public static final QName XS_G_MONTH_DAY =
+ new QName("xs", NamespaceConstant.SCHEMA, "gMonthDay");
+ /** QName denoting the schema type xs:gDay **/
+ public static final QName XS_G_DAY =
+ new QName("xs", NamespaceConstant.SCHEMA, "gDay");
+ /** QName denoting the schema type xs:gMonth **/
+ public static final QName XS_G_MONTH =
+ new QName("xs", NamespaceConstant.SCHEMA, "gMonth");
+ /** QName denoting the schema type xs:hexBinary **/
+ public static final QName XS_HEX_BINARY =
+ new QName("xs", NamespaceConstant.SCHEMA, "hexBinary");
+ /** QName denoting the schema type xs:base64Binary **/
+ public static final QName XS_BASE64_BINARY =
+ new QName("xs", NamespaceConstant.SCHEMA, "base64Binary");
+ /** QName denoting the schema type xs:anyURI **/
+ public static final QName XS_ANY_URI =
+ new QName("xs", NamespaceConstant.SCHEMA, "anyURI");
+ /** QName denoting the schema type xs:QName **/
+ public static final QName XS_QNAME =
+ new QName("xs", NamespaceConstant.SCHEMA, "QName");
+ /** QName denoting the schema type xs:NOTATION **/
+ public static final QName XS_NOTATION =
+ new QName("xs", NamespaceConstant.SCHEMA, "NOTATION");
+ /** QName denoting the schema type xs:integer **/
+ public static final QName XS_INTEGER =
+ new QName("xs", NamespaceConstant.SCHEMA, "integer");
+ /** QName denoting the schema type xs:nonPositiveInteger **/
+ public static final QName XS_NON_POSITIVE_INTEGER =
+ new QName("xs", NamespaceConstant.SCHEMA, "nonPositiveInteger");
+ /** QName denoting the schema type xs:negativeInteger **/
+ public static final QName XS_NEGATIVE_INTEGER =
+ new QName("xs", NamespaceConstant.SCHEMA, "negativeInteger");
+ /** QName denoting the schema type xs:long **/
+ public static final QName XS_LONG =
+ new QName("xs", NamespaceConstant.SCHEMA, "long");
+ /** QName denoting the schema type xs:int **/
+ public static final QName XS_INT =
+ new QName("xs", NamespaceConstant.SCHEMA, "int");
+ /** QName denoting the schema type xs:short **/
+ public static final QName XS_SHORT =
+ new QName("xs", NamespaceConstant.SCHEMA, "short");
+ /** QName denoting the schema type xs:byte **/
+ public static final QName XS_BYTE =
+ new QName("xs", NamespaceConstant.SCHEMA, "byte");
+ /** QName denoting the schema type xs:nonNegativeInteger **/
+ public static final QName XS_NON_NEGATIVE_INTEGER =
+ new QName("xs", NamespaceConstant.SCHEMA, "nonNegativeInteger");
+ /** QName denoting the schema type xs:positiveInteger **/
+ public static final QName XS_POSITIVE_INTEGER =
+ new QName("xs", NamespaceConstant.SCHEMA, "positiveInteger");
+ /** QName denoting the schema type xs:unsignedLong **/
+ public static final QName XS_UNSIGNED_LONG =
+ new QName("xs", NamespaceConstant.SCHEMA, "unsignedLong");
+ /** QName denoting the schema type xs:unsignedInt **/
+ public static final QName XS_UNSIGNED_INT =
+ new QName("xs", NamespaceConstant.SCHEMA, "unsignedInt");
+ /** QName denoting the schema type xs:unsignedShort **/
+ public static final QName XS_UNSIGNED_SHORT =
+ new QName("xs", NamespaceConstant.SCHEMA, "unsignedShort");
+ /** QName denoting the schema type xs:unsignedByte **/
+ public static final QName XS_UNSIGNED_BYTE =
+ new QName("xs", NamespaceConstant.SCHEMA, "unsignedByte");
+ /** QName denoting the schema type xs:normalizedString **/
+ public static final QName XS_NORMALIZED_STRING =
+ new QName("xs", NamespaceConstant.SCHEMA, "normalizedString");
+ /** QName denoting the schema type xs:token **/
+ public static final QName XS_TOKEN =
+ new QName("xs", NamespaceConstant.SCHEMA, "token");
+ /** QName denoting the schema type xs:language **/
+ public static final QName XS_LANGUAGE =
+ new QName("xs", NamespaceConstant.SCHEMA, "language");
+ /** QName denoting the schema type xs:NMTOKEN **/
+ public static final QName XS_NMTOKEN =
+ new QName("xs", NamespaceConstant.SCHEMA, "NMTOKEN");
+ /** QName denoting the schema type xs:NMTOKENS **/
+ public static final QName XS_NMTOKENS =
+ new QName("xs", NamespaceConstant.SCHEMA, "NMTOKENS");
+ /** QName denoting the schema type xs:Name **/
+ public static final QName XS_NAME =
+ new QName("xs", NamespaceConstant.SCHEMA, "Name");
+ /** QName denoting the schema type xs:NCName **/
+ public static final QName XS_NCNAME =
+ new QName("xs", NamespaceConstant.SCHEMA, "NCName");
+ /** QName denoting the schema type xs:ID **/
+ public static final QName XS_ID =
+ new QName("xs", NamespaceConstant.SCHEMA, "ID");
+ /** QName denoting the schema type xs:IDREF **/
+ public static final QName XS_IDREF =
+ new QName("xs", NamespaceConstant.SCHEMA, "IDREF");
+ /** QName denoting the schema type xs:IDREFS **/
+ public static final QName XS_IDREFS =
+ new QName("xs", NamespaceConstant.SCHEMA, "IDREFS");
+ /** QName denoting the schema type xs:ENTITY **/
+ public static final QName XS_ENTITY =
+ new QName("xs", NamespaceConstant.SCHEMA, "ENTITY");
+ /** QName denoting the schema type xs:ENTITIES **/
+ public static final QName XS_ENTITIES =
+ new QName("xs", NamespaceConstant.SCHEMA, "ENTITIES");
+ /** QName denoting the schema type xs:untyped **/
+ public static final QName XS_UNTYPED =
+ new QName("xs", NamespaceConstant.SCHEMA, "untyped");
+ /** QName denoting the schema type xs:untypedAtomic **/
+ public static final QName XS_UNTYPED_ATOMIC =
+ new QName("xs", NamespaceConstant.SCHEMA, "untypedAtomic");
+ /** QName denoting the schema type xs:anyAtomicType **/
+ public static final QName XS_ANY_ATOMIC_TYPE =
+ new QName("xs", NamespaceConstant.SCHEMA, "anyAtomicType");
+ /** QName denoting the schema type xs:yearMonthDuration **/
+ public static final QName XS_YEAR_MONTH_DURATION =
+ new QName("xs", NamespaceConstant.SCHEMA, "yearMonthDuration");
+ /** QName denoting the schema type xs:dayTimeDuration **/
+ public static final QName XS_DAY_TIME_DURATION =
+ new QName("xs", NamespaceConstant.SCHEMA, "dayTimeDuration");
+ /** QName denoting the schema type xs:dateTimeStamp **/
+ /*@NotNull*/ public static final QName XS_DATE_TIME_STAMP =
+ new QName("xs", NamespaceConstant.SCHEMA, "dateTimeStamp");
+
+}
+
diff --git a/sf/saxon/s9api/SAXDestination.java b/sf/saxon/s9api/SAXDestination.java
new file mode 100644
index 0000000..c912e0d
--- /dev/null
+++ b/sf/saxon/s9api/SAXDestination.java
@@ -0,0 +1,66 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.ContentHandlerProxy;
+import net.sf.saxon.event.Receiver;
+import org.xml.sax.ContentHandler;
+
+
+/**
+ * This class represents a Destination (for example, the destination of the output of a transformation)
+ * in which events representing the XML document are sent to a user-supplied SAX2 ContentHandler, as
+ * if the ContentHandler were receiving the document directly from an XML parser.
+ */
+
+public class SAXDestination implements Destination {
+
+ private ContentHandler contentHandler;
+
+ /**
+ * Create a SAXDestination, supplying a SAX ContentHandler to which
+ * events will be routed
+ * @param handler the SAX ContentHandler that is to receive the output. If the
+ * ContentHandler is also a {@link org.xml.sax.ext.LexicalHandler} then it will also receive
+ * notification of events such as comments.
+ */
+
+ public SAXDestination(ContentHandler handler) {
+ contentHandler = handler;
+ }
+
+ /**
+ * Return a Receiver. Saxon calls this method to obtain a Receiver, to which it then sends
+ * a sequence of events representing the content of an XML document.
+ *
+ * @param config The Saxon configuration. This is supplied so that the destination can
+ * use information from the configuration (for example, a reference to the name pool)
+ * to construct or configure the returned Receiver.
+ * @return the Receiver to which events are to be sent.
+ * @throws net.sf.saxon.s9api.SaxonApiException
+ * if the Receiver cannot be created
+ */
+
+ /*@NotNull*/ public Receiver getReceiver(Configuration config) throws SaxonApiException {
+ ContentHandlerProxy chp = new ContentHandlerProxy();
+ chp.setUnderlyingContentHandler(contentHandler);
+ chp.setPipelineConfiguration(config.makePipelineConfiguration());
+ return chp;
+ }
+
+ /**
+ * Close the destination, allowing resources to be released. Saxon calls this method when
+ * it has finished writing to the destination.
+ */
+
+ public void close() throws SaxonApiException {
+ // no action
+ }
+}
+
diff --git a/sf/saxon/s9api/SaxonApiException.java b/sf/saxon/s9api/SaxonApiException.java
new file mode 100644
index 0000000..767a42d
--- /dev/null
+++ b/sf/saxon/s9api/SaxonApiException.java
@@ -0,0 +1,72 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * An exception thrown by the Saxon s9api API. This is always a wrapper for some other underlying exception
+ */
+public class SaxonApiException extends Exception {
+
+ /**
+ * Create a SaxonApiException
+ * @param cause the underlying cause of the exception
+ */
+
+ public SaxonApiException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Create a SaxonApiException
+ * @param message the message
+ */
+
+ public SaxonApiException(String message) {
+ super(new XPathException(message));
+ }
+
+ /**
+ * Create a SaxonApiException
+ * @param message the message
+ * @param cause the underlying cause of the exception
+ */
+
+ public SaxonApiException(String message, Throwable cause) {
+ super(new XPathException(message, cause));
+ }
+
+ /**
+ * Returns the detail message string of this throwable.
+ *
+ * @return the detail message string of this <tt>Throwable</tt> instance
+ * (which may be <tt>null</tt>).
+ */
+ public String getMessage() {
+ return getCause().getMessage();
+ }
+
+ /**
+ * Get the error code associated with the exception, if there is one
+ * @return the associated error code, or null if no error code is available
+ * @since 9.3
+ */
+
+ /*@Nullable*/ public QName getErrorCode() {
+ Throwable cause = getCause();
+ if (cause instanceof XPathException) {
+ StructuredQName code = ((XPathException)cause).getErrorCodeQName();
+ return (code==null ? null : new QName(code));
+ } else {
+ return null;
+ }
+ }
+}
+
diff --git a/sf/saxon/s9api/SaxonApiUncheckedException.java b/sf/saxon/s9api/SaxonApiUncheckedException.java
new file mode 100644
index 0000000..db80a38
--- /dev/null
+++ b/sf/saxon/s9api/SaxonApiUncheckedException.java
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+/**
+ * An unchecked exception thrown by the Saxon API. Unchecked exceptions are used only when errors occur in a method
+ * for which the interface specification defines no checked exception, for example {@link java.util.Iterator#next()}.
+ * The exception always wraps some underlying exception, which can be retrieved using {@link #getCause()}
+ */
+public class SaxonApiUncheckedException extends RuntimeException {
+
+ /**
+ * Create an unchecked exception
+ * @param err the underlying cause
+ */
+
+ public SaxonApiUncheckedException(Throwable err) {
+ super(err);
+ }
+
+
+ /**
+ * Returns the detail message string of this throwable.
+ *
+ * @return the detail message string of this <tt>Throwable</tt> instance
+ * (which may be <tt>null</tt>).
+ */
+ public String getMessage() {
+ return getCause().getMessage();
+ }
+}
diff --git a/sf/saxon/s9api/SchemaManager.java b/sf/saxon/s9api/SchemaManager.java
new file mode 100644
index 0000000..3ee78a7
--- /dev/null
+++ b/sf/saxon/s9api/SchemaManager.java
@@ -0,0 +1,170 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.SchemaURIResolver;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaException;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+
+/**
+ * The SchemaManager is used to load schema documents, and to set options for the way in which they are loaded.
+ * At present all the resulting schema components are held in a single pool owned by the Processor object.
+ */
+public class SchemaManager {
+
+ private Configuration config;
+ /*@Nullable*/ private ErrorListener errorListener;
+
+ protected SchemaManager(Configuration config) {
+ this.config = config;
+ this.errorListener = null;
+ }
+
+ /**
+ * Set the version of XSD in use. The value must be "1.0" or "1.1". The default is currently "1.0",
+ * but this may change in a future release.
+ * @param version the version of the XSD specification/language: either "1.0" or "1.1".
+ */
+
+ public void setXsdVersion(String version) {
+ if (version.equals("1.0") || version.equals("1.1")) {
+ config.setConfigurationProperty(FeatureKeys.XSD_VERSION, version);
+ } else {
+ throw new IllegalArgumentException("XsdVersion");
+ }
+ }
+
+ /**
+ * Get the version of XSD in use. The value will be "1.0" or "1.1"
+ * @return the version of XSD in use.
+ */
+
+ public String getXsdVersion() {
+ return config.getXsdVersion() == Configuration.XSD10 ? "1.0" : "1.1";
+ }
+
+ /**
+ * Set the ErrorListener to be used while loading and validating schema documents
+ * @param listener The error listener to be used. This is notified of all errors detected during the
+ * compilation. May be set to null to revert to the default ErrorListener.
+ */
+
+ public void setErrorListener(/*@Nullable*/ ErrorListener listener) {
+ this.errorListener = listener;
+ }
+
+ /**
+ * Get the ErrorListener being used while loading and validating schema documents
+ * @return listener The error listener in use. This is notified of all errors detected during the
+ * compilation. Returns null if no user-supplied ErrorListener has been set.
+ */
+
+ /*@Nullable*/
+ public ErrorListener getErrorListener() {
+ return errorListener;
+ }
+
+ /**
+ * Set the SchemaURIResolver to be used during schema loading. This SchemaURIResolver, despite its name,
+ * is <b>not</b> used for resolving relative URIs against a base URI; it is used for dereferencing
+ * an absolute URI (after resolution) to return a {@link javax.xml.transform.Source} representing the
+ * location where a schema document can be found.
+ *
+ * <p>This SchemaURIResolver is used to dereference the URIs appearing in <code>xs:import</code>,
+ * <code>xs:include</code>, and <code>xs:redefine</code> declarations.
+ *
+ * @param resolver the SchemaURIResolver to be used during schema loading.
+ */
+
+ public void setSchemaURIResolver(SchemaURIResolver resolver) {
+ config.setSchemaURIResolver(resolver);
+ }
+
+ /**
+ * Get the SchemaURIResolver to be used during schema loading.
+ *
+ * @return the URIResolver used during stylesheet compilation. Returns null if no user-supplied
+ * URIResolver has been set.
+ */
+
+ public SchemaURIResolver getSchemaURIResolver() {
+ return config.getSchemaURIResolver();
+ }
+
+ /**
+ * Load a schema document from a given Source. The schema components derived from this schema
+ * document are added to the cache of schema components maintained by this SchemaManager
+ * @param source the document containing the schema. The getSystemId() method applied to this Source
+ * must return a base URI suitable for resolving <code>xs:include</code> and <code>xs:import</code>
+ * directives.
+ * @throws SaxonApiException if the schema document is not valid, or if its contents are inconsistent
+ * with the schema components already held by this SchemaManager.
+ */
+
+ public void load(Source source) throws SaxonApiException {
+ try {
+ config.addSchemaSource(source, errorListener);
+ } catch (SchemaException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Import a precompiled Schema Component Model from a given Source. The schema components derived from this schema
+ * document are added to the cache of schema components maintained by this SchemaManager
+ * @param source the XML file containing the schema component model, as generated by a previous call on
+ * {@link #exportComponents}
+ * @throws SaxonApiException if a failure occurs loading the schema from the supplied source
+ */
+
+ public void importComponents(Source source) throws SaxonApiException {
+ try {
+ config.importComponents(source);
+ } catch (XPathException err) {
+ throw new SaxonApiException(err);
+ }
+ }
+
+ /**
+ * Export a precompiled Schema Component Model containing all the components (except built-in components)
+ * that have been loaded into this Processor.
+ * @param destination the destination to recieve the precompiled Schema Component Model in the form of an
+ * XML document
+ * @throws SaxonApiException if a failure occurs writing the schema components to the supplied destination
+ */
+
+ public void exportComponents(Destination destination) throws SaxonApiException {
+ try {
+ Receiver out = destination.getReceiver(config);
+ config.exportComponents(out);
+ destination.close();
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+
+ /**
+ * Create a SchemaValidator which can be used to validate instance documents against the schema held by this
+ * SchemaManager
+ * @return a new SchemaValidator
+ */
+
+ public SchemaValidator newSchemaValidator() {
+ return new SchemaValidator(config);
+ }
+
+
+}
+
diff --git a/sf/saxon/s9api/SchemaValidator.java b/sf/saxon/s9api/SchemaValidator.java
new file mode 100644
index 0000000..6e970a0
--- /dev/null
+++ b/sf/saxon/s9api/SchemaValidator.java
@@ -0,0 +1,361 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.Sender;
+import net.sf.saxon.event.Sink;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.GlobalParam;
+import net.sf.saxon.expr.instruct.GlobalParameterSet;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.FingerprintedQName;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.ValidationParams;
+import net.sf.saxon.value.SequenceExtent;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+
+/**
+ * A <tt>SchemaValidator</tt> is an object that is used for validating instance documents against a schema.
+ * The schema consists of the collection of schema components that are available within the schema
+ * cache maintained by the SchemaManager, together with any additional schema components located
+ * during the course of validation by means of an xsl:schemaLocation or xsi:noNamespaceSchemaLocation
+ * attribute within the instance document.
+ *
+ * <p>If validation fails, an exception is thrown. If validation succeeds, the validated document
+ * can optionally be written to a specified destination. This will be a copy of the original document,
+ * augmented with default values for absent elements and attributes, and carrying type annotations
+ * derived from the schema processing. Expansion of defaults can be suppressed by means of the method
+ * {@link #setExpandAttributeDefaults(boolean)}.</p>
+ *
+ * <p>A <tt>SchemaValidator</tt> is serially resuable but not thread-safe. That is, it should normally
+ * be used in the thread where it is created, but it can be used more than once, to validate multiple
+ * input documents.</p>
+ *
+ * <p>A <tt>SchemaValidator</tt> is a <tt>Destination</tt>, which allows it to receive the output of a
+ * query or transformation to be validated.</p>
+ *
+ * <p>Saxon does not deliver the full PSVI as described in the XML schema specifications,
+ * only the subset of the PSVI properties featured in the XDM data model.</p>
+ *
+ */
+
+public class SchemaValidator implements Destination {
+
+ private Configuration config;
+ private boolean lax;
+ private ErrorListener errorListener;
+ /*@Nullable*/ private Destination destination;
+ private QName documentElementName;
+ private SchemaType documentElementType;
+ private boolean expandAttributeDefaults = true;
+ private boolean useXsiSchemaLocation;
+ private GlobalParameterSet suppliedParams = new GlobalParameterSet();
+
+ protected SchemaValidator(Configuration config) {
+ this.config = config;
+ this.useXsiSchemaLocation = (Boolean) config.getConfigurationProperty(
+ FeatureKeys.USE_XSI_SCHEMA_LOCATION);
+ }
+
+ /**
+ * The validation mode may be either strict or lax. The default is strict; this method may be called
+ * to indicate that lax validation is required. With strict validation, validation fails if no element
+ * declaration can be located for the outermost element. With lax validation, the absence of an
+ * element declaration results in the content being considered valid.
+ * @param lax true if validation is to be lax, false if it is to be strict
+ */
+
+ public void setLax(boolean lax) {
+ this.lax = lax;
+ }
+
+ /**
+ * Ask whether validation is to be in lax mode.
+ * @return true if validation is to be in lax mode, false if it is to be in strict mode.
+ */
+
+ public boolean isLax() {
+ return lax;
+ }
+
+ /**
+ * Set the ErrorListener to be used while validating instance documents.
+ * @param listener The error listener to be used. This is notified of all errors detected during the
+ * validation episode.
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ this.errorListener = listener;
+ }
+
+ /**
+ * Get the ErrorListener being used while validating instance documents
+ * @return listener The error listener in use. This is notified of all errors detected during the
+ * validation episode. Returns null if no user-supplied ErrorListener has been set.
+ */
+
+ public ErrorListener getErrorListener() {
+ return errorListener;
+ }
+
+ /**
+ * Say whether the schema processor is to take account of any xsi:schemaLocation and
+ * xsi:noNamespaceSchemaLocation attributes encountered while validating an instance document
+ * @param recognize true if these two attributes are to be recognized; false if they are to
+ * be ignored. Default is true.
+ */
+
+ public void setUseXsiSchemaLocation(boolean recognize) {
+ useXsiSchemaLocation = recognize;
+ }
+
+ /**
+ * Ask whether the schema processor is to take account of any xsi:schemaLocation and
+ * xsi:noNamespaceSchemaLocation attributes encountered while validating an instance document
+ * @return true if these two attributes are to be recognized; false if they are to
+ * be ignored. Default is true.
+ */
+
+ public boolean isUseXsiSchemaLocation() {
+ return useXsiSchemaLocation;
+ }
+
+
+ /**
+ * Set the Destination to receive the validated document. If no destination is supplied, the
+ * validated document is discarded.
+ * @param destination the destination to receive the validated document
+ */
+
+ public void setDestination(/*@Nullable*/ Destination destination) {
+ this.destination = destination;
+ }
+
+ /**
+ * Get the Destination that will receive the validated document. Return null if no destination
+ * has been set.
+ * @return the destination to receive the validated document, or null if none has been supplied
+ */
+
+
+ /*@Nullable*/ public Destination getDestination() {
+ return destination;
+ }
+
+ /**
+ * Set the name of the required top-level element of the document to be validated (that is, the
+ * name of the outermost element of the document). If no value is supplied, there is no constraint
+ * on the required element name
+ * @param name the name of the document element, as a QName; or null to remove a previously-specified
+ * value.
+ */
+
+ public void setDocumentElementName(QName name) {
+ documentElementName = name;
+ }
+
+ /**
+ * Get the name of the required top-level element of the document to be validated.
+ * @return the name of the required document element, or null if no value has been set.
+ */
+
+ public QName getDocumentElementName() {
+ return documentElementName;
+ }
+
+ /**
+ * Set the name of the required type of the top-level element of the document to be validated.
+ * If no value is supplied, there is no constraint on the required type
+ * @param name the name of the type of the document element, as a QName;
+ * or null to remove a previously-specified value. This must be the name of a type in the
+ * schema (typically but not necessarily a complex type).
+ * @throws SaxonApiException if there is no known type with this name
+ */
+
+ public void setDocumentElementTypeName(QName name) throws SaxonApiException {
+ int fp = config.getNamePool().allocate(
+ "", name.getNamespaceURI(), name.getLocalName());
+ documentElementType = config.getSchemaType(fp);
+ if (documentElementType == null) {
+ throw new SaxonApiException("Unknown type " + name.getClarkName());
+ }
+ }
+
+ /**
+ * Get the name of the required type of the top-level element of the document to be validated.
+ * @return the name of the required type of the document element, or null if no value has been set.
+ */
+
+ public QName getDocumentElementTypeName() {
+ if (documentElementType == null) {
+ return null;
+ } else {
+ int fp = documentElementType.getFingerprint();
+ return new QName(config.getNamePool().getStructuredQName(fp));
+ }
+ }
+
+ /**
+ * Get the schema type against which the document element is to be validated
+ * @return the schema type
+ */
+
+ protected SchemaType getDocumentElementType() {
+ return documentElementType;
+ }
+
+ /**
+ * Set whether attribute defaults defined in a schema are to be expanded or not
+ * (by default, fixed and default attribute values are expanded, that is, they are inserted
+ * into the document during validation as if they were present in the instance being validated)
+ * @param expand true if defaults are to be expanded, false if not
+ */
+
+ public void setExpandAttributeDefaults(boolean expand) {
+ expandAttributeDefaults = expand;
+ }
+
+ /**
+ * Ask whether attribute defaults defined in a schema are to be expanded or not
+ * (by default, fixed and default attribute values are expanded, that is, they are inserted
+ * into the document during validation as if they were present in the instance being validated)
+ * @return true if defaults are to be expanded, false if not
+ */
+
+ public boolean isExpandAttributeDefaults() {
+ return expandAttributeDefaults;
+ }
+
+ /**
+ * Set the value of a schema parameter (a parameter defined in the schema using
+ * the <code>saxon:param</code> extension)
+ * @param name the name of the schema parameter, as a QName
+ * @param value the value of the schema parameter, or null to clear a previously set value
+ * @since 9.5
+ */
+
+ public void setParameter(QName name, XdmValue value) {
+ suppliedParams.put(name.getStructuredQName(), value==null ? null : value.getUnderlyingValue());
+ }
+
+ /**
+ * Get the value that has been set for a schema parameter (a parameter defined in the schema using
+ * the <code>saxon:param</code> extension)
+ * @param name the parameter whose name is required
+ * @return the value that has been set for the parameter, or null if no value has been set
+ * @since 9.5
+ */
+
+ public XdmValue getParameter(QName name) {
+ return XdmValue.wrap((Sequence)suppliedParams.get(name.getStructuredQName()));
+ }
+
+
+ /**
+ * Validate an instance document supplied as a Source object
+ * @param source the instance document to be validated. The call getSystemId() applied to
+ * this source object must return the base URI used for dereferencing any xsi:schemaLocation
+ * or xsi:noNamespaceSchemaLocation attributes
+ * @throws SaxonApiException if the source document is found to be invalid
+ */
+
+ public void validate(Source source) throws SaxonApiException {
+ Receiver receiver = getReceiver(config, source.getSystemId());
+ try {
+ ParseOptions options = new ParseOptions();
+ //options.setContinueAfterValidationErrors(true);
+ options.setValidationParams(convertParams(suppliedParams, config.getDeclaredSchemaParameters()));
+ Sender.send(source, receiver, options);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ } finally {
+ if (destination != null) {
+ destination.close();
+ }
+ }
+ }
+
+ protected ValidationParams convertParams(GlobalParameterSet suppliedParams, java.util.Collection<GlobalParam> declaredParams)
+ throws XPathException {
+ ValidationParams vp = new ValidationParams();
+ XPathContext context = new Controller(config).newXPathContext();
+ for (GlobalParam cp : declaredParams) {
+ Sequence value = suppliedParams.convertParameterValue(
+ cp.getVariableQName(), cp.getRequiredType(), true, context);
+ if (value != null) {
+ if (!(value instanceof GroundedValue)) {
+ value = new SequenceExtent(value.iterate());
+ }
+ vp.put(cp.getVariableQName(), value);
+ }
+ }
+ return vp;
+ }
+
+ public Receiver getReceiver(Configuration config) throws SaxonApiException {
+ return getReceiver(config, null);
+ }
+
+ private Receiver getReceiver(Configuration config, /*@Nullable*/ String systemId) throws SaxonApiException {
+ Controller controller = new Controller(config);
+ controller.getExecutable().setSchemaAware(true);
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ pipe.setExpandAttributeDefaults(expandAttributeDefaults);
+ pipe.setUseXsiSchemaLocation(useXsiSchemaLocation);
+ pipe.setController(controller);
+
+ ParseOptions options = pipe.getParseOptions();
+ options.setCheckEntityReferences(true);
+ options.setSchemaValidationMode(lax ? Validation.LAX : Validation.STRICT);
+ options.setStripSpace(Whitespace.NONE);
+ options.setTopLevelType(documentElementType);
+ if (documentElementName != null) {
+ options.setTopLevelElement(new FingerprintedQName(
+ documentElementName.getPrefix(), documentElementName.getNamespaceURI(), documentElementName.getLocalName()));
+ }
+ try {
+ options.setValidationParams(convertParams(suppliedParams, config.getDeclaredSchemaParameters()));
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ Receiver output = (destination == null ? new Sink(pipe) : destination.getReceiver(config));
+ output.setPipelineConfiguration(pipe);
+
+ Receiver receiver = config.getDocumentValidator(output, systemId, options);
+ if (errorListener != null) {
+ pipe.setErrorListener(errorListener);
+ }
+ return receiver;
+ }
+
+ /**
+ * Close the destination, allowing resources to be released. Saxon calls this method when
+ * it has finished writing to the destination.
+ */
+
+ public void close() throws SaxonApiException {
+ if (destination != null) {
+ destination.close();
+ destination = null;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/s9api/SequenceType.java b/sf/saxon/s9api/SequenceType.java
new file mode 100644
index 0000000..ec239c5
--- /dev/null
+++ b/sf/saxon/s9api/SequenceType.java
@@ -0,0 +1,81 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+/**
+ * A SequenceType is the combination of an ItemType and an OccurrenceIndicator
+ */
+public class SequenceType {
+
+ private ItemType itemType;
+ private OccurrenceIndicator occurrenceIndicator;
+
+ /**
+ * Construct a SequenceType
+ * @param itemType the ItemType
+ * @param occurrenceIndicator the permitted number of occurrences of the item in the sequence
+ */
+
+ private SequenceType(ItemType itemType, OccurrenceIndicator occurrenceIndicator) {
+ this.itemType = itemType;
+ this.occurrenceIndicator = occurrenceIndicator;
+ }
+
+ /**
+ * Factory method to construct a SequenceType
+ * @param itemType the ItemType
+ * @param occurrenceIndicator the permitted number of occurrences of the item in the sequence
+ * @return the constricted SequenceType
+ */
+
+ /*@NotNull*/ public static SequenceType makeSequenceType(ItemType itemType, OccurrenceIndicator occurrenceIndicator) {
+ return new SequenceType(itemType, occurrenceIndicator);
+ }
+
+ /**
+ * Get the item type
+ * @return the item type
+ */
+
+ public ItemType getItemType() {
+ return itemType;
+ }
+
+ /**
+ * Get the occurrence indicator
+ * @return the occurrence indicator
+ */
+
+ public OccurrenceIndicator getOccurrenceIndicator() {
+ return occurrenceIndicator;
+ }
+
+ /**
+ * Test whether two SequenceType objects represent the same type
+ * @param other the other SequenceType object
+ * @return true if the other object is a SequenceType representing the same type
+ * @since 9.5
+ */
+
+ public final boolean equals(Object other) {
+ return other instanceof SequenceType &&
+ ((SequenceType)other).getOccurrenceIndicator().equals(getOccurrenceIndicator()) &&
+ ((SequenceType)other).getItemType().equals(getItemType());
+ }
+
+ /**
+ * Get a hash code with semantics corresponding to the equals() method
+ * @return the hash code
+ * @since 9.5
+ */
+
+ public final int hashCode() {
+ return getItemType().hashCode() ^ (getOccurrenceIndicator().hashCode() << 17);
+ }
+
+}
diff --git a/sf/saxon/s9api/Serializer.java b/sf/saxon/s9api/Serializer.java
new file mode 100644
index 0000000..d53de88
--- /dev/null
+++ b/sf/saxon/s9api/Serializer.java
@@ -0,0 +1,620 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.NamespaceReducer;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.StreamWriterToReceiver;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.ResultDocument;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.lib.SerializerFactory;
+import net.sf.saxon.query.QueryResult;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.stream.StreamResult;
+import java.io.File;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * A Serializer takes a tree representation of XML and turns it into lexical XML markup.
+ * <p/>
+ * <p><i>Note that this is XML serialization in the sense of the W3C XSLT and XQuery specifications.
+ * This has nothing to do with the serialization of Java objects, or the {@link java.io.Serializable}
+ * interface.</i></p>
+ * <p/>
+ * <p>The serialization may be influenced by a number of serialization parameters. A parameter has a name,
+ * which is an instance of {@link Serializer.Property}, and a value, which is expressed as a string.
+ * The effect of most of the properties is as described in the W3C specification
+ * <a href="http://www.w3.org/TR/xslt-xquery-serialization/">XSLT 2.0 and XQuery 1.0 Serialization</a>.
+ * Saxon supports all the serialization parameters defined in that specification, together with some
+ * additional parameters, whose property names are prefixed "SAXON_".
+ * <p/>
+ * <p>Serialization parameters defined via this interface take precedence over any serialization parameters
+ * defined within the source of the query or stylesheet.
+ */
+ at SuppressWarnings({"ForeachStatement"})
+public class Serializer implements Destination {
+
+ private Configuration config; // Beware: this will often be null
+ private Map<Property, String> properties = new HashMap<Property, String>(10);
+ private StreamResult result = new StreamResult();
+ private boolean mustClose = false;
+
+ /**
+ * Enumerator over the defined serialization properties
+ */
+
+ public enum Property {
+ /**
+ * Serialization method: xml, html, xhtml, or text
+ */
+ METHOD(OutputKeys.METHOD),
+ /**
+ * Version of output method, for example "1.0" or "1.1" for XML
+ */
+ VERSION(OutputKeys.VERSION),
+ /**
+ * Character encoding of output stream
+ */
+ ENCODING(OutputKeys.ENCODING),
+ /**
+ * Set to "yes" if the XML declaration is to be omitted from the output file
+ */
+ OMIT_XML_DECLARATION(OutputKeys.OMIT_XML_DECLARATION),
+ /**
+ * Set to "yes", "no", or "omit" to indicate the required value of the standalone attribute
+ * in the XML declaration of the output file
+ */
+ STANDALONE(OutputKeys.STANDALONE),
+ /**
+ * Set to any string to indicate that the output is to include a DOCTYPE declaration with this public id
+ */
+ DOCTYPE_PUBLIC(OutputKeys.DOCTYPE_PUBLIC),
+ /**
+ * Set to any string to indicate that the output is to include a DOCTYPE declaration with this system id
+ */
+ DOCTYPE_SYSTEM(OutputKeys.DOCTYPE_SYSTEM),
+ /**
+ * Space-separated list of QNames (in Clark form) of elements
+ * whose content is to be wrapped in CDATA sections
+ */
+ CDATA_SECTION_ELEMENTS(OutputKeys.CDATA_SECTION_ELEMENTS),
+ /**
+ * Set to "yes" or "no" to indicate whether indentation is required
+ */
+ INDENT(OutputKeys.INDENT),
+ /**
+ * Set to indicate the media type (MIME type) of the output
+ */
+ MEDIA_TYPE(OutputKeys.MEDIA_TYPE),
+ /**
+ * List of names of character maps to be used. Character maps can only be specified in an XSLT
+ * stylesheet.
+ */
+ USE_CHARACTER_MAPS(SaxonOutputKeys.USE_CHARACTER_MAPS),
+ /**
+ * For HTML and XHTML, set to "yes" or "no" to indicate whether a <meta> element is to be
+ * written to indicate the content type and encoding
+ */
+ INCLUDE_CONTENT_TYPE(SaxonOutputKeys.INCLUDE_CONTENT_TYPE),
+ /**
+ * Set to "yes" or "no" to indicate (for XML 1.1) whether namespace that go out of scope should
+ * be undeclared
+ */
+ UNDECLARE_PREFIXES(SaxonOutputKeys.UNDECLARE_PREFIXES),
+ /**
+ * Set to "yes" or "no" to indicate (for HTML and XHTML) whether URI-valued attributes should be
+ * percent-encoded
+ */
+ ESCAPE_URI_ATTRIBUTES(SaxonOutputKeys.ESCAPE_URI_ATTRIBUTES),
+ /**
+ * Set to "yes" or "no" to indicate whether a byte order mark is to be written
+ */
+ BYTE_ORDER_MARK(SaxonOutputKeys.BYTE_ORDER_MARK),
+ /**
+ * Set to the name of a Unicode normalization form: "NFC", "NFD", "NFKC", or "NFKD", or
+ * "none" to indicate no normalization
+ */
+ NORMALIZATION_FORM(SaxonOutputKeys.NORMALIZATION_FORM),
+
+ /**
+ * Saxon extension: set to an integer (represented as a string) giving the number of spaces
+ * by which each level of nesting should be indented. Default is 3.
+ */
+ SAXON_INDENT_SPACES(SaxonOutputKeys.INDENT_SPACES),
+ /**
+ * Saxon extension: set to an integer (represented as a string) giving the desired maximum
+ * length of lines when indenting. Default is 80.
+ */
+ SAXON_LINE_LENGTH(SaxonOutputKeys.LINE_LENGTH),
+ /**
+ * Saxon extension: set to a space-separated list of attribute names, in Clark notation,
+ * indicating that attributes present in the list should be serialized in the order
+ * indicated, followed by attributes not present in the list (these are sorted first
+ * by namespace, then by local name).
+ */
+ SAXON_ATTRIBUTE_ORDER(SaxonOutputKeys.ATTRIBUTE_ORDER),
+ /**
+ * Saxon extension: set to a space-separated list of element names, in Clark notation,
+ * within which no content is to be indented. This is typically because the element contains
+ * mixed content in which whitespace is significant.
+ */
+ SAXON_SUPPRESS_INDENTATION(SaxonOutputKeys.SUPPRESS_INDENTATION),
+ /**
+ * Saxon extension: set to a space-separated list of element names, in Clark notation,
+ * representing elements that will be preceded by an extra blank line in the output in addition
+ * to normal indentation.
+ */
+ SAXON_DOUBLE_SPACE(SaxonOutputKeys.DOUBLE_SPACE),
+ /**
+ * Saxon extension for internal use: used in XSLT to tell the serializer whether the
+ * stylesheet used version="1.0" or version="2.0"
+ */
+ SAXON_STYLESHEET_VERSION(SaxonOutputKeys.STYLESHEET_VERSION),
+ /**
+ * Saxon extension to indicate how characters outside the encoding should be represented,
+ * for example "hex" for hexadecimal character references, "decimal" for decimal character references
+ */
+ SAXON_CHARACTER_REPRESENTATION(SaxonOutputKeys.CHARACTER_REPRESENTATION),
+ /**
+ * Saxon extension for use when writing to the text output method; this option causes the processing
+ * instructions hex and b64 to be recognized containing hexBinary or base64 data respectively.
+ */
+ SAXON_RECOGNIZE_BINARY(SaxonOutputKeys.RECOGNIZE_BINARY),
+ /**
+ * Saxon extension for use when output is sent to a SAX ContentHandler: indicates that the output
+ * is required to be well-formed (exactly one top-level element)
+ */
+ SAXON_REQUIRE_WELL_FORMED(SaxonOutputKeys.REQUIRE_WELL_FORMED),
+ /**
+ * Saxon extension, indicates that the output of a query is to be wrapped before serialization,
+ * such that each item in the result sequence is enclosed in an element indicating its type
+ */
+ SAXON_WRAP(SaxonOutputKeys.WRAP),
+ /**
+ * Saxon extension for internal use in XSLT, indicates that this output document is the implicitly
+ * created result tree as distinct from a tree created using <xsl:result-document>
+ */
+ SAXON_IMPLICIT_RESULT_DOCUMENT(SaxonOutputKeys.IMPLICIT_RESULT_DOCUMENT),
+ /**
+ * Saxon extension for interfacing with debuggers; indicates that the location information is
+ * available for events in this output stream
+ */
+ SAXON_SUPPLY_SOURCE_LOCATOR(SaxonOutputKeys.SUPPLY_SOURCE_LOCATOR);
+
+ private String name;
+
+ private Property(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the name of the property expressed as a QName in Clark notation.
+ * The namespace will be null for standard serialization properties,
+ * and will be the Saxon namespace <code>http://saxon.sf.net/</code> for Saxon extensions
+ *
+ * @return the name of the serialization property as a QName in Clark notation, {uri}local
+ */
+
+ public String toString() {
+ return name;
+ }
+
+ /**
+ * Get the name of the property expressed as a QName.
+ * The namespace will be null for standard serialization properties,
+ * and will be the Saxon namespace <code>http://saxon.sf.net/</code> for Saxon extensions
+ *
+ * @return the name of the serialization property as a QName
+ */
+
+ public QName getQName() {
+ return QName.fromClarkName(name);
+ }
+ }
+
+ /**
+ * Create a Serializer
+ */
+
+ public Serializer() {
+ }
+
+ /**
+ * Create a Serializer initialized to write to a given OutputStream.
+ * <p>Closing the output stream after use is the responsibility of the caller.</p>
+ *
+ * @param stream The OutputStream to which the Serializer will write
+ */
+
+ public Serializer(OutputStream stream) {
+ setOutputStream(stream);
+ }
+
+ /**
+ * Create a Serializer initialized to write to a given Writer.
+ * <p>Closing the writer after use is the responsibility of the caller.</p>
+ *
+ * @param writer The Writer to which the Serializer will write
+ */
+
+ public Serializer(Writer writer) {
+ setOutputWriter(writer);
+ }
+
+ /**
+ * Create a Serializer initialized to write to a given File.
+ *
+ * @param file The File to which the Serializer will write
+ */
+
+ public Serializer(File file) {
+ setOutputFile(file);
+ }
+
+ /**
+ * Set the Processor associated with this Serializer. This will be called automatically if the
+ * serializer is created using one of the <code>Processor.newSerializer()</code> methods. The Serializer
+ * currently needs to know about the Processor only if the method {@link #getXMLStreamWriter} is called.
+ *
+ * @param processor the associated Processor
+ * @since 9.3
+ */
+
+ public void setProcessor(Processor processor) {
+ this.config = processor.getUnderlyingConfiguration();
+ }
+
+ /**
+ * Set the value of a serialization property. Any existing value of the property is overridden.
+ * If the supplied value is null, any existing value of the property is removed.
+ * <p/>
+ * <p>Example:</p>
+ * <p><code>serializer.setOutputProperty(Serializer.Property.METHOD, "xml");</code></p>
+ * <p/>
+ * <p>Any serialization properties supplied via this interface take precedence over serialization
+ * properties defined in the source stylesheet or query.</p>
+ *
+ * @param property The name of the property to be set
+ * @param value The value of the property, as a string. The format is generally as defined
+ * in the <code>xsl:output</code> declaration in XSLT: this means that boolean properties, for
+ * example, are represented using the strings "yes" and "no". Properties whose values are QNames,
+ * such as <code>cdata-section-elements</code> are expressed using the Clark representation of
+ * a QName, that is "{uri}local". Multi-valued properties (again, <code>cdata-section-elements</code>
+ * is an example) are expressed as a space-separated list.
+ * @throws IllegalArgumentException if the value of the property is invalid. The property is
+ * validated individually; invalid combinations of properties will be detected only when the properties
+ * are actually used to serialize an XML event stream.
+ */
+
+ public void setOutputProperty(Property property, /*@Nullable*/ String value) {
+ try {
+ SaxonOutputKeys.checkOutputProperty(property.toString(), value, null);
+ } catch (XPathException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ if (value == null) {
+ properties.remove(property);
+ } else {
+ properties.put(property, value);
+ }
+ }
+
+ /**
+ * Get the value of a serialization property
+ *
+ * @param property the name of the required property
+ * @return the value of the required property as a string, or null if the property has
+ * not been given any value.
+ */
+
+ public String getOutputProperty(Property property) {
+ return properties.get(property);
+ }
+
+ /**
+ * Set the destination of the serialized output, as a Writer.
+ * <p/>
+ * <p>Note that when this option is used, the serializer does not perform character
+ * encoding. This also means that it never replaces special characters with XML numeric
+ * character references. The final encoding is the responsibility of the supplied Writer.</p>
+ * <p/>
+ * <p>Closing the writer after use is the responsibility of the caller.</p>
+ * <p/>
+ * <p>Calling this method has the side-effect of setting the OutputStream and OutputFile to null.</p>
+ *
+ * @param writer the Writer to which the serialized XML output will be written.
+ */
+
+ public void setOutputWriter(Writer writer) {
+ result.setOutputStream(null);
+ result.setSystemId((String) null);
+ result.setWriter(writer);
+ mustClose = false;
+ }
+
+ /**
+ * Set the destination of the serialized output, as an OutputStream.
+ * <p/>
+ * <p>Closing the output stream after use is the responsibility of the caller.</p>
+ * <p/>
+ * <p>Calling this method has the side-effect of setting the OutputWriter and OutputFile to null.</p>
+ *
+ * @param stream the OutputStream to which the serialized XML output will be written.
+ */
+
+ public void setOutputStream(OutputStream stream) {
+ result.setWriter(null);
+ result.setSystemId((String) null);
+ result.setOutputStream(stream);
+ mustClose = false;
+ }
+
+ /**
+ * Set the destination of the serialized output, as a File.
+ * <p/>
+ * <p>Calling this method has the side-effect of setting the current OutputWriter
+ * and OutputStream to null.</p>
+ *
+ * @param file the File to which the serialized XML output will be written.
+ */
+
+ public void setOutputFile(File file) {
+ result.setOutputStream(null);
+ result.setWriter(null);
+ result.setSystemId(file);
+ mustClose = true;
+ }
+
+ /**
+ * Serialize an XdmNode to the selected output destination using this serializer
+ *
+ * @param node The node to be serialized
+ * @throws IllegalStateException if no outputStream, Writer, or File has been supplied as the
+ * destination for the serialized output
+ * @throws SaxonApiException if a serialization error or I/O error occurs
+ * @since 9.3
+ */
+
+ public void serializeNode(XdmNode node) throws SaxonApiException {
+ StreamResult res = result;
+ if (res.getOutputStream() == null && res.getWriter() == null && res.getSystemId() == null) {
+ throw new IllegalStateException("Either an outputStream, or a Writer, or a File must be supplied");
+ }
+ serializeNodeToResult(node, res);
+ }
+
+ /**
+ * Serialize an arbitrary XdmValue to the selected output destination using this serializer. The supplied
+ * sequence is first wrapped in a document node according to the rules given in section 2 (Sequence Normalization) of the
+ * <a href="http://www.w3.org/TR/xslt-xquery-serialization/">XSLT/XQuery serialization specification</a>; the resulting
+ * document nodes is then serialized using the serialization parameters defined in this serializer.
+ *
+ * @param value The value to be serialized
+ * @throws IllegalStateException if no outputStream, Writer, or File has been supplied as the
+ * destination for the serialized output, or if no Processor is associated with the serializer
+ * @throws SaxonApiException if a serialization error or I/O error occurs
+ * @since 9.3
+ */
+
+
+ public void serializeXdmValue(XdmValue value) throws SaxonApiException {
+ if (value instanceof XdmNode) {
+ serializeNode((XdmNode) value);
+ } else {
+ if (config == null) {
+ throw new IllegalStateException("The serializer is not associated with any s9api Processor (need to call setProcessor())");
+ }
+ try {
+ QueryResult.serializeSequence(value.getUnderlyingValue().iterate(), config, result, getOutputProperties());
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ }
+
+ /**
+ * Serialize an XdmNode to a string using this serializer
+ *
+ * @param node The node to be serialized
+ * @throws SaxonApiException if a serialization error occurs
+ * @return the serialized representation of the node as lexical XML
+ * @since 9.3
+ */
+
+ public String serializeNodeToString(XdmNode node) throws SaxonApiException {
+ StringWriter sw = new StringWriter();
+ StreamResult sr = new StreamResult(sw);
+ serializeNodeToResult(node, sr);
+ return sw.toString();
+ }
+
+ private void serializeNodeToResult(XdmNode node, Result res) throws SaxonApiException {
+ try {
+ QueryResult.serialize(node.getUnderlyingNode(), res, getOutputProperties());
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get an XMLStreamWriter that can be used for writing application-generated XML
+ * to be output via this serializer.
+ *
+ * @return a newly constructed XMLStreamWriter that pipes events into this Serializer
+ * @throws IllegalStateException if no Processor has been set for this Serializer
+ * @throws SaxonApiException if any other failure occurs
+ * @since 9.3
+ */
+
+ public StreamWriterToReceiver getXMLStreamWriter() throws SaxonApiException {
+ if (config == null) {
+ throw new IllegalStateException("This method is available only if a Processor has been set");
+ }
+ Receiver r = getReceiver(config);
+ r = new NamespaceReducer(r);
+ return new StreamWriterToReceiver(r);
+ }
+
+ /**
+ * Get the current output destination.
+ *
+ * @return an OutputStream, Writer, or File, depending on the previous calls to
+ * {@link #setOutputStream}, {@link #setOutputWriter}, or {@link #setOutputFile}; or
+ * null, if no output destination has been set up.
+ */
+
+ public Object getOutputDestination() {
+ if (result.getOutputStream() != null) {
+ return result.getOutputStream();
+ }
+ if (result.getWriter() != null) {
+ return result.getWriter();
+ }
+ String systemId = result.getSystemId();
+ if (systemId != null) {
+ try {
+ return new File(new URI(systemId));
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return a receiver to which Saxon will send events. This method is provided
+ * primarily for internal use, though it could also be called by user applications
+ * wanting to make use of the Saxon serializer.
+ *
+ * @param config The Saxon configuration. This is an internal implementation object
+ * held within the {@link Processor}
+ * @return a receiver to which XML events will be sent
+ */
+
+ public Receiver getReceiver(Configuration config) throws SaxonApiException {
+ try {
+ SerializerFactory sf = config.getSerializerFactory();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ Properties props = getOutputProperties();
+ Receiver target = sf.getReceiver(result, pipe, props);
+ if (target.getSystemId() == null) {
+ target.setSystemId(result.getSystemId());
+ }
+ return target;
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Return a receiver to which Saxon will send events. This method is provided
+ * primarily for internal use, though it could also be called by user applications
+ * wanting to make use of the Saxon serializer.
+ *
+ * @param executable The Saxon Executable for the transformation or query. The serialization
+ * properties defined in this Serializer are supplemented by properties that have been
+ * defined in the query or stylesheet associated with the Executable. The properties defined
+ * in this Serializer take precedence over those in the stylesheet or query.
+ * @return a receiver to which XML events will be sent
+ * @throws SaxonApiException if any failure occurs
+ */
+
+ protected Receiver getReceiver(Executable executable) throws SaxonApiException {
+ try {
+ Configuration config = executable.getConfiguration();
+ SerializerFactory sf = config.getSerializerFactory();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ pipe.setSerializing(true);
+ pipe.setHostLanguage(executable.getHostLanguage());
+ Properties baseProps = executable.getDefaultOutputProperties();
+
+ for (Map.Entry<Property, String> entry : properties.entrySet()) {
+ QName name = entry.getKey().getQName();
+ ResultDocument.setSerializationProperty(
+ baseProps, name.getNamespaceURI(), name.getLocalName(), entry.getValue(), null, true, config);
+ }
+ Receiver target = sf.getReceiver(result, pipe, baseProps, executable.getCharacterMapIndex());
+ if (target.getSystemId() == null) {
+ target.setSystemId(result.getSystemId());
+ }
+ return target;
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Create a Properties object holding the defined serialization properties. This
+ * will be in the same format as JAXP interfaces such as
+ * {@link javax.xml.transform.Transformer#getOutputProperties()}
+ *
+ * @return a newly-constructed Properties object holding the declared serialization properties
+ */
+
+ protected Properties getOutputProperties() {
+ Properties props = new Properties();
+ for (Property p : properties.keySet()) {
+ String value = properties.get(p);
+ props.setProperty(p.toString(), value);
+ }
+ return props;
+ }
+
+ /**
+ * Get the JAXP StreamResult object representing the output destination
+ * of this serializer
+ */
+
+ protected Result getResult() {
+ return result;
+ }
+
+ /**
+ * Close any resources associated with this destination. Note that this does <b>not</b>
+ * close any user-supplied OutputStream or Writer; those must be closed explicitly
+ * by the calling application.
+ */
+
+ public void close() throws SaxonApiException {
+ if (mustClose) {
+ // This relies on the fact that the SerializerFactory sets the OutputStream
+ OutputStream stream = result.getOutputStream();
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (java.io.IOException err) {
+ throw new SaxonApiException("Failed while closing output file", err);
+ }
+ }
+ Writer writer = result.getWriter(); // Path not used, but there for safety
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (java.io.IOException err) {
+ throw new SaxonApiException("Failed while closing output file", err);
+ }
+ }
+ }
+ }
+}
+
diff --git a/sf/saxon/s9api/TeeDestination.java b/sf/saxon/s9api/TeeDestination.java
new file mode 100644
index 0000000..09a41ed
--- /dev/null
+++ b/sf/saxon/s9api/TeeDestination.java
@@ -0,0 +1,65 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.TeeOutputter;
+
+/**
+ * A TeeDestination allows writing to two destinations at once. For example the output of a transformation
+ * can be written simultaneously to a Serializer and to a second Transformation. By chaining together a number
+ * of TeeDestinations it is possible to write to any number of destinations at once.
+ * @since 9.1
+ */
+
+public class TeeDestination implements Destination {
+
+ private Destination dest0;
+ private Destination dest1;
+
+ /**
+ * Create a TeeDestination: a destination which copies everything that is sent to it to two
+ * separate destinations
+ * @param destination0 the first destination
+ * @param destination1 the second destination
+ */
+
+ public TeeDestination(Destination destination0, Destination destination1) {
+ dest0 = destination0;
+ dest1 = destination1;
+ }
+
+ /**
+ * Return a Receiver. Saxon calls this method to obtain a Receiver, to which it then sends
+ * a sequence of events representing the content of an XML document.
+ * @param config The Saxon configuration. This is supplied so that the destination can
+ * use information from the configuration (for example, a reference to the name pool)
+ * to construct or configure the returned Receiver.
+ * @return the Receiver to which events are to be sent. It is the caller's responsibility to
+ * initialize this Receiver with a {@link net.sf.saxon.event.PipelineConfiguration} before calling
+ * its <code>open()</code> method.
+ * @throws net.sf.saxon.s9api.SaxonApiException
+ * if the Receiver cannot be created
+ */
+
+ /*@NotNull*/ public Receiver getReceiver(Configuration config) throws SaxonApiException {
+ return new TeeOutputter(dest0.getReceiver(config), dest1.getReceiver(config));
+ }
+
+ /**
+ * Close the destination, allowing resources to be released. Saxon calls this method when
+ * it has finished writing to the destination.
+ */
+
+ public void close() throws SaxonApiException {
+ dest0.close();
+ dest1.close();
+ }
+}
+
diff --git a/sf/saxon/s9api/ValidationMode.java b/sf/saxon/s9api/ValidationMode.java
new file mode 100644
index 0000000..f53ed25
--- /dev/null
+++ b/sf/saxon/s9api/ValidationMode.java
@@ -0,0 +1,64 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.lib.Validation;
+
+
+/**
+ * Enumeration class defining different schema validation (or construction) modes
+ */
+public enum ValidationMode {
+ /**
+ * Strict validation
+ */
+ STRICT (Validation.STRICT),
+ /**
+ * Lax validation
+ */
+ LAX (Validation.LAX),
+ /**
+ * Preserve existing type annotations if any
+ */
+ PRESERVE (Validation.PRESERVE),
+ /**
+ * Remove any existing type annotations, mark as untyped
+ */
+ STRIP (Validation.STRIP),
+ /**
+ * Value indication no preference: the choice is defined elsewhere
+ */
+ DEFAULT (Validation.DEFAULT);
+
+ private int number;
+
+ private ValidationMode(int number) {
+ this.number = number;
+ }
+
+ protected int getNumber() {
+ return number;
+ }
+
+ /*@NotNull*/ protected static ValidationMode get(int number) {
+ switch (number) {
+ case Validation.STRICT:
+ return STRICT;
+ case Validation.LAX:
+ return LAX;
+ case Validation.STRIP:
+ return STRIP;
+ case Validation.PRESERVE:
+ return PRESERVE;
+ case Validation.DEFAULT:
+ default:
+ return DEFAULT;
+ }
+ }
+}
+
diff --git a/sf/saxon/s9api/WhitespaceStrippingPolicy.java b/sf/saxon/s9api/WhitespaceStrippingPolicy.java
new file mode 100644
index 0000000..d9fd05f
--- /dev/null
+++ b/sf/saxon/s9api/WhitespaceStrippingPolicy.java
@@ -0,0 +1,77 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.event.FilterFactory;
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.Stripper;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.SpaceStrippingRule;
+import net.sf.saxon.value.Whitespace;
+
+/**
+ * WhitespaceStrippingPolicy is class defining the possible policies for handling
+ * whitespace text nodes in a source document.
+ */
+
+public class WhitespaceStrippingPolicy {
+
+ private int policy;
+ private SpaceStrippingRule stripperRules;
+
+ /**
+ * The value NONE indicates that all whitespace text nodes are retained
+ */
+ public static final WhitespaceStrippingPolicy NONE = new WhitespaceStrippingPolicy(Whitespace.NONE);
+
+ /**
+ * The value IGNORABLE indicates that whitespace text nodes in element-only content are
+ * discarded. Content is element-only if it is defined by a schema or DTD definition that
+ * does not allow mixed or PCDATA content.
+ */
+ public static final WhitespaceStrippingPolicy IGNORABLE = new WhitespaceStrippingPolicy(Whitespace.IGNORABLE);
+
+ /**
+ * The value ALL indicates that all whitespace-only text nodes are discarded.
+ */
+ public static final WhitespaceStrippingPolicy ALL = new WhitespaceStrippingPolicy(Whitespace.ALL);
+
+ /**
+ * UNSPECIFIED means that no other value has been specifically requested.
+ */
+ public static final WhitespaceStrippingPolicy UNSPECIFIED = new WhitespaceStrippingPolicy(Whitespace.UNSPECIFIED);
+
+ private WhitespaceStrippingPolicy(int policy) {
+ this.policy = policy;
+ }
+
+ /**
+ * Create a WhitespaceStrippingPolicy based on the xsl:strip-space and xsl:preserve-space declarations
+ * in a given XSLT stylesheet
+ * @param executable the stylesheet containing the xsl:strip-space and xsl:preserve-space declarations
+ */
+
+ protected WhitespaceStrippingPolicy (Executable executable) {
+ policy = Whitespace.XSLT;
+ stripperRules = executable.getStripperRules();
+ }
+
+ protected int ordinal() {
+ return policy;
+ }
+
+ /*@NotNull*/ protected FilterFactory makeStripper() {
+ return new FilterFactory() {
+ public ProxyReceiver makeFilter(Receiver next) {
+ return new Stripper(stripperRules, next);
+ }
+ };
+ }
+}
+
diff --git a/sf/saxon/s9api/XPathCompiler.java b/sf/saxon/s9api/XPathCompiler.java
new file mode 100644
index 0000000..67bd208
--- /dev/null
+++ b/sf/saxon/s9api/XPathCompiler.java
@@ -0,0 +1,647 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.sort.RuleBasedSubstringMatcher;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.sxpath.*;
+import net.sf.saxon.trans.DecimalFormatManager;
+import net.sf.saxon.trans.DecimalSymbols;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ValidationException;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.SequenceType;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.Collator;
+import java.text.RuleBasedCollator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * An XPathCompiler object allows XPath queries to be compiled. The compiler holds information that
+ * represents the static context for an XPath expression.
+ * <p/>
+ * <p>To construct an XPathCompiler, use the factory method
+ * {@link Processor#newXPathCompiler}.</p>
+ * <p/>
+ * <p>An XPathCompiler may be used repeatedly to compile multiple
+ * queries. Any changes made to the XPathCompiler (that is, to the
+ * static context) do not affect queries that have already been compiled.
+ * An XPathCompiler may be used concurrently in multiple threads, but
+ * it should not then be modified once initialized.</p>
+ * <p/>
+ * <p>Changes to an XPathCompiler are cumulative. There is no simple way to reset
+ * the XPathCompiler to its initial state; instead, simply create a new
+ * XPathCompiler.</p>
+ * <p/>
+ * <p>The <code>XPathCompiler</code> has the ability to maintain a cache of compiled
+ * expressions. This is active only if enabled by calling {@link #setCaching(boolean)}.
+ * If caching is enabled, then the compiler will recognize an attempt to compile
+ * the same expression twice, and will avoid the cost of recompiling it. The cache
+ * is emptied by any method that changes the static context for subsequent expressions,
+ * for example, {@link #setBaseURI(java.net.URI)}. Unless the cache is emptied,
+ * it grows indefinitely: compiled expressions are never discarded.</p>
+ *
+ * @since 9.0
+ */
+
+public class XPathCompiler {
+
+ private Processor processor;
+ private IndependentContext env;
+ private ItemType requiredContextItemType;
+
+ /*@Nullable*/ private Map<String, XPathExecutable> cache = null;
+ //private Map<QName, DecimalSymbols> symbolsMap = new HashMap<QName, DecimalSymbols>();
+
+ /**
+ * Protected constructor
+ *
+ * @param processor the s9api Processor
+ */
+
+ protected XPathCompiler(Processor processor) {
+ this.processor = processor;
+ env = new IndependentContext(processor.getUnderlyingConfiguration());
+ }
+
+ /**
+ * Get the Processor from which this XPathCompiler was constructed
+ *
+ * @return the Processor to which this XPathCompiler belongs
+ * @since 9.3
+ */
+
+ public Processor getProcessor() {
+ return processor;
+ }
+
+ /**
+ * Set whether XPath 1.0 backwards compatibility mode is to be used. In backwards compatibility
+ * mode, more implicit type conversions are allowed in XPath expressions, for example it
+ * is possible to compare a number with a string. The default is false (backwards compatibility
+ * mode is off).
+ *
+ * @param option true if XPath 1.0 backwards compatibility is to be enabled, false if it is to
+ * be disabled.
+ */
+
+ public void setBackwardsCompatible(boolean option) {
+ if (cache != null) {
+ cache.clear();
+ }
+ env.setBackwardsCompatibilityMode(option);
+ }
+
+
+ /**
+ * Ask whether XPath 1.0 backwards compatibility mode is in force.
+ *
+ * @return true if XPath 1.0 backwards compatibility is enabled, false if it is disabled.
+ */
+
+ public boolean isBackwardsCompatible() {
+ return env.isInBackwardsCompatibleMode();
+ }
+
+ /**
+ * Say whether XPath expressions compiled using this XPathCompiler are
+ * schema-aware. They will automatically be schema-aware if the method
+ * {@link #importSchemaNamespace(String)} is called. An XPath expression
+ * must be marked as schema-aware if it is to handle typed (validated)
+ * input documents.
+ *
+ * @param schemaAware true if expressions are to be schema-aware, false otherwise
+ * @since 9.3
+ */
+
+ public void setSchemaAware(boolean schemaAware) {
+ env.setSchemaAware(schemaAware);
+ }
+
+ /**
+ * Ask whether XPath expressions compiled using this XPathCompiler are
+ * schema-aware. They will automatically be schema-aware if the method
+ * {@link #importSchemaNamespace(String)} is called. An XPath expression
+ * must be marked as schema-aware if it is to handle typed (validated)
+ * input documents.
+ *
+ * @return true if expressions are to be schema-aware, false otherwise
+ * @since 9.3
+ */
+
+ public boolean isSchemaAware() {
+ return env.isSchemaAware();
+ }
+
+ /**
+ * Say whether an XPath 2.0 or XPath 3.0 processor is required.
+ *
+ * @param version Must be numerically equal to 1.0, 2.0 or 3.0. Currently
+ * support for XPath 3.0 is incomplete: check the release notes.
+ * <p>Setting the option to 1.0 requests an XPath 2.0 processor running in 1.0 compatibility mode;
+ * this is equivalent to setting the language version to 2.0 and backwards compatibility mode to true.</p>
+ * @throws IllegalArgumentException if the version is not numerically equal to 1.0, 2.0 or 3.0.
+ * @since 9.3
+ */
+
+ public void setLanguageVersion(String version) {
+ if (cache != null) {
+ cache.clear();
+ }
+ DecimalValue dv;
+ try {
+ dv = (DecimalValue) DecimalValue.makeDecimalValue(version, true).asAtomic();
+ } catch (ValidationException err) {
+ throw new IllegalArgumentException("Language version must be in the form of a decimal number");
+ }
+ if (DecimalValue.ONE.equals(dv)) {
+ dv = DecimalValue.TWO;
+ env.setBackwardsCompatibilityMode(true);
+ }
+ if (!DecimalValue.ONE.equals(dv) && !DecimalValue.TWO.equals(dv) && !DecimalValue.THREE.equals(dv)) {
+ throw new IllegalArgumentException("Unknown XPath language version " + version);
+ }
+ env.setXPathLanguageLevel(dv);
+ }
+
+ /**
+ * Ask whether an XPath 2.0 or XPath 3.0 processor is being used
+ *
+ * @return version: "2.0" or "3.0"
+ * @since 9.3
+ */
+
+ public String getLanguageVersion() {
+ return (env.getXPathLanguageLevel().equals(DecimalValue.TWO) ? "2.0" : "3.0");
+ }
+
+ /**
+ * Set the static base URI for XPath expressions compiled using this XPathCompiler. The base URI
+ * is part of the static context, and is used to resolve any relative URIs appearing within an XPath
+ * expression, for example a relative URI passed as an argument to the doc() function. If no
+ * static base URI is supplied, then the current working directory is used.
+ *
+ * @param uri the base URI to be set in the static context. This must be an absolute URI, or null
+ * to indicate that no static base URI is available.
+ */
+
+ public void setBaseURI(URI uri) {
+ if (cache != null) {
+ cache.clear();
+ }
+ if (uri == null) {
+ env.setBaseURI(null);
+ } else {
+ if (!uri.isAbsolute()) {
+ throw new IllegalArgumentException("Supplied base URI must be absolute");
+ }
+ env.setBaseURI(uri.toString());
+ }
+ }
+
+ /**
+ * Get the static base URI for XPath expressions compiled using this XPathCompiler. The base URI
+ * is part of the static context, and is used to resolve any relative URIs appearing within an XPath
+ * expression, for example a relative URI passed as an argument to the doc() function. If no
+ * static base URI has been explicitly set, this method returns null.
+ *
+ * @return the base URI from the static context
+ */
+
+ public URI getBaseURI() {
+ try {
+ return new URI(env.getBaseURI());
+ } catch (URISyntaxException err) {
+ throw new IllegalStateException(err);
+ }
+ }
+
+ /**
+ * Declare a namespace binding as part of the static context for XPath expressions compiled using this
+ * XPathCompiler
+ *
+ * @param prefix The namespace prefix. If the value is a zero-length string, this method sets the default
+ * namespace for elements and types.
+ * @param uri The namespace URI. It is possible to specify a zero-length string to "undeclare" a namespace;
+ * in this case the prefix will not be available for use, except in the case where the prefix
+ * is also a zero length string, in which case the absence of a prefix implies that the name
+ * is in no namespace.
+ * @throws NullPointerException if either the prefix or uri is null.
+ */
+
+ public void declareNamespace(String prefix, String uri) {
+ if (cache != null) {
+ cache.clear();
+ }
+ env.declareNamespace(prefix, uri);
+ }
+
+ /**
+ * Import a schema namespace: that is, add the element and attribute declarations and type definitions
+ * contained in a given namespace to the static context for the XPath expression.
+ * <p/>
+ * <p>This method will not cause the schema to be loaded. That must be done separately, using the
+ * {@link SchemaManager}. This method will not fail if the schema has not been loaded (but in that case
+ * the set of declarations and definitions made available to the XPath expression is empty). The schema
+ * document for the specified namespace may be loaded before or after this method is called.</p>
+ * <p/>
+ * <p>This method does not bind a prefix to the namespace. That must be done separately, using the
+ * {@link #declareNamespace(String, String)} method.</p>
+ *
+ * @param uri The schema namespace to be imported. To import declarations in a no-namespace schema,
+ * supply a zero-length string.
+ * @since 9.1
+ */
+
+ public void importSchemaNamespace(String uri) {
+ if (cache != null) {
+ cache.clear();
+ }
+ env.getImportedSchemaNamespaces().add(uri);
+ env.setSchemaAware(true);
+ }
+
+ /**
+ * Say whether undeclared variables are allowed. By default, they are not allowed. When
+ * undeclared variables are allowed, it is not necessary to predeclare the variables that
+ * may be used in the XPath expression; instead, a variable is automatically declared when a reference
+ * to the variable is encountered within the expression.
+ *
+ * @param allow true if undeclared variables are allowed, false if they are not allowed.
+ * @since 9.2
+ */
+
+ public void setAllowUndeclaredVariables(boolean allow) {
+ if (cache != null) {
+ cache.clear();
+ }
+ env.setAllowUndeclaredVariables(allow);
+ }
+
+ /**
+ * Ask whether undeclared variables are allowed. By default, they are not allowed. When
+ * undeclared variables are allowed, it is not necessary to predeclare the variables that
+ * may be used in the XPath expression; instead, a variable is automatically declared when a reference
+ * to the variable is encountered within the expression.
+ *
+ * @return true if undeclared variables are allowed, false if they are not allowed.
+ * @since 9.2
+ */
+
+ public boolean isAllowUndeclaredVariables() {
+ return env.isAllowUndeclaredVariables();
+ }
+
+ /**
+ * Declare a variable as part of the static context for XPath expressions compiled using this
+ * XPathCompiler. It is an error for the XPath expression to refer to a variable unless it has been
+ * declared. This method declares the existence of the variable, but it does not
+ * bind any value to the variable; that is done later, when the XPath expression is evaluated.
+ * The variable is allowed to have any type (that is, the required type is <code>item()*</code>).
+ *
+ * @param qname The name of the variable, expressions as a QName
+ */
+
+ public void declareVariable(QName qname) {
+ if (cache != null) {
+ cache.clear();
+ }
+ env.declareVariable(qname.getNamespaceURI(), qname.getLocalName());
+ //declaredVariables.add(var);
+ }
+
+ /**
+ * Declare a variable as part of the static context for XPath expressions compiled using this
+ * XPathCompiler. It is an error for the XPath expression to refer to a variable unless it has been
+ * declared. This method declares the existence of the variable, and defines the required type
+ * of the variable, but it does not bind any value to the variable; that is done later,
+ * when the XPath expression is evaluated.
+ *
+ * @param qname The name of the variable, expressed as a QName
+ * @param itemType The required item type of the value of the variable
+ * @param occurrences The allowed number of items in the sequence forming the value of the variable
+ * @throws SaxonApiException if the requiredType is syntactically invalid or if it refers to namespace
+ * prefixes or schema components that are not present in the static context
+ */
+
+ public void declareVariable(QName qname, ItemType itemType, OccurrenceIndicator occurrences) throws SaxonApiException {
+ if (cache != null) {
+ cache.clear();
+ }
+ XPathVariable var = env.declareVariable(qname.getNamespaceURI(), qname.getLocalName());
+ var.setRequiredType(
+ SequenceType.makeSequenceType(
+ itemType.getUnderlyingItemType(), occurrences.getCardinality()));
+ }
+
+ /**
+ * Declare the static type of the context item. If this type is declared, and if a context item
+ * is supplied when the query is invoked, then the context item must conform to this type (no
+ * type conversion will take place to force it into this type).
+ *
+ * @param type the required type of the context item
+ * @since 9.3
+ */
+
+ public void setRequiredContextItemType(ItemType type) {
+ requiredContextItemType = type;
+ env.setRequiredContextItemType(type.getUnderlyingItemType());
+ }
+
+ /**
+ * Get the required type of the context item. If no type has been explicitly declared for the context
+ * item, an instance of AnyItemType (representing the type item()) is returned.
+ *
+ * @return the required type of the context item
+ * @since 9.3
+ */
+
+ public ItemType getRequiredContextItemType() {
+ return requiredContextItemType;
+ }
+
+ /**
+ * Bind a collation URI to a collation
+ *
+ * @param uri the absolute collation URI
+ * @param collation a {@link Collator} object that implements the required collation
+ * @throws IllegalArgumentException if an attempt is made to rebind the standard URI
+ * for the Unicode codepoint collation
+ * @since 9.4
+ */
+
+ public void declareCollation(String uri, final java.text.Collator collation) {
+ if (uri.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
+ throw new IllegalArgumentException("Cannot declare the Unicode codepoint collation URI");
+ }
+ StringCollator saxonCollation;
+ if (collation instanceof RuleBasedCollator) {
+ saxonCollation = new RuleBasedSubstringMatcher((RuleBasedCollator) collation);
+ } else {
+ saxonCollation = new SimpleCollation(collation);
+ }
+ env.getCollationMap().setNamedCollation(uri, saxonCollation);
+ }
+
+ /**
+ * Declare the default collation
+ *
+ * @param uri the absolute URI of the default collation. This URI must have been bound to a collation
+ * using the method {@link #declareCollation(String, Collator)}
+ * @throws IllegalStateException if the collation URI has not been registered, unless it is the standard
+ * Unicode codepoint collation which is registered implicitly
+ * @since 9.4
+ */
+
+ public void declareDefaultCollation(String uri) {
+ if (env.getCollationMap().getNamedCollation(uri) == null) {
+ throw new IllegalStateException("Unknown collation " + uri);
+ }
+ env.getCollationMap().setDefaultCollationName(uri);
+ }
+
+
+ /**
+ * Say whether the compiler should maintain a cache of compiled expressions.
+ *
+ * @param caching if set to true, caching of compiled expressions is enabled.
+ * If set to false, any existing cache is cleared, and future compiled expressions
+ * will not be cached until caching is re-enabled. The cache is also cleared
+ * (but without disabling future caching)
+ * if any method is called that changes the static context for compiling
+ * expressions, for example {@link #declareVariable(QName)} or
+ * {@link #declareNamespace(String, String)}.
+ * @since 9.3
+ */
+
+ public void setCaching(boolean caching) {
+ if (caching) {
+ if (cache == null) {
+ cache = new ConcurrentHashMap<String, XPathExecutable>();
+ }
+ } else {
+ cache = null;
+ }
+ }
+
+ /**
+ * Ask whether the compiler is maintaining a cache of compiled expressions
+ *
+ * @return true if a cache is being maintained
+ * @since 9.3
+ */
+
+ public boolean isCaching() {
+ return cache != null;
+ }
+
+ /**
+ * Compile an XPath expression, supplied as a character string.
+ *
+ * @param source A string containing the source text of the XPath expression
+ * @return An XPathExecutable which represents the compiled xpath expression object.
+ * The XPathExecutable may be run as many times as required, in the same or a different thread.
+ * The XPathExecutable is not affected by any changes made to the XPathCompiler once it has been compiled.
+ * @throws SaxonApiException if any static error is detected while analyzing the expression
+ */
+
+ public XPathExecutable compile(String source) throws SaxonApiException {
+ if (cache != null) {
+ synchronized (this) {
+ XPathExecutable expr = cache.get(source);
+ if (expr == null) {
+ expr = internalCompile(source);
+ cache.put(source, expr);
+ }
+ return expr;
+ }
+ } else {
+ return internalCompile(source);
+ }
+ }
+
+ private XPathExecutable internalCompile(String source) throws SaxonApiException {
+ try {
+ env.getDecimalFormatManager().checkConsistency();
+ } catch (net.sf.saxon.trans.XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ IndependentContext ic = env;
+ if (ic.isAllowUndeclaredVariables()) {
+ // self-declaring variables modify the static context. The XPathCompiler must not change state
+ // as the result of compiling an expression, so we need to copy the static context.
+ ic = new DedicatedStaticContext(env);
+ for (Iterator iter = env.iterateExternalVariables(); iter.hasNext(); ) {
+ XPathVariable var = (XPathVariable) iter.next();
+ XPathVariable var2 = ic.declareVariable(var.getVariableQName());
+ var2.setRequiredType(var.getRequiredType());
+ }
+ }
+ try {
+ XPathEvaluator eval = new XPathEvaluator(processor.getUnderlyingConfiguration());
+ eval.setStaticContext(ic);
+ XPathExpression cexp = eval.createExpression(source);
+ cexp.getInternalExpression().getExecutable().setCollationMap(env.getCollationMap());
+ return new XPathExecutable(cexp, processor, ic);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Compile and evaluate an XPath expression, supplied as a character string, with a given
+ * context item.
+ *
+ * @param expression A string containing the source text of the XPath expression
+ * @param contextItem The context item to be used for evaluating the expression. This
+ * may be null if the expression is to be evaluated with no context item.
+ * @return the result of evaluating the XPath expression with this context item. Note that
+ * the result is an iterable, so that it can be used in a construct such as
+ * <code>for (XdmItem item : xpath.evaluate("//x", doc) {...}</code>
+ * @throws SaxonApiException if any static error is detected while analyzing the expression,
+ * or if any dynamic error is detected while evaluating it.
+ * @since 9.3
+ */
+
+ public XdmValue evaluate(String expression, /*@Nullable*/ XdmItem contextItem) throws SaxonApiException {
+ XPathSelector selector = compile(expression).load();
+ if (contextItem != null) {
+ selector.setContextItem(contextItem);
+ }
+ return selector.evaluate();
+ }
+
+ /**
+ * Compile and evaluate an XPath expression whose result is expected to be
+ * a single item, with a given context item. The expression is supplied as
+ * a character string.
+ *
+ * @param expression A string containing the source text of the XPath expression
+ * @param contextItem The context item to be used for evaluating the expression. This
+ * may be null if the expression is to be evaluated with no context item.
+ * @return the result of evaluating the XPath expression with this context item.
+ * If the result is a singleton it is returned as an XdmItem; if it is an empty
+ * sequence, the return value is null. If the expression returns a sequence of more than one item,
+ * any items after the first are ignored.
+ * @throws SaxonApiException if any static error is detected while analyzing the expression,
+ * or if any dynamic error is detected while evaluating it.
+ * @since 9.3
+ */
+
+ public XdmItem evaluateSingle(String expression, /*@Nullable*/ XdmItem contextItem) throws SaxonApiException {
+ XPathSelector selector = compile(expression).load();
+ if (contextItem != null) {
+ selector.setContextItem(contextItem);
+ }
+ return selector.evaluateSingle();
+ }
+
+ /**
+ * Compile an XSLT 2.0 pattern, supplied as a character string. The compiled pattern behaves as a boolean
+ * expression which, when evaluated in a particular context, returns true if the context node matches
+ * the pattern, and false if it does not. An error is reported if there is no context item or it the context
+ * item is not a node.
+ *
+ * @param source A string conforming to the syntax of XSLT 2.0 patterns
+ * @return An XPathExecutable representing an expression which evaluates to true when the context node matches
+ * the pattern, and false when it does not.
+ * @throws SaxonApiException if the pattern contains static errors: for example, if its syntax is incorrect,
+ * or if it refers to undeclared variables or namespaces
+ * @since 9.1
+ */
+
+ public XPathExecutable compilePattern(String source) throws SaxonApiException {
+ try {
+ env.getDecimalFormatManager().checkConsistency();
+ } catch (net.sf.saxon.trans.XPathException e) {
+ throw new SaxonApiException(e);
+ }
+
+ try {
+ XPathEvaluator eval = new XPathEvaluator(processor.getUnderlyingConfiguration());
+ eval.setStaticContext(env);
+ XPathExpression cexp = eval.createPattern(source);
+ return new XPathExecutable(cexp, processor, env);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Registers the required decimal format properties
+ *
+ * @param format The name of the decimal format
+ * @param property The decimal symbols name to update
+ * @param value The value to update the decimal symbol property
+ * @throws SaxonApiException if there are two conflicting definitions of the named decimal-format
+ * @since 9.4
+ */
+ public void setDecimalFormatProperty(QName format, String property, String value) throws SaxonApiException {
+ DecimalFormatManager dfm = env.getDecimalFormatManager();
+ if (dfm == null) {
+ dfm = new DecimalFormatManager();
+ env.setDecimalFormatManager(dfm);
+ }
+
+ DecimalSymbols symbols = dfm.getNamedDecimalFormat(format.getStructuredQName());
+
+ try {
+ if (property.equals("decimal-separator")) {
+ symbols.setDecimalSeparator((value));
+ } else if (property.equals("grouping-separator")) {
+ symbols.setGroupingSeparator((value));
+ } else if (property.equals("infinity")) {
+ symbols.setInfinity(value);
+ } else if (property.equals("NaN")) {
+ symbols.setNaN(value);
+ } else if (property.equals("minus-sign")) {
+ symbols.setMinusSign((value));
+ } else if (property.equals("percent")) {
+ symbols.setPercent((value));
+ } else if (property.equals("per-mille")) {
+ symbols.setPerMille((value));
+ } else if (property.equals("zero-digit")) {
+ symbols.setZeroDigit((value));
+ } else if (property.equals("digit")) {
+ symbols.setDigit((value));
+ } else if (property.equals("pattern-separator")) {
+ symbols.setPatternSeparator((value));
+ } else {
+ throw new IllegalArgumentException("**** Unknown decimal format attribute " + property);
+ }
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+
+ }
+
+
+ /**
+ * Escape-hatch method to get the underlying static context object used by the implementation.
+ *
+ * @return the underlying static context object. In the current implementation this will always
+ * be an instance of {@link IndependentContext}.
+ * <p/>
+ * <p>This method provides an escape hatch to internal Saxon implementation objects that offer a finer and
+ * lower-level degree of control than the s9api classes and methods. Some of these classes and methods may change
+ * from release to release.</p>
+ * @since 9.1
+ */
+
+ public StaticContext getUnderlyingStaticContext() {
+ return env;
+ }
+}
+
diff --git a/sf/saxon/s9api/XPathExecutable.java b/sf/saxon/s9api/XPathExecutable.java
new file mode 100644
index 0000000..a83fc4f
--- /dev/null
+++ b/sf/saxon/s9api/XPathExecutable.java
@@ -0,0 +1,182 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.sxpath.IndependentContext;
+import net.sf.saxon.sxpath.XPathExpression;
+import net.sf.saxon.sxpath.XPathVariable;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * An XPathExecutable represents the compiled form of an XPath expression.
+ * To evaluate the expression, it must first be loaded to form an {@link XPathSelector}.
+ *
+ * <p>An XPathExecutable is immutable, and therefore thread-safe. It is simplest to load
+ * a new XPathSelector each time the expression is to be evaluated. However, the XPathSelector
+ * is serially reusable within a single thread.</p>
+ *
+ * <p>An XPathExecutable is created by using the {@link XPathCompiler#compile} method
+ * on the {@link XPathCompiler} class.</p>
+ */
+
+public class XPathExecutable {
+
+ private XPathExpression exp;
+ private Processor processor;
+ private IndependentContext env;
+
+ // protected constructor
+
+ protected XPathExecutable(XPathExpression exp, Processor processor, IndependentContext env) {
+ this.exp = exp;
+ this.processor = processor;
+ this.env = env;
+ //this.declaredVariables = declaredVariables;
+ }
+
+ /**
+ * Load the compiled XPath expression to prepare it for execution.
+ * @return An XPathSelector. The returned XPathSelector can be used to set up the
+ * dynamic context, and then to evaluate the expression.
+ */
+
+ public XPathSelector load() {
+ ArrayList<XPathVariable> declaredVariables = new ArrayList<XPathVariable>();
+ for (Iterator iter=env.iterateExternalVariables(); iter.hasNext();) {
+ XPathVariable var = (XPathVariable)iter.next();
+ declaredVariables.add(var);
+ }
+ return new XPathSelector(exp, declaredVariables);
+ }
+
+ /**
+ * Get the ItemType of the items in the result of the expression, as determined by static analysis. This
+ * is the most precise ItemType that the processor is able to determine from static examination of the
+ * expression; the actual items in the expression result are guaranteed to belong to this ItemType or to a subtype
+ * of this ItemType.
+ * @return the statically-determined ItemType of the result of the expression
+ * @since 9.1
+ */
+
+ public ItemType getResultItemType() {
+ net.sf.saxon.type.ItemType it =
+ exp.getInternalExpression().getItemType(processor.getUnderlyingConfiguration().getTypeHierarchy());
+ return new ConstructedItemType(it, processor);
+ }
+
+ /**
+ * Get the statically-determined cardinality of the result of the expression. This is the most precise cardinality
+ * that the processor is able to determine from static examination of the expression.
+ * @return the statically-determined cardinality of the result of the expression
+ * @since 9.1
+ */
+
+ public OccurrenceIndicator getResultCardinality() {
+ int card = exp.getInternalExpression().getCardinality();
+ return OccurrenceIndicator.getOccurrenceIndicator(card);
+ }
+
+ /**
+ * Get an iterator over the names of all the external variables. This includes both variables that have
+ * been explicitly declared using a call to <tt>declareVariable()</tt>, and variables that are implicitly
+ * declared by reference in the case where the <tt>allowUndeclaredVariables</tt> option is set. It does
+ * not include range variables bound in a <tt>for</tt>, <tt>some</tt>, or <tt>every</tt> expression.
+ *
+ * <p>If the <tt>allowUndeclaredVariables</tt> option is set, this method allows discovery of the variable
+ * references that appear in the compiled expression.</p>
+ * @return an iterator over the names of the external variables defined in the XPath expression
+ * @since 9.2
+ */
+
+ public Iterator<QName> iterateExternalVariables() {
+ final Iterator varIterator = env.iterateExternalVariables();
+ return new Iterator<QName>() {
+ public boolean hasNext() {
+ return varIterator.hasNext();
+ }
+ public QName next() {
+ return new QName(((XPathVariable)varIterator.next()).getVariableQName());
+ }
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+ };
+ }
+
+ /**
+ * Get the required item type of a declared variable in the static context of the expression.
+ * @param variableName the name of a declared variable
+ * @return the required item type.
+ * <p>If the variable was explicitly declared, this will be the item type that was set when the
+ * variable was declared. If no item type was set, it will be {@link ItemType#ANY_ITEM}.</p>
+ * <p>If the variable was implicitly declared by reference (which can happen only when the
+ * <tt>allowUndeclaredVariables</tt> option is set), the returned type will be {@link ItemType#ANY_ITEM}.</p>
+ * <p>If no variable with the specified QName has been declared either explicitly or implicitly,
+ * the method returns null.</p>
+ * @since 9.2
+ */
+
+ /*@Nullable*/ public ItemType getRequiredItemTypeForVariable(QName variableName) {
+ XPathVariable var = env.getExternalVariable(variableName.getStructuredQName());
+ if (var == null) {
+ return null;
+ } else {
+ return new ConstructedItemType(var.getRequiredType().getPrimaryType(), processor);
+ }
+ }
+
+ /**
+ * Get the required cardinality of a declared variable in the static context of the expression.
+ * @param variableName the name of a declared variable
+ * @return the required cardinality.
+ * <p>If the variable was explicitly declared, this will be the occurrence indicator that was set when the
+ * variable was declared. If no item type was set, it will be {@link OccurrenceIndicator#ZERO_OR_MORE}.</p>
+ * <p>If the variable was implicitly declared by reference (which can happen only when the
+ * <tt>allowUndeclaredVariables</tt> option is set), the returned type will be
+ * {@link OccurrenceIndicator#ZERO_OR_MORE}.</p>
+ * <p>If no variable with the specified QName has been declared either explicitly or implicitly,
+ * the method returns null.</p>
+ * @since 9.2
+ */
+
+ public OccurrenceIndicator getRequiredCardinalityForVariable(QName variableName) {
+ XPathVariable var = env.getExternalVariable(variableName.getStructuredQName());
+ if (var == null) {
+ return null;
+ } else {
+ return OccurrenceIndicator.getOccurrenceIndicator(var.getRequiredType().getCardinality());
+ }
+ }
+
+ /**
+ * Get the underlying implementation object representing the compiled XPath expression.
+ * This method provides access to lower-level Saxon classes and methods which may be subject to change
+ * from one release to the next.
+ * @return the underlying compiled XPath expression.
+ */
+
+ public XPathExpression getUnderlyingExpression() {
+ return exp;
+ }
+
+ /**
+ * Get the underlying implementation object representing the static context of the compiled
+ * XPath expression. This method provides access to lower-level Saxon classes and methods which may be
+ * subject to change from one release to the next.
+ * @return the underlying static context.
+ */
+
+ public StaticContext getUnderlyingStaticContext() {
+ return env;
+ }
+
+}
+
diff --git a/sf/saxon/s9api/XPathSelector.java b/sf/saxon/s9api/XPathSelector.java
new file mode 100644
index 0000000..506ac0b
--- /dev/null
+++ b/sf/saxon/s9api/XPathSelector.java
@@ -0,0 +1,247 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.sxpath.XPathDynamicContext;
+import net.sf.saxon.sxpath.XPathExpression;
+import net.sf.saxon.sxpath.XPathVariable;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceExtent;
+
+import javax.xml.transform.URIResolver;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * An XPathSelector represents a compiled and loaded XPath expression ready for execution.
+ * The XPathSelector holds details of the dynamic evaluation context for the XPath expression.
+ */
+ at SuppressWarnings({"ForeachStatement"})
+public class XPathSelector implements Iterable<XdmItem> {
+
+ private XPathExpression exp;
+ private XPathDynamicContext dynamicContext;
+ private List<XPathVariable> declaredVariables;
+
+ // protected constructor
+
+ protected XPathSelector(XPathExpression exp,
+ ArrayList<XPathVariable> declaredVariables) {
+ this.exp = exp;
+ this.declaredVariables = declaredVariables;
+ dynamicContext = exp.createDynamicContext();
+ }
+
+ /**
+ * Set the context item for evaluating the XPath expression.
+ * This may be either a node or an atomic value. Most commonly it will be a document node,
+ * which might be constructed using the {@link DocumentBuilder#build} method.
+ *
+ * @param item The context item for evaluating the expression. Must not be null.
+ * @throws SaxonApiException if an error occurs, for example because the type
+ * of item supplied does not match the required item type
+ *
+ */
+
+ public void setContextItem(XdmItem item) throws SaxonApiException {
+ if (item == null) {
+ throw new NullPointerException("contextItem");
+ }
+ try {
+ dynamicContext.setContextItem((Item)item.getUnderlyingValue());
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get the context item used for evaluating the XPath expression.
+ * This may be either a node or an atomic value. Most commonly it will be a document node,
+ * which might be constructed using the Build method of the DocumentBuilder object.
+ *
+ * @return The context item for evaluating the expression, or null if no context item
+ * has been set.
+ */
+
+ public XdmItem getContextItem() {
+ return XdmItem.wrapItem(dynamicContext.getContextItem());
+ }
+
+ /**
+ * Set the value of a variable
+ *
+ * @param name The name of the variable. This must match the name of a variable
+ * that was declared to the XPathCompiler. No error occurs if the expression does not
+ * actually reference a variable with this name.
+ * @param value The value to be given to the variable.
+ * @throws SaxonApiException if the variable has not been declared or if the type of the value
+ * supplied does not conform to the required type that was specified when the variable was declared
+ */
+
+ public void setVariable(QName name, XdmValue value) throws SaxonApiException {
+ XPathVariable var = null;
+ StructuredQName qn = name.getStructuredQName();
+ for (XPathVariable v : declaredVariables) {
+ if (v.getVariableQName().equals(qn)) {
+ var = v;
+ break;
+ }
+ }
+ if (var == null) {
+ throw new SaxonApiException(
+ new XPathException("Variable has not been declared: " + name));
+ }
+ try {
+ dynamicContext.setVariable(var, value.getUnderlyingValue());
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Set an object that will be used to resolve URIs used in
+ * fn:doc() and related functions.
+ *
+ * @param resolver An object that implements the URIResolver interface, or null.
+ * @since 9.4
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ dynamicContext.setURIResolver(resolver);
+ }
+
+ /**
+ * Get the URI resolver.
+ *
+ * @return the user-supplied URI resolver if there is one, or the
+ * system-defined one otherwise
+ * @since 9.4
+ */
+
+ public URIResolver getURIResolver() {
+ return dynamicContext.getURIResolver();
+ }
+
+ /**
+ * Evaluate the expression, returning the result as an <code>XdmValue</code> (that is,
+ * a sequence of nodes and/or atomic values).
+ *
+ * <p>Note: Although a singleton result <i>may</i> be represented as an <code>XdmItem</code>, there is
+ * no guarantee that this will always be the case. If you know that the expression will return at
+ * most one node or atomic value, it is best to use the <code>evaluateSingle</code> method, which
+ * does guarantee that an <code>XdmItem</code> (or null) will be returned.</p>
+ *
+ * @return An <code>XdmValue</code> representing the results of the expression.
+ * @throws SaxonApiException if a dynamic error occurs during the expression evaluation.
+ */
+
+ public XdmValue evaluate() throws SaxonApiException {
+ Sequence value;
+ try {
+ value = SequenceExtent.makeSequenceExtent(exp.iterate(dynamicContext));
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ return XdmValue.wrap(value);
+ }
+
+ /**
+ * Evaluate the XPath expression, returning the result as an <code>XdmItem</code> (that is,
+ * a single node or atomic value).
+ *
+ * @return an <code>XdmItem</code> representing the result of the expression, or null if the expression
+ * returns an empty sequence. If the expression returns a sequence of more than one item,
+ * any items after the first are ignored.
+ * @throws SaxonApiException if a dynamic error occurs during the expression evaluation.
+ */
+
+
+ public XdmItem evaluateSingle() throws SaxonApiException {
+ try {
+ Item i = exp.evaluateSingle(dynamicContext);
+ if (i == null) {
+ return null;
+ }
+ return (XdmItem) XdmValue.wrap(i);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Evaluate the expression, returning the result as an <code>Iterator</code> (that is,
+ * an iterator over a sequence of nodes and/or atomic values).
+ *
+ * <p>Because an <code>XPathSelector</code> is an {@link Iterable}, it is possible to
+ * iterate over the result using a Java 5 "for each" expression, for example:</p>
+ *
+ * <p><pre>
+ * XPathCompiler compiler = processor.newXPathCompiler();
+ * XPathSelector seq = compiler.compile("1 to 20").load();
+ * for (XdmItem item : seq) {
+ * System.err.println(item);
+ * }
+ * </pre></p>
+ *
+ * @return An iterator over the sequence that represents the results of the expression.
+ * Each object in this sequence will be an instance of <code>XdmItem</code>. Note
+ * that the expression may be evaluated lazily, which means that a successful response
+ * from this method does not imply that the expression has executed successfully: failures
+ * may be reported later while retrieving items from the iterator.
+ * @throws SaxonApiUncheckedException
+ * if a dynamic error occurs during XPath evaluation that
+ * can be detected at this point. It is also possible that an SaxonApiUncheckedException will
+ * be thrown by the <code>hasNext()</code> method of the returned iterator.
+ */
+
+ public Iterator<XdmItem> iterator() throws SaxonApiUncheckedException {
+ try {
+ return new XdmSequenceIterator(exp.iterate(dynamicContext));
+ } catch (XPathException e) {
+ throw new SaxonApiUncheckedException(e);
+ }
+ }
+
+ /**
+ * Evaluate the XPath expression, returning the effective boolean value of the result.
+ *
+ * @return a <code>boolean</code> representing the effective boolean value of the result of evaluating
+ * the expression, as defined by the rules for the fn:boolean() function.
+ * @throws SaxonApiException if a dynamic error occurs during the expression evaluation, or if the result
+ * of the expression is a value whose effective boolean value is not defined (for example, a date or a
+ * sequence of three integers)
+ * @since 9.1
+ */
+
+
+ public boolean effectiveBooleanValue() throws SaxonApiException {
+ try {
+ return exp.effectiveBooleanValue(dynamicContext);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get the underlying dynamic context object. This provides an escape hatch to the underlying
+ * implementation classes, which contain methods that may change from one release to another.
+ *
+ * @return the underlying object representing the dynamic context for query execution
+ */
+
+ public XPathDynamicContext getUnderlyingXPathContext() {
+ return dynamicContext;
+ }
+
+
+}
+
diff --git a/sf/saxon/s9api/XQueryCompiler.java b/sf/saxon/s9api/XQueryCompiler.java
new file mode 100644
index 0000000..2ba6ace
--- /dev/null
+++ b/sf/saxon/s9api/XQueryCompiler.java
@@ -0,0 +1,585 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.expr.sort.RuleBasedSubstringMatcher;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.lib.ModuleURIResolver;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.query.StaticQueryContext;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ValidationException;
+import net.sf.saxon.value.DecimalValue;
+
+import javax.xml.transform.ErrorListener;
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.RuleBasedCollator;
+
+/**
+ * An XQueryCompiler object allows XQuery 1.0 queries to be compiled. The compiler holds information that
+ * represents the static context for the compilation.
+ * <p/>
+ * <p>To construct an XQueryCompiler, use the factory method {@link Processor#newXQueryCompiler}.</p>
+ * <p/>
+ * <p>An XQueryCompiler may be used repeatedly to compile multiple queries. Any changes made to the
+ * XQueryCompiler (that is, to the static context) do not affect queries that have already been compiled.
+ * An XQueryCompiler may be used concurrently in multiple threads, but it should not then be modified once
+ * initialized.</p>
+ *
+ * @since 9.0
+ */
+
+public class XQueryCompiler {
+
+ private Processor processor;
+ private StaticQueryContext env;
+ private ItemType requiredContextItemType;
+ private String encoding;
+
+ /**
+ * Protected constructor
+ *
+ * @param processor the Saxon Processor
+ */
+
+ protected XQueryCompiler(Processor processor) {
+ this.processor = processor;
+ this.env = processor.getUnderlyingConfiguration().newStaticQueryContext();
+ }
+
+ /**
+ * Get the Processor from which this XQueryCompiler was constructed
+ *
+ * @return the Processor to which this XQueryCompiler belongs
+ * @since 9.3
+ */
+
+ public Processor getProcessor() {
+ return processor;
+ }
+
+ /**
+ * Set the static base URI for the query
+ *
+ * @param baseURI the static base URI; or null to indicate that no base URI is available
+ */
+
+ public void setBaseURI(URI baseURI) {
+ if (baseURI == null) {
+ env.setBaseURI(null);
+ } else {
+ if (!baseURI.isAbsolute()) {
+ throw new IllegalArgumentException("Base URI must be an absolute URI: " + baseURI);
+ }
+ env.setBaseURI(baseURI.toString());
+ }
+ }
+
+ /**
+ * Get the static base URI for the query
+ *
+ * @return the static base URI
+ */
+
+ public URI getBaseURI() {
+ try {
+ return new URI(env.getBaseURI());
+ } catch (URISyntaxException err) {
+ throw new IllegalStateException(err);
+ }
+ }
+
+ /**
+ * Set the ErrorListener to be used during this query compilation episode
+ *
+ * @param listener The error listener to be used. This is notified of all errors detected during the
+ * compilation.
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ env.setErrorListener(listener);
+ }
+
+ /**
+ * Get the ErrorListener being used during this compilation episode
+ *
+ * @return listener The error listener in use. This is notified of all errors detected during the
+ * compilation. If no user-supplied ErrorListener has been set, returns the system-supplied
+ * ErrorListener.
+ */
+
+ public ErrorListener getErrorListener() {
+ return env.getErrorListener();
+ }
+
+ /**
+ * Set whether trace hooks are to be included in the compiled code. To use tracing, it is necessary
+ * both to compile the code with trace hooks included, and to supply a TraceListener at run-time
+ *
+ * @param option true if trace code is to be compiled in, false otherwise
+ */
+
+ public void setCompileWithTracing(boolean option) {
+ env.setCompileWithTracing(option);
+ }
+
+ /**
+ * Ask whether trace hooks are included in the compiled code.
+ *
+ * @return true if trace hooks are included, false if not.
+ */
+
+ public boolean isCompileWithTracing() {
+ return env.isCompileWithTracing();
+ }
+
+ /**
+ * Set a user-defined ModuleURIResolver for resolving URIs used in <code>import module</code>
+ * declarations in the XQuery prolog.
+ * This will override any ModuleURIResolver that was specified as part of the configuration.
+ *
+ * @param resolver the ModuleURIResolver to be used
+ */
+
+ public void setModuleURIResolver(ModuleURIResolver resolver) {
+ env.setModuleURIResolver(resolver);
+ }
+
+ /**
+ * Get the user-defined ModuleURIResolver for resolving URIs used in <code>import module</code>
+ * declarations in the XQuery prolog; returns null if none has been explicitly set either
+ * here or in the Saxon Configuration.
+ *
+ * @return the registered ModuleURIResolver
+ */
+
+ /*@Nullable*/
+ public ModuleURIResolver getModuleURIResolver() {
+ return env.getModuleURIResolver();
+ }
+
+ /**
+ * Set the encoding of the supplied query. This is ignored if the query is supplied
+ * in character form, that is, as a <code>String</code> or as a <code>Reader</code>. If no value
+ * is set, the query processor will attempt to infer the encoding, defaulting to UTF-8 if no
+ * information is available.
+ *
+ * @param encoding the encoding of the supplied query, for example "iso-8859-1"
+ * @since 9.1
+ */
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ /**
+ * Get the encoding previously set for the supplied query.
+ *
+ * @return the encoding previously set using {@link #setEncoding(String)}, or null
+ * if no value has been set. Note that this is not necessarily the actual encoding of the query.
+ * @since 9.2
+ */
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Say whether the query is allowed to be updating. XQuery update syntax will be rejected
+ * during query compilation unless this flag is set. XQuery Update is supported only under Saxon-EE.
+ *
+ * @param updating true if the query is allowed to use the XQuery Update facility
+ * (requires Saxon-EE). If set to false, the query must not be an updating query. If set
+ * to true, it may be either an updating or a non-updating query.
+ * @since 9.1
+ */
+
+ public void setUpdatingEnabled(boolean updating) {
+ env.setUpdatingEnabled(updating);
+ }
+
+ /**
+ * Ask whether the query is allowed to use XQuery Update syntax
+ *
+ * @return true if the query is allowed to use the XQuery Update facility. Note that this
+ * does not necessarily mean that the query is an updating query; but if the value is false,
+ * the it must definitely be non-updating.
+ * @since 9.1
+ */
+
+ public boolean isUpdatingEnabled() {
+ return env.isUpdatingEnabled();
+ }
+
+ /**
+ * Say that the query must be compiled to be schema-aware, even if it contains no
+ * "import schema" declarations. Normally a query is treated as schema-aware
+ * only if it contains one or more "import schema" declarations. If it is not schema-aware,
+ * then all input documents must be untyped (or xs:anyType), and validation of temporary nodes is disallowed
+ * (though validation of the final result tree is permitted). Setting the argument to true
+ * means that schema-aware code will be compiled regardless.
+ *
+ * @param schemaAware If true, the stylesheet will be compiled with schema-awareness
+ * enabled even if it contains no xsl:import-schema declarations. If false, the stylesheet
+ * is treated as schema-aware only if it contains one or more xsl:import-schema declarations.
+ * Note that setting the value to false does not disable use of an import-schema declaration.
+ * @since 9.2
+ */
+
+ public void setSchemaAware(boolean schemaAware) {
+ env.setSchemaAware(schemaAware);
+ }
+
+ /**
+ * Ask whether schema-awareness has been requested either by means of a call on
+ * {@link #setSchemaAware}
+ *
+ * @return true if schema-awareness has been requested
+ * @since 9.2
+ */
+
+ public boolean isSchemaAware() {
+ return env.isSchemaAware();
+ }
+
+ /**
+ * Say whether an XQuery 1.0 or XQuery 3.0 processor is required.
+ *
+ * @param version Must be "1.0" or "3.0". At present onle limited support
+ * for XQuery 3.01 is available. This functionality is available only in Saxon-EE, and it cannot
+ * be used in conjunction with XQuery Updates. To use XQuery 3.0 features,
+ * the query prolog must also specify version="3.0".
+ * <p>In Saxon 9.3, the value "1.1" is accepted as a synonym for "3.0".</p>
+ * @throws IllegalArgumentException if the version is not 1.0 or 3.0.
+ * @since 9.2
+ */
+
+ public void setLanguageVersion(String version) {
+ DecimalValue v;
+ try {
+ v = (DecimalValue) DecimalValue.makeDecimalValue(version, true).asAtomic();
+ } catch (ValidationException ve) {
+ throw new IllegalArgumentException(ve);
+ }
+ if (DecimalValue.ONE_POINT_ONE.equals(v)) {
+ v = DecimalValue.THREE;
+ }
+ if (!DecimalValue.ONE.equals(v) && !DecimalValue.THREE.equals(v)) {
+ throw new IllegalArgumentException("LanguageVersion " + v);
+ }
+ env.setLanguageVersion(v);
+ }
+
+ /**
+ * Ask whether an XQuery 1.0 or XQuery 1.1 processor is being used
+ *
+ * @return version: "1.0" or "3.0"
+ * @since 9.2
+ */
+
+ public String getLanguageVersion() {
+ return env.getLanguageVersion().toString();
+ }
+
+ /**
+ * Declare a namespace binding as part of the static context for queries compiled using this
+ * XQueryCompiler. This binding may be overridden by a binding that appears in the query prolog.
+ * The namespace binding will form part of the static context of the query, but it will not be copied
+ * into result trees unless the prefix is actually used in an element or attribute name.
+ *
+ * @param prefix The namespace prefix. If the value is a zero-length string, this method sets the default
+ * namespace for elements and types.
+ * @param uri The namespace URI. It is possible to specify a zero-length string to "undeclare" a namespace;
+ * in this case the prefix will not be available for use, except in the case where the prefix
+ * is also a zero length string, in which case the absence of a prefix implies that the name
+ * is in no namespace.
+ * @throws NullPointerException if either the prefix or uri is null.
+ * @throws IllegalArgumentException in the event of an invalid declaration of the XML namespace
+ */
+
+ public void declareNamespace(String prefix, String uri) {
+ env.declareNamespace(prefix, uri);
+ }
+
+ /**
+ * Bind a collation URI to a collation
+ *
+ * @param uri the absolute collation URI
+ * @param collation a {@link java.text.Collator} object that implements the required collation
+ * @throws IllegalArgumentException if an attempt is made to rebind the standard URI
+ * for the Unicode codepoint collation
+ * @since 9.4
+ */
+
+ public void declareCollation(String uri, final java.text.Collator collation) {
+ if (uri.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
+ throw new IllegalArgumentException("Cannot declare the Unicode codepoint collation URI");
+ }
+ StringCollator saxonCollation;
+ if (collation instanceof RuleBasedCollator) {
+ saxonCollation = new RuleBasedSubstringMatcher((RuleBasedCollator) collation);
+ } else {
+ saxonCollation = new SimpleCollation(collation);
+ }
+ env.getCollationMap().setNamedCollation(uri, saxonCollation);
+ }
+
+ /**
+ * Declare the default collation
+ *
+ * @param uri the absolute URI of the default collation. This URI must have been bound to a collation
+ * using the method {@link #declareCollation(String, java.text.Collator)}
+ * @throws IllegalStateException if the collation URI has not been registered, unless it is the standard
+ * Unicode codepoint collation which is registered implicitly
+ * @since 9.4
+ */
+
+ public void declareDefaultCollation(String uri) {
+ if (env.getCollationMap().getNamedCollation(uri) == null) {
+ throw new IllegalStateException("Unknown collation " + uri);
+ }
+ env.getCollationMap().setDefaultCollationName(uri);
+ }
+
+
+ /**
+ * Declare the static type of the context item. If this type is declared, and if a context item
+ * is supplied when the query is invoked, then the context item must conform to this type (no
+ * type conversion will take place to force it into this type).
+ *
+ * @param type the required type of the context item
+ */
+
+ public void setRequiredContextItemType(ItemType type) {
+ requiredContextItemType = type;
+ env.setRequiredContextItemType(type.getUnderlyingItemType());
+ }
+
+ /**
+ * Get the required type of the context item. If no type has been explicitly declared for the context
+ * item, an instance of AnyItemType (representing the type item()) is returned.
+ *
+ * @return the required type of the context item
+ */
+
+ public ItemType getRequiredContextItemType() {
+ return requiredContextItemType;
+ }
+
+ /**
+ * Compile a library module supplied as a string. The code generated by compiling the library is available
+ * for importing by all subsequent compilations using the same XQueryCompiler; it is identified by an
+ * "import module" declaration that specifies the module URI of the library module. No module location
+ * hint is required, and if one is present, it is ignored.
+ * <p>The base URI of the query should be supplied by calling {@link #setBaseURI(java.net.URI)} </p>
+ * <p>Separate compilation of library modules is supported only under Saxon-EE</p>
+ *
+ * @param query the text of the query
+ * @throws SaxonApiException if the query compilation fails with a static error
+ * @since 9.2
+ */
+
+ public void compileLibrary(String query) throws SaxonApiException {
+ try {
+ env.compileLibrary(query);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Compile a library module supplied as a file. The code generated by compiling the library is available
+ * for importing by all subsequent compilations using the same XQueryCompiler; it is identified by an
+ * "import module" declaration that specifies the module URI of the library module. No module location
+ * hint is required, and if one is present, it is ignored.
+ * <p>The encoding of the input stream may be specified using {@link #setEncoding(String)};
+ * if this has not been set, the compiler determines the encoding from the version header of the
+ * query, and if that too is absent, it assumes UTF-8.</p>
+ * <p>Separate compilation of library modules is supported only under Saxon-EE</p>
+ *
+ * @param query the file containing the query. The URI corresponding to this file will be used as the
+ * base URI of the query, overriding any URI supplied using {@link #setBaseURI(java.net.URI)} (but not
+ * overriding any base URI specified within the query prolog)
+ * @throws SaxonApiException if the query compilation fails with a static error
+ * @throws IOException if the file does not exist or cannot be read
+ * @since 9.2
+ */
+
+ public void compileLibrary(File query) throws SaxonApiException, IOException {
+ FileInputStream stream = null;
+ try {
+ stream = new FileInputStream(query);
+ String savedBaseUri = env.getBaseURI();
+ env.setBaseURI(query.toURI().toString());
+ env.compileLibrary(stream, encoding);
+ env.setBaseURI(savedBaseUri);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ } finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+ }
+
+ /**
+ * Compile a library module supplied as a Reader. The code generated by compiling the library is available
+ * for importing by all subsequent compilations using the same XQueryCompiler; it is identified by an
+ * "import module" declaration that specifies the module URI of the library module. No module location
+ * hint is required, and if one is present, it is ignored.
+ * <p>The base URI of the query should be supplied by calling {@link #setBaseURI(java.net.URI)} </p>
+ * <p>Separate compilation of library modules is supported only under Saxon-EE</p>
+ *
+ * @param query the text of the query
+ * @throws SaxonApiException if the query compilation fails with a static error
+ * @since 9.2
+ */
+
+ public void compileLibrary(Reader query) throws SaxonApiException {
+ try {
+ env.compileLibrary(query);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ } catch (IOException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Compile a library module supplied as an InputStream. The code generated by compiling the library is available
+ * for importing by all subsequent compilations using the same XQueryCompiler; it is identified by an
+ * "import module" declaration that specifies the module URI of the library module. No module location
+ * hint is required, and if one is present, it is ignored.
+ * <p>The encoding of the input stream may be specified using {@link #setEncoding(String)};
+ * if this has not been set, the compiler determines the encoding from the version header of the
+ * query, and if that too is absent, it assumes UTF-8. </p>
+ * <p>The base URI of the query should be supplied by calling {@link #setBaseURI(java.net.URI)} </p>
+ * <p>Separate compilation of library modules is supported only under Saxon-EE</p>
+ *
+ * @param query the text of the query
+ * @throws SaxonApiException if the query compilation fails with a static error
+ * @since 9.2
+ */
+
+ public void compileLibrary(InputStream query) throws SaxonApiException {
+ try {
+ env.compileLibrary(query, encoding);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ } catch (IOException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Compile a query supplied as a string.
+ * <p>The base URI of the query should be supplied by calling {@link #setBaseURI(java.net.URI)} </p>
+ *
+ * @param query the text of the query
+ * @return an XQueryExecutable representing the compiled query
+ * @throws SaxonApiException if the query compilation fails with a static error
+ * @since 9.0
+ */
+
+ public XQueryExecutable compile(String query) throws SaxonApiException {
+ try {
+ return new XQueryExecutable(processor, env.compileQuery(query));
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Compile a query supplied as a file
+ *
+ * @param query the file containing the query. The URI corresponding to this file will be used as the
+ * base URI of the query, overriding any URI supplied using {@link #setBaseURI(java.net.URI)} (but not
+ * overriding any base URI specified within the query prolog)
+ * @return an XQueryExecutable representing the compiled query
+ * @throws SaxonApiException if the query compilation fails with a static error
+ * @throws IOException if the file does not exist or cannot be read
+ * @since 9.1
+ */
+
+ public XQueryExecutable compile(File query) throws SaxonApiException, IOException {
+ FileInputStream stream = null;
+ try {
+ stream = new FileInputStream(query);
+ String savedBaseUri = env.getBaseURI();
+ env.setBaseURI(query.toURI().toString());
+ XQueryExecutable exec =
+ new XQueryExecutable(processor, env.compileQuery(stream, encoding));
+ env.setBaseURI(savedBaseUri);
+ return exec;
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ } finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+ }
+
+ /**
+ * Compile a query supplied as an InputStream
+ * <p>The base URI of the query should be supplied by calling {@link #setBaseURI(java.net.URI)} </p>
+ *
+ * @param query the input stream on which the query is supplied. This will be consumed by this method
+ * @return an XQueryExecutable representing the compiled query
+ * @throws SaxonApiException if the query compilation fails with a static error
+ * @throws IOException if the file does not exist or cannot be read
+ * @since 9.1
+ */
+
+ public XQueryExecutable compile(InputStream query) throws SaxonApiException, IOException {
+ try {
+ return new XQueryExecutable(processor, env.compileQuery(query, encoding));
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Compile a query supplied as a Reader
+ * <p>The base URI of the query should be supplied by calling {@link #setBaseURI(java.net.URI)} </p>
+ *
+ * @param query the input stream on which the query is supplied. This will be consumed by this method
+ * @return an XQueryExecutable representing the compiled query
+ * @throws SaxonApiException if the query compilation fails with a static error
+ * @throws IOException if the file does not exist or cannot be read
+ * @since 9.1
+ */
+
+ public XQueryExecutable compile(Reader query) throws SaxonApiException, IOException {
+ try {
+ return new XQueryExecutable(processor, env.compileQuery(query));
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get the underlying {@link net.sf.saxon.query.StaticQueryContext} object that maintains the static context
+ * information on behalf of this XQueryCompiler. This method provides an escape hatch to internal Saxon
+ * implementation objects that offer a finer and lower-level degree of control than the s9api classes and
+ * methods. Some of these classes and methods may change from release to release.
+ *
+ * @return the underlying StaticQueryContext object
+ * @since 9.2
+ */
+
+ public StaticQueryContext getUnderlyingStaticContext() {
+ return env;
+ }
+
+
+}
+
diff --git a/sf/saxon/s9api/XQueryEvaluator.java b/sf/saxon/s9api/XQueryEvaluator.java
new file mode 100644
index 0000000..061e0e5
--- /dev/null
+++ b/sf/saxon/s9api/XQueryEvaluator.java
@@ -0,0 +1,575 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.instruct.UserFunction;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.*;
+import net.sf.saxon.query.DynamicQueryContext;
+import net.sf.saxon.query.XQueryExpression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceExtent;
+
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import java.io.PrintStream;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * An <code>XQueryEvaluator</code> represents a compiled and loaded query ready for execution.
+ * The <code>XQueryEvaluator</code> holds details of the dynamic evaluation context for the query.
+ * <p/>
+ * <p>An <code>XQueryEvaluator</code> must not be used concurrently in multiple threads.
+ * It is safe, however, to reuse the object within a single thread to run the same
+ * query several times. Running the query does not change the context
+ * that has been established.</p>
+ * <p/>
+ * <p>An <code>XQueryEvaluator</code> is always constructed by running the <code>Load</code>
+ * method of an {@link net.sf.saxon.s9api.XQueryExecutable}.</p>
+ * <p/>
+ * <p>An <code>XQueryEvaluator</code> is itself a <code>Iterable</code>. This makes it possible to
+ * evaluate the results in a for-each expression.</p>
+ * <p/>
+ * <p>An <code>XQueryEvaluator</code> is itself a <code>Destination</code>. This means it is possible to use
+ * one <code>XQueryEvaluator</code> as the destination to receive the results of another transformation,
+ * this providing a simple way for transformations to be chained into a pipeline. When the query is executed
+ * this way, {@link #setDestination(Destination)} must be called to provide a destination for the result of this query.
+ * Note however that a when the input to a query is supplied in this way, it will always be built as a tree in
+ * memory, rather than the transformation being streamed.</p>
+ */
+public class XQueryEvaluator implements Iterable<XdmItem>, Destination {
+
+ private Processor processor;
+ private XQueryExpression expression;
+ private DynamicQueryContext context;
+ private Controller controller; // used only when making direct calls to global functions
+ private Destination destination;
+ private Set<XdmNode> updatedDocuments;
+ /*@Nullable*/ private Builder sourceTreeBuilder;
+
+ /**
+ * Protected constructor
+ *
+ * @param processor the Saxon processor
+ * @param expression the XQuery expression
+ */
+
+ protected XQueryEvaluator(Processor processor, XQueryExpression expression) {
+ this.processor = processor;
+ this.expression = expression;
+ this.context = new DynamicQueryContext(expression.getExecutable().getConfiguration());
+ }
+
+ /**
+ * Set the schema validation mode for the transformation. This indicates how source documents
+ * loaded specifically for this transformation will be handled. This applies to the
+ * principal source document if supplied as a SAXSource or StreamSource, and to all
+ * documents loaded during the transformation using the <code>doc()</code>, <code>document()</code>,
+ * or <code>collection()</code> functions.
+ *
+ * @param mode the validation mode. Passing null causes no change to the existing value.
+ * Passing <code>Validation.DEFAULT</code> resets to the initial value, which determines
+ * the validation requirements from the Saxon Configuration.
+ */
+
+ public void setSchemaValidationMode(ValidationMode mode) {
+ if (mode != null) {
+ context.setSchemaValidationMode(mode.getNumber());
+ }
+ }
+
+ /**
+ * Get the schema validation mode for the transformation. This indicates how source documents
+ * loaded specifically for this transformation will be handled. This applies to the
+ * principal source document if supplied as a SAXSource or StreamSource, and to all
+ * documents loaded during the transformation using the <code>doc()</code>, <code>document()</code>,
+ * or <code>collection()</code> functions.
+ *
+ * @return the validation mode.
+ */
+
+ public ValidationMode getSchemaValidationMode() {
+ return ValidationMode.get(context.getSchemaValidationMode());
+ }
+
+
+ /**
+ * Set the source document for the query.
+ * <p/>
+ * <p>If the source is an instance of {@link net.sf.saxon.om.NodeInfo}, the supplied node is used
+ * directly as the context node of the query.</p>
+ * <p/>
+ * <p>If the source is an instance of {@link javax.xml.transform.dom.DOMSource}, the DOM node identified
+ * by the DOMSource is wrapped as a Saxon node, and this is then used as the context item</p>
+ * <p/>
+ * <p>In all other cases a new Saxon tree is built, by calling
+ * {@link net.sf.saxon.s9api.DocumentBuilder#build(javax.xml.transform.Source)}, and the document
+ * node of this tree is then used as the context item for the query.</p>
+ *
+ * @param source the source document to act as the initial context item for the query.
+ */
+
+ public void setSource(Source source) throws SaxonApiException {
+ if (source instanceof NodeInfo) {
+ setContextItem(new XdmNode((NodeInfo) source));
+ } else if (source instanceof DOMSource) {
+ setContextItem(processor.newDocumentBuilder().wrap(source));
+ } else {
+ setContextItem(processor.newDocumentBuilder().build(source));
+ }
+ }
+
+ /**
+ * Set the initial context item for the query
+ *
+ * @param item the initial context item, or null if there is to be no initial context item
+ */
+
+ public void setContextItem(XdmItem item) {
+ if (item != null) {
+ context.setContextItem((Item) item.getUnderlyingValue());
+ }
+ }
+
+ /**
+ * Get the initial context item for the query, if one has been set
+ *
+ * @return the initial context item, or null if none has been set. This will not necessarily
+ * be the same object as was supplied, but it will be an XdmItem object that represents
+ * the same underlying node or atomic value.
+ */
+
+ public XdmItem getContextItem() {
+ return (XdmItem) XdmValue.wrap(context.getContextItem());
+ }
+
+ /**
+ * Set the value of external variable defined in the query
+ *
+ * @param name the name of the external variable, as a QName
+ * @param value the value of the external variable, or null to clear a previously set value
+ */
+
+ public void setExternalVariable(QName name, XdmValue value) {
+ context.setParameterValue(name.getClarkName(),
+ (value == null ? null : value.getUnderlyingValue()));
+ }
+
+ /**
+ * Get the value that has been set for an external variable
+ *
+ * @param name the name of the external variable whose value is required
+ * @return the value that has been set for the external variable, or null if no value has been set
+ */
+
+ public XdmValue getExternalVariable(QName name) {
+ Object oval = context.getParameter(name.getClarkName());
+ if (oval == null) {
+ return null;
+ }
+ if (oval instanceof Sequence) {
+ return XdmValue.wrap((Sequence) oval);
+ }
+ throw new IllegalStateException(oval.getClass().getName());
+ }
+
+ /**
+ * Set an object that will be used to resolve URIs used in
+ * fn:doc() and related functions.
+ *
+ * @param resolver An object that implements the URIResolver interface, or
+ * null.
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ context.setURIResolver(resolver);
+ }
+
+ /**
+ * Get the URI resolver.
+ *
+ * @return the user-supplied URI resolver if there is one, or the
+ * system-defined one otherwise
+ */
+
+ public URIResolver getURIResolver() {
+ return context.getURIResolver();
+ }
+
+ /**
+ * Set the error listener. The error listener receives reports of all run-time
+ * errors and can decide how to report them.
+ *
+ * @param listener the ErrorListener to be used
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ context.setErrorListener(listener);
+ }
+
+ /**
+ * Get the error listener.
+ *
+ * @return the ErrorListener in use
+ */
+
+ public ErrorListener getErrorListener() {
+ return context.getErrorListener();
+ }
+
+ /**
+ * Set a TraceListener which will receive messages relating to the evaluation of all expressions.
+ * This option has no effect unless the query was compiled to enable tracing.
+ *
+ * @param listener the TraceListener to use
+ */
+
+ public void setTraceListener(TraceListener listener) {
+ context.setTraceListener(listener);
+ }
+
+ /**
+ * Get the registered TraceListener, if any
+ *
+ * @return listener the TraceListener in use, or null if none has been set
+ */
+
+ public TraceListener getTraceListener() {
+ return context.getTraceListener();
+ }
+
+ /**
+ * Set the destination for output from the fn:trace() function.
+ * By default, the destination is System.err. If a TraceListener is in use,
+ * this is ignored, and the trace() output is sent to the TraceListener.
+ *
+ * @param stream the PrintStream to which trace output will be sent. If set to
+ * null, trace output is suppressed entirely. It is the caller's responsibility
+ * to close the stream after use.
+ * @since 9.1
+ */
+
+ public void setTraceFunctionDestination(PrintStream stream) {
+ context.setTraceFunctionDestination(stream);
+ }
+
+ /**
+ * Get the destination for output from the fn:trace() function.
+ *
+ * @return the PrintStream to which trace output will be sent. If no explicitly
+ * destination has been set, returns System.err. If the destination has been set
+ * to null to suppress trace output, returns null.
+ * @since 9.1
+ */
+
+ public PrintStream getTraceFunctionDestination() {
+ return context.getTraceFunctionDestination();
+ }
+
+ /**
+ * Set the destination to be used for the query results
+ *
+ * @param destination the destination to which the results of the query will be sent
+ */
+
+ public void setDestination(Destination destination) {
+ this.destination = destination;
+ }
+
+ /**
+ * Perform the query.
+ * <p/>
+ * <ul><li>In the case of a non-updating query, the results are sent to the
+ * registered Destination.</li>
+ * <li>In the case of an updating query, all updated documents will be available after query
+ * execution as the result of the {@link #getUpdatedDocuments} method.</li>
+ * </ul>
+ *
+ * @throws net.sf.saxon.s9api.SaxonApiException
+ * if any dynamic error occurs during the query
+ * @throws IllegalStateException if this is a non-updating query and no Destination has been
+ * supplied for the query results
+ */
+
+ public void run() throws SaxonApiException {
+ try {
+ if (expression.isUpdateQuery()) {
+ Set<MutableNodeInfo> docs = expression.runUpdate(context);
+ updatedDocuments = new HashSet<XdmNode>();
+ for (MutableNodeInfo doc : docs) {
+ updatedDocuments.add((XdmNode) XdmNode.wrapItem(doc));
+ }
+ } else {
+ if (destination == null) {
+ throw new IllegalStateException("No destination supplied");
+ }
+
+ Result receiver;
+ if (destination instanceof Serializer) {
+ //receiver = ((Serializer) destination).getResult();
+ //context.set
+ receiver = ((Serializer) destination).getReceiver(expression.getExecutable());
+ } else {
+ receiver = destination.getReceiver(expression.getExecutable().getConfiguration());
+ }
+ expression.run(context, receiver, null);
+ destination.close();
+ }
+ } catch (TransformerException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Perform the query, sending the results to a specified destination.
+ * <p/>
+ * <p>This method must not be used with an updating query.</p>
+ * <p/>
+ * <p>This method is designed for use with a query that produces a single node (typically
+ * a document node or element node) as its result. If the query produces multiple nodes,
+ * the effect depends on the kind of destination. For example, if the result is an
+ * <code>XdmDestination</code>, only the last of the nodes will be accessible.</p>
+ *
+ * @param destination The destination where the result document will be sent
+ * @throws net.sf.saxon.s9api.SaxonApiException
+ * if any dynamic error occurs during the query
+ * @throws IllegalStateException if this is an updating query
+ */
+
+ public void run(Destination destination) throws SaxonApiException {
+ if (expression.isUpdateQuery()) {
+ throw new IllegalStateException("Query is updating");
+ }
+ try {
+ Receiver receiver;
+ if (destination instanceof Serializer) {
+ receiver = ((Serializer) destination).getReceiver(expression.getExecutable());
+ } else {
+ receiver = destination.getReceiver(expression.getExecutable().getConfiguration());
+ }
+ expression.run(context, receiver, null);
+ } catch (TransformerException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Perform the query, returning the results as an XdmValue. This method
+ * must not be used with an updating query
+ *
+ * @return an XdmValue representing the results of the query
+ * @throws SaxonApiException if the query fails with a dynamic error
+ * @throws IllegalStateException if this is an updating query
+ */
+
+ public XdmValue evaluate() throws SaxonApiException {
+ if (expression.isUpdateQuery()) {
+ throw new IllegalStateException("Query is updating");
+ }
+ try {
+ SequenceIterator iter = expression.iterator(context);
+ Sequence result = SequenceExtent.makeSequenceExtent(iter);
+ return XdmValue.wrap(result);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Evaluate the XQuery expression, returning the result as an <code>XdmItem</code> (that is,
+ * a single node or atomic value).
+ *
+ * @return an <code>XdmItem</code> representing the result of the query, or null if the query
+ * returns an empty sequence. If the expression returns a sequence of more than one item,
+ * any items after the first are ignored.
+ * @throws SaxonApiException if a dynamic error occurs during the query evaluation.
+ * @since 9.2
+ */
+
+
+ public XdmItem evaluateSingle() throws SaxonApiException {
+ try {
+ SequenceIterator iter = expression.iterator(context);
+ Item next = iter.next();
+ return (next == null ? null : (XdmItem)XdmValue.wrap(next));
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Evaluate the query, and return an iterator over its results.
+ * <p>This method must not be used with an updating query.</p>
+ *
+ * @throws SaxonApiUncheckedException if a dynamic error is detected while constructing the iterator.
+ * It is also possible for an SaxonApiUncheckedException to be thrown by the hasNext() method of the
+ * returned iterator if a dynamic error occurs while evaluating the result sequence.
+ * @throws IllegalStateException if this is an updating query
+ */
+
+ public Iterator<XdmItem> iterator() throws SaxonApiUncheckedException {
+ if (expression.isUpdateQuery()) {
+ throw new IllegalStateException("Query is updating");
+ }
+ try {
+ return new XdmSequenceIterator(expression.iterator(context));
+ } catch (XPathException e) {
+ throw new SaxonApiUncheckedException(e);
+ }
+ }
+
+ /**
+ * Return a Receiver which can be used to supply the principal source document for the transformation.
+ * This method is intended primarily for internal use, though it can also
+ * be called by a user application that wishes to feed events into the query engine.
+ * <p/>
+ * <p>Saxon calls this method to obtain a Receiver, to which it then sends
+ * a sequence of events representing the content of an XML document. This method is provided so that
+ * <code>XQueryEvaluator</code> implements <code>Destination</code>, allowing one transformation
+ * to receive the results of another in a pipeline.</p>
+ * <p/>
+ * <p>Note that when an <code>XQueryEvaluator</code> is used as a <code>Destination</code>, the initial
+ * context node set on that <code>XQueryEvaluator</code> (using {@link #setSource(javax.xml.transform.Source)}) is ignored.</p>
+ *
+ * @param config The Saxon configuration. This is supplied so that the destination can
+ * use information from the configuration (for example, a reference to the name pool)
+ * to construct or configure the returned Receiver.
+ * @return the Receiver to which events are to be sent.
+ * @throws SaxonApiException if the Receiver cannot be created
+ * @throws IllegalStateException if no Destination has been supplied
+ */
+
+ public Receiver getReceiver(Configuration config) throws SaxonApiException {
+ if (destination == null) {
+ throw new IllegalStateException("No destination has been supplied");
+ }
+ if (controller == null) {
+ controller = expression.newController();
+ context.initializeController(controller);
+ try {
+ controller.defineGlobalParameters();
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+ sourceTreeBuilder = controller.makeBuilder();
+ Receiver stripper = controller.makeStripper(sourceTreeBuilder);
+ if (controller.getExecutable().stripsInputTypeAnnotations()) {
+ stripper = controller.getConfiguration().getAnnotationStripper(stripper);
+ }
+ return stripper;
+ }
+
+ /**
+ * Close this destination, allowing resources to be released. Used when this <code>XQueryEvaluator</code> is acting
+ * as the destination of another transformation or query. Saxon calls this method when it has finished writing
+ * to the destination.
+ */
+
+ public void close() throws SaxonApiException {
+ if (sourceTreeBuilder != null) {
+ DocumentInfo doc = (DocumentInfo) sourceTreeBuilder.getCurrentRoot();
+ setSource(doc);
+ sourceTreeBuilder = null;
+ if (doc == null) {
+ throw new SaxonApiException("No source document has been built by the previous pipeline stage");
+ }
+ run(destination);
+ destination.close();
+ }
+ }
+
+
+ /**
+ * After executing an updating query using the {@link #run()} method, iterate over the root
+ * nodes of the documents updated by the query.
+ * <p/>
+ * <p>The results remain available until a new query is executed. This method returns the results
+ * of the most recently executed query. It does not consume the results.</p>
+ *
+ * @return an iterator over the root nodes of documents (or other trees) that were updated by the query
+ * @since 9.1
+ */
+
+ public Iterator<XdmNode> getUpdatedDocuments() {
+ return updatedDocuments.iterator();
+ }
+
+ /**
+ * Call a global user-defined function in the compiled query.
+ * <p/>
+ * If this is called more than once (to evaluate the same function repeatedly with different arguments,
+ * or to evaluate different functions) then the sequence of evaluations uses the same values of global
+ * variables including external variables (query parameters); the effect of any changes made to query parameters
+ * between calls is undefined.
+ *
+ * @param function The name of the function to be called
+ * @param arguments The values of the arguments to be supplied to the function. These
+ * must be of the correct type as defined in the function signature (there is no automatic
+ * conversion to the required type).
+ * @throws SaxonApiException if no function has been defined with the given name and arity;
+ * or if any of the arguments does not match its required type according to the function
+ * signature; or if a dynamic error occurs in evaluating the function.
+ * @since 9.3
+ */
+
+ public XdmValue callFunction(QName function, XdmValue[] arguments) throws SaxonApiException {
+ final UserFunction fn = expression.getStaticContext().getUserDefinedFunction(
+ function.getNamespaceURI(), function.getLocalName(), arguments.length);
+ if (fn == null) {
+ throw new SaxonApiException("No function with name " + function.getClarkName() +
+ " and arity " + arguments.length + " has been declared in the query");
+ }
+ try {
+ // TODO: use the same controller in other interfaces such as run(), and expose it in a trapdoor API
+ if (controller == null) {
+ controller = expression.newController();
+ context.initializeController(controller);
+ controller.defineGlobalParameters();
+ } else {
+ context.initializeController(controller);
+ controller.defineGlobalParameters();
+ }
+ Sequence[] vr = new Sequence[arguments.length];
+
+ for (int i = 0; i < arguments.length; i++) {
+ net.sf.saxon.value.SequenceType type = fn.getParameterDefinitions()[i].getRequiredType();
+ vr[i] = arguments[i].getUnderlyingValue();
+ if (!type.matches(vr[i], controller.getConfiguration())) {
+ throw new SaxonApiException("Argument " + (i + 1) +
+ " of function " + function.getClarkName() +
+ " does not match the required type " + type.toString());
+ }
+ }
+ Sequence result = fn.call(vr, controller);
+ return XdmValue.wrap(result);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get the underlying dynamic context object. This provides an escape hatch to the underlying
+ * implementation classes, which contain methods that may change from one release to another.
+ *
+ * @return the underlying object representing the dynamic context for query execution
+ */
+
+ public DynamicQueryContext getUnderlyingQueryContext() {
+ return context;
+ }
+
+
+}
+
diff --git a/sf/saxon/s9api/XQueryExecutable.java b/sf/saxon/s9api/XQueryExecutable.java
new file mode 100644
index 0000000..d78a40d
--- /dev/null
+++ b/sf/saxon/s9api/XQueryExecutable.java
@@ -0,0 +1,95 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.query.XQueryExpression;
+
+/**
+ * An XQueryExecutable represents the compiled form of a query.
+ * To execute the query, it must first be loaded to form an {@link net.sf.saxon.s9api.XQueryEvaluator}.
+ *
+ * <p>An XQueryExecutable is immutable, and therefore thread-safe.
+ * It is simplest to load a new XQueryEvaluator each time the query is to be run.
+ * However, the XQueryEvaluator is serially reusable within a single thread. </p>
+ *
+ * <p>An XQueryExecutable is created by using one of the <code>compile</code> methods on the
+ * {@link net.sf.saxon.s9api.XQueryCompiler} class.</p>
+ *
+ * @since 9.0
+ */
+public class XQueryExecutable {
+
+ Processor processor;
+ XQueryExpression exp;
+
+ protected XQueryExecutable(Processor processor, XQueryExpression exp) {
+ this.processor = processor;
+ this.exp = exp;
+ }
+
+ /**
+ * Load the stylesheet to prepare it for execution.
+ * @return An XsltTransformer. The returned XsltTransformer can be used to set up the
+ * dynamic context for stylesheet evaluation, and to run the stylesheet.
+ */
+
+ public XQueryEvaluator load() {
+ return new XQueryEvaluator(processor, exp);
+ }
+
+ /**
+ * Get the ItemType of the items in the result of the query, as determined by static analysis. This
+ * is the most precise ItemType that the processor is able to determine from static examination of the
+ * query; the actual items in the query result are guaranteed to belong to this ItemType or to a subtype
+ * of this ItemType.
+ * @return the statically-determined ItemType of the result of the query
+ * @since 9.1
+ */
+
+ public ItemType getResultItemType() {
+ net.sf.saxon.type.ItemType it =
+ exp.getExpression().getItemType(processor.getUnderlyingConfiguration().getTypeHierarchy());
+ return new ConstructedItemType(it, processor);
+ }
+
+ /**
+ * Get the statically-determined cardinality of the result of the query. This is the most precise cardinality
+ * that the processor is able to determine from static examination of the query.
+ * @return the statically-determined cardinality of the result of the query
+ * @since 9.1
+ */
+
+ /*@NotNull*/ public OccurrenceIndicator getResultCardinality() {
+ int card = exp.getExpression().getCardinality();
+ return OccurrenceIndicator.getOccurrenceIndicator(card);
+ }
+
+ /**
+ * Ask whether the query is an updating query: that is, whether it returns a Pending Update List
+ * rather than a Value. Note that queries using the XUpdate copy-modify syntax are not considered
+ * to be updating queries.
+ * @return true if the query is an updating query, false if not
+ * @since 9.1
+ */
+
+ public boolean isUpdateQuery() {
+ return exp.isUpdateQuery();
+ }
+
+ /**
+ * Get the underlying implementation object representing the compiled stylesheet. This provides
+ * an escape hatch into lower-level APIs. The object returned by this method may change from release
+ * to release.
+ * @return the underlying implementation of the compiled stylesheet
+ */
+
+ public XQueryExpression getUnderlyingCompiledQuery() {
+ return exp;
+ }
+}
+
diff --git a/sf/saxon/s9api/XdmAtomicValue.java b/sf/saxon/s9api/XdmAtomicValue.java
new file mode 100644
index 0000000..540a855
--- /dev/null
+++ b/sf/saxon/s9api/XdmAtomicValue.java
@@ -0,0 +1,296 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+import java.math.BigDecimal;
+import java.net.URI;
+
+/**
+ * The class XdmAtomicValue represents an item in an XPath 2.0 sequence that is an atomic value.
+ * The value may belong to any of the 19 primitive types defined in XML Schema, or to a type
+ * derived from these primitive types, or the XPath 2.0 type xs:untypedAtomic. The type may
+ * be either a built-in type or a user-defined type.
+ *
+ * <p>An <code>XdmAtomicValue</code> is immutable.</p>
+ */
+public class XdmAtomicValue extends XdmItem {
+
+ protected XdmAtomicValue(AtomicValue value) {
+ super(value);
+ }
+
+ /**
+ * Create an <tt>xs:boolean</tt> atomic value
+ * @param value the boolean value, true or false
+ */
+
+ public XdmAtomicValue(boolean value) {
+ super(BooleanValue.get(value));
+ }
+
+ /**
+ * Create an <tt>xs:integer</tt> atomic value
+ * @param value the <tt>xs:integer</tt> value, as a long
+ */
+
+ public XdmAtomicValue(long value) {
+ super(Int64Value.makeIntegerValue(value));
+ }
+
+ /**
+ * Create an <tt>xs:decimal</tt> atomic value
+ * @param value the <tt>xs:decimal</tt> value, as a BigDecimal
+ */
+
+ public XdmAtomicValue(BigDecimal value) {
+ super(new DecimalValue(value));
+ }
+
+ /**
+ * Create an <tt>xs:double</tt> atomic value
+ * @param value the <tt>xs:double</tt> value, as a double
+ */
+
+ public XdmAtomicValue(double value) {
+ super(new DoubleValue(value));
+ }
+
+ /**
+ * Create an <tt>xs:float</tt> atomic value
+ * @param value the <tt>xs:float</tt> value, as a float
+ */
+
+ public XdmAtomicValue(float value) {
+ super(new FloatValue(value));
+ }
+
+ /**
+ * Create an <tt>xs:string</tt> atomic value
+ * @param value the <tt>xs:string</tt> value, as a string
+ */
+
+ public XdmAtomicValue(String value) {
+ super(new StringValue(value));
+ }
+
+ /**
+ * Create an <tt>xs:anyURI</tt> atomic value
+ * @param value the <tt>xs:anyURI</tt> value, as a URI
+ */
+
+ public XdmAtomicValue(URI value) {
+ super(new AnyURIValue(value.toString()));
+ }
+
+ /**
+ * Create an <tt>xs:QName</tt> atomic value
+ * @param value the <tt>xs:QName</tt> value, as a QName
+ */
+
+ public XdmAtomicValue(QName value) {
+ super(new QNameValue(value.getStructuredQName(), BuiltInAtomicType.QNAME));
+ }
+
+ /**
+ * Construct an atomic value given its lexical representation and the name of the required
+ * built-in atomic type.
+ * <p>This method cannot be used to construct values that are namespace-sensitive (QNames and Notations)</p>
+ * @param lexicalForm the value in the lexical space of the target data type. More strictly, the input
+ * value before the actions of the whitespace facet for the target data type are applied.
+ * @param type the required atomic type. This must either be one of the built-in
+ * atomic types defined in XML Schema, or a user-defined type whose definition appears
+ * in a schema that is known to the Processor. It must not be an abstract type.
+ * @throws SaxonApiException if the type is unknown, or is not atomic, or is namespace-sensitive;
+ * or if the value supplied in <tt>lexicalForm</tt> is not in the lexical space of the specified atomic
+ * type.
+ */
+
+ public XdmAtomicValue(String lexicalForm, ItemType type) throws SaxonApiException {
+ net.sf.saxon.type.ItemType it = type.getUnderlyingItemType();
+ if (!it.isPlainType()) {
+ throw new SaxonApiException("Requested type is not atomic");
+ }
+ if (((AtomicType)it).isAbstract()) {
+ throw new SaxonApiException("Requested type is an abstract type");
+ }
+ if (((AtomicType)it).isNamespaceSensitive()) {
+ throw new SaxonApiException("Requested type is namespace-sensitive");
+ }
+ try {
+ StringConverter converter = type.getConversionRules().getStringConverter((AtomicType)it);
+ setValue(converter.convertString(lexicalForm).asAtomic());
+ } catch (ValidationException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get the result of converting the atomic value to a string. This has the same
+ * effect as the XPath string() function.
+ */
+
+ public String toString() {
+ return getStringValue();
+ }
+
+ /**
+ * Get the primitive type of this atomic value, as a QName. The primitive types for this purpose are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is xs:anyAtomicType.
+ * @return a QName naming the primitive type of this atomic value. This will always be an atomic type.
+ */
+
+ /*@NotNull*/ public QName getPrimitiveTypeName() {
+ AtomicValue value = (AtomicValue)getUnderlyingValue();
+ BuiltInAtomicType type = value.getPrimitiveType();
+ return new QName(type.getQualifiedName());
+ }
+
+ /**
+ * Get the value as a Java object of the nearest equivalent type.
+ *
+ * <p>The result type is as follows:</p>
+ *
+ * <table>
+ * <tr><th>XPath type</th> <th>Java class</th></tr>
+ * <tr><td>xs:string</td> <td>String</td></tr>
+ * <tr><td>xs:integer</td> <td>java.math.BigInteger</td></tr>
+ * <tr><td>xs:decimal</td> <td>java.math.BigDecimal</td></tr>
+ * <tr><td>xs:double</td> <td>Double</td></tr>
+ * <tr><td>xs:float</td> <td>Float</td></tr>
+ * <tr><td>xs:boolean</td> <td>Boolean</td></tr>
+ * <tr><td>xs:QName</td> <td>QName</td></tr>
+ * <tr><td>xs:anyURI</td> <td>String</td></tr>
+ * <tr><td>xs:untypedAtomic</td><td>String</td></tr>
+ * <tr><td>Other types</td> <td>currently String, but this may change in the future</td></tr>
+ * </table>
+ * @return the value, converted to a Java object of a suitable type
+ */
+
+ @SuppressWarnings({"AutoBoxing"})
+ public Object getValue() {
+ AtomicValue av = (AtomicValue)getUnderlyingValue();
+ if (av instanceof StringValue) {
+ return av.getStringValue();
+ } else if (av instanceof IntegerValue) {
+ return ((IntegerValue)av).asBigInteger();
+ } else if (av instanceof DoubleValue) {
+ return ((DoubleValue)av).getDoubleValue();
+ } else if (av instanceof FloatValue) {
+ return ((FloatValue)av).getFloatValue();
+ } else if (av instanceof BooleanValue) {
+ return ((BooleanValue)av).getBooleanValue();
+ } else if (av instanceof DecimalValue) {
+ return ((DecimalValue)av).getDecimalValue();
+ } else if (av instanceof QNameValue) {
+ QNameValue q = (QNameValue)av;
+ return new QName(q.getPrefix(), q.getNamespaceURI(), q.getLocalName());
+ } else {
+ return av.getStringValue();
+ }
+ }
+
+ /**
+ * Get the value converted to a boolean using the XPath casting rules
+ * @return the result of converting to a boolean (Note: this is not the same as the
+ * effective boolean value).
+ * @throws SaxonApiException if the value cannot be cast to a boolean
+ */
+
+ public boolean getBooleanValue() throws SaxonApiException {
+ AtomicValue av = (AtomicValue)getUnderlyingValue();
+ if (av instanceof BooleanValue) {
+ return ((BooleanValue)av).getBooleanValue();
+ } else if (av instanceof NumericValue) {
+ return !av.isNaN() && ((NumericValue)av).signum() != 0;
+ } else if (av instanceof StringValue) {
+ String s = av.getStringValue().trim();
+ return "1".equals(s) || "true".equals(s);
+ } else {
+ throw new SaxonApiException("Cannot cast item to a boolean");
+ }
+ }
+
+ /**
+ * Get the value converted to an integer using the XPath casting rules
+ * @return the result of converting to an integer
+ * @throws SaxonApiException if the value cannot be cast to an integer
+ */
+
+ public long getLongValue() throws SaxonApiException {
+ AtomicValue av = (AtomicValue)getUnderlyingValue();
+ if (av instanceof BooleanValue) {
+ return ((BooleanValue)av).getBooleanValue() ? 0L : 1L;
+ } else if (av instanceof NumericValue) {
+ try {
+ return ((NumericValue)av).longValue();
+ } catch (XPathException e) {
+ throw new SaxonApiException("Cannot cast item to an integer");
+ }
+ } else if (av instanceof StringValue) {
+ StringToDouble converter = StringToDouble.getInstance();
+ return (long)converter.stringToNumber(av.getStringValueCS());
+ } else {
+ throw new SaxonApiException("Cannot cast item to an integer");
+ }
+ }
+
+ /**
+ * Get the value converted to a double using the XPath casting rules.
+ * <p>If the value is a string, the XSD 1.1 rules are used, which means that the string
+ * "+INF" is recognised.</p>
+ * @return the result of converting to a double
+ * @throws SaxonApiException if the value cannot be cast to a double
+ */
+
+ public double getDoubleValue() throws SaxonApiException {
+ AtomicValue av = (AtomicValue)getUnderlyingValue();
+ if (av instanceof BooleanValue) {
+ return ((BooleanValue)av).getBooleanValue() ? 0.0 : 1.0;
+ } else if (av instanceof NumericValue) {
+ return ((NumericValue)av).getDoubleValue();
+ } else if (av instanceof StringValue) {
+ try {
+ StringToDouble converter = StringToDouble11.getInstance();
+ return converter.stringToNumber(av.getStringValueCS());
+ } catch (NumberFormatException e) {
+ throw new SaxonApiException(e.getMessage());
+ }
+ } else {
+ throw new SaxonApiException("Cannot cast item to a double");
+ }
+ }
+
+ /**
+ * Get the value converted to a decimal using the XPath casting rules
+ * @return the result of converting to a decimal
+ * @throws SaxonApiException if the value cannot be cast to a double
+ */
+
+ public BigDecimal getDecimalValue() throws SaxonApiException {
+ AtomicValue av = (AtomicValue)getUnderlyingValue();
+ if (av instanceof BooleanValue) {
+ return ((BooleanValue)av).getBooleanValue() ? BigDecimal.ZERO : BigDecimal.ONE;
+ } else if (av instanceof NumericValue) {
+ try {
+ return ((NumericValue)av).getDecimalValue();
+ } catch (XPathException e) {
+ throw new SaxonApiException("Cannot cast item to a decimal");
+ }
+ } else if (av instanceof StringValue) {
+ return new BigDecimal(av.getStringValueCS().toString());
+ } else {
+ throw new SaxonApiException("Cannot cast item to a decimal");
+ }
+ }
+}
+
diff --git a/sf/saxon/s9api/XdmDestination.java b/sf/saxon/s9api/XdmDestination.java
new file mode 100644
index 0000000..0be170e
--- /dev/null
+++ b/sf/saxon/s9api/XdmDestination.java
@@ -0,0 +1,255 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.TreeModel;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+
+import java.net.URI;
+
+/**
+ * An <code>XdmDestination</code> is a {@link Destination} in which an {@link XdmNode}
+ * is constructed to hold the output of a query or transformation:
+ * that is, a tree using Saxon's implementation of the XDM data model
+ *
+ * <p>No data needs to be supplied to the <code>XdmDestination</code> object. The query or transformation
+ * populates an <code>XdmNode</code>, which may then be retrieved using the <code>getXdmNode</code>
+ * method.</p>
+ *
+ * <p>An <code>XdmDestination</code> is designed to hold a single tree rooted at a document or element node.
+ * It should therefore not be used as the destination of a query that produces multiple
+ * documents, multiple elements, nodes other than elements and documents, or atomic values. If the query
+ * does produce such a result, an exception will be thrown.</p>
+ *
+ * <p>An XdmDestination can be reused to hold the results of a second query or transformation only
+ * if the {@link #reset} method is first called to reset its state.</p>
+ *
+ * <p>If an XDM tree is to be built from a lexical XML document, or programmatically from the application
+ * by writing a sequence of events, the recommended mechanism is to use a {@link DocumentBuilder} rather
+ * than this class.</p>
+ */
+
+public class XdmDestination implements Destination {
+
+ TreeModel treeModel = TreeModel.TINY_TREE;
+ URI baseURI;
+ Builder builder;
+
+ public XdmDestination() {
+ //builder = new TinyBuilder();
+ }
+
+ /**
+ * Set the base URI for the document node that will be created when the XdmDestination is written to.
+ * This method must be called before writing to the destination; it has no effect on an XdmNode that
+ * has already been constructed.
+ * @param baseURI the base URI for the node that will be constructed when the XdmDestination is written to.
+ * This must be an absolute URI
+ * @throws IllegalArgumentException if the baseURI supplied is not an absolute URI
+ * @since 9.1
+ */
+
+ public void setBaseURI(URI baseURI) {
+ if (!baseURI.isAbsolute()) {
+ throw new IllegalArgumentException("Supplied base URI must be absolute");
+ }
+ //builder.setBaseURI(baseURI.toString());
+ this.baseURI = baseURI;
+ }
+
+ /**
+ * Get the base URI that will be used for the document node when the XdmDestination is written to.
+ * @return the base URI that will be used for the node that is constructed when the XdmDestination is written to.
+ * @since 9.1
+ */
+
+ public URI getBaseURI() {
+ return baseURI;
+ }
+
+ /**
+ * Set the tree model to be used for documents constructed using this XdmDestination.
+ * By default, the TinyTree is used.
+ * @param model typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link TreeModel#TINY_TREE_CONDENSED}, or {@link TreeModel#LINKED_TREE}. However, in principle
+ * a user-defined tree model can be used.
+ * @since 9.2
+ */
+
+ public void setTreeModel(TreeModel model) {
+ this.treeModel = model;
+ }
+
+ /**
+ * Get the tree model to be used for documents constructed using this XdmDestination.
+ * By default, the TinyTree is used.
+ * @return the tree model in use: typically one of the constants {@link net.sf.saxon.om.TreeModel#TINY_TREE},
+ * {@link net.sf.saxon.om.TreeModel#TINY_TREE_CONDENSED}, or {@link TreeModel#LINKED_TREE}. However, in principle
+ * a user-defined tree model can be used.
+ * @since 9.2
+ */
+
+ public TreeModel getTreeModel() {
+ return treeModel;
+ }
+
+ /**
+ * Return a Receiver. Saxon calls this method to obtain a Receiver, to which it then sends
+ * a sequence of events representing the content of an XML document.
+ * @param config The Saxon configuration. This is supplied so that the destination can
+ * use information from the configuration (for example, a reference to the name pool)
+ * to construct or configure the returned Receiver.
+ * @return the Receiver to which events are to be sent.
+ * @throws net.sf.saxon.s9api.SaxonApiException
+ * if the Receiver cannot be created
+ */
+
+ public Receiver getReceiver(Configuration config) throws SaxonApiException {
+ TreeModel model = treeModel;
+ if (model == null) {
+ model = TreeModel.getTreeModel(config.getTreeModel());
+ }
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ builder = model.makeBuilder(pipe);
+ if (baseURI != null) {
+ builder.setBaseURI(baseURI.toString());
+ }
+ return new TreeProtector(builder);
+ }
+
+ /**
+ * Close the destination, allowing resources to be released. Saxon calls this method when
+ * it has finished writing to the destination.
+ */
+
+ public void close() throws SaxonApiException {
+ // no action
+ }
+
+ /**
+ * Return the node at the root of the tree, after it has been constructed.
+ * <p/>
+ * <p>This method should not be called while the tree is under construction.</p>
+ * @return the root node of the tree (always a document or element node); or null if
+ * nothing is written to the tree (for example, the result of a query that returns the
+ * empty sequence)
+ * @throws IllegalStateException if called during the execution of the process that
+ * is writing the tree.
+ */
+
+ public XdmNode getXdmNode() {
+ if (builder == null) {
+ throw new IllegalStateException("The document has not yet been built");
+ }
+ NodeInfo node = builder.getCurrentRoot();
+ return (node == null ? null : (XdmNode)XdmValue.wrap(node));
+ }
+
+ /**
+ * Allow the <code>XdmDestination</code> to be reused, without resetting other properties
+ * of the destination.
+ */
+
+ public void reset() {
+ builder = null;
+ }
+
+ /**
+ * TreeProtector is a filter that ensures that the events reaching the Builder constitute a single
+ * tree rooted at an element or document node (because anything else will crash the builder)
+ */
+
+ private static class TreeProtector extends ProxyReceiver {
+
+ private int level = 0;
+ private boolean ended = false;
+
+ public TreeProtector(Receiver next) {
+ super(next);
+ }
+
+ @Override
+ public void startDocument(int properties) throws XPathException {
+ if (ended) {
+ throw new XPathException("Only a single document can be written to an XdmDestination");
+ }
+ super.startDocument(properties);
+ level++;
+ }
+
+ @Override
+ public void endDocument() throws XPathException {
+ super.endDocument();
+ level--;
+ if (level == 0) {
+ ended = true;
+ }
+ }
+
+ @Override
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (ended) {
+ throw new XPathException("Only a single root node can be written to an XdmDestination");
+ }
+ super.startElement(nameCode, typeCode, locationId, properties);
+ level++;
+ }
+
+ @Override
+ public void endElement() throws XPathException {
+ super.endElement();
+ level--;
+ if (level == 0) {
+ ended = true;
+ }
+ }
+
+ @Override
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (level == 0) {
+ throw new XPathException("When writing to an XdmDestination, text nodes are only allowed within a document or element node");
+ }
+ super.characters(chars, locationId, properties);
+ }
+
+ @Override
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (level == 0) {
+ throw new XPathException("When writing to an XdmDestination, processing instructions are only allowed within a document or element node");
+ }
+ super.processingInstruction(target, data, locationId, properties);
+ }
+
+ @Override
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (level == 0) {
+ throw new XPathException("When writing to an XdmDestination, comment nodes are only allowed within a document or element node");
+ }
+ super.comment(chars, locationId, properties);
+ }
+
+ @Override
+ public void append(Item item, int locationId, int copyNamespaces) throws XPathException {
+ if (level == 0) {
+ throw new XPathException("When writing to an XdmDestination, atomic values are only allowed within a document or element node");
+ }
+ super.append(item, locationId, copyNamespaces);
+ }
+
+ }
+}
+
diff --git a/sf/saxon/s9api/XdmEmptySequence.java b/sf/saxon/s9api/XdmEmptySequence.java
new file mode 100644
index 0000000..91751f8
--- /dev/null
+++ b/sf/saxon/s9api/XdmEmptySequence.java
@@ -0,0 +1,52 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.value.EmptySequence;
+
+/**
+ * The class <tt>XdmEmptySequence</tt> represents an empty sequence in the XDM Data Model.
+ *
+ * <p>This is a singleton class: there is only one instance, which may be obtained
+ * using the {@link #getInstance} method.</p>
+ *
+ * <p>An empty sequence may also be represented by an {@link XdmValue} whose length happens to be zero.
+ * Applications should therefore not test to see whether an object is an instance of this class
+ * in order to decide whether it is empty.</p>
+ *
+ * <p>Note: in interfaces that expect an {@link XdmItem}, an empty sequence is represented by a
+ * Java null value.</p>
+ */
+
+public class XdmEmptySequence extends XdmValue {
+
+ private static XdmEmptySequence THE_INSTANCE = new XdmEmptySequence();
+
+ /**
+ * Return the singleton instance of this class
+ * @return an XdmValue representing an empty sequence
+ */
+
+ /*@NotNull*/ public static XdmEmptySequence getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private XdmEmptySequence() {
+ super(EmptySequence.getInstance());
+ }
+
+ /**
+ * Get the number of items in the sequence
+ * @return the number of items in the value - always zero
+ */
+
+ @Override
+ public int size() {
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/s9api/XdmExternalObject.java b/sf/saxon/s9api/XdmExternalObject.java
new file mode 100644
index 0000000..77c4b4b
--- /dev/null
+++ b/sf/saxon/s9api/XdmExternalObject.java
@@ -0,0 +1,82 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.StringConverter;
+import net.sf.saxon.type.ValidationException;
+import net.sf.saxon.value.ObjectValue;
+
+/**
+ * The class XdmExternalObject represents an XDM item that wraps an external (Java or .NET) object.
+ * As such, it is outside the scope of the XDM specification (but permitted as an extension).
+ *
+ * <p>In releases prior to 9.5, external objects in Saxon were represented as atomic values. From
+ * 9.5 they are represented as a fourth kind of item, alongside nodes, atomic values, and functions.</p>
+ *
+ */
+public class XdmExternalObject extends XdmItem {
+
+ protected XdmExternalObject(Object value) {
+ super(new ObjectValue(value));
+ }
+
+
+ /**
+ * Construct an atomic value given its lexical representation and the name of the required
+ * built-in atomic type.
+ * <p>This method cannot be used to construct values that are namespace-sensitive (QNames and Notations)</p>
+ * @param lexicalForm the value in the lexical space of the target data type. More strictly, the input
+ * value before the actions of the whitespace facet for the target data type are applied.
+ * @param type the required atomic type. This must either be one of the built-in
+ * atomic types defined in XML Schema, or a user-defined type whose definition appears
+ * in a schema that is known to the Processor. It must not be an abstract type.
+ * @throws net.sf.saxon.s9api.SaxonApiException if the type is unknown, or is not atomic, or is namespace-sensitive;
+ * or if the value supplied in <tt>lexicalForm</tt> is not in the lexical space of the specified atomic
+ * type.
+ */
+
+ public XdmExternalObject(String lexicalForm, ItemType type) throws SaxonApiException {
+ net.sf.saxon.type.ItemType it = type.getUnderlyingItemType();
+ if (!it.isPlainType()) {
+ throw new SaxonApiException("Requested type is not atomic");
+ }
+ if (((AtomicType)it).isAbstract()) {
+ throw new SaxonApiException("Requested type is an abstract type");
+ }
+ if (((AtomicType)it).isNamespaceSensitive()) {
+ throw new SaxonApiException("Requested type is namespace-sensitive");
+ }
+ try {
+ StringConverter converter = type.getConversionRules().getStringConverter((AtomicType)it);
+ setValue(converter.convertString(lexicalForm).asAtomic());
+ } catch (ValidationException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get the wrapped Java object
+ * @return the wrapped object
+ */
+
+ public Object getExternalObject() {
+ return ((ObjectValue)getUnderlyingValue()).getObject();
+ }
+
+ /**
+ * Get the result of converting the external value to a string.
+ */
+
+ public String toString() {
+ return getExternalObject().toString();
+ }
+
+
+}
+
diff --git a/sf/saxon/s9api/XdmFunctionItem.java b/sf/saxon/s9api/XdmFunctionItem.java
new file mode 100644
index 0000000..f0f1f29
--- /dev/null
+++ b/sf/saxon/s9api/XdmFunctionItem.java
@@ -0,0 +1,84 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceExtent;
+
+/**
+ * The class XdmFunctionItem represents a function item
+ */
+
+public class XdmFunctionItem extends XdmItem {
+
+ protected XdmFunctionItem(FunctionItem fi) {
+ super(fi);
+ }
+
+ /**
+ * Get the name of the function
+ * @return the function name, as a QName, or null for an anonymous inline function item
+ */
+
+ public QName getName() {
+ FunctionItem fi = (FunctionItem)getUnderlyingValue();
+ StructuredQName sq = fi.getFunctionName();
+ return (sq == null ? null : new QName(sq));
+ }
+
+ /**
+ * Get the arity of the function
+ * @return the arity of the function, that is, the number of arguments in the function's signature
+ */
+
+ public int getArity() {
+ FunctionItem fi = (FunctionItem)getUnderlyingValue();
+ return fi.getArity();
+ }
+
+ /**
+ * Determine whether the item is an atomic value
+ * @return false, the item is not an atomic value, it is a function item
+ */
+
+ @Override
+ public boolean isAtomicValue() {
+ return false;
+ }
+
+ /**
+ * Call the function
+ * @param arguments the values to be supplied as arguments to the function
+ * @param processor the s9api Processor
+ * @return the result of calling the function
+ */
+
+ public XdmValue call(XdmValue[] arguments, Processor processor) throws SaxonApiException {
+ if (arguments.length != getArity()) {
+ throw new SaxonApiException("Supplied " + arguments.length + " arguments, required " + getArity());
+ }
+ try {
+ FunctionItem fi = (FunctionItem)getUnderlyingValue();
+ Sequence[] argVals = new Sequence[arguments.length];
+ for (int i=0; i<arguments.length; i++) {
+ argVals[i] = arguments[i].getUnderlyingValue();
+ }
+ XPathContext context = processor.getUnderlyingConfiguration().getConversionContext();
+ Sequence result = fi.call(context, argVals);
+ Sequence se = SequenceExtent.makeSequenceExtent(result.iterate());
+ return XdmValue.wrap(se);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/s9api/XdmItem.java b/sf/saxon/s9api/XdmItem.java
new file mode 100644
index 0000000..b38e4d4
--- /dev/null
+++ b/sf/saxon/s9api/XdmItem.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * The class XdmItem represents an item in a sequence, as defined by the XDM data model.
+ * An item is either an atomic value or a node.
+ *
+ * <p>An item is a member of a sequence, but it can also be considered as a sequence
+ * (of length one) in its own right. <tt>XdmItem</tt> is a subtype of <tt>XdmValue</tt> because every
+ * Item in the XDM data model is also a value.</p>
+ *
+ * <p>It cannot be assumed that every sequence of length one will be represented by
+ * an <tt>XdmItem</tt>. It is quite possible for an <tt>XdmValue</tt> that is not an <tt>XdmItem</tt> to hold
+ * a singleton sequence.</p>
+ *
+ * <p>Saxon provides two concrete subclasses of <code>XdmItem</code>, namely
+ * {@link XdmNode} and {@link XdmAtomicValue}. Users must not attempt to create
+ * additional subclasses.</p>
+ */
+
+public abstract class XdmItem extends XdmValue {
+
+ // internal protected constructor
+
+ protected XdmItem() { }
+
+ /**
+ * Construct an XdmItem as a wrapper around an existing Saxon Item object
+ * @param item the Item object to be wrapped. This can be retrieved using the
+ * {@link #getUnderlyingValue} method.
+ * @throws NullPointerException if item is null
+ * @since 9.5 (previously a protected constructor)
+ */
+
+ public XdmItem(Item item) {
+ super(item);
+ }
+
+ // internal factory mathod to wrap an Item
+
+ /*@Nullable*/ protected static XdmItem wrapItem(Item item) {
+ return item == null ? null : (XdmItem)XdmValue.wrap(item);
+ }
+
+
+ /**
+ * Factory method to construct an atomic value given its lexical representation and the
+ * required item type
+ * @param value the lexical representation of the required value
+ * @param type the item type of the required value
+ * @return the constructed item
+ * @throws SaxonApiException if the supplied string is not in the lexical space of the target type, or
+ * if the target type is not atomic
+ * @deprecated since 9.1. This factory method duplicates the constructor
+ * {@link XdmAtomicValue#XdmAtomicValue(String, ItemType)} which should be used in preference
+ */
+
+ public static XdmItem newAtomicValue(String value, ItemType type) throws SaxonApiException {
+ return new XdmAtomicValue(value, type);
+ }
+
+ /**
+ * Get the string value of the item. For a node, this gets the string value
+ * of the node. For an atomic value, it has the same effect as casting the value
+ * to a string. In all cases the result is the same as applying the XPath string()
+ * function.
+ *
+ * <p>For atomic values, the result is the same as the result of calling
+ * <code>toString</code>. This is not the case for nodes, where <code>toString</code>
+ * returns an XML serialization of the node.</p>
+ *
+ * @return the result of converting the item to a string.
+ */
+
+ public String getStringValue() {
+ //noinspection RedundantCast
+ return ((Item)getUnderlyingValue()).getStringValue();
+ }
+
+ /**
+ * Determine whether the item is an atomic value or a node
+ * @return true if the item is an atomic value, false if it is a node
+ */
+
+ public boolean isAtomicValue() {
+ return ((Item)getUnderlyingValue()) instanceof AtomicValue;
+ }
+
+ /**
+ * Get the number of items in the sequence
+ * @return the number of items in the value - always one
+ */
+
+ @Override
+ public int size() {
+ return 1;
+ }
+
+}
+
diff --git a/sf/saxon/s9api/XdmNode.java b/sf/saxon/s9api/XdmNode.java
new file mode 100644
index 0000000..a4d6c30
--- /dev/null
+++ b/sf/saxon/s9api/XdmNode.java
@@ -0,0 +1,409 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.query.QueryResult;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.wrapper.VirtualNode;
+import net.sf.saxon.type.Type;
+
+import javax.xml.transform.Source;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * This class represents a node in the XDM data model. A Node is an {@link XdmItem}, and is therefore an
+ * {@link XdmValue} in its own right, and may also participate as one item within a sequence value.
+ * <p/>
+ * <p>An XdmNode is implemented as a wrapper around an object of type {@link net.sf.saxon.om.NodeInfo}.
+ * Because this is a key interface within Saxon, it is exposed via this API.</p>
+ * <p/>
+ * <p>The XdmNode interface exposes basic properties of the node, such as its name, its string value, and
+ * its typed value. Navigation to other nodes is supported through a single method, {@link #axisIterator},
+ * which allows other nodes to be retrieved by following any of the XPath axes.</p>
+ * <p/>
+ * <p>Note that node identity cannot be inferred from object identity. The same node may be represented
+ * by different <code>XdmNode</code> instances at different times, or even at the same time. The equals()
+ * method on this class can be used to test for node identity.</p>
+ * @since 9.0
+ */
+public class XdmNode extends XdmItem {
+
+ /**
+ * Construct an XdmNode as a wrapper around an existing NodeInfo object
+ * @param node the NodeInfo object to be wrapped. This can be retrieved using the
+ * {@link #getUnderlyingNode} method.
+ * @since 9.2 (previously a protected constructor)
+ */
+
+ public XdmNode(NodeInfo node) {
+ super(node);
+ }
+
+ /**
+ * Get the kind of node.
+ * @return the kind of node, for example {@link XdmNodeKind#ELEMENT} or {@link XdmNodeKind#ATTRIBUTE}
+ */
+
+ public XdmNodeKind getNodeKind() {
+ switch (getUnderlyingNode().getNodeKind()) {
+ case Type.DOCUMENT:
+ return XdmNodeKind.DOCUMENT;
+ case Type.ELEMENT:
+ return XdmNodeKind.ELEMENT;
+ case Type.ATTRIBUTE:
+ return XdmNodeKind.ATTRIBUTE;
+ case Type.TEXT:
+ return XdmNodeKind.TEXT;
+ case Type.COMMENT:
+ return XdmNodeKind.COMMENT;
+ case Type.PROCESSING_INSTRUCTION:
+ return XdmNodeKind.PROCESSING_INSTRUCTION;
+ case Type.NAMESPACE:
+ return XdmNodeKind.NAMESPACE;
+ default:
+ throw new IllegalStateException("nodeKind");
+ }
+ }
+
+ /**
+ * Get the name of the node, as a QName
+ *
+ * @return the name of the node. In the case of unnamed nodes (for example, text and comment nodes)
+ * return null.
+ */
+
+ /*@Nullable*/ public QName getNodeName() {
+ NodeInfo n = getUnderlyingNode();
+ switch (n.getNodeKind()) {
+ case Type.DOCUMENT:
+ case Type.TEXT:
+ case Type.COMMENT:
+ return null;
+ case Type.PROCESSING_INSTRUCTION:
+ case Type.NAMESPACE:
+ if (n.getLocalPart().isEmpty()) {
+ return null;
+ } else {
+ return new QName(new StructuredQName("", "", n.getLocalPart()));
+ }
+ case Type.ELEMENT:
+ case Type.ATTRIBUTE:
+ return new QName(n.getPrefix(), n.getURI(), n.getLocalPart());
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Get the typed value of this node, as defined in XDM
+ *
+ * @return the typed value. If the typed value is atomic, this will be returned as an instance
+ * of {@link XdmAtomicValue}
+ * @throws SaxonApiException if an error occurs obtaining the typed value, for example because
+ * the node is an element with element-only content
+ */
+
+ public XdmValue getTypedValue() throws SaxonApiException {
+ try {
+ Sequence v = getUnderlyingNode().atomize();
+ return XdmValue.wrap(v);
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get the line number of the node in a source document. For a document constructed using the document
+ * builder, this is available only if the line numbering option was set when the document was built (and
+ * then only for element nodes). If the line number is not available, the value -1 is returned. Line numbers
+ * will typically be as reported by a SAX parser: this means that the line number for an element node is the
+ * line number containing the closing ">" of the start tag.
+ * @return the line number of the node, or -1 if not available.
+ */
+
+ public int getLineNumber() {
+ return getUnderlyingNode().getLineNumber();
+ }
+
+ /**
+ * Get the column number of the node in a source document. For a document constructed using the document
+ * builder, this is available only if the line numbering option was set when the document was built (and
+ * then only for element nodes). If the column number is not available, the value -1 is returned. Column numbers
+ * will typically be as reported by a SAX parser: this means that the column number for an element node is the
+ * position of the closing ">" of the start tag.
+ * @return the column number of the node, or -1 if not available.
+ */
+
+ public int getColumnNumber() {
+ return getUnderlyingNode().getColumnNumber();
+ }
+
+ /**
+ * Get a JAXP Source object corresponding to this node, allowing the node to be
+ * used as input to transformations or queries.
+ *
+ * <p>The Source object that is returned will generally be one that is acceptable to Saxon interfaces
+ * that expect an instance of <code>javax.xml.transform.Source</code>. However, there is no guarantee
+ * that it will be recognized by other products.</p>
+ *
+ * <p><i>In fact, the current implementation always returns an instance of
+ * <code>net.sf.saxon.om.NodeInfo</code>.</i></p>
+ * @return a Source object corresponding to this node
+ */
+
+ public Source asSource() {
+ return getUnderlyingNode();
+ }
+
+ /**
+ * Get an iterator over the nodes reachable from this node via a given axis.
+ *
+ * @param axis identifies which axis is to be navigated
+ * @return an iterator over the nodes on the specified axis, starting from this node as the
+ * context node. The nodes are returned in axis order, that is, in document order for a forwards
+ * axis and in reverse document order for a reverse axis.
+ */
+
+ public XdmSequenceIterator axisIterator(Axis axis) {
+ AxisIterator base = getUnderlyingNode().iterateAxis(axis.getAxisNumber());
+ return new XdmSequenceIterator(base);
+ }
+
+ /**
+ * Get an iterator over the nodes reachable from this node via a given axis, selecting only
+ * those nodes with a specified name.
+ *
+ * @param axis identifies which axis is to be navigated
+ * @param name identifies the name of the nodes to be selected. The selected nodes will be those
+ * whose node kind is the principal node kind of the axis (that is, attributes for the attribute
+ * axis, namespaces for the namespace axis, and elements for all other axes) whose name matches
+ * the specified name.
+ * <p>For example, specify <code>new QName("", "item")</code> to select nodes with local name
+ * "item", in no namespace.</p>
+ * @return an iterator over the nodes on the specified axis, starting from this node as the
+ * context node. The nodes are returned in axis order, that is, in document order for a forwards
+ * axis and in reverse document order for a reverse axis.
+ */
+
+ public XdmSequenceIterator axisIterator(Axis axis, QName name) {
+ int kind;
+ switch (axis) {
+ case ATTRIBUTE:
+ kind = Type.ATTRIBUTE;
+ break;
+ case NAMESPACE:
+ kind = Type.NAMESPACE;
+ break;
+ default:
+ kind = Type.ELEMENT;
+ break;
+ }
+ NamePool pool = getUnderlyingNode().getNamePool();
+ int nameCode = pool.allocate("", name.getNamespaceURI(), name.getLocalName());
+ NameTest test = new NameTest(kind, nameCode, pool);
+ AxisIterator base = getUnderlyingNode().iterateAxis(axis.getAxisNumber(), test);
+ return new XdmSequenceIterator(base);
+ }
+
+ /**
+ * Get the parent of this node
+ *
+ * @return the parent of this node (a document or element node), or null if this node has no parent.
+ */
+
+ public XdmNode getParent() {
+ return (XdmNode) XdmValue.wrap(getUnderlyingNode().getParent());
+ }
+
+ /**
+ * Get the string value of a named attribute of this element
+ * @param name the name of the required attribute
+ * @return null if this node is not an element, or if this element has no
+ * attribute with the specified name. Otherwise return the string value of the
+ * selected attribute node.
+ */
+
+ public String getAttributeValue(QName name) {
+ NodeInfo node = getUnderlyingNode();
+ return node.getAttributeValue(name.getNamespaceURI(), name.getLocalName());
+ }
+
+ /**
+ * Get the base URI of this node
+ *
+ * @return the base URI, as defined in the XDM model. The value may be null if no base URI is known for the
+ * node, for example if the tree was built from a StreamSource with no associated URI, or if the node has
+ * no parent.
+ * @throws IllegalStateException if the base URI property of the underlying node is not a valid URI.
+ */
+
+ public URI getBaseURI() {
+ try {
+ String uri = getUnderlyingNode().getBaseURI();
+ if (uri == null) {
+ return null;
+ }
+ return new URI(uri);
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("baseURI", e);
+ }
+ }
+
+ /**
+ * Get the document URI of this node.
+ * @return the document URI, as defined in the XDM model. Returns null if no document URI is known
+ * @throws IllegalStateException if the document URI property of the underlying node is not a valid URI.
+ * @since 9.1
+ */
+
+ public URI getDocumentURI() {
+ try {
+ String systemId = getUnderlyingNode().getSystemId();
+ return (systemId == null || systemId.length() == 0 ? null : new URI(systemId));
+ } catch (URISyntaxException e) {
+ throw new IllegalStateException("documentURI", e);
+ }
+ }
+
+
+
+ /**
+ * The hashcode is such that two XdmNode instances have the same hashCode if they represent the same
+ * node. Note that the same node might be represented by different XdmNode objects, but these will
+ * compare equal.
+ *
+ * @return a hashCode representing node identity
+ */
+
+ public int hashCode() {
+ return getUnderlyingNode().hashCode();
+ }
+
+ /**
+ * The <code>equals()</code> relation between two XdmNode objects is true if they both represent the same
+ * node. That is, it corresponds to the "is" operator in XPath.
+ *
+ * @param other the object to be compared
+ * @return true if and only if the other object is an XdmNode instance representing the same node
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof XdmNode &&
+ getUnderlyingNode().isSameNodeInfo(((XdmNode) other).getUnderlyingNode());
+ }
+
+ /**
+ * The toString() method returns a simple XML serialization of the node
+ * with defaulted serialization parameters.
+ *
+ * <p>In the case of an element node, the result will be a well-formed
+ * XML document serialized as defined in the W3C XSLT/XQuery serialization specification,
+ * using options method="xml", indent="yes", omit-xml-declaration="yes".</p>
+ *
+ * <p>In the case of a document node, the result will be a well-formed
+ * XML document provided that the document node contains exactly one element child,
+ * and no text node children. In other cases it will be a well-formed external
+ * general parsed entity.</p>
+ *
+ * <p>In the case of an attribute node, the output is a string in the form
+ * <code>name="value"</code>. The name will use the original namespace prefix.</p>
+ *
+ * <p>In the case of a namespace node, the output is a string in the form of a namespace
+ * declaration, that is <code>xmlns="uri"</code> or <code>xmlns:pre="uri"</code>.</p>
+ *
+ * <p>Other nodes, such as text nodes, comments, and processing instructions, are
+ * represented as they would appear in lexical XML.</p>
+ *
+ * <p><i>For more control over serialization, use the {@link Serializer} class.</i></p>
+ *
+ * @return a simple XML serialization of the node. Under error conditions the method
+ * may return an error message which will always begin with the label "Error: ".
+ */
+
+ public String toString() {
+ NodeInfo node = getUnderlyingNode();
+
+ if (node.getNodeKind() == Type.ATTRIBUTE) {
+ String val = node.getStringValue().replace("\"", """);
+ val = val.replace("<", "<");
+ val = val.replace("&", "&");
+ return node.getDisplayName() + "=\"" + val + '"';
+ } else if (node.getNodeKind() == Type.NAMESPACE) {
+ String val = node.getStringValue().replace("\"", """);
+ val = val.replace("<", "<");
+ val = val.replace("&", "&");
+ String name = node.getDisplayName();
+ name = (name.equals("") ? "xmlns" : "xmlns:" + name);
+ return name + "=\"" + val + '"';
+ }
+
+ try {
+ return QueryResult.serialize(node);
+ } catch (XPathException err) {
+ throw new IllegalStateException(err);
+ }
+ }
+
+ /**
+ * Get the Processor that was used to construct this node.
+ * @return the Processor used to construct this node, either via a {@link DocumentBuilder},
+ * or by executing XSLT or XQuery code.
+ * @since 9.2
+ */
+
+ public Processor getProcessor() {
+ Object p = getUnderlyingNode().getConfiguration().getProcessor();
+ if (p instanceof Processor) {
+ return (Processor)p;
+ } else {
+ // I don't think this can happen, hence not a documented exception
+ throw new IllegalStateException("Node was not created using a s9api Processor");
+ }
+ }
+
+ /**
+ * Get the underlying Saxon implementation object representing this node. This provides
+ * access to classes and methods in the Saxon implementation that may be subject to change
+ * from one release to another.
+ *
+ * @return the underlying implementation object
+ */
+
+ public NodeInfo getUnderlyingNode() {
+ return (NodeInfo) getUnderlyingValue();
+ }
+
+ /**
+ * In the case of an XdmNode that wraps a node in an external object model such as DOM, JDOM,
+ * XOM, or DOM4J, get the underlying wrapped node
+ * @return the underlying external node if there is one, or null if this is not an XdmNode that
+ * wraps such an external node
+ * @since 9.1.0.2
+ */
+
+ public Object getExternalNode() {
+ NodeInfo saxonNode = getUnderlyingNode();
+ if (saxonNode instanceof VirtualNode) {
+ Object externalNode = ((VirtualNode)saxonNode).getRealNode();
+ return (externalNode instanceof NodeInfo ? null : externalNode);
+ } else {
+ return null;
+ }
+ }
+
+
+
+}
+
diff --git a/sf/saxon/s9api/XdmNodeKind.java b/sf/saxon/s9api/XdmNodeKind.java
new file mode 100644
index 0000000..53b8805
--- /dev/null
+++ b/sf/saxon/s9api/XdmNodeKind.java
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.type.Type;
+
+/**
+ * Enumeration class defining the seven kinds of node defined in the XDM model
+ */
+public enum XdmNodeKind {
+ DOCUMENT (Type.DOCUMENT),
+ ELEMENT (Type.ELEMENT),
+ ATTRIBUTE (Type.ATTRIBUTE),
+ TEXT (Type.TEXT),
+ COMMENT (Type.COMMENT),
+ PROCESSING_INSTRUCTION (Type.PROCESSING_INSTRUCTION),
+ NAMESPACE (Type.NAMESPACE);
+
+ private int number;
+ private XdmNodeKind(int number) {
+ this.number = number;
+ }
+ protected int getNumber() {
+ return number;
+ }
+}
+
diff --git a/sf/saxon/s9api/XdmSequenceIterator.java b/sf/saxon/s9api/XdmSequenceIterator.java
new file mode 100644
index 0000000..c91014e
--- /dev/null
+++ b/sf/saxon/s9api/XdmSequenceIterator.java
@@ -0,0 +1,113 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.Iterator;
+
+/**
+ * An iterator over an XPath sequence.
+ *
+ * <p>This class implements the standard Java Iterator interface.</p>
+ *
+ * <p>Because the <code>Iterator</code> interface does not define any checked
+ * exceptions, the <code>hasNext()</code> method of this iterator throws an unchecked
+ * exception if a dynamic error occurs while evaluating the expression. Applications
+ * wishing to control error handling should take care to catch this exception.</p>
+ */
+public class XdmSequenceIterator implements Iterator<XdmItem> {
+
+ /*@Nullable*/ private XdmItem next = null;
+ private int state = BEFORE_ITEM;
+ private SequenceIterator base;
+
+ private final static int BEFORE_ITEM = 0;
+ private final static int ON_ITEM = 1;
+ private final static int FINISHED = 2;
+
+ protected XdmSequenceIterator(SequenceIterator base) {
+ this.base = base;
+ this.state = BEFORE_ITEM;
+ }
+
+ /**
+ * Returns <tt>true</tt> if the iteration has more elements. (In other
+ * words, returns <tt>true</tt> if <tt>next</tt> would return an element
+ * rather than throwing an exception.)
+ *
+ * @return <tt>true</tt> if the iterator has more elements.
+ *
+ * @throws SaxonApiUncheckedException if a dynamic error occurs during XPath evaluation that
+ * is detected at this point.
+ */
+ public boolean hasNext() throws SaxonApiUncheckedException {
+ switch (state) {
+ case ON_ITEM:
+ return true;
+ case FINISHED:
+ return false;
+ case BEFORE_ITEM:
+ try {
+ next = XdmItem.wrapItem(base.next());
+ if (next == null) {
+ state = FINISHED;
+ return false;
+ } else {
+ state = ON_ITEM;
+ return true;
+ }
+ } catch (XPathException err) {
+ throw new SaxonApiUncheckedException(err);
+ }
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Returns the next element in the iteration. Calling this method
+ * repeatedly until the {@link #hasNext()} method returns false will
+ * return each element in the underlying collection exactly once.
+ *
+ * @return the next element in the iteration.
+ * @throws java.util.NoSuchElementException
+ * iteration has no more elements.
+ */
+ public XdmItem next() {
+ switch (state) {
+ case ON_ITEM:
+ state = BEFORE_ITEM;
+ return next;
+ case FINISHED:
+ throw new java.util.NoSuchElementException();
+ case BEFORE_ITEM:
+ if (hasNext()) {
+ state = BEFORE_ITEM;
+ return next;
+ } else {
+ throw new java.util.NoSuchElementException();
+ }
+ default:
+ throw new IllegalStateException();
+ }
+
+ }
+
+ /**
+ * Not supported on this implementation.
+ *
+ * @throws UnsupportedOperationException always
+ */
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/sf/saxon/s9api/XdmValue.java b/sf/saxon/s9api/XdmValue.java
new file mode 100644
index 0000000..4ab1b22
--- /dev/null
+++ b/sf/saxon/s9api/XdmValue.java
@@ -0,0 +1,215 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An value in the XDM data model. A value is a sequence of zero or more items,
+ * each item being either an atomic value or a node.
+ *
+ * <p>An XdmValue is immutable.</p>
+ *
+ * <p>A sequence consisting of a single item may be represented as an instance of {@link XdmItem},
+ * which is a subtype of XdmValue. However, there is no guarantee that a sequence of length one
+ * will always be an instance of XdmItem.</p>
+ *
+ * <p>Similarly, a zero-length sequence may be represented as an instance of {@link XdmEmptySequence},
+ * but there is no guarantee that every sequence of length zero will always be an instance of
+ * XdmEmptySequence.</p>
+ *
+ * @since 9.0
+ */
+
+public class XdmValue implements Iterable<XdmItem> {
+
+ private Sequence value; // this must be a materialized value
+
+ protected XdmValue() {
+ // must be followed by setValue()
+ }
+
+ /**
+ * Create an XdmValue as a sequence of XdmItem objects
+ * @param items a sequence of XdmItem objects. Note that if this is supplied as a list or similar
+ * collection, subsequent changes to the list/collection will have no effect on the XdmValue.
+ * @since 9.0.0.4
+ */
+
+ public XdmValue(Iterable<XdmItem> items) {
+ List<Item> values = new ArrayList<Item>();
+ for (XdmItem item : items) {
+ values.add((Item)item.getUnderlyingValue());
+ }
+ value = new SequenceExtent<Item>(values);
+ }
+
+ protected XdmValue(Sequence value) {
+ setValue(value);
+ }
+
+ protected void setValue(Sequence value) {
+ this.value = value;
+ }
+
+ /**
+ * Create an XdmValue that wraps an existing Saxon Sequence
+ * @param value the supplied Sequence (which may be a singleton Item),
+ * @return an XdmValue corresponding to the supplied Sequence. If the
+ * supplied value is null, an empty sequence is returned. If the supplied
+ * value is an atomic value, the result will be an instance of XdmAtomicValue.
+ * If the supplied value is a node, the result will be an instance of XdmNode.
+ * If the supplied value is a function item (including map items), the result will be an instance of
+ * XdmFunctionItem.
+ * @throws SaxonApiUncheckedException if the supplied Sequence is not yet fully evaluated, and evaluation
+ * of the underlying expression fails with a dynamic error.
+ * @since 9.5 (previously a protected method)
+ */
+
+ public static XdmValue wrap(Sequence value) {
+ if (value == null) {
+ return XdmEmptySequence.getInstance();
+ }
+ if (value instanceof NodeInfo) {
+ return new XdmNode((NodeInfo)value);
+ }
+ if (value instanceof AtomicValue) {
+ return new XdmAtomicValue((AtomicValue)value);
+ }
+ if (value instanceof ObjectValue) {
+ return new XdmExternalObject(((ObjectValue)value).getObject());
+ }
+ if (value instanceof SingletonItem) {
+ Item item = ((SingletonItem)value).asItem();
+ if (item instanceof NodeInfo) {
+ return new XdmNode((NodeInfo)item);
+ } else if (item instanceof AtomicValue) {
+ return new XdmAtomicValue((AtomicValue)item);
+ } else if (item instanceof FunctionItem) {
+ return new XdmFunctionItem((FunctionItem)item);
+ } else if (item instanceof ObjectValue) {
+ return new XdmExternalObject(((ObjectValue)item).getObject());
+ } else {
+ throw new AssertionError("Unknown kind of item");
+ }
+ }
+ try {
+ value = SequenceTool.toGroundedValue(value);
+ } catch (XPathException e) {
+ throw new SaxonApiUncheckedException(e);
+ }
+ if (value == null) {
+ return XdmEmptySequence.getInstance();
+ } else if (value instanceof NodeInfo) {
+ return new XdmNode((NodeInfo)value);
+ } else if (value instanceof AtomicValue) {
+ return new XdmAtomicValue((AtomicValue)value);
+ } else if (value instanceof EmptySequence) {
+ return XdmEmptySequence.getInstance();
+ } else if (value instanceof FunctionItem) {
+ return new XdmFunctionItem((FunctionItem)value);
+ } else if (value instanceof SingletonItem) {
+ return wrap(((SingletonItem)value).asItem());
+ } else {
+ return new XdmValue(value);
+ }
+ }
+
+ /**
+ * Create a new XdmValue by concatenating the contents of this XdmValue and another
+ * XdmValue. The two input XdmValue objects are unaffected by this operation.
+ * <p>Note: creating a sequence of N values by successive calls on this method
+ * takes time proportional to N-squared.</p>
+ * @param otherValue the value to be appended
+ * @return a new XdmValue containing the concatenation of the two input XdmValue objects
+ * @since 9.2
+ */
+
+ public XdmValue append(XdmValue otherValue) {
+ List<Item> values = new ArrayList<Item>();
+ for (XdmItem item : this) {
+ values.add((Item)item.getUnderlyingValue());
+ }
+ for (XdmItem item : otherValue) {
+ values.add((Item)item.getUnderlyingValue());
+ }
+ return new XdmValue(new SequenceExtent<Item>(values));
+ }
+
+ /**
+ * Get the number of items in the sequence
+ * @return the number of items in the value
+ * @throws SaxonApiUncheckedException if the value is lazily evaluated and the delayed
+ * evaluation fails with a dynamic error.
+ */
+
+ public int size() {
+ try {
+ return SequenceTool.getLength(value);
+ } catch (XPathException err) {
+ throw new SaxonApiUncheckedException(err);
+ }
+ }
+
+ /**
+ * Get the n'th item in the value, counting from zero.
+ * @param n the item that is required, counting the first item in the sequence as item zero
+ * @return the n'th item in the sequence making up the value, counting from zero
+ * @throws IndexOutOfBoundsException if n is less than zero or greater than or equal to the number
+ * of items in the value
+ * @throws SaxonApiUncheckedException if the value is lazily evaluated and the delayed
+ * evaluation fails with a dynamic error.
+ */
+
+ public XdmItem itemAt(int n) throws IndexOutOfBoundsException, SaxonApiUncheckedException {
+ if (n < 0 || n >= size()) {
+ throw new IndexOutOfBoundsException(""+n);
+ }
+ try {
+ Item item = SequenceTool.itemAt(value, n);
+ return (XdmItem)XdmItem.wrap(item);
+ } catch (XPathException e) {
+ throw new SaxonApiUncheckedException(e);
+ }
+ }
+
+ /**
+ * Get an iterator over the items in this value.
+ * @return an Iterator over the items in this value.
+ * @throws SaxonApiUncheckedException if the value is lazily evaluated and the delayed
+ * evaluation fails with a dynamic error.
+ */
+ public XdmSequenceIterator iterator() throws SaxonApiUncheckedException {
+ try {
+ Sequence v = getUnderlyingValue();
+ return new XdmSequenceIterator(v.iterate());
+ } catch (XPathException e) {
+ throw new SaxonApiUncheckedException(e);
+ }
+ }
+
+ /**
+ * Get the underlying implementation object representing the value. This method allows
+ * access to lower-level Saxon functionality, including classes and methods that offer
+ * no guarantee of stability across releases.
+ * @return the underlying implementation object representing the value
+ */
+
+ public Sequence getUnderlyingValue() {
+ return value;
+ }
+
+
+
+}
+
diff --git a/sf/saxon/s9api/XsltCompiler.java b/sf/saxon/s9api/XsltCompiler.java
new file mode 100644
index 0000000..9a84418
--- /dev/null
+++ b/sf/saxon/s9api/XsltCompiler.java
@@ -0,0 +1,350 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.expr.CollationMap;
+import net.sf.saxon.expr.sort.RuleBasedSubstringMatcher;
+import net.sf.saxon.expr.sort.SimpleCollation;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trace.XSLTTraceCodeInjector;
+import net.sf.saxon.trans.CompilerInfo;
+import net.sf.saxon.type.ValidationException;
+import net.sf.saxon.value.DecimalValue;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.URIResolver;
+import java.text.RuleBasedCollator;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An XsltCompiler object allows XSLT 2.0 stylesheets to be compiled. The compiler holds information that
+ * represents the static context for the compilation.
+ * <p/>
+ * <p>To construct an XsltCompiler, use the factory method {@link Processor#newXsltCompiler} on the Processor object.</p>
+ * <p/>
+ * <p>An XsltCompiler may be used repeatedly to compile multiple queries. Any changes made to the
+ * XsltCompiler (that is, to the static context) do not affect queries that have already been compiled.
+ * An XsltCompiler may be used concurrently in multiple threads, but it should not then be modified once
+ * initialized.</p>
+ *
+ * @since 9.0
+ */
+public class XsltCompiler {
+
+ private Processor processor;
+ private Configuration config;
+ private CompilerInfo compilerInfo;
+ private Map<QName, XdmValue> variableList = new HashMap<QName, XdmValue>();
+
+ /**
+ * Protected constructor. The public way to create an <tt>XsltCompiler</tt> is by using the factory method
+ * {@link Processor#newXsltCompiler} .
+ *
+ * @param processor the Saxon processor
+ */
+
+ protected XsltCompiler(Processor processor) {
+ this.processor = processor;
+ this.config = processor.getUnderlyingConfiguration();
+ compilerInfo = new CompilerInfo(config.getDefaultXsltCompilerInfo());
+ compilerInfo.setCollationMap(new CollationMap(config.getCollationMap()));
+ }
+
+ /**
+ * Get the Processor from which this XsltCompiler was constructed
+ *
+ * @return the Processor to which this XsltCompiler belongs
+ * @since 9.3
+ */
+
+ public Processor getProcessor() {
+ return processor;
+ }
+
+ /**
+ * Set the URIResolver to be used during stylesheet compilation. This URIResolver, despite its name,
+ * is <b>not</b> used for resolving relative URIs against a base URI; it is used for dereferencing
+ * an absolute URI (after resolution) to return a {@link javax.xml.transform.Source} representing the
+ * location where a stylesheet module can be found.
+ * <p/>
+ * <p>This URIResolver is used to dereference the URIs appearing in <code>xsl:import</code>,
+ * <code>xsl:include</code>, and <code>xsl:import-schema</code> declarations.
+ * It is not used at run-time for resolving requests to the <code>document()</code> or similar functions.</p>
+ *
+ * @param resolver the URIResolver to be used during stylesheet compilation.
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ compilerInfo.setURIResolver(resolver);
+ }
+
+
+ /**
+ * Set the non-static paramters as well as static ones.
+ *
+ * @param name the StructuredQName of the parameter
+ * @param value as a XdmValue of the parameter
+ */
+ public void setParameter(QName name, XdmValue value) {
+ variableList.put(name, value);
+ compilerInfo.setParameter(name.getStructuredQName(), value.getUnderlyingValue());
+ }
+
+ /**
+ * Get the URIResolver to be used during stylesheet compilation.
+ *
+ * @return the URIResolver used during stylesheet compilation. Returns null if no user-supplied
+ * URIResolver has been set.
+ */
+
+ public URIResolver getURIResolver() {
+ return compilerInfo.getURIResolver();
+ }
+
+ /**
+ * Set the ErrorListener to be used during this compilation episode
+ *
+ * @param listener The error listener to be used. This is notified of all errors detected during the
+ * compilation.
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ compilerInfo.setErrorListener(listener);
+ }
+
+ /**
+ * Get the ErrorListener being used during this compilation episode
+ *
+ * @return listener The error listener in use. This is notified of all errors detected during the
+ * compilation. Returns null if no user-supplied ErrorListener has been set.
+ */
+
+ public ErrorListener getErrorListener() {
+ return compilerInfo.getErrorListener();
+ }
+
+ /**
+ * Say that the stylesheet must be compiled to be schema-aware, even if it contains no
+ * xsl:import-schema declarations. Normally a stylesheet is treated as schema-aware
+ * only if it contains one or more xsl:import-schema declarations. If it is not schema-aware,
+ * then all input documents must be untyped, and validation of temporary trees is disallowed
+ * (though validation of the final result tree is permitted). Setting the argument to true
+ * means that schema-aware code will be compiled regardless.
+ *
+ * @param schemaAware If true, the stylesheet will be compiled with schema-awareness
+ * enabled even if it contains no xsl:import-schema declarations. If false, the stylesheet
+ * is treated as schema-aware only if it contains one or more xsl:import-schema declarations.
+ * @since 9.2
+ */
+
+ public void setSchemaAware(boolean schemaAware) {
+ compilerInfo.setSchemaAware(schemaAware);
+ }
+
+ /**
+ * Ask whether schema-awareness has been requested by means of a call on
+ * {@link #setSchemaAware}
+ *
+ * @return true if schema-awareness has been requested
+ * @since 9.2
+ */
+
+ public boolean isSchemaAware() {
+ return compilerInfo.isSchemaAware();
+ }
+
+ /**
+ * Bind a collation URI to a collation
+ *
+ * @param uri the absolute collation URI
+ * @param collation a {@link java.text.Collator} object that implements the required collation
+ * @throws IllegalArgumentException if an attempt is made to rebind the standard URI
+ * for the Unicode codepoint collation
+ * @since 9.5
+ */
+
+ public void declareCollation(String uri, final java.text.Collator collation) {
+ if (uri.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
+ throw new IllegalArgumentException("Cannot declare the Unicode codepoint collation URI");
+ }
+ StringCollator saxonCollation;
+ if (collation instanceof RuleBasedCollator) {
+ saxonCollation = new RuleBasedSubstringMatcher((RuleBasedCollator) collation);
+ } else {
+ saxonCollation = new SimpleCollation(collation);
+ }
+ compilerInfo.getCollationMap().setNamedCollation(uri, saxonCollation);
+ }
+
+ /**
+ * Declare the default collation
+ *
+ * @param uri the absolute URI of the default collation. This URI must have been bound to a collation
+ * using the method {@link #declareCollation(String, java.text.Collator)}
+ * @throws IllegalStateException if the collation URI has not been registered, unless it is the standard
+ * Unicode codepoint collation which is registered implicitly
+ * @since 9.5
+ */
+
+ public void declareDefaultCollation(String uri) {
+ if (compilerInfo.getCollationMap().getNamedCollation(uri) == null) {
+ throw new IllegalStateException("Unknown collation " + uri);
+ }
+ compilerInfo.getCollationMap().setDefaultCollationName(uri);
+ }
+
+ /**
+ * Set the XSLT (and XPath) language level to be supported by the processor.
+ *
+ * @param version the language level to be supported. The value 2.0 requests a processor conforming to the
+ * XSLT 2.0 specification; 3.0 requests an XSLT 3.0 processor, while the value "0.0" (which is the
+ * default setting in the absence of a call on this method) gets a processor according to the
+ * value of the version attribute on the xsl:stylesheet element.
+ * <p>Although this interface can be used to run a 1.0 stylesheet, it is not possible to request
+ * a 1.0 processor; Saxon will handle it as a 2.0 processor in backwards-compatibility mode, which
+ * is not quite the same thing.</p>
+ * <p>The value 2.1 is accepted temporarily as a synonym for 3.0</p>
+ * @throws IllegalArgumentException if the value is not numerically equal to 0.0, 2.0, or 3.0
+ * @since 9.3
+ */
+
+ public void setXsltLanguageVersion(String version) {
+ DecimalValue v;
+ try {
+ v = (DecimalValue) DecimalValue.makeDecimalValue(version, true).asAtomic();
+ } catch (ValidationException ve) {
+ throw new IllegalArgumentException(ve);
+ }
+ if (DecimalValue.TWO_POINT_ONE.equals(v)) {
+ v = DecimalValue.THREE;
+ }
+ if (!DecimalValue.TWO.equals(v) && !DecimalValue.THREE.equals(v)) {
+ throw new IllegalArgumentException("LanguageVersion " + v);
+ }
+ compilerInfo.setXsltVersion(v);
+ }
+
+ /**
+ * Get the XSLT (and XPath) language level supported by the processor.
+ *
+ * @return the language level supported. The value 2.0 represents a processor conforming to the
+ * XSLT 2.0 specification; 3.0 represents an XSLT 3.0 processor, while the value zero (which is the
+ * default setting in the absence of a call on {@link #setXsltLanguageVersion} represents a processor
+ * selected according to the value of the version attribute on the xsl:stylesheet element.
+ * @since 9.3
+ */
+
+ public String getXsltLanguageVersion() {
+ return compilerInfo.getXsltVersion().toString();
+ }
+
+ /**
+ * Set whether trace hooks are to be included in the compiled code. To use tracing, it is necessary
+ * both to compile the code with trace hooks included, and to supply a TraceListener at run-time
+ *
+ * @param option true if trace code is to be compiled in, false otherwise
+ * @since 9.3
+ */
+
+ public void setCompileWithTracing(boolean option) {
+ if (option) {
+ compilerInfo.setCodeInjector(new XSLTTraceCodeInjector());
+ } else {
+ compilerInfo.setCodeInjector(null);
+ }
+ }
+
+ /**
+ * Ask whether trace hooks are included in the compiled code.
+ *
+ * @return true if trace hooks are included, false if not.
+ * @since 9.3
+ */
+
+ public boolean isCompileWithTracing() {
+ return compilerInfo.isCompileWithTracing();
+ }
+
+// /**
+// * Import a compiled XQuery function library.
+// * @param queryCompiler An XQueryCompiler that has been used to compile a library of XQuery functions
+// * (by using one of the overloaded methods named <code>compileLibrary</code>).
+// * @param namespace The namespace of the functions that are to be made available to the stylesheet.
+// * All the global functions with this namespace that are
+// * defined in libraries that have been compiled using this XQueryCompiler are added to the static context
+// * of the XSLT stylesheet. The stylesheet does not need to (and should not) contain a call on
+// * <code>saxon:import-query</code> to import these functions.
+// */
+//
+// public void importXQueryLibrary(XQueryCompiler queryCompiler, String namespace) {
+// compilerInfo.importXQueryLibrary(queryCompiler.)
+// }
+
+ /**
+ * Compile a stylesheet.
+ * <p/>
+ * <p><i>Note: the term "compile" here indicates that the stylesheet is converted into an executable
+ * form. There is no implication that this involves code generation.</i></p>
+ * <p/>
+ * <p>The source argument identifies the XML document holding the principal stylesheet module. Other
+ * modules will be located relative to this module by resolving against the base URI that is defined
+ * as the systemId property of the supplied Source.</p>
+ * <p/>
+ * <p>The following kinds of {@link javax.xml.transform.Source} are recognized:</p>
+ * <p/>
+ * <ul>
+ * <li>{@link javax.xml.transform.stream.StreamSource}, allowing the stylesheet to be supplied as a
+ * URI, as a {@link java.io.File}, as an {@link java.io.InputStream}, or as a {@link java.io.Reader}</li>
+ * <li>{@link javax.xml.transform.sax.SAXSource}, allowing the stylesheet to be supplied as a stream
+ * of SAX events from a SAX2-compliant XML parser (or any other source of SAX events)</li>
+ * <li>{@link javax.xml.transform.dom.DOMSource}, allowing the stylesheet to be supplied as a
+ * DOM tree. This option is available only if saxon9-dom.jar is on the classpath.</li>
+ * <li>Document wrappers for XOM, JDOM, or DOM4J trees, provided the appropriate support libraries
+ * are on the classpath</li>
+ * <li>A Saxon NodeInfo, representing the root of a tree in any of the native tree formats supported
+ * by Saxon</li>
+ * <li>An {@link XdmNode} representing the document node of the stylesheet module</li>
+ * </ul>
+ *
+ * @param source Source object representing the principal stylesheet module to be compiled
+ * @return an XsltExecutable, which represents the compiled stylesheet.
+ * @throws SaxonApiException if the stylesheet contains static errors or if it cannot be read. Note that
+ * the exception that is thrown will <b>not</b> contain details of the actual errors found in the stylesheet. These
+ * will instead be notified to the registered ErrorListener. The default ErrorListener displays error messages
+ * on the standard error output.
+ */
+
+ public XsltExecutable compile(/*@NotNull*/ Source source) throws SaxonApiException {
+ try {
+ PreparedStylesheet pss = PreparedStylesheet.compile(source, config, compilerInfo);
+ return new XsltExecutable(processor, pss);
+ } catch (TransformerConfigurationException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get the underlying CompilerInfo object, which provides more detailed (but less stable) control
+ * over some compilation options
+ *
+ * @return the underlying CompilerInfo object, which holds compilation-time options. The methods on
+ * this object are not guaranteed stable from release to release.
+ */
+
+ public CompilerInfo getUnderlyingCompilerInfo() {
+ return compilerInfo;
+ }
+
+}
+
diff --git a/sf/saxon/s9api/XsltExecutable.java b/sf/saxon/s9api/XsltExecutable.java
new file mode 100644
index 0000000..a6c7b52
--- /dev/null
+++ b/sf/saxon/s9api/XsltExecutable.java
@@ -0,0 +1,158 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.expr.instruct.GlobalParam;
+import net.sf.saxon.expr.instruct.GlobalVariable;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.value.SequenceType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An XsltExecutable represents the compiled form of a stylesheet.
+ * To execute the stylesheet, it must first be loaded to form an {@link XsltTransformer}.
+ *
+ * <p>An XsltExecutable is immutable, and therefore thread-safe.
+ * It is simplest to load a new XsltTransformer each time the stylesheet is to be run.
+ * However, the XsltTransformer is serially reusable within a single thread. </p>
+ *
+ * <p>An XsltExecutable is created by using one of the <code>compile</code> methods on the
+ * {@link XsltCompiler} class.</p>
+ */
+public class XsltExecutable {
+
+ Processor processor;
+ PreparedStylesheet pss;
+
+ protected XsltExecutable(Processor processor, PreparedStylesheet pss) {
+ this.processor = processor;
+ this.pss = pss;
+ }
+
+ /**
+ * Load the stylesheet to prepare it for execution.
+ * @return An XsltTransformer. The returned XsltTransformer can be used to set up the
+ * dynamic context for stylesheet evaluation, and to run the stylesheet.
+ */
+
+ public XsltTransformer load() {
+ return new XsltTransformer(processor, (Controller)pss.newTransformer());
+ }
+
+ /**
+ * Produce a diagnostic representation of the compiled stylesheet, in XML form.
+ * <p><i>The detailed form of this representation is not stable (or even documented).<i></p>
+ * @param destination the destination for the XML document containing the diagnostic representation
+ * of the compiled stylesheet
+ * @since 9.1
+ */
+
+ public void explain(Destination destination) throws SaxonApiException {
+ Configuration config = processor.getUnderlyingConfiguration();
+ pss.explain(new ExpressionPresenter(config, destination.getReceiver(config)));
+ }
+
+ /**
+ * Get the whitespace stripping policy defined by this stylesheet, that is, the policy
+ * defined by the xsl:strip-space and xsl:preserve-space elements in the stylesheet
+ * @return a newly constructed WhitespaceStrippingPolicy based on the declarations in this
+ * stylesheet. This policy can be used as input to a {@link DocumentBuilder}.
+ */
+
+ public WhitespaceStrippingPolicy getWhitespaceStrippingPolicy() {
+ return new WhitespaceStrippingPolicy(pss);
+ }
+
+ /**
+ * Get the names of the xsl:param elements defined in this stylesheet, with details
+ * of each parameter including its required type, and whether it is required or optional
+ * @return a HashMap whose keys are the names of global parameters in the stylesheet,
+ * and whose values are {@link ParameterDetails} objects giving information about the
+ * corresponding parameter.
+ * @since 9.3
+ */
+
+ /*@NotNull*/ public HashMap<QName, ParameterDetails> getGlobalParameters() {
+ HashMap<StructuredQName, GlobalVariable> globals = pss.getCompiledGlobalVariables();
+ HashMap<QName, ParameterDetails> params = new HashMap<QName, ParameterDetails>(globals.size());
+ for (Map.Entry<StructuredQName, GlobalVariable> e : globals.entrySet()) {
+ StructuredQName name = e.getKey();
+ GlobalVariable var = e.getValue();
+ if (var instanceof GlobalParam) {
+ ParameterDetails details = new ParameterDetails(var.getRequiredType(), var.isRequiredParam());
+ params.put(new QName(name), details);
+ }
+ }
+ return params;
+ }
+
+ /**
+ * Inner class containing information about a global parameter to a compiled stylesheet
+ * @since 9.3
+ */
+
+ public class ParameterDetails {
+
+ private SequenceType type;
+ private boolean isRequired;
+
+ protected ParameterDetails(SequenceType type, boolean isRequired) {
+ this.type = type;
+ this.isRequired = isRequired;
+ }
+
+ /**
+ * Get the declared item type of the parameter
+ * @return the type defined in the <code>as</code> attribute of the <code>xsl:param</code> element,
+ * without its occurrence indicator
+ */
+
+ public ItemType getDeclaredItemType() {
+ return new ConstructedItemType(type.getPrimaryType(), processor);
+ }
+
+ /**
+ * Get the declared cardinality of the parameter
+ * @return the occurrence indicator from the type appearing in the <code>as</code> attribute
+ * of the <code>xsl:param</code> element
+ */
+
+ public OccurrenceIndicator getDeclaredCardinality() {
+ return OccurrenceIndicator.getOccurrenceIndicator(type.getCardinality());
+ }
+
+ /**
+ * Ask whether the parameter is required (mandatory) or optional
+ * @return true if the parameter is mandatory (<code>required="yes"</code>), false
+ * if it is optional
+ */
+
+ public boolean isRequired() {
+ return this.isRequired;
+ }
+ }
+
+
+ /**
+ * Get the underlying implementation object representing the compiled stylesheet. This provides
+ * an escape hatch into lower-level APIs. The object returned by this method may change from release
+ * to release.
+ * @return the underlying implementation of the compiled stylesheet
+ */
+
+ public PreparedStylesheet getUnderlyingCompiledStylesheet() {
+ return pss;
+ }
+}
+
diff --git a/sf/saxon/s9api/XsltTransformer.java b/sf/saxon/s9api/XsltTransformer.java
new file mode 100644
index 0000000..74d9467
--- /dev/null
+++ b/sf/saxon/s9api/XsltTransformer.java
@@ -0,0 +1,554 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.s9api;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Properties;
+
+/**
+ * An <code>XsltTransformer</code> represents a compiled and loaded stylesheet ready for execution.
+ * The <code>XsltTransformer</code> holds details of the dynamic evaluation context for the stylesheet.
+ * <p/>
+ * <p>An <code>XsltTransformer</code> must not be used concurrently in multiple threads.
+ * It is safe, however, to reuse the object within a single thread to run the same
+ * stylesheet several times. Running the stylesheet does not change the context
+ * that has been established.</p>
+ * <p/>
+ * <p>An <code>XsltTransformer</code> is always constructed by running the <code>Load</code>
+ * method of an {@link XsltExecutable}.</p>
+ * <p/>
+ * <p>An <code>XsltTransformer</code> is itself a <code>Destination</code>. This means it is possible to use
+ * one <code>XsltTransformer</code> as the destination to receive the results of another transformation,
+ * this providing a simple way for transformations to be chained into a pipeline. Note however that a
+ * when the input to a transformation is supplied in this way, it will always be built as a tree in
+ * memory, rather than the transformation being streamed.</p>
+ *
+ * @since 9.0
+ */
+public class XsltTransformer implements Destination {
+
+ // TODO: when input is piped into an XsltTransformer, do a streamed transformation where appropriate.
+
+ private Processor processor;
+ private Controller controller;
+ /*@Nullable*/ private Source initialSource;
+ private Destination destination;
+ private Builder sourceTreeBuilder;
+ boolean baseOutputUriWasSet = false;
+
+ /**
+ * Protected constructor
+ *
+ * @param processor the S9API processor
+ * @param controller the Saxon controller object
+ */
+
+ protected XsltTransformer(Processor processor, Controller controller) {
+ this.processor = processor;
+ this.controller = controller;
+ }
+
+ /**
+ * Set the initial named template for the transformation
+ *
+ * @param templateName the name of the initial template, or null to indicate
+ * that there should be no initial named template
+ * @throws SaxonApiException if there is no named template with this name
+ */
+
+ public void setInitialTemplate(QName templateName) throws SaxonApiException {
+ try {
+ controller.setInitialTemplate(
+ templateName == null ? null : templateName.getClarkName());
+ } catch (XPathException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ /**
+ * Get the initial named template for the transformation, if one has been set
+ *
+ * @return the name of the initial template, or null if none has been set
+ */
+
+ public QName getInitialTemplate() {
+ String template = controller.getInitialTemplate();
+ return (template == null ? null : QName.fromClarkName(template));
+ }
+
+ /**
+ * Set the initial mode for the transformation
+ *
+ * @param modeName the name of the initial mode, or null to indicate the default
+ * (unnamed) mode
+ */
+
+ public void setInitialMode(QName modeName) {
+ controller.setInitialMode(modeName == null ? null : modeName.getClarkName());
+ }
+
+ /**
+ * Get the name of the initial mode for the transformation, if one has been set.
+ *
+ * @return the initial mode for the transformation. Returns null if no mode has been set,
+ * or if the mode was set to null to represent the default (unnamed) mode
+ */
+
+ public QName getInitialMode() {
+ String mode = controller.getInitialModeName();
+ if (mode == null) {
+ return null;
+ } else {
+ return QName.fromClarkName(mode);
+ }
+ }
+
+ /**
+ * Set the schema validation mode for the transformation. This indicates how source documents
+ * loaded specifically for this transformation will be handled. This applies to the
+ * principal source document if supplied as a SAXSource or StreamSource, and to all
+ * documents loaded during the transformation using the <code>doc()</code>, <code>document()</code>,
+ * or <code>collection()</code> functions.
+ *
+ * @param mode the validation mode. Passing null causes no change to the existing value.
+ * Passing {@link ValidationMode#DEFAULT} resets to the initial value, which determines
+ * the validation requirements from the Saxon Configuration.
+ */
+
+ public void setSchemaValidationMode(ValidationMode mode) {
+ if (mode != null) {
+ controller.setSchemaValidationMode(mode.getNumber());
+ }
+ }
+
+ /**
+ * Get the schema validation mode for the transformation. This indicates how source documents
+ * loaded specifically for this transformation will be handled. This applies to the
+ * principal source document if supplied as a SAXSource or StreamSource, and to all
+ * documents loaded during the transformation using the <code>doc()</code>, <code>document()</code>,
+ * or <code>collection()</code> functions.
+ *
+ * @return the validation mode.
+ */
+
+ public ValidationMode getSchemaValidationMode() {
+ return ValidationMode.get(controller.getSchemaValidationMode());
+ }
+
+ /**
+ * Set the source document for the transformation.
+ * <p/>
+ * <p>If the source is an instance of {@link net.sf.saxon.om.NodeInfo}, the supplied node is used
+ * directly as the initial context item of the transformation.</p>
+ * <p/>
+ * <p>If the source is an instance of {@link javax.xml.transform.dom.DOMSource}, the DOM node identified
+ * by the DOMSource is wrapped as a Saxon node, and this is then used as the context item</p>
+ * <p/>
+ * <p>In other cases a new Saxon tree will be built by the transformation engine when the
+ * transformation starts, unless it is a Saxon-EE streaming transformation, in which case the
+ * document is processed in streaming fashion as it is being parsed.</p>
+ * <p/>
+ * <p>To run a transformation in streaming mode, the source should be supplied as an instance
+ * of {@link javax.xml.transform.stream.StreamSource}, {@link javax.xml.transform.sax.SAXSource},
+ * or {@link net.sf.saxon.event.Transmitter}.</p>
+ *
+ * @param source the principal source document for the transformation
+ * @throws SaxonApiException if for example the Source is an unrecognized type of source
+ */
+
+ public void setSource(Source source) throws SaxonApiException {
+ if (source instanceof NodeInfo) {
+ setInitialContextNode(new XdmNode((NodeInfo) source));
+ } else if (source instanceof DOMSource) {
+ setInitialContextNode(processor.newDocumentBuilder().wrap(source));
+ } else {
+ initialSource = source;
+ }
+ }
+
+ /**
+ * Set the initial context node for the transformation.
+ * <p>This is ignored in the case where the {@link XsltTransformer} is used as the
+ * {@link Destination} of another process. In that case the initial context node will always
+ * be the document node of the document that is being streamed to this destination.</p>
+ * <p>Calling this method has the side-effect of setting the initial source to null.</p>
+ *
+ * @param node the initial context node, or null if there is to be no initial context node
+ */
+
+ public void setInitialContextNode(XdmNode node) {
+ initialSource = (node == null ? null : node.getUnderlyingNode());
+ }
+
+ /**
+ * Get the initial context node for the transformation, if one has been set
+ *
+ * @return the initial context node, or null if none has been set. This will not necessarily
+ * be the same {@link XdmNode} instance as was supplied, but it will be an XdmNode object that represents
+ * the same underlying node.
+ */
+
+ public XdmNode getInitialContextNode() {
+ if (initialSource instanceof NodeInfo) {
+ return (XdmNode) XdmValue.wrap(((NodeInfo) initialSource));
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set the value of a stylesheet parameter
+ *
+ * @param name the name of the stylesheet parameter, as a QName
+ * @param value the value of the stylesheet parameter, or null to clear a previously set value
+ */
+
+ public void setParameter(QName name, XdmValue value) {
+ controller.setParameter(name.getStructuredQName(),
+ (value == null ? null : value.getUnderlyingValue()));
+ }
+
+ /**
+ * Get the value that has been set for a stylesheet parameter
+ *
+ * @param name the parameter whose name is required
+ * @return the value that has been set for the parameter, or null if no value has been set
+ */
+
+ public XdmValue getParameter(QName name) {
+ Object oval = controller.getParameter(name.getClarkName());
+ if (oval == null) {
+ return null;
+ }
+ if (oval instanceof Sequence) {
+ return XdmValue.wrap((Sequence) oval);
+ }
+ throw new IllegalStateException(oval.getClass().getName());
+ }
+
+ /**
+ * Set the destination to be used for the result of the transformation.
+ * <p>This method can be used to chain transformations into a pipeline, by using one
+ * {@link XsltTransformer} as the destination of another</p>
+ * <p>If the destination is a {@link Serializer}, then a side-effect of this method is to set
+ * the base output URI for the transformation. This acts as the base URI for resolving
+ * the <code>href</code> attribute of any <code>xsl:result-document</code> instruction.
+ * The serialiation parameters defined in the <code>Serializer</code> override any
+ * serialization parameters defined using <code>xsl:output</code> for the principal
+ * output of the transformation. However, they have no effect on any output produced
+ * using <code>xsl:result-document</code>.
+ *
+ * @param destination the destination to be used
+ */
+
+ public void setDestination(Destination destination) {
+ this.destination = destination;
+ }
+
+ /**
+ * Get the destination that was specified in a previous call of {@link #setDestination}
+ *
+ * @return the destination, or null if none has been supplied
+ */
+
+ public Destination getDestination() {
+ return destination;
+ }
+
+ /**
+ * Set the base output URI.
+ * <p/>
+ * <p>This defaults to the system ID of the Destination for the principal output
+ * of the transformation if this is known; if it is not known, it defaults
+ * to the current directory.</p>
+ * <p/>
+ * <p>If no base output URI is supplied, but the <code>Destination</code> of the transformation
+ * is a <code>Serializer</code> that writes to a file, then the URI of this file is used as
+ * the base output URI.</p>
+ * <p/>
+ * <p> The base output URI is used for resolving relative URIs in the <code>href</code> attribute
+ * of the <code>xsl:result-document</code> instruction.</p>
+ *
+ * @param uri the base output URI
+ * @since 9.2
+ */
+
+ public void setBaseOutputURI(String uri) {
+ controller.setBaseOutputURI(uri);
+ baseOutputUriWasSet = (uri != null);
+ }
+
+ /**
+ * Get the base output URI.
+ * <p/>
+ * <p>This returns the value set using the {@link #setBaseOutputURI} method. If no value has been set
+ * explicitly, then the method returns null if called before the transformation, or the computed
+ * default base output URI if called after the transformation.</p>
+ * <p/>
+ * <p> The base output URI is used for resolving relative URIs in the <code>href</code> attribute
+ * of the <code>xsl:result-document</code> instruction.</p>
+ *
+ * @return the base output URI
+ * @since 9.2
+ */
+
+ public String getBaseOutputURI() {
+ return controller.getBaseOutputURI();
+ }
+
+ /**
+ * Set an object that will be used to resolve URIs used in
+ * fn:doc() and related functions.
+ *
+ * @param resolver An object that implements the URIResolver interface, or
+ * null.
+ * @since 9.3
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ controller.setURIResolver(resolver);
+ }
+
+ /**
+ * Get the URI resolver.
+ *
+ * @return the user-supplied URI resolver if there is one, or null otherwise
+ * @since 9.3
+ */
+
+ public URIResolver getURIResolver() {
+ return controller.getURIResolver();
+ }
+
+
+ /**
+ * Set the ErrorListener to be used during this transformation
+ *
+ * @param listener The error listener to be used. This is notified of all dynamic errors detected during the
+ * transformation.
+ * @since 9.2
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ controller.setErrorListener(listener);
+ }
+
+ /**
+ * Get the ErrorListener being used during this compilation episode
+ *
+ * @return listener The error listener in use. This is notified of all dynamic errors detected during the
+ * transformation. If no user-supplied ErrorListener has been set the method will return a system-supplied
+ * ErrorListener.
+ * @since 9.2
+ */
+
+ public ErrorListener getErrorListener() {
+ return controller.getErrorListener();
+ }
+
+ /**
+ * Set the MessageListener to be notified whenever the stylesheet evaluates an
+ * <code>xsl:message</code> instruction. If no MessageListener is nominated,
+ * the output of <code>xsl:message</code> instructions will be serialized and sent
+ * to the standard error stream.
+ *
+ * @param listener the MessageListener to be used
+ * @since 9.1
+ */
+
+ public void setMessageListener(MessageListener listener) {
+ controller.setMessageEmitter(new MessageListenerProxy(listener, controller.makePipelineConfiguration()));
+ }
+
+ /**
+ * Get the MessageListener to be notified whenever the stylesheet evaluates an
+ * <code>xsl:message</code> instruction. If no MessageListener has been nominated,
+ * return null
+ *
+ * @return the user-supplied MessageListener, or null if none has been supplied
+ * @since 9.1
+ */
+
+ public MessageListener getMessageListener() {
+ Receiver r = controller.getMessageEmitter();
+ if (r instanceof MessageListenerProxy) {
+ return ((MessageListenerProxy) r).getMessageListener();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set a TraceListener to be notified of all events occurring during the transformation.
+ * This will only be effective if the stylesheet was compiled with trace code enabled
+ * (see {@link XsltCompiler#setCompileWithTracing(boolean)})
+ *
+ * @param listener the TraceListener to be used. Note that the TraceListener has access to
+ * interal Saxon interfaces which may vary from one release to the next. It is also possible that
+ * the TraceListener interface itself may be changed in future releases.
+ * @since 9.2
+ */
+
+ public void setTraceListener(TraceListener listener) {
+ controller.setTraceListener(listener);
+ }
+
+ /**
+ * Get the TraceListener to be notified of all events occurring during the transformation.
+ * If no TraceListener has been nominated, return null
+ *
+ * @return the user-supplied TraceListener, or null if none has been supplied
+ * @since 9.2
+ */
+
+ public TraceListener getTraceListener() {
+ return controller.getTraceListener();
+ }
+
+ /**
+ * Perform the transformation. If this method is used, a destination must have been supplied
+ * previously
+ *
+ * @throws SaxonApiException if any dynamic error occurs during the transformation
+ * @throws IllegalStateException if no destination has been supplied
+ */
+
+ public void transform() throws SaxonApiException {
+ if (destination == null) {
+ throw new IllegalStateException("No destination has been supplied");
+ }
+ if (baseOutputUriWasSet &&
+ destination instanceof XdmDestination &&
+ ((XdmDestination)destination).getBaseURI() == null &&
+ controller.getBaseOutputURI() != null
+ ) {
+ try {
+ ((XdmDestination)destination).setBaseURI(new URI(controller.getBaseOutputURI()));
+ } catch (URISyntaxException e) {
+ // no action
+ }
+ }
+ try {
+ controller.transform(initialSource, getDestinationResult());
+ destination.close();
+ } catch (TransformerException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+
+ private Result getDestinationResult() throws SaxonApiException {
+ if (destination instanceof Serializer) {
+ Properties props = ((Serializer) destination).getOutputProperties();
+ controller.setOutputProperties(props);
+ if (!baseOutputUriWasSet) {
+ Object dest = ((Serializer) destination).getOutputDestination();
+ if (dest instanceof File) {
+ controller.setBaseOutputURI(((File) dest).toURI().toString());
+ }
+ }
+ return ((Serializer)destination).getResult();
+ } else {
+ return destination.getReceiver(controller.getConfiguration());
+ }
+ }
+
+ /**
+ * Return a Receiver which can be used to supply the principal source document for the transformation.
+ * This method is intended primarily for internal use, though it can also
+ * be called by a user application that wishes to feed events into the transformation engine.
+ * <p/>
+ * <p>Saxon calls this method to obtain a Receiver, to which it then sends
+ * a sequence of events representing the content of an XML document. This method is provided so that
+ * <code>XsltTransformer</code> implements <code>Destination</code>, allowing one transformation
+ * to receive the results of another in a pipeline.</p>
+ * <p/>
+ * <p>Before calling this method, the {@link #setDestination} method must be called to supply a destination
+ * for the transformation.</p>
+ * <p/>
+ * <p>Note that when an <code>XsltTransformer</code> is used as a <code>Destination</code>, the initial
+ * context node set on that <code>XsltTransformer</code> using {@link #setInitialContextNode(XdmNode)} is ignored,
+ * as is the source set using {@link #setSource(javax.xml.transform.Source)}.</p>
+ *
+ * @param config The Saxon configuration. This is supplied so that the destination can
+ * use information from the configuration (for example, a reference to the name pool)
+ * to construct or configure the returned Receiver.
+ * @return the Receiver to which events are to be sent.
+ * @throws SaxonApiException if the Receiver cannot be created
+ * @throws IllegalStateException if no Destination has been supplied
+ */
+
+ public Receiver getReceiver(Configuration config) throws SaxonApiException {
+ if (destination == null) {
+ throw new IllegalStateException("No destination has been supplied");
+ }
+ if (controller.getInitialMode().isStreamable()) {
+ try {
+ return controller.getStreamingReceiver(controller.getInitialMode(), getDestinationResult());
+ } catch (TransformerException e) {
+ throw new SaxonApiException(e);
+ }
+ } else {
+ sourceTreeBuilder = controller.makeBuilder();
+ Receiver stripper = controller.makeStripper(sourceTreeBuilder);
+ if (controller.getExecutable().stripsInputTypeAnnotations()) {
+ stripper = controller.getConfiguration().getAnnotationStripper(stripper);
+ }
+ return stripper;
+ }
+ }
+
+ /**
+ * Close this destination, allowing resources to be released. Used when this XsltTransformer is acting
+ * as the destination of another transformation. Saxon calls this method when it has finished writing
+ * to the destination.
+ */
+
+ public void close() throws SaxonApiException {
+ if (sourceTreeBuilder != null) {
+ NodeInfo doc = sourceTreeBuilder.getCurrentRoot();
+ //sourceTreeBuilder.reset();
+ sourceTreeBuilder = null;
+ if (doc == null) {
+ throw new SaxonApiException("No source document has been built by the previous pipeline stage");
+ }
+ Result result = getDestinationResult();
+ try {
+ controller.transformDocument(doc, result);
+ } catch (TransformerException e) {
+ throw new SaxonApiException(e);
+ }
+ destination.close();
+ }
+ }
+
+ /**
+ * Get the underlying Controller used to implement this XsltTransformer. This provides access
+ * to lower-level methods not otherwise available in the s9api interface. Note that classes
+ * and methods obtained by this route cannot be guaranteed stable from release to release.
+ *
+ * @since 9.0.0.4
+ */
+
+ public Controller getUnderlyingController() {
+ return controller;
+ }
+}
+
diff --git a/sf/saxon/s9api/package.html b/sf/saxon/s9api/package.html
new file mode 100644
index 0000000..f51dd9e
--- /dev/null
+++ b/sf/saxon/s9api/package.html
@@ -0,0 +1,60 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.s9api</title>
+</head>
+
+<body>
+
+<p>This package provides Saxon's preferred Java API for XSLT, XQuery, XPath, and XML Schema processing.
+ The interface is designed to hide as much as possible of the detail of the
+implementation. However, the API architecture faithfully reflects the internal architecture
+of the Saxon product, unlike standard APIs such as JAXP and XQJ which in many cases force
+compromises in the design and performance of the application.</p>
+
+<p>An application starts by loading a {@link net.sf.saxon.s9api.Processor}, which allows configuration options
+to be set. As far as possible, an application should instantiate a single <code>Processor</code>.</p>
+
+<p>The interfaces for XSLT, XQuery, and XPath processing all follow the same pattern. There is a three-stage
+execution model: first a compiler is created using a factory method in the <code>Processor</code> object.
+The compiler holds compile-time options and the static context information. Then the compiler's
+<code>compile()</code> method is called to create an executable, a representation of the compiled stylesheet,
+query, or expression. This is thread-safe and immutable once created. To run the query or transformation,
+first call the <code>load()</code> method to create a run-time object called variously an {@link net.sf.saxon.s9api.XsltTransformer},
+{@link net.sf.saxon.s9api.XQueryEvaluator}, or {@link net.sf.saxon.s9api.XPathSelector}. This holds run-time context information
+such as parameter settings and the initial context node; the object is therefore not shareable and should
+only be run in a single thread; indeed it should normally only be used once. This object also provides
+methods allowing the transformation or query to be executed.</p>
+
+<p>In Saxon-EE the <code>Processor</code> owns a {@link net.sf.saxon.s9api.SchemaManager} that holds
+the cache of schema components and can be used to load new schema components from source schema documents.
+It can also be used to create a {@link net.sf.saxon.s9api.SchemaValidator}, which in turn is used to validate instance
+documents.</p>
+
+<p>Source documents can be constructed using a {@link net.sf.saxon.s9api.DocumentBuilder}, which holds all the options
+and parameters to control document building.</p>
+
+<p>The output of a transformation, or of any other process that generates an XML tree, can be sent to a
+{@link net.sf.saxon.s9api.Destination}. There are a number of implementations of this interface, including a
+{@link net.sf.saxon.s9api.Serializer} which translates the XML document tree into lexical XML form.</p>
+
+<p>There are classes to represent the objects of the XDM data model, including {@link net.sf.saxon.s9api.XdmValue},
+{@link net.sf.saxon.s9api.XdmItem}, {@link net.sf.saxon.s9api.XdmNode}, and {@link net.sf.saxon.s9api.XdmAtomicValue}.</p>
+
+<p>The s9api interface is designed to take advantage of Java 5 constructs such as generics and
+iterables. </p>
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+30 July 2010</i></p>
+</body>
+</html>
diff --git a/sf/saxon/serialize/AttributeSorter.java b/sf/saxon/serialize/AttributeSorter.java
new file mode 100644
index 0000000..14e8528
--- /dev/null
+++ b/sf/saxon/serialize/AttributeSorter.java
@@ -0,0 +1,157 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.sort.GenericSorter;
+import net.sf.saxon.expr.sort.Sortable;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * AttributeSorter: This filter sorts attributes into the order requested using the
+ * saxon:attribute-order serialization property
+*
+* @author Michael Kay
+*/
+
+
+public class AttributeSorter extends ProxyReceiver implements Sortable {
+
+ private Map<NodeName, Integer> knownAttributes; // mapping of named attributes to relative position
+ private AttributeCollectionImpl attributes;
+
+ /**
+ * Create a CDATA Filter
+ * @param next the next receiver in the pipeline
+ */
+
+ public AttributeSorter(Receiver next) {
+ super(next);
+ }
+
+ /**
+ * Set the properties for this CDATA filter
+ * @param details the output properties
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void setOutputProperties (/*@NotNull*/ Properties details)
+ throws XPathException {
+ String attOrder = details.getProperty(SaxonOutputKeys.ATTRIBUTE_ORDER);
+ if (attOrder == null) {
+ attOrder = ""; // just in case
+ }
+ knownAttributes = new HashMap<NodeName, Integer>();
+ int pos = 0;
+ StringTokenizer st2 = new StringTokenizer(attOrder, " \t\n\r", false);
+ while (st2.hasMoreTokens()) {
+ String expandedName = st2.nextToken();
+ StructuredQName sq = StructuredQName.fromClarkName(expandedName);
+ knownAttributes.put(new FingerprintedQName("", sq.getURI(), sq.getLocalPart()), pos++);
+ }
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param elemName integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties properties of the element node
+ */
+ @Override
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ attributes = new AttributeCollectionImpl(getConfiguration());
+ super.startElement(elemName, typeCode, locationId, properties);
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ * @param nameCode The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+ @Override
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ attributes.addAttribute(nameCode, typeCode, value.toString(), locationId, properties);
+ }
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+ @Override
+ public void startContent() throws XPathException {
+ if (attributes.getLength() > 1) {
+ GenericSorter.quickSort(0, attributes.getLength(), this);
+ }
+ for (int i=0; i<attributes.getLength(); i++) {
+ nextReceiver.attribute(
+ attributes.getNodeName(i),
+ attributes.getTypeAnnotation(i),
+ attributes.getValue(i),
+ attributes.getLocationId(i),
+ attributes.getProperties(i));
+ }
+ super.startContent();
+ }
+
+ /**
+ * Compare two objects within this Sortable, identified by their position.
+ *
+ * @return <0 if obj[a]<obj[b], 0 if obj[a]=obj[b], >0 if obj[a]>obj[b]
+ */
+ public int compare(int a, int b) {
+ NodeName n0 = attributes.getNodeName(a);
+ NodeName n1 = attributes.getNodeName(b);
+ Integer r0 = knownAttributes.get(n0);
+ Integer r1 = knownAttributes.get(n1);
+ if (r0 != null) {
+ if (r1 == null) {
+ return -1;
+ } else {
+ return r0.compareTo(r1);
+ }
+ } else if (r1 != null) {
+ return +1;
+ } else {
+ // both null: sort on URI/local-name
+ int x = n0.getURI().compareTo(n1.getURI());
+ if (x == 0) {
+ x = n1.getLocalPart().compareTo(n1.getLocalPart());
+ }
+ return x;
+ }
+ }
+
+ /**
+ * Swap two objects within this Sortable, identified by their position.
+ */
+ public void swap(int a, int b) {
+ attributes.swap(a, b);
+ }
+}
+
diff --git a/sf/saxon/serialize/BinaryTextDecoder.java b/sf/saxon/serialize/BinaryTextDecoder.java
new file mode 100644
index 0000000..4146950
--- /dev/null
+++ b/sf/saxon/serialize/BinaryTextDecoder.java
@@ -0,0 +1,88 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.value.Base64BinaryValue;
+import net.sf.saxon.value.HexBinaryValue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+/**
+ * This class generates decodes processing instructions in text output that represent text encoded
+ * in base64 binary or hexBinary
+ * @author Michael H. Kay
+ */
+
+public class BinaryTextDecoder extends ProxyReceiver {
+
+ String outputEncoding = "utf8";
+
+ public BinaryTextDecoder(Receiver next, Properties details) throws XPathException {
+ super(next);
+ setOutputProperties(details);
+ }
+
+ /**
+ * Set output properties
+ * @param details the output serialization properties
+ */
+
+ public void setOutputProperties(Properties details) throws XPathException {
+ outputEncoding = details.getProperty("encoding", "utf8");
+ }
+
+
+ /**
+ * Output a processing instruction. <br>
+ * Does nothing with this output method, unless the saxon:recognize-binary option is set, and this is the
+ * processing instructions hex or b64. The name of the processing instruction may be followed by an encoding
+ * name, for example b64.ascii indicates base64-encoded ASCII strings; if no encoding is present, the encoding
+ * of the output method is assumed.
+ */
+
+ public void processingInstruction(String name, /*@NotNull*/ CharSequence value, int locationId, int properties)
+ throws XPathException {
+ String encoding;
+ byte[] bytes = null;
+ int dot = name.indexOf('.');
+ if (dot >= 0 && dot != name.length()-1) {
+ encoding = name.substring(dot+1);
+ name = name.substring(0, dot);
+ } else {
+ encoding = outputEncoding;
+ }
+ if (name.equals("hex")) {
+ bytes = new HexBinaryValue(value).getBinaryValue();
+ } else if (name.equals("b64")) {
+ bytes = new Base64BinaryValue(value).getBinaryValue();
+ }
+ if (bytes != null) {
+ try {
+ ByteArrayInputStream stream = new ByteArrayInputStream(bytes);
+ InputStreamReader reader = new InputStreamReader(stream, encoding);
+ char[] array = new char[bytes.length];
+ int used = reader.read(array, 0, array.length);
+ nextReceiver.characters(new CharSlice(array, 0, used), locationId, properties);
+ } catch (IOException e) {
+ throw new XPathException(
+ "Text output method: failed to decode binary data " + Err.wrap(value.toString(), Err.VALUE));
+ }
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/serialize/CDATAFilter.java b/sf/saxon/serialize/CDATAFilter.java
new file mode 100644
index 0000000..cf73eb6
--- /dev/null
+++ b/sf/saxon/serialize/CDATAFilter.java
@@ -0,0 +1,267 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.FingerprintedQName;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.serialize.charcode.CharacterSet;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.SchemaType;
+
+import javax.xml.transform.OutputKeys;
+import java.util.*;
+
+/**
+* CDATAFilter: This ProxyEmitter converts character data to CDATA sections,
+* if the character data belongs to one of a set of element types to be handled this way.
+*
+* @author Michael Kay
+*/
+
+
+public class CDATAFilter extends ProxyReceiver {
+
+ private FastStringBuffer buffer = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ private Stack<NodeName> stack = new Stack<NodeName>();
+ private Set<NodeName> nameList; // names of cdata elements
+ private CharacterSet characterSet;
+
+ /**
+ * Create a CDATA Filter
+ * @param next the next receiver in the pipeline
+ */
+
+ public CDATAFilter(Receiver next) {
+ super(next);
+ }
+
+ /**
+ * Set the properties for this CDATA filter
+ * @param details the output properties
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void setOutputProperties (/*@NotNull*/ Properties details)
+ throws XPathException {
+ getCdataElements(details);
+ characterSet = getConfiguration().getCharacterSetFactory().getCharacterSet(details);
+ }
+
+ /**
+ * Output element start tag
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ flush();
+ stack.push(elemName);
+ nextReceiver.startElement(elemName, typeCode, locationId, properties);
+ }
+
+ /**
+ * Output element end tag
+ */
+
+ public void endElement() throws XPathException {
+ flush();
+ stack.pop();
+ nextReceiver.endElement();
+ }
+
+ /**
+ * Output a processing instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ flush();
+ nextReceiver.processingInstruction(target, data, locationId, properties);
+ }
+
+ /**
+ * Output character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+
+ if ((properties & ReceiverOptions.DISABLE_ESCAPING) == 0) {
+ buffer.append(chars.toString());
+ } else {
+ // if the user requests disable-output-escaping, this overrides the CDATA request. We end
+ // the CDATA section and output the characters as supplied.
+ flush();
+ nextReceiver.characters(chars, locationId, properties);
+ }
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ flush();
+ nextReceiver.comment(chars, locationId, properties);
+ }
+
+
+ /**
+ * Flush the buffer containing accumulated character data,
+ * generating it as CDATA where appropriate
+ */
+
+ private void flush() throws XPathException {
+ boolean cdata;
+ int end = buffer.length();
+ if (end==0) return;
+
+ if (stack.isEmpty()) {
+ cdata = false; // text is not part of any element
+ } else {
+ NodeName top = stack.peek();
+ cdata = isCDATA(top);
+ }
+
+ if (cdata) {
+
+ // Check that the buffer doesn't include a character not available in the current
+ // encoding
+
+ int start = 0;
+ int k = 0;
+ while ( k < end ) {
+ int next = buffer.charAt(k);
+ int skip = 1;
+ if (UTF16CharacterSet.isHighSurrogate((char)next)) {
+ next = UTF16CharacterSet.combinePair((char)next, buffer.charAt(k+1));
+ skip = 2;
+ }
+ if (next != 0 && characterSet.inCharset(next)) {
+ k++;
+ } else {
+
+ // flush out the preceding characters as CDATA
+
+ char[] array = new char[k-start];
+ buffer.getChars(start, k, array, 0);
+ flushCDATA(array, k-start);
+
+ while (k < end) {
+ // output consecutive non-encodable characters
+ // before restarting the CDATA section
+ //super.characters(CharBuffer.wrap(buffer, k, k+skip), 0, 0);
+ nextReceiver.characters(buffer.subSequence(k, k+skip), 0, ReceiverOptions.DISABLE_CHARACTER_MAPS);
+ // was: (..., ReceiverOptions.DISABLE_ESCAPING);
+ k += skip;
+ if (k >= end) {
+ break;
+ }
+ next = buffer.charAt(k);
+ skip = 1;
+ if (UTF16CharacterSet.isHighSurrogate((char)next)) {
+ next = UTF16CharacterSet.combinePair((char)next, buffer.charAt(k+1));
+ skip = 2;
+ }
+ if (characterSet.inCharset(next)) {
+ break;
+ }
+ }
+ start=k;
+ }
+ }
+ char[] rest = new char[end-start];
+ buffer.getChars(start, end, rest, 0);
+ flushCDATA(rest, end-start);
+
+ } else {
+ nextReceiver.characters(buffer, 0, 0);
+ }
+
+ buffer.setLength(0);
+
+ }
+
+ /**
+ * Output an array as a CDATA section. At this stage we have checked that all the characters
+ * are OK, but we haven't checked that there is no "]]>" sequence in the data
+ * @param array the data to be output
+ * @param len the number of characters in the array actually used
+ */
+
+ private void flushCDATA(char[] array, int len) throws XPathException {
+ if (len == 0) {
+ return;
+ }
+ final int chprop = ReceiverOptions.DISABLE_ESCAPING | ReceiverOptions.DISABLE_CHARACTER_MAPS;
+ nextReceiver.characters("<![CDATA[", 0, chprop);
+
+ // Check that the character data doesn't include the substring "]]>"
+ // Also get rid of any zero bytes inserted by character map expansion
+
+ int i=0;
+ int doneto=0;
+ while (i<len-2) {
+ if (array[i]==']' && array[i+1]==']' && array[i+2]=='>') {
+ nextReceiver.characters(new CharSlice(array, doneto, i+2-doneto), 0, chprop);
+ nextReceiver.characters("]]><![CDATA[", 0, chprop);
+ doneto=i+2;
+ } else if (array[i]==0) {
+ nextReceiver.characters(new CharSlice(array, doneto, i-doneto), 0, chprop);
+ doneto=i+1;
+ }
+ i++;
+ }
+ nextReceiver.characters(new CharSlice(array, doneto, len-doneto), 0, chprop);
+ nextReceiver.characters("]]>", 0, chprop);
+ }
+
+
+ /**
+ * See if a particular element is a CDATA element
+ * @param elementName identifies the name of element we are interested
+ * @return true if this element is included in cdata-section-elements
+ */
+
+ private boolean isCDATA(NodeName elementName) {
+ return nameList.contains(elementName);
+ }
+
+ /**
+ * Extract the list of CDATA elements from the output properties
+ * @param details the output properties
+ */
+
+ private void getCdataElements(Properties details) {
+ boolean isHTML = "html".equals(details.getProperty(OutputKeys.METHOD));
+ boolean isHTML5 = isHTML && "5.0".equals(details.getProperty(OutputKeys.VERSION));
+ boolean isHTML4 = isHTML && !isHTML5;
+ String cdata = details.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
+ if (cdata==null) {
+ // this doesn't happen, but there's no harm allowing for it
+ nameList = new HashSet<NodeName>(0);
+ return;
+ }
+ nameList = new HashSet<NodeName>(10);
+ StringTokenizer st2 = new StringTokenizer(cdata, " \t\n\r", false);
+ while (st2.hasMoreTokens()) {
+ String expandedName = st2.nextToken();
+ StructuredQName sq = StructuredQName.fromClarkName(expandedName);
+ String uri = sq.getURI();
+ if (!isHTML || (isHTML4 && !uri.equals("")) || (isHTML5 && !uri.equals("") && !uri.equals(NamespaceConstant.XHTML))) {
+ nameList.add(new FingerprintedQName("", sq.getURI(), sq.getLocalPart()));
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/serialize/CharacterMap.java b/sf/saxon/serialize/CharacterMap.java
new file mode 100644
index 0000000..dbc0f02
--- /dev/null
+++ b/sf/saxon/serialize/CharacterMap.java
@@ -0,0 +1,159 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntIterator;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.tree.tiny.CompressedWhitespace;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.Whitespace;
+
+import java.io.Serializable;
+import java.util.Iterator;
+
+/**
+ * This class defines a character map, that is, a mapping from characters to strings used by the serializer
+ * when mapping individual characters in the output.
+ */
+public class CharacterMap implements Serializable {
+
+ private IntHashMap<String> charMap;
+ private int min = Integer.MAX_VALUE; // the lowest mapped character
+ private int max = 0; // the highest mapped character
+ private boolean mapsWhitespace = false;
+
+ /**
+ * Create a CharacterMap from a raw map of integers to strings
+ * @param map the mapping of integer Unicode character codes to strings
+ */
+
+ public CharacterMap(IntHashMap<String> map) {
+ this.charMap = map;
+ init();
+ }
+
+ /**
+ * Create a CharacterMap that combines a set of existing character maps.
+ * @param list the list of existing character maps. If the same character
+ * is mapped by more than one map in the list, the last mapping takes
+ * precedence
+ */
+
+ public CharacterMap(Iterable<CharacterMap> list) {
+ charMap = new IntHashMap(64);
+ for (Iterator iter = list.iterator(); iter.hasNext(); ) {
+ CharacterMap map = (CharacterMap)iter.next();
+ IntIterator keys = map.charMap.keyIterator();
+ while (keys.hasNext()) {
+ int next = keys.next();
+ charMap.put(next, map.charMap.get(next));
+ }
+ }
+ init();
+ }
+
+ private void init() {
+ IntIterator keys = charMap.keyIterator();
+ while (keys.hasNext()) {
+ int next = keys.next();
+ if (next < min) {
+ min = next;
+ }
+ if (next > max) {
+ max = next;
+ }
+ if (!mapsWhitespace && Whitespace.isWhitespace(next)) {
+ mapsWhitespace = true;
+ }
+ }
+ if (min > 0xD800) {
+ // if all the mapped characters are above the BMP, we need to check
+ // surrogates
+ min = 0xD800;
+ }
+ }
+
+
+ /**
+ * Expand all the characters in a string using this character mapping
+ * @param in the input string to be mapped
+ * @param insertNulls true if null (0) characters are to be inserted before
+ * and after replacement characters. This is done to signal
+ * that output escaping of these characters is disabled. The flag is set to true when writing
+ * XML or HTML, but to false when writing TEXT.
+ */
+
+ /*@NotNull*/ public CharSequence map(CharSequence in, boolean insertNulls) {
+
+ if ((!mapsWhitespace) && in instanceof CompressedWhitespace) {
+ return in;
+ }
+
+ // First scan the string to see if there are any possible mapped
+ // characters; if not, don't bother creating the new buffer
+
+ boolean move = false;
+ for (int i=0; i<in.length();) {
+ char c = in.charAt(i++);
+ if (c >= min && c <= max) {
+ move = true;
+ break;
+ }
+ }
+ if (!move) {
+ return in;
+ }
+
+ FastStringBuffer buffer = new FastStringBuffer(in.length()*2);
+ int i = 0;
+ while(i < in.length()) {
+ char c = in.charAt(i++);
+ if (c >= min && c <= max) {
+ if (UTF16CharacterSet.isHighSurrogate(c)) {
+ // assume the string is properly formed
+ char d = in.charAt(i++);
+ int s = UTF16CharacterSet.combinePair(c, d);
+ String rep = charMap.get(s);
+ if (rep == null) {
+ buffer.append(c);
+ buffer.append(d);
+ } else {
+ if (insertNulls) {
+ buffer.append((char)0);
+ buffer.append(rep);
+ buffer.append((char)0);
+ } else {
+ buffer.append(rep);
+ }
+ }
+ } else {
+ String rep = charMap.get(c);
+ if (rep == null) {
+ buffer.append(c);
+ } else {
+ if (insertNulls) {
+ buffer.append((char)0);
+ buffer.append(rep);
+ buffer.append((char)0);
+ } else {
+ buffer.append(rep);
+ }
+ }
+ }
+ } else {
+ buffer.append(c);
+ }
+ }
+ return buffer;
+ }
+
+
+
+}
+
diff --git a/sf/saxon/serialize/CharacterMapExpander.java b/sf/saxon/serialize/CharacterMapExpander.java
new file mode 100644
index 0000000..d40076b
--- /dev/null
+++ b/sf/saxon/serialize/CharacterMapExpander.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SimpleType;
+
+/**
+* CharacterMapExpander: This ProxyReceiver expands characters occurring in a character map,
+ * as specified by the XSLT 2.0 xsl:character-map declaration
+*
+* @author Michael Kay
+*/
+
+
+public class CharacterMapExpander extends ProxyReceiver {
+
+ private CharacterMap charMap;
+ private boolean useNullMarkers = true;
+
+ public CharacterMapExpander(Receiver next) {
+ super(next);
+ }
+
+ /**
+ * Set the character maps to be used by this CharacterMapExpander.
+ * They are merged into a single character map if there is more than one.
+ */
+
+ public void setCharacterMap(CharacterMap map) {
+ charMap = map;
+ }
+
+ /**
+ * Indicate whether the result of character mapping should be marked using NUL
+ * characters to prevent subsequent XML or HTML character escaping. The default value
+ * is true (used for the XML and HTML output methods); the value false is used by the text
+ * output method.
+ */
+
+ public void setUseNullMarkers(boolean use) {
+ useNullMarkers = use;
+ }
+
+ /**
+ * Output an attribute
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ if ((properties & ReceiverOptions.DISABLE_CHARACTER_MAPS) == 0) {
+ CharSequence mapped = charMap.map(value, useNullMarkers);
+ if (mapped == value) {
+ // no mapping was done
+ nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ } else {
+ nextReceiver.attribute(nameCode, typeCode, mapped,
+ locationId,
+ (properties | ReceiverOptions.USE_NULL_MARKERS) & ~ReceiverOptions.NO_SPECIAL_CHARS);
+ }
+ } else {
+ nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+ }
+
+ /**
+ * Output character data
+ */
+
+ public void characters(/*@NotNull*/ CharSequence chars, int locationId, int properties) throws XPathException {
+
+ if ((properties & ReceiverOptions.DISABLE_ESCAPING) == 0) {
+ CharSequence mapped = charMap.map(chars, useNullMarkers);
+ if (mapped != chars) {
+ properties = (properties | ReceiverOptions.USE_NULL_MARKERS) & ~ReceiverOptions.NO_SPECIAL_CHARS;
+ }
+ nextReceiver.characters(mapped, locationId, properties);
+ } else {
+ // if the user requests disable-output-escaping, this overrides the character
+ // mapping
+ nextReceiver.characters(chars, locationId, properties);
+ }
+
+ }
+
+
+}
+
diff --git a/sf/saxon/serialize/CharacterMapIndex.java b/sf/saxon/serialize/CharacterMapIndex.java
new file mode 100644
index 0000000..ae6e0d2
--- /dev/null
+++ b/sf/saxon/serialize/CharacterMapIndex.java
@@ -0,0 +1,85 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.SerializerFactory;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * This class represents a set of named character maps. Each character map in the set is identified by a unique
+ * QName.
+ */
+
+public class CharacterMapIndex implements Serializable {
+
+ private HashMap<StructuredQName, CharacterMap> index = new HashMap<StructuredQName, CharacterMap>(10);
+
+ public CharacterMapIndex() {}
+
+ public CharacterMap getCharacterMap(StructuredQName name) {
+ return index.get(name);
+ }
+
+ public void putCharacterMap(StructuredQName name, CharacterMap charMap) {
+ index.put(name, charMap);
+ }
+
+ /**
+ * Make a CharacterMapExpander to handle the character map definitions in the serialization
+ * properties.
+ * <p>
+ * This method is intended for internal use only.
+ *
+ * @param useMaps the expanded use-character-maps property: a space-separated list of names
+ * of character maps to be used, each one expressed as an expanded-QName in Clark notation
+ * (that is, {uri}local-name).
+ * @param next the next receiver in the pipeline
+ * @param sf the SerializerFactory - used to create a CharacterMapExpander. This callback
+ * is provided so that a user-defined SerializerFactory can customize the result of this function,
+ * for example by returning a subclass of the standard CharacterMapExpander.
+ * @return a CharacterMapExpander if one is required, or null if not (for example, if the
+ * useMaps argument is an empty string).
+ * @throws net.sf.saxon.trans.XPathException if a name in the useMaps property cannot be resolved to a declared
+ * character map.
+ */
+
+ public CharacterMapExpander makeCharacterMapExpander(
+ /*@NotNull*/ String useMaps, /*@NotNull*/ Receiver next, /*@NotNull*/ SerializerFactory sf) throws XPathException {
+ CharacterMapExpander characterMapExpander = null;
+ List<CharacterMap> characterMaps = new ArrayList<CharacterMap>(5);
+ StringTokenizer st = new StringTokenizer(useMaps, " \t\n\r", false);
+ while (st.hasMoreTokens()) {
+ String expandedName = st.nextToken();
+ StructuredQName qName = StructuredQName.fromClarkName(expandedName);
+ CharacterMap map = getCharacterMap(qName);
+ if (map==null) {
+ throw new XPathException("Character map '" + expandedName + "' has not been defined", "SEPM0016");
+ }
+ characterMaps.add(map);
+ }
+ if (!characterMaps.isEmpty()) {
+ characterMapExpander = sf.newCharacterMapExpander(next);
+ if (characterMaps.size() == 1) {
+ characterMapExpander.setCharacterMap(characterMaps.get(0));
+ } else {
+ characterMapExpander.setCharacterMap(new CharacterMap(characterMaps));
+ }
+ }
+ return characterMapExpander;
+ }
+
+}
+
diff --git a/sf/saxon/serialize/CharacterReferenceGenerator.java b/sf/saxon/serialize/CharacterReferenceGenerator.java
new file mode 100644
index 0000000..8e95d7e
--- /dev/null
+++ b/sf/saxon/serialize/CharacterReferenceGenerator.java
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import java.io.Writer;
+
+/**
+ * A class that expands a character to a character reference, entity reference, etc,
+ * and writes the resulting reference to a writer
+ */
+
+public interface CharacterReferenceGenerator {
+
+ /**
+ * Generate a character reference
+ * @param charval the unicode code point of the character concerned
+ * @param writer the Writer to which the character reference is to be written
+ * @throws java.io.IOException if the Writer reports an error
+ */
+ void outputCharacterReference(int charval, Writer writer) throws java.io.IOException ;
+}
+
diff --git a/sf/saxon/serialize/Emitter.java b/sf/saxon/serialize/Emitter.java
new file mode 100644
index 0000000..6dbbb13
--- /dev/null
+++ b/sf/saxon/serialize/Emitter.java
@@ -0,0 +1,378 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.serialize.charcode.CharacterSet;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.serialize.charcode.UTF8CharacterSet;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.stream.StreamResult;
+import java.io.*;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Properties;
+
+
+/**
+ * Emitter: This abstract class defines methods that must be implemented by
+ * components that format SAXON output. There is one emitter for XML,
+ * one for HTML, and so on. Additional methods are concerned with
+ * setting options and providing a Writer.<p>
+ *
+ * The interface is deliberately designed to be as close as possible to the
+ * standard SAX2 ContentHandler interface, however, it allows additional
+ * information to be made available.
+ *
+ * An Emitter is a Receiver, specifically it is a Receiver that can direct output
+ * to a Writer or OutputStream, using serialization properties defined in a Properties
+ * object.
+ */
+
+public abstract class Emitter implements Result, Receiver
+{
+ /*@NotNull*/ protected PipelineConfiguration pipelineConfig;
+ /*@NotNull*/ protected NamePool namePool;
+ protected String systemId;
+ protected StreamResult streamResult;
+ protected Writer writer;
+ protected OutputStream outputStream;
+ protected Properties outputProperties;
+ protected CharacterSet characterSet;
+ protected boolean allCharactersEncodable = false;
+ private boolean mustClose = false;
+
+ /**
+ * Set the pipelineConfiguration
+ */
+
+ public void setPipelineConfiguration(/*@NotNull*/ PipelineConfiguration pipe) {
+ pipelineConfig = pipe;
+ namePool = pipe.getConfiguration().getNamePool();
+ }
+
+ /**
+ * Get the pipeline configuration used for this document
+ */
+
+ /*@NotNull*/
+ public PipelineConfiguration getPipelineConfiguration() {
+ return pipelineConfig;
+ }
+
+ /**
+ * Get the configuration used for this document
+ * @return the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return pipelineConfig.getConfiguration();
+ }
+
+ /**
+ * Set the System ID
+ * @param systemId the system identifier (=base URI)
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the System ID
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Set output properties
+ * @param details the output serialization properties
+ * @throws net.sf.saxon.trans.XPathException if an error occurs finding the encoding property
+ */
+
+ public void setOutputProperties(Properties details) throws XPathException {
+ if (characterSet==null) {
+ characterSet = getConfiguration().getCharacterSetFactory().getCharacterSet(details);
+ allCharactersEncodable = (characterSet instanceof UTF8CharacterSet ||
+ characterSet instanceof UTF16CharacterSet);
+ }
+ outputProperties = details;
+ }
+
+ /**
+ * Get the output properties
+ * @return the output serialization properties. The returned value will be null if setOutputProperties()
+ * has not been called
+ */
+
+ public Properties getOutputProperties() {
+ return outputProperties;
+ }
+
+ /**
+ * Set the StreamResult acting as the output destination of the Emitter
+ * @param result the output destination
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void setStreamResult(StreamResult result) throws XPathException {
+ streamResult = result;
+ if (systemId == null) {
+ systemId = result.getSystemId();
+ }
+ }
+
+ /**
+ * Make a Writer for this Emitter to use, given a StreamResult.
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ protected void makeWriter() throws XPathException {
+ if (writer != null) {
+ return;
+ }
+ if (streamResult == null) {
+ throw new IllegalStateException("Emitter must have either a Writer or a StreamResult to write to");
+ }
+ writer = streamResult.getWriter();
+ if (writer == null) {
+ OutputStream os = streamResult.getOutputStream();
+ if (os != null) {
+ setOutputStream(os);
+ }
+ }
+ if (writer == null) {
+ makeOutputStream();
+ }
+ }
+
+ @SuppressWarnings({"ResultOfMethodCallIgnored"})
+ protected OutputStream makeOutputStream() throws XPathException {
+ String uriString = streamResult.getSystemId();
+ if (uriString == null) {
+ throw new XPathException("Result has no system ID, writer, or output stream defined");
+ }
+
+ try {
+ URI uri = new URI(uriString);
+ if (!uri.isAbsolute()) {
+ try {
+ uri = new File(uriString).getAbsoluteFile().toURI();
+ } catch (Exception e) {
+ // if we fail, we'll get another exception
+ }
+ }
+ File file = new File(uri);
+ try {
+ if ("file".equals(uri.getScheme()) && !file.exists()) {
+ File directory = file.getParentFile();
+ if (directory != null && !directory.exists()) {
+ directory.mkdirs();
+ }
+ file.createNewFile();
+ }
+ } catch (IOException err) {
+ throw new XPathException("Failed to create output file " + uri, err);
+ }
+ setOutputStream(new FileOutputStream(file));
+ // Set the outputstream in the StreamResult object so that the
+ // call on OutputURIResolver.close() can close it
+ streamResult.setOutputStream(outputStream);
+ mustClose = true;
+ } catch (FileNotFoundException fnf) {
+ throw new XPathException(fnf);
+ } catch (URISyntaxException use) {
+ throw new XPathException(use);
+ } catch (IllegalArgumentException iae) {
+ // for example, the system ID doesn't use the file: scheme
+ throw new XPathException(iae);
+ }
+ return outputStream;
+ }
+
+ /**
+ * Determine whether the Emitter wants a Writer for character output or
+ * an OutputStream for binary output. The standard Emitters all use a Writer, so
+ * this returns true; but a subclass can override this if it wants to use an OutputStream
+ * @return true if a Writer is needed, as distinct from an OutputStream
+ */
+
+ public boolean usesWriter() {
+ return true;
+ }
+
+ /**
+ * Set the output destination as a character stream
+ * @param writer the Writer to use as an output destination
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void setWriter(Writer writer) throws XPathException {
+ this.writer = writer;
+
+ // If the writer uses a known encoding, change the encoding in the XML declaration
+ // to match. Any encoding actually specified in xsl:output is ignored, because encoding
+ // is being done by the user-supplied Writer, and not by Saxon itself.
+
+ if (writer instanceof OutputStreamWriter && outputProperties != null) {
+ String enc = ((OutputStreamWriter)writer).getEncoding();
+ outputProperties.setProperty(OutputKeys.ENCODING, enc);
+ characterSet = getConfiguration().getCharacterSetFactory().getCharacterSet(outputProperties);
+ allCharactersEncodable = (characterSet instanceof UTF8CharacterSet ||
+ characterSet instanceof UTF16CharacterSet);
+ }
+ }
+
+ /**
+ * Get the output writer
+ * @return the Writer being used as an output destination, if any
+ */
+
+ public Writer getWriter() {
+ return writer;
+ }
+
+ /**
+ * Set the output destination as a byte stream.
+ * <p>Note that if a specific encoding (other than the default, UTF-8) is required, then
+ * {@link #setOutputProperties(java.util.Properties)} must be called <i>before</i> calling
+ * this method.</p>
+ * @param stream the OutputStream being used as an output destination
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void setOutputStream(OutputStream stream) throws XPathException {
+ outputStream = stream;
+
+ // If the user supplied an OutputStream, but the Emitter is written to
+ // use a Writer (this is the most common case), then we create a Writer
+ // to wrap the supplied OutputStream; the complications are to ensure that
+ // the character encoding is correct.
+
+ if (usesWriter()) {
+
+ if (outputProperties == null) {
+ outputProperties = new Properties();
+ }
+
+ String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
+ if (encoding==null) {
+ encoding = "UTF8";
+ allCharactersEncodable = true;
+ } else if (encoding.equalsIgnoreCase("UTF-8")) {
+ encoding = "UTF8";
+ allCharactersEncodable = true;
+ } else if (encoding.equalsIgnoreCase("UTF-16")) {
+ encoding = "UTF16";
+ }
+
+ if (characterSet == null) {
+ characterSet = getConfiguration().getCharacterSetFactory().getCharacterSet(outputProperties);
+ }
+
+ String byteOrderMark = outputProperties.getProperty(SaxonOutputKeys.BYTE_ORDER_MARK);
+ if ("no".equals(byteOrderMark) && "UTF16".equals(encoding)) {
+ // Java always writes a bom for UTF-16, so if the user doesn't want one, use utf16-be
+ encoding = "UTF-16BE";
+ } else if (!(characterSet instanceof UTF8CharacterSet)) {
+
+ //if (characterSet instanceof PluggableCharacterSet) {
+ encoding = characterSet.getCanonicalName();
+ }
+
+ while (true) {
+ try {
+ String javaEncoding = encoding;
+ if (encoding.equalsIgnoreCase("iso-646") || encoding.equalsIgnoreCase("iso646")) {
+ javaEncoding = "US-ASCII";
+ }
+ if (encoding.equalsIgnoreCase("UTF8")) {
+ writer = new UTF8Writer(outputStream);
+ } else {
+ writer = new BufferedWriter(
+ new OutputStreamWriter(
+ outputStream, javaEncoding));
+ }
+ break;
+ } catch (Exception err) {
+ if (encoding.equalsIgnoreCase("UTF8")) {
+ throw new XPathException("Failed to create a UTF8 output writer");
+ }
+ XPathException de = new XPathException("Encoding " + encoding + " is not supported: using UTF8");
+ de.setErrorCode("SESU0007");
+ try {
+ getPipelineConfiguration().getErrorListener().error(de);
+ } catch (TransformerException e) {
+ throw XPathException.makeXPathException(e);
+ }
+ encoding = "UTF8";
+ characterSet = UTF8CharacterSet.getInstance();
+ allCharactersEncodable = true;
+ outputProperties.setProperty(OutputKeys.ENCODING, "UTF-8");
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Get the output stream
+ * @return the OutputStream being used as an output destination, if any
+ */
+
+ public OutputStream getOutputStream() {
+ return outputStream;
+ }
+
+ /**
+ * Set unparsed entity URI. Needed to satisfy the Receiver interface, but not used,
+ * because unparsed entities can occur only in input documents, not in output documents.
+ * @param name the entity name
+ * @param uri the entity system ID
+ * @param publicId the entity public ID
+ */
+
+ public void setUnparsedEntity(String name, String uri, String publicId) throws XPathException {}
+
+ /**
+ * Notify the end of the event stream
+ */
+
+ public void close() throws XPathException {
+ if (mustClose && outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ throw new XPathException("Failed to close output stream");
+ }
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ *
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation (or conversely, it may
+ * avoid stripping unwanted type annotations)
+ */
+ public boolean usesTypeAnnotations() {
+ return false;
+ }
+}
+
diff --git a/sf/saxon/serialize/HTML40Emitter.java b/sf/saxon/serialize/HTML40Emitter.java
new file mode 100644
index 0000000..1290214
--- /dev/null
+++ b/sf/saxon/serialize/HTML40Emitter.java
@@ -0,0 +1,93 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.OutputKeys;
+
+/**
+ * This class generates HTML 4.0 output
+ */
+public class HTML40Emitter extends HTMLEmitter{
+
+ static {
+ setEmptyTag("area");
+ setEmptyTag("base");
+ setEmptyTag("basefont");
+ setEmptyTag("br");
+ setEmptyTag("col");
+ setEmptyTag("embed");
+ setEmptyTag("frame");
+ setEmptyTag("hr");
+ setEmptyTag("img");
+ setEmptyTag("input");
+ setEmptyTag("isindex");
+ setEmptyTag("link");
+ setEmptyTag("meta");
+ setEmptyTag("param");
+ }
+
+
+ /**
+ * Constructor
+ */
+
+ public HTML40Emitter() {
+
+ }
+
+ /**
+ * Decide whether an element is "serialized as an HTML element" in the language of the 3.0 specification
+ *
+ * @return true if the element is to be serialized as an HTML element
+ */
+ @Override
+ protected boolean isHTMLElement(NodeName name) {
+ return name.getURI().equals("");
+ }
+
+ @Override
+ protected void openDocument() throws XPathException {
+
+ String versionProperty = outputProperties.getProperty(SaxonOutputKeys.HTML_VERSION);
+ // Note, we recognize html-version even when running XSLT 2.0.
+ if (versionProperty == null) {
+ versionProperty = outputProperties.getProperty(OutputKeys.VERSION);
+ }
+
+ if (versionProperty != null) {
+ if (versionProperty.equals("4.0") || versionProperty.equals("4.01")) {
+ version = 4;
+ } else {
+ XPathException err = new XPathException("Unsupported HTML version: " + versionProperty);
+ err.setErrorCode("SESU0013");
+ throw err;
+ }
+ }
+ String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
+ String publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
+
+ // Treat "" as equivalent to absent. This goes beyond what the spec strictly allows.
+ if ("".equals(systemId)) {
+ systemId = null;
+ }
+ if ("".equals(publicId)) {
+ publicId = null;
+ }
+
+ super.openDocument();
+ if (systemId != null || publicId != null) {
+ writeDocType(null, "html", systemId, publicId);
+ }
+ }
+
+
+}
diff --git a/sf/saxon/serialize/HTML50Emitter.java b/sf/saxon/serialize/HTML50Emitter.java
new file mode 100644
index 0000000..dbdbead
--- /dev/null
+++ b/sf/saxon/serialize/HTML50Emitter.java
@@ -0,0 +1,119 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.OutputKeys;
+
+/**
+ * This class generates HTML 5.0 output
+ */
+public class HTML50Emitter extends HTMLEmitter {
+
+
+ static {
+ setEmptyTag("area");
+ setEmptyTag("base");
+ setEmptyTag("base");
+ setEmptyTag("basefont");
+ setEmptyTag("br");
+ setEmptyTag("col");
+ setEmptyTag("command");
+ setEmptyTag("embed");
+ setEmptyTag("frame");
+ setEmptyTag("hr");
+ setEmptyTag("img");
+ setEmptyTag("input");
+ setEmptyTag("isindex");
+ setEmptyTag("keygen");
+ setEmptyTag("link");
+ setEmptyTag("meta");
+ setEmptyTag("param");
+ setEmptyTag("source");
+ setEmptyTag("track");
+ setEmptyTag("wbr");
+ }
+
+ /**
+ * Constructor
+ */
+
+ public HTML50Emitter() {
+ version = 5;
+ }
+
+ /**
+ * Decide whether an element is "serialized as an HTML element" in the language of the 3.0 specification
+ *
+ * @return true if the element is to be serialized as an HTML element
+ */
+ @Override
+ protected boolean isHTMLElement(NodeName name) {
+ String uri = name.getURI();
+ return uri.equals("") || uri.equals(NamespaceConstant.XHTML);
+ }
+
+ protected void openDocument() throws XPathException {
+ String versionProperty = outputProperties.getProperty(SaxonOutputKeys.HTML_VERSION);
+ // Note, we recognize html-version even when running XSLT 2.0.
+ if (versionProperty == null) {
+ versionProperty = outputProperties.getProperty(OutputKeys.VERSION);
+ }
+
+ if (versionProperty != null) {
+ if (versionProperty.equals("5.0")) {
+ version = 5;
+ } else {
+ XPathException err = new XPathException("Unsupported HTML version: " + versionProperty);
+ err.setErrorCode("SESU0013");
+ throw err;
+ }
+ }
+ String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
+ String publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
+
+ // Treat "" as equivalent to absent. This goes beyond what the spec strictly allows.
+ if ("".equals(systemId)) {
+ systemId = null;
+ }
+ if ("".equals(publicId)) {
+ publicId = null;
+ }
+ super.openDocument();
+ writeDocType(null, "html", systemId, publicId);
+ }
+
+ /**
+ * Output the document type declaration
+ *
+ * @param displayName The element name
+ * @param systemId The DOCTYP system identifier
+ * @param publicId The DOCTYPE public identifier
+ */
+
+ protected void writeDocType(NodeName name, String displayName, String systemId, String publicId) throws XPathException {
+ try {
+ if (systemId == null && publicId == null) {
+ writer.write("<!DOCTYPE HTML>\n");
+ } else {
+ super.writeDocType(name, displayName, systemId, publicId);
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException(err);
+ }
+
+ }
+
+ protected boolean writeDocTypeWithNullSystemId() {
+ return true;
+ }
+}
diff --git a/sf/saxon/serialize/HTMLEmitter.java b/sf/saxon/serialize/HTMLEmitter.java
new file mode 100644
index 0000000..7a5b24c
--- /dev/null
+++ b/sf/saxon/serialize/HTMLEmitter.java
@@ -0,0 +1,424 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CompressedWhitespace;
+import net.sf.saxon.type.SchemaType;
+
+import javax.xml.transform.OutputKeys;
+import java.util.Stack;
+
+/**
+ * This class generates HTML output
+ * @author Michael H. Kay
+ */
+
+public abstract class HTMLEmitter extends XMLEmitter {
+
+ /**
+ * Preferred character representations
+ */
+
+ private static final int REP_NATIVE = 0;
+ private static final int REP_ENTITY = 1;
+ private static final int REP_DECIMAL = 2;
+ private static final int REP_HEX = 3;
+
+ private int nonASCIIRepresentation = REP_NATIVE;
+ private int excludedRepresentation = REP_ENTITY;
+
+ private int inScript;
+ protected int version = 4;
+ private String parentElement;
+ private String uri;
+ private boolean escapeNonAscii = false;
+ private Stack<NodeName> nodeNameStack = new Stack<NodeName>();
+
+ /**
+ * Decode preferred representation
+ * @param rep string containing preferred representation (native, entity, decimal, or hex)
+ * @return integer code for the preferred representation
+ */
+
+ private static int representationCode(String rep) {
+ if (rep.equalsIgnoreCase("native")) return REP_NATIVE;
+ if (rep.equalsIgnoreCase("entity")) return REP_ENTITY;
+ if (rep.equalsIgnoreCase("decimal")) return REP_DECIMAL;
+ if (rep.equalsIgnoreCase("hex")) return REP_HEX;
+ return REP_ENTITY;
+ }
+
+ /**
+ * Table of HTML tags that have no closing tag
+ */
+
+ static HTMLTagHashSet emptyTags = new HTMLTagHashSet(31);
+
+
+
+ protected static void setEmptyTag(String tag) {
+ emptyTags.add(tag);
+ }
+
+ protected static boolean isEmptyTag(String tag) {
+ return emptyTags.contains(tag);
+ }
+
+ /**
+ * Table of boolean attributes
+ */
+
+ // we use two HashMaps to avoid unnecessary string concatenations
+
+ private static HTMLTagHashSet booleanAttributes = new HTMLTagHashSet(31);
+ private static HTMLTagHashSet booleanCombinations = new HTMLTagHashSet(53);
+
+ static {
+ setBooleanAttribute("area", "nohref");
+ setBooleanAttribute("button", "disabled");
+ setBooleanAttribute("dir", "compact");
+ setBooleanAttribute("dl", "compact");
+ setBooleanAttribute("frame", "noresize");
+ setBooleanAttribute("hr", "noshade");
+ setBooleanAttribute("img", "ismap");
+ setBooleanAttribute("input", "checked");
+ setBooleanAttribute("input", "disabled");
+ setBooleanAttribute("input", "readonly");
+ setBooleanAttribute("menu", "compact");
+ setBooleanAttribute("object", "declare");
+ setBooleanAttribute("ol", "compact");
+ setBooleanAttribute("optgroup", "disabled");
+ setBooleanAttribute("option", "selected");
+ setBooleanAttribute("option", "disabled");
+ setBooleanAttribute("script", "defer");
+ setBooleanAttribute("select", "multiple");
+ setBooleanAttribute("select", "disabled");
+ setBooleanAttribute("td", "nowrap");
+ setBooleanAttribute("textarea", "disabled");
+ setBooleanAttribute("textarea", "readonly");
+ setBooleanAttribute("th", "nowrap");
+ setBooleanAttribute("ul", "compact");
+ }
+
+ private static void setBooleanAttribute(String element, String attribute) {
+ booleanAttributes.add(attribute);
+ booleanCombinations.add(element + '+' + attribute);
+ }
+
+ private static boolean isBooleanAttribute(String element, String attribute, String value) {
+ return attribute.equalsIgnoreCase(value) &&
+ booleanAttributes.contains(attribute) &&
+ booleanCombinations.contains(element + '+' + attribute);
+ }
+
+ /**
+ * Constructor
+ */
+
+ public HTMLEmitter() {
+
+ }
+
+ /**
+ * Say that all non-ASCII characters should be escaped, regardless of the character encoding
+ * @param escape true if all non ASCII characters should be escaped
+ */
+
+ public void setEscapeNonAscii(Boolean escape) {
+ escapeNonAscii = escape;
+ }
+
+ /**
+ * Decide whether an element is "serialized as an HTML element" in the language of the 3.0 specification
+ * @return true if the element
+ */
+
+ protected abstract boolean isHTMLElement(NodeName name);
+
+ /**
+ * Output start of document
+ */
+
+ public void open() throws XPathException {}
+
+ protected void openDocument() throws XPathException {
+ if (writer==null) {
+ makeWriter();
+ }
+ if (started) {
+ return;
+ }
+ started = true;
+ // This method is sometimes called twice, especially during an identity transform
+ // This check stops two DOCTYPE declarations being output.
+
+
+ String byteOrderMark = outputProperties.getProperty(SaxonOutputKeys.BYTE_ORDER_MARK);
+
+ if ("yes".equals(byteOrderMark) &&
+ "UTF-8".equalsIgnoreCase(outputProperties.getProperty(OutputKeys.ENCODING))) {
+ try {
+ writer.write('\uFEFF');
+ } catch (java.io.IOException err) {
+ // Might be an encoding exception; just ignore it
+ }
+ }
+ inScript = -1000000;
+ }
+
+ /**
+ * Output the document type declaration
+ * @param displayName The element name
+ * @param systemId The DOCTYPE system identifier
+ * @param publicId The DOCTYPE public identifier
+ */
+
+ protected void writeDocType(NodeName name, String displayName, String systemId, String publicId) throws XPathException {
+ super.writeDocType(name, displayName, systemId, publicId);
+ }
+
+ /**
+ * Output element start tag
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+
+ super.startElement(elemName, typeCode, locationId, properties);
+ uri = elemName.getURI();
+ parentElement = elementStack.peek();
+ if (elemName.isInNamespace("") &&
+ ( parentElement.equalsIgnoreCase("script") ||
+ parentElement.equalsIgnoreCase("style"))) {
+ inScript = 0;
+ }
+ inScript++;
+ nodeNameStack.push(elemName);
+ }
+
+ public void startContent() throws XPathException {
+ closeStartTag(); // prevent <xxx/> syntax
+ }
+
+ /**
+ * Write attribute name=value pair. Overrides the XML behaviour if the name and value
+ * are the same (we assume this is a boolean attribute to be minimised), or if the value is
+ * a URL.
+ */
+
+ protected void writeAttribute(NodeName elCode, String attname, CharSequence value, int properties) throws XPathException {
+ try {
+ if (uri.length()==0) {
+ if (isBooleanAttribute(parentElement, attname, value.toString())) {
+ writer.write(attname);
+ return;
+ }
+ }
+ super.writeAttribute(elCode, attname, value, properties);
+ } catch (java.io.IOException err) {
+ throw new XPathException(err);
+ }
+ }
+
+
+ /**
+ * Escape characters. Overrides the XML behaviour
+ */
+
+ protected void writeEscape(final CharSequence chars, final boolean inAttribute)
+ throws java.io.IOException, XPathException {
+
+ int segstart = 0;
+ final boolean[] specialChars = (inAttribute ? specialInAtt : specialInText);
+
+ if (chars instanceof CompressedWhitespace) {
+ ((CompressedWhitespace)chars).writeEscape(specialChars, writer);
+ return;
+ }
+ boolean disabled = false;
+
+ while (segstart < chars.length()) {
+ int i = segstart;
+
+ // find a maximal sequence of "ordinary" characters
+
+ if (escapeNonAscii) {
+ char c;
+ while (i < chars.length() && (c = chars.charAt(i)) < 127 && !specialChars[c]) {
+ i++;
+ }
+ } else {
+ char c;
+ while (i < chars.length() &&
+ ((c = chars.charAt(i)) < 127 ? !specialChars[c] : (characterSet.inCharset(c) && c > 160)
+ )
+ ) {
+ i++;
+ }
+ }
+
+ // if this was the whole string, output the string and quit
+
+ if (i == chars.length()) {
+ if (segstart == 0) {
+ writeCharSequence(chars);
+ } else {
+ writeCharSequence(chars.subSequence(segstart, i));
+ }
+ return;
+ }
+
+ // otherwise, output this sequence and continue
+ if (i > segstart) {
+ writeCharSequence(chars.subSequence(segstart, i));
+ }
+
+ final char c = chars.charAt(i);
+ if (c==0) {
+ // used to switch escaping on and off
+ disabled = !disabled;
+ } else if (disabled) {
+ writer.write(c);
+ } else if (c<=127) {
+ // handle a special ASCII character
+ if (inAttribute) {
+ if (c=='<') {
+ writer.write('<'); // not escaped
+ } else if (c=='>') {
+ writer.write(">"); // recommended for older browsers
+ } else if (c=='&') {
+ if (i+1<chars.length() && chars.charAt(i+1)=='{') {
+ writer.write('&'); // not escaped if followed by '{'
+ } else {
+ writer.write("&");
+ }
+ } else if (c=='\"') {
+ writer.write(""");
+ } else if (c=='\n') {
+ writer.write("
");
+ } else if (c=='\t') {
+ writer.write("	");
+ } else if (c=='\r') {
+ writer.write("
");
+ }
+ } else {
+ if (c=='<') {
+ writer.write("<");
+ } else if (c=='>') {
+ writer.write(">"); // changed to allow for "]]>"
+ } else if (c=='&') {
+ writer.write("&");
+ } else if (c=='\r') {
+ writer.write("
");
+ }
+ }
+
+ } else if (c < 160) {
+ // these control characters are illegal in HTML
+ XPathException err = new XPathException("Illegal HTML character: decimal " + (int)c);
+ err.setErrorCode("SERE0014");
+ throw err;
+
+ } else if (c==160) {
+ // always output NBSP as an entity reference
+ writer.write(" ");
+
+ } else if (c>=55296 && c<=56319) { //handle surrogate pair
+
+ //A surrogate pair is two consecutive Unicode characters. The first
+ //is in the range D800 to DBFF, the second is in the range DC00 to DFFF.
+ //To compute the numeric value of the character corresponding to a surrogate
+ //pair, use this formula (all numbers are hex):
+ //(FirstChar - D800) * 400 + (SecondChar - DC00) + 10000
+
+ // we'll trust the data to be sound
+ int charval = (((int)c - 55296) * 1024) + ((int)chars.charAt(i+1) - 56320) + 65536;
+ characterReferenceGenerator.outputCharacterReference(charval, writer);
+ i++;
+
+ } else if (escapeNonAscii || !characterSet.inCharset(c)) {
+ characterReferenceGenerator.outputCharacterReference(c, writer);
+ } else {
+ writer.write(c);
+ }
+ segstart = ++i;
+ }
+
+ }
+
+ /**
+ * Output an element end tag.
+ */
+
+ public void endElement() throws XPathException {
+ NodeName nodeName = nodeNameStack.pop();
+ String name = elementStack.peek();
+ inScript--;
+ if (inScript==0) {
+ inScript = -1000000;
+ }
+
+ if (isEmptyTag(name) && isHTMLElement(nodeName)) {
+ // no end tag required
+ elementStack.pop();
+ } else {
+ super.endElement();
+ }
+
+ }
+
+ /**
+ * Character data.
+ */
+
+ public void characters (CharSequence chars, int locationId, int properties)
+ throws XPathException {
+ int options = properties;
+ if (inScript>0) {
+ options |= ReceiverOptions.DISABLE_ESCAPING;
+ }
+ super.characters(chars, locationId, options);
+ }
+
+ /**
+ * Handle a processing instruction.
+ */
+
+ public void processingInstruction (String target, CharSequence data, int locationId, int properties)
+ throws XPathException
+ {
+ if (!started) {
+ openDocument();
+ }
+ for (int i=0; i<data.length(); i++) {
+ if (data.charAt(i) == '>') {
+ XPathException err = new XPathException("A processing instruction in HTML must not contain a > character");
+ err.setErrorCode("SERE0015");
+ throw err;
+ }
+ }
+ try {
+ writer.write("<?");
+ writer.write(target);
+ writer.write(' ');
+ writeCharSequence(data);
+ writer.write('>');
+ } catch (java.io.IOException err) {
+ throw new XPathException(err);
+ }
+ }
+
+
+
+
+
+}
+
diff --git a/sf/saxon/serialize/HTMLIndenter.java b/sf/saxon/serialize/HTMLIndenter.java
new file mode 100644
index 0000000..8157acb
--- /dev/null
+++ b/sf/saxon/serialize/HTMLIndenter.java
@@ -0,0 +1,305 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.FingerprintedQName;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.type.SchemaType;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+* HTMLIndenter: This ProxyEmitter indents HTML elements, by adding whitespace
+* character data where appropriate.
+* The character data is never added when within an inline element.
+* The string used for indentation defaults to three spaces
+*
+* @author Michael Kay
+*/
+
+
+public class HTMLIndenter extends ProxyReceiver {
+
+ private int level = 0;
+
+ private char[] indentChars = {'\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
+
+ private boolean sameLine = false;
+ private boolean inFormattedTag = false;
+ private boolean afterInline = false;
+ private boolean afterFormatted = true; // to prevent a newline at the start
+ /*@NotNull*/ private int[] propertyStack = new int[20];
+ private NameClassifier classifier;
+
+ /**
+ * The NameClassifier classifies element names according to whether the element is (a) an inline element,
+ * and/or (b) a formatted element
+ */
+
+ interface NameClassifier {
+
+ static final int IS_INLINE = 1;
+ static final int IS_FORMATTED = 2;
+
+ /**
+ * Classify an element name as inline, formatted, or both or neither.
+ * This method is overridden in the XHTML indenter
+ * @param name the element name
+ * @return a bit-significant integer containing flags IS_INLINE and/or IS_FORMATTED
+ */
+ int classifyTag(NodeName name);
+
+ }
+
+ final private static String[] inlineTags = {
+ "tt", "i", "b", "u", "s", "strike", "big", "small", "em", "strong", "dfn", "code", "samp",
+ "kbd", "var", "cite", "abbr", "acronym", "a", "img", "applet", "object", "font",
+ "basefont", "br", "script", "map", "q", "sub", "sup", "span", "bdo", "iframe", "input",
+ "select", "textarea", "label", "button", "ins", "del" };
+
+ // INS and DEL are not actually inline elements, but the SGML DTD for HTML
+ // (apparently) permits them to be used as if they were.
+
+ final static String[] formattedTags = {"pre", "script", "style", "textarea", "xmp"};
+ // "xmp" is obsolete but still encountered!
+
+ /**
+ * Class to classify HTML names
+ */
+
+ static class HTMLNameClassifier implements NameClassifier {
+
+ // the list of inline tags is from the HTML 4.0 (loose) spec. The significance is that we
+ // mustn't add spaces immediately before or after one of these elements.
+
+ final static HTMLNameClassifier THE_INSTANCE = new HTMLNameClassifier();
+
+ private static HTMLTagHashSet inlineTable = new HTMLTagHashSet(101);
+
+ static {
+ for (String inlineTag : inlineTags) {
+ inlineTable.add(inlineTag);
+ }
+ }
+
+ /**
+ * Classify an element name as inline, formatted, or both or neither.
+ * This method is overridden in the XHTML indenter
+ * @param elemName the element name
+ * @return a bit-significant integer containing flags IS_INLINE and/or IS_FORMATTED
+ */
+ public int classifyTag(NodeName elemName) {
+ int r = 0;
+ String tag = elemName.getDisplayName();
+ if (inlineTable.contains(tag)) {
+ r |= IS_INLINE;
+ }
+ if (formattedTable.contains(tag)) {
+ r |= IS_FORMATTED;
+ }
+ return r;
+ }
+
+ // Table of preformatted elements
+
+ private static HTMLTagHashSet formattedTable = new HTMLTagHashSet(23);
+
+
+ static {
+ for (String formattedTag : formattedTags) {
+ formattedTable.add(formattedTag);
+ }
+ }
+ }
+
+ /**
+ * Class to classify XHTML names
+ */
+
+ static class XHTMLNameClassifier implements NameClassifier {
+
+ final static XHTMLNameClassifier THE_INSTANCE = new XHTMLNameClassifier();
+
+ private final static HashSet<NodeName> inlineTagSet;
+ private final static HashSet<NodeName> formattedTagSet;
+
+ static {
+ inlineTagSet = new HashSet<NodeName>(50);
+ formattedTagSet = new HashSet<NodeName>(10);
+ for (String inlineTag : inlineTags) {
+ inlineTagSet.add(new FingerprintedQName("", NamespaceConstant.XHTML, inlineTag));
+ }
+ for (String formattedTag : formattedTags) {
+ formattedTagSet.add(new FingerprintedQName("", NamespaceConstant.XHTML, formattedTag));
+ }
+ }
+
+ /**
+ * Classify an element name as inline, formatted, or both or neither.
+ * This method is overridden in the XHTML indenter
+ * @param name the element name
+ * @return a bit-significant integer containing flags IS_INLINE and/or IS_FORMATTED
+ */
+
+ public int classifyTag(NodeName name) {
+ int r = 0;
+ if (inlineTagSet.contains(name)) {
+ r |= IS_INLINE;
+ }
+ if (formattedTagSet.contains(name)) {
+ r |= IS_FORMATTED;
+ }
+ return r;
+ }
+
+ }
+
+ public HTMLIndenter(Receiver next, String method) {
+ super(next);
+ if ("xhtml".equals(method)) {
+ classifier = XHTMLNameClassifier.THE_INSTANCE;
+ } else {
+ classifier = HTMLNameClassifier.THE_INSTANCE;
+ }
+ }
+
+ /**
+ * Output element start tag
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ int tagProps = classifier.classifyTag(nameCode);
+ if (level >= propertyStack.length) {
+ int[] p2 = new int[level*2];
+ System.arraycopy(propertyStack, 0, p2, 0, propertyStack.length);
+ propertyStack = p2;
+ }
+ propertyStack[level] = tagProps;
+ boolean inlineTag = (tagProps & NameClassifier.IS_INLINE) != 0;
+ inFormattedTag = inFormattedTag || ((tagProps & NameClassifier.IS_FORMATTED) != 0);
+ if (!inlineTag && !inFormattedTag &&
+ !afterInline && !afterFormatted) {
+ indent();
+ }
+
+ nextReceiver.startElement(nameCode, typeCode, locationId, properties);
+ level++;
+ sameLine = true;
+ afterInline = false;
+ afterFormatted = false;
+ }
+
+ /**
+ * Output element end tag
+ */
+
+ public void endElement() throws XPathException {
+ level--;
+ boolean thisInline = (propertyStack[level] & NameClassifier.IS_INLINE) != 0;
+ boolean thisFormatted = (propertyStack[level] & NameClassifier.IS_FORMATTED) != 0;
+ if (!thisInline && !thisFormatted && !afterInline &&
+ !sameLine && !afterFormatted && !inFormattedTag) {
+ indent();
+ afterInline = false;
+ afterFormatted = false;
+ } else {
+ afterInline = thisInline;
+ afterFormatted = thisFormatted;
+ }
+ nextReceiver.endElement();
+ inFormattedTag = inFormattedTag && !thisFormatted;
+ sameLine = false;
+ }
+
+ /**
+ * Output character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (inFormattedTag || (properties & ReceiverOptions.USE_NULL_MARKERS) != 0) {
+ // don't split the text if in a tag such as <pre>, or if the text contains the result of
+ // expanding a character map
+ nextReceiver.characters(chars, locationId, properties);
+ } else {
+ // otherwise try to split long lines into multiple lines
+ int lastNL = 0;
+ for (int i=0; i<chars.length(); i++) {
+ if (chars.charAt(i)=='\n' || (i-lastNL > getLineLength() && chars.charAt(i)==' ')) {
+ sameLine = false;
+ nextReceiver.characters(chars.subSequence(lastNL, i), locationId, properties);
+ indent();
+ lastNL = i+1;
+ while (lastNL<chars.length() && chars.charAt(lastNL)==' ') {
+ lastNL++;
+ }
+ }
+ }
+ if (lastNL < chars.length()) {
+ nextReceiver.characters(chars.subSequence(lastNL, chars.length()), locationId, properties);
+ }
+ }
+ afterInline = false;
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ indent();
+ nextReceiver.comment(chars, locationId, properties);
+ }
+
+ /**
+ * Output white space to reflect the current indentation level
+ * @throws net.sf.saxon.trans.XPathException if an error occurs downstream in the pipeline
+ */
+
+ private void indent() throws XPathException {
+ int spaces = level * getIndentation();
+ if (spaces+1 >= indentChars.length) {
+ int increment = 5 * getIndentation();
+ if (spaces + 1 > indentChars.length + increment) {
+ increment += spaces + 1;
+ }
+ char[] c2 = new char[indentChars.length + increment];
+ System.arraycopy(indentChars, 0, c2, 0, indentChars.length);
+ Arrays.fill(c2, indentChars.length, c2.length, ' ');
+ indentChars = c2;
+ }
+ nextReceiver.characters(new CharSlice(indentChars, 0, spaces+1), 0, 0);
+ sameLine = false;
+ }
+
+ /**
+ * Get the number of spaces to be used for indentation
+ * @return the number of spaces to be added to the indentation for each level
+ */
+
+ protected int getIndentation() {
+ return 3;
+ }
+
+ /**
+ * Get the maximum length of lines, after which long lines will be word-wrapped
+ * @return the maximum line length
+ */
+
+ protected int getLineLength() {
+ return 80;
+ }
+
+}
+
diff --git a/sf/saxon/serialize/HTMLTagHashSet.java b/sf/saxon/serialize/HTMLTagHashSet.java
new file mode 100644
index 0000000..e27cfb3
--- /dev/null
+++ b/sf/saxon/serialize/HTMLTagHashSet.java
@@ -0,0 +1,69 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+/**
+* A simple class for testing membership of a fixed set of case-insensitive ASCII strings.
+* The class must be initialised with enough space for all the strings,
+* it will go into an infinite loop if it fills. The string matching is case-blind,
+* using an algorithm that works only for ASCII.
+*
+* The class implements part of the java.util.Set interface; it could be replaced with
+* an implementation of java.util.Set together with a class that implemented a customized
+* equals() method.
+*/
+
+public class HTMLTagHashSet {
+
+ String[] strings;
+ int size;
+
+ public HTMLTagHashSet(int size) {
+ strings = new String[size];
+ this.size = size;
+ }
+
+ public void add(String s) {
+ int hash = (hashCode(s) & 0x7fffffff) % size;
+ while(true) {
+ if (strings[hash]==null) {
+ strings[hash] = s;
+ return;
+ }
+ if (strings[hash].equalsIgnoreCase(s)) {
+ return;
+ }
+ hash = (hash + 1) % size;
+ }
+ }
+
+ public boolean contains(/*@NotNull*/ String s) {
+ int hash = (hashCode(s) & 0x7fffffff) % size;
+ while(true) {
+ if (strings[hash]==null) {
+ return false;
+ }
+ if (strings[hash].equalsIgnoreCase(s)) {
+ return true;
+ }
+ hash = (hash + 1) % size;
+ }
+ }
+
+ private int hashCode(String s) {
+ // get a hashcode that doesn't depend on the case of characters.
+ // This relies on the fact that char & 0xDF is case-blind in ASCII
+ int hash = 0;
+ int limit = s.length();
+ if (limit>24) limit = 24;
+ for (int i=0; i<limit; i++) {
+ hash = (hash<<1) + (s.charAt(i) & 0xdf);
+ }
+ return hash;
+ }
+}
diff --git a/sf/saxon/serialize/HTMLURIEscaper.java b/sf/saxon/serialize/HTMLURIEscaper.java
new file mode 100644
index 0000000..23ba8c4
--- /dev/null
+++ b/sf/saxon/serialize/HTMLURIEscaper.java
@@ -0,0 +1,203 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.serialize.charcode.UTF8CharacterSet;
+import net.sf.saxon.serialize.codenorm.Normalizer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+/**
+ * This class is used as a filter on the serialization pipeline; it performs the function
+ * of escaping URI-valued attributes in HTML
+ * @author Michael H. Kay
+ */
+
+public class HTMLURIEscaper extends ProxyReceiver {
+
+ /**
+ * Table of attributes whose value is a URL
+ */
+
+ // we use two HashMaps to avoid unnecessary string concatenations
+
+ private static HTMLTagHashSet urlAttributes = new HTMLTagHashSet(47);
+ private static HTMLTagHashSet urlCombinations = new HTMLTagHashSet(101);
+
+ static {
+ setUrlAttribute("form", "action");
+ setUrlAttribute("object", "archive");
+ setUrlAttribute("body", "background");
+ setUrlAttribute("q", "cite");
+ setUrlAttribute("blockquote", "cite");
+ setUrlAttribute("del", "cite");
+ setUrlAttribute("ins", "cite");
+ setUrlAttribute("object", "classid");
+ setUrlAttribute("object", "codebase");
+ setUrlAttribute("applet", "codebase");
+ setUrlAttribute("object", "data");
+ setUrlAttribute("button", "datasrc");
+ setUrlAttribute("div", "datasrc");
+ setUrlAttribute("input", "datasrc");
+ setUrlAttribute("object", "datasrc");
+ setUrlAttribute("select", "datasrc");
+ setUrlAttribute("span", "datasrc");
+ setUrlAttribute("table", "datasrc");
+ setUrlAttribute("textarea", "datasrc");
+ setUrlAttribute("script", "for");
+ setUrlAttribute("a", "href");
+ setUrlAttribute("a", "name"); // see second note in section B.2.1 of HTML 4 specification
+ setUrlAttribute("area", "href");
+ setUrlAttribute("link", "href");
+ setUrlAttribute("base", "href");
+ setUrlAttribute("img", "longdesc");
+ setUrlAttribute("frame", "longdesc");
+ setUrlAttribute("iframe", "longdesc");
+ setUrlAttribute("head", "profile");
+ setUrlAttribute("script", "src");
+ setUrlAttribute("input", "src");
+ setUrlAttribute("frame", "src");
+ setUrlAttribute("iframe", "src");
+ setUrlAttribute("img", "src");
+ setUrlAttribute("img", "usemap");
+ setUrlAttribute("input", "usemap");
+ setUrlAttribute("object", "usemap");
+ }
+
+ private static void setUrlAttribute(String element, String attribute) {
+ urlAttributes.add(attribute);
+ urlCombinations.add(element + '+' + attribute);
+ }
+
+ public boolean isUrlAttribute(NodeName element, NodeName attribute) {
+ if (pool == null) {
+ pool = getNamePool();
+ }
+ String attributeName = attribute.getDisplayName();
+ if (!urlAttributes.contains(attributeName)) {
+ return false;
+ }
+ String elementName = element.getDisplayName();
+ return urlCombinations.contains(elementName + '+' + attributeName);
+ }
+
+ protected NodeName currentElement;
+ protected boolean escapeURIAttributes = true;
+ protected NamePool pool;
+
+ public HTMLURIEscaper(Receiver nextReceiver) {
+ super(nextReceiver);
+ }
+
+ /**
+ * Start of a document node.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ nextReceiver.startDocument(properties);
+ pool = getPipelineConfiguration().getConfiguration().getNamePool();
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param nameCode integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties properties of the element node
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ currentElement = nameCode;
+ nextReceiver.startElement(nameCode, typeCode, locationId, properties);
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param nameCode The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ if (escapeURIAttributes &&
+ isUrlAttribute(currentElement, nameCode) &&
+ (properties & ReceiverOptions.DISABLE_ESCAPING) == 0) {
+ nextReceiver.attribute(nameCode, typeCode, escapeURL(value, true, getConfiguration()), locationId,
+ properties | ReceiverOptions.DISABLE_CHARACTER_MAPS);
+ } else {
+ nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+ }
+
+ /**
+ * Escape a URI according to the HTML rules: that is, a non-ASCII character (specifically,
+ * a character outside the range 32 - 126) is replaced by the %HH encoding of the octets in
+ * its UTF-8 representation
+ * @param url the URI to be escaped
+ * @param normalize
+ * @return the URI after escaping non-ASCII characters
+ */
+
+ /*@NotNull*/ public static CharSequence escapeURL(CharSequence url, boolean normalize, Configuration config) throws XPathException {
+ // optimize for the common case where the string is all ASCII characters
+ for (int i=url.length()-1; i>=0; i--) {
+ char ch = url.charAt(i);
+ if (ch<32 || ch>126) {
+ if (normalize) {
+ CharSequence normalized = new Normalizer(Normalizer.C, config).normalize(url);
+ return reallyEscapeURL(normalized);
+ } else {
+ return reallyEscapeURL(url);
+ }
+ }
+ }
+ return url;
+ }
+
+ private static CharSequence reallyEscapeURL(CharSequence url) {
+ FastStringBuffer sb = new FastStringBuffer(url.length() + 20);
+ final String hex = "0123456789ABCDEF";
+ byte[] array = new byte[4];
+
+ for (int i=0; i<url.length(); i++) {
+ char ch = url.charAt(i);
+ if (ch<32 || ch>126) {
+ int used = UTF8CharacterSet.getUTF8Encoding(ch,
+ (i+1 < url.length() ? url.charAt(i+1): ' '), array);
+ for (int b=0; b<used; b++) {
+ //int v = (array[b]>=0 ? array[b] : 256 + array[b]);
+ int v = ((int)array[b]) & 0xff;
+ sb.append('%');
+ sb.append(hex.charAt(v/16));
+ sb.append(hex.charAt(v%16));
+ }
+
+ } else {
+ sb.append(ch);
+ }
+ }
+ return sb;
+ }
+}
+
diff --git a/sf/saxon/serialize/HexCharacterReferenceGenerator.java b/sf/saxon/serialize/HexCharacterReferenceGenerator.java
new file mode 100644
index 0000000..dfddd80
--- /dev/null
+++ b/sf/saxon/serialize/HexCharacterReferenceGenerator.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import java.io.Writer;
+
+/**
+ * A class that represents a character as a hexadecimal character reference
+ * and writes the result to a supplied Writer
+*/
+
+public class HexCharacterReferenceGenerator implements CharacterReferenceGenerator {
+
+ public final static HexCharacterReferenceGenerator THE_INSTANCE = new HexCharacterReferenceGenerator();
+
+ private HexCharacterReferenceGenerator(){}
+
+ public void outputCharacterReference(int charval, Writer writer) throws java.io.IOException {
+ writer.write("&#x");
+ writer.write(Integer.toHexString(charval));
+ writer.write(';');
+ }
+}
+
diff --git a/sf/saxon/serialize/ImplicitResultChecker.java b/sf/saxon/serialize/ImplicitResultChecker.java
new file mode 100644
index 0000000..2bfa177
--- /dev/null
+++ b/sf/saxon/serialize/ImplicitResultChecker.java
@@ -0,0 +1,107 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+
+/**
+ * This filter is inserted into the serializer pipeline when serializing an implicit XSLT result tree, that
+ * is, one that is created without use of xsl:result-document. Its main purpose is to check, if and only if
+ * the result destination is actually written to, that it does not conflict with an explicit result destination
+ * with the same URI. It also ensures that the output destination is opened before it is first written to.
+ */
+public class ImplicitResultChecker extends ProxyReceiver {
+
+ private boolean clean = true;
+ private boolean open = false;
+ private Controller controller;
+
+ /**
+ * Create an ImplicitResultChecker. This is a filter on the output pipeline.
+ * @param next the next receiver on the pipeline
+ * @param controller the controller of the XSLT transformation
+ */
+
+ public ImplicitResultChecker(Receiver next, Controller controller) {
+ super(next);
+ this.controller = controller;
+ }
+
+ public void open() throws XPathException {
+ super.open();
+ open = true;
+ }
+
+ public synchronized void startDocument(int properties) throws XPathException {
+ if (!open) {
+ open();
+ }
+ nextReceiver.startDocument(properties);
+ }
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (clean) {
+ firstContent();
+ }
+ nextReceiver.startElement(nameCode, typeCode, locationId, properties);
+ }
+
+ public synchronized void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (clean) {
+ firstContent();
+ }
+ nextReceiver.characters(chars, locationId, properties);
+ }
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (clean) {
+ firstContent();
+ }
+ nextReceiver.processingInstruction(target, data, locationId, properties);
+ }
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (clean) {
+ firstContent();
+ }
+ nextReceiver.comment(chars, locationId, properties);
+ }
+
+ /**
+ * This method does the real work. It is called when the first output is written to the implicit output
+ * destination, and checks that no explicit result document has been written to the same URI
+ * as the implicit result document
+ * @throws XPathException
+ */
+
+ private synchronized void firstContent() throws XPathException {
+ controller.checkImplicitResultTree();
+ if (!open) {
+ open();
+ startDocument(0);
+ }
+ clean = false;
+ }
+
+ public void close() throws XPathException {
+ // If we haven't written any output, do the close only if no explicit result document has been written.
+ // This will cause a file to be created and perhaps an XML declaration to be written
+ if (!clean || !controller.hasThereBeenAnExplicitResultDocument()) {
+ if (!open) {
+ open();
+ }
+ nextReceiver.close();
+ }
+ }
+}
+
diff --git a/sf/saxon/serialize/MessageEmitter.java b/sf/saxon/serialize/MessageEmitter.java
new file mode 100644
index 0000000..0962e50
--- /dev/null
+++ b/sf/saxon/serialize/MessageEmitter.java
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+import net.sf.saxon.trans.XPathException;
+
+
+/**
+ * MessageEmitter is the default Receiver for xsl:message output.
+ * It is the same as XMLEmitter except for an extra newline at the end of the message
+ */
+
+public class MessageEmitter extends XMLEmitter
+{
+ public void endDocument() throws XPathException {
+ try {
+ writer.write('\n');
+ } catch (java.io.IOException err) {
+ throw new XPathException(err);
+ }
+ super.close();
+ }
+
+ public void close() throws XPathException {
+ try {
+ if (writer != null) {
+ writer.flush();
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException(err);
+ }
+ super.close();
+ }
+
+}
+
diff --git a/sf/saxon/serialize/MessageWarner.java b/sf/saxon/serialize/MessageWarner.java
new file mode 100644
index 0000000..dd527eb
--- /dev/null
+++ b/sf/saxon/serialize/MessageWarner.java
@@ -0,0 +1,58 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.TransformerException;
+import java.io.StringWriter;
+
+
+/**
+ * MessageWarner is a user-selectable receiver for XSLT xsl:message output. It causes xsl:message output
+ * to be notified to the warning() method of the JAXP ErrorListener, or to the error() method if
+ * terminate="yes" is specified. This behaviour is specified in recent versions of the JAXP interface
+ * specifications, but it is not the default behaviour, for backwards compatibility reasons.
+ *
+ * <p>The text of the message that is sent to the ErrorListener is an XML serialization of the actual
+ * message content.</p>
+ */
+
+public class MessageWarner extends XMLEmitter {
+
+ boolean abort = false;
+
+ public void startDocument(int properties) throws XPathException {
+ setWriter(new StringWriter());
+ abort = (properties & ReceiverOptions.TERMINATE) != 0;
+ super.startDocument(properties);
+ }
+
+ public void endDocument() throws XPathException {
+ ErrorListener listener = getPipelineConfiguration().getErrorListener();
+ XPathException de = new XPathException(getWriter().toString());
+ de.setErrorCode("XTMM9000");
+ try {
+ if (abort) {
+ listener.error(de);
+ } else {
+ listener.warning(de);
+ }
+ } catch (TransformerException te) {
+ throw XPathException.makeXPathException(te);
+ }
+ }
+
+ public void close() {
+ // do nothing
+ }
+
+}
+
diff --git a/sf/saxon/serialize/MetaTagAdjuster.java b/sf/saxon/serialize/MetaTagAdjuster.java
new file mode 100644
index 0000000..fae1648
--- /dev/null
+++ b/sf/saxon/serialize/MetaTagAdjuster.java
@@ -0,0 +1,248 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.OutputKeys;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * The MetaTagAdjuster adds a meta element to the content of the head element, indicating
+ * the required content type and encoding; it also removes any existing meta element
+ * containing this information
+ */
+
+public class MetaTagAdjuster extends ProxyReceiver {
+
+ boolean seekingHead = true;
+ int droppingMetaTags = -1;
+ boolean inMetaTag = false;
+ boolean foundHead = false;
+ /*@Nullable*/ String headPrefix = null;
+ NodeName metaCode;
+ String requiredURI = "";
+ AttributeCollectionImpl attributes;
+ List<NamespaceBinding> namespaces = new ArrayList<NamespaceBinding>();
+ String encoding;
+ String mediaType;
+ int level = 0;
+ boolean isXHTML = false;
+
+ /**
+ * Create a new MetaTagAdjuster
+ * @param next the next receiver in the pipeline
+ */
+
+ public MetaTagAdjuster(Receiver next) {
+ super(next);
+ }
+
+ /**
+ * Set output properties
+ * @param details the serialization properties
+ */
+
+ public void setOutputProperties(Properties details) {
+ encoding = details.getProperty(OutputKeys.ENCODING);
+ if (encoding == null) {
+ encoding = "UTF-8";
+ }
+ mediaType = details.getProperty(OutputKeys.MEDIA_TYPE);
+ if (mediaType == null) {
+ mediaType = "text/html";
+ }
+ }
+
+ /**
+ * Indicate whether we're handling HTML or XHTML
+ */
+
+ public void setIsXHTML(boolean xhtml) {
+ isXHTML = xhtml;
+ if (xhtml) {
+ requiredURI = NamespaceConstant.XHTML;
+ } else {
+ requiredURI = "";
+ }
+ }
+
+ /**
+ * Compare a name: case-blindly in the case of HTML, case-sensitive for XHTML
+ */
+
+ private boolean comparesEqual(String name1, String name2) {
+ if (isXHTML) {
+ return name1.equals(name2);
+ } else {
+ return name1.equalsIgnoreCase(name2);
+ }
+
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param nameCode integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties properties of the element node
+ */
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (droppingMetaTags == level) {
+ metaCode = nameCode;
+ String localName = nameCode.getLocalPart();
+ if (nameCode.isInNamespace(requiredURI) && comparesEqual(localName, "meta")) {
+ inMetaTag = true;
+ attributes.clear();
+ namespaces.clear();
+ return;
+ }
+ }
+ level++;
+ nextReceiver.startElement(nameCode, typeCode, locationId, properties);
+ if (seekingHead) {
+ NamePool namePool = getNamePool();
+ String localName = nameCode.getLocalPart();
+ if (nameCode.isInNamespace(requiredURI) && comparesEqual(localName, "head")) {
+ foundHead = true;
+ headPrefix = nameCode.getPrefix();
+ }
+ }
+
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param nameCode The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ if (inMetaTag) {
+ attributes.addAttribute(nameCode, typeCode, value.toString(), locationId, properties);
+ } else {
+ nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element; however, duplicates may be reported.
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ *
+ * @param namespaceBinding the prefix/uri pair representing the namespace binding
+ * @param properties any special properties to be passed on this call
+ * @throws IllegalStateException: attempt to output a namespace when there is no open element
+ * start tag
+ */
+ @Override
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ if (inMetaTag) {
+ // fix bug 1788
+ namespaces.add(namespaceBinding);
+ } else {
+ nextReceiver.namespace(namespaceBinding, properties);
+ }
+ }
+
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+
+
+ public void startContent() throws XPathException {
+ if (foundHead) {
+ foundHead = false;
+ NamePool namePool = getNamePool();
+ nextReceiver.startContent();
+ FingerprintedQName metaCode = new FingerprintedQName(headPrefix, requiredURI, "meta");
+ nextReceiver.startElement(metaCode, Untyped.getInstance(), 0, 0);
+ nextReceiver.attribute(new NoNamespaceName("http-equiv"), BuiltInAtomicType.UNTYPED_ATOMIC, "Content-Type", 0, 0);
+ nextReceiver.attribute(new NoNamespaceName("content"), BuiltInAtomicType.UNTYPED_ATOMIC, mediaType + "; charset=" + encoding, 0, 0);
+ nextReceiver.startContent();
+ droppingMetaTags = level;
+ seekingHead = false;
+ attributes = new AttributeCollectionImpl(getConfiguration());
+ nextReceiver.endElement();
+ }
+ if (!inMetaTag) {
+ nextReceiver.startContent();
+ }
+ }
+
+ /**
+ * End of element
+ */
+
+ public void endElement() throws XPathException {
+ if (inMetaTag) {
+ inMetaTag = false;
+ // if there was an http-equiv="ContentType" attribute, discard the meta element entirely
+ boolean found = false;
+ for (int i=0; i<attributes.getLength(); i++) {
+ String name = attributes.getLocalName(i);
+ if (comparesEqual(name, "http-equiv")) {
+ String value = Whitespace.trim(attributes.getValue(i));
+ if (value.equalsIgnoreCase("Content-Type")) {
+ // case-blind comparison even for XHTML
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ // this was a meta element, but not one of the kind that we discard
+ nextReceiver.startElement(metaCode, Untyped.getInstance(), 0, 0);
+ for (int i=0; i<attributes.getLength(); i++) {
+ NodeName name = attributes.getNodeName(i);
+ SimpleType typeCode = attributes.getTypeAnnotation(i);
+ String value = attributes.getValue(i);
+ int locationId = attributes.getLocationId(i);
+ int properties = attributes.getProperties(i);
+ nextReceiver.attribute(name, typeCode, value, locationId, properties);
+ }
+ nextReceiver.startContent();
+ nextReceiver.endElement();
+ }
+ } else {
+ level--;
+ if (droppingMetaTags == level+1) {
+ droppingMetaTags = -1;
+ }
+ nextReceiver.endElement();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/serialize/TEXTEmitter.java b/sf/saxon/serialize/TEXTEmitter.java
new file mode 100644
index 0000000..baf9085
--- /dev/null
+++ b/sf/saxon/serialize/TEXTEmitter.java
@@ -0,0 +1,136 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.serialize.charcode.UTF8CharacterSet;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+import javax.xml.transform.OutputKeys;
+
+/**
+ * This class generates TEXT output
+ * @author Michael H. Kay
+ */
+
+public class TEXTEmitter extends XMLEmitter {
+
+ /**
+ * Start of the document.
+ */
+
+ public void open () throws XPathException {}
+
+ protected void openDocument() throws XPathException {
+
+ if (writer==null) {
+ makeWriter();
+ }
+ if (characterSet==null) {
+ characterSet = UTF8CharacterSet.getInstance();
+ }
+ // Write a BOM if requested
+ String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
+ if (encoding==null || encoding.equalsIgnoreCase("utf8")) {
+ encoding = "UTF-8";
+ }
+ String byteOrderMark = outputProperties.getProperty(SaxonOutputKeys.BYTE_ORDER_MARK);
+
+ if ("yes".equals(byteOrderMark) && (
+ "UTF-8".equalsIgnoreCase(encoding) ||
+ "UTF-16LE".equalsIgnoreCase(encoding) ||
+ "UTF-16BE".equalsIgnoreCase(encoding))) {
+ try {
+ writer.write('\uFEFF');
+ } catch (java.io.IOException err) {
+ // Might be an encoding exception; just ignore it
+ }
+ }
+ started = true;
+ }
+
+ /**
+ * Output the XML declaration. This implementation does nothing.
+ */
+
+ public void writeDeclaration() throws XPathException {}
+
+ /**
+ * Produce output using the current Writer. <BR>
+ * Special characters are not escaped.
+ * @param chars Character sequence to be output
+ * @param properties bit fields holding special properties of the characters
+ * @exception XPathException for any failure
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (!started) {
+ openDocument();
+ }
+ if ((properties & ReceiverOptions.NO_SPECIAL_CHARS) == 0) {
+ int badchar = testCharacters(chars);
+ if (badchar != 0) {
+ throw new XPathException(
+ "Output character not available in this encoding (decimal " + badchar + ")");
+ }
+ }
+ try {
+ writer.write(chars.toString());
+ } catch (java.io.IOException err) {
+ throw new XPathException(err);
+ }
+ }
+
+ /**
+ * Output an element start tag. <br>
+ * Does nothing with this output method.
+ * @param elemName The element name (tag)
+ * @param typeCode The type annotation
+ * @param properties Bit fields holding any special properties of the element
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) {
+ // no-op
+ }
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) {}
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) {}
+
+
+ /**
+ * Output an element end tag. <br>
+ * Does nothing with this output method.
+ */
+
+ public void endElement() {
+ // no-op
+ }
+
+ /**
+ * Output a processing instruction. <br>
+ * Does nothing with this output method.
+ */
+
+ public void processingInstruction(String name, /*@NotNull*/ CharSequence value, int locationId, int properties)
+ throws XPathException {}
+
+ /**
+ * Output a comment. <br>
+ * Does nothing with this output method.
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {}
+
+}
+
diff --git a/sf/saxon/serialize/UTF8Writer.java b/sf/saxon/serialize/UTF8Writer.java
new file mode 100644
index 0000000..e98e4a7
--- /dev/null
+++ b/sf/saxon/serialize/UTF8Writer.java
@@ -0,0 +1,407 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+
+/**
+ * Specialized buffering UTF-8 writer.
+ * The main reason for custom version is to allow for efficient
+ * buffer recycling; the second benefit is that encoder has less
+ * overhead for short content encoding (compared to JDK default
+ * codecs).
+ *
+ * @author Tatu Saloranta
+ */
+public final class UTF8Writer
+ extends Writer
+{
+ private final static int MIN_BUF_LEN = 32;
+ private final static int DEFAULT_BUF_LEN = 4000;
+
+ final static int SURR1_FIRST = 0xD800;
+ final static int SURR1_LAST = 0xDBFF;
+ final static int SURR2_FIRST = 0xDC00;
+ final static int SURR2_LAST = 0xDFFF;
+
+ /*@Nullable*/ protected OutputStream _out;
+
+ protected byte[] _outBuffer;
+
+ final protected int _outBufferLast;
+
+ protected int _outPtr;
+
+ /**
+ * When outputting chars from BMP, surrogate pairs need to be coalesced.
+ * To do this, both pairs must be known first; and since it is possible
+ * pairs may be split, we need temporary storage for the first half
+ */
+ int _surrogate = 0;
+
+ public UTF8Writer(OutputStream out)
+ {
+ this(out, DEFAULT_BUF_LEN);
+ }
+
+ public UTF8Writer(OutputStream out, int bufferLength)
+ {
+ if (bufferLength < MIN_BUF_LEN) {
+ bufferLength = MIN_BUF_LEN;
+ }
+ _out = out;
+ _outBuffer = new byte[bufferLength];
+ /* Max. expansion for a single Unicode code point is 4 bytes when
+ * recombining UCS-2 surrogate pairs, so:
+ */
+ _outBufferLast = bufferLength - 4;
+ _outPtr = 0;
+ }
+
+ /*
+ ////////////////////////////////////////////////////////
+ // java.io.Writer implementation
+ ////////////////////////////////////////////////////////
+ */
+
+ /* Due to co-variance between Appendable and
+ * Writer, this would not compile with javac 1.5, in 1.4 mode
+ * (source and target set to "1.4". Not a huge deal, but since
+ * the base impl is just fine, no point in overriding it.
+ */
+ /*
+ public Writer append(char c) throws IOException {
+ // note: this is a JDK 1.5 method
+ write(c);
+ return this;
+ }
+ */
+
+ public void close() throws IOException
+ {
+ if (_out != null) {
+ _flushBuffer();
+ _outBuffer = null;
+ _out.close();
+ _out = null;
+
+ /* If we are left with partial surrogate we have a problem, but
+ * let's not let it prevent closure of the underlying stream
+ */
+ if (_surrogate != 0) {
+ int code = _surrogate;
+ // but let's clear it, to get just one problem?
+ _surrogate = 0;
+ throwIllegal(code);
+ }
+ }
+ }
+
+ public void flush() throws IOException
+ {
+ _flushBuffer();
+ _out.flush();
+ }
+
+ public void write(char[] cbuf) throws IOException
+ {
+ write(cbuf, 0, cbuf.length);
+ }
+
+ public void write(char[] cbuf, int off, int len)
+ throws IOException
+ {
+ if (len < 2) {
+ if (len == 1) {
+ write(cbuf[off]);
+ }
+ return;
+ }
+
+ // First: do we have a leftover surrogate to deal with?
+ if (_surrogate > 0) {
+ char second = cbuf[off++];
+ --len;
+ write(_convertSurrogate(second));
+ // will have at least one more char
+ }
+
+ int outPtr = _outPtr;
+ byte[] outBuf = _outBuffer;
+ int outBufLast = _outBufferLast; // has 4 'spare' bytes
+
+ // All right; can just loop it nice and easy now:
+ len += off; // len will now be the end of input buffer
+
+ output_loop:
+ for (; off < len; ) {
+ /* First, let's ensure we can output at least 4 bytes
+ * (longest UTF-8 encoded codepoint):
+ */
+ if (outPtr >= outBufLast) {
+ _out.write(outBuf, 0, outPtr);
+ outPtr = 0;
+ }
+
+ int c = cbuf[off++];
+ // And then see if we have an Ascii char:
+ if (c < 0x80) { // If so, can do a tight inner loop:
+ outBuf[outPtr++] = (byte)c;
+ // Let's calc how many ascii chars we can copy at most:
+ int maxInCount = (len - off);
+ int maxOutCount = (outBufLast - outPtr);
+
+ if (maxInCount > maxOutCount) {
+ maxInCount = maxOutCount;
+ }
+ maxInCount += off;
+ ascii_loop:
+ while (true) {
+ if (off >= maxInCount) { // done with max. ascii seq
+ continue output_loop;
+ }
+ c = cbuf[off++];
+ if (c >= 0x80) {
+ break ascii_loop;
+ }
+ outBuf[outPtr++] = (byte) c;
+ }
+ }
+
+ // Nope, multi-byte:
+ if (c < 0x800) { // 2-byte
+ outBuf[outPtr++] = (byte) (0xc0 | (c >> 6));
+ outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+ } else { // 3 or 4 bytes
+ // Surrogates?
+ if (c < SURR1_FIRST || c > SURR2_LAST) {
+ outBuf[outPtr++] = (byte) (0xe0 | (c >> 12));
+ outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+ outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+ continue;
+ }
+ // Yup, a surrogate:
+ if (c > SURR1_LAST) { // must be from first range
+ _outPtr = outPtr;
+ throwIllegal(c);
+ }
+ _surrogate = c;
+ // and if so, followed by another from next range
+ if (off >= len) { // unless we hit the end?
+ break;
+ }
+ c = _convertSurrogate(cbuf[off++]);
+ if (c > 0x10FFFF) { // illegal, as per RFC 3629
+ _outPtr = outPtr;
+ throwIllegal(c);
+ }
+ outBuf[outPtr++] = (byte) (0xf0 | (c >> 18));
+ outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+ outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+ outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+ }
+ }
+ _outPtr = outPtr;
+ }
+
+ public void write(int c)
+ throws IOException
+ {
+ // First; do we have a left over surrogate?
+ if (_surrogate > 0) {
+ c = _convertSurrogate(c);
+ // If not, do we start with a surrogate?
+ } else if (c >= SURR1_FIRST && c <= SURR2_LAST) {
+ // Illegal to get second part without first:
+ if (c > SURR1_LAST) {
+ throwIllegal(c);
+ }
+ // First part just needs to be held for now
+ _surrogate = c;
+ return;
+ }
+
+ if (_outPtr >= _outBufferLast) { // let's require enough room, first
+ _flushBuffer();
+ }
+
+ if (c < 0x80) { // ascii
+ _outBuffer[_outPtr++] = (byte) c;
+ } else {
+ int ptr = _outPtr;
+ if (c < 0x800) { // 2-byte
+ _outBuffer[ptr++] = (byte) (0xc0 | (c >> 6));
+ _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
+ } else if (c <= 0xFFFF) { // 3 bytes
+ _outBuffer[ptr++] = (byte) (0xe0 | (c >> 12));
+ _outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+ _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
+ } else { // 4 bytes
+ if (c > 0x10FFFF) { // illegal, as per RFC 3629
+ throwIllegal(c);
+ }
+ _outBuffer[ptr++] = (byte) (0xf0 | (c >> 18));
+ _outBuffer[ptr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+ _outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+ _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
+ }
+ _outPtr = ptr;
+ }
+ }
+
+ public void write(String str)
+ throws IOException
+ {
+ write(str, 0, str.length());
+ }
+
+ public void write(String str, int off, int len)
+ throws IOException
+ {
+ if (len < 2) {
+ if (len == 1) {
+ write(str.charAt(off));
+ }
+ return;
+ }
+
+ // First: do we have a leftover surrogate to deal with?
+ if (_surrogate > 0) {
+ char second = str.charAt(off++);
+ --len;
+ write(_convertSurrogate(second));
+ // will have at least one more char (case of 1 char was checked earlier on)
+ }
+
+ int outPtr = _outPtr;
+ byte[] outBuf = _outBuffer;
+ int outBufLast = _outBufferLast; // has 4 'spare' bytes
+
+ // All right; can just loop it nice and easy now:
+ len += off; // len will now be the end of input buffer
+
+ output_loop:
+ for (; off < len; ) {
+ // First, let's ensure we can output at least 4 bytes
+ // (longest UTF-8 encoded codepoint):
+ if (outPtr >= outBufLast) {
+ _out.write(outBuf, 0, outPtr);
+ outPtr = 0;
+ }
+
+ int c = str.charAt(off++);
+ // And then see if we have an Ascii char:
+ if (c < 0x80) { // If so, can do a tight inner loop:
+ outBuf[outPtr++] = (byte)c;
+ // Let's calc how many ascii chars we can copy at most:
+ int maxInCount = (len - off);
+ int maxOutCount = (outBufLast - outPtr);
+
+ if (maxInCount > maxOutCount) {
+ maxInCount = maxOutCount;
+ }
+ maxInCount += off;
+ ascii_loop:
+ while (true) {
+ if (off >= maxInCount) { // done with max. ascii seq
+ continue output_loop;
+ }
+ c = str.charAt(off++);
+ if (c >= 0x80) {
+ break ascii_loop;
+ }
+ outBuf[outPtr++] = (byte) c;
+ }
+ }
+
+ // Nope, multi-byte:
+ if (c < 0x800) { // 2-byte
+ outBuf[outPtr++] = (byte) (0xc0 | (c >> 6));
+ outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+ } else { // 3 or 4 bytes
+ // Surrogates?
+ if (c < SURR1_FIRST || c > SURR2_LAST) {
+ outBuf[outPtr++] = (byte) (0xe0 | (c >> 12));
+ outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+ outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+ continue;
+ }
+ // Yup, a surrogate:
+ if (c > SURR1_LAST) { // must be from first range
+ _outPtr = outPtr;
+ throwIllegal(c);
+ }
+ _surrogate = c;
+ // and if so, followed by another from next range
+ if (off >= len) { // unless we hit the end?
+ break;
+ }
+ c = _convertSurrogate(str.charAt(off++));
+ if (c > 0x10FFFF) { // illegal, as per RFC 3629
+ _outPtr = outPtr;
+ throwIllegal(c);
+ }
+ outBuf[outPtr++] = (byte) (0xf0 | (c >> 18));
+ outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+ outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+ outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+ }
+ }
+ _outPtr = outPtr;
+ }
+
+ /*
+ ////////////////////////////////////////////////////////////
+ // Internal methods
+ ////////////////////////////////////////////////////////////
+ */
+
+ private void _flushBuffer() throws IOException
+ {
+ if (_outPtr > 0 && _outBuffer != null) {
+ _out.write(_outBuffer, 0, _outPtr);
+ _outPtr = 0;
+ }
+ }
+
+ /**
+ * Method called to calculate UTF codepoint, from a surrogate pair.
+ */
+ private final int _convertSurrogate(int secondPart)
+ throws IOException
+ {
+ int firstPart = _surrogate;
+ _surrogate = 0;
+
+ // Ok, then, is the second part valid?
+ if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) {
+ throw new IOException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination");
+ }
+ return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST);
+ }
+
+ private void throwIllegal(int code)
+ throws IOException
+ {
+ if (code > 0x10FFFF) { // over max?
+ throw new IOException("Illegal character point (0x"+Integer.toHexString(code)+") to output; max is 0x10FFFF as per RFC 3629");
+ }
+ if (code >= SURR1_FIRST) {
+ if (code <= SURR1_LAST) { // Unmatched first part (closing without second part?)
+ throw new IOException("Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")");
+ }
+ throw new IOException("Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")");
+ }
+
+ // should we ever get this?
+ throw new IOException("Illegal character point (0x"+Integer.toHexString(code)+") to output");
+ }
+}
+
diff --git a/sf/saxon/serialize/UncommittedSerializer.java b/sf/saxon/serialize/UncommittedSerializer.java
new file mode 100644
index 0000000..cfa0837
--- /dev/null
+++ b/sf/saxon/serialize/UncommittedSerializer.java
@@ -0,0 +1,211 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.lib.SerializerFactory;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * This class is used when the decision on which serialization method to use has to be delayed until the first
+ * element is read. It buffers comments and processing instructions until that happens; then when the first
+ * element arrives it creates a real serialization pipeline and uses that for future output.
+ * @author Michael H. Kay
+ */
+
+public class UncommittedSerializer extends ProxyReceiver {
+
+ boolean committed = false;
+ /*@Nullable*/ List<PendingNode> pending = null;
+ Result finalResult;
+ Properties outputProperties;
+
+ /**
+ * Create an uncommitted Serializer
+ * @param finalResult the output destination
+ * @param next the next receiver in the pipeline
+ * @param outputProperties the serialization properties
+ */
+
+ public UncommittedSerializer(Result finalResult, Receiver next, Properties outputProperties) {
+ super(next);
+ this.finalResult = finalResult;
+ this.outputProperties = outputProperties;
+ }
+
+ public void open() throws XPathException {
+ committed = false;
+ }
+
+ /**
+ * End of document
+ */
+
+ public void close() throws XPathException {
+ // empty output: must send a beginDocument()/endDocument() pair to the content handler
+ if (!committed) {
+ switchToMethod("xml");
+ }
+ getUnderlyingReceiver().close();
+ }
+
+ /**
+ * Produce character output using the current Writer. <BR>
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (committed) {
+ getUnderlyingReceiver().characters(chars, locationId, properties);
+ } else {
+ if (pending==null) {
+ pending = new ArrayList<PendingNode>(10);
+ }
+ PendingNode node = new PendingNode();
+ node.kind = Type.TEXT;
+ node.name = null;
+ node.content = chars.toString(); // needs to be immutable
+ node.locationId = locationId;
+ node.properties = properties;
+ pending.add(node);
+ if (!Whitespace.isWhite(chars)) {
+ switchToMethod("xml");
+ }
+ }
+ }
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (committed) {
+ getUnderlyingReceiver().processingInstruction(target, data, locationId, properties);
+ } else {
+ if (pending==null) {
+ pending = new ArrayList<PendingNode>(10);
+ }
+ PendingNode node = new PendingNode();
+ node.kind = Type.PROCESSING_INSTRUCTION;
+ node.name = target;
+ node.content = data;
+ node.locationId = locationId;
+ node.properties = properties;
+ pending.add(node);
+ }
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment (CharSequence chars, int locationId, int properties) throws XPathException {
+ if (committed) {
+ getUnderlyingReceiver().comment(chars, locationId, properties);
+ } else {
+ if (pending==null) {
+ pending=new ArrayList<PendingNode>(10);
+ }
+ PendingNode node = new PendingNode();
+ node.kind = Type.COMMENT;
+ node.name = null;
+ node.content = chars;
+ node.locationId = locationId;
+ node.properties = properties;
+ pending.add(node);
+ }
+ }
+
+ /**
+ * Output an element start tag. <br>
+ * This can only be called once: it switches to a substitute output generator for XML, XHTML, or HTML,
+ * depending on the element name.
+ * @param elemName The element name (tag)
+ * @param typeCode The type annotation
+ * @param properties Bit field holding special properties of the element
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (!committed) {
+ String name = elemName.getLocalPart();
+ String uri = elemName.getURI();
+ if (name.equalsIgnoreCase("html") && uri.length()==0) {
+ switchToMethod("html");
+ } else if (name.equals("html") && uri.equals(NamespaceConstant.XHTML)) {
+ String version = outputProperties.getProperty(SaxonOutputKeys.STYLESHEET_VERSION);
+ if ("1.0".equals(version)) {
+ switchToMethod("xml");
+ } else {
+ switchToMethod("xhtml");
+ }
+ } else {
+ switchToMethod("xml");
+ }
+ }
+ getUnderlyingReceiver().startElement(elemName, typeCode, locationId, properties);
+ }
+
+ /**
+ * Switch to a specific emitter once the output method is known
+ * @param method the method to switch to (xml, html, xhtml)
+ */
+
+ private void switchToMethod(String method) throws XPathException {
+ Properties newProperties = new Properties(outputProperties);
+ newProperties.setProperty(OutputKeys.METHOD, method);
+ SerializerFactory sf = getConfiguration().getSerializerFactory();
+ Receiver target = sf.getReceiver(finalResult, getPipelineConfiguration(), newProperties);
+ committed = true;
+ target.open();
+ target.startDocument(0);
+ if (pending!=null) {
+ for (PendingNode node : pending) {
+ switch (node.kind) {
+ case Type.COMMENT:
+ target.comment(node.content, node.locationId, node.properties);
+ break;
+ case Type.PROCESSING_INSTRUCTION:
+ target.processingInstruction(node.name, node.content, node.locationId, node.properties);
+ break;
+ case Type.TEXT:
+ target.characters(node.content, node.locationId, node.properties);
+ break;
+ }
+ }
+ pending = null;
+ }
+ setUnderlyingReceiver(target);
+ }
+
+ /**
+ * A text, comment, or PI node that hasn't been output yet because we don't yet know what output
+ * method to use
+ */
+
+ private static final class PendingNode {
+ int kind;
+ String name;
+ CharSequence content;
+ int properties;
+ int locationId;
+ }
+
+}
+
diff --git a/sf/saxon/serialize/UnicodeNormalizer.java b/sf/saxon/serialize/UnicodeNormalizer.java
new file mode 100644
index 0000000..05f199a
--- /dev/null
+++ b/sf/saxon/serialize/UnicodeNormalizer.java
@@ -0,0 +1,72 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.serialize.codenorm.Normalizer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.value.Whitespace;
+
+/**
+ * UnicodeNormalizer: This ProxyReceiver performs unicode normalization on the contents
+ * of attribute and text nodes.
+ *
+ * @author Michael Kay
+*/
+
+
+public class UnicodeNormalizer extends ProxyReceiver {
+
+ private Normalizer normalizer;
+
+ public UnicodeNormalizer(String form, Receiver next) throws XPathException {
+ super(next);
+ byte fb;
+ if (form.equals("NFC")) {
+ fb = Normalizer.C;
+ } else if (form.equals("NFD")) {
+ fb = Normalizer.D;
+ } else if (form.equals("NFKC")) {
+ fb = Normalizer.KC;
+ } else if (form.equals("NFKD")) {
+ fb = Normalizer.KD;
+ } else {
+ XPathException err = new XPathException("Unknown normalization form " + form);
+ err.setErrorCode("SESU0011");
+ throw err;
+ }
+
+ normalizer = new Normalizer(fb, getConfiguration());
+ }
+
+ /**
+ * Output an attribute
+ */
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ nextReceiver.attribute(nameCode, typeCode, normalizer.normalize(value), locationId, properties);
+ }
+
+ /**
+ * Output character data
+ */
+
+ public void characters(/*@NotNull*/ CharSequence chars, int locationId, int properties) throws XPathException {
+ if (Whitespace.isWhite(chars)) {
+ nextReceiver.characters(chars, locationId, properties);
+ } else {
+ nextReceiver.characters(normalizer.normalize(chars), locationId, properties);
+ }
+ }
+
+};
+
diff --git a/sf/saxon/serialize/XHTML1Emitter.java b/sf/saxon/serialize/XHTML1Emitter.java
new file mode 100644
index 0000000..f460e63
--- /dev/null
+++ b/sf/saxon/serialize/XHTML1Emitter.java
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NodeName;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * XHTMLEmitter is an Emitter that generates XHTML 1 output.
+ * It is the same as XMLEmitter except that it follows the legacy HTML browser
+ * compatibility rules: for example, generating empty elements such as [BR /], and
+ * using [p][/p] for empty paragraphs rather than [p/]
+ */
+
+public class XHTML1Emitter extends XMLEmitter {
+
+ /**
+ * Table of XHTML tags that have no closing tag
+ */
+
+ static Set<String> emptyTags1 = new HashSet<String>(31);
+
+ private static String[] emptyTagNames1 = {
+ "area", "base", "basefont", "br", "col", "embed", "frame", "hr", "img", "input", "isindex", "link", "meta", "param"
+ // added "embed" in 9.5
+ };
+
+
+ static {
+ Collections.addAll(emptyTags1, emptyTagNames1);
+ }
+
+
+ private boolean isRecognizedHtmlElement(NodeName name) {
+ return name.isInNamespace(NamespaceConstant.XHTML);
+
+ }
+
+ /**
+ * Close an empty element tag.
+ */
+
+ protected String emptyElementTagCloser(String displayName, /*@NotNull*/ NodeName name) {
+ if (isRecognizedHtmlElement(name) && emptyTags1.contains(name.getLocalPart())) {
+ return " />";
+ } else {
+ return "></" + displayName + '>';
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/serialize/XHTMLURIEscaper.java b/sf/saxon/serialize/XHTMLURIEscaper.java
new file mode 100644
index 0000000..a906157
--- /dev/null
+++ b/sf/saxon/serialize/XHTMLURIEscaper.java
@@ -0,0 +1,142 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.serialize.codenorm.Normalizer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SimpleType;
+
+import java.util.HashSet;
+
+
+/**
+ * This class performs URI escaping for the XHTML output method. The logic for performing escaping
+ * is the same as the HTML output method, but the way in which attributes are identified for escaping
+ * is different, because XHTML is case-sensitive.
+ */
+
+public class XHTMLURIEscaper extends HTMLURIEscaper {
+
+ /**
+ * Table of attributes whose value is a URL
+ */
+
+ private static HashSet<String> urlTable = new HashSet<String>(70);
+ private static HashSet<String> attTable = new HashSet<String>(20);
+
+ private static void setUrlAttribute(String element, String attribute) {
+ attTable.add(attribute);
+ urlTable.add(element + "+" + attribute);
+ }
+
+ static {
+ setUrlAttribute("form", "action");
+ setUrlAttribute("object", "archive");
+ setUrlAttribute("body", "background");
+ setUrlAttribute("q", "cite");
+ setUrlAttribute("blockquote", "cite");
+ setUrlAttribute("del", "cite");
+ setUrlAttribute("ins", "cite");
+ setUrlAttribute("object", "classid");
+ setUrlAttribute("object", "codebase");
+ setUrlAttribute("applet", "codebase");
+ setUrlAttribute("object", "data");
+ setUrlAttribute("button", "datasrc");
+ setUrlAttribute("div", "datasrc");
+ setUrlAttribute("input", "datasrc");
+ setUrlAttribute("object", "datasrc");
+ setUrlAttribute("select", "datasrc");
+ setUrlAttribute("span", "datasrc");
+ setUrlAttribute("table", "datasrc");
+ setUrlAttribute("textarea", "datasrc");
+ setUrlAttribute("script", "for");
+ setUrlAttribute("a", "href");
+ setUrlAttribute("a", "name"); // see second note in section B.2.1 of HTML 4 specification
+ setUrlAttribute("area", "href");
+ setUrlAttribute("link", "href");
+ setUrlAttribute("base", "href");
+ setUrlAttribute("img", "longdesc");
+ setUrlAttribute("frame", "longdesc");
+ setUrlAttribute("iframe", "longdesc");
+ setUrlAttribute("head", "profile");
+ setUrlAttribute("script", "src");
+ setUrlAttribute("input", "src");
+ setUrlAttribute("frame", "src");
+ setUrlAttribute("iframe", "src");
+ setUrlAttribute("img", "src");
+ setUrlAttribute("img", "usemap");
+ setUrlAttribute("input", "usemap");
+ setUrlAttribute("object", "usemap");
+ }
+
+ public XHTMLURIEscaper(Receiver next) {
+ super(next);
+ }
+
+ /**
+ * Determine whether a given attribute is a URL attribute
+ */
+
+ private static boolean isURLAttribute(NodeName elcode, NodeName atcode) {
+ if (!elcode.isInNamespace(NamespaceConstant.XHTML)) {
+ return false;
+ }
+ if (!atcode.isInNamespace("")) {
+ return false;
+ }
+ String attName = atcode.getLocalPart();
+ return attTable.contains(attName) && urlTable.contains(elcode.getLocalPart() + "+" + attName);
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param attName The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ if (escapeURIAttributes &&
+ isURLAttribute(currentElement, attName) &&
+ (properties & ReceiverOptions.DISABLE_ESCAPING) == 0) {
+ CharSequence normalized = (isAllAscii(value)
+ ? value
+ : new Normalizer(Normalizer.C, getConfiguration()).normalize(value) );
+
+ getUnderlyingReceiver().attribute(
+ attName, typeCode, escapeURL(normalized, true, getConfiguration()), locationId,
+ properties | ReceiverOptions.DISABLE_CHARACTER_MAPS);
+ } else {
+ getUnderlyingReceiver().attribute(
+ attName, typeCode, value, locationId, properties);
+ }
+ }
+
+ private static boolean isAllAscii(/*@NotNull*/ CharSequence value) {
+ for (int i=0; i<value.length(); i++) {
+ if (value.charAt(i) > 127) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
+
diff --git a/sf/saxon/serialize/XML10ContentChecker.java b/sf/saxon/serialize/XML10ContentChecker.java
new file mode 100644
index 0000000..3fee034
--- /dev/null
+++ b/sf/saxon/serialize/XML10ContentChecker.java
@@ -0,0 +1,177 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.z.IntHashSet;
+import net.sf.saxon.om.Name10Checker;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.serialize.charcode.XMLCharacterData;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+import java.util.HashSet;
+
+/**
+ * This class is used on the serialization pipeline to check that the document conforms
+ * to XML 1.0 rules. It is placed on the pipeline only when the configuration permits
+ * XML 1.1 constructs, but the particular output document is being serialized as XML 1.0
+ */
+
+public class XML10ContentChecker extends ProxyReceiver {
+
+ private NameChecker checker = Name10Checker.getInstance();
+ // TODO: is the cache thread-safe?
+ private IntHashSet fingerprintCache = new IntHashSet(100);
+ private HashSet<NodeName> nameCache = new HashSet<NodeName>(100);
+
+ public XML10ContentChecker(Receiver next) {
+ super(next);
+ }
+
+ /**
+ * Notify the start of an element
+ *
+ * @param elemName integer code identifying the name of the element within the name pool.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties properties of the element node
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (elemName.hasFingerprint()) {
+ if (!fingerprintCache.contains(elemName.getNameCode())) {
+ checkLocalName(elemName, locationId);
+ fingerprintCache.add(elemName.getNameCode());
+ }
+ } else {
+ if (!nameCache.contains(elemName)) {
+ checkLocalName(elemName, locationId);
+ nameCache.add(elemName);
+ }
+ }
+ nextReceiver.startElement(elemName, typeCode, locationId, properties);
+ }
+
+ private void checkLocalName(NodeName elemName, int locationId) throws XPathException {
+ if (!checker.isValidNCName(elemName.getLocalPart())) {
+ XPathException err = new XPathException("Invalid XML 1.0 element name " +
+ Err.wrap(elemName.getLocalPart(), Err.ELEMENT));
+ err.setErrorCode("SERE0005");
+ err.setLocator(new ExpressionLocation(getPipelineConfiguration().getLocationProvider(), locationId));
+ throw err;
+ }
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ *
+ *
+ * @param attName The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ if (attName.hasFingerprint()) {
+ if (!fingerprintCache.contains(attName.getFingerprint())) {
+ if (!checker.isValidNCName(attName.getLocalPart())) {
+ XPathException err = new XPathException("Invalid XML 1.0 attribute name " +
+ Err.wrap(attName.getLocalPart(), Err.ATTRIBUTE));
+ err.setErrorCode("SERE0005");
+ err.setLocator(new ExpressionLocation(getPipelineConfiguration().getLocationProvider(), locationId));
+ throw err;
+ }
+ fingerprintCache.add(attName.getFingerprint());
+ }
+ }
+ if (!nameCache.contains(attName)) {
+ if (!checker.isValidNCName(attName.getLocalPart())) {
+ XPathException err = new XPathException("Invalid XML 1.0 attribute name " +
+ Err.wrap(attName.getLocalPart(), Err.ATTRIBUTE));
+ err.setErrorCode("SERE0005");
+ err.setLocator(new ExpressionLocation(getPipelineConfiguration().getLocationProvider(), locationId));
+ throw err;
+ }
+ nameCache.add(attName);
+ }
+ checkString(value, locationId);
+ nextReceiver.attribute(attName, typeCode, value, locationId, properties);
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ checkString(chars, locationId);
+ nextReceiver.characters(chars, locationId, properties);
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ checkString(chars, locationId);
+ nextReceiver.comment(chars, locationId, properties);
+ }
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, /*@NotNull*/ CharSequence data, int locationId, int properties) throws XPathException {
+ if (!checker.isValidNCName(target)) {
+ XPathException err = new XPathException("Invalid XML 1.0 processing instruction name " +
+ Err.wrap(target));
+ err.setErrorCode("SERE0005");
+ err.setLocator(new ExpressionLocation(getPipelineConfiguration().getLocationProvider(), locationId));
+ throw err;
+ }
+ checkString(data, locationId);
+ nextReceiver.processingInstruction(target, data, locationId, properties);
+ }
+
+ /**
+ * Check that a string consists of valid XML 1.0 characters (UTF-16 encoded)
+ * @param in the string to be checked
+ * @param locationId the location of the string
+ */
+
+ private void checkString(CharSequence in, long locationId) throws XPathException {
+ final int len = in.length();
+ for (int c=0; c<len; c++) {
+ int ch32 = in.charAt(c);
+ if (UTF16CharacterSet.isHighSurrogate(ch32)) {
+ char low = in.charAt(++c);
+ ch32 = UTF16CharacterSet.combinePair((char)ch32, low);
+ }
+ if (!XMLCharacterData.isValid10(ch32)) {
+ XPathException err = new XPathException("The result tree contains a character not allowed by XML 1.0 (hex " +
+ Integer.toHexString(ch32) + ')');
+ err.setErrorCode("SERE0006");
+ err.setLocator(new ExpressionLocation(getPipelineConfiguration().getLocationProvider(), locationId));
+ throw err;
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/serialize/XMLEmitter.java b/sf/saxon/serialize/XMLEmitter.java
new file mode 100644
index 0000000..6bda076
--- /dev/null
+++ b/sf/saxon/serialize/XMLEmitter.java
@@ -0,0 +1,860 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.serialize.charcode.UTF8CharacterSet;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.tree.tiny.CompressedWhitespace;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.OutputKeys;
+import java.util.Properties;
+import java.util.Stack;
+
+/**
+ * XMLEmitter is an Emitter that generates XML output
+ * to a specified destination.
+ */
+
+public class XMLEmitter extends Emitter {
+
+ // NOTE: we experimented with XMLUTF8Emitter which combines XML escaping and UTF8 encoding
+ // into a single loop. Scrapped it because we couldn't measure any benefits - but there
+ // ought to be, in theory. Perhaps we weren't buffering the writes carefully enough.
+
+ protected boolean started = false;
+ protected boolean startedElement = false;
+ protected boolean openStartTag = false;
+ protected boolean declarationIsWritten = false;
+ protected NodeName elementCode;
+ protected int indentForNextAttribute = -1;
+ protected boolean undeclareNamespaces = false;
+
+ // The element stack holds the display names (lexical QNames) of elements that
+ // have been started but not finished. It is used to obtain the element name
+ // for the end tag.
+
+ protected Stack<String> elementStack = new Stack<String>();
+
+ // For other names we use a hashtable. It
+
+ private boolean indenting = false;
+ private String indentChars = "\n ";
+ private boolean requireWellFormed = false;
+ protected CharacterReferenceGenerator characterReferenceGenerator = HexCharacterReferenceGenerator.THE_INSTANCE;
+
+
+ static boolean[] specialInText; // lookup table for special characters in text
+ static boolean[] specialInAtt; // lookup table for special characters in attributes
+ // create look-up table for ASCII characters that need special treatment
+
+ static {
+ specialInText = new boolean[128];
+ for (int i=0; i<=31; i++) specialInText[i] = true; // allowed in XML 1.1 as character references
+ for (int i=32; i<=127; i++) specialInText[i] = false;
+ // note, 0 is used to switch escaping on and off for mapped characters
+ specialInText['\n'] = false;
+ specialInText['\t'] = false;
+ specialInText['\r'] = true;
+ specialInText['<'] = true;
+ specialInText['>'] = true;
+ specialInText['&'] = true;
+
+ specialInAtt = new boolean[128];
+ for (int i=0; i<=31; i++) specialInAtt[i] = true; // allowed in XML 1.1 as character references
+ for (int i=32; i<=127; i++) specialInAtt[i] = false;
+ specialInAtt[(char)0] = true;
+ // used to switch escaping on and off for mapped characters
+ specialInAtt['\r'] = true;
+ specialInAtt['\n'] = true;
+ specialInAtt['\t'] = true;
+ specialInAtt['<'] = true;
+ specialInAtt['>'] = true;
+ specialInAtt['&'] = true;
+ specialInAtt['\"'] = true;
+ }
+
+ /**
+ * Set the character reference generator to be used for generating hexadecimal or decimal
+ * character references
+ * @param generator the character reference generator to be used
+ */
+
+ public void setCharacterReferenceGenerator(CharacterReferenceGenerator generator) {
+ this.characterReferenceGenerator = generator;
+ }
+
+ /**
+ * Say that all non-ASCII characters should be escaped, regardless of the character encoding
+ * @param escape true if all non ASCII characters should be escaped
+ */
+
+ public void setEscapeNonAscii(Boolean escape) {
+ // no action (not currently supported for this output method
+ }
+
+ /**
+ * Start of the event stream. Nothing is done at this stage: the opening of the output
+ * file is deferred until some content is written to it.
+ */
+
+ public void open() throws XPathException {
+ }
+
+ /**
+ * Start of a document node. Nothing is done at this stage: the opening of the output
+ * file is deferred until some content is written to it.
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ }
+
+ /**
+ * Notify the end of a document node
+ */
+
+ public void endDocument() throws XPathException {
+ if (!elementStack.isEmpty()) {
+ throw new IllegalStateException("Attempt to end document in serializer when elements are unclosed");
+ }
+ }
+
+ /**
+ * Do the real work of starting the document. This happens when the first
+ * content is written.
+ * @throws XPathException f an error occurs opening the output file
+ */
+
+ protected void openDocument () throws XPathException
+ {
+ if (writer==null) {
+ makeWriter();
+ }
+ if (characterSet==null) {
+ characterSet = UTF8CharacterSet.getInstance();
+ }
+ if (outputProperties==null) {
+ outputProperties = new Properties();
+ }
+
+ String rep = outputProperties.getProperty(SaxonOutputKeys.UNDECLARE_PREFIXES);
+ if (rep!=null) {
+ undeclareNamespaces = (rep.equalsIgnoreCase("yes"));
+ }
+ writeDeclaration();
+ }
+
+ /**
+ * Output the XML declaration
+ */
+
+ public void writeDeclaration() throws XPathException {
+ if (declarationIsWritten) return;
+ declarationIsWritten = true;
+ try {
+ indenting = "yes".equals(outputProperties.getProperty(OutputKeys.INDENT));
+
+ String byteOrderMark = outputProperties.getProperty(SaxonOutputKeys.BYTE_ORDER_MARK);
+ String encoding = outputProperties.getProperty(OutputKeys.ENCODING);
+ if (encoding==null || encoding.equalsIgnoreCase("utf8")) {
+ encoding = "UTF-8";
+ }
+
+ if ("yes".equals(byteOrderMark) && (
+ "UTF-8".equalsIgnoreCase(encoding) ||
+ "UTF-16LE".equalsIgnoreCase(encoding) ||
+ "UTF-16BE".equalsIgnoreCase(encoding))) {
+ writer.write('\uFEFF');
+ }
+
+ String omitXMLDeclaration = outputProperties.getProperty(OutputKeys.OMIT_XML_DECLARATION);
+ if (omitXMLDeclaration==null) {
+ omitXMLDeclaration = "no";
+ }
+
+ String version = outputProperties.getProperty(OutputKeys.VERSION);
+ if (version==null) {
+ version = getConfiguration().getNameChecker().getXMLVersion();
+ } else {
+ if (!version.equals("1.0") && !version.equals("1.1")) {
+ XPathException err = new XPathException("XML version must be 1.0 or 1.1");
+ err.setErrorCode("SESU0013");
+ throw err;
+ }
+ if (!version.equals("1.0") && omitXMLDeclaration.equals("yes") &&
+ outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM) != null) {
+ XPathException err = new XPathException("Values of 'version', 'omit-xml-declaration', and 'doctype-system' conflict");
+ err.setErrorCode("SEPM0009");
+ throw err;
+ }
+ }
+
+ String undeclare = outputProperties.getProperty(SaxonOutputKeys.UNDECLARE_PREFIXES);
+ if ("yes".equals(undeclare)) {
+ undeclareNamespaces = true;
+ }
+
+ if (version.equals("1.0") && undeclareNamespaces) {
+ XPathException err = new XPathException("Cannot undeclare namespaces with XML version 1.0");
+ err.setErrorCode("SEPM0010");
+ throw err;
+ }
+
+ String standalone = outputProperties.getProperty(OutputKeys.STANDALONE);
+ if ("omit".equals(standalone)) {
+ standalone = null;
+ }
+
+ if (standalone != null) {
+ requireWellFormed = true;
+ if (omitXMLDeclaration.equals("yes")) {
+ XPathException err = new XPathException("Values of 'standalone' and 'omit-xml-declaration' conflict");
+ err.setErrorCode("SEPM0009");
+ throw err;
+ }
+ }
+
+ String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
+ if (systemId != null && !"".equals(systemId)) {
+ requireWellFormed = true;
+ }
+
+ if (omitXMLDeclaration.equals("no")) {
+ writer.write("<?xml version=\"" + version + "\" " + "encoding=\"" + encoding + '\"' +
+ (standalone != null ? " standalone=\"" + standalone + '\"' : "") + "?>");
+ // don't write a newline character: it's wrong if the output is an
+ // external general parsed entity
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+ /**
+ * Output the document type declaration
+ * @param name the qualified name of the element
+ * @param displayName The element name as displayed
+ * @param systemId The DOCTYP system identifier
+ * @param publicId The DOCTYPE public identifier
+ * @throws net.sf.saxon.trans.XPathException if an error occurs writing to the output
+ */
+
+ protected void writeDocType(NodeName name, String displayName, String systemId, String publicId) throws XPathException {
+ try {
+ if (declarationIsWritten && !indenting) {
+ // don't add a newline if indenting, because the indenter will already have done so
+ writer.write("\n");
+ }
+ writer.write("<!DOCTYPE " + displayName + '\n');
+ String quotedSystemId = null;
+ if (systemId != null) {
+ if (systemId.contains("\"")) {
+ quotedSystemId = "'" + systemId + "'";
+ } else {
+ quotedSystemId = '"' + systemId + '"';
+ }
+ }
+ if (systemId!=null && publicId==null) {
+ writer.write(" SYSTEM " + quotedSystemId + ">\n");
+ } else if (systemId==null && publicId!=null) { // handles the HTML case
+ writer.write(" PUBLIC \"" + publicId + "\">\n");
+ } else {
+ writer.write(" PUBLIC \"" + publicId + "\" " + quotedSystemId + ">\n");
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+ /**
+ * End of the document.
+ */
+
+ public void close() throws XPathException {
+ // if nothing has been written, we should still create the file and write an XML declaration
+ if (!started) {
+ openDocument();
+ }
+ try {
+ if (writer != null) {
+ writer.flush();
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ super.close();
+ }
+
+ /**
+ * Start of an element. Output the start tag, escaping special characters.
+ */
+
+ public void startElement (NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException
+ {
+ if (!started) {
+ openDocument();
+ } else if (requireWellFormed && elementStack.isEmpty() && startedElement) {
+ XPathException err = new XPathException("When 'standalone' or 'doctype-system' is specified, " +
+ "the document must be well-formed; but this document contains more than one top-level element");
+ err.setErrorCode("SEPM0004");
+ throw err;
+ }
+ startedElement = true;
+
+ String displayName = elemName.getDisplayName();
+ if (!allCharactersEncodable) {
+ int badchar = testCharacters(displayName);
+ if (badchar!=0) {
+ XPathException err = new XPathException("Element name contains a character (decimal + " +
+ badchar + ") not available in the selected encoding");
+ err.setErrorCode("SERE0008");
+ throw err;
+ }
+ }
+
+ elementStack.push(displayName);
+ elementCode = elemName;
+
+ try {
+ if (!started) {
+ String systemId = outputProperties.getProperty(OutputKeys.DOCTYPE_SYSTEM);
+ String publicId = outputProperties.getProperty(OutputKeys.DOCTYPE_PUBLIC);
+ // Treat "" as equivalent to absent. This goes beyond what the spec strictly allows.
+ if ("".equals(systemId)) {
+ systemId = null;
+ }
+ if ("".equals(publicId)) {
+ publicId = null;
+ }
+ if (systemId!=null) {
+ requireWellFormed = true;
+ writeDocType(elemName, displayName, systemId, publicId);
+ } else if (writeDocTypeWithNullSystemId()) {
+ writeDocType(elemName, displayName, systemId, publicId);
+ }
+ started = true;
+ }
+ if (openStartTag) {
+ closeStartTag();
+ }
+ writer.write('<');
+ writer.write(displayName);
+ openStartTag = true;
+ indentForNextAttribute = -1;
+
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+ protected boolean writeDocTypeWithNullSystemId() {
+ return false;
+ }
+
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ try {
+ String nsprefix = namespaceBinding.getPrefix();
+ String nsuri = namespaceBinding.getURI();
+
+ String sep = getAttributeIndentString();
+
+ if (nsprefix.length() == 0) {
+ writer.write(sep);
+ writeAttribute(elementCode, "xmlns", nsuri, 0);
+ } else if (nsprefix.equals("xml")) {
+ //return;
+ } else {
+ int badchar = testCharacters(nsprefix);
+ if (badchar!=0) {
+ XPathException err = new XPathException("Namespace prefix contains a character (decimal + " +
+ badchar + ") not available in the selected encoding");
+ err.setErrorCode("SERE0008");
+ throw err;
+ }
+ if (undeclareNamespaces || nsuri.length() != 0) {
+ writer.write(sep);
+ writeAttribute(elementCode, "xmlns:" + nsprefix, nsuri, 0);
+ }
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+ public void setIndentForNextAttribute(int indent) {
+ indentForNextAttribute = indent;
+ }
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+
+ String displayName = nameCode.getDisplayName();
+ if (!allCharactersEncodable) {
+ int badchar = testCharacters(displayName);
+ if (badchar!=0) {
+ XPathException err = new XPathException("Attribute name contains a character (decimal + " +
+ badchar + ") not available in the selected encoding");
+ err.setErrorCode("SERE0008");
+ throw err;
+ }
+ }
+
+ try {
+ writer.write(getAttributeIndentString());
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+
+
+ writeAttribute(
+ elementCode,
+ displayName,
+ value,
+ properties );
+
+
+ }
+
+ protected String getAttributeIndentString() {
+ if (indentForNextAttribute < 0) {
+ return " ";
+ } else {
+ int indent = indentForNextAttribute;
+ while (indent >= indentChars.length()) {
+ indentChars += " ";
+ }
+ return indentChars.substring(0, indent);
+ }
+ }
+
+
+ public void startContent() throws XPathException {
+ // don't add ">" to the start tag until we know whether the element has content
+ }
+
+ /**
+ * Mark the end of the start tag
+ * @throws XPathException if an IO exception occurs
+ */
+
+ public void closeStartTag() throws XPathException {
+ try {
+ if (openStartTag) {
+ writer.write('>');
+ openStartTag = false;
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+ /**
+ * Close an empty element tag. (This is overridden in XHTMLEmitter).
+ *
+ *
+ * @param displayName the name of the empty element
+ * @param nameCode the fingerprint of the name of the empty element
+ * @return the string used to close an empty element tag.
+ */
+
+ protected String emptyElementTagCloser(String displayName, NodeName nameCode) {
+ return "/>";
+ }
+
+ /**
+ * Write attribute name=value pair.
+ * @param elCode The element name is not used in this version of the
+ * method, but is used in the HTML subclass.
+ * @param attname The attribute name, which has already been validated to ensure
+ * it can be written in this encoding
+ * @param value The value of the attribute
+ * @param properties Any special properties of the attribute
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ protected void writeAttribute(NodeName elCode, String attname, CharSequence value, int properties) throws XPathException {
+ try {
+ String val = value.toString();
+ writer.write(attname);
+ if ((properties & ReceiverOptions.NO_SPECIAL_CHARS) != 0) {
+ writer.write('=');
+ writer.write('"');
+ writer.write(val);
+ writer.write('"');
+ } else if ((properties & ReceiverOptions.USE_NULL_MARKERS) != 0) {
+ // null (0) characters will be used before and after any section of
+ // the value generated from a character map
+ writer.write('=');
+ char delimiter = (val.indexOf('"') >= 0 && val.indexOf('\'') < 0 ? '\'' : '"');
+ writer.write(delimiter);
+ writeEscape(value, true);
+ writer.write(delimiter);
+ } else {
+ writer.write("=\"");
+ writeEscape(value, true);
+ writer.write('\"');
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+
+ /**
+ * Test that all characters in a name (for example) are supported in the target encoding.
+ * @param chars the characters to be tested
+ * @return zero if all the characters are available, or the value of the
+ * first offending character if not
+ */
+
+ protected int testCharacters(CharSequence chars) throws XPathException {
+ for (int i=0; i<chars.length(); i++) {
+ char c = chars.charAt(i);
+ if (c > 127) {
+ if (UTF16CharacterSet.isHighSurrogate(c)) {
+ int cc = UTF16CharacterSet.combinePair(c, chars.charAt(++i));
+ if (!characterSet.inCharset(cc)) {
+ return cc;
+ }
+ } else if (!characterSet.inCharset(c)) {
+ return c;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * End of an element.
+ */
+
+ public void endElement () throws XPathException
+ {
+ String displayName = (String)elementStack.pop();
+ try {
+ if (openStartTag) {
+ writer.write(emptyElementTagCloser(displayName, elementCode));
+ openStartTag = false;
+ } else {
+ writer.write("</");
+ writer.write(displayName);
+ writer.write('>');
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+ /**
+ * Character data.
+ */
+
+ public void characters (CharSequence chars, int locationId, int properties) throws XPathException
+ {
+ if (!started) {
+ openDocument();
+ }
+
+ if (requireWellFormed && elementStack.isEmpty() && !Whitespace.isWhite(chars)) {
+ XPathException err = new XPathException("When 'standalone' or 'doctype-system' is specified, " +
+ "the document must be well-formed; but this document contains a top-level text node");
+ err.setErrorCode("SEPM0004");
+ throw err;
+ }
+
+ try {
+ if (openStartTag) {
+ closeStartTag();
+ }
+
+ if ((properties & ReceiverOptions.NO_SPECIAL_CHARS) != 0) {
+ writeCharSequence(chars);
+ } else if ((properties & ReceiverOptions.DISABLE_ESCAPING) == 0) {
+ writeEscape(chars, false);
+ } else {
+ // disable-output-escaping="yes"
+ if (testCharacters(chars) == 0) {
+ if ((properties & ReceiverOptions.USE_NULL_MARKERS) == 0) {
+ // null (0) characters will be used before and after any section of
+ // the value generated from a character map
+ writeCharSequence(chars);
+ } else {
+ // Need to strip out any null markers. See test output-html109
+ final int len = chars.length();
+ for (int i=0; i<len; i++) {
+ char c = chars.charAt(i);
+ if (c != 0) {
+ writer.write(c);
+ }
+ }
+ }
+ } else {
+ // Using disable output escaping with characters
+ // that are not available in the target encoding
+ // The required action is to ignore d-o-e in respect of those characters that are
+ // not available in the encoding. This is slow...
+ final int len = chars.length();
+ for (int i=0; i<len; i++) {
+ char c = chars.charAt(i);
+ if (c != 0) {
+ if (c > 127 && UTF16CharacterSet.isHighSurrogate(c)) {
+ char[] pair = new char[2];
+ pair[0] = c;
+ pair[1] = chars.charAt(++i);
+ int cc = UTF16CharacterSet.combinePair(c, pair[1]);
+ if (!characterSet.inCharset(cc)) {
+ writeEscape(new CharSlice(pair), false);
+ } else {
+ writeCharSequence(new CharSlice(pair));
+ }
+ } else {
+ char[] ca = {c};
+ if (!characterSet.inCharset(c)) {
+ writeEscape(new CharSlice(ca), false);
+ } else {
+ writeCharSequence(new CharSlice(ca));
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+ /**
+ * Write a CharSequence (without any escaping of special characters): various implementations
+ * @param s the character sequence to be written
+ * @throws java.io.IOException in the event of a failure to write to the output file
+ */
+
+ public void writeCharSequence(CharSequence s) throws java.io.IOException {
+ if (s instanceof String) {
+ writer.write((String)s);
+ } else if (s instanceof CharSlice) {
+ ((CharSlice)s).write(writer);
+ } else if (s instanceof FastStringBuffer) {
+ ((FastStringBuffer)s).write(writer);
+ } else if (s instanceof CompressedWhitespace) {
+ ((CompressedWhitespace)s).write(writer);
+ } else {
+ writer.write(s.toString());
+ }
+ }
+
+
+ /**
+ * Handle a processing instruction.
+ */
+
+ public void processingInstruction (String target, CharSequence data, int locationId, int properties)
+ throws XPathException {
+ if (!started) {
+ openDocument();
+ }
+ int x = testCharacters(target);
+ if (x != 0) {
+ XPathException err = new XPathException("Character in processing instruction name cannot be represented " +
+ "in the selected encoding (code " + x + ')');
+ err.setErrorCode("SERE0008");
+ throw err;
+ }
+ x = testCharacters(data);
+ if (x != 0) {
+ XPathException err = new XPathException("Character in processing instruction data cannot be represented " +
+ "in the selected encoding (code " + x + ')');
+ err.setErrorCode("SERE0008");
+ throw err;
+ }
+ try {
+ if (openStartTag) {
+ closeStartTag();
+ }
+ writer.write("<?" + target + (data.length()>0 ? ' ' + data.toString() : "") + "?>");
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+ /**
+ * Write contents of array to current writer, after escaping special characters.
+ * This method converts the XML special characters (such as < and &) into their
+ * predefined entities.
+ * @param chars The character sequence containing the string
+ * @param inAttribute Set to true if the text is in an attribute value
+ */
+
+ protected void writeEscape(final CharSequence chars, final boolean inAttribute)
+ throws java.io.IOException, XPathException {
+ int segstart = 0;
+ boolean disabled = false;
+ final boolean[] specialChars = (inAttribute ? specialInAtt : specialInText);
+
+ if (chars instanceof CompressedWhitespace) {
+ ((CompressedWhitespace)chars).writeEscape(specialChars, writer);
+ return;
+ }
+
+ final int clength = chars.length();
+ while (segstart < clength) {
+ int i = segstart;
+ // find a maximal sequence of "ordinary" characters
+ while (i < clength) {
+ final char c = chars.charAt(i);
+ if (c < 127) {
+ if (specialChars[c]) {
+ break;
+ } else {
+ i++;
+ }
+ } else if (c < 160) {
+ break;
+ } else if (c == 0x2028) {
+ break;
+ } else if (UTF16CharacterSet.isHighSurrogate(c)) {
+ break;
+ } else if (!characterSet.inCharset(c)) {
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ // if this was the whole string write it out and exit
+ if (i >= clength) {
+ if (segstart == 0) {
+ writeCharSequence(chars);
+ } else {
+ writeCharSequence(chars.subSequence(segstart, i));
+ }
+ return;
+ }
+
+ // otherwise write out this sequence
+ if (i > segstart) {
+ writeCharSequence(chars.subSequence(segstart, i));
+ }
+
+ // examine the special character that interrupted the scan
+ final char c = chars.charAt(i);
+ if (c==0) {
+ // used to switch escaping on and off
+ disabled = !disabled;
+ } else if (disabled) {
+ if (c > 127) {
+ if (UTF16CharacterSet.isHighSurrogate(c)) {
+ int cc = UTF16CharacterSet.combinePair(c, chars.charAt(i+1));
+ if (!characterSet.inCharset(cc)) {
+ XPathException de = new XPathException("Character x" + Integer.toHexString(cc) +
+ " is not available in the chosen encoding");
+ de.setErrorCode("SERE0008");
+ throw de;
+ }
+ } else if (!characterSet.inCharset(c)) {
+ XPathException de = new XPathException("Character " + c + " (x" + Integer.toHexString((int)c) +
+ ") is not available in the chosen encoding");
+ de.setErrorCode("SERE0008");
+ throw de;
+ }
+ }
+ writer.write(c);
+ } else if (c < 127) {
+ // process special ASCII characters
+ if (c=='<') {
+ writer.write("<");
+ } else if (c=='>') {
+ writer.write(">");
+ } else if (c=='&') {
+ writer.write("&");
+ } else if (c=='\"') {
+ writer.write(""");
+ } else if (c=='\n') {
+ writer.write("
");
+ } else if (c=='\r') {
+ writer.write("
");
+ } else if (c=='\t') {
+ writer.write("	");
+ } else {
+ // C0 control characters
+ characterReferenceGenerator.outputCharacterReference(c, writer);
+ }
+ } else if (c < 160 || c == 0x2028) {
+ // XML 1.1 requires these characters to be written as character references
+ characterReferenceGenerator.outputCharacterReference(c, writer);
+ } else if (UTF16CharacterSet.isHighSurrogate(c)) {
+ char d = chars.charAt(++i);
+ int charval = UTF16CharacterSet.combinePair(c, d);
+ if (characterSet.inCharset(charval)) {
+ writer.write(c);
+ writer.write(d);
+ } else {
+ characterReferenceGenerator.outputCharacterReference(charval, writer);
+ }
+ } else {
+ // process characters not available in the current encoding
+ characterReferenceGenerator.outputCharacterReference(c, writer);
+ }
+ segstart = ++i;
+ }
+ }
+
+
+ /**
+ * Handle a comment.
+ */
+
+ public void comment (CharSequence chars, int locationId, int properties) throws XPathException
+ {
+ if (!started) {
+ openDocument();
+ }
+ int x = testCharacters(chars);
+ if (x != 0) {
+ XPathException err = new XPathException("Character in comment cannot be represented " +
+ "in the selected encoding (code " + x + ')');
+ err.setErrorCode("SERE0008");
+ throw err;
+ }
+ try {
+ if (openStartTag) {
+ closeStartTag();
+ }
+ writer.write("<!--");
+ writer.write(chars.toString());
+ writer.write("-->");
+ } catch (java.io.IOException err) {
+ throw new XPathException("Failure writing to " + getSystemId(), err);
+ }
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return false;
+ }
+
+
+}
+
diff --git a/sf/saxon/serialize/XMLIndenter.java b/sf/saxon/serialize/XMLIndenter.java
new file mode 100644
index 0000000..a8f445a
--- /dev/null
+++ b/sf/saxon/serialize/XMLIndenter.java
@@ -0,0 +1,351 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize;
+
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.FingerprintedQName;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.type.ComplexType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.OutputKeys;
+import java.util.*;
+
+/**
+* XMLIndenter: This ProxyReceiver indents elements, by adding character data where appropriate.
+* The character data is always added as "ignorable white space", that is, it is never added
+* adjacent to existing character data.
+*
+* @author Michael Kay
+*/
+
+
+public class XMLIndenter extends ProxyReceiver {
+
+ private int level = 0;
+
+ private char[] indentChars = {'\n', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
+ private boolean sameline = false;
+ private boolean afterStartTag = false;
+ private boolean afterEndTag = true;
+ private boolean allWhite = true;
+ private int line = 0; // line and column measure the number of lines and columns
+ private int column = 0; // .. in whitespace text nodes between tags
+ private int suppressedAtLevel = -1;
+ /*@Nullable*/ private Set<NodeName> suppressedElements = null;
+ private XMLEmitter emitter;
+ private AttributeCollectionImpl bufferedAttributes;
+ private List<NamespaceBinding> bufferedNamespaces = new ArrayList<NamespaceBinding>(8);
+
+
+ /**
+ * Create an XML Indenter
+ * @param next the next receiver in the pipeline, always an XMLEmitter
+ */
+
+ public XMLIndenter(XMLEmitter next) {
+ super(next);
+ emitter = next;
+ bufferedAttributes = new AttributeCollectionImpl(getConfiguration());
+ }
+
+ /**
+ * Set the properties for this indenter
+ * @param props the serialization properties
+ */
+
+ public void setOutputProperties(Properties props) {
+
+ String omit = props.getProperty(OutputKeys.OMIT_XML_DECLARATION);
+ afterEndTag = omit==null || !"yes".equals(Whitespace.trim(omit)) ||
+ props.getProperty(OutputKeys.DOCTYPE_SYSTEM)!=null ;
+ String s = props.getProperty(SaxonOutputKeys.SUPPRESS_INDENTATION);
+ if (s == null) {
+ s = props.getProperty("{http://saxon.sf.net/}suppress-indentation");
+ // for compatibility: since 9.3 also available in default namespace
+ }
+ if (s != null) {
+ suppressedElements = new HashSet<NodeName>(8);
+ StringTokenizer st = new StringTokenizer(s, " \t\r\n");
+ while (st.hasMoreTokens()) {
+ String clarkName = st.nextToken();
+ suppressedElements.add(FingerprintedQName.fromClarkName(clarkName));
+ }
+ }
+
+ }
+
+ /**
+ * Start of document
+ */
+
+ public void open() throws XPathException {
+ nextReceiver.open();
+ }
+
+ /**
+ * Output element start tag
+ */
+
+ public void startElement(NodeName nameCode, SchemaType type, int locationId, int properties) throws XPathException {
+ if (afterStartTag || afterEndTag) {
+ if (isDoubleSpaced(nameCode)) {
+ nextReceiver.characters("\n", 0, 0);
+ line = 0;
+ column = 0;
+ }
+ indent();
+ }
+ nextReceiver.startElement(nameCode, type, locationId, properties);
+ level++;
+ sameline = true;
+ afterStartTag = true;
+ afterEndTag = false;
+ allWhite = true;
+ line = 0;
+ if (suppressedElements != null && suppressedAtLevel == -1 && suppressedElements.contains(nameCode)) {
+ suppressedAtLevel = level;
+ }
+ int typeCode = type.getFingerprint();
+ if (typeCode >= 1024 && suppressedAtLevel < 0 &&
+ ((type = getConfiguration().getSchemaType(typeCode)) != null && type.isComplexType() &&
+ ((ComplexType)type).isMixedContent())) {
+ // suppress indentation for elements with mixed content. (Note this also suppresses
+ // indentation for all descendants of such elements. We could be smarter than this.)
+ suppressedAtLevel = level;
+ }
+ bufferedAttributes.clear();
+ bufferedNamespaces.clear();
+ }
+
+ @Override
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ bufferedNamespaces.add(namespaceBinding);
+ }
+
+ /**
+ * Output an attribute
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ if (value.equals("preserve") &&
+ attName.isInNamespace(NamespaceConstant.XML) &&
+ attName.getLocalPart().equals("space") &&
+ suppressedAtLevel < 0) {
+ // Note, we are suppressing indentation within an xml:space="preserve" region even if a descendant
+ // specifies xml:space="default
+ suppressedAtLevel = level;
+ }
+ bufferedAttributes.addAttribute(attName, typeCode, value.toString(), locationId, properties);
+ //nextReceiver.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+
+ public void startContent() throws XPathException {
+ int len = 0;
+ int count = 0;
+ int indent = -1;
+ AttributeCollectionImpl ba = bufferedAttributes;
+ if (suppressedAtLevel < 0) {
+ for (NamespaceBinding binding : bufferedNamespaces) {
+ String prefix = binding.getPrefix();
+ if (prefix.length()==0) {
+ len += 9 + binding.getURI().length();
+ } else {
+ len += prefix.length() + 10 + binding.getURI().length();
+ }
+ }
+ for (int i=0; i<ba.getLength(); i++) {
+ String prefix = ba.getPrefix(i);
+ len += ba.getLocalName(i).length()
+ + ba.getValue(i).length()
+ + 4 + (prefix.length()==0 ? 4 : prefix.length() + 5);
+ }
+ if (len > getLineLength()) {
+ indent = (level-1) * getIndentation() + emitter.elementStack.peek().length() + 3;
+ }
+ }
+ for (NamespaceBinding binding : bufferedNamespaces) {
+ nextReceiver.namespace(binding, 0);
+ if (indent > 0 && count++ == 0) {
+ emitter.setIndentForNextAttribute(indent);
+ }
+ }
+ for (int i=0; i<ba.getLength(); i++) {
+ nextReceiver.attribute(ba.getNodeName(i), ba.getTypeAnnotation(i),
+ ba.getValue(i), ba.getLocationId(i), ba.getProperties(i));
+ if (indent > 0 && count++ == 0) {
+ emitter.setIndentForNextAttribute(indent);
+ }
+ }
+ nextReceiver.startContent();
+
+ }
+
+ /**
+ * Output element end tag
+ */
+
+ public void endElement() throws XPathException {
+ level--;
+ if (afterEndTag && !sameline) {
+ indent();
+ }
+ nextReceiver.endElement();
+ sameline = false;
+ afterEndTag = true;
+ afterStartTag = false;
+ allWhite = true;
+ line = 0;
+ if (level == (suppressedAtLevel - 1)) {
+ suppressedAtLevel = -1;
+ // remove the suppression of indentation
+ }
+ }
+
+ /**
+ * Output a processing instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (afterEndTag) {
+ indent();
+ }
+ nextReceiver.processingInstruction(target, data, locationId, properties);
+ //afterStartTag = false;
+ //afterEndTag = false;
+ }
+
+ /**
+ * Output character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ for (int i=0; i<chars.length(); i++) {
+ char c = chars.charAt(i);
+ if (c=='\n') {
+ sameline = false;
+ line++;
+ column = 0;
+ }
+ if (!Character.isWhitespace(c)) {
+ allWhite = false;
+ }
+ column++;
+ }
+ nextReceiver.characters(chars, locationId, properties);
+ if (!allWhite) {
+ afterStartTag = false;
+ afterEndTag = false;
+ }
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (afterEndTag) {
+ indent();
+ }
+ nextReceiver.comment(chars, locationId, properties);
+ //afterStartTag = false;
+ //afterEndTag = false;
+ }
+
+ /**
+ * Ask whether this Receiver (or the downstream pipeline) makes any use of the type annotations
+ * supplied on element and attribute events
+ * @return true if the Receiver makes any use of this information. If false, the caller
+ * may supply untyped nodes instead of supplying the type annotation
+ */
+
+ public boolean usesTypeAnnotations() {
+ return true;
+ }
+
+ /**
+ * Output white space to reflect the current indentation level
+ * @throws XPathException if a downstream error occurs doing the output
+ */
+
+ private void indent() throws XPathException {
+ if (suppressedAtLevel >= 0) {
+ // indentation has been suppressed (e.g. by xmlspace="preserve")
+ return;
+ }
+ int spaces = level * getIndentation();
+ if (line>0) {
+ spaces -= column;
+ if (spaces <= 0) {
+ return; // there's already enough white space, don't add more
+ }
+ }
+ if (spaces+2 >= indentChars.length) {
+ int increment = 5 * getIndentation();
+ if (spaces + 2 > indentChars.length + increment) {
+ increment += spaces + 2;
+ }
+ char[] c2 = new char[indentChars.length + increment];
+ System.arraycopy(indentChars, 0, c2, 0, indentChars.length);
+ Arrays.fill(c2, indentChars.length, c2.length, ' ');
+ indentChars = c2;
+ }
+ // output the initial newline character only if line==0
+ int start = (line == 0 ? 0 : 1);
+ //super.characters(indentChars.subSequence(start, start+spaces+1), 0, ReceiverOptions.NO_SPECIAL_CHARS);
+ nextReceiver.characters(new CharSlice(indentChars, start, spaces+1), 0, ReceiverOptions.NO_SPECIAL_CHARS);
+ sameline = false;
+ }
+
+ @Override
+ public void endDocument() throws XPathException {
+ if (afterEndTag) {
+ characters("\n", 0, 0); // if permitted, output a trailing newline, for tidier console output
+ }
+ super.endDocument();
+ }
+
+ /**
+ * Get the number of spaces to be used for indentation
+ * @return the number of spaces to be added to the indentation for each level
+ */
+
+ protected int getIndentation() {
+ return 3;
+ }
+
+ /**
+ * Ask whether a particular element is to be double-spaced
+ * @param name the element name
+ * @return true if double-spacing is in effect for this element
+ */
+
+ protected boolean isDoubleSpaced(NodeName name) {
+ return false;
+ }
+
+ /**
+ * Get the suggested maximum length of a line
+ * @return the suggested maximum line length (used for wrapping attributes)
+ */
+
+ protected int getLineLength() {
+ return 80;
+ }
+}
+
diff --git a/sf/saxon/serialize/charcode/ASCIICharacterSet.java b/sf/saxon/serialize/charcode/ASCIICharacterSet.java
new file mode 100644
index 0000000..2ea8571
--- /dev/null
+++ b/sf/saxon/serialize/charcode/ASCIICharacterSet.java
@@ -0,0 +1,31 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.charcode;
+
+/**
+* This class defines properties of the US-ASCII character set
+*/
+
+public class ASCIICharacterSet implements CharacterSet {
+
+ public static final ASCIICharacterSet theInstance = new ASCIICharacterSet();
+
+ private ASCIICharacterSet() {}
+
+ public static ASCIICharacterSet getInstance() {
+ return theInstance;
+ }
+
+ public final boolean inCharset(int c) {
+ return c <= 0x7f;
+ }
+
+ /*@NotNull*/ public String getCanonicalName() {
+ return "US-ASCII";
+ }
+}
diff --git a/sf/saxon/serialize/charcode/CharacterSet.java b/sf/saxon/serialize/charcode/CharacterSet.java
new file mode 100644
index 0000000..f06325f
--- /dev/null
+++ b/sf/saxon/serialize/charcode/CharacterSet.java
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.charcode;
+
+
+/**
+* This interface defines properties of a character set, built in to the Saxon product.
+* This is selected in xsl:output using encoding="encoding-name", where
+* the mapping from an encoding-name to a class is defined in CharacterSetFactory.
+*/
+
+public interface CharacterSet {
+
+ /**
+ * Determine if a character is present in the character set
+ */
+
+ public boolean inCharset(int ch);
+
+ /**
+ * Get the preferred Java name of the character set. Note that Java in many
+ * cases also supports a "historic name".
+ */
+
+ public String getCanonicalName();
+
+}
+
diff --git a/sf/saxon/serialize/charcode/CharacterSetFactory.java b/sf/saxon/serialize/charcode/CharacterSetFactory.java
new file mode 100644
index 0000000..a3b9722
--- /dev/null
+++ b/sf/saxon/serialize/charcode/CharacterSetFactory.java
@@ -0,0 +1,135 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.charcode;
+
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.OutputKeys;
+import java.nio.charset.Charset;
+import java.nio.charset.IllegalCharsetNameException;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+* This class delivers a CharacterSet object for a given named encoding.
+ *
+ * It maintains a mapping from character set names to class names, and a separate mapping
+ * from character set names to instances of those classes. This means that a class is not
+ * actually instantiated until the encoding is used, but once instantiated, the same instance
+ * is used whenever that encoding is used again in the same Configuration.
+ *
+ * Note that the purpose of the CharacterSet object is only to record which Unicode
+ * characters are represented in the encoding, so that non-encodable characters can
+ * be represented as XML or HTML character references. The actual translation from Unicode
+ * codepoints to bytes in the chosen encoding is left to the Java IO library.
+*/
+
+
+public class CharacterSetFactory {
+
+ //private HashMap<String, String> characterSetClassNames = new HashMap<String, String>(40);
+ private HashMap<String, CharacterSet> characterSets = new HashMap<String, CharacterSet>(10);
+
+ /**
+ * Class has a single instance per Configuration
+ */
+ public CharacterSetFactory() {
+ HashMap<String, CharacterSet> c = characterSets;
+ UTF8CharacterSet utf8 = UTF8CharacterSet.getInstance();
+ c.put("utf8", utf8);
+ UTF16CharacterSet utf16 = UTF16CharacterSet.getInstance();
+ c.put("utf16", utf16);
+ ASCIICharacterSet acs = ASCIICharacterSet.getInstance();
+ c.put("ascii", acs);
+ c.put("iso646", acs);
+ c.put("usascii", acs);
+ ISO88591CharacterSet lcs = ISO88591CharacterSet.getInstance();
+ c.put("iso88591", lcs);
+ }
+
+ /**
+ * Register an implementation of a character set, using the class name
+ * @param encoding the name of the character set
+ * @param charSet the name of a class that implements {@link net.sf.saxon.serialize.charcode.CharacterSet}
+ */
+
+ public void setCharacterSetImplementation(/*@NotNull*/ String encoding, CharacterSet charSet) {
+ characterSets.put(normalizeCharsetName(encoding), charSet);
+ }
+
+ /**
+ * Normalize the name of a character set
+ * @param name the character set name
+ * @return the normalized name (removes hyphens and underscores and converts to lower-case)
+ */
+
+ private static String normalizeCharsetName(String name) {
+ return name.replace("-", "").replace("_", "").toLowerCase();
+ }
+
+ /**
+ * Make a CharacterSet appropriate to the encoding
+ * @param details the serialization properties
+ * @return the constructed CharacterSet
+ */
+
+ public CharacterSet getCharacterSet(Properties details)
+ throws XPathException {
+
+ String encoding = details.getProperty(OutputKeys.ENCODING);
+ if (encoding == null) {
+ return UTF8CharacterSet.getInstance();
+ } else {
+ String encodingKey = normalizeCharsetName(encoding);
+ CharacterSet cs = characterSets.get(encodingKey);
+ if (cs != null) {
+ return cs;
+ }
+
+ // Otherwise see if the Java VM knows anything about the character set
+
+ Charset charset;
+ try {
+ charset = Charset.forName(encoding);
+ CharacterSet res = JavaCharacterSet.makeCharSet(charset);
+ characterSets.put(encodingKey, res);
+ return res;
+ } catch (IllegalCharsetNameException err) {
+ XPathException e = new XPathException("Invalid encoding name: " + encoding);
+ e.setErrorCode("SESU0007");
+ throw e;
+ } catch (UnsupportedCharsetException err) {
+ XPathException e = new XPathException("Unknown encoding requested: " + encoding);
+ e.setErrorCode("SESU0007");
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * Main program is a utility to give a list of the character sets supported
+ * by the Java VM
+ * @param args command line arguments
+ */
+
+ public static void main(String[] args) throws Exception {
+ System.err.println("Available Character Sets in the java.nio package for this Java VM:");
+ for (String s : Charset.availableCharsets().keySet()) {
+ System.err.println(" " + s);
+ }
+ System.err.println("Registered Character Sets in Saxon:");
+ CharacterSetFactory factory = new CharacterSetFactory();
+ for (Map.Entry e : factory.characterSets.entrySet()) {
+ System.err.println(" " + e.getKey() + " = " + e.getValue().getClass().getName());
+ }
+ }
+
+}
+
diff --git a/sf/saxon/serialize/charcode/ISO88591CharacterSet.java b/sf/saxon/serialize/charcode/ISO88591CharacterSet.java
new file mode 100644
index 0000000..8c1b366
--- /dev/null
+++ b/sf/saxon/serialize/charcode/ISO88591CharacterSet.java
@@ -0,0 +1,31 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.charcode;
+
+/**
+* This class defines properties of the ISO-8859-1 character set
+*/
+
+public class ISO88591CharacterSet implements CharacterSet {
+
+ private static ISO88591CharacterSet theInstance = new ISO88591CharacterSet();
+
+ private ISO88591CharacterSet() {}
+
+ public static ISO88591CharacterSet getInstance() {
+ return theInstance;
+ }
+
+ public final boolean inCharset(int c) {
+ return c <= 0xff;
+ }
+
+ /*@NotNull*/ public String getCanonicalName() {
+ return "ISO-8859-1";
+ }
+}
diff --git a/sf/saxon/serialize/charcode/JavaCharacterSet.java b/sf/saxon/serialize/charcode/JavaCharacterSet.java
new file mode 100644
index 0000000..8ca31ec
--- /dev/null
+++ b/sf/saxon/serialize/charcode/JavaCharacterSet.java
@@ -0,0 +1,87 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.charcode;
+
+import net.sf.saxon.tree.tiny.CharSlice;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+import java.util.HashMap;
+
+/**
+ * This class establishes properties of a character set that is
+ * known to the Java VM but not specifically known to Saxon. It determines whether particular
+ * characters are encodable by calling {@link CharsetEncoder#canEncode(char)}, and then caches
+ * this information locally.
+*/
+
+public class JavaCharacterSet implements CharacterSet {
+
+ public static HashMap<Charset, JavaCharacterSet> map;
+
+ private CharsetEncoder encoder;
+
+ // This class is written on the assumption that the CharsetEncoder.canEncode()
+ // method may be expensive. For BMP characters, it therefore remembers the results
+ // so each character is only looked up the first time it is encountered.
+
+ private byte[] charinfo = new byte[65536];
+ // rely on initialization to zeroes
+
+ //private final static byte UNKNOWN = 0;
+ private static final byte GOOD = 1;
+ private static final byte BAD = 2;
+
+ private JavaCharacterSet(Charset charset) {
+ encoder = charset.newEncoder();
+ }
+
+ public static synchronized JavaCharacterSet makeCharSet(/*@NotNull*/ Charset charset) {
+ if (map == null) {
+ map = new HashMap<Charset, JavaCharacterSet>(10);
+ }
+ JavaCharacterSet c = map.get(charset);
+ if (c == null) {
+ c = new JavaCharacterSet(charset);
+ map.put(charset, c);
+ }
+ return c;
+ }
+
+ public final boolean inCharset(int c) {
+ // Assume ASCII chars are always OK
+ if (c <= 127) {
+ return true;
+ }
+ if (c <= 65535) {
+ if (charinfo[c] == GOOD) {
+ return true;
+ } else if (charinfo[c] == BAD) {
+ return false;
+ } else {
+ if (encoder.canEncode((char)c)) {
+ charinfo[c] = GOOD;
+ return true;
+ } else {
+ charinfo[c] = BAD;
+ return false;
+ }
+ }
+ } else {
+ char[] cc = new char[2];
+ cc[0] = UTF16CharacterSet.highSurrogate(c);
+ cc[1] = UTF16CharacterSet.lowSurrogate(c);
+ return encoder.canEncode(new CharSlice(cc));
+ }
+ }
+
+ public String getCanonicalName() {
+ return encoder.charset().name();
+ }
+}
+
diff --git a/sf/saxon/serialize/charcode/UTF16CharacterSet.java b/sf/saxon/serialize/charcode/UTF16CharacterSet.java
new file mode 100644
index 0000000..8dd5c67
--- /dev/null
+++ b/sf/saxon/serialize/charcode/UTF16CharacterSet.java
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.charcode;
+
+/**
+ * A class to hold some static constants and methods associated with processing UTF16 and surrogate pairs
+ */
+
+public class UTF16CharacterSet implements CharacterSet {
+
+ private static UTF16CharacterSet theInstance = new UTF16CharacterSet();
+
+ /**
+ * Private constructor to force the singular instance to be used
+ */
+
+ private UTF16CharacterSet() {}
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular instance of this classthe singular instance of this class
+ */
+
+ public static UTF16CharacterSet getInstance() {
+ return theInstance;
+ }
+
+ public boolean inCharset(int c) {
+ return true;
+ }
+
+ public String getCanonicalName() {
+ return "UTF-16";
+ }
+
+
+ public static final int NONBMP_MIN = 0x10000;
+ public static final int NONBMP_MAX = 0x10FFFF;
+
+ public static final char SURROGATE1_MIN = 0xD800;
+ public static final char SURROGATE1_MAX = 0xDBFF;
+ public static final char SURROGATE2_MIN = 0xDC00;
+ public static final char SURROGATE2_MAX = 0xDFFF;
+
+ /**
+ * Return the non-BMP character corresponding to a given surrogate pair
+ * surrogates.
+ * @param high The high surrogate.
+ * @param low The low surrogate.
+ * @return the Unicode codepoint represented by the surrogate pair
+ */
+ public static int combinePair(char high, char low) {
+ return (high - SURROGATE1_MIN) * 0x400 + (low - SURROGATE2_MIN) + NONBMP_MIN;
+ }
+
+ /**
+ * Return the high surrogate of a non-BMP character
+ * @param ch The Unicode codepoint of the non-BMP character to be divided.
+ * @return the first character in the surrogate pair
+ */
+
+ public static char highSurrogate(int ch) {
+ return (char) (((ch - NONBMP_MIN) >> 10) + SURROGATE1_MIN);
+ }
+
+ /**
+ * Return the low surrogate of a non-BMP character
+ * @param ch The Unicode codepoint of the non-BMP character to be divided.
+ * @return the second character in the surrogate pair
+ */
+
+ public static char lowSurrogate(int ch) {
+ return (char) (((ch - NONBMP_MIN) & 0x3FF) + SURROGATE2_MIN);
+ }
+
+ /**
+ * Test whether a given character is a surrogate (high or low)
+ * @param c the character to test
+ * @return true if the character is the high or low half of a surrogate pair
+ */
+
+ public static boolean isSurrogate(int c) {
+ return (c & 0xF800) == 0xD800;
+ }
+
+ /**
+ * Test whether the given character is a high surrogate
+ * @param ch The character to test.
+ * @return true if the character is the first character in a surrogate pair
+ */
+ public static boolean isHighSurrogate(int ch) {
+ return (SURROGATE1_MIN <= ch && ch <= SURROGATE1_MAX);
+ }
+
+ /**
+ * Test whether the given character is a low surrogate
+ * @param ch The character to test.
+ * @return true if the character is the second character in a surrogate pair
+ */
+
+ public static boolean isLowSurrogate(int ch) {
+ return (SURROGATE2_MIN <= ch && ch <= SURROGATE2_MAX);
+ }
+
+ /**
+ * Test whether a CharSequence contains any surrogates (i.e. any non-BMP characters
+ * @param s the string to be tested
+ */
+
+ public static boolean containsSurrogates(/*@NotNull*/ CharSequence s) {
+ for (int i=0; i<s.length(); i++) {
+ if (isSurrogate(s.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void main(String[] args) {
+ System.err.println(Integer.toHexString(highSurrogate(0xeffff)));
+ System.err.println(Integer.toHexString(lowSurrogate(0xeffff)));
+ }
+}
+
diff --git a/sf/saxon/serialize/charcode/UTF8CharacterSet.java b/sf/saxon/serialize/charcode/UTF8CharacterSet.java
new file mode 100644
index 0000000..56cb673
--- /dev/null
+++ b/sf/saxon/serialize/charcode/UTF8CharacterSet.java
@@ -0,0 +1,134 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.charcode;
+
+/**
+* This class defines properties of the UTF-8 character set
+*/
+
+public final class UTF8CharacterSet implements CharacterSet {
+
+ private static UTF8CharacterSet theInstance = new UTF8CharacterSet();
+
+ /**
+ * Private constructor to force the singular instance to be used
+ */
+
+ private UTF8CharacterSet() {}
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular instance of this classthe singular instance of this class
+ */
+
+ public static UTF8CharacterSet getInstance() {
+ return theInstance;
+ }
+
+ public boolean inCharset(int c) {
+ return true;
+ }
+
+ /*@NotNull*/ public String getCanonicalName() {
+ return "UTF-8";
+ }
+
+ /**
+ * Static method to generate the UTF-8 representation of a Unicode character
+ * @param in the Unicode character, or the high half of a surrogate pair
+ * @param in2 the low half of a surrogate pair (ignored unless the first argument is in the
+ * range for a surrogate pair)
+ * @param out an array of at least 4 bytes to hold the UTF-8 representation.
+ * @return the number of bytes in the UTF-8 representation
+ */
+
+ public static int getUTF8Encoding(char in, char in2, byte[] out) {
+ // See Tony Graham, "Unicode, a Primer", page 92
+ int i = (int)in;
+ if (i<=0x7f) {
+ out[0] = (byte)i;
+ return 1;
+ } else if (i<=0x7ff) {
+ out[0] = (byte)(0xc0 | ((in >> 6) & 0x1f));
+ out[1] = (byte)(0x80 | (in & 0x3f));
+ return 2;
+ } else if (i>=0xd800 && i<=0xdbff) {
+ // surrogate pair
+ int j = (int)in2;
+ if (!(j>=0xdc00 && j<=0xdfff)) {
+ throw new IllegalArgumentException("Malformed Unicode Surrogate Pair (" + i + ',' + j + ')');
+ }
+ byte xxxxxx = (byte)(j & 0x3f);
+ byte yyyyyy = (byte)(((i & 0x03) << 4) | ((j >> 6) & 0x0f));
+ byte zzzz = (byte)((i >> 2) & 0x0f);
+ byte uuuuu = (byte)(((i >> 6) & 0x0f) + 1);
+ out[0] = (byte)(0xf0 | ((uuuuu >> 2) & 0x07));
+ out[1] = (byte)(0x80 | ((uuuuu & 0x03) << 4) | zzzz);
+ out[2] = (byte)(0x80 | yyyyyy);
+ out[3] = (byte)(0x80 | xxxxxx);
+ return 4;
+ } else if (i>=0xdc00 && i<=0xdfff) {
+ // second half of surrogate pair - ignore it
+ return 0;
+ } else {
+ out[0] = (byte)(0xe0 | ((in >> 12) & 0x0f));
+ out[1] = (byte)(0x80 | ((in >> 6) & 0x3f));
+ out[2] = (byte)(0x80 | (in & 0x3f));
+ return 3;
+ }
+ }
+
+ /**
+ * Decode a UTF8 character
+ * @param in array of bytes representing a single UTF-8 encoded character
+ * @param used number of bytes in the array that are actually used
+ * @return the Unicode codepoint of this character
+ * @throws IllegalArgumentException if the byte sequence is not a valid UTF-8 representation
+ */
+
+ public static int decodeUTF8(byte[] in, int used) throws IllegalArgumentException {
+ int bottom = 0;
+ for (int i=1; i<used; i++) {
+ if ((in[i] & 0xc0) != 0x80) {
+ throw new IllegalArgumentException("Byte " + (i+1) + " in UTF-8 sequence has wrong top bits");
+ }
+ bottom = (bottom<<6) + (in[i] & 0x3f);
+ }
+ if ((in[0] & 0x80) == 0) {
+ // single byte sequence 0xxxxxxx
+ if (used == 1) {
+ return in[0];
+ } else {
+ throw new IllegalArgumentException("UTF8 single byte expected");
+ }
+ } else if ((in[0] & 0xe0) == 0xc0) {
+ // two byte sequence
+ if (used != 2) {
+ throw new IllegalArgumentException("UTF8 sequence of two bytes expected");
+ }
+ return ((in[0] & 0x1f) << 6) + bottom;
+ } else if ((in[0] & 0xf0) == 0xe0) {
+ // three byte sequence
+ if (used != 3) {
+ throw new IllegalArgumentException("UTF8 sequence of three bytes expected");
+ }
+ return ((in[0] & 0x0f) << 12) + bottom;
+ } else if ((in[0] & 0xf8) == 0xf8) {
+ // four-byte sequence 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx
+ if (used != 4) {
+ throw new IllegalArgumentException("UTF8 sequence of four bytes expected");
+ }
+ return ((in[0] & 0x07) << 24) + bottom;
+ } else {
+ throw new IllegalArgumentException("UTF8 invalid first byte");
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/serialize/charcode/XMLCharacterData.java b/sf/saxon/serialize/charcode/XMLCharacterData.java
new file mode 100644
index 0000000..39d23f2
--- /dev/null
+++ b/sf/saxon/serialize/charcode/XMLCharacterData.java
@@ -0,0 +1,773 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.charcode;
+
+import net.sf.saxon.z.IntRangeSet;
+
+import java.util.Arrays;
+
+/**
+ * This module contains data regarding the classification of characters in XML 1.0 and XML 1.1, and a number
+ * of interrogative methods to support queries on this data.
+ *
+ * For characters in the BMP, the information is tabulated by means of an array of one-byte entries,
+ * one for each character, with bit-significant property settings. For characters outside the BMP, the
+ * rules are built in to the interrogative methods.
+ */
+public class XMLCharacterData {
+
+ private final static byte[] data = new byte[65536];
+
+ /**
+ * Bit setting to indicate that a character is valid in XML 1.0
+ */
+ public final static byte VALID_10_MASK = 1;
+ /**
+ * Bit setting to indicate that a character is valid in an XML 1.0 name
+ */
+ public final static byte NAME_10_MASK = 2;
+ /**
+ * Bit setting to indicate that a character is valid at the start of an XML 1.0 name
+ */
+ public final static byte NAME_START_10_MASK = 4;
+ /**
+ * Bit setting to indicate that a character is valid in XML 1.1
+ */
+ public final static byte VALID_11_MASK = 8;
+ /**
+ * Bit setting to indicate that a character is valid in an XML 1.1 name
+ */
+ public final static byte NAME_11_MASK = 16;
+ /**
+ * Bit setting to indicate that a character is valid at the start of an XML 1.1 name
+ */
+ public final static byte NAME_START_11_MASK = 32;
+ /**
+ * Maximum code point for a character permitted in an XML 1.1 name
+ */
+ public final static int MAX_XML11_NAME_CHAR = 0xEFFFF;
+
+ /**
+ * Determine whether a character is valid in XML 1.0
+ * @param i the character
+ * @return true if the character is valid in XML 1.0
+ */
+
+ public static boolean isValid10(int i) {
+ return i < 65536 ? (data[i]&VALID_10_MASK) != 0 : (UTF16CharacterSet.NONBMP_MIN <= i && i <= UTF16CharacterSet.NONBMP_MAX);
+ }
+
+ /**
+ * Determine whether a character is valid in an NCName in XML 1.0
+ * @param i the character
+ * @return true if the character is valid in an NCName in XML 1.0
+ */
+
+ public static boolean isNCName10(int i) {
+ return i < 65536 && (data[i]&NAME_10_MASK) != 0;
+ }
+
+ /**
+ * Determine whether a character is valid at the start of an NCName in XML 1.0
+ * @param i the character
+ * @return true if the character is valid at the start of an NCName in XML 1.0
+ */
+
+ public static boolean isNCNameStart10(int i) {
+ return i < 65536 && (data[i]&NAME_START_10_MASK) != 0;
+ }
+
+ /**
+ * Determine whether a character is valid in XML 1.1
+ * @param i the character
+ * @return true if the character is valid in XML 1.1
+ */
+
+ public static boolean isValid11(int i) {
+ return i < 65536 ? (data[i]&VALID_11_MASK) != 0 : (UTF16CharacterSet.NONBMP_MIN <= i && i <= UTF16CharacterSet.NONBMP_MAX);
+ }
+
+ /**
+ * Determine whether a character is valid in an NCName in XML 1.1
+ * @param i the character
+ * @return true if the character is valid in an NCName in XML 1.1
+ */
+
+ public static boolean isNCName11(int i) {
+ return i < 65536 ? (data[i]&NAME_11_MASK) != 0 : (UTF16CharacterSet.NONBMP_MIN <= i && i <= MAX_XML11_NAME_CHAR);
+ }
+
+ /**
+ * Determine whether a character is valid at the start of an NCName in XML 1.1
+ * @param i the character
+ * @return true if the character is valid at the start of an NCName in XML 1.1
+ */
+
+ public static boolean isNCNameStart11(int i) {
+ return i < 65536 ? (data[i]&NAME_START_11_MASK) != 0 : (UTF16CharacterSet.NONBMP_MIN <= i && i <= MAX_XML11_NAME_CHAR);
+ }
+
+ /**
+ * Static code to initialize the data table
+ */
+
+ static {
+ data[0] = (byte)0;
+ Arrays.fill(data, 1, 9, (byte)8);
+ Arrays.fill(data, 9, 11, (byte)9);
+ Arrays.fill(data, 11, 13, (byte)8);
+ data[13] = (byte)9;
+ Arrays.fill(data, 14, 32, (byte)8);
+ Arrays.fill(data, 32, 45, (byte)9);
+ Arrays.fill(data, 45, 47, (byte)27);
+ data[47] = (byte)9;
+ Arrays.fill(data, 48, 58, (byte)27);
+ data[58] = (byte)9; // colon
+ Arrays.fill(data, 59, 65, (byte)9);
+ Arrays.fill(data, 65, 91, (byte)63);
+ Arrays.fill(data, 91, 95, (byte)9);
+ data[95] = (byte)63;
+ data[96] = (byte)9;
+ Arrays.fill(data, 97, 123, (byte)63);
+ Arrays.fill(data, 123, 183, (byte)9);
+ data[183] = (byte)27;
+ Arrays.fill(data, 184, 192, (byte)9);
+ Arrays.fill(data, 192, 215, (byte)63);
+ data[215] = (byte)9;
+ Arrays.fill(data, 216, 247, (byte)63);
+ data[247] = (byte)9;
+ Arrays.fill(data, 248, 306, (byte)63);
+ Arrays.fill(data, 306, 308, (byte)57);
+ Arrays.fill(data, 308, 319, (byte)63);
+ Arrays.fill(data, 319, 321, (byte)57);
+ Arrays.fill(data, 321, 329, (byte)63);
+ data[329] = (byte)57;
+ Arrays.fill(data, 330, 383, (byte)63);
+ data[383] = (byte)57;
+ Arrays.fill(data, 384, 452, (byte)63);
+ Arrays.fill(data, 452, 461, (byte)57);
+ Arrays.fill(data, 461, 497, (byte)63);
+ Arrays.fill(data, 497, 500, (byte)57);
+ Arrays.fill(data, 500, 502, (byte)63);
+ Arrays.fill(data, 502, 506, (byte)57);
+ Arrays.fill(data, 506, 536, (byte)63);
+ Arrays.fill(data, 536, 592, (byte)57);
+ Arrays.fill(data, 592, 681, (byte)63);
+ Arrays.fill(data, 681, 699, (byte)57);
+ Arrays.fill(data, 699, 706, (byte)63);
+ Arrays.fill(data, 706, 720, (byte)57);
+ Arrays.fill(data, 720, 722, (byte)59);
+ Arrays.fill(data, 722, 768, (byte)57);
+ Arrays.fill(data, 768, 838, (byte)27);
+ Arrays.fill(data, 838, 864, (byte)25);
+ Arrays.fill(data, 864, 866, (byte)27);
+ Arrays.fill(data, 866, 880, (byte)25);
+ Arrays.fill(data, 880, 894, (byte)57);
+ data[894] = (byte)9;
+ Arrays.fill(data, 895, 902, (byte)57);
+ data[902] = (byte)63;
+ data[903] = (byte)59;
+ Arrays.fill(data, 904, 907, (byte)63);
+ data[907] = (byte)57;
+ data[908] = (byte)63;
+ data[909] = (byte)57;
+ Arrays.fill(data, 910, 930, (byte)63);
+ data[930] = (byte)57;
+ Arrays.fill(data, 931, 975, (byte)63);
+ data[975] = (byte)57;
+ Arrays.fill(data, 976, 983, (byte)63);
+ Arrays.fill(data, 983, 986, (byte)57);
+ data[986] = (byte)63;
+ data[987] = (byte)57;
+ data[988] = (byte)63;
+ data[989] = (byte)57;
+ data[990] = (byte)63;
+ data[991] = (byte)57;
+ data[992] = (byte)63;
+ data[993] = (byte)57;
+ Arrays.fill(data, 994, 1012, (byte)63);
+ Arrays.fill(data, 1012, 1025, (byte)57);
+ Arrays.fill(data, 1025, 1037, (byte)63);
+ data[1037] = (byte)57;
+ Arrays.fill(data, 1038, 1104, (byte)63);
+ data[1104] = (byte)57;
+ Arrays.fill(data, 1105, 1117, (byte)63);
+ data[1117] = (byte)57;
+ Arrays.fill(data, 1118, 1154, (byte)63);
+ data[1154] = (byte)57;
+ Arrays.fill(data, 1155, 1159, (byte)59);
+ Arrays.fill(data, 1159, 1168, (byte)57);
+ Arrays.fill(data, 1168, 1221, (byte)63);
+ Arrays.fill(data, 1221, 1223, (byte)57);
+ Arrays.fill(data, 1223, 1225, (byte)63);
+ Arrays.fill(data, 1225, 1227, (byte)57);
+ Arrays.fill(data, 1227, 1229, (byte)63);
+ Arrays.fill(data, 1229, 1232, (byte)57);
+ Arrays.fill(data, 1232, 1260, (byte)63);
+ Arrays.fill(data, 1260, 1262, (byte)57);
+ Arrays.fill(data, 1262, 1270, (byte)63);
+ Arrays.fill(data, 1270, 1272, (byte)57);
+ Arrays.fill(data, 1272, 1274, (byte)63);
+ Arrays.fill(data, 1274, 1329, (byte)57);
+ Arrays.fill(data, 1329, 1367, (byte)63);
+ Arrays.fill(data, 1367, 1369, (byte)57);
+ data[1369] = (byte)63;
+ Arrays.fill(data, 1370, 1377, (byte)57);
+ Arrays.fill(data, 1377, 1415, (byte)63);
+ Arrays.fill(data, 1415, 1425, (byte)57);
+ Arrays.fill(data, 1425, 1442, (byte)59);
+ data[1442] = (byte)57;
+ Arrays.fill(data, 1443, 1466, (byte)59);
+ data[1466] = (byte)57;
+ Arrays.fill(data, 1467, 1470, (byte)59);
+ data[1470] = (byte)57;
+ data[1471] = (byte)59;
+ data[1472] = (byte)57;
+ Arrays.fill(data, 1473, 1475, (byte)59);
+ data[1475] = (byte)57;
+ data[1476] = (byte)59;
+ Arrays.fill(data, 1477, 1488, (byte)57);
+ Arrays.fill(data, 1488, 1515, (byte)63);
+ Arrays.fill(data, 1515, 1520, (byte)57);
+ Arrays.fill(data, 1520, 1523, (byte)63);
+ Arrays.fill(data, 1523, 1569, (byte)57);
+ Arrays.fill(data, 1569, 1595, (byte)63);
+ Arrays.fill(data, 1595, 1600, (byte)57);
+ data[1600] = (byte)59;
+ Arrays.fill(data, 1601, 1611, (byte)63);
+ Arrays.fill(data, 1611, 1619, (byte)59);
+ Arrays.fill(data, 1619, 1632, (byte)57);
+ Arrays.fill(data, 1632, 1642, (byte)59);
+ Arrays.fill(data, 1642, 1648, (byte)57);
+ data[1648] = (byte)59;
+ Arrays.fill(data, 1649, 1720, (byte)63);
+ Arrays.fill(data, 1720, 1722, (byte)57);
+ Arrays.fill(data, 1722, 1727, (byte)63);
+ data[1727] = (byte)57;
+ Arrays.fill(data, 1728, 1743, (byte)63);
+ data[1743] = (byte)57;
+ Arrays.fill(data, 1744, 1748, (byte)63);
+ data[1748] = (byte)57;
+ data[1749] = (byte)63;
+ Arrays.fill(data, 1750, 1765, (byte)59);
+ Arrays.fill(data, 1765, 1767, (byte)63);
+ Arrays.fill(data, 1767, 1769, (byte)59);
+ data[1769] = (byte)57;
+ Arrays.fill(data, 1770, 1774, (byte)59);
+ Arrays.fill(data, 1774, 1776, (byte)57);
+ Arrays.fill(data, 1776, 1786, (byte)59);
+ Arrays.fill(data, 1786, 2305, (byte)57);
+ Arrays.fill(data, 2305, 2308, (byte)59);
+ data[2308] = (byte)57;
+ Arrays.fill(data, 2309, 2362, (byte)63);
+ Arrays.fill(data, 2362, 2364, (byte)57);
+ data[2364] = (byte)59;
+ data[2365] = (byte)63;
+ Arrays.fill(data, 2366, 2382, (byte)59);
+ Arrays.fill(data, 2382, 2385, (byte)57);
+ Arrays.fill(data, 2385, 2389, (byte)59);
+ Arrays.fill(data, 2389, 2392, (byte)57);
+ Arrays.fill(data, 2392, 2402, (byte)63);
+ Arrays.fill(data, 2402, 2404, (byte)59);
+ Arrays.fill(data, 2404, 2406, (byte)57);
+ Arrays.fill(data, 2406, 2416, (byte)59);
+ Arrays.fill(data, 2416, 2433, (byte)57);
+ Arrays.fill(data, 2433, 2436, (byte)59);
+ data[2436] = (byte)57;
+ Arrays.fill(data, 2437, 2445, (byte)63);
+ Arrays.fill(data, 2445, 2447, (byte)57);
+ Arrays.fill(data, 2447, 2449, (byte)63);
+ Arrays.fill(data, 2449, 2451, (byte)57);
+ Arrays.fill(data, 2451, 2473, (byte)63);
+ data[2473] = (byte)57;
+ Arrays.fill(data, 2474, 2481, (byte)63);
+ data[2481] = (byte)57;
+ data[2482] = (byte)63;
+ Arrays.fill(data, 2483, 2486, (byte)57);
+ Arrays.fill(data, 2486, 2490, (byte)63);
+ Arrays.fill(data, 2490, 2492, (byte)57);
+ data[2492] = (byte)59;
+ data[2493] = (byte)57;
+ Arrays.fill(data, 2494, 2501, (byte)59);
+ Arrays.fill(data, 2501, 2503, (byte)57);
+ Arrays.fill(data, 2503, 2505, (byte)59);
+ Arrays.fill(data, 2505, 2507, (byte)57);
+ Arrays.fill(data, 2507, 2510, (byte)59);
+ Arrays.fill(data, 2510, 2519, (byte)57);
+ data[2519] = (byte)59;
+ Arrays.fill(data, 2520, 2524, (byte)57);
+ Arrays.fill(data, 2524, 2526, (byte)63);
+ data[2526] = (byte)57;
+ Arrays.fill(data, 2527, 2530, (byte)63);
+ Arrays.fill(data, 2530, 2532, (byte)59);
+ Arrays.fill(data, 2532, 2534, (byte)57);
+ Arrays.fill(data, 2534, 2544, (byte)59);
+ Arrays.fill(data, 2544, 2546, (byte)63);
+ Arrays.fill(data, 2546, 2562, (byte)57);
+ data[2562] = (byte)59;
+ Arrays.fill(data, 2563, 2565, (byte)57);
+ Arrays.fill(data, 2565, 2571, (byte)63);
+ Arrays.fill(data, 2571, 2575, (byte)57);
+ Arrays.fill(data, 2575, 2577, (byte)63);
+ Arrays.fill(data, 2577, 2579, (byte)57);
+ Arrays.fill(data, 2579, 2601, (byte)63);
+ data[2601] = (byte)57;
+ Arrays.fill(data, 2602, 2609, (byte)63);
+ data[2609] = (byte)57;
+ Arrays.fill(data, 2610, 2612, (byte)63);
+ data[2612] = (byte)57;
+ Arrays.fill(data, 2613, 2615, (byte)63);
+ data[2615] = (byte)57;
+ Arrays.fill(data, 2616, 2618, (byte)63);
+ Arrays.fill(data, 2618, 2620, (byte)57);
+ data[2620] = (byte)59;
+ data[2621] = (byte)57;
+ Arrays.fill(data, 2622, 2627, (byte)59);
+ Arrays.fill(data, 2627, 2631, (byte)57);
+ Arrays.fill(data, 2631, 2633, (byte)59);
+ Arrays.fill(data, 2633, 2635, (byte)57);
+ Arrays.fill(data, 2635, 2638, (byte)59);
+ Arrays.fill(data, 2638, 2649, (byte)57);
+ Arrays.fill(data, 2649, 2653, (byte)63);
+ data[2653] = (byte)57;
+ data[2654] = (byte)63;
+ Arrays.fill(data, 2655, 2662, (byte)57);
+ Arrays.fill(data, 2662, 2674, (byte)59);
+ Arrays.fill(data, 2674, 2677, (byte)63);
+ Arrays.fill(data, 2677, 2689, (byte)57);
+ Arrays.fill(data, 2689, 2692, (byte)59);
+ data[2692] = (byte)57;
+ Arrays.fill(data, 2693, 2700, (byte)63);
+ data[2700] = (byte)57;
+ data[2701] = (byte)63;
+ data[2702] = (byte)57;
+ Arrays.fill(data, 2703, 2706, (byte)63);
+ data[2706] = (byte)57;
+ Arrays.fill(data, 2707, 2729, (byte)63);
+ data[2729] = (byte)57;
+ Arrays.fill(data, 2730, 2737, (byte)63);
+ data[2737] = (byte)57;
+ Arrays.fill(data, 2738, 2740, (byte)63);
+ data[2740] = (byte)57;
+ Arrays.fill(data, 2741, 2746, (byte)63);
+ Arrays.fill(data, 2746, 2748, (byte)57);
+ data[2748] = (byte)59;
+ data[2749] = (byte)63;
+ Arrays.fill(data, 2750, 2758, (byte)59);
+ data[2758] = (byte)57;
+ Arrays.fill(data, 2759, 2762, (byte)59);
+ data[2762] = (byte)57;
+ Arrays.fill(data, 2763, 2766, (byte)59);
+ Arrays.fill(data, 2766, 2784, (byte)57);
+ data[2784] = (byte)63;
+ Arrays.fill(data, 2785, 2790, (byte)57);
+ Arrays.fill(data, 2790, 2800, (byte)59);
+ Arrays.fill(data, 2800, 2817, (byte)57);
+ Arrays.fill(data, 2817, 2820, (byte)59);
+ data[2820] = (byte)57;
+ Arrays.fill(data, 2821, 2829, (byte)63);
+ Arrays.fill(data, 2829, 2831, (byte)57);
+ Arrays.fill(data, 2831, 2833, (byte)63);
+ Arrays.fill(data, 2833, 2835, (byte)57);
+ Arrays.fill(data, 2835, 2857, (byte)63);
+ data[2857] = (byte)57;
+ Arrays.fill(data, 2858, 2865, (byte)63);
+ data[2865] = (byte)57;
+ Arrays.fill(data, 2866, 2868, (byte)63);
+ Arrays.fill(data, 2868, 2870, (byte)57);
+ Arrays.fill(data, 2870, 2874, (byte)63);
+ Arrays.fill(data, 2874, 2876, (byte)57);
+ data[2876] = (byte)59;
+ data[2877] = (byte)63;
+ Arrays.fill(data, 2878, 2884, (byte)59);
+ Arrays.fill(data, 2884, 2887, (byte)57);
+ Arrays.fill(data, 2887, 2889, (byte)59);
+ Arrays.fill(data, 2889, 2891, (byte)57);
+ Arrays.fill(data, 2891, 2894, (byte)59);
+ Arrays.fill(data, 2894, 2902, (byte)57);
+ Arrays.fill(data, 2902, 2904, (byte)59);
+ Arrays.fill(data, 2904, 2908, (byte)57);
+ Arrays.fill(data, 2908, 2910, (byte)63);
+ data[2910] = (byte)57;
+ Arrays.fill(data, 2911, 2914, (byte)63);
+ Arrays.fill(data, 2914, 2918, (byte)57);
+ Arrays.fill(data, 2918, 2928, (byte)59);
+ Arrays.fill(data, 2928, 2946, (byte)57);
+ Arrays.fill(data, 2946, 2948, (byte)59);
+ data[2948] = (byte)57;
+ Arrays.fill(data, 2949, 2955, (byte)63);
+ Arrays.fill(data, 2955, 2958, (byte)57);
+ Arrays.fill(data, 2958, 2961, (byte)63);
+ data[2961] = (byte)57;
+ Arrays.fill(data, 2962, 2966, (byte)63);
+ Arrays.fill(data, 2966, 2969, (byte)57);
+ Arrays.fill(data, 2969, 2971, (byte)63);
+ data[2971] = (byte)57;
+ data[2972] = (byte)63;
+ data[2973] = (byte)57;
+ Arrays.fill(data, 2974, 2976, (byte)63);
+ Arrays.fill(data, 2976, 2979, (byte)57);
+ Arrays.fill(data, 2979, 2981, (byte)63);
+ Arrays.fill(data, 2981, 2984, (byte)57);
+ Arrays.fill(data, 2984, 2987, (byte)63);
+ Arrays.fill(data, 2987, 2990, (byte)57);
+ Arrays.fill(data, 2990, 2998, (byte)63);
+ data[2998] = (byte)57;
+ Arrays.fill(data, 2999, 3002, (byte)63);
+ Arrays.fill(data, 3002, 3006, (byte)57);
+ Arrays.fill(data, 3006, 3011, (byte)59);
+ Arrays.fill(data, 3011, 3014, (byte)57);
+ Arrays.fill(data, 3014, 3017, (byte)59);
+ data[3017] = (byte)57;
+ Arrays.fill(data, 3018, 3022, (byte)59);
+ Arrays.fill(data, 3022, 3031, (byte)57);
+ data[3031] = (byte)59;
+ Arrays.fill(data, 3032, 3047, (byte)57);
+ Arrays.fill(data, 3047, 3056, (byte)59);
+ Arrays.fill(data, 3056, 3073, (byte)57);
+ Arrays.fill(data, 3073, 3076, (byte)59);
+ data[3076] = (byte)57;
+ Arrays.fill(data, 3077, 3085, (byte)63);
+ data[3085] = (byte)57;
+ Arrays.fill(data, 3086, 3089, (byte)63);
+ data[3089] = (byte)57;
+ Arrays.fill(data, 3090, 3113, (byte)63);
+ data[3113] = (byte)57;
+ Arrays.fill(data, 3114, 3124, (byte)63);
+ data[3124] = (byte)57;
+ Arrays.fill(data, 3125, 3130, (byte)63);
+ Arrays.fill(data, 3130, 3134, (byte)57);
+ Arrays.fill(data, 3134, 3141, (byte)59);
+ data[3141] = (byte)57;
+ Arrays.fill(data, 3142, 3145, (byte)59);
+ data[3145] = (byte)57;
+ Arrays.fill(data, 3146, 3150, (byte)59);
+ Arrays.fill(data, 3150, 3157, (byte)57);
+ Arrays.fill(data, 3157, 3159, (byte)59);
+ Arrays.fill(data, 3159, 3168, (byte)57);
+ Arrays.fill(data, 3168, 3170, (byte)63);
+ Arrays.fill(data, 3170, 3174, (byte)57);
+ Arrays.fill(data, 3174, 3184, (byte)59);
+ Arrays.fill(data, 3184, 3202, (byte)57);
+ Arrays.fill(data, 3202, 3204, (byte)59);
+ data[3204] = (byte)57;
+ Arrays.fill(data, 3205, 3213, (byte)63);
+ data[3213] = (byte)57;
+ Arrays.fill(data, 3214, 3217, (byte)63);
+ data[3217] = (byte)57;
+ Arrays.fill(data, 3218, 3241, (byte)63);
+ data[3241] = (byte)57;
+ Arrays.fill(data, 3242, 3252, (byte)63);
+ data[3252] = (byte)57;
+ Arrays.fill(data, 3253, 3258, (byte)63);
+ Arrays.fill(data, 3258, 3262, (byte)57);
+ Arrays.fill(data, 3262, 3269, (byte)59);
+ data[3269] = (byte)57;
+ Arrays.fill(data, 3270, 3273, (byte)59);
+ data[3273] = (byte)57;
+ Arrays.fill(data, 3274, 3278, (byte)59);
+ Arrays.fill(data, 3278, 3285, (byte)57);
+ Arrays.fill(data, 3285, 3287, (byte)59);
+ Arrays.fill(data, 3287, 3294, (byte)57);
+ data[3294] = (byte)63;
+ data[3295] = (byte)57;
+ Arrays.fill(data, 3296, 3298, (byte)63);
+ Arrays.fill(data, 3298, 3302, (byte)57);
+ Arrays.fill(data, 3302, 3312, (byte)59);
+ Arrays.fill(data, 3312, 3330, (byte)57);
+ Arrays.fill(data, 3330, 3332, (byte)59);
+ data[3332] = (byte)57;
+ Arrays.fill(data, 3333, 3341, (byte)63);
+ data[3341] = (byte)57;
+ Arrays.fill(data, 3342, 3345, (byte)63);
+ data[3345] = (byte)57;
+ Arrays.fill(data, 3346, 3369, (byte)63);
+ data[3369] = (byte)57;
+ Arrays.fill(data, 3370, 3386, (byte)63);
+ Arrays.fill(data, 3386, 3390, (byte)57);
+ Arrays.fill(data, 3390, 3396, (byte)59);
+ Arrays.fill(data, 3396, 3398, (byte)57);
+ Arrays.fill(data, 3398, 3401, (byte)59);
+ data[3401] = (byte)57;
+ Arrays.fill(data, 3402, 3406, (byte)59);
+ Arrays.fill(data, 3406, 3415, (byte)57);
+ data[3415] = (byte)59;
+ Arrays.fill(data, 3416, 3424, (byte)57);
+ Arrays.fill(data, 3424, 3426, (byte)63);
+ Arrays.fill(data, 3426, 3430, (byte)57);
+ Arrays.fill(data, 3430, 3440, (byte)59);
+ Arrays.fill(data, 3440, 3585, (byte)57);
+ Arrays.fill(data, 3585, 3631, (byte)63);
+ data[3631] = (byte)57;
+ data[3632] = (byte)63;
+ data[3633] = (byte)59;
+ Arrays.fill(data, 3634, 3636, (byte)63);
+ Arrays.fill(data, 3636, 3643, (byte)59);
+ Arrays.fill(data, 3643, 3648, (byte)57);
+ Arrays.fill(data, 3648, 3654, (byte)63);
+ Arrays.fill(data, 3654, 3663, (byte)59);
+ data[3663] = (byte)57;
+ Arrays.fill(data, 3664, 3674, (byte)59);
+ Arrays.fill(data, 3674, 3713, (byte)57);
+ Arrays.fill(data, 3713, 3715, (byte)63);
+ data[3715] = (byte)57;
+ data[3716] = (byte)63;
+ Arrays.fill(data, 3717, 3719, (byte)57);
+ Arrays.fill(data, 3719, 3721, (byte)63);
+ data[3721] = (byte)57;
+ data[3722] = (byte)63;
+ Arrays.fill(data, 3723, 3725, (byte)57);
+ data[3725] = (byte)63;
+ Arrays.fill(data, 3726, 3732, (byte)57);
+ Arrays.fill(data, 3732, 3736, (byte)63);
+ data[3736] = (byte)57;
+ Arrays.fill(data, 3737, 3744, (byte)63);
+ data[3744] = (byte)57;
+ Arrays.fill(data, 3745, 3748, (byte)63);
+ data[3748] = (byte)57;
+ data[3749] = (byte)63;
+ data[3750] = (byte)57;
+ data[3751] = (byte)63;
+ Arrays.fill(data, 3752, 3754, (byte)57);
+ Arrays.fill(data, 3754, 3756, (byte)63);
+ data[3756] = (byte)57;
+ Arrays.fill(data, 3757, 3759, (byte)63);
+ data[3759] = (byte)57;
+ data[3760] = (byte)63;
+ data[3761] = (byte)59;
+ Arrays.fill(data, 3762, 3764, (byte)63);
+ Arrays.fill(data, 3764, 3770, (byte)59);
+ data[3770] = (byte)57;
+ Arrays.fill(data, 3771, 3773, (byte)59);
+ data[3773] = (byte)63;
+ Arrays.fill(data, 3774, 3776, (byte)57);
+ Arrays.fill(data, 3776, 3781, (byte)63);
+ data[3781] = (byte)57;
+ data[3782] = (byte)59;
+ data[3783] = (byte)57;
+ Arrays.fill(data, 3784, 3790, (byte)59);
+ Arrays.fill(data, 3790, 3792, (byte)57);
+ Arrays.fill(data, 3792, 3802, (byte)59);
+ Arrays.fill(data, 3802, 3864, (byte)57);
+ Arrays.fill(data, 3864, 3866, (byte)59);
+ Arrays.fill(data, 3866, 3872, (byte)57);
+ Arrays.fill(data, 3872, 3882, (byte)59);
+ Arrays.fill(data, 3882, 3893, (byte)57);
+ data[3893] = (byte)59;
+ data[3894] = (byte)57;
+ data[3895] = (byte)59;
+ data[3896] = (byte)57;
+ data[3897] = (byte)59;
+ Arrays.fill(data, 3898, 3902, (byte)57);
+ Arrays.fill(data, 3902, 3904, (byte)59);
+ Arrays.fill(data, 3904, 3912, (byte)63);
+ data[3912] = (byte)57;
+ Arrays.fill(data, 3913, 3946, (byte)63);
+ Arrays.fill(data, 3946, 3953, (byte)57);
+ Arrays.fill(data, 3953, 3973, (byte)59);
+ data[3973] = (byte)57;
+ Arrays.fill(data, 3974, 3980, (byte)59);
+ Arrays.fill(data, 3980, 3984, (byte)57);
+ Arrays.fill(data, 3984, 3990, (byte)59);
+ data[3990] = (byte)57;
+ data[3991] = (byte)59;
+ data[3992] = (byte)57;
+ Arrays.fill(data, 3993, 4014, (byte)59);
+ Arrays.fill(data, 4014, 4017, (byte)57);
+ Arrays.fill(data, 4017, 4024, (byte)59);
+ data[4024] = (byte)57;
+ data[4025] = (byte)59;
+ Arrays.fill(data, 4026, 4256, (byte)57);
+ Arrays.fill(data, 4256, 4294, (byte)63);
+ Arrays.fill(data, 4294, 4304, (byte)57);
+ Arrays.fill(data, 4304, 4343, (byte)63);
+ Arrays.fill(data, 4343, 4352, (byte)57);
+ data[4352] = (byte)63;
+ data[4353] = (byte)57;
+ Arrays.fill(data, 4354, 4356, (byte)63);
+ data[4356] = (byte)57;
+ Arrays.fill(data, 4357, 4360, (byte)63);
+ data[4360] = (byte)57;
+ data[4361] = (byte)63;
+ data[4362] = (byte)57;
+ Arrays.fill(data, 4363, 4365, (byte)63);
+ data[4365] = (byte)57;
+ Arrays.fill(data, 4366, 4371, (byte)63);
+ Arrays.fill(data, 4371, 4412, (byte)57);
+ data[4412] = (byte)63;
+ data[4413] = (byte)57;
+ data[4414] = (byte)63;
+ data[4415] = (byte)57;
+ data[4416] = (byte)63;
+ Arrays.fill(data, 4417, 4428, (byte)57);
+ data[4428] = (byte)63;
+ data[4429] = (byte)57;
+ data[4430] = (byte)63;
+ data[4431] = (byte)57;
+ data[4432] = (byte)63;
+ Arrays.fill(data, 4433, 4436, (byte)57);
+ Arrays.fill(data, 4436, 4438, (byte)63);
+ Arrays.fill(data, 4438, 4441, (byte)57);
+ data[4441] = (byte)63;
+ Arrays.fill(data, 4442, 4447, (byte)57);
+ Arrays.fill(data, 4447, 4450, (byte)63);
+ data[4450] = (byte)57;
+ data[4451] = (byte)63;
+ data[4452] = (byte)57;
+ data[4453] = (byte)63;
+ data[4454] = (byte)57;
+ data[4455] = (byte)63;
+ data[4456] = (byte)57;
+ data[4457] = (byte)63;
+ Arrays.fill(data, 4458, 4461, (byte)57);
+ Arrays.fill(data, 4461, 4463, (byte)63);
+ Arrays.fill(data, 4463, 4466, (byte)57);
+ Arrays.fill(data, 4466, 4468, (byte)63);
+ data[4468] = (byte)57;
+ data[4469] = (byte)63;
+ Arrays.fill(data, 4470, 4510, (byte)57);
+ data[4510] = (byte)63;
+ Arrays.fill(data, 4511, 4520, (byte)57);
+ data[4520] = (byte)63;
+ Arrays.fill(data, 4521, 4523, (byte)57);
+ data[4523] = (byte)63;
+ Arrays.fill(data, 4524, 4526, (byte)57);
+ Arrays.fill(data, 4526, 4528, (byte)63);
+ Arrays.fill(data, 4528, 4535, (byte)57);
+ Arrays.fill(data, 4535, 4537, (byte)63);
+ data[4537] = (byte)57;
+ data[4538] = (byte)63;
+ data[4539] = (byte)57;
+ Arrays.fill(data, 4540, 4547, (byte)63);
+ Arrays.fill(data, 4547, 4587, (byte)57);
+ data[4587] = (byte)63;
+ Arrays.fill(data, 4588, 4592, (byte)57);
+ data[4592] = (byte)63;
+ Arrays.fill(data, 4593, 4601, (byte)57);
+ data[4601] = (byte)63;
+ Arrays.fill(data, 4602, 7680, (byte)57);
+ Arrays.fill(data, 7680, 7836, (byte)63);
+ Arrays.fill(data, 7836, 7840, (byte)57);
+ Arrays.fill(data, 7840, 7930, (byte)63);
+ Arrays.fill(data, 7930, 7936, (byte)57);
+ Arrays.fill(data, 7936, 7958, (byte)63);
+ Arrays.fill(data, 7958, 7960, (byte)57);
+ Arrays.fill(data, 7960, 7966, (byte)63);
+ Arrays.fill(data, 7966, 7968, (byte)57);
+ Arrays.fill(data, 7968, 8006, (byte)63);
+ Arrays.fill(data, 8006, 8008, (byte)57);
+ Arrays.fill(data, 8008, 8014, (byte)63);
+ Arrays.fill(data, 8014, 8016, (byte)57);
+ Arrays.fill(data, 8016, 8024, (byte)63);
+ data[8024] = (byte)57;
+ data[8025] = (byte)63;
+ data[8026] = (byte)57;
+ data[8027] = (byte)63;
+ data[8028] = (byte)57;
+ data[8029] = (byte)63;
+ data[8030] = (byte)57;
+ Arrays.fill(data, 8031, 8062, (byte)63);
+ Arrays.fill(data, 8062, 8064, (byte)57);
+ Arrays.fill(data, 8064, 8117, (byte)63);
+ data[8117] = (byte)57;
+ Arrays.fill(data, 8118, 8125, (byte)63);
+ data[8125] = (byte)57;
+ data[8126] = (byte)63;
+ Arrays.fill(data, 8127, 8130, (byte)57);
+ Arrays.fill(data, 8130, 8133, (byte)63);
+ data[8133] = (byte)57;
+ Arrays.fill(data, 8134, 8141, (byte)63);
+ Arrays.fill(data, 8141, 8144, (byte)57);
+ Arrays.fill(data, 8144, 8148, (byte)63);
+ Arrays.fill(data, 8148, 8150, (byte)57);
+ Arrays.fill(data, 8150, 8156, (byte)63);
+ Arrays.fill(data, 8156, 8160, (byte)57);
+ Arrays.fill(data, 8160, 8173, (byte)63);
+ Arrays.fill(data, 8173, 8178, (byte)57);
+ Arrays.fill(data, 8178, 8181, (byte)63);
+ data[8181] = (byte)57;
+ Arrays.fill(data, 8182, 8189, (byte)63);
+ Arrays.fill(data, 8189, 8192, (byte)57);
+ Arrays.fill(data, 8192, 8204, (byte)9);
+ Arrays.fill(data, 8204, 8206, (byte)57);
+ Arrays.fill(data, 8206, 8255, (byte)9);
+ Arrays.fill(data, 8255, 8257, (byte)25);
+ Arrays.fill(data, 8257, 8304, (byte)9);
+ Arrays.fill(data, 8304, 8400, (byte)57);
+ Arrays.fill(data, 8400, 8413, (byte)59);
+ Arrays.fill(data, 8413, 8417, (byte)57);
+ data[8417] = (byte)59;
+ Arrays.fill(data, 8418, 8486, (byte)57);
+ data[8486] = (byte)63;
+ Arrays.fill(data, 8487, 8490, (byte)57);
+ Arrays.fill(data, 8490, 8492, (byte)63);
+ Arrays.fill(data, 8492, 8494, (byte)57);
+ data[8494] = (byte)63;
+ Arrays.fill(data, 8495, 8576, (byte)57);
+ Arrays.fill(data, 8576, 8579, (byte)63);
+ Arrays.fill(data, 8579, 8592, (byte)57);
+ Arrays.fill(data, 8592, 11264, (byte)9);
+ Arrays.fill(data, 11264, 12272, (byte)57);
+ Arrays.fill(data, 12272, 12289, (byte)9);
+ Arrays.fill(data, 12289, 12293, (byte)57);
+ data[12293] = (byte)59;
+ data[12294] = (byte)57;
+ data[12295] = (byte)63;
+ Arrays.fill(data, 12296, 12321, (byte)57);
+ Arrays.fill(data, 12321, 12330, (byte)63);
+ Arrays.fill(data, 12330, 12336, (byte)59);
+ data[12336] = (byte)57;
+ Arrays.fill(data, 12337, 12342, (byte)59);
+ Arrays.fill(data, 12342, 12353, (byte)57);
+ Arrays.fill(data, 12353, 12437, (byte)63);
+ Arrays.fill(data, 12437, 12441, (byte)57);
+ Arrays.fill(data, 12441, 12443, (byte)59);
+ Arrays.fill(data, 12443, 12445, (byte)57);
+ Arrays.fill(data, 12445, 12447, (byte)59);
+ Arrays.fill(data, 12447, 12449, (byte)57);
+ Arrays.fill(data, 12449, 12539, (byte)63);
+ data[12539] = (byte)57;
+ Arrays.fill(data, 12540, 12543, (byte)59);
+ Arrays.fill(data, 12543, 12549, (byte)57);
+ Arrays.fill(data, 12549, 12589, (byte)63);
+ Arrays.fill(data, 12589, 19968, (byte)57);
+ Arrays.fill(data, 19968, 40870, (byte)63);
+ Arrays.fill(data, 40870, 44032, (byte)57);
+ Arrays.fill(data, 44032, 55204, (byte)63);
+ Arrays.fill(data, 55204, 55296, (byte)57);
+ Arrays.fill(data, 55296, 57344, (byte)0);
+ Arrays.fill(data, 57344, 63744, (byte)9);
+ Arrays.fill(data, 63744, 64976, (byte)57);
+ Arrays.fill(data, 64976, 65008, (byte)9);
+ Arrays.fill(data, 65008, 65534, (byte)57);
+ Arrays.fill(data, 65534, 65536, (byte)0);
+ }
+
+ /**
+ * Get all the characters in a given category, as an integer set. This must be one of the four
+ * name classes: Name characters or Name Start characters in XML 1.0 or XML 1.1. (This method
+ * is used to populate the data tables used by the regular expression translators)
+ * @param mask identifies the properties of the required category
+ * @return the set of characters in the given category.
+ */
+
+ /*@NotNull*/ public static IntRangeSet getCategory(byte mask) {
+ IntRangeSet irs = new IntRangeSet();
+ for (int i=0; i<65536; i++) {
+ if ((data[i]&mask) != 0) {
+ irs.add(i);
+ }
+ }
+ if ((mask & (NAME_START_11_MASK | NAME_11_MASK)) != 0) {
+ irs.addRange(UTF16CharacterSet.NONBMP_MIN, MAX_XML11_NAME_CHAR);
+ }
+ return irs;
+ }
+
+}
+
diff --git a/sf/saxon/serialize/charcode/package.html b/sf/saxon/serialize/charcode/package.html
new file mode 100644
index 0000000..ba8915a
--- /dev/null
+++ b/sf/saxon/serialize/charcode/package.html
@@ -0,0 +1,50 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.charcode</title>
+</head>
+
+<body>
+
+<p>This package provides classes for handling different character sets, especially
+ when serializing the output of a query or transformation. </p>
+
+<p>Most of the classes in this package are implementations of the interface <code>CharacterSet</code>.
+ The sole
+function of these classes is to determine whether a particular character is present in the
+character set or not: if not, Saxon has to replace it with a character reference.</p>
+
+<p>The actual translation of Unicode characters to characters in the selected encoding
+is left to the Java run-time library. (Note that different versions of Java support
+different sets of encodings, and there is no easy way to find out which encodings
+are supported in a given installation).</p>
+
+<p>It is possible to configure Saxon to support additional character sets by writing an
+implementation of the <code>CharacterSet</code> interface, and registering this class with the
+<code>Configuration</code> using the call <code>getCharaterSetFactory().setCharacterSetImplementation()</code></p>
+
+<p>If an output encoding is requested that Saxon does not recognize, but which the Java
+platform does recognize, then Saxon attempts to determine which characters the encoding
+can represent, so that unsupported characters can be written as numeric character references.
+Saxon wraps the Java <code>CharSet</code> object in a <code>JavaCharacterSet</code> object,
+and tests whether a character is encodable by calling the Java interrogative
+<code>encoding.canEncode()</code>, caching the result locally. Since this mechanism
+appears to have become reliable in JDK 1.5, it is now used much more widely than before,
+and most character sets are now supported in Saxon by relying on this mechanism.</p>
+
+
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 June 2009</i></p>
+</body>
+</html>
diff --git a/sf/saxon/serialize/codenorm/Normalizer.java b/sf/saxon/serialize/codenorm/Normalizer.java
new file mode 100644
index 0000000..907957f
--- /dev/null
+++ b/sf/saxon/serialize/codenorm/Normalizer.java
@@ -0,0 +1,269 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.codenorm;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+/**
+ * Implements Unicode Normalization Forms C, D, KC, KD.
+ * Copyright (c) 1991-2005 Unicode, Inc.
+ * For terms of use, see http://www.unicode.org/terms_of_use.html
+ * For documentation, see UAX#15.<br>
+ * The Unicode Consortium makes no expressed or implied warranty of any
+ * kind, and assumes no liability for errors or omissions.
+ * No liability is assumed for incidental and consequential damages
+ * in connection with or arising out of the use of the information here.
+ * @author Mark Davis
+ * Updates for supplementary code points: Vladimir Weinstein & Markus Scherer
+ * Modified to remove dependency on ICU code: Michael Kay
+ */
+
+public class Normalizer {
+
+ /**
+ * The requested normalization form.
+ */
+ private int form;
+
+
+ /**
+ * Create a normalizer for a given form.
+ * @param form the normalization form required: for example {@link Normalizer#C}, {@link Normalizer#D}
+ * @param config the Saxon configuration
+ * @throws XPathException if normalization fails
+ */
+ public Normalizer(int form, Configuration config) throws XPathException {
+ this.form = form;
+ if (data == null) {
+ //data = UnicodeDataParser.build(); // load 1st time
+ data = UnicodeDataParserFromXML.build(config);
+ }
+ }
+
+ /**
+ * Masks for the form selector
+ */
+ static final int
+ COMPATIBILITY_MASK = 1,
+ COMPOSITION_MASK = 2;
+
+ /**
+ * Normalization Form Selector
+ */
+ public static final int
+ D = 0 ,
+ C = COMPOSITION_MASK,
+ KD = COMPATIBILITY_MASK,
+ KC = (byte)(COMPATIBILITY_MASK + COMPOSITION_MASK),
+ NO_ACTION = 8;
+
+ /**
+ * Normalizes text according to the chosen form
+ * @param source the original text, unnormalized
+ * @return target the resulting normalized text
+ */
+ public CharSequence normalize(CharSequence source) {
+
+ if (form == NO_ACTION || source.length() == 0) {
+ return source;
+ }
+
+ // First decompose the source into target,
+ // then compose if the form requires.
+
+ FastStringBuffer target = new FastStringBuffer(source.length()+8);
+ internalDecompose(source, target);
+ if ((form & COMPOSITION_MASK) != 0) {
+ internalCompose(target);
+ }
+ return target;
+ }
+
+
+ /**
+ * Decomposes text, either canonical or compatibility,
+ * replacing contents of the target buffer.
+// * @param form the normalization form. If COMPATIBILITY_MASK
+// * bit is on in this byte, then selects the recursive
+// * compatibility decomposition, otherwise selects
+// * the recursive canonical decomposition.
+ * @param source the original text, unnormalized
+ * @param target the resulting normalized text
+ */
+ private void internalDecompose(CharSequence source, FastStringBuffer target) {
+ FastStringBuffer buffer = new FastStringBuffer(8);
+ boolean canonical = (form & COMPATIBILITY_MASK) == 0;
+ int ch32;
+ for (int i = 0; i < source.length();) {
+ buffer.setLength(0);
+ ch32 = source.charAt(i++);
+ if (ch32 < 128) {
+ // fast path - for ASCII characters, decomposition is a no-op
+ target.append((char)ch32);
+ continue;
+ }
+ if (UTF16CharacterSet.isHighSurrogate(ch32)) {
+ char low = source.charAt(i++);
+ ch32 = UTF16CharacterSet.combinePair((char)ch32, low);
+ }
+ data.getRecursiveDecomposition(canonical, ch32, buffer);
+
+ // add all of the characters in the decomposition.
+ // (may be just the original character, if there was
+ // no decomposition mapping)
+
+ int ch;
+ for (int j = 0; j < buffer.length();) {
+ ch = buffer.charAt(j++);
+ if (UTF16CharacterSet.isHighSurrogate(ch)) {
+ char low = buffer.charAt(j++);
+ ch = UTF16CharacterSet.combinePair((char)ch, low);
+ }
+ int chClass = data.getCanonicalClass(ch);
+ int k = target.length(); // insertion point
+ if (chClass != 0) {
+
+ // bubble-sort combining marks as necessary
+
+ int ch2;
+ while (k > 0) {
+ int step = 1;
+ ch2 = target.charAt(k-1);
+ if (UTF16CharacterSet.isSurrogate(ch2)) {
+ step = 2;
+ char high = target.charAt(k-2);
+ ch2 = UTF16CharacterSet.combinePair(high, (char)ch2);
+ }
+ if (data.getCanonicalClass(ch2) <= chClass) break;
+ k -= step;
+ }
+ }
+ if (ch < 65536) {
+ target.insert(k, (char)ch);
+ } else {
+ target.insertWideChar(k, ch);
+ }
+ }
+ }
+ }
+
+ /**
+ * Composes text in place. Target must already
+ * have been decomposed.
+ * @param target input: decomposed text.
+ * output: the resulting normalized text.
+ */
+ private void internalCompose(FastStringBuffer target) {
+
+ int starterPos = 0;
+ //int starterCh = UTF16.charAt(target,0);
+ //int compPos = (starterCh<65536 ? 1 : 2); // length of last composition
+ int starterCh = target.charAt(0);
+ int compPos = 1;
+ if (UTF16CharacterSet.isHighSurrogate(starterCh)) {
+ starterCh = UTF16CharacterSet.combinePair((char)starterCh, target.charAt(1));
+ compPos++;
+ }
+ int lastClass = data.getCanonicalClass(starterCh);
+ if (lastClass != 0) lastClass = 256; // fix for strings staring with a combining mark
+ int oldLen = target.length();
+
+ // Loop on the decomposed characters, combining where possible
+
+ int ch;
+ //for (int decompPos = compPos; decompPos < target.length(); decompPos += (ch<65536 ? 1 : 2)) {
+ for (int decompPos = compPos; decompPos < target.length();) {
+ ch = target.charAt(decompPos++);
+ if (UTF16CharacterSet.isHighSurrogate(ch)) {
+ ch = UTF16CharacterSet.combinePair((char)ch, target.charAt(decompPos++));
+ }
+ //ch = UTF16.charAt(target, decompPos);
+ int chClass = data.getCanonicalClass(ch);
+ int composite = data.getPairwiseComposition(starterCh, ch);
+ if (composite != NormalizerData.NOT_COMPOSITE && (lastClass < chClass || lastClass == 0)) {
+ setCharAt(target, starterPos, composite);
+ // we know that we will only be replacing non-supplementaries by non-supplementaries
+ // so we don't have to adjust the decompPos
+ starterCh = composite;
+ } else {
+ if (chClass == 0) {
+ starterPos = compPos;
+ starterCh = ch;
+ }
+ lastClass = chClass;
+ setCharAt(target, compPos, ch);
+ if (target.length() != oldLen) { // MAY HAVE TO ADJUST!
+ decompPos += target.length() - oldLen;
+ oldLen = target.length();
+ }
+ compPos += (ch<65536 ? 1 : 2);
+ }
+ }
+ target.setLength(compPos);
+ }
+
+ /**
+ * Set the 32-bit character at a particular 16-bit offset in a string buffer,
+ * replacing the previous character at that position, and taking account of the
+ * fact that either, both, or neither of the characters might be a surrogate pair.
+ * @param target the StringBuffer in which the data is to be inserted
+ * @param offset the position at which the data is to be inserted
+ * @param ch32 the character to be inserted, as a 32-bit Unicode codepoint
+ */
+
+ private static void setCharAt(FastStringBuffer target, int offset, int ch32) {
+ if (ch32 < 65536) {
+ if (UTF16CharacterSet.isHighSurrogate(target.charAt(offset))) {
+ target.setCharAt(offset, (char)ch32);
+ target.removeCharAt(offset + 1);
+ } else {
+ target.setCharAt(offset, (char)ch32);
+ }
+ } else {
+ if (UTF16CharacterSet.isHighSurrogate(target.charAt(offset))) {
+ target.setCharAt(offset, UTF16CharacterSet.highSurrogate(ch32));
+ target.setCharAt(offset+1, UTF16CharacterSet.lowSurrogate(ch32));
+ } else {
+ target.setCharAt(offset, UTF16CharacterSet.highSurrogate(ch32));
+ target.insert(offset+1, UTF16CharacterSet.lowSurrogate(ch32));
+ }
+ }
+ }
+
+ /**
+ * Contains normalization data from the Unicode Character Database.
+ */
+ /*@Nullable*/ private static NormalizerData data = null;
+
+ /**
+ * Just accessible for testing.
+ * @param ch a character
+ * @return true if the character is an excluded character
+ */
+// boolean getExcluded (char ch) {
+// return data.getExcluded(ch);
+// }
+
+ /**
+ * Just accessible for testing.
+ * @param ch a character
+ * @return the raw decomposition mapping of the character
+ */
+// String getRawDecompositionMapping (char ch) {
+// return data.getRawDecompositionMapping(ch);
+// }
+}
+
+// * The class is derived from the sample program Normalizer.java published by the
+// * Unicode consortium.
+
+// * Updates for supplementary code points: Vladimir Weinstein & Markus Scherer
+// * Modified to remove dependency on ICU code: Michael Kay, Saxonica Limited
\ No newline at end of file
diff --git a/sf/saxon/serialize/codenorm/NormalizerData.java b/sf/saxon/serialize/codenorm/NormalizerData.java
new file mode 100644
index 0000000..289043d
--- /dev/null
+++ b/sf/saxon/serialize/codenorm/NormalizerData.java
@@ -0,0 +1,147 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.codenorm;
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntToIntMap;
+
+import java.util.BitSet;
+
+/**
+ * Accesses the Normalization Data used for Forms C and D.
+ * <p>Copyright (c) 1998-1999 Unicode, Inc. All Rights Reserved.<br>
+ * The Unicode Consortium makes no expressed or implied warranty of any
+ * kind, and assumes no liability for errors or omissions.
+ * No liability is assumed for incidental and consequential damages
+ * in connection with or arising out of the use of the information here.</p>
+ * @author Mark Davis
+ */
+public class NormalizerData {
+ static final String copyright = "Copyright (c) 1998-1999 Unicode, Inc.";
+
+ /**
+ * Constant for use in getPairwiseComposition
+ */
+ public static final int NOT_COMPOSITE = '\uFFFF';
+
+ /**
+ * Gets the combining class of a character from the
+ * Unicode Character Database.
+ * @param ch the source character
+ * @return value from 0 to 255
+ */
+ public int getCanonicalClass(int ch) {
+ return canonicalClass.get(ch);
+ }
+
+ /**
+ * Returns the composite of the two characters. If the two
+ * characters don't combine, returns NOT_COMPOSITE.
+ * Only has to worry about BMP characters, since those are the only ones that can ever compose.
+ * @param first first character (e.g. 'c')
+ * @param second second character (e.g. '�' cedilla)
+ * @return composite (e.g. '�')
+ */
+ public char getPairwiseComposition(int first, int second) {
+ if (first < 0 || first > 0x10FFFF || second < 0 || second > 0x10FFFF) return NOT_COMPOSITE;
+ return (char)compose.get((first << 16) | second);
+ }
+
+ /**
+ * Gets recursive decomposition of a character from the
+ * Unicode Character Database.
+ * @param canonical If true
+ * bit is on in this byte, then selects the recursive
+ * canonical decomposition, otherwise selects
+ * the recursive compatibility and canonical decomposition.
+ * @param ch the source character
+ * @param buffer buffer to be filled with the decomposition
+ */
+ public void getRecursiveDecomposition(boolean canonical, int ch, FastStringBuffer buffer) {
+ String decomp = (String)decompose.get(ch);
+ if (decomp != null && !(canonical && isCompatibility.get(ch))) {
+ for (int i = 0; i < decomp.length(); ++i) {
+ getRecursiveDecomposition(canonical, decomp.charAt(i), buffer);
+ }
+ } else { // if no decomp, append
+ buffer.appendWideChar(ch);
+ }
+ }
+
+ // =================================================
+ // PRIVATES
+ // =================================================
+
+ /**
+ * Only accessed by NormalizerBuilder.
+ */
+ NormalizerData(IntToIntMap canonicalClass, IntHashMap decompose,
+ IntToIntMap compose, BitSet isCompatibility, BitSet isExcluded) {
+ this.canonicalClass = canonicalClass;
+ this.decompose = decompose;
+ this.compose = compose;
+ this.isCompatibility = isCompatibility;
+ this.isExcluded = isExcluded;
+ }
+
+ /**
+ * Just accessible for testing.
+ */
+ boolean getExcluded (char ch) {
+ return isExcluded.get(ch);
+ }
+
+ /**
+ * Just accessible for testing.
+ */
+ /*@NotNull*/ String getRawDecompositionMapping (char ch) {
+ return (String)decompose.get(ch);
+ }
+
+ /**
+ * For now, just use IntHashtable
+ * Two-stage tables would be used in an optimized implementation.
+ */
+ private IntToIntMap canonicalClass;
+
+ /**
+ * The main data table maps chars to a 32-bit int.
+ * It holds either a pair: top = first, bottom = second
+ * or singleton: top = 0, bottom = single.
+ * If there is no decomposition, the value is 0.
+ * Two-stage tables would be used in an optimized implementation.
+ * An optimization could also map chars to a small index, then use that
+ * index in a small array of ints.
+ */
+ private IntHashMap decompose;
+
+ /**
+ * Maps from pairs of characters to single.
+ * If there is no decomposition, the value is NOT_COMPOSITE.
+ */
+ private IntToIntMap compose;
+
+ /**
+ * Tells whether decomposition is canonical or not.
+ */
+ private BitSet isCompatibility;
+
+ /**
+ * Tells whether character is script-excluded or not.
+ * Used only while building, and for testing.
+ */
+
+ private BitSet isExcluded;
+}
+
+// * The class is derived from the sample program NormalizerData.java published by the
+// * Unicode consortium. That code has been modified so that instead of building the run-time
+// * data structures directly, they are written to a Java "source" module, which is then
+// * compiled. Also, the ability to construct a condensed version of the data tables has been
+// * removed.
\ No newline at end of file
diff --git a/sf/saxon/serialize/codenorm/UnicodeDataGenerator.java b/sf/saxon/serialize/codenorm/UnicodeDataGenerator.java
new file mode 100644
index 0000000..5c25c3d
--- /dev/null
+++ b/sf/saxon/serialize/codenorm/UnicodeDataGenerator.java
@@ -0,0 +1,657 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.codenorm;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.StreamWriterToReceiver;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.Result;
+import javax.xml.transform.stream.StreamResult;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * This class reads the Unicode character database, extracts information needed
+ * to perform unicode normalization, and writes this information out in the form of the
+ * Java "source" module UnicodeData.java. This class is therefore executed (via its main()
+ * method) at the time Saxon is built - it only needs to be rerun when the Unicode data tables
+ * have changed.
+ * <p/>
+ * The class is derived from the sample program NormalizerData.java published by the
+ * Unicode consortium. That code has been modified so that instead of building the run-time
+ * data structures directly, they are written to a Java "source" module, which is then
+ * compiled. Also, the ability to construct a condensed version of the data tables has been
+ * removed.
+ * <p/>
+ * Copyright (c) 1991-2005 Unicode, Inc.
+ * For terms of use, see http://www.unicode.org/terms_of_use.html
+ * For documentation, see UAX#15.<br>
+ *
+ * @author Mark Davis
+ * @author Michael Kay: Saxon modifications.
+ */
+class UnicodeDataGenerator {
+ static final String copyright = "Copyright � 1998-1999 Unicode, Inc.";
+
+ /**
+ * Testing flags
+ */
+
+ private static final boolean DEBUG = false;
+
+ /**
+ * Constants for the data file version to use.
+ */
+// static final boolean NEW_VERSION = true;
+ private static String dir;
+
+ private static String UNICODE_DATA = "UnicodeData.txt";
+ private static String COMPOSITION_EXCLUSIONS = "CompositionExclusions.txt";
+
+ private static List canonicalClassKeys = new ArrayList(30000);
+ private static List canonicalClassValues = new ArrayList(30000);
+
+ private static List decompositionKeys = new ArrayList(6000);
+ private static List<String> decompositionValues = new ArrayList(6000);
+
+ private static List exclusionList = new ArrayList(200);
+ private static List compatibilityList = new ArrayList(8000);
+
+ private UnicodeDataGenerator() {
+ }
+
+ /**
+ * Called exactly once by NormalizerData to build the static data
+ */
+
+ static void build() {
+ try {
+ readExclusionList();
+ buildDecompositionTables();
+ } catch (java.io.IOException e) {
+ System.err.println("Can't load data file." + e + ", " + e.getMessage());
+ }
+ }
+
+// =============================================================
+// Building Decomposition Tables
+// =============================================================
+
+ /**
+ * Reads exclusion list and stores the data
+ */
+
+ // Modified by MHK: the original code expects the hex character code to be always four hex digits
+ private static void readExclusionList() throws java.io.IOException {
+ if (DEBUG) {
+ System.out.println("Reading Exclusions");
+ }
+ BufferedReader in = new BufferedReader(new FileReader(dir + '/' + COMPOSITION_EXCLUSIONS), 5 * 1024);
+ while (true) {
+
+ // read a line, discarding comments and blank lines
+
+ String line = in.readLine();
+ if (line == null) {
+ break;
+ }
+ int comment = line.indexOf('#'); // strip comments
+ if (comment != -1) {
+ line = line.substring(0, comment);
+ }
+ if (line.length() == 0) {
+ continue;
+ } // ignore blanks
+
+ // store -1 in the excluded table for each character hit
+
+ int z = line.indexOf(' ');
+ if (z < 0) {
+ z = line.length();
+ }
+ int value = Integer.parseInt(line.substring(0, z), 16);
+ exclusionList.add(Integer.valueOf(value));
+
+ }
+ in.close();
+ }
+
+ /**
+ * Builds a decomposition table from a UnicodeData file
+ */
+ private static void buildDecompositionTables()
+ throws java.io.IOException {
+ if (DEBUG) {
+ System.out.println("Reading Unicode Character Database");
+ }
+ BufferedReader in = new BufferedReader(new FileReader(dir + '/' + UNICODE_DATA), 64 * 1024);
+ int value;
+ int counter = 0;
+ while (true) {
+
+ // read a line, discarding comments and blank lines
+
+ String line = in.readLine();
+ if (line == null) {
+ break;
+ }
+ int comment = line.indexOf('#'); // strip comments
+ if (comment != -1) {
+ line = line.substring(0, comment);
+ }
+ if (line.length() == 0) {
+ continue;
+ }
+ if (DEBUG) {
+ counter++;
+ if ((counter & 0xFF) == 0) {
+ System.out.println("At: " + line);
+ }
+ }
+
+ // find the values of the particular fields that we need
+ // Sample line: 00C0;LATIN ...A GRAVE;Lu;0;L;0041 0300;;;;N;LATIN ... GRAVE;;;00E0;
+
+ int start = 0;
+ int end = line.indexOf(';'); // code
+ try {
+ value = Integer.parseInt(line.substring(start, end), 16);
+ } catch (NumberFormatException e) {
+ throw new IllegalStateException("Bad hex value in line:\n" + line);
+ }
+ if (true && value == '\u00c0') {
+ System.out.println("debug: " + line);
+ }
+ end = line.indexOf(';', end + 1); // name
+ //String name = line.substring(start,end);
+ end = line.indexOf(';', end + 1); // general category
+ end = line.indexOf(';', start = end + 1); // canonical class
+
+ // check consistency: canonical classes must be from 0 to 255
+
+ int cc = Integer.parseInt(line.substring(start, end));
+ if (cc != (cc & 0xFF)) {
+ System.err.println("Bad canonical class at: " + line);
+ }
+ canonicalClassKeys.add(Integer.valueOf(value));
+ canonicalClassValues.add(Integer.valueOf(cc));
+ //canonicalClass.put(value,cc);
+ end = line.indexOf(';', end + 1); // BIDI
+ end = line.indexOf(';', start = end + 1); // decomp
+
+ // decomp requires more processing.
+ // store whether it is canonical or compatibility.
+ // store the decomp in one table, and the reverse mapping (from pairs) in another
+
+ if (start != end) {
+ String segment = line.substring(start, end);
+ boolean compat = segment.charAt(0) == '<';
+ if (compat) {
+ compatibilityList.add(Integer.valueOf(value));
+ //isCompatibility.set(value);
+ }
+ String decomp = fromHex(segment);
+
+ // check consistency: all canon decomps must be singles or pairs!
+
+ if (decomp.length() < 1 || decomp.length() > 2 && !compat) {
+ System.err.println("Bad decomp at: " + line);
+ }
+
+ decompositionKeys.add(Integer.valueOf(value));
+ decompositionValues.add(decomp);
+ //decompose.put(value, decomp);
+
+ // only compositions are canonical pairs
+ // skip if script exclusion
+
+// if (!compat && !isExcluded.get(value)) {
+// char first = '\u0000';
+// char second = decomp.charAt(0);
+// if (decomp.length() > 1) {
+// first = second;
+// second = decomp.charAt(1);
+// }
+//
+// // store composition pair in single integer
+//
+// pair = (first << 16) | second;
+// if (DEBUG && value == '\u00C0') {
+// System.out.println("debug2: " + line);
+// }
+// compose.put(pair, value);
+// } else if (DEBUG) {
+// System.out.println("Excluding: " + decomp);
+// }
+ }
+ }
+ in.close();
+ if (DEBUG) {
+ System.out.println("Done reading Unicode Character Database");
+ }
+
+ // add algorithmic Hangul decompositions
+ // this is more compact if done at runtime, but for simplicity we
+ // do it this way.
+
+// if (DEBUG) System.out.println("Adding Hangul");
+//
+// for (int SIndex = 0; SIndex < SCount; ++SIndex) {
+// int TIndex = SIndex % TCount;
+// char first, second;
+// if (TIndex != 0) { // triple
+// first = (char)(SBase + SIndex - TIndex);
+// second = (char)(TBase + TIndex);
+// } else {
+// first = (char)(LBase + SIndex / NCount);
+// second = (char)(VBase + (SIndex % NCount) / TCount);
+// }
+// pair = (first << 16) | second;
+// value = SIndex + SBase;
+// decompose.put(value, String.valueOf(first) + second);
+// compose.put(pair, value);
+// }
+// if (DEBUG) System.out.println("Done adding Hangul");
+ }
+
+ /**
+ * Hangul composition constants
+ */
+// static final int
+// SBase = 0xAC00, LBase = 0x1100, VBase = 0x1161, TBase = 0x11A7,
+// LCount = 19, VCount = 21, TCount = 28,
+// NCount = VCount * TCount, // 588
+// SCount = LCount * NCount; // 11172
+
+ /**
+ * Utility: Parses a sequence of hex Unicode characters separated by spaces
+ */
+
+ // Modified by MHK. Original code assumed the characters were each 4 hex digits!
+ public static String fromHex(String source) {
+ FastStringBuffer result = new FastStringBuffer(8);
+ for (int i = 0; i < source.length(); ++i) {
+ char c = source.charAt(i);
+ switch (c) {
+ case ' ':
+ break; // ignore
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ int z = source.indexOf(' ', i);
+ if (z < 0) {
+ z = source.length();
+ }
+ try {
+ result.append((char) Integer.parseInt(source.substring(i, z), 16));
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Bad hex value in " + source);
+ }
+ i = z; // skip rest of number
+ break;
+ case '<':
+ int j = source.indexOf('>', i); // skip <...>
+ if (j > 0) {
+ i = j;
+ break;
+ } // else fall through--error
+ default:
+ throw new IllegalArgumentException("Bad hex value in " + source);
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Utility: Supplies a zero-padded hex representation of a Unicode character (without 0x, \\u)
+ */
+ public static String hex(char i) {
+ String result = Integer.toString(i, 16).toUpperCase();
+ return "0000".substring(result.length(), 4) + result;
+ }
+
+ /**
+ * Utility: Supplies a zero-padded hex representation of a Unicode character (without 0x, \\u)
+ */
+ public static String hex(String s, String sep) {
+ FastStringBuffer result = new FastStringBuffer(20);
+ for (int i = 0; i < s.length(); ++i) {
+ if (i != 0) {
+ result.append(sep);
+ }
+ result.append(hex(s.charAt(i)));
+ }
+ return result.toString();
+ }
+
+ /**
+ * Generate the Java output from the data structure
+ */
+
+ private static void generateJava(PrintStream o) {
+ o.println("package net.sf.saxon.serialize.codenorm;");
+ o.println("");
+ o.println("//This module was generated by running net.sf.saxon.serialize.codenorm.UnicodeDataGenerator");
+ o.println("//*** DO NOT EDIT! ***");
+ o.println("//The strange format of this file is carefully chosen to avoid breaking Java compiler limits");
+ o.println("");
+ o.println("public class UnicodeData {");
+
+ // Output the canonical class table
+ o.println("public static final String[] canonicalClassKeys = {");
+ printArray(o, canonicalClassKeys.iterator());
+ o.println("};");
+ o.println("public static final String[] canonicalClassValues = {");
+ printArray(o, canonicalClassValues.iterator());
+ o.println("};");
+
+ // Output the decomposition values (not including Hangul algorithmic decompositions)
+ o.println("public static final String[] decompositionKeys = {");
+ printArray(o, decompositionKeys.iterator());
+ o.println("};");
+ o.println("public static final String[] decompositionValues = {");
+ printStringArray(o, decompositionValues.iterator());
+ o.println("};");
+
+ // Output the composition exclusions
+ o.println("public static final String[] exclusionList = {");
+ printArray(o, exclusionList.iterator());
+ o.println("};");
+
+ // Output the compatibility list
+ o.println("public static final String[] compatibilityList = {");
+ printArray(o, compatibilityList.iterator());
+ o.println("};");
+
+ o.println("}");
+
+ }
+
+ /**
+ * Generate the XML output from the data structure
+ */
+
+ private static void generateXML(PrintStream o) throws XPathException, XMLStreamException {
+ Configuration config = new Configuration();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ Result result = new StreamResult(o);
+ Properties props = new Properties();
+ Receiver receiver = config.getSerializerFactory().getReceiver(result, pipe, props);
+ XMLStreamWriter w = new StreamWriterToReceiver(receiver);
+ w.writeStartDocument();
+ w.writeStartElement("UnicodeData");
+ w.writeAttribute("version", "6.0.0");
+ w.writeStartElement("CanonicalClassKeys");
+ w.writeAttribute("format", "base32chars");
+ w.writeCharacters(base32array(canonicalClassKeys));
+ w.writeEndElement();
+ w.writeStartElement("CanonicalClassValues");
+ w.writeAttribute("format", "base32chars,runLength");
+ w.writeCharacters(base32arrayRunLength(canonicalClassValues));
+ w.writeEndElement();
+ w.writeStartElement("DecompositionKeys");
+ w.writeAttribute("format", "base32chars");
+ w.writeCharacters(base32array(decompositionKeys));
+ w.writeEndElement();
+ w.writeStartElement("DecompositionValues");
+ w.writeAttribute("format", "UCS16Strings,base16");
+ w.writeCharacters(base32StringArray(decompositionValues));
+ w.writeEndElement();
+ w.writeStartElement("ExclusionList");
+ w.writeAttribute("format", "base32chars");
+ w.writeCharacters(base32array(exclusionList));
+ w.writeEndElement();
+ w.writeStartElement("CompatibilityList");
+ w.writeAttribute("format", "base32chars");
+ w.writeCharacters(base32array(compatibilityList));
+ w.writeEndElement();
+ w.writeEndElement();
+ w.writeEndDocument();
+ w.close();
+ }
+
+
+ /**
+ * Output an array of integer values
+ */
+
+ private static void printArray(PrintStream o, Iterator iter) {
+ int count = 0;
+ FastStringBuffer buff = new FastStringBuffer(128);
+ if (!iter.hasNext()) {
+ return;
+ }
+ buff.append('"');
+ while (true) {
+ if (++count == 20) {
+ count = 0;
+ buff.append("\",");
+ o.println(buff.toString());
+ buff.setLength(0);
+ buff.append('"');
+ }
+ int next = ((Integer) iter.next()).intValue();
+ buff.append(Integer.toString(next, 32)); // values are written in base-32 notation
+ if (iter.hasNext()) {
+ buff.append(",");
+ } else {
+ buff.append("\"");
+ o.println(buff.toString());
+ return;
+ }
+ }
+ }
+
+ private static String base32array(List<Integer> list) {
+ int count = 0;
+ Iterator<Integer> iter = list.iterator();
+ FastStringBuffer buff = new FastStringBuffer(128);
+ if (!iter.hasNext()) {
+ return buff.toString();
+ }
+ while (true) {
+ if (++count == 20) {
+ count = 0;
+ buff.append("\n");
+ }
+ int next = iter.next().intValue();
+ buff.append(Integer.toString(next, 32)); // values are written in base-32 notation
+ if (iter.hasNext()) {
+ buff.append(" ");
+ } else {
+ buff.append("\n");
+ return buff.toString();
+ }
+ }
+ }
+
+ private static String base32arrayRunLength(List<Integer> list) {
+ int count = 0;
+ Iterator<Integer> iter = list.iterator();
+ FastStringBuffer buff = new FastStringBuffer(128);
+ if (!iter.hasNext()) {
+ return buff.toString();
+ }
+ int runLength = 1;
+ int val = iter.next().intValue();
+ int next;
+ do {
+ while (true) {
+ if (iter.hasNext()) {
+ next = iter.next().intValue();
+ if (next == val) {
+ runLength++;
+ } else {
+ break;
+ }
+ } else {
+ next = -1;
+ break;
+ }
+ }
+ if (runLength != 1) {
+ buff.append(Integer.toString(runLength));
+ buff.append("*");
+ }
+ buff.append(Integer.toString(val, 32)); // values are written in base-32 notation
+ if (++count == 20) {
+ count = 0;
+ buff.append("\n");
+ } else {
+ buff.append(" ");
+ }
+ val = next;
+ runLength = 1;
+ } while (val != -1);
+
+ buff.append("\n");
+ return buff.toString();
+ }
+
+ /**
+ * Output an array of string values (using backslash-uuuu notation where appropriate)
+ */
+
+ private static String base32StringArray(List<String> in) {
+ Iterator<String> iter = in.iterator();
+ int count = 0;
+ FastStringBuffer buff = new FastStringBuffer(128);
+ if (!iter.hasNext()) {
+ return "";
+ }
+ while (true) {
+ if (++count == 20) {
+ count = 0;
+ buff.append("\n");
+ }
+ String value = iter.next();
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ char b0 = "0123456789abcdef".charAt(c & 0xf);
+ char b1 = "0123456789abcdef".charAt((c >> 4) & 0xf);
+ char b2 = "0123456789abcdef".charAt((c >> 8) & 0xf);
+ char b3 = "0123456789abcdef".charAt((c >> 12) & 0xf);
+ buff.append(b3);
+ buff.append(b2);
+ buff.append(b1);
+ buff.append(b0);
+ }
+ if (iter.hasNext()) {
+ buff.append(" ");
+ } else {
+ return buff.toString();
+ }
+ }
+ }
+
+ private static void printStringArray(PrintStream o, Iterator iter) {
+ int count = 0;
+ FastStringBuffer buff = new FastStringBuffer(128);
+ if (!iter.hasNext()) {
+ return;
+ }
+ while (true) {
+ if (++count == 20) {
+ count = 0;
+ o.println(buff.toString());
+ buff.setLength(0);
+ }
+ String next = (String) iter.next();
+ appendJavaString(next, buff);
+ if (iter.hasNext()) {
+ buff.append(", ");
+ } else {
+ o.println(buff.toString());
+ return;
+ }
+ }
+ }
+
+
+ private static void appendJavaString(String value, FastStringBuffer buff) {
+ buff.append('"');
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ if (c == '\\') {
+ buff.append("\\\\");
+ } else if (c == '"') {
+ buff.append("\\\"");
+ } else if (c > 32 && c < 127) {
+ buff.append(c);
+ } else {
+ buff.append("\\u");
+ char b0 = "0123456789abcdef".charAt(c & 0xf);
+ char b1 = "0123456789abcdef".charAt((c >> 4) & 0xf);
+ char b2 = "0123456789abcdef".charAt((c >> 8) & 0xf);
+ char b3 = "0123456789abcdef".charAt((c >> 12) & 0xf);
+ buff.append(b3);
+ buff.append(b2);
+ buff.append(b1);
+ buff.append(b0);
+ }
+ }
+ buff.append('"');
+ }
+
+ /**
+ * Main program. Run this program to regenerate the Java module UnicodeData.java against revised data
+ * from the Unicode character database.
+ * <p/>
+ * Usage: java UnicodeDataGenerator dir >UnicodeData.java
+ * <p/>
+ * where dir is the directory containing the files UnicodeData.text and CompositionExclusions.txt from the
+ * Unicode character database.
+ */
+
+ public static void main(/*@NotNull*/ String[] args) throws Exception {
+ if (args.length != 2) {
+ System.err.println("Usage: java UnicodeDataGenerator dir UnicodeData.java");
+ System.err.println("where dir is the directory containing the files UnicodeData.text and" +
+ " CompositionExclusions.txt from the Unicode character database");
+ }
+ dir = args[0];
+ build();
+ PrintStream o = new PrintStream(new FileOutputStream(new File(args[1])));
+ //generateJava(o);
+ generateXML(o);
+ }
+}
+
+// * The class is derived from the sample program NormalizerData.java published by the
+// * Unicode consortium. That code has been modified so that instead of building the run-time
+// * data structures directly, they are written to a Java "source" module, which is then
+// * compiled. Also, the ability to construct a condensed version of the data tables has been
+// * removed.
diff --git a/sf/saxon/serialize/codenorm/UnicodeDataParserFromXML.java b/sf/saxon/serialize/codenorm/UnicodeDataParserFromXML.java
new file mode 100644
index 0000000..d8a06a6
--- /dev/null
+++ b/sf/saxon/serialize/codenorm/UnicodeDataParserFromXML.java
@@ -0,0 +1,253 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.serialize.codenorm;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntToIntHashMap;
+import net.sf.saxon.z.IntToIntMap;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+
+import javax.xml.transform.stream.StreamSource;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * This class reads the data compiled into class UnicodeData, and builds hash tables
+ * that can be used by the Unicode normalization routines. This operation is performed
+ * once only, the first time normalization is attempted after Saxon is loaded.
+ */
+
+class UnicodeDataParserFromXML {
+
+ // This class is never instantiated
+ private UnicodeDataParserFromXML(){}
+
+ /**
+ * Called exactly once by NormalizerData to build the static data
+ */
+
+ static NormalizerData build(Configuration config) throws XPathException {
+
+ InputStream in = Configuration.locateResource("normalizationData.xml", new ArrayList(), new ArrayList());
+ if (in == null) {
+ throw new XPathException("Unable to read normalizationData.xml file");
+ }
+
+ BitSet isExcluded = new BitSet(128000);
+ BitSet isCompatibility = new BitSet(128000);
+
+ ParseOptions options = new ParseOptions();
+ options.setSchemaValidationMode(Validation.SKIP);
+ DocumentInfo doc = config.buildDocument(new StreamSource(in, "normalizationData.xml"), options);
+ NodeInfo canonicalClassKeys = null;
+ NodeInfo canonicalClassValues = null;
+ NodeInfo decompositionKeys = null;
+ NodeInfo decompositionValues = null;
+
+ AxisIterator iter = doc.iterateAxis(AxisInfo.DESCENDANT, NodeKindTest.ELEMENT);
+ while (true) {
+ NodeInfo item = (NodeInfo)iter.next();
+ if (item == null) {
+ break;
+ }
+ if (item.getLocalPart().equals("CanonicalClassKeys")) {
+ canonicalClassKeys = item;
+ } else if (item.getLocalPart().equals("CanonicalClassValues")) {
+ canonicalClassValues = item;
+ } else if (item.getLocalPart().equals("DecompositionKeys")) {
+ decompositionKeys = item;
+ } else if (item.getLocalPart().equals("DecompositionValues")) {
+ decompositionValues = item;
+ } else if (item.getLocalPart().equals("ExclusionList")) {
+ readExclusionList(item.getStringValue(), isExcluded);
+ } else if (item.getLocalPart().equals("CompatibilityList")) {
+ readCompatibilityList(item.getStringValue(), isCompatibility);
+ }
+ }
+
+ IntToIntMap canonicalClass = new IntToIntHashMap(400);
+ canonicalClass.setDefaultValue(0);
+ readCanonicalClassTable(canonicalClassKeys.getStringValue(), canonicalClassValues.getStringValue(), canonicalClass);
+
+
+ IntHashMap decompose = new IntHashMap(18000);
+ IntToIntMap compose = new IntToIntHashMap(15000);
+ compose.setDefaultValue(NormalizerData.NOT_COMPOSITE);
+
+ readDecompositionTable(decompositionKeys.getStringValue(), decompositionValues.getStringValue(),
+ decompose, compose, isExcluded, isCompatibility);
+
+ return new NormalizerData(canonicalClass, decompose, compose,
+ isCompatibility, isExcluded);
+ }
+
+ /**
+ * Reads exclusion list and stores the data
+ */
+
+ private static void readExclusionList(String s, BitSet isExcluded) {
+ StringTokenizer st = new StringTokenizer(s);
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ int value = Integer.parseInt(tok, 32);
+ isExcluded.set(value);
+ }
+ }
+
+ /**
+ * Reads compatibility list and stores the data
+ */
+
+ private static void readCompatibilityList(String s, BitSet isCompatible) {
+ StringTokenizer st = new StringTokenizer(s);
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ int value = Integer.parseInt(tok, 32);
+ isCompatible.set(value);
+ }
+ }
+
+ /**
+ * Read canonical class table (mapping from character codes to their canonical class)
+ */
+
+ private static void readCanonicalClassTable(String keyString, String valueString, IntToIntMap canonicalClasses) {
+ ArrayList keys = new ArrayList(5000);
+
+ StringTokenizer st = new StringTokenizer(keyString);
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ int value = Integer.parseInt(tok, 32);
+ keys.add(Integer.valueOf(value));
+ }
+
+ int k = 0;
+ st = new StringTokenizer(valueString);
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ int clss;
+ int repeat = 0;
+ int star = tok.indexOf('*');
+ if (star < 0) {
+ clss = Integer.parseInt(tok, 32);
+ } else {
+ repeat = Integer.parseInt(tok.substring(0, star));
+ clss = Integer.parseInt(tok.substring(star+1), 32);
+ }
+ for (int i=0; i<repeat; i++) {
+ canonicalClasses.put(((Integer)keys.get(k++)).intValue(), clss);
+ }
+ }
+ }
+
+ /**
+ * Read canonical class table (mapping from character codes to their canonical class)
+ */
+
+ private static void readDecompositionTable(
+ String decompositionKeyString, String decompositionValuesString,
+ IntHashMap decompose, IntToIntMap compose,
+ BitSet isExcluded, /*@NotNull*/ BitSet isCompatibility) {
+ int k = 0;
+
+ List<String> values = new ArrayList<String>(1000);
+ StringTokenizer st = new StringTokenizer(decompositionValuesString);
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ String value = "";
+ for (int c=0; c<tok.length();) {
+ char h0 = tok.charAt(c++);
+ char h1 = tok.charAt(c++);
+ char h2 = tok.charAt(c++);
+ char h3 = tok.charAt(c++);
+ int code = ("0123456789abcdef".indexOf(h0)<<12) +
+ ("0123456789abcdef".indexOf(h1)<<8) +
+ ("0123456789abcdef".indexOf(h2)<<4) +
+ ("0123456789abcdef".indexOf(h3));
+ value += (char)code;
+ }
+ values.add(value);
+ }
+
+
+ st = new StringTokenizer(decompositionKeyString);
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ int key = Integer.parseInt(tok, 32);
+ String value = values.get(k++);
+ decompose.put(key, value);
+ // only compositions are canonical pairs
+ // skip if script exclusion
+
+ if (!isCompatibility.get(key) && !isExcluded.get(key)) {
+ char first = '\u0000';
+ char second = value.charAt(0);
+ if (value.length() > 1) {
+ first = second;
+ second = value.charAt(1);
+ }
+
+ // store composition pair in single integer
+
+ int pair = (first << 16) | second;
+ compose.put(pair, key);
+ }
+ }
+
+ // Add algorithmic Hangul decompositions
+ // This fragment code is copied from the normalization code published by Unicode consortium.
+ // See module net.sf.saxon.serialize.codenorm.Normalizer for applicable copyright information.
+
+ for (int SIndex = 0; SIndex < SCount; ++SIndex) {
+ int TIndex = SIndex % TCount;
+ char first, second;
+ if (TIndex != 0) { // triple
+ first = (char)(SBase + SIndex - TIndex);
+ second = (char)(TBase + TIndex);
+ } else {
+ first = (char)(LBase + SIndex / NCount);
+ second = (char)(VBase + (SIndex % NCount) / TCount);
+ }
+ int pair = (first << 16) | second;
+ int key = SIndex + SBase;
+ decompose.put(key, String.valueOf(first) + second);
+ compose.put(pair, key);
+ }
+ }
+
+ /**
+ * Hangul composition constants
+ */
+ private static final int
+ SBase = 0xAC00, LBase = 0x1100, VBase = 0x1161, TBase = 0x11A7,
+ LCount = 19, VCount = 21, TCount = 28,
+ NCount = VCount * TCount, // 588
+ SCount = LCount * NCount; // 11172
+
+ // end of Unicode consortium code
+
+}
+
+// * Copyright (c) 1991-2005 Unicode, Inc.
+// * For terms of use, see http://www.unicode.org/terms_of_use.html
+// * For documentation, see UAX#15.<br>
+// * The Unicode Consortium makes no expressed or implied warranty of any
+// * kind, and assumes no liability for errors or omissions.
+// * No liability is assumed for incidental and consequential damages
+// * in connection with or arising out of the use of the information here.
\ No newline at end of file
diff --git a/sf/saxon/serialize/codenorm/package.html b/sf/saxon/serialize/codenorm/package.html
new file mode 100644
index 0000000..e1be533
--- /dev/null
+++ b/sf/saxon/serialize/codenorm/package.html
@@ -0,0 +1,23 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.serialize.charnorm</title>
+
+</head>
+ <body>
+ <p>This package contains the code to implement Unicode normalization.</p>
+
+ <p>Much of the code is derived directly from the reference implementation
+ published by the Unicode Consortium. However, the code for constructing the data
+ tables used as input to the algorithm has been redesigned and rewritten for efficiency.</p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/serialize/package.html b/sf/saxon/serialize/package.html
new file mode 100644
index 0000000..86cb285
--- /dev/null
+++ b/sf/saxon/serialize/package.html
@@ -0,0 +1,57 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.serialize</title>
+</head>
+
+<body>
+
+<p>This package contains code for serializing trees using the standard W3C-defined serialization methods
+(xml, html, text). Additional Saxon-specific serialization methods are in package {@link com.saxonica.serialize}.</p>
+
+<p>Serialization in Saxon operates as a push-based event pipeline in which the components of the pipeline
+implement the {@link net.sf.saxon.event.Receiver} interface.
+ This defines a class that accepts a stream of events, with one method
+defined for each kind of event. The events are modelled on the design of SAX, but adapted
+to the XPath data model and to the use of Saxon's NamePool. Attributes and namespaces are
+notified individually <i>after</i> the start of the relevant element.</p>
+
+<p>The pipeline for serialization is assembled by the {@link net.sf.saxon.lib.SerializerFactory} based
+on a supplied set of serialization parameters. Only those components needed for the chosen serialization
+parameters are included in the pipeline; for example, a Unicode normalizer is inserted at the appropriate
+place in the pipeline if Unicode normalization is requested in the serialization parameters.</p>
+
+<p>The immediate output of node constructors in a query or stylesheet goes to a {@link net.sf.saxon.event.SequenceReceiver}.
+This is a subclass of <code>Receiver</code> that can handle an arbitrary sequence, containing atomic values
+as well as nodes. When constructing the content of an element, a {@link net.sf.saxon.event.ComplexContentOutputter} is used;
+when constructing the content of a node such as a text node or attribute, a <code>SequenceOutputter</code>
+is used instead.</p>
+
+<p>The final serialization classes are subclasses of {@link net.sf.saxon.serialize.Emitter}, but much of the serialization work
+(such as indentation or application of character maps) is done by other classes on the pipeline. These
+are generally constructed by extending the {@link net.sf.saxon.event.ProxyReceiver} class.</p>
+
+<p>The Emitter is an abstract implementation of the Receiver interface. As well as supporting
+the Receiver interface, it provides methods for controlling the destination of serialized output
+(a Writer or OutputStream) and for setting serialization properties (in a Properties object).
+In practice nearly all the implementations of Receiver are currently subclasses of Emitter,
+but this may change in the future.</p>
+
+<p>The package includes emitters for the standard output methods xml, html, and text, and
+proxy emitters to allow a sequence of filters to be applied to the output.</p>,
+
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+30 July 2010</i></p>
+</body>
+</html>
diff --git a/sf/saxon/stax/ReceiverToXMLStreamWriter.java b/sf/saxon/stax/ReceiverToXMLStreamWriter.java
new file mode 100644
index 0000000..7bcfd53
--- /dev/null
+++ b/sf/saxon/stax/ReceiverToXMLStreamWriter.java
@@ -0,0 +1,167 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.stax;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+/**
+ * ReceiverToXMLStreamWriter is a Receiver writes XML by using the XMLStreamWriter
+ */
+public class ReceiverToXMLStreamWriter implements Receiver {
+
+ protected PipelineConfiguration pipe;
+ protected Configuration config;
+ protected String systemId;
+ protected String baseURI;
+ private XMLStreamWriter writer;
+
+ public ReceiverToXMLStreamWriter(XMLStreamWriter writer) {
+ this.writer = writer;
+ }
+
+ public void setPipelineConfiguration(PipelineConfiguration pipe) {
+ this.pipe=pipe;
+ config = pipe.getConfiguration();
+ }
+
+ public PipelineConfiguration getPipelineConfiguration() {
+ return pipe;
+ }
+
+ public void setSystemId(String systemId) {
+ this.systemId =systemId;
+ }
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ public void open() throws XPathException {
+ }
+
+ public void startDocument(int properties) throws XPathException {
+ try {
+ writer.writeStartDocument();
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public void endDocument() throws XPathException {
+ try {
+ writer.writeEndDocument();
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public void setUnparsedEntity(String name, String systemID, String publicID) throws XPathException {
+
+ }
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ String local = elemName.getLocalPart();
+ String uri = elemName.getURI();
+ String prefix = elemName.getPrefix();
+ try {
+ if(prefix.equals("") && uri.equals("")){
+ writer.writeStartElement(local);
+ } else if(prefix.equals("") && !uri.equals("")) {
+ writer.writeStartElement(prefix, local, uri);
+ } else {
+ writer.writeStartElement(prefix, local, uri);
+ }
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+
+ }
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ try {
+ writer.writeNamespace(namespaceBinding.getPrefix(), namespaceBinding.getURI());
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ String local = attName.getLocalPart();
+ String uri = attName.getURI();
+ String prefix = attName.getPrefix();
+ try {
+ if(prefix.equals("") && uri.equals("")) {
+ writer.writeAttribute(local, value.toString());
+ } else if(prefix.equals("") & !uri.equals("")){
+ writer.writeAttribute(uri, local, value.toString());
+ } else {
+ writer.writeAttribute(prefix, uri, local, value.toString());
+ }
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public void startContent() throws XPathException {
+ }
+
+ public void endElement() throws XPathException {
+ try {
+ writer.writeEndElement();
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ try {
+ writer.writeCharacters(chars.toString());
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public void processingInstruction(String name, CharSequence data, int locationId, int properties) throws XPathException {
+ try {
+ writer.writeProcessingInstruction(name, data.toString());
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public void comment(CharSequence content, int locationId, int properties) throws XPathException {
+ try {
+ writer.writeComment(content.toString());
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public void close() throws XPathException {
+ try {
+ writer.close();
+ } catch (XMLStreamException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public boolean usesTypeAnnotations() {
+ return false;
+ }
+}
+
diff --git a/sf/saxon/stax/StAXResultHandlerImpl.java b/sf/saxon/stax/StAXResultHandlerImpl.java
new file mode 100644
index 0000000..3ae0822
--- /dev/null
+++ b/sf/saxon/stax/StAXResultHandlerImpl.java
@@ -0,0 +1,31 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.stax;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.StAXResultHandler;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.stax.StAXResult;
+import java.util.Properties;
+
+/**
+ * StAxResultHandler is a helper class
+ */
+public class StAXResultHandlerImpl implements StAXResultHandler {
+
+ public Receiver getReceiver(Result result, Properties properties) {
+ if(((StAXResult)result).getXMLStreamWriter() != null) {
+ return new ReceiverToXMLStreamWriter(((StAXResult) result).getXMLStreamWriter());
+
+ } else if(((StAXResult)result).getXMLEventWriter() != null) {} {
+ throw new UnsupportedOperationException("XMLEventWriter currently not supported in saxon");
+ }
+ }
+}
+
diff --git a/sf/saxon/stax/XMLStreamWriterDestination.java b/sf/saxon/stax/XMLStreamWriterDestination.java
new file mode 100644
index 0000000..3e73811
--- /dev/null
+++ b/sf/saxon/stax/XMLStreamWriterDestination.java
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.stax;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.s9api.Destination;
+import net.sf.saxon.s9api.SaxonApiException;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+
+/**
+ * StAxResultHandler is a helper class
+ */
+public class XMLStreamWriterDestination implements Destination{
+
+ private XMLStreamWriter writer;
+
+ public XMLStreamWriterDestination(XMLStreamWriter writer) {
+ this.writer = writer;
+ }
+
+ public Receiver getReceiver(Configuration config) throws SaxonApiException {
+ return new ReceiverToXMLStreamWriter(writer);
+ }
+
+ public void close() throws SaxonApiException {
+ try {
+ writer.close();
+ } catch (XMLStreamException e) {
+ throw new SaxonApiException(e);
+ }
+ }
+}
diff --git a/sf/saxon/style/AbsentExtensionElement.java b/sf/saxon/style/AbsentExtensionElement.java
new file mode 100644
index 0000000..6e720d6
--- /dev/null
+++ b/sf/saxon/style/AbsentExtensionElement.java
@@ -0,0 +1,78 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* This element is a surrogate for an extension element (or indeed an xsl element)
+* for which no implementation is available.
+*/
+
+public class AbsentExtensionElement extends StyleElement {
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ /**
+ * Process the attributes of this element and all its children
+ */
+
+ public void processAllAttributes() throws XPathException {
+ if (isTopLevel() && forwardsCompatibleModeIsEnabled()) {
+ // do nothing
+ } else {
+ super.processAllAttributes();
+ }
+ }
+
+ public void prepareAttributes() throws XPathException {
+ }
+
+ /**
+ * Recursive walk through the stylesheet to validate all nodes
+ * @param decl
+ */
+
+ public void validateSubtree(Declaration decl) throws XPathException {
+ if (isTopLevel() && forwardsCompatibleModeIsEnabled()) {
+ // do nothing
+ } else {
+ super.validateSubtree(decl);
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ }
+
+ /*@Nullable*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+
+ if (isTopLevel()) {
+ return null;
+ }
+
+ // if there are fallback children, compile the code for the fallback elements
+
+ if (validationError==null) {
+ validationError = new XPathException("Unknown instruction");
+ }
+ return fallbackProcessing(exec, decl, this);
+ }
+}
+
diff --git a/sf/saxon/style/AttributeValueTemplate.java b/sf/saxon/style/AttributeValueTemplate.java
new file mode 100644
index 0000000..003d988
--- /dev/null
+++ b/sf/saxon/style/AttributeValueTemplate.java
@@ -0,0 +1,198 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionParser;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.functions.Concat;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* This class represents an attribute value template. The class allows an AVT to be parsed, and
+* can construct an Expression that returns the effective value of the AVT.
+*
+* This is an abstract class that is never instantiated, it contains static methods only.
+*/
+
+public abstract class AttributeValueTemplate {
+
+ private AttributeValueTemplate() {}
+
+
+ /**
+ * Static factory method to create an AVT from an XSLT string representation.
+ */
+
+ public static Expression make(String avt,
+ int lineNumber,
+ ExpressionContext env) throws XPathException {
+
+ List<Expression> components = new ArrayList<Expression>(5);
+
+ int i0, i1, i8, i9;
+ int len = avt.length();
+ int last = 0;
+ ExpressionVisitor visitor = ExpressionVisitor.make(env, env.getExecutable());
+ while (last < len) {
+
+ i0 = avt.indexOf("{", last);
+ i1 = avt.indexOf("{{", last);
+ i8 = avt.indexOf("}", last);
+ i9 = avt.indexOf("}}", last);
+
+ if ((i0 < 0 || len < i0) && (i8 < 0 || len < i8)) { // found end of string
+ addStringComponent(components, avt, last, len);
+ break;
+ } else if (i8 >= 0 && (i0 < 0 || i8 < i0)) { // found a "}"
+ if (i8 != i9) { // a "}" that isn't a "}}"
+ XPathException err = new XPathException("Closing curly brace in attribute value template \"" + avt.substring(0, len) + "\" must be doubled");
+ err.setErrorCode("XTSE0370");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ addStringComponent(components, avt, last, i8 + 1);
+ last = i8 + 2;
+ } else if (i1 >= 0 && i1 == i0) { // found a doubled "{{"
+ addStringComponent(components, avt, last, i1 + 1);
+ last = i1 + 2;
+ } else if (i0 >= 0) { // found a single "{"
+ if (i0 > last) {
+ addStringComponent(components, avt, last, i0);
+ }
+ Expression exp;
+ ExpressionParser parser = env.getConfiguration().newExpressionParser("XP", false, env.getXPathLanguageLevel());
+ parser.setDefaultContainer(env.getStyleElement());
+ parser.setLanguage(ExpressionParser.XPATH, env.getXPathLanguageLevel());
+ exp = parser.parse(avt, i0 + 1, Token.RCURLY, lineNumber, env);
+ exp = visitor.simplify(exp);
+ last = parser.getTokenizer().currentTokenStartOffset + 1;
+
+ if (env.getStyleElement() instanceof XSLAnalyzeString && isIntegerOrIntegerPair(exp)) {
+ env.issueWarning("Found {" + showIntegers(exp) + "} in regex attribute: perhaps {{" + showIntegers(exp) + "}} was intended? (The attribute is an AVT, so curly braces should be doubled)", exp);
+ }
+
+ if (env.isInBackwardsCompatibleMode()) {
+ components.add(makeFirstItem(exp, env));
+ } else {
+ components.add(visitor.simplify(
+ XSLLeafNodeConstructor.makeSimpleContentConstructor(
+ exp,
+ new StringLiteral(StringValue.SINGLE_SPACE), env.getConfiguration())));
+ }
+
+ } else {
+ throw new IllegalStateException("Internal error parsing AVT");
+ }
+ }
+
+ // is it empty?
+
+ if (components.size() == 0) {
+ return new StringLiteral(StringValue.EMPTY_STRING);
+ }
+
+ // is it a single component?
+
+ if (components.size() == 1) {
+ return visitor.simplify((Expression) components.get(0));
+ }
+
+ // otherwise, return an expression that concatenates the components
+
+ Expression[] args = new Expression[components.size()];
+ components.toArray(args);
+ Concat fn = (Concat) SystemFunctionCall.makeSystemFunction("concat", args);
+ fn.setLocationId(env.getLocationMap().allocateLocationId(env.getSystemId(), lineNumber));
+ return visitor.simplify(fn);
+
+ }
+
+ /**
+ * Used to detect warning condition when braces are undoubled in the regex attribute of xsl:analyze-string
+ * @param exp an expression
+ * @return true if the expression is an integer literal or a pair of two integer literals
+ */
+
+ private static boolean isIntegerOrIntegerPair(Expression exp) {
+ if (exp instanceof Literal) {
+ GroundedValue val = ((Literal)exp).getValue();
+ if (val instanceof IntegerValue) {
+ return true;
+ }
+ if (val.getLength() == 2) {
+ if (val.itemAt(0) instanceof IntegerValue && val.itemAt(1) instanceof IntegerValue) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Used to report warning condition when braces are undoubled in the regex attribute of xsl:analyze-string
+ * @param exp an expression
+ * @return string representation of an integer literal or a pair of two integer literals
+ */
+
+ private static String showIntegers(Expression exp) {
+ if (exp instanceof Literal) {
+ GroundedValue val = ((Literal)exp).getValue();
+ if (val instanceof IntegerValue) {
+ return val.toString();
+ }
+ if (val.getLength() == 2) {
+ if (val.itemAt(0) instanceof IntegerValue && val.itemAt(1) instanceof IntegerValue) {
+ return val.itemAt(0).toString() + "," + val.itemAt(1).toString();
+ }
+ }
+ }
+ return "";
+ }
+
+ private static void addStringComponent(List<Expression> components, String avt, int start, int end) {
+ if (start < end) {
+ components.add(new StringLiteral(avt.substring(start, end)));
+ }
+ }
+
+ /**
+ * Make an expression that extracts the first item of a sequence, after atomization
+ */
+
+ /*@NotNull*/ public static Expression makeFirstItem(Expression exp, StaticContext env) {
+ if(Literal.isEmptySequence(exp)){
+ return exp;
+ }
+ final TypeHierarchy th = env.getConfiguration().getTypeHierarchy();
+ if (!exp.getItemType(th).isPlainType()) {
+ exp = Atomizer.makeAtomizer(exp);
+ }
+ if (Cardinality.allowsMany(exp.getCardinality())) {
+ exp = FirstItemExpression.makeFirstItemExpression(exp);
+ }
+ if (!th.isSubType(exp.getItemType(th), BuiltInAtomicType.STRING)) {
+ exp = new AtomicSequenceConverter(exp, BuiltInAtomicType.STRING);
+ ((AtomicSequenceConverter)exp).allocateConverter(env.getConfiguration(), false);
+ }
+ return exp;
+ }
+
+}
+
diff --git a/sf/saxon/style/CollationDeclaration.java b/sf/saxon/style/CollationDeclaration.java
new file mode 100644
index 0000000..ea6635e
--- /dev/null
+++ b/sf/saxon/style/CollationDeclaration.java
@@ -0,0 +1,27 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.lib.StringCollator;
+
+/**
+ * Interface implemented by SaxonCollation, which is not available in Saxon-HE
+ */
+public interface CollationDeclaration {
+
+ public String getCollationName();
+
+ /**
+ * Get the collator defined by this collation declaration
+ * @return the StringCollator
+ */
+
+ public StringCollator getCollator();
+
+}
+
diff --git a/sf/saxon/style/DataElement.java b/sf/saxon/style/DataElement.java
new file mode 100644
index 0000000..f19463f
--- /dev/null
+++ b/sf/saxon/style/DataElement.java
@@ -0,0 +1,21 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+import net.sf.saxon.tree.linked.ElementImpl;
+
+
+/**
+* This element represents a top-level element in a user-defined namespace,
+* or a child/descendant of such an element. It serves no useful purpose.
+*/
+
+public class DataElement extends ElementImpl {
+
+
+}
+
diff --git a/sf/saxon/style/Declaration.java b/sf/saxon/style/Declaration.java
new file mode 100644
index 0000000..8cb9d36
--- /dev/null
+++ b/sf/saxon/style/Declaration.java
@@ -0,0 +1,39 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+/**
+ * The object represents a declaration (that is, a top-level element) in a stylesheet.
+ * A declaration exists within a stylesheet module and takes its import precedence
+ * from that of the module. The declaration corresponds to a source element in a stylesheet
+ * document. However, if a stylesheet module is imported twice with different precedences,
+ * then two declarations may share the same source element.
+ */
+
+public class Declaration {
+
+ private StyleElement sourceElement;
+ private StylesheetModule module;
+
+ public Declaration(StylesheetModule module, StyleElement source) {
+ this.module = module;
+ this.sourceElement = source;
+ }
+
+ public StylesheetModule getModule() {
+ return module;
+ }
+
+ public StyleElement getSourceElement() {
+ return sourceElement;
+ }
+
+ public int getPrecedence() {
+ return module.getPrecedence();
+ }
+}
diff --git a/sf/saxon/style/ExpressionContext.java b/sf/saxon/style/ExpressionContext.java
new file mode 100644
index 0000000..7911394
--- /dev/null
+++ b/sf/saxon/style/ExpressionContext.java
@@ -0,0 +1,422 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.GlobalVariable;
+import net.sf.saxon.expr.instruct.LocationMap;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.functions.IntegratedFunctionLibrary;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.trans.DecimalFormatManager;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.DecimalValue;
+
+import javax.xml.transform.SourceLocator;
+import java.util.Set;
+
+/**
+* An ExpressionContext represents the context for an XPath expression written
+* in the stylesheet.
+*/
+
+public class ExpressionContext implements XSLTStaticContext {
+
+ private StyleElement element;
+ private NamePool namePool;
+ /*@Nullable*/ private NamespaceResolver namespaceResolver = null;
+
+ /**
+ * Create a static context for XPath expressions in an XSLT stylesheet
+ * @param styleElement the element on which the XPath expression appears
+ */
+
+ public ExpressionContext(StyleElement styleElement) {
+ element = styleElement;
+ namePool = styleElement.getNamePool();
+ }
+
+ /**
+ * Get the system configuration
+ */
+
+ public Configuration getConfiguration() {
+ return element.getConfiguration();
+ }
+
+ /**
+ * Get the executable
+ * @return the executable
+ */
+
+ public Executable getExecutable() {
+ return element.getPreparedStylesheet();
+ }
+
+ /**
+ * Ask whether expressions compiled under this static context are schema-aware.
+ * They must be schema-aware if the expression is to handle typed (validated) nodes
+ *
+ * @return true if expressions are schema-aware
+ */
+ public boolean isSchemaAware() {
+ return getExecutable().isSchemaAware();
+ }
+
+ /**
+ * Construct a dynamic context for early evaluation of constant subexpressions
+ */
+
+ public XPathContext makeEarlyEvaluationContext() {
+ return new EarlyEvaluationContext(getConfiguration(),
+ element.getPrincipalStylesheetModule().getCollationMap());
+ }
+
+
+ /**
+ * Get the location map
+ */
+
+ public LocationMap getLocationMap() {
+ return getExecutable().getLocationMap();
+ }
+
+ /**
+ * Issue a compile-time warning
+ */
+
+ public void issueWarning(String s, SourceLocator locator) {
+ element.issueWarning(s, locator);
+ }
+
+ /**
+ * Get the NamePool used for compiling expressions
+ */
+
+ public NamePool getNamePool() {
+ return namePool;
+ }
+
+ /**
+ * Get the System ID of the entity containing the expression (used for diagnostics)
+ */
+
+ public String getSystemId() {
+ return element.getSystemId();
+ }
+
+ /**
+ * Get the line number of the expression within its containing entity
+ * Returns -1 if no line number is available
+ */
+
+ public int getLineNumber() {
+ return element.getLineNumber();
+ }
+
+ /**
+ * Get the Base URI of the element containing the expression, for resolving any
+ * relative URI's used in the expression.
+ * Used by the document() function.
+ */
+
+ public String getBaseURI() {
+ return element.getBaseURI();
+ }
+
+ /**
+ * Get the URI for a prefix, using this Element as the context for namespace resolution.
+ * The default namespace will not be used when the prefix is empty.
+ * @param prefix The prefix
+ * @throws XPathException if the prefix is not declared
+ */
+
+ public String getURIForPrefix(String prefix) throws XPathException {
+ String uri = element.getURIForPrefix(prefix, false);
+ if (uri == null) {
+ XPathException err = new XPathException("Undeclared namespace prefix " + Err.wrap(prefix));
+ err.setErrorCode("XPST0081");
+ err.setIsStaticError(true);
+ throw err;
+ }
+ return uri;
+ }
+
+ /**
+ * Get a copy of the NamespaceResolver suitable for saving in the executable code
+ * @return a NamespaceResolver
+ */
+
+
+ public NamespaceResolver getNamespaceResolver() {
+ if (namespaceResolver == null) {
+ namespaceResolver = element.makeNamespaceContext();
+ }
+ return namespaceResolver;
+ }
+
+ /**
+ * Get the required type of the context item. If no type has been explicitly declared for the context
+ * item, an instance of AnyItemType (representing the type item()) is returned.
+ * @return the required type of the context item
+ * @since 9.3
+ */
+
+ public ItemType getRequiredContextItemType() {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Get a DecimalFormatManager to resolve the names of decimal formats used in calls
+ * to the format-number() function.
+ * @return the decimal format manager for this static context, or null if named decimal
+ * formats are not supported in this environment.
+ */
+
+ public DecimalFormatManager getDecimalFormatManager() {
+ return element.getPreparedStylesheet().getDecimalFormatManager();
+ }
+
+ /**
+ * Get a fingerprint for a name, using this as the context for namespace resolution
+ * @param qname The name as written, in the form "[prefix:]localname"
+ * @param useDefault Defines the action when there is no prefix. If true, use
+ * the default namespace URI (as for element names). If false, use no namespace URI
+ * (as for attribute names).
+ * @return -1 if the name is not already present in the name pool
+ */
+
+ public int getFingerprint(String qname, boolean useDefault) throws XPathException {
+
+ String[] parts;
+ try {
+ parts = getConfiguration().getNameChecker().getQNameParts(qname);
+ } catch (QNameException err) {
+ throw new XPathException(err.getMessage());
+ }
+ String prefix = parts[0];
+ if (prefix.length() == 0) {
+ String uri = "";
+
+ if (useDefault) {
+ uri = getURIForPrefix(prefix);
+ }
+
+ return namePool.getFingerprint(uri, qname);
+
+ } else {
+
+ String uri = getURIForPrefix(prefix);
+ return namePool.getFingerprint(uri, parts[1]);
+ }
+ }
+
+ /**
+ * Bind a variable to an object that can be used to refer to it
+ * @param qName the name of the variable
+ * @return a VariableDeclaration object that can be used to identify it in the Bindery,
+ * @throws XPathException if the variable has not been declared
+ */
+
+ public Expression bindVariable(StructuredQName qName) throws XPathException {
+ if (element.getLocalPart().equals("accumulator-rule") && qName.getClarkName().equals("value")) {
+ // XSLT 3.0
+ return new SuppliedParameterReference(0);
+ }
+ SourceBinding xslVariableDeclaration = element.bindVariable(qName);
+ if (xslVariableDeclaration == null) {
+ // it might have been declared in an imported query
+ GlobalVariable var = getExecutable().getGlobalVariable(qName);
+ if (var != null) {
+ return new VariableReference(var);
+ }
+ // it might be an implicit error variable in try/catch
+ if (getXPathLanguageLevel().equals(DecimalValue.THREE) && NamespaceConstant.ERR.equals(qName.getURI())) {
+ AxisIterator catchers = element.iterateAxis(AxisInfo.ANCESTOR_OR_SELF,
+ new NameTest(Type.ELEMENT, StandardNames.XSL_CATCH, getNamePool()));
+ StyleElement catcher = (StyleElement)catchers.next();
+ if (catcher != null) {
+ for (StructuredQName errorVariable : StandardNames.errorVariables) {
+ if (errorVariable.getLocalPart().equals(qName.getLocalPart())) {
+ IntegratedFunctionLibrary lib = getConfiguration().getVendorFunctionLibrary();
+ StructuredQName functionName =
+ new StructuredQName("saxon", NamespaceConstant.SAXON, "dynamic-error-info");
+ Expression[] args = new Expression[]{new StringLiteral(qName.getLocalPart())};
+ return lib.bind(functionName, 1, args, this, element);
+ }
+ }
+ }
+ }
+ XPathException err = new XPathException("Variable " + qName.getDisplayName() +
+ " has not been declared (or its declaration is not in scope)");
+ err.setErrorCode("XPST0008");
+ err.setIsStaticError(true);
+ throw err;
+ }
+
+ VariableReference var;
+ if (xslVariableDeclaration.hasProperty(SourceBinding.GLOBAL)) {
+ var = new VariableReference();
+ } else if (xslVariableDeclaration.hasProperty(SourceBinding.GROUP)) {
+ var = new GroupVariableReference();
+ } else {
+ var = new LocalVariableReference();
+ }
+ xslVariableDeclaration.registerReference(var);
+ return var;
+ }
+
+ /**
+ * Get the function library containing all the in-scope functions available in this static
+ * context
+ */
+
+ public FunctionLibrary getFunctionLibrary() {
+ return element.getPrincipalStylesheetModule().getFunctionLibrary();
+ }
+
+ /**
+ * Determine if an extension element is available
+ * @throws XPathException if the name is invalid or the prefix is not declared
+ */
+
+ public boolean isElementAvailable(String qname) throws XPathException {
+ try {
+ String[] parts = getConfiguration().getNameChecker().getQNameParts(qname);
+ String uri;
+ if (parts[0].length() == 0) {
+ uri = getDefaultElementNamespace();
+ } else {
+ uri = getURIForPrefix(parts[0]);
+ }
+ return element.getPreparedStylesheet().getStyleNodeFactory().isElementAvailable(uri, parts[1]);
+ } catch (QNameException e) {
+ XPathException err = new XPathException("Invalid element name. " + e.getMessage());
+ err.setErrorCode("XTDE1440");
+ throw err;
+ }
+ }
+
+ /**
+ * Get a named collation.
+ * @param name The name of the required collation. Supply null to get the default collation.
+ * @return the collation; or null if the required collation is not found.
+ */
+
+ public StringCollator getCollation(String name) {
+ if (name == null) {
+ return getCollation(getDefaultCollationName());
+ } else {
+ return element.getPrincipalStylesheetModule().findCollation(name, element.getBaseURI());
+ }
+ }
+
+ /**
+ * Get the default collation. Return null if no default collation has been defined
+ */
+
+ public String getDefaultCollationName() {
+ return element.getDefaultCollationName();
+ }
+
+ /**
+ * Get the default XPath namespace for elements and types
+ * Return NamespaceConstant.NULL for the non-namespace
+ */
+
+ public String getDefaultElementNamespace() {
+ return element.getDefaultXPathNamespace();
+ }
+
+ /**
+ * Get the default function namespace
+ */
+
+ public String getDefaultFunctionNamespace() {
+ return NamespaceConstant.FN;
+ }
+
+ /**
+ * Determine whether Backwards Compatible Mode is used
+ */
+
+ public boolean isInBackwardsCompatibleMode() {
+ return element.xPath10ModeIsEnabled();
+ }
+
+ /**
+ * Get the XPath language level supported, as a string.
+ * The current levels supported are 2.0, and 3.0. The XPath language level will be the same as the
+ * XSLT processor version
+ * @return the XPath language level; the return value will be either
+ * {@link net.sf.saxon.value.DecimalValue#TWO} or {@link net.sf.saxon.value.DecimalValue#THREE}
+ * @since 9.3
+ */
+
+ public DecimalValue getXPathLanguageLevel() {
+ return element.getPreparedStylesheet().getStyleNodeFactory().getXsltProcessorVersion();
+ }
+
+ /**
+ * Test whether a schema has been imported for a given namespace
+ * @param namespace the target namespace of the required schema
+ * @return true if a schema for this namespace has been imported
+ */
+
+ public boolean isImportedSchema(String namespace) {
+ return element.getPrincipalStylesheetModule().isImportedSchema(namespace);
+ }
+
+ /**
+ * Get the set of imported schemas
+ * @return a Set, the set of URIs representing the names of imported schemas
+ */
+
+ public Set<String> getImportedSchemaNamespaces() {
+ return element.getPrincipalStylesheetModule().getImportedSchemaTable();
+ }
+
+ /**
+ * Determine whether a built-in type is available in this context. This method caters for differences
+ * between host languages as to which set of types are built in.
+ *
+ * @param type the supposedly built-in type. This will always be a type in the
+ * XS or XDT namespace.
+ * @return true if this type can be used in this static context
+ */
+
+ public boolean isAllowedBuiltInType(BuiltInAtomicType type) {
+ Configuration config = getConfiguration();
+ if (type.getFingerprint() == StandardNames.XS_DATE_TIME_STAMP) {
+ return config.getXsdVersion() == Configuration.XSD11;
+ }
+ return type.isAllowedInBasicXSLT() ||
+ config.isLicensedFeature(Configuration.LicenseFeature.PROFESSIONAL_EDITION);
+ }
+
+ /**
+ * Get the stylesheet element containing this XPath expression
+ * @return the element in the tree representation of the source stylesheet
+ */
+
+ public StyleElement getStyleElement() {
+ return element;
+ }
+}
+
diff --git a/sf/saxon/style/ExtensionInstruction.java b/sf/saxon/style/ExtensionInstruction.java
new file mode 100644
index 0000000..8eb5b05
--- /dev/null
+++ b/sf/saxon/style/ExtensionInstruction.java
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+/**
+* Abstract class representing an extension instruction
+*/
+
+public abstract class ExtensionInstruction extends StyleElement {
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public final boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain an xsl:fallback
+ * instruction. Note that this is only relevant if the element is an instruction.
+ * @return true: this element (assuming it is an instruction) may contain an xsl:fallback
+ * child instruction, which is ignored if the element is recognized.
+ */
+
+ public final boolean mayContainFallback() {
+ return true;
+ }
+
+}
diff --git a/sf/saxon/style/LiteralResultElement.java b/sf/saxon/style/LiteralResultElement.java
new file mode 100644
index 0000000..b8fb0d0
--- /dev/null
+++ b/sf/saxon/style/LiteralResultElement.java
@@ -0,0 +1,506 @@
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.linked.DocumentImpl;
+import net.sf.saxon.tree.linked.LinkedTreeBuilder;
+import net.sf.saxon.tree.util.NamespaceIterator;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Untyped;
+
+import javax.xml.transform.TransformerException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+* This class represents a literal result element in the style sheet
+* (typically an HTML element to be output). <br>
+* It is also used to represent unknown top-level elements, which are ignored.
+*/
+
+public class LiteralResultElement extends StyleElement {
+
+ private int resultNameCode;
+ private NodeName[] attributeNames;
+ private Expression[] attributeValues;
+ private Expression onEmpty;
+ private int numberOfAttributes;
+ private boolean toplevel;
+ private List<NamespaceBinding> namespaceCodes = new ArrayList<NamespaceBinding>();
+ private AttributeSet[] attributeSets;
+ /*@Nullable*/ private SchemaType schemaType = null;
+ private int validation = Validation.STRIP;
+ private boolean inheritNamespaces = true;
+
+ /**
+ * Determine whether this type of element is allowed to contain a sequence constructor
+ * @return true: yes, it may contain a sequence constructor
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ /**
+ * Specify that this is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Process the attribute list
+ */
+
+ public void prepareAttributes() throws XPathException {
+
+ // Process the values of all attributes. At this stage we deal with attribute
+ // values (especially AVTs), but we do not apply namespace aliasing to the
+ // attribute names.
+
+ AttributeCollection atts = getAttributeList();
+ int num = atts.getLength();
+
+ if (num == 0) {
+ numberOfAttributes = 0;
+ } else {
+ NamePool namePool = getNamePool();
+ attributeNames = new NodeName[num];
+ attributeValues = new Expression[num];
+ numberOfAttributes = 0;
+
+ for (int i=0; i<num; i++) {
+
+ int anameCode = atts.getNameCode(i);
+ short attURIcode = namePool.getURICode(anameCode);
+
+ if (attURIcode==NamespaceConstant.XSLT_CODE) {
+ int fp = anameCode & NamePool.FP_MASK;
+
+ if (fp == StandardNames.XSL_USE_ATTRIBUTE_SETS) {
+ // deal with this later
+ } else if (fp == StandardNames.XSL_DEFAULT_COLLATION) {
+ // already dealt with
+ } else if (fp == StandardNames.XSL_EXTENSION_ELEMENT_PREFIXES) {
+ // already dealt with
+ } else if (fp == StandardNames.XSL_EXCLUDE_RESULT_PREFIXES) {
+ // already dealt with
+ } else if (fp == StandardNames.XSL_EXPAND_TEXT) {
+ // already dealt with
+ } else if (fp == StandardNames.XSL_VERSION) {
+ // already dealt with
+ } else if (fp == StandardNames.XSL_XPATH_DEFAULT_NAMESPACE) {
+ // already dealt with
+ } else if (fp == StandardNames.XSL_TYPE) {
+ // deal with this later
+ } else if (fp == StandardNames.XSL_USE_WHEN) {
+ // already dealt with
+ } else if (fp == StandardNames.XSL_VALIDATION) {
+ // deal with this later
+ } else if (fp == StandardNames.XSL_INHERIT_NAMESPACES) {
+ String inheritAtt = atts.getValue(i);
+ if (inheritAtt.equals("yes")) {
+ inheritNamespaces = true;
+ } else if (inheritAtt.equals("no")) {
+ inheritNamespaces = false;
+ } else {
+ compileError("The xsl:inherit-namespaces attribute has permitted values (yes, no)", "XTSE0020");
+ }
+ } else if (fp == StandardNames.XSL_ON_EMPTY) {
+ if (!isXslt30Processor()) {
+ compileError("The 'xsl:on-empty' attribute requires XSLT 3.0");
+ }
+ onEmpty = makeExpression(atts.getValue(i));
+ } else {
+ compileError("Unknown XSL attribute " + namePool.getDisplayName(anameCode), "XTSE0805");
+ }
+ } else {
+ attributeNames[numberOfAttributes] = new FingerprintedQName(atts.getPrefix(i), atts.getURI(i), atts.getLocalName(i), anameCode);
+ Expression exp = makeAttributeValueTemplate(atts.getValue(i));
+ attributeValues[numberOfAttributes] = exp;
+ numberOfAttributes++;
+ }
+ }
+
+ // now shorten the arrays if necessary. This is necessary if there are [xsl:]-prefixed
+ // attributes that weren't copied into the arrays.
+
+ if (numberOfAttributes < attributeNames.length) {
+
+ NodeName[] attributeNames2 = new NodeName[numberOfAttributes];
+ System.arraycopy(attributeNames, 0, attributeNames2, 0, numberOfAttributes);
+ attributeNames = attributeNames2;
+
+ Expression[] attributeValues2 = new Expression[numberOfAttributes];
+ System.arraycopy(attributeValues, 0, attributeValues2, 0, numberOfAttributes);
+ attributeValues = attributeValues2;
+ }
+ }
+ }
+
+ /**
+ * Validate that this node is OK
+ * @param decl
+ */
+
+ public void validate(Declaration decl) throws XPathException {
+
+ toplevel = (getParent() instanceof XSLStylesheet);
+
+ resultNameCode = getNameCode();
+
+ NamePool namePool = getNamePool();
+ String elementURI = namePool.getURI(resultNameCode);
+
+ if (toplevel) {
+ // A top-level element can never be a "real" literal result element,
+ // but this class gets used for unknown elements found at the top level
+
+ if (elementURI.length()==0) {
+ compileError("Top level elements must have a non-null namespace URI", "XTSE0130");
+ }
+ } else {
+
+ // Build the list of output namespace nodes. Note we no longer optimize this list.
+ // See comments in the 9.1 source code for some history of this decision.
+
+ Iterator<NamespaceBinding> inscope = NamespaceIterator.iterateNamespaces(this);
+ while (inscope.hasNext()) {
+ namespaceCodes.add(inscope.next());
+ }
+
+ // Spec bug 5857: if there is no other binding for the default namespace, add an undeclaration
+// String defaultNamespace = getURIForPrefix("", true);
+// if (defaultNamespace.length()==0) {
+// namespaceCodes.add(NamespaceBinding.DEFAULT_UNDECLARATION);
+// }
+
+ // apply any aliases required to create the list of output namespaces
+
+ PrincipalStylesheetModule sheet = getPrincipalStylesheetModule();
+
+ if (sheet.hasNamespaceAliases()) {
+ for (int i=0; i<namespaceCodes.size(); i++) {
+ // System.err.println("Examining namespace " + namespaceCodes[i]);
+ String suri = namespaceCodes.get(i).getURI();
+ NamespaceBinding ncode = sheet.getNamespaceAlias(suri);
+ if (ncode != null && !ncode.getURI().equals(suri)) {
+ // apply the namespace alias. Change in 7.3: use the prefix associated
+ // with the new namespace, not the old prefix.
+ namespaceCodes.set(i, ncode);
+ }
+ }
+
+ // determine if there is an alias for the namespace of the element name
+
+ NamespaceBinding elementAlias = sheet.getNamespaceAlias(elementURI);
+ if (elementAlias != null && !elementAlias.getURI().equals(elementURI)) {
+ resultNameCode = namePool.allocate(elementAlias.getPrefix(),
+ elementAlias.getURI(),
+ getLocalPart());
+ }
+ }
+
+ // deal with special attributes
+
+ String useAttSets = getAttributeValue(NamespaceConstant.XSLT, "use-attribute-sets");
+ if (useAttSets != null) {
+ attributeSets = getAttributeSets(useAttSets, null);
+ }
+
+ validation = getContainingStylesheet().getDefaultValidation();
+ String type = getAttributeValue(NamespaceConstant.XSLT, "type");
+ if (type != null) {
+ if (!getPreparedStylesheet().isSchemaAware()) {
+ compileError("The xsl:type attribute is available only with a schema-aware XSLT processor", "XTSE1660");
+ }
+ schemaType = getSchemaType(type);
+ validation = Validation.BY_TYPE;
+ }
+
+ String validate = getAttributeValue(NamespaceConstant.XSLT, "validation");
+ if (validate != null) {
+ validation = Validation.getCode(validate);
+ if (validation != Validation.STRIP && !getPreparedStylesheet().isSchemaAware()) {
+ validation = Validation.STRIP;
+ compileError("To perform validation, a schema-aware XSLT processor is needed", "XTSE1660");
+ }
+ if (validation == Validation.INVALID) {
+ compileError("Invalid value for xsl:validation. " +
+ "Permitted values are (strict, lax, preserve, strip)", "XTSE0020");
+ }
+ if (schemaType != null) {
+ compileError("The attributes xsl:type and xsl:validation are mutually exclusive", "XTSE1505");
+ }
+ }
+
+ // establish the names to be used for all the output attributes;
+ // also type-check the AVT expressions
+
+ if (numberOfAttributes > 0) {
+
+ for (int i=0; i<numberOfAttributes; i++) {
+
+ NodeName anameCode = attributeNames[i];
+ NodeName alias = anameCode;
+ String attURI = anameCode.getURI();
+
+ if (attURI.length()!=0) { // attribute has a namespace prefix
+ NamespaceBinding newBinding = sheet.getNamespaceAlias(attURI);
+ if (newBinding != null && !newBinding.getURI().equals(attURI)) {
+ alias = new FingerprintedQName(
+ newBinding.getPrefix(),
+ newBinding.getURI(),
+ getAttributeList().getLocalName(i));
+ }
+ }
+
+ attributeNames[i] = alias;
+ attributeValues[i] = typeCheck(alias.getDisplayName(), attributeValues[i]);
+ }
+ }
+
+ // remove any namespaces that are on the exclude-result-prefixes list.
+ // The namespace is excluded even if it is the namespace of the element or an attribute,
+ // though in that case namespace fixup will reinstate it.
+
+ for (int n=namespaceCodes.size()-1; n>=0; n--) {
+ String uri = namespaceCodes.get(n).getURI();
+ if (isExcludedNamespace(uri) && !sheet.isAliasResultNamespace(uri)) {
+ namespaceCodes.remove(n);
+ }
+ }
+ }
+ }
+
+ /**
+ * Validate the children of this node, recursively. Overridden for top-level
+ * data elements.
+ * @param decl
+ */
+
+ protected void validateChildren(Declaration decl) throws XPathException {
+ if (!toplevel) {
+ super.validateChildren(decl);
+ }
+ }
+
+ /**
+ * Compile code to process the literal result element at runtime
+ */
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ // top level elements in the stylesheet are ignored
+ if (toplevel) return null;
+
+ NamespaceBinding[] bindings = namespaceCodes.toArray(new NamespaceBinding[namespaceCodes.size()]);
+ FixedElement inst = new FixedElement(
+ new CodedName(resultNameCode, getNamePool()),
+ bindings,
+ inheritNamespaces,
+ schemaType,
+ validation);
+
+ inst.setBaseURI(getBaseURI());
+
+ if (onEmpty != null) {
+ inst.setOnEmpty(onEmpty);
+ }
+ Expression content = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+
+ if (numberOfAttributes > 0) {
+ for (int i=attributeNames.length - 1; i>=0; i--) {
+ FixedAttribute att = new FixedAttribute(
+ attributeNames[i],
+ Validation.STRIP,
+ null);
+ att.setSelect(attributeValues[i], exec.getConfiguration());
+ att.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+ Expression exp = att;
+ if (getConfiguration().isCompileWithTracing()) {
+ TraceExpression trace = new TraceExpression(exp);
+ trace.setNamespaceResolver(getNamespaceResolver());
+ trace.setConstructType(Location.LITERAL_RESULT_ATTRIBUTE);
+ trace.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+ trace.setObjectName(attributeNames[i].getStructuredQName());
+ exp = trace;
+ }
+
+ if (content == null) {
+ content = exp;
+ } else {
+ content = Block.makeBlock(exp, content);
+ content.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+ }
+ }
+ }
+
+ if (attributeSets != null) {
+ UseAttributeSets use = new UseAttributeSets(attributeSets);
+ if (content == null) {
+ content = use;
+ } else {
+ content = Block.makeBlock(use, content);
+ content.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+ }
+ }
+
+ if (content == null) {
+ content = Literal.makeEmptySequence();
+ }
+ inst.setContentExpression(content);
+ return inst;
+ }
+
+ /**
+ * Make a top-level literal result element into a stylesheet. This implements
+ * the "Simplified Stylesheet" facility.
+ * @param pss the PreparedStylesheet (the compiled stylesheet as provided)
+ * @return the reconstructed stylesheet with an xsl:stylesheet and xsl:template element added
+ */
+
+ public DocumentImpl makeStylesheet(PreparedStylesheet pss)
+ throws XPathException {
+
+ // the implementation grafts the LRE node onto a containing xsl:template and
+ // xsl:stylesheet
+
+ StyleNodeFactory nodeFactory = pss.getStyleNodeFactory();
+ NamePool pool = getNamePool();
+ String xslPrefix = getPrefixForURI(NamespaceConstant.XSLT);
+ if (xslPrefix==null) {
+ String message;
+ if (getLocalPart().equals("stylesheet") || getLocalPart().equals("transform")) {
+ if (getPrefixForURI(NamespaceConstant.MICROSOFT_XSL) != null) {
+ message = "Saxon is not able to process Microsoft's WD-xsl dialect";
+ } else {
+ message = "Namespace for stylesheet element should be " + NamespaceConstant.XSLT;
+ }
+ } else {
+ message = "The supplied file does not appear to be a stylesheet";
+ }
+ XPathException err = new XPathException(message);
+ err.setLocator(this);
+ err.setErrorCode("XTSE0150");
+ err.setIsStaticError(true);
+ //noinspection EmptyCatchBlock
+ try {
+ pss.reportError(err);
+ } catch (TransformerException err2) {
+ }
+ throw err;
+
+ }
+
+ // check there is an xsl:version attribute (it's mandatory), and copy
+ // it to the new xsl:stylesheet element
+
+ String version = getAttributeValue(NamespaceConstant.XSLT, "version");
+ if (version==null) {
+ XPathException err = new XPathException("Simplified stylesheet: xsl:version attribute is missing");
+ err.setErrorCode("XTSE0150");
+ err.setIsStaticError(true);
+ err.setLocator(this);
+ //noinspection EmptyCatchBlock
+ try {
+ pss.reportError(err);
+ } catch (TransformerException err2) {
+ }
+ throw err;
+ }
+
+ try {
+ DocumentImpl oldRoot = (DocumentImpl)getDocumentRoot();
+ LinkedTreeBuilder builder = new LinkedTreeBuilder(pss.getConfiguration().makePipelineConfiguration());
+ builder.setNodeFactory(nodeFactory);
+ builder.setSystemId(this.getSystemId());
+
+ builder.open();
+ builder.startDocument(0);
+
+ int st = StandardNames.XSL_STYLESHEET;
+ builder.startElement(new CodedName(st, getNamePool()), Untyped.getInstance(), 0, 0);
+ builder.namespace(new NamespaceBinding("xsl", NamespaceConstant.XSLT), 0);
+ builder.attribute(new NoNamespaceName("version"), BuiltInAtomicType.UNTYPED_ATOMIC, version, 0, 0);
+ builder.startContent();
+
+ int te = StandardNames.XSL_TEMPLATE;
+ builder.startElement(new CodedName(te, getNamePool()), Untyped.getInstance(), 0, 0);
+ builder.attribute(new NoNamespaceName("match"), BuiltInAtomicType.UNTYPED_ATOMIC, "/", 0, 0);
+ builder.startContent();
+
+ builder.graftElement(this);
+
+ builder.endElement();
+ builder.endElement();
+ builder.endDocument();
+ builder.close();
+
+ DocumentImpl newRoot = (DocumentImpl)builder.getCurrentRoot();
+ newRoot.graftLocationMap(oldRoot);
+ return newRoot;
+ } catch (XPathException err) {
+ //TransformerConfigurationException e = new TransformerConfigurationException(err);
+ err.setLocator(this);
+ throw err;
+ }
+
+ }
+
+ /**
+ * Get the type of construct. This will be a constant in
+ * class {@link net.sf.saxon.trace.Location}. This method is part of the
+ * {@link net.sf.saxon.trace.InstructionInfo} interface
+ */
+
+ public int getConstructType() {
+ return Location.LITERAL_RESULT_ELEMENT;
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ * If there is no name, the value will be -1.
+ * @return the name of the literal result element
+ */
+
+ public StructuredQName getObjectName() {
+ return new StructuredQName(getPrefix(), getURI(), getLocalPart());
+ }
+
+ /**
+ * Get the value of a particular property of the instruction. This is part of the
+ * {@link net.sf.saxon.trace.InstructionInfo} interface for run-time tracing and debugging. The properties
+ * available include all the attributes of the source instruction (named by the attribute name):
+ * these are all provided as string values.
+ * @param name The name of the required property
+ * @return The value of the requested property, or null if the property is not available
+ */
+
+ public Object getProperty(String name) {
+ if (name.equals("name")) {
+ return getDisplayName();
+ }
+ return null;
+ }
+
+}
diff --git a/sf/saxon/style/PrincipalStylesheetModule.java b/sf/saxon/style/PrincipalStylesheetModule.java
new file mode 100644
index 0000000..01830e9
--- /dev/null
+++ b/sf/saxon/style/PrincipalStylesheetModule.java
@@ -0,0 +1,1130 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.expr.CollationMap;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntIterator;
+import net.sf.saxon.functions.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.query.XQueryFunction;
+import net.sf.saxon.query.XQueryFunctionLibrary;
+import net.sf.saxon.serialize.CharacterMap;
+import net.sf.saxon.serialize.CharacterMapIndex;
+import net.sf.saxon.trans.CompilerInfo;
+import net.sf.saxon.trans.RuleManager;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.TransformerException;
+import java.util.*;
+
+/**
+ * Represents the stylesheet module at the root of the import tree, that is, the module
+ * that includes or imports all the others. Note that this object is present at compile time only,
+ * unlike the Executable, which also exists at run-time.
+ */
+public class PrincipalStylesheetModule extends StylesheetModule {
+
+ // diagnostic switch to control output of timing information
+ private final static boolean TIMING = false;
+
+ private PreparedStylesheet preparedStylesheet;
+
+ // table of imported schemas. The members of this set are strings holding the target namespace.
+ private HashSet<String> schemaIndex = new HashSet<String>(10);
+
+ // table of functions imported from XQuery library modules
+ private XQueryFunctionLibrary queryFunctions;
+
+ // library of functions that are in-scope for XPath expressions in this stylesheet
+ private FunctionLibraryList functionLibrary;
+
+ // version attribute on xsl:stylesheet element of principal stylesheet module
+ private String version;
+
+ // index of global variables and parameters, by StructuredQName
+ // (overridden variables are excluded).
+ // Used at compile-time only, except for debugging
+ private HashMap<StructuredQName, Declaration> globalVariableIndex =
+ new HashMap<StructuredQName, Declaration>(20);
+
+ // table of named templates. Key is the integer fingerprint of the template name;
+ // value is the XSLTemplate object in the source stylesheet.
+ private HashMap<StructuredQName, Declaration> templateIndex =
+ new HashMap<StructuredQName, Declaration>(20);
+
+ // Table of named stylesheet functions. A two level lookup, using first the arity and then
+ // the expanded name of the function.
+ private IntHashMap<HashMap<StructuredQName, Declaration>> functionIndex =
+ new IntHashMap<HashMap<StructuredQName, Declaration>>(8);
+
+ // map for allocating unique numbers to local parameter names. Key is a
+ // StructuredQName; value is a boxed int.
+ /*@Nullable*/ private HashMap<StructuredQName, Integer> localParameterNumbers = null;
+
+
+ // namespace aliases. This information is needed at compile-time only
+ private int numberOfAliases = 0;
+ private List<Declaration> namespaceAliasList = new ArrayList<Declaration>(5);
+ private HashMap<String, NamespaceBinding> namespaceAliasMap;
+ private Set<String> aliasResultUriSet;
+ //private short[] aliasSCodes;
+ //private int[] aliasNCodes;
+
+ // flag: true if there's an xsl:result-document that uses a dynamic format
+ private boolean needsDynamicOutputProperties = false;
+
+ // count of the maximum number of local variables in xsl:template match patterns
+ private int largestPatternStackFrame = 0;
+
+ // cache of stylesheet documents. Note that multiple imports of the same URI
+ // lead to the stylesheet tree being reused
+ private HashMap<DocumentURI, XSLStylesheet> moduleCache = new HashMap<DocumentURI, XSLStylesheet>(4);
+
+ public PrincipalStylesheetModule(XSLStylesheet sourceElement, int precedence) {
+ super(sourceElement, precedence);
+ }
+
+ public void setPreparedStylesheet(PreparedStylesheet preparedStylesheet) {
+ this.preparedStylesheet = preparedStylesheet;
+ }
+
+ public PreparedStylesheet getPreparedStylesheet() {
+ return preparedStylesheet;
+ }
+
+ /*@NotNull*/
+ public PrincipalStylesheetModule getPrincipalStylesheetModule() {
+ return this;
+ }
+
+ /**
+ * Create the function library
+ * @return the resulting function library
+ */
+
+ public FunctionLibraryList createFunctionLibrary(CompilerInfo info) {
+ Configuration config = getPreparedStylesheet().getConfiguration();
+ FunctionLibraryList functionLibrary = new FunctionLibraryList();
+ int functionSet = StandardFunction.CORE | StandardFunction.XSLT;
+ if ("3.0".equals(getVersion())) {
+ functionSet |= (StandardFunction.XSLT30 | StandardFunction.XPATH30);
+ }
+ functionLibrary.addFunctionLibrary(
+ SystemFunctionLibrary.getSystemFunctionLibrary(functionSet));
+ functionLibrary.addFunctionLibrary(
+ new StylesheetFunctionLibrary(this, true));
+ functionLibrary.addFunctionLibrary(
+ config.getVendorFunctionLibrary());
+ functionLibrary.addFunctionLibrary(
+ new ConstructorFunctionLibrary(config));
+ if (info.getExtensionFunctionLibrary() != null) {
+ functionLibrary.addFunctionLibrary(info.getExtensionFunctionLibrary());
+ }
+ queryFunctions = new XQueryFunctionLibrary(config);
+ functionLibrary.addFunctionLibrary(queryFunctions);
+ functionLibrary.addFunctionLibrary(config.getIntegratedFunctionLibrary());
+ config.addExtensionBinders(functionLibrary);
+ functionLibrary.addFunctionLibrary(
+ new StylesheetFunctionLibrary(this, false));
+ if (getPreparedStylesheet().getAccumulatorManager() != null) {
+ // TODO: think about where in the library list this function library should be placed
+ functionLibrary.addFunctionLibrary(getPreparedStylesheet().getAccumulatorManager().getAccumulatorFunctionLibrary());
+ }
+ return (this.functionLibrary = functionLibrary);
+ }
+
+ /**
+ * Get the function library. Available only on the principal stylesheet module
+ * @return the function library
+ */
+
+ public FunctionLibrary getFunctionLibrary() {
+ return functionLibrary;
+ }
+
+
+ /**
+ * Declare an imported XQuery function
+ * @param function the imported function
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void declareXQueryFunction(XQueryFunction function) throws XPathException {
+ queryFunctions.declareFunction(function);
+ }
+
+ /**
+ * Add a module to the cache
+ * @param key the key to be used (based on the absolute URI)
+ * @param module the stylesheet document tree corresponding to this absolute URI
+ */
+
+ public void putStylesheetDocument(DocumentURI key, XSLStylesheet module) {
+ moduleCache.put(key, module);
+ }
+
+ /**
+ * Get a module from the cache
+ * @param key the key to be used (based on the absolute URI)
+ * @return the stylesheet document tree corresponding to this absolute URI
+
+ */
+
+ public XSLStylesheet getStylesheetDocument(DocumentURI key) {
+ XSLStylesheet sheet = moduleCache.get(key);
+ if (sheet != null) {
+ TransformerException warning = new TransformerException(
+ "Stylesheet module " + key + " is included or imported more than once. " +
+ "This is permitted, but may lead to errors or unexpected behavior");
+ getPreparedStylesheet().reportWarning(warning);
+ }
+ return sheet;
+ }
+
+ /**
+ * Preprocess does all the processing possible before the source document is available.
+ * It is done once per stylesheet, so the stylesheet can be reused for multiple source
+ * documents. The method is called only on the XSLStylesheet element representing the
+ * principal stylesheet module
+ * @throws net.sf.saxon.trans.XPathException if errors are found in the stylesheet
+ */
+
+ public void preprocess() throws XPathException {
+
+ // process any xsl:include and xsl:import elements
+ net.sf.saxon.trans.Timer timer;
+ if (TIMING) {
+ timer = new net.sf.saxon.trans.Timer();
+ }
+
+ spliceIncludes();
+
+ if (TIMING) {
+ timer.report("spliceIncludes");
+ }
+
+ // build indexes for selected top-level elements
+
+ buildIndexes();
+
+ if (TIMING) {
+ timer.report("buildIndexes");
+ }
+
+ // check for use of schema-aware constructs
+
+ checkForSchemaAwareness();
+
+ if (TIMING) {
+ timer.report("checkForSchemaAwareness");
+ }
+
+ // process the attributes of every node in the tree
+
+ processAllAttributes();
+
+ if (TIMING) {
+ timer.report("processAllAttributes");
+ }
+ // collect any namespace aliases
+
+ collectNamespaceAliases();
+
+ if (TIMING) {
+ timer.report("collectNamespaceAliases");
+ }
+
+ // fix up references from XPath expressions to variables and functions, for static typing
+
+ for (Declaration decl : topLevel) {
+ StyleElement inst = decl.getSourceElement();
+ if (!inst.isActionCompleted(StyleElement.ACTION_FIXUP)) {
+ inst.setActionCompleted(StyleElement.ACTION_FIXUP);
+// if (inst instanceof XSLVariableDeclaration) {
+// System.err.println("Fixup global variable " + ((XSLVariableDeclaration)inst).getVariableQName());
+// }
+ inst.fixupReferences();
+ }
+ }
+
+ if (TIMING) {
+ timer.report("fixupReferences");
+ }
+ // Validate the whole logical style sheet (i.e. with included and imported sheets)
+
+ XSLStylesheet top = getSourceElement();
+ setInputTypeAnnotations(top.getInputTypeAnnotationsAttribute());
+ Declaration decl = new Declaration(this, top);
+ if (!top.isActionCompleted(StyleElement.ACTION_VALIDATE)) {
+ top.setActionCompleted(StyleElement.ACTION_VALIDATE);
+ top.validate(decl);
+ for (Declaration d : topLevel) {
+ d.getSourceElement().validateSubtree(d);
+ }
+ }
+
+ if (TIMING) {
+ timer.report("validate");
+ timer.reportCumulative("total preprocess");
+ }
+ }
+
+ /**
+ * Build indexes for selected top-level declarations
+ * @throws net.sf.saxon.trans.XPathException if errors are detected
+ */
+
+ private void buildIndexes() throws XPathException {
+ // Scan the declarations in reverse order, that is, highest precedence first
+ for (int i = topLevel.size() - 1; i >= 0; i--) {
+ Declaration decl = topLevel.get(i);
+ decl.getSourceElement().index(decl, this);
+ }
+ // Now seal all the schemas that have been imported to guarantee consistency with instance documents
+ Configuration config = getPreparedStylesheet().getConfiguration();
+ for (String ns : schemaIndex) {
+ config.sealNamespace(ns);
+ }
+ }
+
+ /**
+ * Process the attributes of every node in the stylesheet
+ * @throws net.sf.saxon.trans.XPathException if static errors are found in the stylesheet
+ */
+
+ public void processAllAttributes() throws XPathException {
+ getSourceElement().processDefaultCollationAttribute();
+ getSourceElement().prepareAttributes();
+ for (Declaration decl : topLevel) {
+ StyleElement inst = decl.getSourceElement();
+ if (!inst.isActionCompleted(StyleElement.ACTION_PROCESS_ATTRIBUTES)) {
+ inst.setActionCompleted(StyleElement.ACTION_PROCESS_ATTRIBUTES);
+ try {
+ inst.processAllAttributes();
+ } catch (XPathException err) {
+ decl.getSourceElement().compileError(err);
+ }
+ }
+ }
+ }
+
+ /**
+ * Add a stylesheet function to the index
+ * @param decl The declaration wrapping an XSLFunction object
+ * @throws XPathException if errors are found
+ */
+ protected void indexFunction(Declaration decl) throws XPathException {
+ XSLFunction function = (XSLFunction)decl.getSourceElement();
+ StructuredQName qName = function.getObjectName();
+ int arity = function.getNumberOfArguments();
+
+ // see if there is already a named function with this precedence
+ Declaration other = getFunctionDeclaration(qName, arity);
+ if (other == null) {
+ // this is the first
+ putFunction(decl);
+ } else {
+ // check the precedences
+ int thisPrecedence = decl.getPrecedence();
+ int otherPrecedence = other.getPrecedence();
+ if (thisPrecedence == otherPrecedence) {
+ StyleElement f2 = other.getSourceElement();
+ if (decl.getSourceElement() == f2) {
+ function.compileError(
+ "Function " + qName.getDisplayName() + " is declared more than once " +
+ "(caused by including the containing module more than once)",
+ "XTSE0770");
+ } else {
+ function.compileError("Duplicate function declaration (see line " +
+ f2.getLineNumber() + " of " + f2.getSystemId() + ')', "XTSE0770");
+ }
+ } else if (thisPrecedence < otherPrecedence) {
+ //
+ } else {
+ // can't happen, but we'll play safe
+ putFunction(decl);
+ }
+ }
+ }
+
+ protected Declaration getFunctionDeclaration(StructuredQName name, int arity) {
+ HashMap<StructuredQName, Declaration> m = functionIndex.get(arity);
+ return (m == null ? null : m.get(name));
+ }
+
+ /**
+ * Get the function with a given name and arity
+ * @param name the name of the function
+ * @param arity the arity of the function, or -1 if any arity will do
+ * @return the requested function, or null if none can be found
+ */
+
+ protected XSLFunction getFunction(StructuredQName name, int arity) {
+ if (arity == -1) {
+ // supports the single-argument function-available() function
+ for (IntIterator arities = functionIndex.keyIterator(); arities.hasNext();) {
+ int a = arities.next();
+ Declaration decl = getFunctionDeclaration(name, a);
+ if (decl != null) {
+ return (XSLFunction)decl.getSourceElement();
+ }
+ }
+ return null;
+ } else {
+ Declaration decl = getFunctionDeclaration(name, arity);
+ return (decl == null ? null : (XSLFunction)decl.getSourceElement());
+ }
+ }
+
+ protected void putFunction(Declaration decl) {
+ XSLFunction function = (XSLFunction)decl.getSourceElement();
+ StructuredQName qName = function.getObjectName();
+ int arity = function.getNumberOfArguments();
+ HashMap<StructuredQName, Declaration> m = functionIndex.get(arity);
+ if (m == null) {
+ m = new HashMap<StructuredQName, Declaration>();
+ functionIndex.put(arity, m);
+ }
+ m.put(qName, decl);
+ }
+
+
+ /**
+ * Index a global xsl:variable or xsl:param element
+ * @param decl The Declaration referencing the XSLVariable or XSLParam element
+ * @throws XPathException if an error occurs
+ */
+
+ protected void indexVariableDeclaration(Declaration decl) throws XPathException {
+ XSLGlobalVariable var = (XSLGlobalVariable)decl.getSourceElement();
+ StructuredQName qName = var.getSourceBinding().getVariableQName();
+ if (qName != null) {
+ // see if there is already a global variable with this precedence
+ Declaration other = globalVariableIndex.get(qName);
+ if (other == null) {
+ // this is the first
+ globalVariableIndex.put(qName, decl);
+ } else {
+ // check the precedences
+ int thisPrecedence = decl.getPrecedence();
+ int otherPrecedence = other.getPrecedence();
+ if (thisPrecedence == otherPrecedence) {
+ StyleElement v2 = other.getSourceElement();
+ if (v2 == var) {
+ var.compileError(
+ "Global variable " + qName.getDisplayName() + " is declared more than once " +
+ "(caused by including the containing module more than once)",
+ "XTSE0630");
+ } else {
+ var.compileError("Duplicate global variable declaration (see line " +
+ v2.getLineNumber() + " of " + v2.getSystemId() + ')', "XTSE0630");
+ }
+ } else if (thisPrecedence < otherPrecedence && var != other.getSourceElement()) {
+ var.setRedundant(true);
+ } else if (var != other.getSourceElement()) {
+ ((XSLGlobalVariable)other.getSourceElement()).setRedundant(true);
+ globalVariableIndex.put(qName, decl);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the global variable or parameter with a given name (taking
+ * precedence rules into account)
+ * @param qName name of the global variable or parameter
+ * @return the variable declaration, or null if it does not exist
+ */
+
+ public SourceBinding getGlobalVariable(StructuredQName qName) {
+ Declaration decl = globalVariableIndex.get(qName);
+ return (decl == null ? null : ((XSLGlobalVariable)decl.getSourceElement()).getSourceBinding());
+ }
+
+ /**
+ * Allocate a unique number to a local parameter name. This should only be called on the principal
+ * stylesheet module.
+ * @param qName the local parameter name
+ * @return an integer that uniquely identifies this parameter name within the stylesheet
+ */
+
+ public int allocateUniqueParameterNumber(StructuredQName qName) {
+ HashMap<StructuredQName, Integer> params = localParameterNumbers;
+ if (params == null) {
+ localParameterNumbers = new HashMap<StructuredQName, Integer>(50);
+ params = localParameterNumbers;
+ }
+ Integer x = params.get(qName);
+ if (x == null) {
+ x = params.size();
+ params.put(qName, x);
+ }
+ return x;
+ }
+
+ /**
+ * Add a named template to the index
+ * @param decl the declaration of the Template object
+ * @throws XPathException if an error occurs
+ */
+ protected void indexNamedTemplate(Declaration decl) throws XPathException {
+ XSLTemplate template = (XSLTemplate)decl.getSourceElement();
+ StructuredQName qName = template.getTemplateName();
+ if (qName != null) {
+ // see if there is already a named template with this precedence
+ Declaration other = templateIndex.get(qName);
+ if (other == null) {
+ // this is the first
+ templateIndex.put(qName, decl);
+ getPreparedStylesheet().putNamedTemplate(qName, template.getCompiledTemplate());
+ } else {
+ // check the precedences
+ int thisPrecedence = decl.getPrecedence();
+ int otherPrecedence = other.getPrecedence();
+ if (thisPrecedence == otherPrecedence) {
+ StyleElement t2 = other.getSourceElement();
+ template.compileError("Duplicate named template (see line " +
+ t2.getLineNumber() + " of " + t2.getSystemId() + ')', "XTSE0660");
+ } else if (thisPrecedence < otherPrecedence) {
+ //template.setRedundantNamedTemplate();
+ } else {
+ // can't happen, but we'll play safe
+ //other.setRedundantNamedTemplate();
+ templateIndex.put(qName, decl);
+ getPreparedStylesheet().putNamedTemplate(qName, template.getCompiledTemplate());
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the named template with a given name
+ * @param name the name of the required template
+ * @return the template with the given name, if there is one, or null otherwise. If there
+ * are several templates with the same name, the one with highest import precedence
+ * is returned.
+ */
+
+ public XSLTemplate getNamedTemplate(StructuredQName name) {
+ Declaration decl = templateIndex.get(name);
+ return (decl == null ? null : (XSLTemplate)decl.getSourceElement());
+ }
+
+
+ /**
+ * Check for schema-awareness.
+ * Typed input nodes are recognized if and only if the stylesheet contains an import-schema declaration.
+ */
+
+ private void checkForSchemaAwareness() {
+ Executable exec = getPreparedStylesheet();
+ if (!exec.isSchemaAware() && exec.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XSLT)) {
+ for (Declaration decl : topLevel) {
+ StyleElement node = decl.getSourceElement();
+ if (node instanceof XSLImportSchema) {
+ exec.setSchemaAware(true);
+ return;
+ }
+ }
+ }
+ }
+
+
+ protected void addNamespaceAlias(Declaration node) {
+ namespaceAliasList.add(node);
+ numberOfAliases++;
+ }
+
+ /**
+ * Get the declared namespace alias for a given namespace URI code if there is one.
+ * If there is more than one, we get the last.
+ * @param uri The uri used in the stylesheet.
+ * @return The namespace binding to be used (prefix and uri): return null
+ * if no alias is defined
+ */
+
+ protected NamespaceBinding getNamespaceAlias(String uri) {
+ return namespaceAliasMap.get(uri);
+ }
+
+ /**
+ * Determine if a namespace is included in the result-prefix of a namespace-alias
+ * @param uri the namespace URI
+ * @return true if an xsl:namespace-alias has been defined for this namespace URI
+ */
+
+ protected boolean isAliasResultNamespace(String uri) {
+ return aliasResultUriSet.contains(uri);
+ }
+
+ /**
+ * Collect any namespace aliases
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ private void collectNamespaceAliases() throws XPathException {
+ namespaceAliasMap = new HashMap<String, NamespaceBinding>(numberOfAliases);
+ aliasResultUriSet = new HashSet<String>(numberOfAliases);
+ HashSet<String> aliasesAtThisPrecedence = new HashSet<String>();
+ int currentPrecedence = -1;
+ // Note that we are processing the list in reverse stylesheet order,
+ // that is, highest precedence first.
+ for (int i = 0; i < numberOfAliases; i++) {
+ Declaration decl = namespaceAliasList.get(i);
+ XSLNamespaceAlias xna = (XSLNamespaceAlias)decl.getSourceElement();
+ String scode = xna.getStylesheetURI();
+ NamespaceBinding resultBinding = xna.getResultNamespaceBinding();
+ int prec = decl.getPrecedence();
+
+ // check that there isn't a conflict with another xsl:namespace-alias
+ // at the same precedence
+
+ if (currentPrecedence != prec) {
+ currentPrecedence = prec;
+ aliasesAtThisPrecedence.clear();
+ //precedenceBoundary = i;
+ }
+ if (aliasesAtThisPrecedence.contains(scode)) {
+ if (!namespaceAliasMap.get(scode).getURI().equals(resultBinding.getURI())) {
+ xna.compileError("More than one alias is defined for the same namespace", "XTSE0810");
+ }
+ }
+ if (namespaceAliasMap.get(scode) == null) {
+ namespaceAliasMap.put(scode, resultBinding);
+ aliasResultUriSet.add(resultBinding.getURI());
+ }
+ aliasesAtThisPrecedence.add(scode);
+ }
+ namespaceAliasList = null; // throw it in the garbage
+ }
+
+ protected boolean hasNamespaceAliases() {
+ return numberOfAliases > 0;
+ }
+
+ /**
+ * Get the collation map
+ * @return the CollationMap
+ */
+
+ public CollationMap getCollationMap() {
+ return getPreparedStylesheet().getCollationTable();
+ }
+
+ /**
+ * Register a named collation (actually a StringCollator)
+ * @param name the name of the collation
+ * @param collation the StringCollator that implements this collation
+ */
+
+ public void setCollation(String name, StringCollator collation) {
+ Executable exec = getPreparedStylesheet();
+ if (exec.getCollationTable() == null) {
+ exec.setCollationMap(new CollationMap(exec.getConfiguration()));
+ }
+ exec.getCollationTable().setNamedCollation(name, collation);
+ }
+
+ /**
+ * Find a named collation. Note this method should only be used at compile-time, before declarations
+ * have been pre-processed. After that time, use getCollation().
+ * @param name identifies the name of the collation required
+ * @param baseURI the base URI to be used for resolving the collation name if it is relative
+ * @return null if the collation is not found
+ */
+
+ public StringCollator findCollation(String name, String baseURI) {
+
+ Executable exec = getPreparedStylesheet();
+ Configuration config = exec.getConfiguration();
+
+ if (name.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
+ return CodepointCollator.getInstance();
+ }
+
+ // First try to find it in the table
+
+ StringCollator c = null;
+
+ if (exec.getCollationTable() != null) {
+ c = exec.getCollationTable().getNamedCollation(name);
+ }
+ if (c != null) return c;
+
+ // At compile-time, the collation might not yet be in the table. So search for it
+ // Search for a matching collation name, starting at the end in case of duplicates.
+ // this also ensures we get the one with highest import precedence.
+ for (int i = topLevel.size() - 1; i >= 0; i--) {
+ Declaration decl = topLevel.get(i);
+ if (decl.getSourceElement() instanceof CollationDeclaration) {
+ CollationDeclaration t = (CollationDeclaration)decl.getSourceElement();
+ if (t.getCollationName().equals(name)) {
+ return t.getCollator();
+ }
+ }
+ }
+
+ return config.getCollationURIResolver().resolve(name, baseURI, config);
+ }
+
+ /**
+ * Create an output properties object representing the xsl:output elements in the stylesheet.
+ * @param formatQName The name of the output format required. If set to null, gathers
+ * information for the unnamed output format
+ * @return the Properties object containing the details of the specified output format
+ * @throws XPathException if a named output format does not exist in
+ * the stylesheet
+ */
+
+ public Properties gatherOutputProperties(/*@Nullable*/ StructuredQName formatQName) throws XPathException {
+ boolean found = (formatQName == null);
+ Configuration config = getPreparedStylesheet().getConfiguration();
+ Properties details = new Properties(config.getDefaultSerializationProperties());
+ HashMap precedences = new HashMap(10);
+ for (int i = topLevel.size()-1; i >= 0; i--) {
+ Declaration decl = topLevel.get(i);
+ if (decl.getSourceElement() instanceof XSLOutput) {
+ XSLOutput xo = (XSLOutput)decl.getSourceElement();
+ if (formatQName == null
+ ? xo.getFormatQName() == null
+ : formatQName.equals(xo.getFormatQName())) {
+ found = true;
+ xo.gatherOutputProperties(details, precedences, decl.getPrecedence());
+ }
+ }
+ }
+ if (!found) {
+ compileError("Requested output format " + formatQName.getDisplayName() +
+ " has not been defined", "XTDE1460");
+ }
+ return details;
+ }
+
+
+
+ /**
+ * Compile the stylesheet to create an executable.
+ * @throws net.sf.saxon.trans.XPathException if compilation fails for any reason
+ */
+
+ public void compileStylesheet() throws XPathException {
+
+ try {
+
+ net.sf.saxon.trans.Timer timer;
+ if (TIMING) {
+ timer = new net.sf.saxon.trans.Timer();
+ }
+
+ PreparedStylesheet pss = getPreparedStylesheet();
+ Configuration config = pss.getConfiguration();
+
+ // If any XQuery functions were imported, fix up all function calls
+ // registered against these functions.
+ try {
+ Iterator qf = queryFunctions.getFunctionDefinitions();
+ while (qf.hasNext()) {
+ XQueryFunction f = (XQueryFunction) qf.next();
+ f.fixupReferences();
+ }
+ } catch (XPathException e) {
+ compileError(e);
+ }
+
+ if (TIMING) {
+ timer.report("fixup Query functions");
+ }
+
+ // Register template rules with the rule manager
+
+ for (Declaration decl : topLevel) {
+ StyleElement snode = decl.getSourceElement();
+ if (snode instanceof XSLTemplate) {
+ ((XSLTemplate) snode).register(decl);
+ }
+ }
+
+ if (TIMING) {
+ timer.report("register templates");
+ }
+
+
+ // Call compile method for each top-level object in the stylesheet
+ // Note, some declarations (templates) need to be compiled repeatedly if the module
+ // is imported repeatedly; others (variables, functions) do not
+
+ for (Declaration decl : topLevel) {
+ StyleElement snode = decl.getSourceElement();
+ if (!snode.isActionCompleted(StyleElement.ACTION_COMPILE)) {
+ snode.setActionCompleted(StyleElement.ACTION_COMPILE);
+ snode.compileDeclaration(pss, decl);
+ }
+ }
+
+ if (TIMING) {
+ timer.report("compile top-level objects");
+ }
+
+ // Call type-check method for each user-defined function in the stylesheet. This is no longer
+ // done during the optimize step, to avoid functions being inlined before they are type-checked.
+
+// for (int i = 0; i < topLevel.size(); i++) {
+// NodeInfo node = (NodeInfo) topLevel.get(i);
+// if (node instanceof XSLFunction) {
+// ((XSLFunction) node).typeCheckBody();
+// }
+// }
+
+ for (IntIterator arities = functionIndex.keyIterator(); arities.hasNext();) {
+ for (Declaration decl : functionIndex.get(arities.next()).values()) {
+ StyleElement node = decl.getSourceElement();
+ if (!node.isActionCompleted(StyleElement.ACTION_TYPECHECK)) {
+ node.setActionCompleted(StyleElement.ACTION_TYPECHECK);
+ ((XSLFunction) node).typeCheckBody();
+ }
+ }
+ }
+
+ if (TIMING) {
+ timer.report("typeCheck functions");
+ }
+
+ if (getPreparedStylesheet().getErrorCount() > 0) {
+ // not much point carrying on
+ return;
+ }
+
+ // Call optimize method for each top-level object in the stylesheet
+ // But for functions, do it only for those of highest precedence.
+
+ for (Declaration decl : topLevel) {
+ StyleElement node = decl.getSourceElement();
+ if (node instanceof StylesheetProcedure && !(node instanceof XSLFunction) &&
+ !node.isActionCompleted(StyleElement.ACTION_OPTIMIZE)) {
+ node.setActionCompleted(StyleElement.ACTION_OPTIMIZE);
+ ((StylesheetProcedure) node).optimize(decl);
+ }
+ }
+
+ // optimize functions that aren't overridden
+
+ for (IntIterator arities = functionIndex.keyIterator(); arities.hasNext();) {
+ for (Declaration decl : functionIndex.get(arities.next()).values()) {
+ StyleElement node = decl.getSourceElement();
+ if (!node.isActionCompleted(StyleElement.ACTION_OPTIMIZE)) {
+ node.setActionCompleted(StyleElement.ACTION_OPTIMIZE);
+ ((StylesheetProcedure) node).optimize(decl);
+ }
+ }
+ }
+
+ if (TIMING) {
+ timer.report("optimize");
+ }
+
+ if (config.isTiming() && config.isGenerateByteCode(Configuration.XSLT)) {
+ config.getStandardErrorOutput().println("Generating byte code...");
+ }
+
+ pss.setStripsWhitespace(stripsWhitespace());
+
+ Properties props = gatherOutputProperties(null);
+ props.setProperty(SaxonOutputKeys.STYLESHEET_VERSION, getVersion());
+ pss.setDefaultOutputProperties(props);
+
+ // handle named output formats for use at run-time
+ HashSet<StructuredQName> outputNames = new HashSet<StructuredQName>(5);
+ for (Declaration decl : topLevel) {
+ if (decl.getSourceElement() instanceof XSLOutput) {
+ XSLOutput out = (XSLOutput) decl.getSourceElement();
+ StructuredQName qName = out.getFormatQName();
+ if (qName != null) {
+ outputNames.add(qName);
+ }
+ }
+ }
+ if (outputNames.isEmpty()) {
+ if (needsDynamicOutputProperties) {
+ compileError("The stylesheet contains xsl:result-document instructions that calculate the output " +
+ "format name at run-time, but there are no named xsl:output declarations", "XTDE1460");
+ }
+ } else {
+ for (StructuredQName qName : outputNames) {
+ Properties oprops = gatherOutputProperties(qName);
+ if (needsDynamicOutputProperties) {
+ pss.setOutputProperties(qName, oprops);
+ }
+ }
+ }
+
+ pss.setPatternSlotSpace(largestPatternStackFrame);
+ pss.setStripsInputTypeAnnotations(getInputTypeAnnotations() == XSLStylesheet.ANNOTATION_STRIP);
+
+ // Build the index of named character maps
+
+ for (Declaration decl : topLevel) {
+ if (decl.getSourceElement() instanceof XSLCharacterMap) {
+ XSLCharacterMap t = (XSLCharacterMap) decl.getSourceElement();
+ if (!t.isRedundant()) {
+ StructuredQName qn = t.getCharacterMapName();
+ IntHashMap<String> charMap = new IntHashMap<String>();
+ t.assemble(charMap);
+ CharacterMap map = new CharacterMap(charMap);
+ if (pss.getCharacterMapIndex() == null) {
+ pss.setCharacterMapIndex(new CharacterMapIndex());
+ }
+ pss.getCharacterMapIndex().putCharacterMap(qn, map);
+ }
+ }
+ }
+
+ if (TIMING) {
+ timer.report("miscellanea");
+ }
+
+ // Check consistency of decimal formats
+
+ pss.getDecimalFormatManager().checkConsistency();
+
+ // Finish off the lists of template rules
+
+ RuleManager ruleManager = getPreparedStylesheet().getRuleManager();
+ ruleManager.computeRankings();
+ ruleManager.invertStreamableTemplates(config.obtainOptimizer());
+
+ if (TIMING) {
+ timer.report("build template rule tables");
+ }
+
+ // Build a run-time function library. This supports the use of function-available()
+ // with a dynamic argument, and extensions such as saxon:evaluate(). The run-time
+ // function library differs from the compile-time function library in that both
+ // the StylesheetFunctionLibrary's on the library list are replaced by equivalent
+ // ExecutableFunctionLibrary's. This is to prevent the retaining of run-time links
+ // to the stylesheet document tree.
+
+ ExecutableFunctionLibrary overriding = new ExecutableFunctionLibrary(config);
+ ExecutableFunctionLibrary underriding = new ExecutableFunctionLibrary(config);
+
+ for (Declaration decl : topLevel) {
+ if (decl.getSourceElement() instanceof XSLFunction) {
+ XSLFunction func = (XSLFunction) decl.getSourceElement();
+ if (func.isOverriding()) {
+ overriding.addFunction(func.getCompiledFunction());
+ } else {
+ underriding.addFunction(func.getCompiledFunction());
+ }
+ }
+ }
+
+ FunctionLibraryList libraryList = new FunctionLibraryList();
+ for (FunctionLibrary lib : functionLibrary.getLibraryList()) {
+ if (lib instanceof StylesheetFunctionLibrary) {
+ if (((StylesheetFunctionLibrary) lib).isOverriding()) {
+ libraryList.addFunctionLibrary(overriding);
+ } else {
+ libraryList.addFunctionLibrary(underriding);
+ }
+ } else {
+ libraryList.addFunctionLibrary(lib);
+ }
+ }
+ pss.setFunctionLibrary(libraryList);
+
+ if (TIMING) {
+ timer.report("build runtime function tables");
+ timer.reportCumulative("total compile phase");
+ }
+
+ } catch (RuntimeException err) {
+ // if syntax errors were reported earlier, then exceptions may occur during this phase
+ // due to inconsistency of data structures. We can ignore these exceptions as they
+ // will go away when the user corrects the stylesheet
+ if (getPreparedStylesheet().getErrorCount() == 0) {
+ // rethrow the exception
+ throw err;
+ }
+ }
+
+ }
+
+ /**
+ * Get an imported schema with a given namespace
+ * @param targetNamespace The target namespace of the required schema.
+ * Supply an empty string for the default namespace
+ * @return the required Schema, or null if no such schema has been imported
+ */
+
+ protected boolean isImportedSchema(String targetNamespace) {
+ return schemaIndex.contains(targetNamespace);
+ }
+
+ protected void addImportedSchema(String targetNamespace) {
+ schemaIndex.add(targetNamespace);
+ }
+
+ protected HashSet<String> getImportedSchemaTable() {
+ return schemaIndex;
+ }
+
+ /**
+ * Get the list of attribute-set declarations associated with a given QName.
+ * This is used for xsl:element, xsl:copy, xsl:attribute-set, and on literal
+ * result elements
+ *
+ * @param name the name of the required attribute set
+ * @param list a list to hold the list of XSLAttributeSet elements in the stylesheet tree.
+ * @return true if any declarations were found and added to the list; false if none were found
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ protected boolean getAttributeSets(StructuredQName name, List<Declaration> list)
+ throws XPathException {
+
+ boolean found = false;
+
+ // search for the named attribute set, using all of them if there are several with the
+ // same name
+
+ for (Declaration decl : topLevel) {
+ if (decl.getSourceElement() instanceof XSLAttributeSet) {
+ XSLAttributeSet t = (XSLAttributeSet) decl.getSourceElement();
+ if (t.getAttributeSetName().equals(name)) {
+ t.incrementReferenceCount();
+ list.add(decl);
+ found = true;
+ }
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Determine whether this stylesheet does any whitespace stripping
+ * @return true if this stylesheet strips whitespace from source documents
+ */
+
+ public boolean stripsWhitespace() {
+ for (Declaration aTopLevel : topLevel) {
+ NodeInfo s = aTopLevel.getSourceElement();
+ if (s.getFingerprint() == StandardNames.XSL_STRIP_SPACE) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Set the value of the version attribute on the xsl:stylesheet element of the
+ * principal stylesheet module
+ * @param version the value of the version attribute
+ */
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ /**
+ * Get the value of the version attribute on the xsl:stylesheet element of the
+ * principal stylesheet module
+ * @return the value of the version attribute
+ */
+
+ public String getVersion() {
+ return version;
+ }
+
+ /**
+ * Say that this stylesheet needs dynamic output properties
+ * @param b true if this stylesheet needs dynamic output properties
+ */
+
+ public void setNeedsDynamicOutputProperties(boolean b) {
+ needsDynamicOutputProperties = b;
+ }
+
+ /**
+ * Get a character map, identified by the fingerprint of its name.
+ * Search backwards through the stylesheet.
+ * @param name The character map name being sought
+ * @return the identified character map, or null if not found
+ */
+
+ public Declaration getCharacterMap(StructuredQName name) {
+ for (int i = topLevel.size() - 1; i >= 0; i--) {
+ Declaration decl = topLevel.get(i);
+ if (decl.getSourceElement() instanceof XSLCharacterMap) {
+ XSLCharacterMap t = (XSLCharacterMap) decl.getSourceElement();
+ if (t.getCharacterMapName().equals(name)) {
+ return decl;
+ }
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Ensure there is enough space for local variables or parameters when evaluating the match pattern of
+ * template rules
+ * @param n the number of slots to be allocated
+ */
+
+ public void allocatePatternSlots(int n) {
+ if (n > largestPatternStackFrame) {
+ largestPatternStackFrame = n;
+ }
+ }
+
+
+
+ /**
+ * Compile time error, specifying an error code
+ * @param message the error message
+ * @param errorCode the error code. May be null if not known or not defined
+ * @throws XPathException unconditionally
+ */
+
+ protected void compileError(String message, String errorCode) throws XPathException {
+ XPathException tce = new XPathException(message);
+ tce.setErrorCode(errorCode);
+ compileError(tce);
+ }
+
+ /**
+ * Report an error with diagnostic information
+ * @param error contains information about the error
+ * @throws XPathException unconditionally, after reporting the error to the ErrorListener
+ */
+
+ protected void compileError(XPathException error)
+ throws XPathException {
+ error.setIsStaticError(true);
+ PreparedStylesheet pss = getPreparedStylesheet();
+ try {
+ if (pss == null) {
+ // it is null before the stylesheet has been fully built
+ throw error;
+ } else {
+ pss.reportError(error);
+ }
+ } catch (TransformerException err2) {
+ throw XPathException.makeXPathException(err2);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/style/SourceBinding.java b/sf/saxon/style/SourceBinding.java
new file mode 100644
index 0000000..dff7c44
--- /dev/null
+++ b/sf/saxon/style/SourceBinding.java
@@ -0,0 +1,583 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.DocumentInstr;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.GeneralVariable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+* Helper class for xsl:variable and xsl:param elements. <br>
+*/
+
+public class SourceBinding {
+
+ private StyleElement sourceElement;
+ private StructuredQName name;
+ private Expression select = null;
+ private SequenceType declaredType = null;
+ private SequenceType inferredType = null;
+ protected String constantText = null;
+ protected SlotManager slotManager = null; // used only for global variable declarations
+ protected GeneralVariable compiledVariable = null;
+ private boolean textonly;
+
+ private int properties;
+
+ public static final int PRIVATE = 1;
+ public static final int GLOBAL = 2;
+ public static final int PARAM = 4;
+ public static final int TUNNEL = 8;
+ public static final int REQUIRED = 16;
+ public static final int IMPLICITLY_REQUIRED = 32;
+ public static final int ASSIGNABLE = 64;
+ public static final int SELECT = 128;
+ public static final int AS = 256;
+ public static final int DISALLOWS_CONTENT = 512;
+ public static final int GROUP = 1024;
+ public static final int STATIC = 2048;
+
+ // List of VariableReference objects that reference this XSLVariableDeclaration
+ private List<BindingReference> references = new ArrayList<BindingReference>(10);
+
+ public SourceBinding(StyleElement sourceElement) {
+ this.sourceElement = sourceElement;
+ }
+
+
+ public void prepareAttributes(int permittedAttributes) throws XPathException {
+
+ AttributeCollection atts = sourceElement.getAttributeList();
+
+ String selectAtt = null;
+ String asAtt = null;
+ String requiredAtt = null;
+ String tunnelAtt = null;
+ String assignableAtt = null;
+ String staticAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.NAME)) {
+ if (name == null || name.equals(errorName())) {
+ processVariableName(atts.getValue(a));
+ }
+ } else if (f.equals(StandardNames.SELECT)) {
+ if ((permittedAttributes & SELECT) != 0) {
+ selectAtt = atts.getValue(a);
+ } else {
+ sourceElement.compileError("The select attribute is not permitted on a function parameter", "XTSE0760");
+ }
+ } else if (f.equals(StandardNames.AS) && ((permittedAttributes & AS) != 0)) {
+ asAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.REQUIRED) && ((permittedAttributes & REQUIRED) != 0)) {
+ requiredAtt = Whitespace.trim(atts.getValue(a)) ;
+ } else if (f.equals(StandardNames.TUNNEL)) {
+ tunnelAtt = Whitespace.trim(atts.getValue(a)) ;
+ } else if (f.equals(StandardNames.STATIC) && ((permittedAttributes & STATIC) != 0)) {
+ staticAtt = Whitespace.trim(atts.getValue(a)) ;
+ } else if (atts.getLocalName(a).equals("assignable") && NamespaceConstant.SAXON.equals(atts.getURI(a)) && ((permittedAttributes & ASSIGNABLE) != 0)) {
+ assignableAtt = Whitespace.trim(atts.getValue(a)) ;
+ } else {
+ sourceElement.checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (name==null) {
+ sourceElement.reportAbsence("name");
+ name = errorName();
+ }
+
+ if (selectAtt!=null) {
+ select = sourceElement.makeExpression(selectAtt);
+ }
+
+ if (requiredAtt!=null) {
+ if (requiredAtt.equals("yes")) {
+ setProperty(REQUIRED, true);
+ } else if (requiredAtt.equals("no")) {
+ setProperty(REQUIRED, false);
+ } else {
+ sourceElement.compileError("The attribute 'required' must be set to 'yes' or 'no'", "XTSE0020");
+ }
+ }
+
+ if (tunnelAtt!=null) {
+ if (tunnelAtt.equals("yes")) {
+ if ((permittedAttributes & TUNNEL) == 0) {
+ sourceElement.compileError("The only permitted value of the 'tunnel' attribute is 'no'", "XTSE0020");
+ } else {
+ setProperty(TUNNEL, true);
+ }
+ } else if (tunnelAtt.equals("no")) {
+ setProperty(TUNNEL, false);
+ } else {
+ sourceElement.compileError("The attribute 'tunnel' must be set to 'yes' or 'no'", "XTSE0020");
+ }
+ }
+
+ if (assignableAtt!=null) {
+ if (assignableAtt.equals("yes")) {
+ setProperty(ASSIGNABLE, true);
+ } else if (assignableAtt.equals("no")) {
+ setProperty(ASSIGNABLE, false);
+ } else {
+ sourceElement.compileError("The attribute 'saxon:assignable' must be set to 'yes' or 'no'", "XTSE0020");
+ }
+ }
+
+ if (staticAtt!=null) {
+ if(sourceElement.isXslt30Processor()) {
+ if (staticAtt.equals("yes")) {
+ setProperty(STATIC, true);
+ setProperty(SourceBinding.DISALLOWS_CONTENT, true);
+ } else if (staticAtt.equals("no")) {
+ setProperty(STATIC, false);
+ }
+ } else if(!sourceElement.forwardsCompatibleModeIsEnabled()) {
+ sourceElement.compileError("The attribute 'static' is not allowed", "XTSE0020");
+ }
+ }
+
+ if (asAtt!=null) {
+ declaredType = sourceElement.makeSequenceType(asAtt);
+ }
+ }
+
+ /**
+ * Get the declaration in the stylesheet
+ * @return the node in the stylesheet containing this variable binding
+ */
+
+ public StyleElement getSourceElement() {
+ return sourceElement;
+ }
+
+ /**
+ * Set the name of the variable
+ * @param name the name of the variable as a QName
+ */
+
+ public void setVariableQName(StructuredQName name) {
+ this.name = name;
+ }
+
+ /**
+ * Process the QName of the variable. Validate the name and place it in the "name" field;
+ * if invalid, construct an error message and place a dummy name in the "name" field for
+ * recovery purposes.
+ * @param nameAttribute the lexical QName
+ * @throws XPathException if the name is invalid
+ */
+
+ private void processVariableName(String nameAttribute) throws XPathException {
+ try {
+ if (nameAttribute != null) {
+ name = sourceElement.makeQName(nameAttribute);
+ }
+ } catch (NamespaceException err) {
+ name = errorName();
+ throw new XPathException("Invalid variable name: " + err.getMessage(), "XTSE0020");
+ } catch (XPathException err) {
+ name = errorName();
+ throw new XPathException("Invalid variable name: " + err.getMessage() +
+ (nameAttribute.startsWith("$") ? " (No '$' sign needed)" : ""), "XTSE0020");
+ }
+ }
+
+ /**
+ * Fallback name to be returned if there is anything wrong with the variable's real name
+ * (after reporting an error)
+ * @return a fallback name
+ */
+
+ private StructuredQName errorName() {
+ return new StructuredQName("saxon", NamespaceConstant.SAXON, "error-variable-name");
+ }
+
+ /**
+ * Validate the declartaion
+ */
+
+ public void validate() throws XPathException {
+ if (select!=null && sourceElement.hasChildNodes()) {
+ sourceElement.compileError("An " + name.getDisplayName() + " element with a select attribute must be empty", "XTSE0620");
+ }
+ if (hasProperty(DISALLOWS_CONTENT) && sourceElement.hasChildNodes()) {
+ sourceElement.compileError("The xsl:param child of xsl:function must have no content", "XTSE0620");
+ }
+ }
+
+ /**
+ * Hook to allow additional validation of a parent element immediately after its
+ * children have been validated.
+ */
+
+ public void postValidate() throws XPathException {
+ checkAgainstRequiredType(declaredType);
+
+ if (select==null && !hasProperty(DISALLOWS_CONTENT)) {
+ textonly = true;
+ AxisIterator kids = sourceElement.iterateAxis(AxisInfo.CHILD);
+ NodeInfo first = kids.next();
+ if (first == null) {
+ if (declaredType == null) {
+ select = new StringLiteral(StringValue.EMPTY_STRING);
+ } else {
+ if (sourceElement instanceof XSLLocalParam || sourceElement instanceof XSLGlobalParam) {
+ if (!hasProperty(REQUIRED)) {
+ if (Cardinality.allowsZero(declaredType.getCardinality())) {
+ select = Literal.makeEmptySequence();
+ } else {
+ // The implicit default value () is not valid for the required type, so
+ // it is treated as if there is no default
+ setProperty(IMPLICITLY_REQUIRED, true);
+ }
+ }
+ } else {
+ if (Cardinality.allowsZero(declaredType.getCardinality())) {
+ select = Literal.makeEmptySequence();
+ } else {
+ sourceElement.compileError("The implicit value () is not valid for the declared type", "XTTE0570");
+ }
+ }
+ }
+ } else {
+ if (kids.next() == null) {
+ // there is exactly one child node
+ if (first.getNodeKind() == Type.TEXT) {
+ // it is a text node: optimize for this case
+ constantText = first.getStringValue();
+ }
+ }
+
+ // Determine if the temporary tree can only contain text nodes
+ textonly = (sourceElement.getCommonChildItemType() == NodeKindTest.TEXT);
+ }
+ }
+ select = sourceElement.typeCheck("select", select);
+ }
+
+ public boolean isStatic(){
+ return hasProperty(STATIC);
+ }
+
+ /**
+ * Check the supplied select expression against the required type.
+ * @param required The type required by the variable declaration, or in the case
+ * of xsl:with-param, the signature of the called template
+ * @throws net.sf.saxon.trans.XPathException if an error is detected
+ */
+
+ public void checkAgainstRequiredType(SequenceType required)
+ throws XPathException {
+ try {
+
+ if (required!=null) {
+ // check that the expression is consistent with the required type
+ if (select != null) {
+ int category = RoleLocator.VARIABLE;
+ String errorCode = "XTTE0570";
+ if (sourceElement instanceof XSLLocalParam) {
+ category = RoleLocator.PARAM;
+ errorCode = "XTTE0600";
+ } else if (sourceElement instanceof XSLWithParam) {
+ category = RoleLocator.PARAM;
+ errorCode = "XTTE0590";
+ }
+ RoleLocator role = new RoleLocator(category, name.getDisplayName(), 0);
+ //role.setSourceLocator(new ExpressionLocation(this));
+ role.setErrorCode(errorCode);
+ select = TypeChecker.staticTypeCheck(select, required, false, role, sourceElement.makeExpressionVisitor());
+ } else {
+ // do the check later
+ }
+ }
+ } catch (XPathException err) {
+ err.setLocator(sourceElement); // because the expression wasn't yet linked into the module
+ sourceElement.compileError(err);
+ select = new ErrorExpression(err);
+ }
+ }
+
+
+ /**
+ * Get the name of the variable
+ * @return the variable's name
+ */
+
+ public StructuredQName getVariableQName() {
+ if (name == null) {
+ try {
+ processVariableName(sourceElement.getAttributeValue("", "name"));
+ } catch (XPathException e) {
+ return errorName();
+ }
+ }
+ return name;
+ }
+
+ /**
+ * Set a boolean property of the variable
+ * @param prop the property to be set (e.g. {@link #TUNNEL})
+ * @param flag true to set the property on, false to set it off
+ */
+
+ public void setProperty(int prop, boolean flag) {
+ if (flag) {
+ properties |= prop;
+ } else {
+ properties &= (~prop);
+ }
+ }
+
+ /**
+ * Get a boolean property of the variable
+ * @param prop the property whose value is required
+ * @return true if the variable has the specified property, otherwise false
+ */
+
+ public boolean hasProperty(int prop) {
+ return (properties & prop) != 0;
+ }
+
+ /**
+ * Get all the known references to this variable
+ * @return the list of references
+ */
+
+ public List<BindingReference> getReferences() {
+ return references;
+ }
+
+ /**
+ * Get the SlotManager associated with this stylesheet construct. The SlotManager contains the
+ * information needed to manage the local stack frames used by run-time instances of the code.
+ * @return the associated SlotManager object
+ */
+
+ public SlotManager getSlotManager() {
+ return slotManager;
+ }
+
+ /**
+ * If the element contains a sequence constructor, convert this to an expression and assign it to the
+ * select attribute
+ * @param exec the executable
+ * @param decl the declaration being compiled
+ * @throws XPathException if a static error is found, for example a type error
+ */
+
+ public void handleSequenceConstructor(Executable exec, Declaration decl) throws XPathException {
+ // handle the "temporary tree" case by creating a Document sub-instruction
+ // to construct and return a document node.
+ if (sourceElement.hasChildNodes()) {
+ if (declaredType==null) {
+ DocumentInstr doc = new DocumentInstr(textonly, constantText, sourceElement.getBaseURI());
+ Expression b = sourceElement.compileSequenceConstructor(
+ exec, decl, sourceElement.iterateAxis(AxisInfo.CHILD), true);
+ if (b == null) {
+ b = Literal.makeEmptySequence();
+ }
+ doc.setContentExpression(b);
+ select = doc;
+ } else {
+ select = sourceElement.compileSequenceConstructor(
+ exec, decl, sourceElement.iterateAxis(AxisInfo.CHILD), true);
+ if (select == null) {
+ select = Literal.makeEmptySequence();
+ }
+ try {
+ assert select != null;
+ select.setContainer(sourceElement);
+ RoleLocator role =
+ new RoleLocator(RoleLocator.VARIABLE, name.getDisplayName(), 0);
+ role.setErrorCode("XTTE0570");
+ //role.setSourceLocator(new ExpressionLocation(this));
+ select = sourceElement.makeExpressionVisitor().simplify(select);
+ select = TypeChecker.staticTypeCheck(select, declaredType, false, role, sourceElement.makeExpressionVisitor());
+ } catch (XPathException err) {
+ err.setLocator(sourceElement);
+ sourceElement.compileError(err);
+ select = new ErrorExpression(err);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the type actually declared for the attribute
+ * @return the type appearing in the "as" attribute, or null if the attribute was absent
+ */
+
+ public SequenceType getDeclaredType() {
+ if (declaredType == null) {
+ // may be handling a forwards reference - see hof-038
+ String asAtt = sourceElement.getAttributeValue("", "as");
+ if (asAtt == null) {
+ return null;
+ } else {
+ try {
+ declaredType = sourceElement.makeSequenceType(asAtt);
+ } catch (XPathException err) {
+ // the error will be reported when we get round to processing the function declaration
+ }
+ }
+
+ }
+ return declaredType;
+ }
+
+ /**
+ * Get the select expression actually appearing in the variable declaration
+ * @return the select expression as it appears, or null if it is absent
+ */
+
+ public Expression getSelectExpression() {
+ return select;
+ }
+
+ /**
+ * Get the best available static type of the variable.
+ * @param useContentRules set to true if the standard rules for xsl:variable and similar elements apply,
+ * whereby the element's contained sequence constructor substitutes for the select attribute
+ * @return the static type declared for the variable, or inferred from its initialization
+ */
+
+ public SequenceType getInferredType(boolean useContentRules) {
+ if (inferredType != null) {
+ return inferredType;
+ }
+ if (hasProperty(PARAM) || hasProperty(ASSIGNABLE)) {
+ return (inferredType = (declaredType == null ? SequenceType.ANY_SEQUENCE : declaredType));
+ }
+ if (select != null) {
+ TypeHierarchy th = sourceElement.getConfiguration().getTypeHierarchy();
+ if (Literal.isEmptySequence(select)) {
+ // returning Type.EMPTY gives problems with static type checking
+ return (inferredType = (declaredType == null ? SequenceType.ANY_SEQUENCE : declaredType));
+ }
+ ItemType actual = select.getItemType(th);
+ int card = select.getCardinality();
+ if (declaredType != null) {
+ if (!th.isSubType(actual, declaredType.getPrimaryType())) {
+ actual = declaredType.getPrimaryType();
+ }
+ if (!Cardinality.subsumes(declaredType.getCardinality(), card)) {
+ card = declaredType.getCardinality();
+ }
+ }
+ inferredType = SequenceType.makeSequenceType(actual, card);
+ return inferredType;
+ }
+ if (useContentRules) {
+ if (sourceElement.hasChildNodes()) {
+ if (declaredType == null) {
+ return SequenceType.makeSequenceType(NodeKindTest.DOCUMENT, StaticProperty.EXACTLY_ONE);
+ } else {
+ return declaredType;
+ }
+ } else {
+ if (declaredType == null) {
+ // no select attribute or content: value is an empty string
+ return SequenceType.SINGLE_STRING;
+ } else {
+ return declaredType;
+ }
+ }
+ }
+ return declaredType;
+ }
+
+ /**
+ * Method called by VariableReference to register the variable reference for
+ * subsequent fixup
+ * @param ref the variable reference being registered
+ */
+
+ public void registerReference(BindingReference ref) {
+ references.add(ref);
+ }
+
+ /**
+ * Notify all references to this variable of the data type
+ */
+
+ public void fixupReferences() throws XPathException {
+ final SequenceType type = getInferredType(true);
+ final TypeHierarchy th = sourceElement.getConfiguration().getTypeHierarchy();
+ GroundedValue constantValue = null;
+ int properties = 0;
+ if (!hasProperty(ASSIGNABLE) && !hasProperty(PARAM)) {
+ if (select instanceof Literal) {
+ // we can't rely on the constant value because it hasn't yet been type-checked,
+ // which could change it (eg by numeric promotion). Rather than attempt all the type-checking
+ // now, we do a quick check. See test bug64
+ int relation = th.relationship(select.getItemType(th), type.getPrimaryType());
+ if (relation == TypeHierarchy.SAME_TYPE || relation == TypeHierarchy.SUBSUMED_BY) {
+ constantValue = ((Literal) select).getValue();
+ }
+ }
+ if (select != null) {
+ properties = select.getSpecialProperties();
+ }
+ }
+ for (BindingReference reference : references) {
+ reference.setStaticType(type, constantValue, properties);
+ }
+ }
+
+ /**
+ * Notify all variable references of the Binding instruction
+ * @param binding the Binding that represents this variable declaration in the executable code tree
+ */
+
+ protected void fixupBinding(Binding binding) {
+ for (BindingReference reference : references) {
+ (reference).fixup(binding);
+ }
+ }
+
+ /**
+ * Set the number of references to this variable. This code is invoked only for a global variable,
+ * and only if there is at least one reference.
+ * @param var the variable
+ */
+
+ protected void setReferenceCount(GeneralVariable var) {
+ var.setReferenceCount(10); // TODO: temporary
+ }
+
+ /**
+ * Get the compiled variable
+ * @return the compiled variable if it has been compiled, or null otherwise
+ */
+
+ /*@Nullable*/ public GeneralVariable getCompiledVariable() {
+ return compiledVariable;
+ }
+}
+
diff --git a/sf/saxon/style/StyleElement.java b/sf/saxon/style/StyleElement.java
new file mode 100644
index 0000000..1be1825
--- /dev/null
+++ b/sf/saxon/style/StyleElement.java
@@ -0,0 +1,2240 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.expr.sort.SortKeyDefinition;
+import net.sf.saxon.functions.Current;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trace.InstructionInfo;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.linked.ElementImpl;
+import net.sf.saxon.tree.linked.TextImpl;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.NamespaceIterator;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+import org.xml.sax.Locator;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * Abstract superclass for all element nodes in the stylesheet.
+ * <p>Note: this class implements Locator. The element retains information about its own location
+ * in the stylesheet, which is useful when an XSLT static error is found.</p>
+ */
+
+public abstract class StyleElement extends ElementImpl
+implements Locator, Container, InstructionInfo {
+
+ /*@Nullable*/ protected String[] extensionNamespaces = null; // a list of URIs
+ private String[] excludedNamespaces = null; // a list of URIs
+ protected DecimalValue version = null; // the effective version of this element
+ protected ExpressionContext staticContext = null;
+ protected XPathException validationError = null;
+ protected int reportingCircumstances = REPORT_ALWAYS;
+ protected String defaultXPathNamespace = null;
+ protected String defaultCollationName = null;
+ protected boolean expandText = false;
+ private boolean explaining = false;
+ // true if saxon:explain="yes"
+ private StructuredQName objectName;
+ // for instructions that define an XSLT named object, the name of that object
+ private XSLStylesheet containingStylesheet;
+
+ // Conditions under which an error is to be reported
+
+ public static final int REPORT_ALWAYS = 1;
+ public static final int REPORT_UNLESS_FORWARDS_COMPATIBLE = 2;
+ public static final int REPORT_IF_INSTANTIATED = 3;
+ public static final int REPORT_UNLESS_FALLBACK_AVAILABLE = 4;
+
+ protected int actionsCompleted = 0;
+ public static final int ACTION_VALIDATE = 1;
+ public static final int ACTION_COMPILE = 2;
+ public static final int ACTION_TYPECHECK = 4;
+ public static final int ACTION_OPTIMIZE = 8;
+ public static final int ACTION_FIXUP = 16;
+ public static final int ACTION_PROCESS_ATTRIBUTES = 32;
+
+
+ /**
+ * Constructor
+ */
+
+ public StyleElement() {
+ }
+
+ /*@NotNull*/
+ public Executable getExecutable() {
+ return getPreparedStylesheet();
+ }
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ */
+
+ public LocationProvider getLocationProvider() {
+ return getPreparedStylesheet().getLocationMap();
+ }
+
+ /**
+ * Get the static context for expressions on this element
+ * @return the static context
+ */
+
+ public StaticContext getStaticContext() {
+ if (staticContext == null) {
+ staticContext = new ExpressionContext(this);
+ }
+ return staticContext;
+ }
+
+ /**
+ * Get the granularity of the container.
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 1;
+ }
+
+ /**
+ * Make an expression visitor
+ * @return the expression visitor
+ */
+
+ public ExpressionVisitor makeExpressionVisitor() {
+ return ExpressionVisitor.make(getStaticContext(), getPreparedStylesheet());
+ }
+
+ /**
+ * Determine whether saxon:explain has been set to "yes"
+ * @return true if saxon:explain has been set to "yes" on this element
+ */
+
+ protected boolean isExplaining() {
+ return explaining;
+ }
+
+ /**
+ * Make this node a substitute for a temporary one previously added to the tree. See
+ * StyleNodeFactory for details. "A node like the other one in all things but its class".
+ * Note that at this stage, the node will not yet be known to its parent, though it will
+ * contain a reference to its parent; and it will have no children.
+ * @param temp the element which this one is substituting for
+ */
+
+ public void substituteFor(StyleElement temp) {
+ setRawParent(temp.getRawParent());
+ setAttributeList(temp.getAttributeList());
+ setNamespaceList(temp.getNamespaceList());
+ setNameCode(temp.getNameCode());
+ setRawSequenceNumber(temp.getRawSequenceNumber());
+ extensionNamespaces = temp.extensionNamespaces;
+ excludedNamespaces = temp.excludedNamespaces;
+ version = temp.version;
+ staticContext = temp.staticContext;
+ validationError = temp.validationError;
+ reportingCircumstances = temp.reportingCircumstances;
+ //lineNumber = temp.lineNumber;
+ }
+
+ /**
+ * Set a validation error. This is an error detected during construction of this element on the
+ * stylesheet, but which is not to be reported until later.
+ * @param reason the details of the error
+ * @param circumstances a code identifying the circumstances under which the error is to be reported
+ */
+
+ public void setValidationError(TransformerException reason,
+ int circumstances) {
+ validationError = XPathException.makeXPathException(reason);
+ reportingCircumstances = circumstances;
+ }
+
+ /**
+ * Ask whether this node is an instruction. The default implementation says it isn't.
+ * @return true if this element is an instruction
+ */
+
+ public boolean isInstruction() {
+ return false;
+ }
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import). The default implementation returns false
+ * @return true if the element is a permitted child of xsl:stylesheet or xsl:transform
+ */
+
+ public boolean isDeclaration() {
+ return false;
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction). Default implementation returns Type.ITEM, indicating
+ * that we don't know, it might be anything. Returns null in the case of an element
+ * such as xsl:sort or xsl:variable that can appear in a sequence constructor but
+ * contributes nothing to the result sequence.
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Get the most general type of item returned by the children of this instruction
+ * @return the lowest common supertype of the item types returned by the children
+ */
+
+ protected ItemType getCommonChildItemType() {
+ final TypeHierarchy th = getConfiguration().getTypeHierarchy();
+ ItemType t = ErrorType.getInstance();
+ AxisIterator children = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo next = children.next();
+ if (next == null) {
+ return t;
+ }
+ if (next instanceof StyleElement) {
+ ItemType ret = ((StyleElement)next).getReturnedItemType();
+ if (ret != null) {
+ t = Type.getCommonSuperType(t, ret, th);
+ }
+ } else {
+ t = Type.getCommonSuperType(t, NodeKindTest.TEXT, th);
+ }
+ if (t == AnyItemType.getInstance()) {
+ return t; // no point looking any further
+ }
+ }
+ }
+
+ /**
+ * Mark tail-recursive calls on templates and functions.
+ * For most instructions, this returns false.
+ * @return true if one or more tail calls were identified
+ */
+
+ protected boolean markTailCalls() {
+ return false;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a sequence constructor
+ * @return true if this instruction is allowed to contain a sequence constructor
+ */
+
+ protected boolean mayContainSequenceConstructor() {
+ return false;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain an xsl:fallback
+ * instruction. Note that this is only relevant if the element is an instruction.
+ * @return true if this element is allowed to contain an xsl:fallback
+ */
+
+ protected boolean mayContainFallback() {
+ return mayContainSequenceConstructor();
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain an xsl:param element
+ * @param attName if null, the method tests whether an xsl:param child is allowed.
+ * If non-null, it tests whether an xsl:param child with the given attribute name is allowed
+ * @return true if this element is allowed to contain an xsl:param
+ */
+
+ protected boolean mayContainParam(String attName) {
+ return false;
+ }
+
+ /**
+ * Get the containing XSLStylesheet element
+ * @return the XSLStylesheet element representing the outermost element of the containing
+ * stylesheet module. Exceptionally, return null if there is no containing XSLStylesheet element
+ */
+
+ public XSLStylesheet getContainingStylesheet() {
+ if (containingStylesheet == null) {
+ if (this instanceof XSLStylesheet) {
+ containingStylesheet = (XSLStylesheet)this;
+ } else {
+ NodeInfo parent = getParent();
+ if (parent instanceof StyleElement) {
+ containingStylesheet = ((StyleElement)parent).getContainingStylesheet();
+ } else {
+ // this can happen when early errors are detected in a simplified stylesheet,
+ return null;
+ }
+ }
+ }
+ return containingStylesheet;
+ }
+
+ /**
+ * Make a structured QName, using this Element as the context for namespace resolution.
+ * If the name is unprefixed, the default namespace is <b>not</b> used.
+ * @param lexicalQName The lexical QName as written, in the form "[prefix:]localname". The name must have
+ * already been validated as a syntactically-correct QName. Leading and trailing whitespace
+ * will be trimmed. If XSLT 3.0 is enabled, then the EQName syntax "Q{uri}local" is also
+ * accepted.
+ * @return the StructuredQName representation of this lexical QName
+ * @throws XPathException if the qname is not a lexically-valid QName, or if the name
+ * is in a reserved namespace.
+ * @throws NamespaceException if the prefix of the qname has not been declared
+ */
+
+ public final StructuredQName makeQName(String lexicalQName)
+ throws XPathException, NamespaceException {
+
+ StructuredQName qName;
+ try {
+ qName = StructuredQName.fromLexicalQName(lexicalQName, false,
+ isXslt30Processor(), getConfiguration().getNameChecker(), this);
+ } catch (XPathException e) {
+ e.setIsStaticError(true);
+ String code = e.getErrorCodeLocalPart();
+ if ("FONS0004".equals(code)) {
+ e.setErrorCode("XTSE0280");
+ } else if ("FOCA0002".equals(code)) {
+ e.setErrorCode("XTSE0020");
+ } else if (code == null) {
+ e.setErrorCode("XTSE0020");
+ }
+ throw e;
+ }
+ if (NamespaceConstant.isReserved(qName.getURI())) {
+ XPathException err = new XPathException("Namespace prefix " +
+ qName.getPrefix() + " refers to a reserved namespace");
+ err.setIsStaticError(true);
+ err.setErrorCode("XTSE0080");
+ throw err;
+ }
+ return qName;
+ }
+
+
+ /**
+ * Make a NamespaceContext object representing the list of in-scope namespaces. This will
+ * be a copy of the namespace context with no references to objects in the stylesheet tree,
+ * so that it can be kept until run-time without locking the tree down in memory.
+ * @return a copy of the namespace context
+ */
+
+ public SavedNamespaceContext makeNamespaceContext() {
+ return new SavedNamespaceContext(NamespaceIterator.iterateNamespaces(this));
+ }
+
+ /**
+ * Get the namespace context of the instruction.
+ * @return the namespace context. This method does not make a copy of the namespace context,
+ * so a reference to the returned NamespaceResolver will lock the stylesheet tree in memory.
+ */
+
+ public NamespaceResolver getNamespaceResolver() {
+ return this;
+ }
+
+ /**
+ * Process the attributes of this element and all its children
+ * @throws XPathException in the event of a static error being detected
+ */
+
+ protected void processAllAttributes() throws XPathException {
+ processDefaultCollationAttribute();
+ staticContext = new ExpressionContext(this);
+ processAttributes();
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ return;
+ }
+ if (child instanceof StyleElement) {
+ ((StyleElement)child).processAllAttributes();
+ if (((StyleElement)child).explaining) {
+ // saxon:explain on any element in a template/function now causes an explanation at the
+ // level of the template/function
+ explaining = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Process the standard attributes such as [xsl:]default-collation
+ * @param namespace either "" to find the attributes in the null namespace,
+ * or NamespaceConstant.XSLT to find them in the XSLT namespace
+ * @throws net.sf.saxon.trans.XPathException if any of the standard attributes is incorrect
+ */
+
+ public void processStandardAttributes(String namespace) throws XPathException {
+ //processDefaultCollationAttribute(namespace);
+ processExtensionElementAttribute(namespace);
+ processExcludedNamespaces(namespace);
+ processVersionAttribute(namespace);
+ processDefaultXPathNamespaceAttribute(namespace);
+ processExpandTextAttribute(namespace);
+ }
+
+ /**
+ * Get an attribute value given the Clark name of the attribute (that is,
+ * the name in {uri}local format).
+ * @param clarkName the name of the attribute in {uri}local format
+ * @return the value of the attribute if it exists, or null otherwise
+ */
+
+ public String getAttributeValue(String clarkName) {
+ NodeName nn = FingerprintedQName.fromClarkName(clarkName);
+ return getAttributeValue(nn.getURI(), nn.getLocalPart());
+ }
+
+ /**
+ * Process the attribute list for the element. This is a wrapper method that calls
+ * prepareAttributes (provided in the subclass) and traps any exceptions
+ * @throws net.sf.saxon.trans.XPathException if a static error is detected
+ */
+
+ protected final void processAttributes() throws XPathException {
+ try {
+ prepareAttributes();
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ }
+
+ /**
+ * Check whether an unknown attribute is permitted.
+ * @param nc The name code of the attribute name
+ * @throws XPathException (and reports the error) if this is an attribute
+ * that is not permitted on the containing element
+ */
+
+ protected void checkUnknownAttribute(NodeName nc) throws XPathException {
+
+ String attributeURI = nc.getURI();
+ String elementURI = getURI();
+ String clarkName = nc.getStructuredQName().getClarkName();
+
+ if (clarkName.equals(StandardNames.SAXON_EXPLAIN)) {
+ explaining = "yes".equals(getAttributeValue(clarkName));
+ }
+
+ if (forwardsCompatibleModeIsEnabled()) {
+ // then unknown attributes are permitted and ignored
+ return;
+ }
+
+ // allow xsl:extension-element-prefixes etc on an extension element
+
+ if (isInstruction() &&
+ attributeURI.equals(NamespaceConstant.XSLT) &&
+ !(elementURI.equals(NamespaceConstant.XSLT)) &&
+ (clarkName.endsWith("}default-collation") ||
+ clarkName.endsWith("}xpath-default-namespace") ||
+ clarkName.endsWith("}expand-text") ||
+ clarkName.endsWith("}extension-element-prefixes") ||
+ clarkName.endsWith("}exclude-result-prefixes") ||
+ clarkName.endsWith("}version") ||
+ clarkName.endsWith("}use-when"))) {
+ return;
+ }
+
+ // allow standard attributes on an XSLT element
+
+ if (elementURI.equals(NamespaceConstant.XSLT) &&
+ (clarkName.equals(StandardNames.DEFAULT_COLLATION) ||
+ clarkName.equals("expand-text") ||
+ clarkName.equals(StandardNames.XPATH_DEFAULT_NAMESPACE) ||
+ clarkName.equals(StandardNames.EXTENSION_ELEMENT_PREFIXES) ||
+ clarkName.equals(StandardNames.EXCLUDE_RESULT_PREFIXES) ||
+ clarkName.equals(StandardNames.VERSION) ||
+ clarkName.equals(StandardNames.USE_WHEN) ||
+ clarkName.equals(StandardNames.STATIC))) {
+ return;
+ }
+
+ if ("".equals(attributeURI) || NamespaceConstant.XSLT.equals(attributeURI)) {
+ compileError("Attribute " + Err.wrap(nc.getDisplayName(), Err.ATTRIBUTE) +
+ " is not allowed on element " + Err.wrap(getDisplayName(), Err.ELEMENT), "XTSE0090");
+ }
+ }
+
+
+ /**
+ * Set the attribute list for the element. This is called to process the attributes (note
+ * the distinction from processAttributes in the superclass).
+ * Must be supplied in a subclass
+ * @throws net.sf.saxon.trans.XPathException if a static error is detected
+ */
+
+ protected abstract void prepareAttributes() throws XPathException;
+
+ /**
+ * Find the last child instruction of this instruction. Returns null if
+ * there are no child instructions, or if the last child is a text node.
+ * @return the last child instruction, or null if there are no child instructions
+ */
+
+ protected StyleElement getLastChildInstruction() {
+ StyleElement last = null;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ return last;
+ }
+ if (child instanceof StyleElement) {
+ last = (StyleElement)child;
+ } else {
+ last = null;
+ }
+ }
+ }
+
+ /**
+ * Compile an XPath expression in the context of this stylesheet element
+ * @param expression the source text of the XPath expression
+ * @return the compiled expression tree for the XPath expression
+ * @throws net.sf.saxon.trans.XPathException if a static error is detected in the XPath expression
+ */
+
+ public Expression makeExpression(String expression) throws XPathException {
+ try {
+ return ExpressionTool.make(expression,
+ staticContext,
+ this, 0, Token.EOF,
+ getLineNumber(),
+ getPreparedStylesheet().getCompilerInfo().getCodeInjector());
+ } catch (XPathException err) {
+ err.setLocator(this);
+ if (err.isReportableStatically()) {
+ compileError(err);
+ }
+ ErrorExpression erexp = new ErrorExpression(err);
+ erexp.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+ erexp.setContainer(this);
+ return erexp;
+ }
+ }
+
+ /**
+ * Make a pattern in the context of this stylesheet element
+ * @param pattern the source text of the pattern
+ * @return the compiled pattern
+ * @throws net.sf.saxon.trans.XPathException if a static error is detected in the pattern
+ */
+
+ public Pattern makePattern(String pattern)
+ throws XPathException {
+ try {
+ return Pattern.make(pattern, staticContext, getPreparedStylesheet());
+ } catch (XPathException err) {
+ if ("XPST0003".equals(err.getErrorCodeLocalPart())) {
+ err.setErrorCode("XTSE0340");
+ }
+ compileError(err);
+ return new ItemTypePattern(AnyNodeTest.getInstance());
+ }
+ }
+
+ /**
+ * Make an attribute value template in the context of this stylesheet element
+ * @param expression the source text of the attribute value template
+ * @return a compiled XPath expression that computes the value of the attribute (including
+ * concatenating the results of embedded expressions with any surrounding fixed text)
+ * @throws net.sf.saxon.trans.XPathException if a static error is detected in the AVT
+ */
+
+ protected Expression makeAttributeValueTemplate(String expression)
+ throws XPathException {
+ try {
+ return AttributeValueTemplate.make(expression, getLineNumber(), staticContext);
+ } catch (XPathException err) {
+ compileError(err);
+ return new StringLiteral(expression);
+ }
+ }
+
+
+ /**
+ * Check the value of an attribute, as supplied statically
+ * @param name the name of the attribute
+ * @param value the value of the attribute
+ * @param avt set to true if the value is permitted to be an attribute value template
+ * @param allowed list of permitted values, which must be in alphabetical order
+ * @throws net.sf.saxon.trans.XPathException if the value given for the attribute is not a permitted value
+ */
+
+ protected void checkAttributeValue(String name, String value, boolean avt, String[] allowed) throws XPathException {
+ if (avt && value.contains("{")) {
+ return;
+ }
+ if (Arrays.binarySearch(allowed, value) < 0) {
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ sb.append("Invalid value for ");
+ sb.append("@");
+ sb.append(name);
+ sb.append(". Value must be one of (");
+ for (int i=0; i<allowed.length; i++) {
+ sb.append((i==0 ? "" : "|"));
+ sb.append(allowed[i]);
+ }
+ sb.append(")");
+ compileError(sb.toString(), "XTSE0020");
+ }
+ }
+
+ protected final static String[] YES_NO = new String[]{"no", "yes"};
+
+ /**
+ * Process an attribute whose value is a SequenceType
+ * @param sequenceType the source text of the attribute
+ * @return the processed sequence type
+ * @throws XPathException if the syntax is invalid or for example if it refers to a type
+ * that is not in the static context
+ */
+
+ public SequenceType makeSequenceType(String sequenceType)
+ throws XPathException {
+ getStaticContext();
+ try {
+ ExpressionParser parser =
+ getConfiguration().newExpressionParser("XP", false, staticContext.getXPathLanguageLevel());
+ parser.setLanguage(ExpressionParser.XPATH, staticContext.getXPathLanguageLevel());
+ return parser.parseSequenceType(sequenceType, staticContext);
+ } catch (XPathException err) {
+ compileError(err);
+ // recovery path after reporting an error, e.g. undeclared namespace prefix
+ return SequenceType.ANY_SEQUENCE;
+ }
+ }
+
+ /**
+ * Process the [xsl:]extension-element-prefixes attribute if there is one
+ * @param ns the namespace URI of the attribute - either the XSLT namespace or "" for the null namespace
+ * @throws net.sf.saxon.trans.XPathException if the value of the attribute is invalid
+ */
+
+ protected void processExtensionElementAttribute(String ns)
+ throws XPathException {
+ String ext = getAttributeValue(ns, StandardNames.EXTENSION_ELEMENT_PREFIXES);
+ if (ext != null) {
+ // go round twice, once to count the values and next to add them to the array
+ int count = 0;
+ StringTokenizer st1 = new StringTokenizer(ext, " \t\n\r", false);
+ while (st1.hasMoreTokens()) {
+ st1.nextToken();
+ count++;
+ }
+ extensionNamespaces = new String[count];
+ count = 0;
+ StringTokenizer st2 = new StringTokenizer(ext, " \t\n\r", false);
+ while (st2.hasMoreTokens()) {
+ String s = st2.nextToken();
+ if ("#default".equals(s)) {
+ s = "";
+ }
+ String uri = getURIForPrefix(s, false);
+ if (uri == null) {
+ extensionNamespaces = null;
+ compileError("Namespace prefix " + s + " is undeclared", "XTSE1430");
+ } else {
+ assert extensionNamespaces != null;
+ extensionNamespaces[count++] = uri;
+ }
+ }
+ }
+ }
+
+ /**
+ * Process the [xsl:]exclude-result-prefixes attribute if there is one
+ * @param ns the namespace URI of the attribute required, either the XSLT namespace or ""
+ * @throws net.sf.saxon.trans.XPathException if the value of the attribute is invalid
+ */
+
+ protected void processExcludedNamespaces(String ns)
+ throws XPathException {
+ String ext = getAttributeValue(ns, StandardNames.EXCLUDE_RESULT_PREFIXES);
+ if (ext != null) {
+ if ("#all".equals(Whitespace.trim(ext))) {
+ Iterator<NamespaceBinding> codes = NamespaceIterator.iterateNamespaces(this);
+ List<String> excluded = new ArrayList<String>();
+ while (codes.hasNext()) {
+ excluded.add(codes.next().getURI());
+ }
+ excludedNamespaces = excluded.toArray(new String[excluded.size()]);
+ } else {
+ // go round twice, once to count the values and next to add them to the array
+ int count = 0;
+ StringTokenizer st1 = new StringTokenizer(ext, " \t\n\r", false);
+ while (st1.hasMoreTokens()) {
+ st1.nextToken();
+ count++;
+ }
+ excludedNamespaces = new String[count];
+ count = 0;
+ StringTokenizer st2 = new StringTokenizer(ext, " \t\n\r", false);
+ while (st2.hasMoreTokens()) {
+ String s = st2.nextToken();
+ if ("#default".equals(s)) {
+ s = "";
+ } else if ("#all".equals(s)) {
+ compileError("In exclude-result-prefixes, cannot mix #all with other values", "XTSE0020");
+ }
+ String uri = getURIForPrefix(s, true);
+ if (uri == null) {
+ excludedNamespaces = null;
+ compileError("Namespace prefix " + s + " is not declared", "XTSE0808");
+ break;
+ }
+ excludedNamespaces[count++] = uri;
+ if (s.length()==0 && uri.length()==0) {
+ compileError("Cannot exclude the #default namespace when no default namespace is declared",
+ "XTSE0809");
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Process the [xsl:]version attribute if there is one
+ * @param ns the namespace URI of the attribute required, either the XSLT namespace or ""
+ * @throws net.sf.saxon.trans.XPathException if the value of the attribute is invalid
+ */
+
+ protected void processVersionAttribute(String ns) throws XPathException {
+ String v = Whitespace.trim(getAttributeValue(ns, StandardNames.VERSION));
+ if (v != null) {
+ ConversionResult val = DecimalValue.makeDecimalValue(v, true);
+ if (val instanceof ValidationFailure) {
+ compileError("The version attribute must be a decimal literal", "XTSE0110");
+ version = DecimalValue.TWO;
+ } else {
+ // Note this will normalize the decimal so that trailing spaces are not significant
+ version = (DecimalValue)val;
+ }
+ }
+ }
+
+ /**
+ * Get the numeric value of the version number appearing as an attribute on this element,
+ * or inherited from its ancestors
+ * @return the version number as a decimal
+ */
+
+ public DecimalValue getEffectiveVersion() {
+ if (version == null) {
+ NodeInfo node = getParent();
+ if (node instanceof StyleElement) {
+ version = ((StyleElement)node).getEffectiveVersion();
+ } else {
+ return DecimalValue.TWO; // defensive programming
+ }
+ }
+ return version;
+ }
+
+ /**
+ * Determine whether forwards-compatible mode is enabled for this element
+ * @return true if forwards-compatible mode is enabled
+ */
+
+ public boolean forwardsCompatibleModeIsEnabled() {
+ return getEffectiveVersion().compareTo(getProcessorVersion()) > 0;
+ }
+
+ /**
+ * Determine whether 1.0-compatible mode is enabled for this element
+ * @return true if 1.0 compatable mode is enabled, that is, if this or an enclosing
+ * element specifies an [xsl:]version attribute whose value is less than 2.0
+ */
+
+ public boolean xPath10ModeIsEnabled() {
+ return getEffectiveVersion().compareTo(DecimalValue.TWO) < 0;
+ }
+
+ /**
+ * Determine what version of the XSLT the configured processor supports
+ * @return the version of XSLT supported by the processor
+ */
+
+ public DecimalValue getProcessorVersion() {
+ return getPreparedStylesheet().getStyleNodeFactory().getXsltProcessorVersion();
+ }
+
+ /**
+ * Ask whether the configured XSLT processor supports XSLT 3.0
+ * @return true if the configured XSLT processor supports XSLT 3.0
+ */
+
+ public boolean isXslt30Processor() {
+ return getProcessorVersion().compareTo(DecimalValue.THREE) >= 0;
+ }
+
+ /**
+ * Process the [xsl:]default-xpath-namespace attribute if there is one
+ * @throws net.sf.saxon.trans.XPathException if the value is not a valid URI, or not a recognized collation URI
+ */
+
+ protected void processDefaultCollationAttribute() throws XPathException {
+ String ns = getURI().equals(NamespaceConstant.XSLT) ? "" : NamespaceConstant.XSLT;
+ String v = getAttributeValue(ns, StandardNames.DEFAULT_COLLATION);
+ if (v != null) {
+ StringTokenizer st = new StringTokenizer(v, " \t\n\r", false);
+ while (st.hasMoreTokens()) {
+ String uri = st.nextToken();
+ if (uri.equals(NamespaceConstant.CODEPOINT_COLLATION_URI)) {
+ defaultCollationName = uri;
+ return;
+ } else if (uri.startsWith("http://saxon.sf.net/")) {
+ defaultCollationName = uri;
+ return;
+ } else {
+ URI collationURI;
+ try {
+ collationURI = new URI(uri);
+ if (!collationURI.isAbsolute()) {
+ URI base = new URI(getBaseURI());
+ collationURI = base.resolve(collationURI);
+ uri = collationURI.toString();
+ }
+ } catch (URISyntaxException err) {
+ compileError("default collation '" + uri + "' is not a valid URI");
+ uri = NamespaceConstant.CODEPOINT_COLLATION_URI;
+ }
+
+ if (uri.startsWith("http://saxon.sf.net/")) {
+ defaultCollationName = uri;
+ return;
+ }
+
+ if (getPreparedStylesheet().getNamedCollation(uri) != null) {
+ defaultCollationName = uri;
+ return;
+ }
+
+ if (getPrincipalStylesheetModule().findCollation(uri, getBaseURI()) != null) {
+ defaultCollationName = uri;
+ return;
+ }
+ }
+ // if not recognized, try the next URI in order
+ }
+ compileError("No recognized collation URI found in default-collation attribute", "XTSE0125");
+ }
+ }
+
+ /**
+ * Get the default collation for this stylesheet element. If no default collation is
+ * specified in the stylesheet, return the Unicode codepoint collation name.
+ * @return the name of the default collation
+ */
+
+ protected String getDefaultCollationName() {
+ StyleElement e = this;
+ while (true) {
+ if (e.defaultCollationName != null) {
+ return e.defaultCollationName;
+ }
+ NodeInfo p = e.getParent();
+ if (!(p instanceof StyleElement)) {
+ break;
+ }
+ e = (StyleElement)p;
+ }
+ String globalDefault = getConfiguration().getCollationMap().getDefaultCollationName();
+ return (globalDefault == null ? NamespaceConstant.CODEPOINT_COLLATION_URI : globalDefault);
+ }
+
+ /**
+ * Check whether a particular extension element namespace is defined on this node.
+ * This checks this node only, not the ancestor nodes.
+ * The implementation checks whether the prefix is included in the
+ * [xsl:]extension-element-prefixes attribute.
+ * @param uri the namespace URI being tested
+ * @return true if this namespace is defined on this element as an extension element namespace
+ */
+
+ protected boolean definesExtensionElement(String uri) {
+ if (extensionNamespaces == null) {
+ return false;
+ }
+ for (String extensionNamespace : extensionNamespaces) {
+ if (extensionNamespace.equals(uri)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether a namespace uri defines an extension element. This checks whether the
+ * namespace is defined as an extension namespace on this or any ancestor node.
+ * @param uri the namespace URI being tested
+ * @return true if the URI is an extension element namespace URI
+ */
+
+ public boolean isExtensionNamespace(String uri) {
+ NodeInfo anc = this;
+ while (anc instanceof StyleElement) {
+ if (((StyleElement)anc).definesExtensionElement(uri)) {
+ return true;
+ }
+ anc = anc.getParent();
+ }
+ return false;
+ }
+
+ /**
+ * Check whether this node excludes a particular namespace from the result.
+ * This method checks this node only, not the ancestor nodes.
+ * @param uri the namespace URI being tested
+ * @return true if the namespace is excluded by virtue of an [xsl:]exclude-result-prefixes attribute
+ */
+
+ protected boolean definesExcludedNamespace(String uri) {
+ if (excludedNamespaces == null) {
+ return false;
+ }
+ for (String excludedNamespace : excludedNamespaces) {
+ if (excludedNamespace.equals(uri)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether a namespace uri defines an namespace excluded from the result.
+ * This checks whether the namespace is defined as an excluded namespace on this
+ * or any ancestor node.
+ * @param uri the namespace URI being tested
+ * @return true if this namespace URI is a namespace excluded by virtue of exclude-result-prefixes
+ * on this element or on an ancestor element
+ */
+
+ public boolean isExcludedNamespace(String uri) {
+ if (uri.equals(NamespaceConstant.XSLT) || uri.equals(NamespaceConstant.XML)) {
+ return true;
+ }
+ if (isExtensionNamespace(uri)) {
+ return true;
+ }
+ NodeInfo anc = this;
+ while (anc instanceof StyleElement) {
+ if (((StyleElement)anc).definesExcludedNamespace(uri)) {
+ return true;
+ }
+ anc = anc.getParent();
+ }
+ return false;
+ }
+
+ /**
+ * Process the [xsl:]xpath-default-namespace attribute if there is one
+ * @param ns the namespace URI of the attribute required (the default namespace or the XSLT namespace.)
+ */
+
+ protected void processDefaultXPathNamespaceAttribute(String ns) {
+ String v = getAttributeValue(ns, StandardNames.XPATH_DEFAULT_NAMESPACE);
+ if (v != null) {
+ defaultXPathNamespace = v;
+ }
+ }
+
+ /**
+ * Get the default XPath namespace for elements and types
+ * @return the default namespace for elements and types.
+ * Return {@link NamespaceConstant#NULL} for the non-namespace
+ */
+
+ protected String getDefaultXPathNamespace() {
+ NodeInfo anc = this;
+ while (anc instanceof StyleElement) {
+ String x = ((StyleElement)anc).defaultXPathNamespace;
+ if (x != null) {
+ return x;
+ }
+ anc = anc.getParent();
+ }
+ return NamespaceConstant.NULL;
+ // indicates that the default namespace is the null namespace
+ }
+
+ /**
+ * Process the [xsl:]expand-text attribute if there is one (and if XSLT 3.0 is enabled)
+ * @param ns the namespace URI of the attribute required (the default namespace or the XSLT namespace.)
+ */
+
+ protected void processExpandTextAttribute(String ns) throws XPathException {
+ String v = getAttributeValue(ns, "expand-text");
+ if (v != null) {
+ v = Whitespace.trim(v);
+ if ("no".equals(v)) {
+ expandText = false;
+ } else if ("yes".equals(v)) {
+ expandText = true;
+ } else {
+ throw new XPathException("The value of expand-text must be 'yes' or 'no'", "XTSE0020");
+ }
+ } else {
+ NodeInfo parent = getParent();
+ if (parent instanceof StyleElement) {
+ expandText = ((StyleElement)parent).expandText;
+ } else {
+ expandText = false;
+ }
+ }
+ }
+
+ /**
+ * Ask whether content value templates are available within this element
+ * @return true if content value templates are enabled
+ */
+
+ public boolean isExpandingText() {
+ return expandText;
+ }
+
+ /**
+ * Get the Schema type definition for a type named in the stylesheet (in a
+ * "type" attribute).
+ * @param typeAtt the value of the type attribute
+ * @return the corresponding schema type
+ * @throws XPathException if the type is not declared in an
+ * imported schema, or is not a built-in type
+ */
+
+ public SchemaType getSchemaType(String typeAtt) throws XPathException {
+ try {
+ String[] parts = getConfiguration().getNameChecker().getQNameParts(typeAtt);
+ String lname = parts[1];
+ String uri;
+ if ("".equals(parts[0])) {
+ // Name is unprefixed: use the default-xpath-namespace
+ uri = getDefaultXPathNamespace();
+ } else {
+ uri = getURIForPrefix(parts[0], false);
+ if (uri == null) {
+ compileError("Namespace prefix for type annotation is undeclared", "XTSE1520");
+ return null;
+ }
+ }
+ int nc = getNamePool().allocate(parts[0], uri, lname);
+ if (uri.equals(NamespaceConstant.SCHEMA)) {
+ SchemaType t = BuiltInType.getSchemaType(StandardNames.getFingerprint(uri, lname));
+ if (t == null) {
+ compileError("Unknown built-in type " + typeAtt, "XTSE1520");
+ return null;
+ }
+ return t;
+ }
+
+ // not a built-in type: look in the imported schemas
+
+ if (!getPrincipalStylesheetModule().isImportedSchema(uri)) {
+ compileError("There is no imported schema for the namespace of type " + typeAtt, "XTSE1520");
+ return null;
+ }
+ SchemaType stype = getConfiguration().getSchemaType(nc & 0xfffff);
+ if (stype == null) {
+ compileError("There is no type named " + typeAtt + " in an imported schema", "XTSE1520");
+ }
+ return stype;
+
+ } catch (QNameException err) {
+ compileError("Invalid type name. " + err.getMessage(), "XTSE1520");
+ }
+ return null;
+ }
+
+ /**
+ * Get the type annotation to use for a given schema type
+ * @param schemaType the schema type
+ * @return the corresponding numeric type annotation
+ */
+
+ public SimpleType getTypeAnnotation(SchemaType schemaType) {
+ return (SimpleType)schemaType;
+ }
+
+ /**
+ * Check that the stylesheet element is valid. This is called once for each element, after
+ * the entire tree has been built. As well as validation, it can perform first-time
+ * initialisation. The default implementation does nothing; it is normally overriden
+ * in subclasses.
+ * @param decl the declaration to be validated
+ * @throws XPathException if any error is found during validation
+ */
+
+ public void validate(Declaration decl) throws XPathException {
+ }
+
+ /**
+ * Hook to allow additional validation of a parent element immediately after its
+ * children have been validated.
+ * @throws XPathException if any error is found during post-traversal validation
+ */
+
+ public void postValidate() throws XPathException {
+ }
+
+ /**
+ * Method supplied by declaration elements to add themselves to a stylesheet-level index
+ * @param decl the Declaration being indexed. (This corresponds to the StyleElement object
+ * except in cases where one module is imported several times with different precedence.)
+ * @param top the outermost XSLStylesheet element
+ * @throws XPathException if any error is encountered
+ */
+
+ protected void index(Declaration decl, PrincipalStylesheetModule top) throws XPathException {
+ }
+
+ /**
+ * Type-check an expression. This is called to check each expression while the containing
+ * instruction is being validated. It is not just a static type-check, it also adds code
+ * to perform any necessary run-time type checking and/or conversion.
+ * @param name the name of the attribute containing the expression to be checked (used for diagnostics)
+ * @param exp the expression to be checked
+ * @return the (possibly rewritten) expression after type checking
+ * @throws XPathException if type-checking fails statically, that is, if it can be determined that the
+ * supplied value for the expression cannot possibly be of the required type
+ */
+
+ // Note: the typeCheck() call is done at the level of individual path expression; the optimize() call is done
+ // for a template or function as a whole. We can't do it all at the function/template level because
+ // the static context (e.g. namespaces) changes from one XPath expression to another.
+ public Expression typeCheck(String name, Expression exp) throws XPathException {
+
+ if (exp == null) {
+ return null;
+ }
+
+ exp.setContainer(this);
+ // temporary, until the instruction is compiled
+
+ try {
+ exp = makeExpressionVisitor().typeCheck(exp, new ExpressionVisitor.ContextItemType(Type.ITEM_TYPE, true));
+ exp = ExpressionTool.resolveCallsToCurrentFunction(exp, getConfiguration());
+ // if (explaining) {
+ // System.err.println("Attribute '" + name + "' of element '" + getDisplayName() + "' at line " + getLineNumber() + ':');
+ // System.err.println("Static type: " +
+ // SequenceType.makeSequenceType(exp.getItemType(), exp.getCardinality()));
+ // System.err.println("Optimized expression tree:");
+ // exp.display(10, getNamePool(), System.err);
+ // }
+ CodeInjector injector = getPreparedStylesheet().getCompilerInfo().getCodeInjector();
+ if (injector != null) {
+ return injector.inject(exp, getStaticContext(), Location.XPATH_IN_XSLT, new StructuredQName("", "", name));
+// InstructionDetails details = new InstructionDetails();
+// details.setConstructType(Location.XPATH_IN_XSLT);
+// details.setLineNumber(getLineNumber());
+// details.setSystemId(getSystemId());
+// details.setProperty("attribute-name", name);
+// TraceWrapper trace = new TraceInstruction(exp, details);
+// trace.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+// trace.setContainer(this);
+// exp = trace;
+ }
+ return exp;
+ } catch (XPathException err) {
+ // we can't report a dynamic error such as divide by zero unless the expression
+ // is actually executed.
+ //err.printStackTrace();
+ if (err.isReportableStatically()) {
+ compileError(err);
+ return exp;
+ } else {
+ ErrorExpression erexp = new ErrorExpression(err);
+ erexp.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+ return erexp;
+ }
+ }
+ }
+
+ /**
+ * Allocate slots in the local stack frame to range variables used in an XPath expression
+ * @param exp the XPath expression for which slots are to be allocated
+ */
+
+ public void allocateSlots(Expression exp) {
+ SlotManager slotManager = getContainingSlotManager();
+ if (slotManager == null) {
+ throw new AssertionError("Slot manager has not been allocated");
+ // previous code: ExpressionTool.allocateSlots(exp, 0, null);
+ } else {
+ int firstSlot = slotManager.getNumberOfVariables();
+ int highWater = ExpressionTool.allocateSlots(exp, firstSlot, slotManager);
+ if (highWater > firstSlot) {
+ slotManager.setNumberOfVariables(highWater);
+ // This algorithm is not very efficient because it never reuses
+ // a slot when a variable goes out of scope. But at least it is safe.
+ // Note that range variables within XPath expressions need to maintain
+ // a slot until the instruction they are part of finishes, e.g. in
+ // xsl:for-each.
+ }
+ }
+ }
+
+ /**
+ * Allocate space for range variables within predicates in the match pattern. The xsl:template
+ * element has no XPath expressions among its attributes, so if this method is called on this
+ * object it can only be because there are variables used in the match pattern. We work out
+ * how many slots are needed for the match pattern in each template rule, and then apply-templates
+ * can allocate a stack frame that is large enough for the most demanding match pattern in the
+ * entire stylesheet.
+ * @param slots the number of slots required
+ */
+
+ public void allocatePatternSlots(int slots) {
+ getPrincipalStylesheetModule().allocatePatternSlots(slots);
+ }
+
+ /**
+ * Type-check a pattern. This is called to check each pattern while the containing
+ * instruction is being validated. It is not just a static type-check, it also adds code
+ * to perform any necessary run-time type checking and/or conversion.
+ * @param name the name of the attribute holding the pattern, for example "match": used in
+ * diagnostics
+ * @param pattern the compiled pattern
+ * @return the original pattern, or a substitute pattern if it has been rewritten. Returns null
+ * if and only if the supplied pattern is null.
+ * @throws net.sf.saxon.trans.XPathException if the pattern fails optimistic static type-checking
+ */
+
+ public Pattern typeCheck(String name, Pattern pattern) throws XPathException {
+ if (pattern == null) {
+ return null;
+ }
+ try {
+ pattern = pattern.analyze(makeExpressionVisitor(), new ExpressionVisitor.ContextItemType(Type.NODE_TYPE, true));
+ boolean usesCurrent = false;
+
+ Iterator sub = pattern.iterateSubExpressions();
+ while (sub.hasNext()) {
+ Expression filter = (Expression)sub.next();
+ if (ExpressionTool.callsFunction(filter, Current.FN_CURRENT)) {
+ usesCurrent = true;
+ break;
+ }
+ }
+ if (usesCurrent) {
+ PatternThatSetsCurrent p2 = new PatternThatSetsCurrent(pattern);
+ pattern.bindCurrent(p2.getCurrentBinding());
+ pattern = p2;
+ }
+
+ return pattern;
+ } catch (XPathException err) {
+ // we can't report a dynamic error such as divide by zero unless the pattern
+ // is actually executed. We don't have an error pattern available, so we
+ // construct one
+ if (err.isReportableStatically()) {
+ XPathException e2 = new XPathException("Error in " + name + " pattern", err);
+ e2.setLocator(this);
+ e2.setErrorCodeQName(err.getErrorCodeQName());
+ throw e2;
+ } else {
+ PatternWithPredicate errpat = new PatternWithPredicate(
+ new ItemTypePattern(AnyItemType.getInstance()), new ErrorExpression(err));
+ errpat.setExecutable(getPreparedStylesheet());
+ return errpat;
+ }
+ }
+ }
+
+ /**
+ * Fix up references from XPath expressions. Overridden for function declarations
+ * and variable declarations
+ * @throws net.sf.saxon.trans.XPathException if any references cannot be fixed up.
+ */
+
+ public void fixupReferences() throws XPathException {
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ return;
+ }
+ if (child instanceof StyleElement) {
+ ((StyleElement)child).fixupReferences();
+ }
+ }
+ }
+
+ /**
+ * Get the SlotManager for the containing Procedure definition
+ * @return the SlotManager associated with the containing Function, Template, etc,
+ * or null if there is no such containing Function, Template etc.
+ */
+
+ public SlotManager getContainingSlotManager() {
+ NodeInfo node = this;
+ while (true) {
+ NodeInfo next = node.getParent();
+ if (next instanceof XSLStylesheet) {
+ if (node instanceof StylesheetProcedure) {
+ return ((StylesheetProcedure)node).getSlotManager();
+ } else {
+ return null;
+ }
+ }
+ node = next;
+ }
+ }
+
+
+ /**
+ * Recursive walk through the stylesheet to validate all nodes
+ * @param decl the declaration to be validated
+ * @throws XPathException if validation fails
+ */
+
+ public void validateSubtree(Declaration decl) throws XPathException {
+ if (isActionCompleted(StyleElement.ACTION_VALIDATE)) {
+ return;
+ }
+ setActionCompleted(StyleElement.ACTION_VALIDATE);
+ if (validationError != null) {
+ if (reportingCircumstances == REPORT_ALWAYS) {
+ compileError(validationError);
+ } else if (reportingCircumstances == REPORT_UNLESS_FORWARDS_COMPATIBLE
+ && !forwardsCompatibleModeIsEnabled()) {
+ compileError(validationError);
+ } else if (reportingCircumstances == REPORT_UNLESS_FALLBACK_AVAILABLE) {
+ if (!forwardsCompatibleModeIsEnabled()) {
+ compileError(validationError);
+ } else {
+ boolean hasFallback = false;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLFallback) {
+ hasFallback = true;
+ ((XSLFallback)child).validateSubtree(decl);
+ }
+ }
+ if (!hasFallback) {
+ compileError(validationError);
+ }
+ }
+ }
+ } else {
+ try {
+ validate(decl);
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ validateChildren(decl);
+ postValidate();
+ }
+ }
+
+ /**
+ * Validate the children of this node, recursively. Overridden for top-level
+ * data elements.
+ * @param decl the declaration whose children are to be validated
+ * @throws XPathException if validation fails
+ */
+
+ protected void validateChildren(Declaration decl) throws XPathException {
+ boolean containsInstructions = mayContainSequenceConstructor();
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ StyleElement lastChild = null;
+ boolean endsWithTextTemplate = false;
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof StyleElement) {
+ endsWithTextTemplate = false;
+ if (containsInstructions && !((StyleElement)child).isInstruction()
+ && !isPermittedChild((StyleElement)child)) {
+ ((StyleElement)child).compileError("An " + getDisplayName() + " element must not contain an " +
+ child.getDisplayName() + " element", "XTSE0010");
+ }
+ ((StyleElement)child).validateSubtree(decl);
+ lastChild = (StyleElement)child;
+ } else {
+ endsWithTextTemplate = getPreparedStylesheet().getStyleNodeFactory().validateTextNode(child);
+ }
+ }
+ if (lastChild instanceof XSLLocalVariable &&
+ !(this instanceof XSLStylesheet) && !endsWithTextTemplate) {
+ lastChild.compileWarning("A variable with no following sibling instructions has no effect",
+ SaxonErrorCode.SXWN9001);
+ }
+ }
+
+ /**
+ * Check whether a given child is permitted for this element. This method is used when a non-instruction
+ * child element such as xsl:sort is encountered in a context where instructions would normally be expected.
+ * @param child the child that may or may not be permitted
+ * @return true if the child is permitted.
+ */
+
+ protected boolean isPermittedChild(StyleElement child) {
+ return false;
+ }
+
+ /**
+ * Get the PreparedStylesheet object.
+ * @return the PreparedStylesheet to which this stylesheet element belongs.
+ * Exceptionally (with early errors in a simplified stylesheet module) return null.
+ */
+
+ public PreparedStylesheet getPreparedStylesheet() {
+ XSLStylesheet xss = getContainingStylesheet();
+ return (xss==null ? null : xss.getPreparedStylesheet());
+ }
+
+ /**
+ * Get the principal stylesheet module
+ * @return the principal stylesheet module
+ */
+
+ public PrincipalStylesheetModule getPrincipalStylesheetModule() {
+ return getContainingStylesheet().getPrincipalStylesheetModule();
+ }
+
+ /**
+ * Check that among the children of this element, any xsl:sort elements precede any other elements
+ * @param sortRequired true if there must be at least one xsl:sort element
+ * @throws XPathException if invalid
+ */
+
+ protected void checkSortComesFirst(boolean sortRequired) throws XPathException {
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ boolean sortFound = false;
+ boolean nonSortFound = false;
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLSort) {
+ if (nonSortFound) {
+ ((XSLSort)child).compileError("Within " + getDisplayName() +
+ ", xsl:sort elements must come before other instructions", "XTSE0010");
+ }
+ sortFound = true;
+ } else if (child.getNodeKind() == Type.TEXT) {
+ // with xml:space=preserve, white space nodes may still be there
+ if (!Whitespace.isWhite(child.getStringValueCS())) {
+ nonSortFound = true;
+ }
+ } else {
+ nonSortFound = true;
+ }
+ }
+ if (sortRequired && !sortFound) {
+ compileError(getDisplayName() + " must have at least one xsl:sort child", "XTSE0010");
+ }
+ }
+
+ /**
+ * Convenience method to check that the stylesheet element is at the top level (that is,
+ * as a child of xsl:stylesheet or xsl:transform)
+ * @param errorCode the error to throw if it is not at the top level; defaults to XTSE0010
+ * if the value is null
+ * @throws XPathException if not at top level
+ */
+
+ public void checkTopLevel(/*@NotNull*/ String errorCode) throws XPathException {
+ if (!(getParent() instanceof XSLStylesheet)) {
+ compileError("Element " + getDisplayName() + " must always be a child of xsl:stylesheet or xsl:transform", errorCode);
+ }
+ }
+
+ /**
+ * Convenience method to check that the stylesheet element is empty
+ * @throws XPathException if it is not empty
+ */
+
+ public void checkEmpty() throws XPathException {
+ if (hasChildNodes()) {
+ compileError("Element must be empty", "XTSE0260");
+ }
+ }
+
+ /**
+ * Convenience method to report the absence of a mandatory attribute
+ * @param attribute the name of the attribute whose absence is to be reported
+ * @throws XPathException if the attribute is missing
+ */
+
+ public void reportAbsence(String attribute)
+ throws XPathException {
+ compileError("Element must have an @" + attribute + " attribute", "XTSE0010");
+ }
+
+
+ /**
+ * Compile the instruction on the stylesheet tree into an executable instruction
+ * for use at run-time.
+ * @param exec the Executable
+ * @param decl the containing top-level declaration, for example xsl:function or xsl:template
+ * @return either a ComputedExpression, or null. The value null is returned when compiling an instruction
+ * that returns a no-op, or when compiling a top-level object such as an xsl:template that compiles
+ * into something other than an instruction.
+ * @throws net.sf.saxon.trans.XPathException if validation fails
+ */
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ // no action: default for non-instruction elements
+ return null;
+ }
+
+ /**
+ * Compile a declaration in the stylesheet tree
+ * for use at run-time.
+ * @param exec the Executable
+ * @param decl the containing top-level declaration, for example xsl:function or xsl:template
+ * @throws net.sf.saxon.trans.XPathException if compilation fails
+ */
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+ // no action: default for elements that are not declarations
+ }
+
+
+ /**
+ * Compile the children of this instruction on the stylesheet tree, adding the
+ * subordinate instructions to the parent instruction on the execution tree.
+ * @param exec the Executable
+ * @param decl the Declaration of the containing top-level stylesheet element
+ * @param iter Iterator over the children. This is used in the case where there are children
+ * that are not part of the sequence constructor, for example the xsl:sort children of xsl:for-each;
+ * the iterator can be positioned past such elements.
+ * @param includeParams true if xsl:param elements are to be treated as child instructions (true
+ * for templates but not for functions) @return an Expression tree representing the children of this instruction
+ * @return the compiled sequence constructor
+ * @throws net.sf.saxon.trans.XPathException if compilation fails
+ */
+
+ public Expression compileSequenceConstructor(Executable exec, Declaration decl,
+ SequenceIterator iter, boolean includeParams)
+ throws XPathException {
+
+ //Expression result = Literal.makeEmptySequence();
+ int locationId = allocateLocationId(getSystemId(), getLineNumber());
+ List<Expression> contents = new ArrayList<Expression>(10);
+ int lineNumber = getLineNumber();
+ while (true) {
+ NodeInfo node = ((NodeInfo)iter.next());
+ if (node == null) {
+ //return result;
+ break;
+ }
+ if (node instanceof StyleElement) {
+ lineNumber = node.getLineNumber(); // this is to get a line number for the next text node
+ }
+ if (node.getNodeKind() == Type.TEXT) {
+ if (isExpandingText()) {
+ getPreparedStylesheet().getStyleNodeFactory().compileContentValueTemplate((TextImpl)node, contents);
+ } else {
+ // handle literal text nodes by generating an xsl:value-of instruction, unless expand-text is enabled
+ AxisIterator lookahead = node.iterateAxis(AxisInfo.FOLLOWING_SIBLING);
+ NodeInfo sibling = lookahead.next();
+ if (!(sibling instanceof XSLLocalParam || sibling instanceof XSLSort)) {
+ // The test for XSLParam and XSLSort is to eliminate whitespace nodes that have been retained
+ // because of xml:space="preserve"
+ Expression text = new ValueOf(new StringLiteral(node.getStringValue()), false, false);
+
+ text.setLocationId(allocateLocationId(getSystemId(), lineNumber));
+
+ CodeInjector injector = getPreparedStylesheet().getCompilerInfo().getCodeInjector();
+ if (injector != null) {
+ Expression tracer = injector.inject(text, getStaticContext(), StandardNames.XSL_TEXT, null);
+ tracer.setLocationId(text.getLocationId());
+ text = tracer;
+ }
+
+ contents.add(text);
+ }
+ }
+
+ } else if (node instanceof XSLLocalVariable) {
+ XSLLocalVariable var = (XSLLocalVariable)node;
+ SourceBinding sourceBinding = var.getSourceBinding();
+ var.compileLocalVariable(exec, decl);
+
+ Expression tail = compileSequenceConstructor(exec, decl, iter, includeParams);
+ if (tail == null || Literal.isEmptySequence(tail)) {
+ // this doesn't happen, because if there are no instructions following
+ // a variable, we'll have taken the var==null path above
+ //return result;
+ } else {
+ LetExpression let = new LetExpression();
+ let.setRequiredType(var.getRequiredType());
+ let.setVariableQName(sourceBinding.getVariableQName());
+ let.setSequence(sourceBinding.getSelectExpression());
+ let.setAction(tail);
+ sourceBinding.fixupBinding(let);
+ locationId = allocateLocationId(node.getSystemId(), node.getLineNumber());
+ let.setLocationId(locationId);
+ if (getPreparedStylesheet().isCompileWithTracing()) {
+ TraceExpression t = new TraceExpression(let);
+ t.setConstructType(Location.LET_EXPRESSION);
+ t.setObjectName(var.getSourceBinding().getVariableQName());
+ t.setNamespaceResolver(getNamespaceResolver());
+ contents.add(t);
+ } else {
+ contents.add(let);
+ }
+ //result.setLocationId(locationId);
+ }
+
+
+ } else if (node instanceof StyleElement) {
+ StyleElement snode = (StyleElement)node;
+ Expression child;
+ if (snode.validationError != null && !(this instanceof AbsentExtensionElement)) {
+ child = fallbackProcessing(exec, decl, snode);
+
+ } else {
+ child = snode.compile(exec, decl);
+ if (child != null) {
+ if (child.getContainer() == null) {
+ // for the time being, the XSLT stylesheet element acts as the container
+ // for the XPath expressions within. This will later be replaced by a
+ // compiled template, variable, or other top-level construct
+ child.setContainer(this);
+ }
+ locationId = allocateLocationId(getSystemId(), snode.getLineNumber());
+ child.setLocationId(locationId);
+ if (includeParams || !(node instanceof XSLLocalParam)) {
+ if (getPreparedStylesheet().isCompileWithTracing()) {
+ child = makeTraceInstruction(snode, child);
+ }
+ }
+ }
+ }
+ if (child != null) {
+ contents.add(child);
+ }
+ }
+ }
+ Expression block = Block.makeBlock(contents);
+ block.setLocationId(locationId);
+ return block;
+ }
+
+ /**
+ * Create a trace instruction to wrap a real instruction
+ *
+ *
+ * @param source the parent element
+ * @param child the compiled expression tree for the instruction to be traced
+ * @return a wrapper instruction that performs the tracing (if activated at run-time)
+ */
+
+ protected static Expression makeTraceInstruction(StyleElement source, Expression child) {
+ if (child instanceof TraceExpression && !(source instanceof StylesheetProcedure)) {
+ return child;
+ // this can happen, for example, after optimizing a compile-time xsl:if
+ }
+ CodeInjector injector = source.getPreparedStylesheet().getCompilerInfo().getCodeInjector();
+ if (injector != null) {
+ int construct = source.getFingerprint();
+ if (source instanceof LiteralResultElement) {
+ construct = Location.LITERAL_RESULT_ELEMENT;
+ }
+ Expression tracer = injector.inject(child, source.getStaticContext(), construct, source.getObjectName());
+ tracer.setLocationId(source.allocateLocationId(source.getSystemId(), source.getLineNumber()));
+ return tracer;
+ }
+ return child;
+ }
+
+ /**
+ * Perform fallback processing. Generate fallback code for an extension
+ * instruction that is not recognized by the implementation.
+ * @param exec the Executable
+ * @param decl the Declaration of the top-level element containing the extension instruction
+ * @param instruction The unknown extension instruction
+ * @return the expression tree representing the fallback code
+ * @throws net.sf.saxon.trans.XPathException if any error occurs
+ */
+
+ protected Expression fallbackProcessing(Executable exec, Declaration decl, StyleElement instruction)
+ throws XPathException {
+ // process any xsl:fallback children; if there are none,
+ // generate code to report the original failure reason
+ Expression fallback = null;
+ AxisIterator kids = instruction.iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLFallback) {
+ //fallback.setLocationId(allocateLocationId(getSystemId(), child.getLineNumber()));
+ //((XSLFallback)child).compileChildren(exec, fallback, true);
+ Expression b = ((XSLFallback)child).compileSequenceConstructor(exec, decl, child.iterateAxis(AxisInfo.CHILD), true);
+ if (b == null) {
+ b = Literal.makeEmptySequence();
+ }
+ if (fallback == null) {
+ fallback = b;
+ } else {
+ fallback = Block.makeBlock(fallback, b);
+ fallback.setLocationId(
+ allocateLocationId(getSystemId(), getLineNumber()));
+ }
+ }
+ }
+ if (fallback != null) {
+ return fallback;
+ } else {
+ return new ErrorExpression(instruction.validationError);
+ // compileError(instruction.validationError);
+ // return EmptySequence.getInstance();
+ }
+
+ }
+
+ /**
+ * Allocate a location identifier
+ * @param systemId identifies the module containing the instruction
+ * @param lineNumber the line number of the instruction
+ * @return an integer location ID which can be used to report the location of the instruction,
+ * by reference to a {@link LocationProvider}
+ */
+
+ protected int allocateLocationId(String systemId, int lineNumber) {
+ return getStaticContext().getLocationMap().allocateLocationId(systemId, lineNumber);
+ }
+
+ /**
+ * Construct sort keys for a SortedIterator
+ * @return an array of SortKeyDefinition objects if there are any sort keys;
+ * or null if there are none.
+ * @param decl the declaration containing the sort keys
+ * @throws XPathException if any error is detected
+ */
+
+ public SortKeyDefinition[] makeSortKeys(Declaration decl) throws XPathException {
+ // handle sort keys if any
+
+ int numberOfSortKeys = 0;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ Item child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLSortOrMergeKey) {
+ ((XSLSortOrMergeKey)child).compile(getPreparedStylesheet(), decl);
+ if (child instanceof XSLSort) {
+ if (numberOfSortKeys != 0 && ((XSLSort)child).getStable() != null) {
+ compileError("stable attribute may appear only on the first xsl:sort element", "XTSE1017");
+ }
+ }
+ numberOfSortKeys++;
+ }
+ }
+
+ if (numberOfSortKeys > 0) {
+ SortKeyDefinition[] keys = new SortKeyDefinition[numberOfSortKeys];
+ kids = iterateAxis(AxisInfo.CHILD);
+ int k = 0;
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLSortOrMergeKey) {
+ keys[k++] = ((XSLSortOrMergeKey)child).getSortKeyDefinition().simplify(makeExpressionVisitor());
+ }
+ }
+ return keys;
+
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the list of attribute-sets associated with this element.
+ * This is used for xsl:element, xsl:copy, xsl:attribute-set, and on literal
+ * result elements
+ * @param use the original value of the [xsl:]use-attribute-sets attribute
+ * @param list an empty list to hold the list of XSLAttributeSet elements in the stylesheet tree.
+ * Or null, if these are not required.
+ * @return an array of AttributeList instructions representing the compiled attribute sets
+ * @throws net.sf.saxon.trans.XPathException if any error is detected
+ */
+
+ protected AttributeSet[] getAttributeSets(String use, /*@Nullable*/ List<Declaration> list)
+ throws XPathException {
+
+ if (list == null) {
+ list = new ArrayList<Declaration>(4);
+ }
+ PrincipalStylesheetModule psm = getPrincipalStylesheetModule();
+ StringTokenizer st = new StringTokenizer(use, " \t\n\r", false);
+ while (st.hasMoreTokens()) {
+ String asetname = st.nextToken();
+ StructuredQName name;
+ try {
+ name = makeQName(asetname);
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE0710");
+ name = null;
+ } catch (XPathException err) {
+ compileError(err.getMessage(), "XTSE0710");
+ name = null;
+ }
+ boolean found = psm.getAttributeSets(name, list);
+ if (!found) {
+ compileError("No attribute-set exists named " + asetname, "XTSE0710");
+ }
+ }
+ AttributeSet[] array = new AttributeSet[list.size()];
+ for (int i=0; i<list.size(); i++) {
+ XSLAttributeSet aset = (XSLAttributeSet)list.get(i).getSourceElement();
+ array[i] = aset.getInstruction();
+ }
+ return array;
+ }
+
+ /**
+ * Get the list of xsl:with-param elements for a calling element (apply-templates,
+ * call-template, apply-imports, next-match). This method can be used to get either
+ * the tunnel parameters, or the non-tunnel parameters.
+ * @param exec the Executable
+ * @param decl the containing stylesheet declaration
+ * @param tunnel true if the tunnel="yes" parameters are wanted, false to get
+ * @param caller the calling instruction (for example xsl:apply-templates), used
+ * only for its location information @return an array of WithParam objects for either the ordinary parameters
+ * or the tunnel parameters
+ * @return an array containing the results of compiling the xsl:with-param children of this instruction (if any)
+ * @throws XPathException if any error is detected
+ */
+
+ protected WithParam[] getWithParamInstructions(Executable exec, Declaration decl, boolean tunnel, Expression caller)
+ throws XPathException {
+ int count = 0;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLWithParam) {
+ XSLWithParam wp = (XSLWithParam)child;
+ if (wp.getSourceBinding().hasProperty(SourceBinding.TUNNEL) == tunnel) {
+ count++;
+ }
+ }
+ }
+ if (count == 0) {
+ return WithParam.EMPTY_ARRAY;
+ }
+ WithParam[] array = new WithParam[count];
+ count = 0;
+ kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ return array;
+ }
+ if (child instanceof XSLWithParam) {
+ XSLWithParam wp = (XSLWithParam)child;
+ if (wp.getSourceBinding().hasProperty(SourceBinding.TUNNEL) == tunnel) {
+ WithParam p = wp.compileWithParam(exec, decl);
+ p.setLocationId(exec.getLocationMap().allocateLocationId(getSystemId(), getLineNumber()));
+ array[count++] = p;
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Report an error with diagnostic information
+ * @param error contains information about the error
+ * @throws XPathException always, after reporting the error to the ErrorListener
+ */
+
+ protected void compileError(XPathException error)
+ throws XPathException {
+ error.setIsStaticError(true);
+ // Set the location of the error if there is no current location information,
+ // or if the current location information is local to an XPath expression, unless we are
+ // positioned on an xsl:function or xsl:template, in which case this would lose too much information
+ if (error.getLocator() == null ||
+ ((error.getLocator() instanceof ExpressionLocation ||
+ error.getLocator() instanceof Expression) && !(this instanceof StylesheetProcedure))) {
+ error.setLocator(this);
+ }
+ PreparedStylesheet pss = getPreparedStylesheet();
+ try {
+ if (pss == null) {
+ // it is null before the stylesheet has been fully built
+ throw error;
+ } else {
+ pss.reportError(error);
+ }
+ } catch (TransformerException err2) {
+ if (err2.getLocator() == null) {
+ err2.setLocator(this);
+ }
+ throw XPathException.makeXPathException(err2);
+ }
+ }
+
+ /**
+ * Report a static error in the stylesheet
+ * @param message the error message
+ * @throws XPathException always, after reporting the error to the ErrorListener
+ */
+
+ protected void compileError(String message)
+ throws XPathException {
+ XPathException tce = new XPathException(message);
+ tce.setLocator(this);
+ compileError(tce);
+ }
+
+ /**
+ * Compile time error, specifying an error code
+ * @param message the error message
+ * @param errorCode the error code. May be null if not known or not defined
+ * @throws XPathException always, after reporting the error to the ErrorListener
+ */
+
+ public void compileError(String message, StructuredQName errorCode) throws XPathException {
+ XPathException tce = new XPathException(message);
+ tce.setErrorCodeQName(errorCode);
+ tce.setLocator(this);
+ compileError(tce);
+ }
+
+ /**
+ * Compile time error, specifying an error code
+ * @param message the error message
+ * @param errorCode the error code. May be null if not known or not defined
+ * @throws XPathException always, after reporting the error to the ErrorListener
+ */
+
+ public void compileError(String message, String errorCode) throws XPathException {
+ XPathException tce = new XPathException(message);
+ tce.setErrorCode(errorCode);
+ tce.setLocator(this);
+ compileError(tce);
+ }
+
+ protected void undeclaredNamespaceError(String prefix, String errorCode) throws XPathException {
+ if (errorCode == null) {
+ errorCode = "XTSE0280";
+ }
+ compileError("Undeclared namespace prefix " + Err.wrap(prefix), errorCode);
+ }
+
+ protected void compileWarning(String message, StructuredQName errorCode)
+ throws XPathException {
+ XPathException tce = new XPathException(message);
+ tce.setErrorCodeQName(errorCode);
+ tce.setLocator(this);
+ PreparedStylesheet pss = getPreparedStylesheet();
+ if (pss != null) {
+ pss.reportWarning(tce);
+ }
+ }
+
+ protected void compileWarning(String message, String errorCode)
+ throws XPathException {
+ XPathException tce = new XPathException(message);
+ tce.setErrorCode(errorCode);
+ tce.setLocator(this);
+ PreparedStylesheet pss = getPreparedStylesheet();
+ if (pss != null) {
+ pss.reportWarning(tce);
+ }
+ }
+
+ /**
+ * Report a warning to the error listener
+ * @param error an exception containing the warning text
+ */
+
+ protected void issueWarning(TransformerException error) {
+ if (error.getLocator() == null) {
+ error.setLocator(this);
+ }
+ PreparedStylesheet pss = getPreparedStylesheet();
+ if (pss != null) {
+ // it is null before the stylesheet has been fully built - ignore it
+ pss.reportWarning(error);
+ }
+ }
+
+ /**
+ * Report a warning to the error listener
+ * @param message the warning message text
+ * @param locator the location of the problem in the source stylesheet
+ */
+
+ protected void issueWarning(String message, SourceLocator locator) {
+ TransformerConfigurationException tce =
+ new TransformerConfigurationException(message);
+ if (locator == null) {
+ tce.setLocator(this);
+ } else {
+ tce.setLocator(locator);
+ }
+ issueWarning(tce);
+ }
+
+ /**
+ * Test whether this is a top-level element
+ * @return true if the element is a child of the xsl:stylesheet element
+ */
+
+ public boolean isTopLevel() {
+ return (getParent() instanceof XSLStylesheet);
+ }
+
+ /**
+ * Ask whether this element contains a binding for a variable with a given name; and if it does,
+ * return the source binding information
+ * @param name the variable name
+ * @return the binding information if this element binds a variable of this name; otherwise null
+ */
+
+ public SourceBinding getBindingInformation(StructuredQName name) {
+ return null;
+ }
+
+ /**
+ * Bind a variable used in this element to the compiled form of the XSLVariable element in which it is
+ * declared
+ * @param qName The name of the variable
+ * @return the XSLVariableDeclaration (that is, an xsl:variable or xsl:param instruction) for the variable,
+ * or null if no declaration of the variable can be found
+ */
+
+ public SourceBinding bindVariable(StructuredQName qName) {
+
+ SourceBinding decl = bindLocalVariable(qName);
+ if (decl != null) {
+ return decl;
+ }
+
+ // Now check for a global variable
+ // we rely on the search following the order of decreasing import precedence.
+ SourceBinding binding = getPrincipalStylesheetModule().getGlobalVariable(qName);
+ if (binding == null || Navigator.isAncestorOrSelf(binding.getSourceElement(), this)) {
+ // test case variable-0118
+ return null;
+ } else {
+ return binding;
+ }
+ }
+
+ /**
+ * Bind a variable reference used in this element to the compiled form of the XSLVariable element in which it is
+ * declared, considering only local variables and params
+ * @param qName The name of the variable
+ * @return the XSLVariableDeclaration (that is, an xsl:variable or xsl:param instruction) for the variable,
+ * or null if no local declaration of the variable can be found
+ */
+
+ public SourceBinding bindLocalVariable(StructuredQName qName) {
+ NodeInfo curr = this;
+ NodeInfo prev = this;
+
+ // first search for a local variable declaration
+ if (!isTopLevel()) {
+ AxisIterator preceding = curr.iterateAxis(AxisInfo.PRECEDING_SIBLING);
+ while (true) {
+ curr = preceding.next();
+ while (curr == null) {
+ curr = prev.getParent();
+ while (curr instanceof StyleElement && !((StyleElement)curr).seesAvuncularVariables()) {
+ // a local variable is not visible within a sibling xsl:fallback or xsl:catch element
+ curr = curr.getParent();
+ }
+ if (curr instanceof XSLForEachGroup) {
+ XSLForEachGroup feg = ((XSLForEachGroup)curr);
+ if (qName.equals(feg.getGroupBindingName())) {
+ return feg.getGroupSourceBinding();
+ } else if (qName.equals(feg.getKeyBindingName())) {
+ return feg.getKeyBinding();
+ }
+ }
+ prev = curr;
+ if (curr.getParent() instanceof XSLStylesheet) {
+ break; // top level
+ }
+ preceding = curr.iterateAxis(AxisInfo.PRECEDING_SIBLING);
+ curr = preceding.next();
+ }
+ if (curr.getParent() instanceof XSLStylesheet) {
+ break;
+ }
+ if (curr instanceof XSLGeneralVariable) {
+ SourceBinding sourceBinding = ((XSLGeneralVariable)curr).getBindingInformation(qName);
+ if (sourceBinding != null) {
+ return sourceBinding;
+ }
+ }
+
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Ask whether variables declared in an "uncle" element are visible.
+ * @return true for all elements except xsl:fallback and saxon:catch
+ */
+
+ protected boolean seesAvuncularVariables() {
+ return true;
+ }
+
+ /**
+ * Get the type of construct. This will be a constant in
+ * class {@link Location}. This method is part of the {@link InstructionInfo} interface
+ */
+
+ public int getConstructType() {
+ return getFingerprint();
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ * If there is no name, the value will be null.
+ * @return the name of the object declared in this element, if any
+ */
+
+ public StructuredQName getObjectName() {
+ return objectName;
+ }
+
+ /**
+ * Set the object name, for example the name of a function, variable, or template declared on this element
+ * @param qName the object name as a QName
+ */
+
+ public void setObjectName(StructuredQName qName) {
+ objectName = qName;
+ }
+
+ /**
+ * Get the value of a particular property of the instruction. This is part of the
+ * {@link InstructionInfo} interface for run-time tracing and debugging. The properties
+ * available include all the attributes of the source instruction (named by the attribute name):
+ * these are all provided as string values.
+ * @param name The name of the required property
+ * @return The value of the requested property, or null if the property is not available
+ */
+
+ public Object getProperty(String name) {
+ return getAttributeValue("", name);
+ }
+
+ /**
+ * Get an iterator over all the properties available. The values returned by the iterator
+ * will be of type String, and each string can be supplied as input to the getProperty()
+ * method to retrieve the value of the property.
+ */
+
+ public Iterator<String> getProperties() {
+ NamePool pool = getNamePool();
+ List<String> list = new ArrayList<String>(10);
+ AxisIterator it = iterateAxis(AxisInfo.ATTRIBUTE);
+ while (true) {
+ NodeInfo a = it.next();
+ if (a == null) {
+ break;
+ }
+ list.add(pool.getClarkName(a.getNameCode()));
+ }
+ return list.iterator();
+ }
+
+ public String getSystemId(long locationId) {
+ return getSystemId();
+ }
+
+ public int getLineNumber(long locationId) {
+ return getLineNumber();
+ }
+
+ public int getColumnNumber(long locationId) {
+ return getColumnNumber();
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+
+ public int getHostLanguage() {
+ return Configuration.XSLT;
+ }
+
+ /**
+ * Ask if an action on this StyleElement has been completed
+ * @param action for example ACTION_VALIDATE
+ * @return true if the action has already been performed
+ */
+
+ public boolean isActionCompleted(int action) {
+ return (actionsCompleted & action) != 0;
+ }
+
+ /**
+ * Say that an action on this StyleElement has been completed
+ * @param action for example ACTION_VALIDATE
+ */
+
+ public void setActionCompleted(int action) {
+ actionsCompleted |= action;
+ }
+
+}
+
diff --git a/sf/saxon/style/StyleNodeFactory.java b/sf/saxon/style/StyleNodeFactory.java
new file mode 100644
index 0000000..8c189e8
--- /dev/null
+++ b/sf/saxon/style/StyleNodeFactory.java
@@ -0,0 +1,452 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.linked.ElementImpl;
+import net.sf.saxon.tree.linked.NodeFactory;
+import net.sf.saxon.tree.linked.TextImpl;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.value.DecimalValue;
+
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import java.util.List;
+
+/**
+ * Class StyleNodeFactory. <br>
+ * A Factory for nodes in the stylesheet tree. <br>
+ * Currently only allows Element nodes to be user-constructed.
+ * @author Michael H. Kay
+ */
+
+public class StyleNodeFactory implements NodeFactory {
+
+
+ protected Configuration config;
+ protected NamePool namePool;
+ protected DecimalValue processorVersion = DecimalValue.TWO;
+
+ /**
+ * Create the node factory for representing an XSLT stylesheet as a tree structure
+ * @param config the Saxon configuration
+ */
+
+ public StyleNodeFactory(Configuration config) {
+ this.config = config;
+ namePool = config.getNamePool();
+ }
+
+ /**
+ * Set the XSLT processor version to be used by this node factory. This must
+ * be 0.0, 2.0 or 3.0. The value 3.0 is ignored in Saxon-HE. The value 0.0 indicates that the
+ * processor version is to be taken from the version attribute of the xsl:stylesheet element.
+ * @param version the XSLT version number, as a decimal
+ */
+
+ public void setXsltProcessorVersion(DecimalValue version) {
+ processorVersion = version;
+ }
+
+ /**
+ * Get the XSLT processor version to be used by this node factory.
+ * @return the processor version (always 2.0 or 3.0 once the stylesheet version attribute
+ * has been read)
+ */
+
+ public DecimalValue getXsltProcessorVersion() {
+ return processorVersion;
+ }
+
+ /**
+ * Create an Element node. Note, if there is an error detected while constructing
+ * the Element, we add the element anyway, and return success, but flag the element
+ * with a validation error. This allows us to report more than
+ * one error from a single compilation.
+ */
+
+ public ElementImpl makeElementNode(
+ NodeInfo parent,
+ NodeName elemName,
+ SchemaType elemType,
+ boolean isNilled,
+ AttributeCollectionImpl attlist,
+ NamespaceBinding[] namespaces,
+ int namespacesUsed,
+ PipelineConfiguration pipe,
+ int locationId,
+ int sequence)
+ {
+ int nameCode = elemName.allocateNameCode(pipe.getConfiguration().getNamePool());
+ boolean toplevel = (parent instanceof XSLStylesheet);
+ String baseURI = null;
+ int lineNumber = -1;
+ int columnNumber = -1;
+ LocationProvider locator = pipe.getLocationProvider();
+ if (locator!=null) {
+ baseURI = locator.getSystemId(locationId);
+ lineNumber = locator.getLineNumber(locationId);
+ columnNumber = locator.getColumnNumber(locationId);
+ }
+
+ if (parent instanceof DataElement) {
+ DataElement d = new DataElement();
+ d.setNamespaceDeclarations(namespaces, namespacesUsed);
+ d.initialise(elemName, elemType, attlist, parent, sequence);
+ d.setLocation(baseURI, lineNumber, columnNumber);
+ return d;
+ }
+
+ int f = nameCode&0xfffff;
+
+ // Try first to make an XSLT element
+
+ StyleElement e = makeXSLElement(f, parent);
+
+ if (e != null) { // recognized as an XSLT element
+
+ e.setNamespaceDeclarations(namespaces, namespacesUsed);
+ e.initialise(elemName, elemType, attlist, parent, sequence);
+ e.setLocation(baseURI, lineNumber, columnNumber);
+ // We're not catching multiple errors in the following attributes, but catching each of the
+ // exceptions helps to ensure we don't report spurious errors through not processing some
+ // of the attributes when others are faulty.
+ try {
+ e.processExtensionElementAttribute("");
+ } catch (TransformerException err) {
+ e.setValidationError(err, StyleElement.REPORT_ALWAYS);
+ }
+ try {
+ e.processExcludedNamespaces("");
+ } catch (TransformerException err) {
+ e.setValidationError(err, StyleElement.REPORT_ALWAYS);
+ }
+ try {
+ e.processVersionAttribute("");
+ } catch (TransformerException err) {
+ e.setValidationError(err, StyleElement.REPORT_ALWAYS);
+ }
+ e.processDefaultXPathNamespaceAttribute("");
+ try {
+ e.processExpandTextAttribute("");
+ } catch (XPathException err) {
+ e.setValidationError(err, StyleElement.REPORT_ALWAYS);
+ }
+
+ if (e instanceof XSLStylesheet && processorVersion.compareTo(DecimalValue.ZERO) == 0) {
+ DecimalValue effectiveVersion = e.getEffectiveVersion();
+ processorVersion = (effectiveVersion.equals(DecimalValue.THREE) ?
+ DecimalValue.THREE : DecimalValue.TWO);
+ }
+ return e;
+
+ }
+
+ short uriCode = namePool.getURICode(nameCode);
+ String uri = namePool.getURI(nameCode);
+
+ if (parent instanceof XSLStylesheet && uriCode != 0 && uriCode != NamespaceConstant.XSLT_CODE) {
+ DataElement d = new DataElement();
+ d.setNamespaceDeclarations(namespaces, namespacesUsed);
+ d.initialise(elemName, elemType, attlist, parent, sequence);
+ d.setLocation(baseURI, lineNumber, columnNumber);
+ return d;
+
+ } else { // not recognized as an XSLT element, not top-level
+
+ String localname = namePool.getLocalName(nameCode);
+ StyleElement temp = null;
+
+ // Detect a misspelt XSLT element, or a 3.0 element used in a 2.0 stylesheet
+
+ if (uri.equals(NamespaceConstant.XSLT)) {
+ if (parent instanceof XSLStylesheet) {
+ if (((XSLStylesheet)parent).getEffectiveVersion().compareTo(DecimalValue.TWO) <= 0 ) {
+ temp = new AbsentExtensionElement();
+ temp.setValidationError(new XPathException("Unknown top-level XSLT declaration"),
+ StyleElement.REPORT_UNLESS_FORWARDS_COMPATIBLE );
+ }
+ } else {
+ temp = new AbsentExtensionElement();
+ temp.initialise(elemName, elemType, attlist, parent, sequence);
+ temp.setLocation(baseURI, lineNumber, columnNumber);
+ try {
+ temp.processStandardAttributes("");
+ if (temp.getEffectiveVersion().compareTo(DecimalValue.TWO) > 0) {
+ temp.setValidationError(new XPathException("Unknown XSLT instruction"),
+ StyleElement.REPORT_UNLESS_FALLBACK_AVAILABLE);
+ } else {
+ temp.setValidationError(new XPathException("Unknown XSLT instruction"),
+ StyleElement.REPORT_IF_INSTANTIATED);
+ }
+ } catch (XPathException err) {
+ temp.setValidationError(err, StyleElement.REPORT_ALWAYS);
+ }
+ }
+ }
+
+ // Detect an unrecognized element in the Saxon namespace
+
+ if (uri.equals(NamespaceConstant.SAXON)) {
+ try {
+ XPathException te = new XPathException(namePool.getDisplayName(nameCode) +
+ " is not recognized as a Saxon instruction");
+ te.setLocator(pipe.getSourceLocation(locationId));
+ te.setErrorCode(SaxonErrorCode.SXWN9008);
+ pipe.getErrorListener().warning(te);
+ } catch (TransformerException e1) {
+ // no action
+ }
+ }
+
+ Class assumedClass = LiteralResultElement.class;
+
+ // We can't work out the final class of the node until we've examined its attributes
+ // such as version and extension-element-prefixes; but we can have a good guess, and
+ // change it later if need be.
+
+ if (temp==null) {
+ temp = new LiteralResultElement();
+ }
+
+ temp.setNamespaceDeclarations(namespaces, namespacesUsed);
+
+ try {
+ temp.initialise(elemName, elemType, attlist, parent, sequence);
+ temp.setLocation(baseURI, lineNumber, columnNumber);
+ temp.processStandardAttributes(NamespaceConstant.XSLT);
+ } catch (XPathException err) {
+ temp.setValidationError(err, StyleElement.REPORT_ALWAYS);
+ }
+
+ // Now we work out what class of element we really wanted, and change it if necessary
+
+ TransformerException reason;
+ Class actualClass;
+
+ if (uri.equals(NamespaceConstant.XSLT)) {
+ reason = new XPathException("Unknown XSLT element: " + localname);
+ ((XPathException)reason).setErrorCode("XTSE0010");
+ ((XPathException)reason).setIsStaticError(true);
+ actualClass = AbsentExtensionElement.class;
+ temp.setValidationError(reason, StyleElement.REPORT_UNLESS_FALLBACK_AVAILABLE);
+
+ } else if (temp.isExtensionNamespace(uri) && !toplevel) {
+
+ // if we can't instantiate an extension element, we don't give up
+ // immediately, because there might be an xsl:fallback defined. We
+ // create a surrogate element called AbsentExtensionElement, and
+ // save the reason for failure just in case there is no xsl:fallback
+
+ actualClass = AbsentExtensionElement.class;
+ XPathException se = new XPathException("Unknown extension instruction", temp);
+ se.setErrorCode("XTDE1450");
+ reason = se;
+ temp.setValidationError(reason, StyleElement.REPORT_IF_INSTANTIATED);
+
+ } else {
+ actualClass = LiteralResultElement.class;
+ }
+
+ StyleElement node;
+ if (actualClass.equals(assumedClass)) {
+ node = temp; // the original element will do the job
+ } else {
+ try {
+ node = (StyleElement)actualClass.newInstance();
+ } catch (InstantiationException err1) {
+ throw new TransformerFactoryConfigurationError(err1, "Failed to create instance of " + actualClass.getName());
+ } catch (IllegalAccessException err2) {
+ throw new TransformerFactoryConfigurationError(err2, "Failed to access class " + actualClass.getName());
+ }
+ node.substituteFor(temp); // replace temporary node with the new one
+ }
+ return node;
+ }
+ }
+
+ /**
+ * Make an XSL element node
+ *
+ * @param f the fingerprint of the node name
+ * @param parent the parent node
+ * @return the constructed element node
+ */
+
+ /*@Nullable*/ protected StyleElement makeXSLElement(int f, NodeInfo parent) {
+ switch (f) {
+ case StandardNames.XSL_ANALYZE_STRING:
+ return new XSLAnalyzeString();
+ case StandardNames.XSL_APPLY_IMPORTS:
+ return new XSLApplyImports();
+ case StandardNames.XSL_APPLY_TEMPLATES:
+ return new XSLApplyTemplates();
+ case StandardNames.XSL_ATTRIBUTE:
+ return new XSLAttribute();
+ case StandardNames.XSL_ATTRIBUTE_SET:
+ return new XSLAttributeSet();
+ case StandardNames.XSL_CALL_TEMPLATE:
+ return new XSLCallTemplate();
+ case StandardNames.XSL_CHARACTER_MAP:
+ return new XSLCharacterMap();
+ case StandardNames.XSL_CHOOSE:
+ return new XSLChoose();
+ case StandardNames.XSL_COMMENT:
+ return new XSLComment();
+ case StandardNames.XSL_COPY:
+ return new XSLCopy();
+ case StandardNames.XSL_COPY_OF:
+ return new XSLCopyOf();
+ case StandardNames.XSL_DECIMAL_FORMAT:
+ return new XSLDecimalFormat();
+ case StandardNames.XSL_DOCUMENT:
+ return new XSLDocument();
+ case StandardNames.XSL_ELEMENT:
+ return new XSLElement();
+ case StandardNames.XSL_FALLBACK:
+ return new XSLFallback();
+ case StandardNames.XSL_FOR_EACH:
+ return new XSLForEach();
+ case StandardNames.XSL_FOR_EACH_GROUP:
+ return new XSLForEachGroup();
+ case StandardNames.XSL_FUNCTION:
+ return new XSLFunction();
+ case StandardNames.XSL_IF:
+ return new XSLIf();
+ case StandardNames.XSL_IMPORT:
+ return new XSLImport();
+ case StandardNames.XSL_IMPORT_SCHEMA:
+ return new XSLImportSchema();
+ case StandardNames.XSL_INCLUDE:
+ return new XSLInclude();
+ case StandardNames.XSL_KEY:
+ return new XSLKey();
+ case StandardNames.XSL_MATCHING_SUBSTRING:
+ return new XSLMatchingSubstring();
+ case StandardNames.XSL_MESSAGE:
+ return new XSLMessage();
+ case StandardNames.XSL_NEXT_MATCH:
+ return new XSLNextMatch();
+ case StandardNames.XSL_NON_MATCHING_SUBSTRING:
+ return new XSLMatchingSubstring(); //sic
+ case StandardNames.XSL_NUMBER:
+ return new XSLNumber();
+ case StandardNames.XSL_NAMESPACE:
+ return new XSLNamespace();
+ case StandardNames.XSL_NAMESPACE_ALIAS:
+ return new XSLNamespaceAlias();
+ case StandardNames.XSL_OTHERWISE:
+ return new XSLOtherwise();
+ case StandardNames.XSL_OUTPUT:
+ return new XSLOutput();
+ case StandardNames.XSL_OUTPUT_CHARACTER:
+ return new XSLOutputCharacter();
+ case StandardNames.XSL_PARAM:
+ return (parent instanceof XSLStylesheet) ? new XSLGlobalParam() : new XSLLocalParam();
+ case StandardNames.XSL_PERFORM_SORT:
+ return new XSLPerformSort();
+ case StandardNames.XSL_PRESERVE_SPACE:
+ return new XSLPreserveSpace();
+ case StandardNames.XSL_PROCESSING_INSTRUCTION:
+ return new XSLProcessingInstruction();
+ case StandardNames.XSL_RESULT_DOCUMENT:
+ return new XSLResultDocument();
+ case StandardNames.XSL_SEQUENCE:
+ return new XSLSequence();
+ case StandardNames.XSL_SORT:
+ return new XSLSort();
+ case StandardNames.XSL_STRIP_SPACE:
+ return new XSLPreserveSpace();
+ case StandardNames.XSL_STYLESHEET:
+ return new XSLStylesheet();
+ case StandardNames.XSL_TEMPLATE:
+ return new XSLTemplate();
+ case StandardNames.XSL_TEXT:
+ return new XSLText();
+ case StandardNames.XSL_TRANSFORM:
+ return new XSLStylesheet();
+ case StandardNames.XSL_VALUE_OF:
+ return new XSLValueOf();
+ case StandardNames.XSL_VARIABLE:
+ return (parent instanceof XSLStylesheet) ? new XSLGlobalVariable() : new XSLLocalVariable();
+ case StandardNames.XSL_WITH_PARAM:
+ return new XSLWithParam();
+ case StandardNames.XSL_WHEN:
+ return new XSLWhen();
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Make a text node
+ *
+ * @param content the content of the text node
+ * @return the constructed text node
+ */
+ public TextImpl makeTextNode(NodeInfo parent, CharSequence content) {
+ return new TextImpl(content.toString());
+ }
+
+ /**
+ * Method to support the element-available() function
+ * @param uri the namespace URI
+ * @param localName the local Name
+ * @return true if an extension element of this name is recognized
+ */
+
+ public boolean isElementAvailable(String uri, String localName) {
+ int fingerprint = namePool.getFingerprint(uri, localName);
+ if (uri.equals(NamespaceConstant.XSLT)) {
+ if (fingerprint == -1) {
+ return false; // all names are pre-registered
+ }
+ StyleElement e = makeXSLElement(fingerprint, null);
+ if (e != null) {
+ return e.isInstruction();
+ }
+ }
+ return false;
+ }
+
+ public void makeAccumulatorManager(PreparedStylesheet pss) {
+ // no action
+ }
+
+ /**
+ * Validate a text node in the stylesheet
+ * @param node the text node
+ * @return true if the node is a text value template containing one or more expressions
+ * @throws XPathException if the node is invalid
+ */
+
+ public boolean validateTextNode(NodeInfo node) throws XPathException {
+ // no action (overridden in Saxon-PE subclass)
+ return false;
+ }
+
+ /**
+ * Compile a content value text node. Dummy implementation (these can exist only in Saxon-PE or higher)
+ */
+
+ public void compileContentValueTemplate(TextImpl node, List<Expression> contents) throws XPathException {
+ // no action
+ }
+
+
+}
+
diff --git a/sf/saxon/style/StylesheetFunctionLibrary.java b/sf/saxon/style/StylesheetFunctionLibrary.java
new file mode 100644
index 0000000..09b12b2
--- /dev/null
+++ b/sf/saxon/style/StylesheetFunctionLibrary.java
@@ -0,0 +1,161 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import com.saxonica.functions.hof.CallableFunctionItem;
+import com.saxonica.functions.hof.SpecificFunctionType;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.UserFunctionCall;
+import net.sf.saxon.expr.instruct.UserFunction;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.query.XQueryFunctionLibrary;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.FunctionItemType;
+
+
+
+/**
+ * A StylesheetFunctionLibrary contains functions defined by the user in a stylesheet. This library is used at
+ * compile time only, as it contains references to the actual XSLFunction objects. Binding to a function in this
+ * library registers the function call on a fix-up list to be notified when the actual compiled function becomes
+ * available.
+ */
+
+public class StylesheetFunctionLibrary implements FunctionLibrary {
+
+ private PrincipalStylesheetModule stylesheet;
+ private boolean overriding;
+
+ /**
+ * Create a FunctionLibrary that provides access to stylesheet functions
+ * @param sheet The XSLStylesheet element of the principal stylesheet module
+ * @param overriding set to true if this library is to contain functions specifying override="yes",
+ * or to false if it is to contain functions specifying override="no". (XSLT uses two instances
+ * of this class, one for overriding functions and one for non-overriding functions.)
+ */
+ public StylesheetFunctionLibrary(PrincipalStylesheetModule sheet, boolean overriding) {
+ this.stylesheet = sheet;
+ this.overriding = overriding;
+ }
+
+ /**
+ * Ask whether the functions in this library are "overriding" functions, that is, defined with
+ * xsl:function override="yes".
+ * @return true if these are overriding functions, false otherwise
+ */
+
+ public boolean isOverriding() {
+ return overriding;
+ }
+
+ /**
+ * Bind a function, given the URI and local parts of the function name,
+ * and the list of expressions supplied as arguments. This method is called at compile
+ * time.
+ *
+ *
+ * @param functionName
+ * @param arity
+ * @param staticArgs The expressions supplied statically in the function call. The intention is
+ * that the static type of the arguments (obtainable via getItemType() and getCardinality() may
+ * be used as part of the binding algorithm.
+ * @param env
+ * @param container
+ * @return An object representing the extension function to be called, if one is found;
+ * null if no extension function was found matching the required name and arity.
+ * @throws net.sf.saxon.trans.XPathException if a function is found with the required name and arity, but
+ * the implementation of the function cannot be loaded or used; or if an error occurs
+ * while searching for the function; or if this function library "owns" the namespace containing
+ * the function call, but no function was found.
+ */
+
+ public Expression bind(StructuredQName functionName, int arity, Expression[] staticArgs, StaticContext env, Container container)
+ throws XPathException {
+ XSLFunction fn = stylesheet.getFunction(functionName, staticArgs.length);
+ if (fn==null) {
+ return null;
+ }
+ if (fn.isOverriding() != overriding) {
+ return null;
+ }
+ UserFunctionCall fc = new UserFunctionCall();
+ fn.registerReference(fc);
+ fc.setFunctionName(functionName);
+ fc.setArguments(staticArgs);
+ fc.setContainer(container);
+ return fc;
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ * <p/>
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ * @throws net.sf.saxon.trans.XPathException
+ * in the event of certain errors, for example attempting to get a function
+ * that is private
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext) throws XPathException {
+ XSLFunction fn = stylesheet.getFunction(functionName, arity);
+ if (fn==null) {
+ return null;
+ }
+ final UserFunction uf = fn.getCompiledFunction();
+ FunctionItemType type = new SpecificFunctionType(fn.getArgumentTypes(), fn.getResultType());
+ if (uf == null) {
+ // not yet compiled
+ XQueryFunctionLibrary.UnresolvedCallable uc = new XQueryFunctionLibrary.UnresolvedCallable(functionName, arity);
+ fn.registerReference(uc);
+ return new CallableFunctionItem(functionName, arity, uc, type);
+ } else {
+ return new CallableFunctionItem(uf);
+ }
+ }
+//#endif
+
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ return stylesheet.getFunction(functionName, arity) != null;
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ *
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ public FunctionLibrary copy() {
+ return this;
+ }
+
+}
+
diff --git a/sf/saxon/style/StylesheetModule.java b/sf/saxon/style/StylesheetModule.java
new file mode 100644
index 0000000..755bb4e
--- /dev/null
+++ b/sf/saxon/style/StylesheetModule.java
@@ -0,0 +1,228 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A stylesheet module represents a module of a stylesheet. It is possible for two modules
+ * to share the same stylesheet tree in the case where two includes or imports reference
+ * the same URI; in this case the two modules will typically have a different import precedence.
+ */
+public class StylesheetModule {
+
+ private XSLStylesheet sourceElement;
+ private int precedence;
+ private int minImportPrecedence;
+ private StylesheetModule importer;
+ boolean wasIncluded;
+
+
+ // the value of the inputTypeAnnotations attribute on this module, combined with the values
+ // on all imported/included modules. This is a combination of the bit-significant values
+ // ANNOTATION_STRIP and ANNOTATION_PRESERVE.
+ private int inputTypeAnnotations = 0;
+
+ // A list of all the declarations in the stylesheet and its descendants, in increasing precedence order
+ protected List<Declaration> topLevel = new ArrayList<Declaration>();
+
+ public StylesheetModule(XSLStylesheet sourceElement, int precedence) {
+ this.sourceElement = sourceElement;
+ this.precedence = precedence;
+ }
+
+ public void setImporter(StylesheetModule importer) {
+ this.importer = importer;
+ }
+
+ public StylesheetModule getImporter() {
+ return importer;
+ }
+
+ /*@NotNull*/ public PrincipalStylesheetModule getPrincipalStylesheetModule() {
+ return importer.getPrincipalStylesheetModule();
+ }
+
+ public XSLStylesheet getSourceElement() {
+ return sourceElement;
+ }
+
+ public int getPrecedence() {
+ return (wasIncluded ? importer.getPrecedence() : precedence); }
+
+ /**
+ * Indicate that this stylesheet was included (by its "importer") using an xsl:include
+ * statement as distinct from xsl:import
+ */
+
+ public void setWasIncluded() {
+ wasIncluded = true;
+ }
+
+ /**
+ * Set the minimum import precedence of this module, that is, the lowest import precedence of the modules
+ * that it imports. This information is used to decide which template rules are eligible for consideration
+ * by xsl:apply-imports
+ * @param min the minimum import precedence
+ */
+
+ public void setMinImportPrecedence(int min) {
+ this.minImportPrecedence = min;
+ }
+
+ /**
+ * Get the minimum import precedence of this module, that is, the lowest import precedence of the modules
+ * that it imports. This information is used to decide which template rules are eligible for consideration
+ * by xsl:apply-imports
+ * @return the minimum import precedence
+ */
+
+ public int getMinImportPrecedence() {
+ return this.minImportPrecedence;
+ }
+
+ /**
+ * Process xsl:include and xsl:import elements.
+ */
+
+ public void spliceIncludes() throws XPathException {
+
+ boolean foundNonImport = false;
+ topLevel = new ArrayList<Declaration>(50);
+ minImportPrecedence = precedence;
+ StyleElement previousElement = sourceElement;
+
+ AxisIterator kids = sourceElement.iterateAxis(AxisInfo.CHILD);
+
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child.getNodeKind() == Type.TEXT) {
+ // in an embedded stylesheet, white space nodes may still be there
+ if (!Whitespace.isWhite(child.getStringValueCS())) {
+ previousElement.compileError(
+ "No character data is allowed between top-level elements", "XTSE0120");
+ }
+
+ } else if (child instanceof DataElement) {
+ foundNonImport = true;
+ } else {
+ previousElement = (StyleElement) child;
+ if (child instanceof XSLGeneralIncorporate) {
+ XSLGeneralIncorporate xslinc = (XSLGeneralIncorporate) child;
+ xslinc.processAttributes();
+
+ if (xslinc.isImport()) {
+ if (foundNonImport && !sourceElement.isXslt30Processor()) {
+ xslinc.compileError("xsl:import elements must come first", "XTSE0200");
+ }
+ } else {
+ foundNonImport = true;
+ }
+
+ // get the included stylesheet. This follows the URL, builds a tree, and splices
+ // in any indirectly-included stylesheets.
+
+ xslinc.validateInstruction();
+ int errors = sourceElement.getPreparedStylesheet().getErrorCount();
+ StylesheetModule inc =
+ xslinc.getIncludedStylesheet(this, precedence);
+ if (inc == null) {
+ return; // error has been reported
+ }
+ errors = sourceElement.getPreparedStylesheet().getErrorCount() - errors;
+ if (errors > 0) {
+ xslinc.compileError("Reported " + errors + (errors==1 ? " error" : " errors") +
+ " in " + (xslinc.isImport() ? "imported" : "included") +
+ " stylesheet module", "XTSE0165");
+ }
+
+ // after processing the imported stylesheet and any others it brought in,
+ // adjust the import precedence of this stylesheet if necessary
+
+ if (xslinc.isImport()) {
+ precedence = inc.getPrecedence() + 1;
+ } else {
+ precedence = inc.getPrecedence();
+ inc.setMinImportPrecedence(minImportPrecedence);
+ inc.setWasIncluded();
+ }
+
+ // copy the top-level elements of the included stylesheet into the top level of this
+ // stylesheet. Normally we add these elements at the end, in order, but if the precedence
+ // of an element is less than the precedence of the previous element, we promote it.
+ // This implements the requirement in the spec that when xsl:include is used to
+ // include a stylesheet, any xsl:import elements in the included document are moved
+ // up in the including document to after any xsl:import elements in the including
+ // document.
+
+ List<Declaration> incchildren = inc.topLevel;
+ for (int j = 0; j < incchildren.size(); j++) {
+ Declaration decl = incchildren.get(j);
+ //StyleElement elem = decl.getSourceElement();
+ int last = topLevel.size() - 1;
+ if (last < 0 || decl.getPrecedence() >= topLevel.get(last).getPrecedence()) {
+ topLevel.add(decl);
+ } else {
+ while (last >= 0 && decl.getPrecedence() < topLevel.get(last).getPrecedence()) {
+ last--;
+ }
+ topLevel.add(last + 1, decl);
+ }
+ }
+ } else {
+ foundNonImport = true;
+ Declaration decl = new Declaration(this, (StyleElement)child);
+ topLevel.add(decl);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the value of the input-type-annotations attribute, for this module combined with that
+ * of all included/imported modules. The value is an or-ed combination of the two bits
+ * {@link XSLStylesheet#ANNOTATION_STRIP} and {@link XSLStylesheet#ANNOTATION_PRESERVE}
+ * @return the value of the input-type-annotations attribute, for this module combined with that
+ * of all included/imported modules
+ */
+
+ public int getInputTypeAnnotations() {
+ return inputTypeAnnotations;
+ }
+
+ /**
+ * Set the value of the input-type-annotations attribute, for this module combined with that
+ * of all included/imported modules. The value is an or-ed combination of the two bits
+ * {@link XSLStylesheet#ANNOTATION_STRIP} and {@link XSLStylesheet#ANNOTATION_PRESERVE}
+ * @param annotations the value of the input-type-annotations attribute, for this module combined with that
+ * of all included/imported modules.
+ */
+
+ public void setInputTypeAnnotations(int annotations) throws XPathException {
+ inputTypeAnnotations |= annotations;
+ if (inputTypeAnnotations == (XSLStylesheet.ANNOTATION_STRIP | XSLStylesheet.ANNOTATION_PRESERVE)) {
+ getPrincipalStylesheetModule().compileError(
+ "One stylesheet module specifies input-type-annotations='strip', " +
+ "another specifies input-type-annotations='preserve'", "XTSE0265");
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/style/StylesheetProcedure.java b/sf/saxon/style/StylesheetProcedure.java
new file mode 100644
index 0000000..2c27fab
--- /dev/null
+++ b/sf/saxon/style/StylesheetProcedure.java
@@ -0,0 +1,37 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This interface is implemented by all XSL elements that can contain local variable declarations.
+ * Specifically, a top-level xsl:template, xsl:variable, xsl:param, or xsl:function element
+ * or an xsl:attribute-set element or xsl:key element.
+ */
+
+public interface StylesheetProcedure {
+
+ /**
+ * Get the SlotManager associated with this stylesheet construct. The SlotManager contains the
+ * information needed to manage the local stack frames used by run-time instances of the code.
+ * @return the associated SlotManager object
+ */
+
+ public SlotManager getSlotManager();
+
+ /**
+ * Optimize the stylesheet construct
+ * @param declaration
+ */
+
+ public void optimize(Declaration declaration) throws XPathException;
+
+}
+
diff --git a/sf/saxon/style/UseWhenFilter.java b/sf/saxon/style/UseWhenFilter.java
new file mode 100644
index 0000000..a528d5d
--- /dev/null
+++ b/sf/saxon/style/UseWhenFilter.java
@@ -0,0 +1,431 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.StartTagBuffer;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * This is a filter inserted into the input pipeline for processing stylesheet modules, whose
+ * task is to evaluate use-when expressions and discard those parts of the stylesheet module
+ * for which the use-when attribute evaluates to false.
+ */
+
+public class UseWhenFilter extends ProxyReceiver {
+
+ private StartTagBuffer startTag;
+ private int depthOfHole = 0;
+ private boolean emptyStylesheetElement = false;
+ private Stack defaultNamespaceStack = new Stack();
+ private DateTimeValue currentDateTime = DateTimeValue.getCurrentDateTime(null);
+ private PreparedStylesheet preparedStylesheet;
+ private Map<StructuredQName, GroundedValue> staticVariables;
+
+
+ /**
+ * Create a UseWhenFilter
+ *
+ * @param preparedStylesheet the containing stylesheet
+ * @param next the next receiver in the pipeline
+ */
+
+ public UseWhenFilter(PreparedStylesheet preparedStylesheet, Receiver next) {
+ super(next);
+ this.preparedStylesheet = preparedStylesheet;
+ staticVariables = new HashMap<StructuredQName, GroundedValue>();
+ }
+
+ /**
+ * Set the start tag buffer
+ *
+ * @param startTag a preceding filter on the pipeline that buffers the attributes of a start tag
+ */
+
+ public void setStartTagBuffer(StartTagBuffer startTag) {
+ this.startTag = startTag;
+ }
+
+ /**
+ * Start of document
+ */
+
+ public void open() throws XPathException {
+ nextReceiver.open();
+ }
+
+ /**
+ * Notify the start of an element.
+ *
+ * @param elemName the name of the element.
+ * @param typeCode integer code identifying the element's type within the name pool.
+ * @param properties bit-significant properties of the element node
+ */
+
+ public void startElement(NodeName elemName, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ boolean inXsltNamespace = elemName.isInNamespace(NamespaceConstant.XSLT);
+ String stdAttUri = (inXsltNamespace ? "" : NamespaceConstant.XSLT);
+ defaultNamespaceStack.push(startTag.getAttribute(stdAttUri, "xpath-default-namespace"));
+ if (emptyStylesheetElement) {
+ depthOfHole++;
+ return;
+ }
+ if (depthOfHole == 0) {
+ LocationProvider lp = getPipelineConfiguration().getLocationProvider();
+
+ String useWhen = startTag.getAttribute(stdAttUri, "use-when");
+
+ boolean isStylesheetElement = inXsltNamespace &&
+ (elemName.getLocalPart().equals("stylesheet") || elemName.getLocalPart().equals("transform"));
+
+ if (isStylesheetElement) {
+ String version = startTag.getAttribute("", "version");
+ if ("3.0".equals(version)) {
+ preparedStylesheet.setHostLanguage(Configuration.XSLT, true);
+ }
+ }
+
+ if (useWhen != null) {
+ try {
+ boolean use = evaluateUseWhen(useWhen, lp.getLineNumber(locationId));
+ if (!use) {
+ if (isStylesheetElement) {
+ emptyStylesheetElement = true;
+ } else {
+ depthOfHole = 1;
+ return;
+ }
+ }
+ } catch (XPathException e) {
+ XPathException err = createXPathException(
+ "Error in use-when expression. " + e.getMessage(), e.getErrorCodeLocalPart(), lp, locationId);
+ err.setErrorCodeQName(e.getErrorCodeQName());
+ throw err;
+ }
+ }
+
+ if (inXsltNamespace && preparedStylesheet.isAllowXPath30()) {
+ boolean isVariable = elemName.getLocalPart().equals("variable");
+ boolean isParam = elemName.getLocalPart().equals("param");
+
+ // Note, the general policy here is to ignore errors on this initial pass through the stylesheet,
+ // if they will be checked and reported more thoroughly later on.
+
+ if ((isVariable || isParam) &&
+ defaultNamespaceStack.size() == 2 &&
+ "yes".equals(Whitespace.trim(startTag.getAttribute("", "static")))) {
+
+ String nameStr = startTag.getAttribute("", "name");
+ String asStr = startTag.getAttribute("", "as");
+ boolean isRequired = "yes".equals(Whitespace.trim(startTag.getAttribute("", "required")));
+
+ UseWhenStaticContext staticContext = new UseWhenStaticContext(getConfiguration(), startTag, staticVariables);
+ SequenceType requiredType = SequenceType.ANY_SEQUENCE;
+ if (asStr != null) {
+ ExpressionParser parser = new ExpressionParser();
+ requiredType = parser.parseSequenceType(asStr, staticContext);
+ }
+
+ StructuredQName varName;
+ try {
+ varName = StructuredQName.fromLexicalQName(nameStr, false, true, getConfiguration().getNameChecker(), startTag);
+ } catch (XPathException err) {
+ throw createXPathException(
+ "Invalid variable name:" + nameStr + ". " + err.getMessage(),
+ err.getErrorCodeLocalPart(), lp, locationId);
+ }
+
+ boolean isSupplied = isParam && preparedStylesheet.getCompilerInfo().getParameters().containsKey(varName);
+ if (isParam) {
+ if (isRequired && !isSupplied) {
+ throw createXPathException(
+ "No value was supplied for the required static parameter " + varName.getDisplayName(),
+ "XTDE0050", lp, locationId);
+ }
+
+ if (isSupplied) {
+ Sequence suppliedValue = preparedStylesheet.getCompilerInfo().getParameters()
+ .convertParameterValue(varName, requiredType, true, staticContext.makeEarlyEvaluationContext());
+
+ staticVariables.put(varName, SequenceTool.toGroundedValue(suppliedValue));
+ }
+ }
+
+ if (isVariable || !isSupplied) {
+ String selectStr = startTag.getAttribute("", "select");
+ Sequence value;
+ if (selectStr == null) {
+ if (isVariable) {
+ throw createXPathException(
+ "The select attribute is required for a static global variable",
+ "XTSE0010", lp, locationId);
+ } else {
+ value = EmptySequence.getInstance();
+ staticVariables.put(varName, EmptySequence.getInstance());
+ }
+
+ } else {
+ try {
+ value = evaluateStatic(selectStr, lp.getLineNumber(locationId));
+ } catch (XPathException e) {
+ throw createXPathException("Error in " + elemName.getLocalPart() + " expression. " + e.getMessage(),
+ e.getErrorCodeLocalPart(), lp, locationId);
+ }
+ }
+ SourceLocator locator = new ExpressionLocation(lp, locationId);
+ RoleLocator role = new RoleLocator(RoleLocator.VARIABLE, varName, 0);
+ TypeHierarchy th = getConfiguration().getTypeHierarchy();
+ value = th.applyFunctionConversionRules(value, requiredType, role, locator);
+ staticVariables.put(varName, SequenceTool.toGroundedValue(value));
+ }
+ }
+ }
+
+ nextReceiver.startElement(elemName, typeCode, locationId, properties);
+ } else {
+ depthOfHole++;
+ }
+ }
+
+ public XPathException createXPathException(String message, String errorCode, LocationProvider lp, int locationId) throws XPathException {
+
+ XPathException err = new XPathException(message);
+ err.setErrorCode(errorCode);
+ err.setIsStaticError(true);
+ ExpressionLocation loc = new ExpressionLocation();
+ loc.setSystemId(lp.getSystemId(locationId));
+ loc.setLineNumber(lp.getLineNumber(locationId));
+ err.setLocator(loc);
+
+ try {
+ getPipelineConfiguration().getErrorListener().fatalError(err);
+ } catch (TransformerException tex) {
+ throw XPathException.makeXPathException(tex);
+ }
+ err.setHasBeenReported(true);
+ return err;
+ }
+
+ /**
+ * Notify a namespace. Namespaces are notified <b>after</b> the startElement event, and before
+ * any children for the element. The namespaces that are reported are only required
+ * to include those that are different from the parent element; however, duplicates may be reported.
+ * A namespace must not conflict with any namespaces already used for element or attribute names.
+ *
+ * @param namespaceBinding the namespace to be notified
+ * @throws IllegalStateException: attempt to output a namespace when there is no open element
+ * start tag
+ */
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ if (depthOfHole == 0) {
+ nextReceiver.namespace(namespaceBinding, properties);
+ }
+ }
+
+ /**
+ * Notify an attribute. Attributes are notified after the startElement event, and before any
+ * children. Namespaces and attributes may be intermingled.
+ *
+ * @param attName The name of the attribute, as held in the name pool
+ * @param typeCode The type of the attribute, as held in the name pool
+ * @param properties Bit significant value. The following bits are defined:
+ * <dd>DISABLE_ESCAPING</dd> <dt>Disable escaping for this attribute</dt>
+ * <dd>NO_SPECIAL_CHARACTERS</dd> <dt>Attribute value contains no special characters</dt>
+ * @throws IllegalStateException: attempt to output an attribute when there is no open element
+ * start tag
+ */
+
+ public void attribute(NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ if (depthOfHole == 0) {
+ nextReceiver.attribute(attName, typeCode, value, locationId, properties);
+ }
+ }
+
+ /**
+ * Notify the start of the content, that is, the completion of all attributes and namespaces.
+ * Note that the initial receiver of output from XSLT instructions will not receive this event,
+ * it has to detect it itself. Note that this event is reported for every element even if it has
+ * no attributes, no namespaces, and no content.
+ */
+
+ public void startContent() throws XPathException {
+ if (depthOfHole == 0) {
+ nextReceiver.startContent();
+ }
+ }
+
+ /**
+ * End of element
+ */
+
+ public void endElement() throws XPathException {
+ defaultNamespaceStack.pop();
+ if (depthOfHole > 0) {
+ depthOfHole--;
+ } else {
+ nextReceiver.endElement();
+ }
+ }
+
+ /**
+ * Character data
+ */
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (depthOfHole == 0) {
+ nextReceiver.characters(chars, locationId, properties);
+ }
+ }
+
+ /**
+ * Processing Instruction
+ */
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) {
+ // these are ignored in a stylesheet
+ }
+
+ /**
+ * Output a comment
+ */
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ // these are ignored in a stylesheet
+ }
+
+ /**
+ * Evaluate a use-when attribute
+ *
+ * @param expression the expression to be evaluated
+ * @param locationId identifies the location of the expression in case error need to be reported
+ * @return the effective boolean value of the result of evaluating the expression
+ */
+
+ public boolean evaluateUseWhen(String expression, int locationId) throws XPathException {
+ UseWhenStaticContext staticContext = new UseWhenStaticContext(getConfiguration(), startTag, staticVariables);
+ LocationProvider lp = getPipelineConfiguration().getLocationProvider();
+ // TODO: The following doesn't take account of xml:base attributes
+ staticContext.setBaseURI(lp.getSystemId(locationId));
+ staticContext.setDefaultElementNamespace(NamespaceConstant.NULL);
+ for (int i = defaultNamespaceStack.size() - 1; i >= 0; i--) {
+ String uri = (String) defaultNamespaceStack.get(i);
+ if (uri != null) {
+ staticContext.setDefaultElementNamespace(uri);
+ break;
+ }
+ }
+ Expression expr = ExpressionTool.make(expression, staticContext,
+ staticContext, 0, Token.EOF, lp.getLineNumber(locationId), null);
+ expr.setContainer(staticContext);
+ ItemType contextItemType = Type.ITEM_TYPE;
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(contextItemType, true);
+ ExpressionVisitor visitor = ExpressionVisitor.make(staticContext, staticContext.getExecutable());
+ expr = visitor.typeCheck(expr, cit);
+ SlotManager stackFrameMap = getPipelineConfiguration().getConfiguration().makeSlotManager();
+ ExpressionTool.allocateSlots(expr, stackFrameMap.getNumberOfVariables(), stackFrameMap);
+ Controller controller = new Controller(getConfiguration());
+ controller.setURIResolver(new URIPreventer());
+ controller.setCurrentDateTime(currentDateTime);
+ // this is to ensure that all use-when expressions in a module use the same date and time
+ XPathContext dynamicContext = controller.newXPathContext();
+ dynamicContext = dynamicContext.newCleanContext();
+ ((XPathContextMajor) dynamicContext).openStackFrame(stackFrameMap);
+ return expr.effectiveBooleanValue(dynamicContext);
+ }
+
+
+ /**
+ * Evaluate a use-when attribute
+ *
+ * @param expression the expression to be evaluated
+ * @param locationId identifies the location of the expression in case error need to be reported
+ * @return the effective boolean value of the result of evaluating the expression
+ */
+
+ public Sequence evaluateStatic(String expression, int locationId) throws XPathException {
+ UseWhenStaticContext staticContext = new UseWhenStaticContext(getConfiguration(), startTag, staticVariables);
+ LocationProvider lp = getPipelineConfiguration().getLocationProvider();
+ // TODO: The following doesn't take account of xml:base attributes
+ staticContext.setBaseURI(lp.getSystemId(locationId));
+ staticContext.setDefaultElementNamespace(NamespaceConstant.NULL);
+ for (int i = defaultNamespaceStack.size() - 1; i >= 0; i--) {
+ String uri = (String) defaultNamespaceStack.get(i);
+ if (uri != null) {
+ staticContext.setDefaultElementNamespace(uri);
+ break;
+ }
+ }
+ Expression expr = ExpressionTool.make(expression, staticContext,
+ staticContext, 0, Token.EOF, lp.getLineNumber(locationId), null);
+ expr.setContainer(staticContext);
+ ItemType contextItemType = Type.ITEM_TYPE;
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(contextItemType, true);
+ ExpressionVisitor visitor = ExpressionVisitor.make(staticContext, staticContext.getExecutable());
+ expr = visitor.typeCheck(expr, cit);
+ SlotManager stackFrameMap = getPipelineConfiguration().getConfiguration().makeSlotManager();
+ ExpressionTool.allocateSlots(expr, stackFrameMap.getNumberOfVariables(), stackFrameMap);
+ Controller controller = new Controller(getConfiguration());
+ controller.setURIResolver(new URIPreventer());
+ controller.setCurrentDateTime(currentDateTime);
+ // this is to ensure that all use-when expressions in a module use the same date and time
+ XPathContext dynamicContext = controller.newXPathContext();
+ dynamicContext = dynamicContext.newCleanContext();
+ ((XPathContextMajor) dynamicContext).openStackFrame(stackFrameMap);
+ return SequenceExtent.makeSequenceExtent(expr.iterate(dynamicContext));
+ }
+
+ /**
+ * Define a URIResolver that disallows all URIs
+ */
+
+ private static class URIPreventer implements URIResolver {
+ /**
+ * Called by the processor when it encounters
+ * an xsl:include, xsl:import, or document() function.
+ *
+ * @param href An href attribute, which may be relative or absolute.
+ * @param base The base URI against which the first argument will be made
+ * absolute if the absolute URI is required.
+ * @return A Source object, or null if the href cannot be resolved,
+ * and the processor should try to resolve the URI itself.
+ * @throws javax.xml.transform.TransformerException
+ * if an error occurs when trying to
+ * resolve the URI.
+ */
+ /*@NotNull*/
+ public Source resolve(String href, String base) throws TransformerException {
+ throw new TransformerException("No external documents are available within an [xsl]use-when expression");
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/style/UseWhenStaticContext.java b/sf/saxon/style/UseWhenStaticContext.java
new file mode 100644
index 0000000..2c8e432
--- /dev/null
+++ b/sf/saxon/style/UseWhenStaticContext.java
@@ -0,0 +1,292 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.functions.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.sxpath.AbstractStaticContext;
+import net.sf.saxon.trans.DecimalFormatManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class implements the static context used for evaluating use-when expressions in XSLT 2.0
+ * A new instance of this class is created for each use-when expression encountered; there are
+ * therefore no issues with reusability. The class provides a Container for the expression as well
+ * as the static context information; the Executable contains the single XPath expression only, and
+ * is created for the purpose.
+ */
+
+public class UseWhenStaticContext extends AbstractStaticContext implements XSLTStaticContext, Container {
+
+ private NamespaceResolver namespaceContext;
+ private FunctionLibrary functionLibrary;
+ private Executable executable;
+ private Map<StructuredQName, GroundedValue> staticVariables;
+
+ /**
+ * Create a static context for evaluating use-when expressions
+ *
+ * @param config the Saxon configuration
+ * @param namespaceContext the namespace context in which the use-when expression appears
+ * @param staticVariables the static variables available for use within use-when expressions
+ */
+
+ public UseWhenStaticContext(Configuration config, NamespaceResolver namespaceContext, Map<StructuredQName, GroundedValue> staticVariables) {
+ setConfiguration(config);
+ this.namespaceContext = namespaceContext;
+
+ FunctionLibraryList lib = new FunctionLibraryList();
+ lib.addFunctionLibrary(SystemFunctionLibrary.getSystemFunctionLibrary(
+ StandardFunction.CORE | StandardFunction.USE_WHEN));
+ lib.addFunctionLibrary(getConfiguration().getVendorFunctionLibrary());
+ lib.addFunctionLibrary(new ConstructorFunctionLibrary(getConfiguration()));
+ lib.addFunctionLibrary(config.getIntegratedFunctionLibrary());
+ config.addExtensionBinders(lib);
+ functionLibrary = lib;
+ executable = new Executable(config);
+ this.staticVariables = staticVariables;
+ }
+
+ /**
+ * Get the Executable representing the containing XSLT stylesheet
+ *
+ * @return the Executable
+ */
+
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Issue a compile-time warning
+ */
+
+ public void issueWarning(String s, SourceLocator locator) {
+ XPathException err = new XPathException(s);
+ err.setLocator(locator);
+ try {
+ getConfiguration().getErrorListener().warning(err);
+ } catch (TransformerException e) {
+ // ignore response
+ }
+ }
+
+ /**
+ * Get the System ID of the container of the expression. This is the containing
+ * entity (file) and is therefore useful for diagnostics. Use getBaseURI() to get
+ * the base URI, which may be different.
+ */
+
+ public String getSystemId() {
+ return getBaseURI();
+ }
+
+ /**
+ * Get the granularity of the container.
+ *
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 1;
+ }
+
+ /**
+ * Get the URI for a namespace prefix. The default namespace is NOT used
+ * when the prefix is empty.
+ *
+ * @param prefix The prefix
+ * @throws net.sf.saxon.trans.XPathException
+ * if the prefix is not declared
+ */
+
+ public String getURIForPrefix(String prefix) throws XPathException {
+ String uri = namespaceContext.getURIForPrefix(prefix, false);
+ if (uri == null) {
+ XPathException err = new XPathException("Namespace prefix '" + prefix + "' has not been declared");
+ err.setErrorCode("XTDE0290");
+ throw err;
+ }
+ return uri;
+ }
+
+ /**
+ * Bind a variable used in this element to its declaration
+ *
+ * @param qName the name of the variable
+ * @return an expression representing the variable reference, This will often be
+ * a {@link net.sf.saxon.expr.VariableReference}, suitably initialized to refer to the corresponding variable declaration,
+ * but in general it can be any expression which returns the variable's value when evaluated. In this version of the method,
+ * the value of the variable is known statically, so the returned expression is a literal containing the variable's value.
+ */
+
+ public Expression bindVariable(StructuredQName qName) throws XPathException {
+ if (staticVariables != null) {
+ GroundedValue val = staticVariables.get(qName);
+ if (val != null) {
+ return Literal.makeLiteral(val);
+ }
+ }
+ XPathException err = new XPathException("Variables (other than XSLT 3.0 static variables) cannot be used in a use-when expression");
+ err.setErrorCode("XPST0008");
+ err.setIsStaticError(true);
+ throw err;
+ }
+
+ /**
+ * Get the function library containing all the in-scope functions available in this static
+ * context
+ */
+
+ public FunctionLibrary getFunctionLibrary() {
+ return functionLibrary;
+ }
+
+ /**
+ * Get a named collation.
+ *
+ * @param name The name of the required collation. Supply null to get the default collation.
+ * @return the collation; or null if the required collation is not found.
+ */
+
+// /*@Nullable*/ public StringCollator getCollation(String name) {
+// return null;
+// }
+
+ /**
+ * Get the name of the default collation.
+ *
+ * @return the name of the default collation; or the name of the codepoint collation
+ * if no default collation has been defined
+ */
+
+ public String getDefaultCollationName() {
+ return NamespaceConstant.CODEPOINT_COLLATION_URI;
+ }
+
+ /**
+ * Get the default function namespace
+ */
+
+ public String getDefaultFunctionNamespace() {
+ return NamespaceConstant.FN;
+ }
+
+ /**
+ * Determine whether Backwards Compatible Mode is used
+ */
+
+ public boolean isInBackwardsCompatibleMode() {
+ return false;
+ }
+
+ /**
+ * Determine whether a Schema for a given target namespace has been imported. Note that the
+ * in-scope element declarations, attribute declarations and schema types are the types registered
+ * with the (schema-aware) configuration, provided that their namespace URI is registered
+ * in the static context as being an imported schema namespace. (A consequence of this is that
+ * within a Configuration, there can only be one schema for any given namespace, including the
+ * null namespace).
+ */
+
+ public boolean isImportedSchema(String namespace) {
+ return false;
+ }
+
+ /**
+ * Get the set of imported schemas
+ *
+ * @return a Set, the set of URIs representing the names of imported schemas
+ */
+
+ public Set<String> getImportedSchemaNamespaces() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * Determine whether a built-in type is available in this context. This method caters for differences
+ * between host languages as to which set of types are built in.
+ *
+ * @param type the supposedly built-in type. This will always be a type in the
+ * XS or XDT namespace.
+ * @return true if this type can be used in this static context
+ */
+
+ public boolean isAllowedBuiltInType(BuiltInAtomicType type) {
+ Configuration config = getConfiguration();
+ if (type.getFingerprint() == StandardNames.XS_DATE_TIME_STAMP) {
+ return config.getXsdVersion() == Configuration.XSD11;
+ }
+ return isSchemaAware() || type.isAllowedInBasicXSLT();
+ }
+
+ /**
+ * Get a namespace resolver to resolve the namespaces declared in this static context.
+ *
+ * @return a namespace resolver.
+ */
+
+ public NamespaceResolver getNamespaceResolver() {
+ return namespaceContext;
+ }
+
+ /**
+ * Get a DecimalFormatManager to resolve the names of decimal formats used in calls
+ * to the format-number() function.
+ *
+ * @return the decimal format manager for this static context, or null if named decimal
+ * formats are not supported in this environment.
+ */
+
+ public DecimalFormatManager getDecimalFormatManager() {
+ return null;
+ }
+
+ /**
+ * Determine if an extension element is available
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if the name is invalid or the prefix is not declared
+ */
+
+ public boolean isElementAvailable(String qname) throws XPathException {
+ try {
+ String[] parts = getConfiguration().getNameChecker().getQNameParts(qname);
+ String uri;
+ if (parts[0].length() == 0) {
+ uri = getDefaultElementNamespace();
+ } else {
+ uri = getURIForPrefix(parts[0]);
+ }
+ StyleNodeFactory factory = getConfiguration().makeStyleNodeFactory();
+ factory.setXsltProcessorVersion(getXPathLanguageLevel());
+ return factory.isElementAvailable(uri, parts[1]);
+ } catch (QNameException e) {
+ XPathException err = new XPathException("Invalid element name. " + e.getMessage());
+ err.setErrorCode("XTDE1440");
+ throw err;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLAnalyzeString.java b/sf/saxon/style/XSLAnalyzeString.java
new file mode 100644
index 0000000..9e4ec4c
--- /dev/null
+++ b/sf/saxon/style/XSLAnalyzeString.java
@@ -0,0 +1,226 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.AnalyzeString;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.regex.RegularExpression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.DecimalValue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* An xsl:analyze-string elements in the stylesheet. New at XSLT 2.0<BR>
+*/
+
+public class XSLAnalyzeString extends StyleElement {
+
+ /*@Nullable*/ private Expression select;
+ private Expression regex;
+ private Expression flags;
+ private StyleElement matching;
+ private StyleElement nonMatching;
+ private RegularExpression pattern;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain an xsl:fallback
+ * instruction
+ */
+
+ public boolean mayContainFallback() {
+ return true;
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return getCommonChildItemType();
+ }
+
+ public void prepareAttributes() throws XPathException {
+ String selectAtt = null;
+ String regexAtt = null;
+ String flagsAtt = null;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.REGEX)) {
+ regexAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.FLAGS)) {
+ flagsAtt = atts.getValue(a); // not trimmed, see bugzilla 4315
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt==null) {
+ reportAbsence("select");
+ selectAtt = "."; // for error recovery
+ }
+ select = makeExpression(selectAtt);
+
+ if (regexAtt==null) {
+ reportAbsence("regex");
+ regexAtt = "xxx"; // for error recovery
+ }
+ regex = makeAttributeValueTemplate(regexAtt);
+
+ if (flagsAtt==null) {
+ flagsAtt = "";
+ }
+ flags = makeAttributeValueTemplate(flagsAtt);
+
+ if (regex instanceof StringLiteral && flags instanceof StringLiteral) {
+ try {
+ final String regex = ((StringLiteral)this.regex).getStringValue();
+ final String flagstr = ((StringLiteral)flags).getStringValue();
+
+ List<String> warnings = new ArrayList<String>();
+ pattern = Configuration.getPlatform().compileRegularExpression(
+ regex, flagstr, (getEffectiveVersion().equals(DecimalValue.THREE)? "XP30" : "XP20"), warnings);
+ for (String w : warnings) {
+ issueWarning(w, this);
+ }
+ if (pattern.matches("")) {
+ invalidRegex("The regular expression must not be one that matches a zero-length string", "XTDE1150");
+ }
+ } catch (XPathException err) {
+ if ("FORX0001".equals(err.getErrorCodeLocalPart())) {
+ invalidRegex("Error in regular expression flags: " + err, "XTDE1145");
+ } else {
+ invalidRegex("Error in regular expression: " + err, "XTDE1140");
+ }
+ }
+ }
+
+ }
+
+ private void invalidRegex(String message, String errorCode) throws XPathException {
+ compileError(message, errorCode);
+ // prevent it being reported more than once
+ pattern = Configuration.getPlatform().compileRegularExpression("x", "", "XP20", null);
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ //checkWithinTemplate();
+
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while(true) {
+ NodeInfo curr = kids.next();
+ if (curr == null) {
+ break;
+ }
+ if (curr instanceof XSLFallback) {
+ // no-op
+ } else if (curr instanceof XSLMatchingSubstring) {
+ boolean b = curr.getLocalPart().equals("matching-substring");
+ if (b) {
+ if (matching!=null) {
+ compileError("xsl:matching-substring element must only appear once", "XTSE0010");
+ }
+ matching = (StyleElement)curr;
+ } else {
+ if (nonMatching!=null) {
+ compileError("xsl:non-matching-substring element must only appear once", "XTSE0010");
+ }
+ nonMatching = (StyleElement)curr;
+ }
+ } else {
+ compileError("Only xsl:matching-substring and xsl:non-matching-substring are allowed here", "XTSE0010");
+ }
+ }
+
+ if (matching==null && nonMatching==null) {
+ compileError("At least one xsl:matching-substring or xsl:non-matching-substring element must be present",
+ "XTSE1130");
+ }
+
+ select = typeCheck("select", select);
+ regex = typeCheck("regex", regex);
+ flags = typeCheck("flags", flags);
+
+ // following code removed because it's done again within AnalyzeString.typeCheck()
+// try {
+// ExpressionVisitor visitor = makeExpressionVisitor();
+//
+// RoleLocator role =
+// new RoleLocator(RoleLocator.INSTRUCTION, "xsl:analyze-string/select", 0);
+// select = TypeChecker.staticTypeCheck(select, SequenceType.SINGLE_STRING, false, role,
+// visitor);
+//
+// role =
+// new RoleLocator(RoleLocator.INSTRUCTION, "xsl:analyze-string/regex", 0);
+// regex = TypeChecker.staticTypeCheck(regex, SequenceType.SINGLE_STRING, false, role, visitor);
+//
+// role =
+// new RoleLocator(RoleLocator.INSTRUCTION, "xsl:analyze-string/flags", 0);
+// flags = TypeChecker.staticTypeCheck(flags, SequenceType.SINGLE_STRING, false, role, visitor);
+// } catch (XPathException err) {
+// compileError(err);
+// }
+
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ Expression matchingBlock = null;
+ if (matching != null) {
+ matchingBlock = matching.compileSequenceConstructor(exec, decl, matching.iterateAxis(AxisInfo.CHILD), false);
+ }
+
+ Expression nonMatchingBlock = null;
+ if (nonMatching != null) {
+ nonMatchingBlock = nonMatching.compileSequenceConstructor(exec, decl, nonMatching.iterateAxis(AxisInfo.CHILD), false);
+ }
+
+ try {
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ return new AnalyzeString(select,
+ regex,
+ flags,
+ (matchingBlock==null ? null : matchingBlock.simplify(visitor)),
+ (nonMatchingBlock==null ? null : nonMatchingBlock.simplify(visitor)),
+ pattern );
+ } catch (XPathException e) {
+ compileError(e);
+ return null;
+ }
+ }
+
+
+
+}
+
diff --git a/sf/saxon/style/XSLApplyImports.java b/sf/saxon/style/XSLApplyImports.java
new file mode 100644
index 0000000..f973cfe
--- /dev/null
+++ b/sf/saxon/style/XSLApplyImports.java
@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.ApplyImports;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* An xsl:apply-imports element in the stylesheet
+*/
+
+public class XSLApplyImports extends StyleElement {
+
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ //checkWithinTemplate();
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLWithParam) {
+ // OK;
+ } else if (child.getNodeKind() == Type.TEXT) {
+ // with xml:space=preserve, white space nodes may still be there
+ if (!Whitespace.isWhite(child.getStringValueCS())) {
+ compileError("No character data is allowed within xsl:apply-imports", "XTSE0010");
+ }
+ } else {
+ compileError("Child element " + child.getDisplayName() +
+ " is not allowed as a child of xsl:apply-imports", "XTSE0010");
+ }
+ }
+ }
+
+ /*@NotNull*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ ApplyImports inst = new ApplyImports();
+ inst.setActualParameters(getWithParamInstructions(exec, decl, false, inst),
+ getWithParamInstructions(exec, decl, true, inst));
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLApplyTemplates.java b/sf/saxon/style/XSLApplyTemplates.java
new file mode 100644
index 0000000..dad51d7
--- /dev/null
+++ b/sf/saxon/style/XSLApplyTemplates.java
@@ -0,0 +1,208 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.AxisExpression;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.ApplyTemplates;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.expr.sort.SortExpression;
+import net.sf.saxon.expr.sort.SortKeyDefinition;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.Mode;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+
+/**
+* An xsl:apply-templates element in the stylesheet
+*/
+
+public class XSLApplyTemplates extends StyleElement {
+
+ /*@Nullable*/ private Expression select;
+ private Expression threads = null;
+ private StructuredQName modeName; // null if no name specified or if conventional values such as #current used
+ private boolean useCurrentMode = false;
+ private boolean useTailRecursion = false;
+ private boolean defaultedSelectExpression = true;
+ private Mode mode;
+ private String modeAttribute;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ String selectAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.MODE)) {
+ modeAttribute = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ defaultedSelectExpression = false;
+ } else if (f.equals("saxon:threads") && atts.getURI(a).equals(NamespaceConstant.SAXON)) {
+ String threadsAtt = Whitespace.trim(atts.getValue(a));
+ threads = makeAttributeValueTemplate(threadsAtt);
+ if (getPreparedStylesheet().isCompileWithTracing()) {
+ compileWarning("saxon:threads - no multithreading takes place when compiling with trace enabled",
+ SaxonErrorCode.SXWN9012);
+ threads = new StringLiteral("0");
+ } else if (!"EE".equals(getConfiguration().getEditionCode())) {
+ compileWarning("saxon:threads - ignored when not running Saxon-EE",
+ SaxonErrorCode.SXWN9013);
+ threads = new StringLiteral("0");
+ }
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (modeAttribute!=null) {
+ if (modeAttribute.equals("#current")) {
+ useCurrentMode = true;
+ } else if (modeAttribute.equals("#unnamed") && isXslt30Processor()) {
+ modeName = Mode.UNNAMED_MODE_NAME;
+ } else if (modeAttribute.equals("#default")) {
+ // do nothing;
+ } else {
+ try {
+ modeName = makeQName(modeAttribute);
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE0280");
+ modeName = null;
+ } catch (XPathException err) {
+ compileError("Mode name " + Err.wrap(modeAttribute) + " is not a valid QName",
+ err.getErrorCodeQName());
+ modeName = null;
+ }
+ }
+ }
+
+ if (selectAtt!=null) {
+ select = makeExpression(selectAtt);
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+
+ // get the Mode object
+ if (useCurrentMode) {
+ // give a warning if we're not inside an xsl:template
+ if (iterateAxis(AxisInfo.ANCESTOR, new NameTest(Type.ELEMENT, StandardNames.XSL_TEMPLATE, getNamePool())).next() == null) {
+ issueWarning("Specifying mode=\"#current\" when not inside an xsl:template serves no useful purpose", this);
+ }
+ } else {
+ if (modeName == null) {
+ // XSLT 3.0 allows a default mode to be specified on the xsl:stylesheet element
+ modeName = getContainingStylesheet().getDefaultMode();
+ }
+ mode = getPreparedStylesheet().getRuleManager().getMode(modeName, true);
+ }
+
+ // handle sorting if requested
+
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLSort) {
+ // no-op
+ } else if (child instanceof XSLWithParam) {
+ // usesParams = true;
+ } else if (child.getNodeKind() == Type.TEXT) {
+ // with xml:space=preserve, white space nodes may still be there
+ if (!Whitespace.isWhite(child.getStringValueCS())) {
+ compileError("No character data is allowed within xsl:apply-templates", "XTSE0010");
+ }
+ } else {
+ compileError("Invalid element within xsl:apply-templates", "XTSE0010");
+ }
+ }
+
+ if (select==null) {
+ int lineNr = getLineNumber();
+ select = new AxisExpression(AxisInfo.CHILD, null);
+ select.setLocationId(lineNr);
+ }
+
+ select = typeCheck("select", select);
+ if (!getEffectiveVersion().equals(DecimalValue.THREE)) {
+ try {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:apply-templates/select", 0);
+ role.setErrorCode("XTTE0520");
+ select = TypeChecker.staticTypeCheck(select,
+ SequenceType.NODE_SEQUENCE,
+ false, role, makeExpressionVisitor());
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ }
+ }
+
+ /**
+ * Mark tail-recursive calls on templates and functions.
+ * For most instructions, this does nothing.
+ */
+
+ public boolean markTailCalls() {
+ useTailRecursion = true;
+ return true;
+ }
+
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ SortKeyDefinition[] sortKeys = makeSortKeys(decl);
+ if (sortKeys != null) {
+ useTailRecursion = false;
+ }
+ assert select != null;
+ Expression sortedSequence = select;
+ if (sortKeys != null) {
+ sortedSequence = new SortExpression(select, sortKeys);
+ }
+ compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ ApplyTemplates app = new ApplyTemplates(
+ sortedSequence,
+ useCurrentMode,
+ useTailRecursion,
+ defaultedSelectExpression,
+ mode,
+ threads);
+ app.setActualParameters(getWithParamInstructions(exec, decl, false, app),
+ getWithParamInstructions(exec, decl, true, app));
+ return app;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLAttribute.java b/sf/saxon/style/XSLAttribute.java
new file mode 100644
index 0000000..d702d39
--- /dev/null
+++ b/sf/saxon/style/XSLAttribute.java
@@ -0,0 +1,300 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.ComputedAttribute;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.FixedAttribute;
+import net.sf.saxon.lib.StandardURIChecker;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* xsl:attribute element in stylesheet. <br>
+*/
+
+public final class XSLAttribute extends XSLLeafNodeConstructor {
+
+ /*@Nullable*/ private Expression attributeName;
+ private Expression separator;
+ private Expression namespace = null;
+ private Expression onEmpty = null;
+ private int validationAction = Validation.PRESERVE;
+ private SimpleType schemaType;
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ String nameAtt = null;
+ String namespaceAtt = null;
+ String selectAtt = null;
+ String separatorAtt = null;
+ String onEmptyAtt = null;
+ String validationAtt = null;
+ String typeAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals("name")) {
+ nameAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals("namespace")) {
+ namespaceAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals("select")) {
+ selectAtt = atts.getValue(a);
+ } else if (f.equals("separator")) {
+ separatorAtt = atts.getValue(a);
+ } else if (f.equals("on-empty")) {
+ onEmptyAtt = atts.getValue(a);
+ } else if (f.equals("validation")) {
+ validationAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals("type")) {
+ typeAtt = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (nameAtt==null) {
+ reportAbsence("name");
+ return;
+ }
+ attributeName = makeAttributeValueTemplate(nameAtt);
+ if (attributeName instanceof StringLiteral) {
+ if (!getConfiguration().getNameChecker().isQName(((StringLiteral)attributeName).getStringValue())) {
+ invalidAttributeName("Attribute name " + Err.wrap(nameAtt) + " is not a valid QName");
+ }
+ if (nameAtt.equals("xmlns")) {
+ if (namespace==null) {
+ invalidAttributeName("Invalid attribute name: xmlns");
+ }
+ }
+ if (nameAtt.startsWith("xmlns:")) {
+ if (namespaceAtt == null) {
+ invalidAttributeName("Invalid attribute name: " + Err.wrap(nameAtt));
+ } else {
+ // ignore the prefix "xmlns"
+ nameAtt = nameAtt.substring(6);
+ attributeName = new StringLiteral(nameAtt);
+ }
+ }
+ }
+
+
+ if (namespaceAtt != null) {
+ namespace = makeAttributeValueTemplate(namespaceAtt);
+ if (namespace instanceof StringLiteral) {
+ if (!StandardURIChecker.getInstance().isValidURI(((StringLiteral)namespace).getStringValue())) {
+ compileError("The value of the namespace attribute must be a valid URI", "XTDE0865");
+ }
+ }
+ }
+
+ if (selectAtt != null) {
+ select = makeExpression(selectAtt);
+ }
+
+ if (separatorAtt == null) {
+ if (selectAtt == null) {
+ separator = new StringLiteral(StringValue.EMPTY_STRING);
+ } else {
+ separator = new StringLiteral(StringValue.SINGLE_SPACE);
+ }
+ } else {
+ separator = makeAttributeValueTemplate(separatorAtt);
+ }
+
+ if (onEmptyAtt != null) {
+ if (isXslt30Processor()) {
+ onEmpty = makeExpression(onEmptyAtt);
+ } else {
+ compileError("The on-empty attribute requires XSLT 3.0 to be enabled", "XTSE0090");
+ }
+ }
+
+ if (validationAtt!=null) {
+ validationAction = Validation.getCode(validationAtt);
+ if (validationAction != Validation.STRIP && !getPreparedStylesheet().isSchemaAware()) {
+ validationAction = Validation.STRIP;
+ compileError("To perform validation, a schema-aware XSLT processor is needed", "XTSE1660");
+ }
+ if (validationAction == Validation.INVALID) {
+ compileError("Invalid value of validation attribute", "XTSE0020");
+ validationAction = getContainingStylesheet().getDefaultValidation();
+ }
+ } else {
+ validationAction = getContainingStylesheet().getDefaultValidation();
+ }
+
+ if (typeAtt!=null) {
+ if (!getPreparedStylesheet().isSchemaAware()) {
+ compileError(
+ "The @type attribute is available only with a schema-aware XSLT processor", "XTSE1660");
+ } else {
+ SchemaType type = getSchemaType(typeAtt);
+ if (type == null) {
+ compileError("Unknown attribute type " + typeAtt, "XTSE1520");
+ } else {
+ if (type.isSimpleType()) {
+ schemaType = (SimpleType)type;
+ } else {
+ compileError("Type annotation for attributes must be a simple type", "XTSE1530");
+ }
+ }
+ validationAction = Validation.BY_TYPE;
+ }
+ }
+
+ if (typeAtt != null && validationAtt != null) {
+ compileError("The validation and type attributes are mutually exclusive", "XTSE1505");
+ validationAction = getContainingStylesheet().getDefaultValidation();
+ schemaType = null;
+ }
+ }
+
+ private void invalidAttributeName(String message) throws XPathException {
+// if (forwardsCompatibleModeIsEnabled()) {
+// DynamicError err = new XPathException(message);
+// err.setErrorCode("XTDE0850");
+// err.setLocator(this);
+// attributeName = new ErrorExpression(err);
+// } else {
+ compileError(message, "XTDE0850");
+ // prevent a duplicate error message...
+ attributeName = new StringLiteral("saxon-error-attribute");
+// }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (schemaType != null) {
+ if (schemaType.isNamespaceSensitive()) {
+ compileError("Validation at attribute level must not specify a " +
+ "namespace-sensitive type (xs:QName or xs:NOTATION)", "XTTE1545");
+ }
+ }
+ attributeName = typeCheck("name", attributeName);
+ namespace = typeCheck("namespace", namespace);
+ select = typeCheck("select", select);
+ separator = typeCheck("separator", separator);
+ onEmpty = typeCheck("on-empty", onEmpty);
+ super.validate(decl);
+ }
+
+ /**
+ * Get the error code to be returned when the element has a select attribute but is not empty.
+ *
+ * @return the error code defined for this condition, for this particular instruction
+ */
+
+ protected String getErrorCodeForSelectPlusContent() {
+ return "XTSE0840";
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ NamespaceResolver nsContext = null;
+
+ // deal specially with the case where the attribute name is known statically
+
+ if (attributeName instanceof StringLiteral && onEmpty == null) {
+ String qName = Whitespace.trim(((StringLiteral)attributeName).getStringValue());
+ String[] parts;
+ try {
+ parts = getConfiguration().getNameChecker().getQNameParts(qName);
+ } catch (QNameException e) {
+ // This can't happen, because of previous checks
+ return null;
+ }
+
+ if (namespace==null) {
+ String nsuri = "";
+ if (!parts[0].equals("")) {
+ nsuri = getURIForPrefix(parts[0], false);
+ if (nsuri == null) {
+ undeclaredNamespaceError(parts[0], "XTDE0860");
+ return null;
+ }
+ }
+ NodeName attributeName = new FingerprintedQName(parts[0], nsuri, parts[1]);
+ attributeName.allocateNameCode(getNamePool());
+ FixedAttribute inst = new FixedAttribute(attributeName,
+ validationAction,
+ schemaType);
+ inst.setContainer(this); // temporarily
+ compileContent(exec, decl, inst, separator);
+ return inst;
+ } else if (namespace instanceof StringLiteral) {
+ String nsuri = ((StringLiteral)namespace).getStringValue();
+ if (nsuri.equals("")) {
+ parts[0] = "";
+ } else if (parts[0].equals("")) {
+ // Need to choose an arbitrary prefix
+ // First see if the requested namespace is declared in the stylesheet
+ AxisIterator iter = iterateAxis(AxisInfo.NAMESPACE);
+ while (true) {
+ NodeInfo ns = iter.next();
+ if (ns == null) {
+ break;
+ }
+ if (ns.getStringValue().equals(nsuri)) {
+ parts[0] = ns.getLocalPart();
+ break;
+ }
+ }
+ // Otherwise see the URI is known to the namepool
+ if (parts[0].equals("")) {
+ String p = getNamePool().suggestPrefixForURI(
+ ((StringLiteral)namespace).getStringValue());
+ if (p != null) {
+ parts[0] = p;
+ }
+ }
+ // Otherwise choose something arbitrary. This will get changed
+ // if it clashes with another attribute
+ if (parts[0].equals("")) {
+ parts[0] = "ns0";
+ }
+ }
+ NodeName nameCode = new FingerprintedQName(parts[0], nsuri, parts[1]);
+ nameCode.allocateNameCode(getNamePool());
+ FixedAttribute inst = new FixedAttribute(nameCode,
+ validationAction,
+ schemaType);
+ compileContent(exec, decl, inst, separator);
+ return inst;
+ }
+ } else {
+ // if the namespace URI must be deduced at run-time from the attribute name
+ // prefix, we need to save the namespace context of the instruction
+
+ if (namespace==null) {
+ nsContext = makeNamespaceContext();
+ }
+ }
+
+ ComputedAttribute inst = new ComputedAttribute( attributeName,
+ namespace,
+ nsContext,
+ validationAction,
+ schemaType,
+ false);
+ inst.setOnEmpty(onEmpty);
+ compileContent(exec, decl, inst, separator);
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLAttributeSet.java b/sf/saxon/style/XSLAttributeSet.java
new file mode 100644
index 0000000..e9b2042
--- /dev/null
+++ b/sf/saxon/style/XSLAttributeSet.java
@@ -0,0 +1,281 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.AttributeSet;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* An xsl:attribute-set element in the stylesheet. <br>
+*/
+
+public class XSLAttributeSet extends StyleElement implements StylesheetProcedure {
+
+ private String nameAtt;
+ // the name of the attribute set as written
+
+ /*@Nullable*/ private String useAtt;
+ // the value of the use-attribute-sets attribute, as supplied
+
+ private SlotManager stackFrameMap;
+ // needed if variables are used
+
+ private List<Declaration> attributeSetElements = null;
+ // list of Declarations of XSLAttributeSet objects referenced by this one
+
+ private AttributeSet[] useAttributeSets = null;
+ // compiled instructions for the attribute sets used by this one
+
+ private AttributeSet procedure = new AttributeSet();
+ // the compiled form of this attribute set
+
+ private int referenceCount = 0;
+ // the number of references to this attribute set
+
+ private boolean validated = false;
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ /**
+ * Get the name of this attribute set
+ * @return the name of the attribute set, as a QName
+ */
+
+ public StructuredQName getAttributeSetName() {
+ return getObjectName();
+ }
+
+ /**
+ * Get the compiled code produced for this XSLT element
+ * @return the compiled AttributeSet
+ */
+
+ public AttributeSet getInstruction() {
+ return procedure;
+ }
+
+ /**
+ * Increment the number of references found to this attribute set
+ */
+
+ public void incrementReferenceCount() {
+ referenceCount++;
+ }
+
+ public void prepareAttributes() throws XPathException {
+ useAtt = null;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.NAME)) {
+ nameAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.USE_ATTRIBUTE_SETS)) {
+ useAtt = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (nameAtt==null) {
+ reportAbsence("name");
+ setObjectName(new StructuredQName("", "", "attribute-set-error-name"));
+ return;
+ }
+
+ try {
+ setObjectName(makeQName(nameAtt));
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE0280");
+ setObjectName(new StructuredQName("", "", "attribute-set-error-name"));
+ } catch (XPathException err) {
+ compileError(err.getMessage(), err.getErrorCodeQName());
+ setObjectName(new StructuredQName("", "", "attribute-set-error-name"));
+ }
+
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ * If there is no name, the value will be null.
+ * @return the name of the object declared in this element, if any
+ */
+
+ public StructuredQName getObjectName() {
+ StructuredQName o = super.getObjectName();
+ if (o == null) {
+ try {
+ prepareAttributes();
+ o = getObjectName();
+ } catch (XPathException err) {
+ o = new StructuredQName("saxon", NamespaceConstant.SAXON, "badly-named-attribute-set");
+ setObjectName(o);
+ }
+ }
+ return o;
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+
+ if (validated) return;
+
+ checkTopLevel("XTSE0010");
+
+ stackFrameMap = getConfiguration().makeSlotManager();
+
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ Item child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (!(child instanceof XSLAttribute)) {
+ compileError("Only xsl:attribute is allowed within xsl:attribute-set", "XTSE0010");
+ }
+ }
+
+ if (useAtt!=null) {
+ // identify any attribute sets that this one refers to
+
+ attributeSetElements = new ArrayList<Declaration>(5);
+ useAttributeSets = getAttributeSets(useAtt, attributeSetElements);
+
+ // check for circularity
+
+ for (Declaration attributeSetElement : attributeSetElements) {
+ ((XSLAttributeSet) attributeSetElement.getSourceElement()).checkCircularity(this);
+ }
+ }
+
+ validated = true;
+ }
+
+ /**
+ * Check for circularity: specifically, check that this attribute set does not contain
+ * a direct or indirect reference to the one supplied as a parameter
+ * @param origin the place from which the search started
+ * @throws net.sf.saxon.trans.XPathException if an error is found
+ */
+
+ public void checkCircularity(XSLAttributeSet origin) throws XPathException {
+ if (this==origin) {
+ compileError("The definition of the attribute set is circular", "XTSE0720");
+ useAttributeSets = null;
+ } else {
+ if (!validated) {
+ // if this attribute set isn't validated yet, we don't check it.
+ // The circularity will be detected when the last attribute set in the cycle
+ // gets validated
+ return;
+ }
+ if (attributeSetElements != null) {
+ for (Declaration attributeSetElement : attributeSetElements) {
+ ((XSLAttributeSet) attributeSetElement.getSourceElement()).checkCircularity(origin);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get details of stack frame
+ */
+
+ public SlotManager getSlotManager() {
+ return stackFrameMap;
+ }
+ /**
+ * Compile the attribute set
+ * @param exec the Executable
+ * @param decl this attribute set declaration
+ * @throws XPathException if a failure is detected
+ */
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+ if (referenceCount > 0 ) {
+ Expression body = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (body == null) {
+ body = Literal.makeEmptySequence();
+ }
+
+ try {
+
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ body = visitor.simplify(body);
+ if (getConfiguration().isCompileWithTracing()) {
+ body = makeTraceInstruction(this, body);
+// trace.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+// trace.setContainer(procedure);
+// body = trace;
+ }
+
+ procedure.setUseAttributeSets(useAttributeSets);
+ procedure.setName(getObjectName());
+ procedure.setBody(body);
+ procedure.setSystemId(getSystemId());
+ procedure.setLineNumber(getLineNumber());
+ procedure.setExecutable(exec);
+
+ Expression exp2 = body.optimize(visitor, new ExpressionVisitor.ContextItemType(AnyItemType.getInstance(), true));
+ if (body != exp2) {
+ procedure.setBody(exp2);
+ body = exp2;
+ }
+
+ super.allocateSlots(body);
+ procedure.setStackFrameMap(stackFrameMap);
+ } catch (XPathException e) {
+ compileError(e);
+ }
+ }
+ }
+
+ /**
+ * Optimize the stylesheet construct
+ * @param declaration this attribute set declaration
+ */
+
+ public void optimize(Declaration declaration) throws XPathException {
+ // Already done earlier
+ }
+
+ /**
+ * Get the type of construct. This will be a constant in
+ * class {@link net.sf.saxon.trace.Location}. This method is part of
+ * the {@link net.sf.saxon.trace.InstructionInfo} interface
+ */
+
+ public int getConstructType() {
+ return StandardNames.XSL_ATTRIBUTE_SET;
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLCallTemplate.java b/sf/saxon/style/XSLCallTemplate.java
new file mode 100644
index 0000000..02eedb2
--- /dev/null
+++ b/sf/saxon/style/XSLCallTemplate.java
@@ -0,0 +1,266 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.CallTemplate;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.Template;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* An xsl:call-template element in the stylesheet
+*/
+
+public class XSLCallTemplate extends StyleElement {
+
+ private StructuredQName calledTemplateName; // the name of the called template
+ /*@Nullable*/ private XSLTemplate template = null;
+ private boolean useTailRecursion = false;
+ private Expression calledTemplateExpression; // allows name to be an AVT
+
+ /**
+ * Determine whether the called template can be specified as an AVT
+ * @return true if the template name can be specified at run-time, that is, if this is a saxon:call-template
+ * instruction
+ */
+
+ protected boolean allowAVT() {
+ return false;
+ }
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ private boolean gettingReturnedItemType = false;
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ if (template==null || gettingReturnedItemType) {
+ return AnyItemType.getInstance();
+ } else {
+ // protect against infinite recursion
+ gettingReturnedItemType = true;
+ ItemType result = template.getReturnedItemType();
+ gettingReturnedItemType = false;
+ return result;
+ }
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ String nameAttribute = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.NAME)) {
+ nameAttribute = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (nameAttribute==null) {
+ calledTemplateName = new StructuredQName("saxon", NamespaceConstant.SAXON, "error-template");
+ reportAbsence("name");
+ return;
+ }
+
+ if (allowAVT() && nameAttribute.indexOf('{')>=0) {
+ calledTemplateExpression = makeAttributeValueTemplate(nameAttribute);
+ } else {
+ try {
+ calledTemplateName = makeQName(nameAttribute);
+ } catch (NamespaceException err) {
+ calledTemplateName = new StructuredQName("saxon", NamespaceConstant.SAXON, "error-template");
+ compileError(err.getMessage(), "XTSE0280");
+ } catch (XPathException err) {
+ calledTemplateName = new StructuredQName("saxon", NamespaceConstant.SAXON, "error-template");
+ compileError(err.getMessage(), err.getErrorCodeQName());
+ }
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLWithParam) {
+ // OK;
+ } else if (child instanceof XSLFallback && mayContainFallback()) {
+ // xsl:fallback is not allowed on xsl:call-template, but is allowed on saxon:call-template (cheat!)
+ } else if (child.getNodeKind() == Type.TEXT) {
+ // with xml:space=preserve, white space nodes may still be there
+ if (!Whitespace.isWhite(child.getStringValueCS())) {
+ compileError("No character data is allowed within xsl:call-template", "XTSE0010");
+ }
+ } else {
+ compileError("Child element " + Err.wrap(child.getDisplayName(), Err.ELEMENT) +
+ " is not allowed as a child of xsl:call-template", "XTSE0010");
+ }
+ }
+ if (calledTemplateExpression==null &&
+ !(calledTemplateName.getURI().equals(NamespaceConstant.SAXON) &&
+ calledTemplateName.getLocalPart().equals("error-template"))) {
+ template = findTemplate(calledTemplateName);
+ if (template==null) {
+ return;
+ }
+ }
+ calledTemplateExpression = typeCheck("name", calledTemplateExpression);
+ }
+
+ public void postValidate() throws XPathException {
+ // check that a parameter is supplied for each required parameter
+ // of the called template
+
+ if (template != null) {
+ AxisIterator declaredParams = template.iterateAxis(AxisInfo.CHILD);
+ while(true) {
+ NodeInfo param = declaredParams.next();
+ if (param == null) {
+ break;
+ }
+ if (param instanceof XSLLocalParam && ((XSLLocalParam)param).isRequiredParam()
+ && !((XSLLocalParam)param).isTunnelParam()) {
+ AxisIterator actualParams = iterateAxis(AxisInfo.CHILD);
+ boolean ok = false;
+ while(true) {
+ NodeInfo withParam = actualParams.next();
+ if (withParam == null) {
+ break;
+ }
+ if (withParam instanceof XSLWithParam &&
+ ((XSLWithParam)withParam).getVariableQName().equals(
+ ((XSLLocalParam)param).getVariableQName())) {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok) {
+ compileError("No value supplied for required parameter " +
+ Err.wrap(((XSLLocalParam)param).getVariableQName().getDisplayName(), Err.VARIABLE), "XTSE0690");
+ }
+ }
+ }
+
+
+ // check that every supplied parameter is declared in the called
+ // template
+
+ AxisIterator actualParams = iterateAxis(AxisInfo.CHILD);
+ while(true) {
+ NodeInfo w = actualParams.next();
+ if (w == null) {
+ break;
+ }
+ if (w instanceof XSLWithParam && !((XSLWithParam)w).isTunnelParam()) {
+ XSLWithParam withParam = (XSLWithParam)w;
+ assert template != null;
+ AxisIterator formalParams = template.iterateAxis(AxisInfo.CHILD);
+ boolean ok = false;
+ while(true) {
+ NodeInfo param = formalParams.next();
+ if (param == null) {
+ break;
+ }
+ if (param instanceof XSLLocalParam &&
+ ((XSLLocalParam)param).getVariableQName().equals(withParam.getVariableQName())
+ /* TODO spec bug 10934: && !((XSLParam)param).isTunnelParam() */
+ ) {
+ ok = true;
+ SequenceType required = ((XSLLocalParam)param).getRequiredType();
+ withParam.checkAgainstRequiredType(required);
+ break;
+ }
+ }
+ if (!ok) {
+ if (!xPath10ModeIsEnabled()) {
+ compileError("Parameter " +
+ withParam.getVariableQName().getDisplayName() +
+ " is not declared in the called template", "XTSE0680");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private XSLTemplate findTemplate(StructuredQName templateName)
+ throws XPathException {
+
+ PrincipalStylesheetModule psm = getPrincipalStylesheetModule();
+ XSLTemplate template = psm.getNamedTemplate(templateName);
+ if (template == null) {
+ compileError("No template exists named " + calledTemplateName, "XTSE0650");
+ }
+ return template;
+ }
+
+ /**
+ * Mark tail-recursive calls on templates and functions.
+ * For most instructions, this does nothing.
+ */
+
+ public boolean markTailCalls() {
+ useTailRecursion = true;
+ return true;
+ }
+
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ Template target = null;
+ NamespaceResolver nsContext = null;
+
+ if (calledTemplateExpression==null) {
+ if (template==null) {
+ return null; // error already reported
+ }
+ target = template.getCompiledTemplate();
+ } else {
+ //getPrincipalStyleSheet().setRequireRuntimeTemplateMap(true);
+ nsContext = makeNamespaceContext();
+ }
+
+ CallTemplate call = new CallTemplate (
+ target,
+ useTailRecursion,
+ calledTemplateExpression,
+ nsContext );
+ call.setActualParameters(getWithParamInstructions(exec, decl, false, call),
+ getWithParamInstructions(exec, decl, true, call));
+ return call;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLCharacterMap.java b/sf/saxon/style/XSLCharacterMap.java
new file mode 100644
index 0000000..527f207
--- /dev/null
+++ b/sf/saxon/style/XSLCharacterMap.java
@@ -0,0 +1,254 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+* An xsl:character-map declaration in the stylesheet. <br>
+*/
+
+public class XSLCharacterMap extends StyleElement {
+
+ /*@Nullable*/ String use;
+ // the value of the use-character-maps attribute, as supplied
+
+ List<XSLCharacterMap> characterMapElements = null;
+ // list of XSLCharacterMap objects referenced by this one
+
+ boolean validated = false;
+ // set to true once validate() has been called
+
+ boolean redundant = false;
+ // set to true if another character-map overrrides this one
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ /**
+ * Get the fingerprint of the name of this character map
+ * @return the fingerprint value
+ */
+
+ public StructuredQName getCharacterMapName() {
+ StructuredQName name = getObjectName();
+ if (name == null) {
+ try {
+ return makeQName(getAttributeValue("", "name"));
+ } catch (Exception err) {
+ // the error will be reported later
+ return new StructuredQName("", "", "unnamedCharacterMap_" + hashCode());
+ }
+ }
+ return name;
+ }
+
+ /**
+ * Test whether this character map is redundant (because another with the
+ * same name has higher import precedence). Note that a character map is not
+ * considered redundant simply because it is not referenced in an xsl:output
+ * declaration; we allow character-maps to be selected at run-time using the
+ * setOutputProperty() API.
+ * @return true if this character map is redundant
+ */
+
+ public boolean isRedundant() {
+ return redundant;
+ }
+
+ /**
+ * Validate the attributes on this instruction
+ * @throws XPathException
+ */
+
+ public void prepareAttributes() throws XPathException {
+
+ String name = null;
+ use = null;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.NAME)) {
+ name = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.USE_CHARACTER_MAPS)) {
+ use = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (name==null) {
+ reportAbsence("name");
+ name = "unnamedCharacterMap_" + hashCode();
+ }
+
+ try {
+ setObjectName(makeQName(name));
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE0280");
+ name = "unnamedCharacterMap_" + hashCode();
+ setObjectName(new StructuredQName("", "", name));
+ } catch (XPathException err) {
+ compileError(err.getMessage(), "XTSE0020");
+ name = "unnamedCharacterMap_" + hashCode();
+ setObjectName(new StructuredQName("", "", name));
+ }
+
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+
+ if (validated) return;
+
+ // check that this is a top-level declaration
+
+ checkTopLevel("XTSE0010");
+
+ // check that the only children are xsl:output-character elements
+
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ Item child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (!(child instanceof XSLOutputCharacter)) {
+ compileError("Only xsl:output-character is allowed within xsl:character-map", "XTSE0010");
+ }
+ }
+
+ // check that there isn't another character-map with the same name and import
+ // precedence
+
+ PrincipalStylesheetModule psm = getPrincipalStylesheetModule();
+ Declaration other = psm.getCharacterMap(getObjectName());
+ if (other.getSourceElement() != this) {
+ if (decl.getPrecedence() == other.getPrecedence()) {
+ compileError("There are two character-maps with the same name and import precedence", "XTSE1580");
+ } else if (decl.getPrecedence() < other.getPrecedence()) {
+ redundant = true;
+ }
+ }
+
+ // validate the use-character-maps attribute
+
+ if (use!=null) {
+
+ // identify any character maps that this one refers to
+
+ characterMapElements = new ArrayList<XSLCharacterMap>(5);
+ StringTokenizer st = new StringTokenizer(use, " \t\n\r", false);
+
+ while (st.hasMoreTokens()) {
+ String displayname = st.nextToken();
+ try {
+ String[] parts = getConfiguration().getNameChecker().getQNameParts(displayname);
+ String uri = getURIForPrefix(parts[0], false);
+ if (uri == null) {
+ compileError("Undeclared namespace prefix " + Err.wrap(parts[0])
+ + " in character map name", "XTSE0280");
+ }
+ StructuredQName qn = new StructuredQName(parts[0], uri, parts[1]);
+ Declaration charMapDecl = psm.getCharacterMap(qn);
+ if (charMapDecl == null) {
+ compileError("No character-map named '" + displayname + "' has been defined", "XTSE1590");
+ } else {
+ XSLCharacterMap ref = (XSLCharacterMap)charMapDecl.getSourceElement();
+ characterMapElements.add(ref);
+ }
+ } catch (QNameException err) {
+ compileError("Invalid character-map name. " + err.getMessage(), "XTSE1590");
+ }
+ }
+
+ // check for circularity
+
+ for (Object characterMapElement : characterMapElements) {
+ ((XSLCharacterMap) characterMapElement).checkCircularity(this);
+ }
+ }
+
+ validated = true;
+ }
+
+ /**
+ * Check for circularity: specifically, check that this attribute set does not contain
+ * a direct or indirect reference to the one supplied as a parameter
+ * @param origin the start point of the search
+ * @throws net.sf.saxon.trans.XPathException if an error is detected
+ */
+
+ private void checkCircularity(XSLCharacterMap origin) throws XPathException {
+ if (this==origin) {
+ compileError("The definition of the character map is circular", "XTSE1600");
+ characterMapElements = null; // for error recovery
+ } else {
+ if (!validated) {
+ // if this attribute set isn't validated yet, we don't check it.
+ // The circularity will be detected when the last attribute set in the cycle
+ // gets validated
+ return;
+ }
+ if (characterMapElements != null) {
+ for (Object characterMapElement : characterMapElements) {
+ ((XSLCharacterMap) characterMapElement).checkCircularity(origin);
+ }
+ }
+ }
+ }
+
+ /**
+ * Assemble all the mappings defined by this character map, adding them to a
+ * HashMap that maps integer codepoints to strings
+ * @param map an IntHash map populated with the character mappings
+ */
+
+ public void assemble(IntHashMap<String> map) {
+ if (characterMapElements != null) {
+ for (Object characterMapElement : characterMapElements) {
+ XSLCharacterMap charmap = (XSLCharacterMap) characterMapElement;
+ charmap.assemble(map);
+ }
+ }
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ Item child = kids.next();
+ if (child == null) {
+ return;
+ }
+ XSLOutputCharacter oc = (XSLOutputCharacter)child;
+ map.put(oc.getCodePoint(), oc.getReplacementString());
+ }
+ }
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+ // no action
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLChoose.java b/sf/saxon/style/XSLChoose.java
new file mode 100644
index 0000000..b98fa06
--- /dev/null
+++ b/sf/saxon/style/XSLChoose.java
@@ -0,0 +1,199 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.Choose;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.BooleanValue;
+
+/**
+* An xsl:choose elements in the stylesheet. <br>
+*/
+
+public class XSLChoose extends StyleElement {
+
+ private StyleElement otherwise;
+ private int numberOfWhens = 0;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return getCommonChildItemType();
+ }
+
+ public void prepareAttributes() throws XPathException {
+ AttributeCollection atts = getAttributeList();
+ for (int a=0; a<atts.getLength(); a++) {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while(true) {
+ NodeInfo curr = kids.next();
+ if (curr == null) {
+ break;
+ }
+ if (curr instanceof XSLWhen) {
+ if (otherwise!=null) {
+ otherwise.compileError("xsl:otherwise must come last", "XTSE0010");
+ }
+ numberOfWhens++;
+ } else if (curr instanceof XSLOtherwise) {
+ if (otherwise!=null) {
+ ((XSLOtherwise)curr).compileError("Only one xsl:otherwise is allowed in an xsl:choose", "XTSE0010");
+ } else {
+ otherwise = (StyleElement)curr;
+ }
+ } else if (curr instanceof StyleElement) {
+ ((StyleElement)curr).compileError("Only xsl:when and xsl:otherwise are allowed here", "XTSE0010");
+ } else {
+ compileError("Only xsl:when and xsl:otherwise are allowed within xsl:choose", "XTSE0010");
+ }
+ }
+
+ if (numberOfWhens==0) {
+ compileError("xsl:choose must contain at least one xsl:when", "XTSE0010");
+ }
+ }
+
+ /**
+ * Mark tail-recursive calls on templates and functions.
+ */
+
+ public boolean markTailCalls() {
+ boolean found = false;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while(true) {
+ NodeInfo curr = kids.next();
+ if (curr == null) {
+ return found;
+ }
+ if (curr instanceof StyleElement) {
+ found |= ((StyleElement)curr).markTailCalls();
+ }
+ }
+ }
+
+
+ /*@Nullable*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+
+ int entries = numberOfWhens + (otherwise==null ? 0 : 1);
+ Expression[] conditions = new Expression[entries];
+ Expression[] actions = new Expression[entries];
+
+ int w = 0;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while(true) {
+ NodeInfo curr = kids.next();
+ if (curr == null) {
+ break;
+ }
+ if (curr instanceof XSLWhen) {
+ conditions[w] = ((XSLWhen)curr).getCondition();
+ Expression b = ((XSLWhen)curr).compileSequenceConstructor(
+ exec, decl, curr.iterateAxis(AxisInfo.CHILD), true);
+ if (b == null) {
+ b = Literal.makeEmptySequence();
+ }
+ try {
+ b = makeExpressionVisitor().simplify(b);
+ actions[w] = b;
+ } catch (XPathException e) {
+ compileError(e);
+ }
+
+ if (getPreparedStylesheet().isCompileWithTracing()) {
+ actions[w] = makeTraceInstruction((XSLWhen)curr, actions[w]);
+ }
+
+ // Optimize for constant conditions (true or false)
+ if (conditions[w] instanceof Literal && ((Literal)conditions[w]).getValue() instanceof BooleanValue) {
+ if (((BooleanValue)((Literal)conditions[w]).getValue()).getBooleanValue()) {
+ // constant true: truncate the tests here
+ entries = w+1;
+ break;
+ } else {
+ // constant false: omit this test
+ w--;
+ entries--;
+ }
+ }
+ w++;
+ } else if (curr instanceof XSLOtherwise) {
+ conditions[w] = Literal.makeLiteral(BooleanValue.TRUE);
+ Expression b = ((XSLOtherwise)curr).compileSequenceConstructor(
+ exec, decl, curr.iterateAxis(AxisInfo.CHILD), true);
+ if (b == null) {
+ b = Literal.makeEmptySequence();
+ }
+ try {
+ b = makeExpressionVisitor().simplify(b);
+ actions[w] = b;
+ } catch (XPathException e) {
+ compileError(e);
+ }
+ if (getPreparedStylesheet().isCompileWithTracing()) {
+ actions[w] = makeTraceInstruction((XSLOtherwise)curr, actions[w]);
+ }
+ w++;
+ } else {
+ // Ignore: problem has already been reported.
+ }
+ }
+
+ if (conditions.length != entries) {
+ // we've optimized some entries away
+ if (entries==0) {
+ return null; // return a no-op
+ }
+ if (entries==1 && (conditions[0] instanceof Literal) &&
+ ((Literal)conditions[0]).getValue() instanceof BooleanValue) {
+ if (((BooleanValue)((Literal)conditions[0]).getValue()).getBooleanValue()) {
+ // only one condition left, and it's known to be true: return the corresponding action
+ return actions[0];
+ } else {
+ // but if it's false, return a no-op
+ return null;
+ }
+ }
+ Expression[] conditions2 = new Expression[entries];
+ System.arraycopy(conditions, 0, conditions2, 0, entries);
+ Expression[] actions2 = new Expression[entries];
+ System.arraycopy(actions, 0, actions2, 0, entries);
+ conditions = conditions2;
+ actions = actions2;
+ }
+
+ return new Choose(conditions, actions);
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLComment.java b/sf/saxon/style/XSLComment.java
new file mode 100644
index 0000000..4a97eaf
--- /dev/null
+++ b/sf/saxon/style/XSLComment.java
@@ -0,0 +1,65 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.Comment;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+/**
+* An xsl:comment elements in the stylesheet. <br>
+*/
+
+public final class XSLComment extends XSLLeafNodeConstructor {
+
+ public void prepareAttributes() throws XPathException {
+
+ String selectAtt = null;
+
+ AttributeCollection atts = getAttributeList();
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt!=null) {
+ select = makeExpression(selectAtt);
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ select = typeCheck("select", select);
+ super.validate(decl);
+ }
+
+ /**
+ * Get the error code to be returned when the element has a select attribute but is not empty.
+ *
+ * @return the error code defined for this condition, for this particular instruction
+ */
+
+ protected String getErrorCodeForSelectPlusContent() {
+ return "XTSE0940";
+ }
+
+ /*@NotNull*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ Comment inst = new Comment();
+ compileContent(exec, decl, inst, new StringLiteral(StringValue.SINGLE_SPACE));
+ return inst;
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/style/XSLCopy.java b/sf/saxon/style/XSLCopy.java
new file mode 100644
index 0000000..c9aa118
--- /dev/null
+++ b/sf/saxon/style/XSLCopy.java
@@ -0,0 +1,198 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* Handler for xsl:copy elements in stylesheet. <br>
+*/
+
+public class XSLCopy extends StyleElement {
+
+ private String use; // value of use-attribute-sets attribute
+ /*@Nullable*/ private AttributeSet[] attributeSets = null;
+ private boolean copyNamespaces = true;
+ private boolean inheritNamespaces = true;
+ private int validationAction = Validation.PRESERVE;
+ private SchemaType schemaType = null;
+ private Expression select = null;
+ private boolean selectSpecified = false;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+ String copyNamespacesAtt = null;
+ String validationAtt = null;
+ String typeAtt = null;
+ String inheritAtt = null;
+ String selectAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.USE_ATTRIBUTE_SETS)) {
+ use = atts.getValue(a);
+ } else if (f.equals(StandardNames.COPY_NAMESPACES)) {
+ copyNamespacesAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.TYPE)) {
+ typeAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.VALIDATION)) {
+ validationAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.INHERIT_NAMESPACES)) {
+ inheritAtt = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (copyNamespacesAtt == null) {
+ copyNamespaces = true;
+ } else {
+ checkAttributeValue("copy-namespaces", copyNamespacesAtt, false, StyleElement.YES_NO);
+ copyNamespaces = (copyNamespacesAtt.equals("yes"));
+ }
+
+ if (typeAtt != null && validationAtt != null) {
+ compileError("The type and validation attributes must not both be specified", "XTSE1505");
+ }
+
+ if (validationAtt != null) {
+ validationAction = Validation.getCode(validationAtt);
+ if (validationAction != Validation.STRIP && !getPreparedStylesheet().isSchemaAware()) {
+ validationAction = Validation.STRIP;
+ compileError("To perform validation, a schema-aware XSLT processor is needed", "XTSE1660");
+ }
+ } else {
+ validationAction = getContainingStylesheet().getDefaultValidation();
+ }
+
+ if (typeAtt != null) {
+ schemaType = getSchemaType(typeAtt);
+ if (!getPreparedStylesheet().isSchemaAware()) {
+ compileError("The @type attribute is available only with a schema-aware XSLT processor", "XTSE1660");
+ }
+ validationAction = Validation.BY_TYPE;
+ }
+ if (inheritAtt != null) {
+ checkAttributeValue("inherit-namespaces", inheritAtt, false, StyleElement.YES_NO);
+ inheritNamespaces = (inheritAtt.equals("yes"));
+ }
+
+ if (selectAtt != null) {
+ if (!isXslt30Processor()) {
+ compileError("The @select attribute of xsl:copy requires XSLT 3.0");
+ }
+ select = makeExpression(selectAtt);
+ selectSpecified = true;
+ }
+
+// if (validationAction == Validation.PRESERVE && !copyNamespaces) {
+// // this is an error only when copying namespace-sensitive content
+// compileError("copy-namespaces must be set to 'yes' when validation is set to 'preserve'", "XTTE0950");
+// }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (use!=null) {
+ // find any referenced attribute sets
+ attributeSets = getAttributeSets(use, null);
+ }
+ if (select != null && !isXslt30Processor()) {
+ // we have a 2.0 processor
+ if (forwardsCompatibleModeIsEnabled()) {
+ compileWarning("xsl:copy/@select is ignored in forwards-compatibility mode", "");
+ } else {
+ compileError("The xsl:copy/@select attribute is not recognized by an XSLT 2.0 processor");
+ }
+ }
+ if (select == null) {
+ select = new ContextItemExpression();
+ select.setLocationId(getLineNumber());
+ }
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+
+ select = typeCheck("select", select);
+ try {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:copy/select", 0);
+ role.setErrorCode("XTTE2170");
+ select = TypeChecker.staticTypeCheck(select,
+ SequenceType.OPTIONAL_ITEM,
+ false, role, makeExpressionVisitor());
+ } catch (XPathException err) {
+ compileError(err);
+ }
+
+ Copy inst = new Copy(select,
+ selectSpecified,
+ copyNamespaces,
+ inheritNamespaces,
+ schemaType,
+ validationAction);
+ Expression content = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+
+ if (attributeSets != null) {
+ UseAttributeSets use = new UseAttributeSets(attributeSets);
+ // The use-attribute-sets is ignored unless the context item is an element node. So we
+ // wrap the UseAttributeSets instruction in a conditional to perform a run-time test
+ Expression condition = new InstanceOfExpression(
+ new ContextItemExpression(),
+ SequenceType.makeSequenceType(NodeKindTest.ELEMENT, StaticProperty.EXACTLY_ONE));
+ Expression choice = Choose.makeConditional(condition, use);
+ if (content == null) {
+ content = choice;
+ } else {
+ content = Block.makeBlock(choice, content);
+ content.setLocationId(
+ allocateLocationId(getSystemId(), getLineNumber()));
+ }
+ }
+
+ if (content == null) {
+ content = Literal.makeEmptySequence();
+ }
+ inst.setContentExpression(content);
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLCopyOf.java b/sf/saxon/style/XSLCopyOf.java
new file mode 100644
index 0000000..2bb1018
--- /dev/null
+++ b/sf/saxon/style/XSLCopyOf.java
@@ -0,0 +1,131 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.CopyOf;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.value.Whitespace;
+
+
+/**
+* An xsl:copy-of element in the stylesheet. <br>
+*/
+
+public final class XSLCopyOf extends StyleElement {
+
+ /*@Nullable*/ private Expression select;
+ private boolean copyNamespaces;
+ private int validation = Validation.PRESERVE;
+ private SchemaType schemaType;
+ private boolean readOnce = false; // extension attribute to enable serial processing
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+ String selectAtt = null;
+ String copyNamespacesAtt = null;
+ String validationAtt = null;
+ String typeAtt = null;
+ String readOnceAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.COPY_NAMESPACES)) {
+ copyNamespacesAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.VALIDATION)) {
+ validationAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.TYPE)) {
+ typeAtt = Whitespace.trim(atts.getValue(a));
+ } else if (atts.getLocalName(a).equals("read-once") && atts.getURI(a).equals(NamespaceConstant.SAXON)) {
+ readOnceAtt = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt!=null) {
+ select = makeExpression(selectAtt);
+ } else {
+ reportAbsence("select");
+ }
+
+ if (copyNamespacesAtt == null) {
+ copyNamespaces = true;
+ } else {
+ checkAttributeValue("copy-namespaces", copyNamespacesAtt, false, StyleElement.YES_NO);
+ copyNamespaces = (copyNamespacesAtt.equals("yes"));
+ }
+
+ if (validationAtt!=null) {
+ validation = Validation.getCode(validationAtt);
+ if (validation != Validation.STRIP && !getPreparedStylesheet().isSchemaAware()) {
+ validation = Validation.STRIP;
+ compileError("To perform validation, a schema-aware XSLT processor is needed", "XTSE1660");
+ }
+ if (validation == Validation.INVALID) {
+ compileError("invalid value of validation attribute", "XTSE0020");
+ }
+ } else {
+ validation = getContainingStylesheet().getDefaultValidation();
+ }
+
+ if (typeAtt!=null) {
+ schemaType = getSchemaType(typeAtt);
+ if (!getPreparedStylesheet().isSchemaAware()) {
+ compileError("The @type attribute is available only with a schema-aware XSLT processor", "XTSE1660");
+ }
+ validation = Validation.BY_TYPE;
+ }
+
+ if (typeAtt != null && validationAtt != null) {
+ compileError("The @validation and @type attributes are mutually exclusive", "XTSE1505");
+ }
+
+ if (readOnceAtt != null) {
+ checkAttributeValue("saxon:read-once", readOnceAtt, false, StyleElement.YES_NO);
+ readOnce = (readOnceAtt.equals("yes"));
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkEmpty();
+ select = typeCheck("select", select);
+ }
+
+ public Expression compile(Executable exec, Declaration decl) {
+ CopyOf inst = new CopyOf(select, copyNamespaces, validation, schemaType, false);
+ if (readOnce) {
+ exec.getConfiguration().checkLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XSLT, "streaming");
+ inst.setReadOnce(readOnce);
+ }
+ inst.setCopyLineNumbers(exec.getConfiguration().isLineNumbering());
+ inst.setStaticBaseUri(getBaseURI());
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLDecimalFormat.java b/sf/saxon/style/XSLDecimalFormat.java
new file mode 100644
index 0000000..7b592fd
--- /dev/null
+++ b/sf/saxon/style/XSLDecimalFormat.java
@@ -0,0 +1,164 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.NamespaceException;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.DecimalFormatManager;
+import net.sf.saxon.trans.DecimalSymbols;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* Handler for xsl:decimal-format elements in stylesheet. <br>
+*/
+
+public class XSLDecimalFormat extends StyleElement {
+
+ boolean prepared = false;
+
+ String name;
+ String decimalSeparator;
+ String groupingSeparator;
+ String infinity;
+ String minusSign;
+ String NaN;
+ String percent;
+ String perMille;
+ String zeroDigit;
+ String digit;
+ String patternSeparator;
+
+ DecimalSymbols symbols;
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ if (prepared) {
+ return;
+ }
+ prepared = true;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.NAME)) {
+ name = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.DECIMAL_SEPARATOR)) {
+ decimalSeparator = atts.getValue(a);
+ } else if (f.equals(StandardNames.GROUPING_SEPARATOR)) {
+ groupingSeparator = atts.getValue(a);
+ } else if (f.equals(StandardNames.INFINITY)) {
+ infinity = atts.getValue(a);
+ } else if (f.equals(StandardNames.MINUS_SIGN)) {
+ minusSign = atts.getValue(a);
+ } else if (f.equals(StandardNames.NAN)) {
+ NaN = atts.getValue(a);
+ } else if (f.equals(StandardNames.PERCENT)) {
+ percent = atts.getValue(a);
+ } else if (f.equals(StandardNames.PER_MILLE)) {
+ perMille = atts.getValue(a);
+ } else if (f.equals(StandardNames.ZERO_DIGIT)) {
+ zeroDigit = atts.getValue(a);
+ } else if (f.equals(StandardNames.DIGIT)) {
+ digit = atts.getValue(a);
+ } else if (f.equals(StandardNames.PATTERN_SEPARATOR)) {
+ patternSeparator = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkTopLevel("XTSE0010");
+ checkEmpty();
+ int precedence = decl.getPrecedence();
+
+ if (symbols == null) {
+ return; // error already reported
+ }
+
+ if (decimalSeparator!=null) {
+ symbols.setProperty(DecimalSymbols.DECIMAL_SEPARATOR, decimalSeparator, precedence);
+ }
+ if (groupingSeparator!=null) {
+ symbols.setProperty(DecimalSymbols.GROUPING_SEPARATOR, groupingSeparator, precedence);
+ }
+ if (infinity!=null) {
+ symbols.setProperty(DecimalSymbols.INFINITY, infinity, precedence);
+ }
+ if (minusSign!=null) {
+ symbols.setProperty(DecimalSymbols.MINUS_SIGN, minusSign, precedence);
+ }
+ if (NaN!=null) {
+ symbols.setProperty(DecimalSymbols.NAN, NaN, precedence);
+ }
+ if (percent!=null) {
+ symbols.setProperty(DecimalSymbols.PERCENT, percent, precedence);
+ }
+ if (perMille!=null) {
+ symbols.setProperty(DecimalSymbols.PER_MILLE, perMille, precedence);
+ }
+ if (zeroDigit!=null) {
+ symbols.setProperty(DecimalSymbols.ZERO_DIGIT, zeroDigit, precedence);
+ }
+ if (digit!=null) {
+ symbols.setProperty(DecimalSymbols.DIGIT, digit, precedence);
+ }
+ if (patternSeparator!=null) {
+ symbols.setProperty(DecimalSymbols.PATTERN_SEPARATOR, patternSeparator, precedence);
+ }
+ }
+
+ /**
+ * Method supplied by declaration elements to add themselves to a stylesheet-level index
+ * @param decl the Declaration being indexed. (This corresponds to the StyleElement object
+ * except in cases where one module is imported several times with different precedence.)
+ * @param top the outermost XSLStylesheet element
+ */
+
+ public void index(Declaration decl, PrincipalStylesheetModule top) throws XPathException
+ {
+ prepareAttributes();
+ DecimalFormatManager dfm = getPreparedStylesheet().getDecimalFormatManager();
+ if (name==null) {
+ symbols = dfm.getDefaultDecimalFormat();
+ } else {
+ try {
+ StructuredQName formatName = makeQName(name);
+ symbols = dfm.obtainNamedDecimalFormat(formatName);
+ symbols.setHostLanguage(Configuration.XSLT);
+ } catch (XPathException err) {
+ compileError("Invalid decimal format name. " + err.getMessage(), "XTSE0020");
+ } catch (NamespaceException err) {
+ compileError("Invalid decimal format name. " + err.getMessage(), "XTSE0280");
+ }
+ }
+ }
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+ // no action
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/style/XSLDocument.java b/sf/saxon/style/XSLDocument.java
new file mode 100644
index 0000000..31374eb
--- /dev/null
+++ b/sf/saxon/style/XSLDocument.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.DocumentInstr;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* An xsl:document instruction in the stylesheet. <BR>
+* This instruction creates a document node in the result tree, optionally
+ * validating it.
+*/
+
+public class XSLDocument extends StyleElement {
+
+ private int validationAction = Validation.STRIP;
+ /*@Nullable*/ private SchemaType schemaType = null;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+ AttributeCollection atts = getAttributeList();
+
+ String validationAtt = null;
+ String typeAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.VALIDATION)) {
+ validationAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.TYPE)) {
+ typeAtt = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (validationAtt==null) {
+ validationAction = getContainingStylesheet().getDefaultValidation();
+ } else {
+ validationAction = Validation.getCode(validationAtt);
+ if (validationAction != Validation.STRIP && !getPreparedStylesheet().isSchemaAware()) {
+ compileError("To perform validation, a schema-aware XSLT processor is needed", "XTSE1660");
+ }
+ if (validationAction == Validation.INVALID) {
+ compileError("Invalid value of @validation attribute", "XTSE0020");
+ }
+ }
+ if (typeAtt!=null) {
+ if (!getPreparedStylesheet().isSchemaAware()) {
+ compileError("The @type attribute is available only with a schema-aware XSLT processor", "XTSE1660");
+ }
+ schemaType = getSchemaType(typeAtt);
+ validationAction = Validation.BY_TYPE;
+ }
+
+ if (typeAtt != null && validationAtt != null) {
+ compileError("The @validation and @type attributes are mutually exclusive", "XTSE1505");
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ //
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+
+ DocumentInstr inst = new DocumentInstr(false, null, getBaseURI());
+ inst.setValidationAction(validationAction, schemaType);
+ Expression b = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (b == null) {
+ b = Literal.makeEmptySequence();
+ }
+ inst.setContentExpression(b);
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLElement.java b/sf/saxon/style/XSLElement.java
new file mode 100644
index 0000000..52ef384
--- /dev/null
+++ b/sf/saxon/style/XSLElement.java
@@ -0,0 +1,249 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.lib.StandardURIChecker;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.value.Whitespace;
+
+
+/**
+ * An xsl:element element in the stylesheet. <br>
+ */
+
+public class XSLElement extends StyleElement {
+
+ /*@Nullable*/ private Expression elementName;
+ private Expression namespace = null;
+ private String use;
+ private AttributeSet[] attributeSets = null;
+ private int validation;
+ private SchemaType schemaType = null;
+ private boolean inheritNamespaces = true;
+ private Expression onEmpty;
+
+ /**
+ * Determine whether this node is an instruction.
+ *
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ *
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ String nameAtt = null;
+ String namespaceAtt = null;
+ String validationAtt = null;
+ String typeAtt = null;
+ String inheritAtt = null;
+ String onEmptyAtt = null;
+
+ for (int a = 0; a < atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals("name")) {
+ nameAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals("namespace")) {
+ namespaceAtt = atts.getValue(a);
+ } else if (f.equals("validation")) {
+ validationAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals("type")) {
+ typeAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals("inherit-namespaces")) {
+ inheritAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals("use-attribute-sets")) {
+ use = atts.getValue(a);
+ } else if (f.equals("on-empty")) {
+ onEmptyAtt = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (nameAtt == null) {
+ reportAbsence("name");
+ } else {
+ elementName = makeAttributeValueTemplate(nameAtt);
+ if (elementName instanceof StringLiteral) {
+ if (!getConfiguration().getNameChecker().isQName(((StringLiteral)elementName).getStringValue())) {
+ compileError("Element name " +
+ Err.wrap(((StringLiteral)elementName).getStringValue(), Err.ELEMENT) +
+ " is not a valid QName", "XTDE0820");
+ // to prevent duplicate error messages:
+ elementName = new StringLiteral("saxon-error-element");
+ }
+ }
+ }
+
+ if (namespaceAtt != null) {
+ namespace = makeAttributeValueTemplate(namespaceAtt);
+ if (namespace instanceof StringLiteral) {
+ if (!StandardURIChecker.getInstance().isValidURI(((StringLiteral)namespace).getStringValue())) {
+ compileError("The value of the namespace attribute must be a valid URI", "XTDE0835");
+ }
+ }
+ }
+
+ if (validationAtt != null) {
+ validation = Validation.getCode(validationAtt);
+ if (validation != Validation.STRIP && !getPreparedStylesheet().isSchemaAware()) {
+ validation = Validation.STRIP;
+ compileError("To perform validation, a schema-aware XSLT processor is needed", "XTSE1660");
+ }
+ if (validation == Validation.INVALID) {
+ compileError("Invalid value for @validation attribute. " +
+ "Permitted values are (strict, lax, preserve, strip)", "XTSE0020");
+ }
+ } else {
+ validation = getContainingStylesheet().getDefaultValidation();
+ }
+
+ if (typeAtt != null) {
+ if (!getPreparedStylesheet().isSchemaAware()) {
+ compileError("The @type attribute is available only with a schema-aware XSLT processor", "XTSE1660");
+ }
+ schemaType = getSchemaType(typeAtt);
+ validation = Validation.BY_TYPE;
+ }
+
+ if (typeAtt != null && validationAtt != null) {
+ compileError("The @validation and @type attributes are mutually exclusive", "XTSE1505");
+ }
+
+ if (inheritAtt != null) {
+ checkAttributeValue("inherit-namespaces", inheritAtt, false, StyleElement.YES_NO);
+ inheritNamespaces = (inheritAtt.equals("yes"));
+ }
+
+ if (onEmptyAtt != null) {
+ if (!isXslt30Processor()) {
+ compileError("The 'on-empty' attribute requires XSLT 3.0");
+ }
+ onEmpty = makeExpression(onEmptyAtt);
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (use != null) {
+ // find any referenced attribute sets
+ attributeSets = getAttributeSets(use, null);
+ }
+ elementName = typeCheck("name", elementName);
+ namespace = typeCheck("namespace", namespace);
+ onEmpty = typeCheck("on-empty", onEmpty);
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+
+ NamespaceResolver nsContext = null;
+
+ // deal specially with the case where the element name is known statically
+
+ if (elementName instanceof StringLiteral) {
+ CharSequence qName = ((StringLiteral)elementName).getStringValue();
+
+ String[] parts;
+ try {
+ parts = getConfiguration().getNameChecker().getQNameParts(qName);
+ } catch (QNameException e) {
+ compileError("Invalid element name: " + qName, "XTDE0820");
+ return null;
+ }
+
+ String nsuri = null;
+ if (namespace instanceof StringLiteral) {
+ nsuri = ((StringLiteral)namespace).getStringValue();
+ if (nsuri.length() == 0) {
+ parts[0] = "";
+ }
+ } else if (namespace == null) {
+ nsuri = getURIForPrefix(parts[0], true);
+ if (nsuri == null) {
+ undeclaredNamespaceError(parts[0], "XTDE0830");
+ }
+ }
+ if (nsuri != null) {
+ // Local name and namespace are both known statically: generate a FixedElement instruction
+ FingerprintedQName qn = new FingerprintedQName(parts[0], nsuri, parts[1]);
+ qn.allocateNameCode(getNamePool());
+ FixedElement inst = new FixedElement(qn,
+ NamespaceBinding.EMPTY_ARRAY,
+ inheritNamespaces,
+ schemaType,
+ validation);
+ inst.setBaseURI(getBaseURI());
+ return compileContentExpression(exec, decl, inst);
+ }
+ } else {
+ // if the namespace URI must be deduced at run-time from the element name
+ // prefix, we need to save the namespace context of the instruction
+
+ if (namespace == null) {
+ nsContext = makeNamespaceContext();
+ }
+ }
+
+ ComputedElement inst = new ComputedElement(elementName,
+ namespace,
+ nsContext,
+ //(nsContext==null ? null : nsContext.getURIForPrefix("", true)),
+ schemaType,
+ validation,
+ inheritNamespaces,
+ false);
+ if (onEmpty != null) {
+ inst.setOnEmpty(onEmpty);
+ }
+ return compileContentExpression(exec, decl, inst);
+ }
+
+ private Expression compileContentExpression(Executable exec, Declaration decl, ElementCreator inst) throws XPathException {
+ Expression content = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+
+ if (attributeSets != null) {
+ UseAttributeSets use = new UseAttributeSets(attributeSets);
+ if (content == null) {
+ content = use;
+ } else {
+ content = Block.makeBlock(use, content);
+ content.setLocationId(
+ allocateLocationId(getSystemId(), getLineNumber()));
+ }
+ }
+ if (content == null) {
+ content = Literal.makeEmptySequence();
+ }
+ inst.setContentExpression(content);
+ return inst;
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLFallback.java b/sf/saxon/style/XSLFallback.java
new file mode 100644
index 0000000..186a67c
--- /dev/null
+++ b/sf/saxon/style/XSLFallback.java
@@ -0,0 +1,69 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.trans.XPathException;
+
+/**
+* xsl:fallback element in stylesheet. <br>
+*/
+
+public class XSLFallback extends StyleElement {
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ /**
+ * Ask whether variables declared in an "uncle" element are visible.
+ * @return true for all elements except xsl:fallback and saxon:catch
+ */
+
+ protected boolean seesAvuncularVariables() {
+ return false;
+ }
+
+ public void prepareAttributes() throws XPathException {
+ AttributeCollection atts = getAttributeList();
+ for (int a=0; a<atts.getLength(); a++) {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ // Parent elements are now responsible for validating their children
+// StyleElement parent = (StyleElement)getParent();
+// if (!parent.mayContainFallback()) {
+// compileError("xsl:fallback is not allowed as a child of " + parent.getDisplayName(), "XT0010");
+// }
+ }
+
+ /*@Nullable*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ // if we get here, then the parent instruction is OK, so the fallback is not activated
+ return null;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLForEach.java b/sf/saxon/style/XSLForEach.java
new file mode 100644
index 0000000..0192383
--- /dev/null
+++ b/sf/saxon/style/XSLForEach.java
@@ -0,0 +1,151 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.ForEach;
+import net.sf.saxon.expr.sort.SortExpression;
+import net.sf.saxon.expr.sort.SortKeyDefinition;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.Whitespace;
+
+
+/**
+* Handler for xsl:for-each elements in stylesheet. <br>
+*/
+
+public class XSLForEach extends StyleElement {
+
+ /*@Nullable*/ private Expression select = null;
+ private boolean containsTailCall = false;
+ private Expression threads = null;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Specify that xsl:sort is a permitted child
+ */
+
+ protected boolean isPermittedChild(StyleElement child) {
+ return (child instanceof XSLSort);
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return getCommonChildItemType();
+ }
+
+ protected boolean markTailCalls() {
+ assert select != null;
+ if (Cardinality.allowsMany(select.getCardinality())) {
+ return false;
+ } else {
+ StyleElement last = getLastChildInstruction();
+ containsTailCall = last != null && last.markTailCalls();
+ return containsTailCall;
+ }
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ String selectAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else if (atts.getLocalName(a).equals("threads") && atts.getURI(a).equals(NamespaceConstant.SAXON)) {
+ String threadsAtt = Whitespace.trim(atts.getValue(a));
+ threads = makeAttributeValueTemplate(threadsAtt);
+ if (getPreparedStylesheet().isCompileWithTracing()) {
+ compileWarning("saxon:threads - no multithreading takes place when compiling with trace enabled",
+ SaxonErrorCode.SXWN9012);
+ threads = new StringLiteral("0");
+ } else if (!"EE".equals(getConfiguration().getEditionCode())) {
+ compileWarning("saxon:threads - ignored when not running Saxon-EE",
+ SaxonErrorCode.SXWN9013);
+ threads = new StringLiteral("0");
+ }
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt==null) {
+ reportAbsence("select");
+ select = Literal.makeEmptySequence();
+ } else {
+ select = makeExpression(selectAtt);
+ }
+
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkSortComesFirst(false);
+ select = typeCheck("select", select);
+ if (!hasChildNodes()) {
+ compileWarning("An empty xsl:for-each instruction has no effect", SaxonErrorCode.SXWN9009);
+ }
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ SortKeyDefinition[] sortKeys = makeSortKeys(decl);
+ Expression sortedSequence = select;
+ if (sortKeys != null) {
+ sortedSequence = new SortExpression(select, sortKeys);
+ }
+
+ Expression block = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (block == null) {
+ // body of for-each is empty: it's a no-op.
+ return Literal.makeEmptySequence();
+ }
+ try {
+ return new ForEach(sortedSequence, makeExpressionVisitor().simplify(block), containsTailCall, threads);
+ } catch (XPathException err) {
+ compileError(err);
+ return null;
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLForEachGroup.java b/sf/saxon/style/XSLForEachGroup.java
new file mode 100644
index 0000000..53f1920
--- /dev/null
+++ b/sf/saxon/style/XSLForEachGroup.java
@@ -0,0 +1,401 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.flwor.LocalVariableBinding;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.ForEachGroup;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.expr.sort.SortKeyDefinition;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.pattern.PatternSponsor;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+
+/**
+* Handler for xsl:for-each-group elements in stylesheet. This is a new instruction
+* defined in XSLT 2.0
+*/
+
+public final class XSLForEachGroup extends StyleElement {
+
+ /*@Nullable*/ private Expression select = null;
+ private Expression groupBy = null;
+ private Expression groupAdjacent = null;
+ private Pattern starting = null;
+ private Pattern ending = null;
+ private Expression collationName;
+ private SourceBinding groupSourceBinding = null;
+ private SourceBinding keySourceBinding = null;
+ private boolean composite = false;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Specify that xsl:sort is a permitted child
+ */
+
+ protected boolean isPermittedChild(StyleElement child) {
+ return (child instanceof XSLSort);
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ String selectAtt = null;
+ String groupByAtt = null;
+ String groupAdjacentAtt = null;
+ String startingAtt = null;
+ String endingAtt = null;
+ String collationAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.GROUP_BY)) {
+ groupByAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.GROUP_ADJACENT)) {
+ groupAdjacentAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.GROUP_STARTING_WITH)) {
+ startingAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.GROUP_ENDING_WITH)) {
+ endingAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.COLLATION)) {
+ collationAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals("bind-group")) {
+ try {
+ StructuredQName groupingVar = makeQName(atts.getValue(a));
+ groupSourceBinding = new SourceBinding(this);
+ groupSourceBinding.setVariableQName(groupingVar);
+ groupSourceBinding.setProperty(SourceBinding.GROUP, true);
+ } catch (NamespaceException e) {
+ compileError(e.getMessage());
+ }
+ } else if (f.equals("bind-grouping-key")) {
+ try {
+ StructuredQName keyVar = makeQName(atts.getValue(a));
+ keySourceBinding = new SourceBinding(this);
+ keySourceBinding.setVariableQName(keyVar);
+ } catch (NamespaceException e) {
+ compileError(e.getMessage());
+ }
+ } else if (f.equals("composite")) {
+ if (!isXslt30Processor()) {
+ compileError("The 'composite' attribute requires XSLT 3.0");
+ }
+ String val = Whitespace.trim(atts.getValue(a));
+ if (val.equals("yes")) {
+ composite = true;
+ } else if (val.equals("no")) {
+ composite = false;
+ } else {
+ compileError("The value of the 'composite' attribute must be 'yes' or 'no'");
+ }
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt==null) {
+ reportAbsence("select");
+ select = Literal.makeEmptySequence(); // for error recovery
+ } else {
+ select = makeExpression(selectAtt);
+ }
+
+ int c = (groupByAtt==null ? 0 : 1) +
+ (groupAdjacentAtt==null ? 0 : 1) +
+ (startingAtt==null ? 0 : 1) +
+ (endingAtt==null ? 0 : 1);
+ if (c!=1) {
+ compileError("Exactly one of the attributes group-by, group-adjacent, group-starting-with, " +
+ "and group-ending-with must be specified", "XTSE1080");
+ }
+
+ if (groupByAtt != null) {
+ groupBy = makeExpression(groupByAtt);
+ }
+
+ if (groupAdjacentAtt != null) {
+ groupAdjacent = makeExpression(groupAdjacentAtt);
+ }
+
+ if (startingAtt != null) {
+ starting = makePattern(startingAtt);
+ }
+
+ if (endingAtt != null) {
+ ending = makePattern(endingAtt);
+ }
+
+ if (collationAtt != null) {
+ if (groupBy==null && groupAdjacent==null) {
+ compileError("A collation may be specified only if group-by or group-adjacent is specified", "XTSE1090");
+ } else {
+ collationName = makeAttributeValueTemplate(collationAtt);
+ if (collationName instanceof StringLiteral) {
+ String collation = ((StringLiteral)collationName).getStringValue();
+ URI collationURI;
+ try {
+ collationURI = new URI(collation);
+ if (!collationURI.isAbsolute()) {
+ URI base = new URI(getBaseURI());
+ collationURI = base.resolve(collationURI);
+ collationName = new StringLiteral(collationURI.toString());
+ }
+ } catch (URISyntaxException err) {
+ compileError("Collation name '" + collationName + "' is not a valid URI", "XTDE1110");
+ collationName = new StringLiteral(NamespaceConstant.CODEPOINT_COLLATION_URI);
+ }
+ }
+ }
+ } else {
+ String defaultCollation = getDefaultCollationName();
+ if (defaultCollation != null) {
+ collationName = new StringLiteral(defaultCollation);
+ }
+ }
+
+ if ((groupSourceBinding != null || keySourceBinding != null) &&
+ !isXslt30Processor()) {
+ compileError("for-each-group binding variables are allowed only with XSLT 3.0");
+ }
+
+ if (keySourceBinding != null && groupBy==null && groupAdjacent==null) {
+ compileError("A grouping-key binding can be specified only with group-by or group-adjacent");
+ }
+
+ if (keySourceBinding != null && groupSourceBinding != null &&
+ keySourceBinding.getVariableQName().equals(groupSourceBinding.getVariableQName())) {
+ compileError("The group variable and grouping-key variable must have different names");
+ }
+
+ if (composite && (starting != null || ending != null)) {
+ compileError("The value composite='yes' cannot be used with " +
+ (starting==null ? "grouping-ending-with" : "group-starting-with"));
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkSortComesFirst(false);
+ select = typeCheck("select", select);
+
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ if (groupBy != null) {
+ groupBy = typeCheck("group-by", groupBy);
+ try {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:for-each-group/group-by", 0);
+ groupBy = TypeChecker.staticTypeCheck(groupBy,
+ SequenceType.ATOMIC_SEQUENCE,
+ false, role, visitor);
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ } else if (groupAdjacent != null) {
+ groupAdjacent = typeCheck("group-adjacent", groupAdjacent);
+ try {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:for-each-group/group-adjacent", 0);
+ role.setErrorCode("XTTE1100");
+ groupAdjacent = TypeChecker.staticTypeCheck(groupAdjacent,
+ (composite ? SequenceType.ATOMIC_SEQUENCE : SequenceType.SINGLE_ATOMIC),
+ false, role, visitor);
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ }
+
+ starting = typeCheck("starting", starting);
+ ending = typeCheck("ending", ending);
+
+ if ((starting != null || ending != null) && !visitor.getStaticContext().getXPathLanguageLevel().equals(DecimalValue.THREE)) {
+ try {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:for-each-group/select", 0);
+ role.setErrorCode("XTTE1120");
+ select = TypeChecker.staticTypeCheck(select,
+ SequenceType.NODE_SEQUENCE,
+ false, role, visitor);
+ } catch (XPathException err) {
+ String prefix = (starting != null ?
+ "With group-starting-with attribute: " :
+ "With group-ending-with attribute: ");
+ compileError(prefix + err.getMessage(), err.getErrorCodeQName());
+ }
+ }
+ if (!hasChildNodes()) {
+ compileWarning("An empty xsl:for-each-group instruction has no effect", SaxonErrorCode.SXWN9009);
+ }
+
+ }
+
+ @Override
+ public void postValidate() throws XPathException {
+ // Check that the variables bound in the xsl:for-each-group are not used inappropriately in any child xsl:sort
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLSort) {
+ if (groupSourceBinding != null) {
+ SortKeyDefinition sk = ((XSLSort)child).getSortKeyDefinition();
+ checkNoDependency(sk.getLanguage(), groupSourceBinding);
+ checkNoDependency(sk.getOrder(), groupSourceBinding);
+ checkNoDependency(sk.getCaseOrder(), groupSourceBinding);
+ checkNoDependency(sk.getDataTypeExpression(), groupSourceBinding);
+ checkNoDependency(sk.getStable(), groupSourceBinding);
+ checkNoDependency(sk.getCollationNameExpression(), groupSourceBinding);
+ }
+ if (keySourceBinding != null) {
+ SortKeyDefinition sk = ((XSLSort)child).getSortKeyDefinition();
+ checkNoDependency(sk.getLanguage(), keySourceBinding);
+ checkNoDependency(sk.getOrder(), keySourceBinding);
+ checkNoDependency(sk.getCaseOrder(), keySourceBinding);
+ checkNoDependency(sk.getDataTypeExpression(), keySourceBinding);
+ checkNoDependency(sk.getStable(), keySourceBinding);
+ checkNoDependency(sk.getCollationNameExpression(), keySourceBinding);
+ }
+ }
+ }
+ }
+
+ private static void checkNoDependency(Expression exp, SourceBinding binding) throws XPathException {
+ if (exp != null) {
+ if (exp instanceof VariableReference) {
+ for (BindingReference ref : binding.getReferences()) {
+ if (ref == exp) {
+ throw new XPathException("Variable bound by xsl:for-each-group " +
+ "must not be referenced in an AVT attribute of a contained xsl:sort element", "XTSE3220");
+ }
+ }
+ } else {
+ for (Iterator<Expression> sub = exp.iterateSubExpressions(); sub.hasNext();) {
+ checkNoDependency(sub.next(), binding);
+ }
+ }
+ }
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+
+ StringCollator collator = null;
+ if (collationName instanceof StringLiteral) {
+ // if the collation name is constant, then we've already resolved it against the base URI
+ final String uri = ((StringLiteral)collationName).getStringValue();
+ collator = getPrincipalStylesheetModule().findCollation(uri, getBaseURI());
+ if (collator==null) {
+ compileError("The collation name '" + collationName + "' has not been defined", "XTDE1110");
+ }
+ }
+
+ byte algorithm = 0;
+ Expression key = null;
+ if (groupBy != null) {
+ algorithm = ForEachGroup.GROUP_BY;
+ key = groupBy;
+ } else if (groupAdjacent != null) {
+ algorithm = ForEachGroup.GROUP_ADJACENT;
+ key = groupAdjacent;
+ } else if (starting != null) {
+ algorithm = ForEachGroup.GROUP_STARTING;
+ key = new PatternSponsor(starting);
+ } else if (ending != null) {
+ algorithm = ForEachGroup.GROUP_ENDING;
+ key = new PatternSponsor(ending);
+ }
+
+ Expression action = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (action == null) {
+ // body of for-each is empty: it's a no-op.
+ return Literal.makeEmptySequence();
+ }
+ try {
+ LocalVariableBinding groupBinding = null;
+ LocalVariableBinding keyBinding = null;
+ if (groupSourceBinding != null) {
+ groupBinding = new LocalVariableBinding(groupSourceBinding.getVariableQName(), SequenceType.ANY_SEQUENCE);
+ groupSourceBinding.fixupBinding(groupBinding);
+ }
+ if (keySourceBinding != null) {
+ keyBinding = new LocalVariableBinding(keySourceBinding.getVariableQName(), SequenceType.ANY_SEQUENCE);
+ keySourceBinding.fixupBinding(keyBinding);
+ }
+ ForEachGroup instr = new ForEachGroup( select,
+ makeExpressionVisitor().simplify(action),
+ algorithm,
+ key,
+ collator,
+ collationName,
+ ExpressionTool.getBaseURI(staticContext, action, true),
+ makeSortKeys(decl) );
+
+ instr.setGroupBinding(groupBinding);
+ instr.setKeyBinding(keyBinding);
+ instr.setComposite(composite);
+ return instr;
+ } catch (XPathException e) {
+ compileError(e);
+ return null;
+ }
+
+ }
+
+ public SourceBinding getGroupSourceBinding() {
+ return groupSourceBinding;
+ }
+
+ public SourceBinding getKeyBinding() {
+ return keySourceBinding;
+ }
+
+ public StructuredQName getGroupBindingName() {
+ return (groupSourceBinding == null ? null : groupSourceBinding.getVariableQName());
+ }
+
+ public StructuredQName getKeyBindingName() {
+ return (keySourceBinding == null ? null : keySourceBinding.getVariableQName());
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLFunction.java b/sf/saxon/style/XSLFunction.java
new file mode 100644
index 0000000..103e31e
--- /dev/null
+++ b/sf/saxon/style/XSLFunction.java
@@ -0,0 +1,521 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* Handler for xsl:function elements in stylesheet (XSLT 2.0). <BR>
+* Attributes: <br>
+* name gives the name of the function
+* saxon:memo-function=yes|no indicates whether it acts as a memo function.
+*/
+
+public class XSLFunction extends StyleElement implements StylesheetProcedure {
+
+ /*@Nullable*/ private String nameAtt = null;
+ private String asAtt = null;
+ private String overrideAtt = null;
+ private SequenceType resultType;
+ private String functionName;
+ private SlotManager stackFrameMap;
+ private boolean memoFunction = false;
+ private boolean override = true;
+ private int numberOfArguments = -1; // -1 means not yet known
+ private UserFunction compiledFunction;
+
+ // List of UserFunctionCall objects that reference this XSLFunction
+ List<UserFunctionReference> references = new ArrayList<UserFunctionReference>(10);
+
+ /**
+ * Method called by UserFunctionCall to register the function call for
+ * subsequent fixup.
+ * @param ref the UserFunctionCall to be registered
+ */
+
+ public void registerReference(UserFunctionReference ref) {
+ references.add(ref);
+ }
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+ overrideAtt = "yes";
+ for (int a=0; a<atts.getLength(); a++) {
+ String uri = atts.getURI(a);
+ String local = atts.getLocalName(a);
+ if ("".equals(uri)) {
+ if (local.equals(StandardNames.NAME)) {
+ nameAtt = Whitespace.trim(atts.getValue(a));
+ assert (nameAtt != null);
+ if (nameAtt.indexOf(':')<0) {
+ compileError("Function name must have a namespace prefix", "XTSE0740");
+ }
+ try {
+ setObjectName(makeQName(nameAtt));
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE0280");
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ } else if (local.equals(StandardNames.AS)) {
+ asAtt = atts.getValue(a);
+ } else if (local.equals(StandardNames.OVERRIDE)) {
+ overrideAtt = Whitespace.trim(atts.getValue(a));
+ if ("yes".equals(overrideAtt)) {
+ override = true;
+ } else if ("no".equals(overrideAtt)) {
+ override = false;
+ } else {
+ override = true;
+ compileError("override must be 'yes' or 'no'", "XTSE0020");
+ }
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ } else if (local.equals("memo-function") && uri.equals(NamespaceConstant.SAXON)) {
+ String memoAtt = Whitespace.trim(atts.getValue(a));
+ if ("yes".equals(memoAtt)) {
+ memoFunction = true;
+ } else if ("no".equals(memoAtt)) {
+ memoFunction = false;
+ } else {
+ compileError("saxon:memo-function must be 'yes' or 'no'", "XTSE0020");
+ }
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (nameAtt == null) {
+ reportAbsence("name");
+ nameAtt="xsl:unnamed-function-" + generateId();
+ }
+
+ if (asAtt == null) {
+ resultType = SequenceType.ANY_SEQUENCE;
+ } else {
+ resultType = makeSequenceType(asAtt);
+ }
+
+ functionName = nameAtt;
+ }
+
+ private String generateId() {
+ FastStringBuffer buff = new FastStringBuffer(FastStringBuffer.TINY);
+ generateId(buff);
+ return buff.toString();
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ * If there is no name, the value will be -1.
+ */
+
+ /*@NotNull*/
+ public StructuredQName getObjectName() {
+ StructuredQName qn = super.getObjectName();
+ if (qn == null) {
+ nameAtt = Whitespace.trim(getAttributeValue("", "name"));
+ if (nameAtt == null) {
+ return new StructuredQName("saxon", NamespaceConstant.SAXON, "badly-named-function" + generateId());
+ }
+ try {
+ qn = makeQName(nameAtt);
+ setObjectName(qn);
+ } catch (NamespaceException err) {
+ return new StructuredQName("saxon", NamespaceConstant.SAXON, "badly-named-function" + generateId());
+ } catch (XPathException err) {
+ return new StructuredQName("saxon", NamespaceConstant.SAXON, "badly-named-function" + generateId());
+ }
+ }
+ return qn;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body.
+ * @return true: yes, it may contain a general template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ protected boolean mayContainParam(String attName) {
+ return !"required".equals(attName);
+ }
+
+ /**
+ * Specify that xsl:param is a permitted child
+ */
+
+ protected boolean isPermittedChild(StyleElement child) {
+ return (child instanceof XSLLocalParam);
+ }
+ /**
+ * Is override="yes"?.
+ * @return true if override="yes" was specified, otherwise false
+ */
+
+ public boolean isOverriding() {
+ if (overrideAtt == null) {
+ // this is a forwards reference
+ try {
+ prepareAttributes();
+ } catch (XPathException e) {
+ // no action: error will be caught later
+ }
+ }
+ return override;
+ }
+
+ protected void index(Declaration decl, PrincipalStylesheetModule top) throws XPathException {
+ top.indexFunction(decl);
+ }
+
+ /**
+ * Notify all references to this function of the data type.
+ * @throws XPathException
+ */
+
+ public void fixupReferences() throws XPathException {
+ for (UserFunctionReference reference : references) {
+ if (reference instanceof UserFunctionCall) {
+ ((UserFunctionCall)reference).setStaticType(resultType);
+ }
+ }
+ super.fixupReferences();
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+
+ stackFrameMap = getConfiguration().makeSlotManager();
+
+ // check the element is at the top level of the stylesheet
+
+ checkTopLevel("XTSE0010");
+ getNumberOfArguments();
+
+ }
+
+
+ /**
+ * Compile the function definition to create an executable representation
+ * The compileDeclaration() method has the side-effect of binding
+ * all references to the function to the executable representation
+ * (a UserFunction object)
+ * @throws XPathException
+ */
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+ compileAsExpression(exec, decl);
+ }
+
+ /**
+ * Compile the function into a UserFunction object, which treats the function
+ * body as a single XPath expression. This involves recursively translating
+ * xsl:variable declarations into let expressions, withe the action part of the
+ * let expression containing the rest of the function body.
+ * The UserFunction that is created will be linked from all calls to
+ * this function, so nothing else needs to be done with the result. If there are
+ * no calls to it, the compiled function will be garbage-collected away.
+ * @param exec the Executable
+ * @param decl this xsl:function declaration
+ * @throws XPathException if a failure occurs
+ */
+
+ private void compileAsExpression(Executable exec, Declaration decl) throws XPathException {
+ Expression exp = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), false);
+ if (exp == null) {
+ exp = Literal.makeEmptySequence();
+ } else {
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ exp = exp.simplify(visitor);
+ }
+
+ if (exec.getConfiguration().isCompileWithTracing()) {
+ TraceExpression trace = new TraceExpression(exp);
+ trace.setConstructType(StandardNames.XSL_FUNCTION);
+ trace.setNamespaceResolver(getNamespaceResolver());
+ trace.setObjectName(getObjectName());
+ exp = trace;
+ }
+
+ UserFunction fn = exec.getConfiguration().newUserFunction(memoFunction);
+ fn.setHostLanguage(Configuration.XSLT);
+ fn.setBody(exp);
+ fn.setFunctionName(getObjectName());
+ setParameterDefinitions(fn);
+ fn.setResultType(getResultType());
+ fn.setLineNumber(getLineNumber());
+ fn.setSystemId(getSystemId());
+ fn.setStackFrameMap(stackFrameMap);
+ fn.setExecutable(exec);
+ compiledFunction = fn;
+ fixupInstruction(fn);
+
+ if (memoFunction && !fn.isMemoFunction()) {
+ compileWarning("Memo functions are not available in Saxon-HE: saxon:memo-function attribute ignored",
+ SaxonErrorCode.SXWN9011);
+ }
+ }
+
+ public void typeCheckBody() throws XPathException {
+ Expression exp = compiledFunction.getBody();
+ ExpressionTool.resetPropertiesWithinSubtree(exp);
+ Expression exp2 = exp;
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ try {
+ // We've already done the typecheck of each XPath expression, but it's worth doing again at this
+ // level because we have more information now.
+
+ exp2 = visitor.typeCheck(exp, null);
+ if (resultType != null) {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.FUNCTION_RESULT, functionName, 0);
+ role.setErrorCode("XTTE0780");
+ exp2 = TypeChecker.staticTypeCheck(exp2, resultType, false, role, visitor);
+ }
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ compileError(err);
+ }
+ if (exp2 != exp) {
+ compiledFunction.setBody(exp2);
+ }
+ }
+
+ public void optimize(Declaration declaration) throws XPathException {
+ Expression exp = compiledFunction.getBody();
+ ExpressionTool.resetPropertiesWithinSubtree(exp);
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ Expression exp2 = exp;
+ Optimizer opt = getConfiguration().obtainOptimizer();
+ try {
+ if (opt.getOptimizationLevel() != Optimizer.NO_OPTIMIZATION) {
+ exp2 = exp.optimize(visitor, null);
+ }
+
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ compileError(err);
+ }
+
+ // Try to extract new global variables from the body of the function
+ if (opt.getOptimizationLevel() != Optimizer.NO_OPTIMIZATION) {
+ Expression exp3 = opt.promoteExpressionsToGlobal(exp2, visitor);
+ if (exp3 != null) {
+ exp2 = visitor.optimize(exp3, null);
+ }
+ }
+
+ // Add trace wrapper code if required
+ exp2 = makeTraceInstruction(this, exp2);
+
+ allocateSlots(exp2);
+ if (exp2 != exp) {
+ compiledFunction.setBody(exp2);
+ }
+
+ int tailCalls = ExpressionTool.markTailFunctionCalls(exp2, getObjectName(), getNumberOfArguments());
+ if (tailCalls != 0) {
+ compiledFunction.setTailRecursive(tailCalls > 0, tailCalls > 1);
+ compiledFunction.setBody(new TailCallLoop(compiledFunction));
+ }
+
+ // Generate byte code if appropriate
+
+ if (getConfiguration().isGenerateByteCode(Configuration.XSLT)) {
+ try {
+ Expression cbody = opt.compileToByteCode(compiledFunction.getBody(), nameAtt,
+ Expression.PROCESS_METHOD | Expression.ITERATE_METHOD);
+ if (cbody != null) {
+ compiledFunction.setBody(cbody);
+ }
+ } catch (Exception e) {
+ System.err.println("Failed while compiling function " + nameAtt);
+ e.printStackTrace();
+ throw new XPathException(e);
+ }
+ }
+
+ compiledFunction.computeEvaluationMode();
+
+ if (isExplaining()) {
+ exp2.explain(getConfiguration().getStandardErrorOutput());
+ }
+ }
+
+ /**
+ * Fixup all function references.
+ * @param compiledFunction the Instruction representing this function in the compiled code
+ * @throws XPathException if an error occurs.
+ */
+
+ private void fixupInstruction(UserFunction compiledFunction)
+ throws XPathException {
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ try {
+ for (UserFunctionReference reference : references) {
+ reference.setFunction(compiledFunction);
+ if (reference instanceof UserFunctionCall) {
+ UserFunctionCall call = (UserFunctionCall)reference;
+ call.checkFunctionCall(compiledFunction, visitor);
+ call.computeArgumentEvaluationModes();
+ }
+ }
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ }
+
+ /**
+ * Get associated Procedure (for details of stack frame).
+ * @return the associated Procedure object
+ */
+
+ public SlotManager getSlotManager() {
+ return stackFrameMap;
+ }
+
+ /**
+ * Get the type of value returned by this function
+ * @return the declared result type, or the inferred result type
+ * if this is more precise
+ */
+ public SequenceType getResultType() {
+ if (resultType == null) {
+ // may be handling a forwards reference - see hof-038
+ String asAtt = getAttributeValue("", "as");
+ if (asAtt != null) {
+ try {
+ resultType = makeSequenceType(asAtt);
+ } catch (XPathException err) {
+ // the error will be reported when we get round to processing the function declaration
+ }
+ }
+ }
+ return resultType == null ? SequenceType.ANY_SEQUENCE : resultType;
+ }
+
+ /**
+ * Get the number of arguments declared by this function (that is, its arity).
+ * @return the arity of the function
+ */
+
+ public int getNumberOfArguments() {
+ if (numberOfArguments == -1) {
+ numberOfArguments = 0;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ Item child = kids.next();
+ if (child instanceof XSLLocalParam) {
+ numberOfArguments++;
+ } else {
+ return numberOfArguments;
+ }
+ }
+ }
+ return numberOfArguments;
+ }
+
+ /**
+ * Set the definitions of the parameters in the compiled function, as an array.
+ * @param fn the compiled object representing the user-written function
+ */
+
+ public void setParameterDefinitions(UserFunction fn) {
+ UserFunctionParameter[] params = new UserFunctionParameter[getNumberOfArguments()];
+ fn.setParameterDefinitions(params);
+ int count = 0;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo node = kids.next();
+ if (node == null) {
+ return;
+ }
+ if (node instanceof XSLLocalParam) {
+ UserFunctionParameter param = new UserFunctionParameter();
+ params[count++] = param;
+ param.setRequiredType(((XSLLocalParam)node).getRequiredType());
+ param.setVariableQName(((XSLLocalParam)node).getVariableQName());
+ param.setSlotNumber(((XSLLocalParam)node).getSlotNumber());
+ ((XSLLocalParam)node).getSourceBinding().fixupBinding(param);
+ int refs = ExpressionTool.getReferenceCount(fn.getBody(), param, false);
+ param.setReferenceCount(refs);
+ }
+ }
+ }
+
+ /**
+ * Get the argument types
+ * @return the declared types of the arguments
+ */
+
+ public SequenceType[] getArgumentTypes() {
+ SequenceType[] types = new SequenceType[getNumberOfArguments()];
+ int count = 0;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo node = kids.next();
+ if (node == null) {
+ return types;
+ }
+ if (node instanceof XSLLocalParam) {
+ types[count++] = (((XSLLocalParam)node).getRequiredType());
+ }
+ }
+ }
+
+ /**
+ * Get the compiled function
+ * @return the object representing the compiled user-written function
+ */
+
+ public UserFunction getCompiledFunction() {
+ return compiledFunction;
+ }
+
+ /**
+ * Get the type of construct. This will be a constant in
+ * class {@link net.sf.saxon.trace.Location}. This method is part of the
+ * {@link net.sf.saxon.trace.InstructionInfo} interface
+ */
+
+ public int getConstructType() {
+ return StandardNames.XSL_FUNCTION;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLGeneralIncorporate.java b/sf/saxon/style/XSLGeneralIncorporate.java
new file mode 100644
index 0000000..f021ace
--- /dev/null
+++ b/sf/saxon/style/XSLGeneralIncorporate.java
@@ -0,0 +1,249 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.event.FilterFactory;
+import net.sf.saxon.event.IDFilter;
+import net.sf.saxon.event.ProxyReceiver;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.functions.DocumentFn;
+import net.sf.saxon.lib.AugmentedSource;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.DocumentURI;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.linked.DocumentImpl;
+import net.sf.saxon.tree.linked.ElementImpl;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+
+
+/**
+* Abstract class to represent xsl:include or xsl:import element in the stylesheet. <br>
+* The xsl:include and xsl:import elements have mandatory attribute href
+*/
+
+public abstract class XSLGeneralIncorporate extends StyleElement {
+
+ private String href;
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+
+ /**
+ * isImport() returns true if this is an xsl:import declaration rather than an xsl:include
+ * @return true if this is an xsl:import declaration, false if it is an xsl:include
+ */
+
+ public abstract boolean isImport();
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.HREF)) {
+ href = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (href==null) {
+ reportAbsence("href");
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ validateInstruction();
+ }
+
+ public void validateInstruction() throws XPathException {
+ checkEmpty();
+ checkTopLevel(isImport() ? "XTSE0190" : "XTSE0170");
+ }
+
+ /**
+ * Get the included or imported stylesheet module
+ * @param importer the module that requested the include or import (used to check for cycles)
+ * @param precedence the import precedence to be allocated to the included or imported module
+ * @return the xsl:stylesheet element at the root of the included/imported module
+ * @throws XPathException if any failure occurs
+ */
+
+ /*@Nullable*/ public StylesheetModule getIncludedStylesheet(StylesheetModule importer, int precedence)
+ throws XPathException {
+
+ if (href==null) {
+ // error already reported
+ return null;
+ }
+
+ //checkEmpty();
+ //checkTopLevel((this instanceof XSLInclude ? "XTSE0170" : "XTSE0190"));
+
+ try {
+ PrincipalStylesheetModule psm = importer.getPrincipalStylesheetModule();
+ PreparedStylesheet pss = psm.getPreparedStylesheet();
+ URIResolver resolver = pss.getCompilerInfo().getURIResolver();
+ Configuration config = pss.getConfiguration();
+ XSLStylesheet includedSheet;
+ StylesheetModule incModule;
+
+ DocumentURI key = DocumentFn.computeDocumentKey(href, getBaseURI(), resolver);
+ includedSheet = psm.getStylesheetDocument(key);
+ if (includedSheet != null) {
+ // we already have the stylesheet document in cache; but we need to create a new module,
+ // because the import precedence might be different. See test impincl30.
+ incModule = new StylesheetModule(includedSheet, precedence);
+ incModule.setImporter(importer);
+
+ // check for recursion
+ if (checkForRecursion(importer, incModule.getSourceElement())) {
+ return null;
+ }
+
+ } else {
+
+ //System.err.println("GeneralIncorporate: href=" + href + " base=" + getBaseURI());
+ String relative = href;
+ String fragment = null;
+ int hash = relative.indexOf('#');
+ if (hash == 0 || relative.length() == 0) {
+ compileError("A stylesheet cannot " + getLocalPart() + " itself",
+ (this instanceof XSLInclude ? "XTSE0180" : "XTSE0210"));
+ return null;
+ } else if (hash == relative.length() - 1) {
+ relative = relative.substring(0, hash);
+ } else if (hash > 0) {
+ if (hash+1 < relative.length()) {
+ fragment = relative.substring(hash+1);
+ }
+ relative = relative.substring(0, hash);
+ }
+ Source source;
+ try {
+ source = resolver.resolve(relative, getBaseURI());
+ } catch (TransformerException e) {
+ throw XPathException.makeXPathException(e);
+ }
+
+ // if a user URI resolver returns null, try the standard one
+ // (Note, the standard URI resolver never returns null)
+ if (source==null) {
+ source = config.getSystemURIResolver().resolve(relative, getBaseURI());
+ }
+
+ if (fragment != null) {
+ final String fragmentFinal = fragment;
+ FilterFactory factory = new FilterFactory() {
+ public ProxyReceiver makeFilter(Receiver next) {
+ return new IDFilter(next, fragmentFinal);
+ }
+ };
+ source = AugmentedSource.makeAugmentedSource(source);
+ ((AugmentedSource)source).addFilter(factory);
+ }
+
+ // check for recursion
+ if (checkForRecursion(importer, source)) {
+ return null;
+ }
+
+ DocumentImpl includedDoc = pss.loadStylesheetModule(source);
+
+ // allow the included document to use "Literal Result Element as Stylesheet" syntax
+
+ ElementImpl outermost = includedDoc.getDocumentElement();
+
+ if (outermost instanceof LiteralResultElement) {
+ includedDoc = ((LiteralResultElement)outermost)
+ .makeStylesheet(getPreparedStylesheet());
+ outermost = includedDoc.getDocumentElement();
+ }
+
+ if (!(outermost instanceof XSLStylesheet)) {
+ compileError("Included document " + href + " is not a stylesheet", "XTSE0165");
+ return null;
+ }
+ includedSheet = (XSLStylesheet)outermost;
+ includedSheet.setPrincipalStylesheetModule(psm);
+ psm.putStylesheetDocument(key, includedSheet);
+
+ incModule = new StylesheetModule(includedSheet, precedence);
+ incModule.setImporter(importer);
+ Declaration decl = new Declaration(incModule, includedSheet);
+ includedSheet.validate(decl);
+
+ if (includedSheet.validationError!=null) {
+ if (reportingCircumstances == REPORT_ALWAYS) {
+ includedSheet.compileError(includedSheet.validationError);
+ } else if (includedSheet.reportingCircumstances == REPORT_UNLESS_FORWARDS_COMPATIBLE
+ // not sure if this can still happen
+ /*&& !incSheet.forwardsCompatibleModeIsEnabled()*/) {
+ includedSheet.compileError(includedSheet.validationError);
+ }
+ }
+ }
+
+ incModule.spliceIncludes(); // resolve any nested imports and includes;
+
+ // Check the consistency of input-type-annotations
+ //assert thisSheet != null;
+ importer.setInputTypeAnnotations(includedSheet.getInputTypeAnnotationsAttribute() |
+ incModule.getInputTypeAnnotations());
+
+ return incModule;
+
+ } catch (XPathException err) {
+ err.setErrorCode("XTSE0165");
+ err.setIsStaticError(true);
+ compileError(err);
+ return null;
+ }
+ }
+
+ private boolean checkForRecursion(StylesheetModule importer, Source source) throws XPathException {
+ StylesheetModule anc = importer;
+
+ if (source.getSystemId() != null) {
+ while(anc!=null) {
+ if (DocumentURI.normalizeURI(source.getSystemId())
+ .equals(DocumentURI.normalizeURI(anc.getSourceElement().getSystemId()))) {
+ compileError("A stylesheet cannot " + getLocalPart() + " itself",
+ (this instanceof XSLInclude ? "XTSE0180" : "XTSE0210"));
+ return true;
+ }
+ anc = anc.getImporter();
+ }
+ }
+ return false;
+ }
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+ // no action. The node will never be compiled, because it replaces itself
+ // by the contents of the included file.
+ }
+}
+
diff --git a/sf/saxon/style/XSLGeneralVariable.java b/sf/saxon/style/XSLGeneralVariable.java
new file mode 100644
index 0000000..c2d7025
--- /dev/null
+++ b/sf/saxon/style/XSLGeneralVariable.java
@@ -0,0 +1,111 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.instruct.GeneralVariable;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+
+/**
+* This class defines common behaviour across xsl:variable, xsl:param, and xsl:with-param
+*/
+
+public abstract class XSLGeneralVariable extends StyleElement {
+
+ protected SourceBinding sourceBinding = new SourceBinding(this);
+ protected GeneralVariable compiledVariable = null;
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned. This is null for a variable: we are not
+ * interested in the type of the variable, but in what the xsl:variable constributes
+ * to the result of the sequence constructor it is part of.
+ */
+
+ protected ItemType getReturnedItemType() {
+ return null;
+ }
+
+ /**
+ * Get the source binding object that holds information about the declared variable.
+ */
+
+ public SourceBinding getSourceBinding() {
+ return sourceBinding;
+ }
+
+ public StructuredQName getVariableQName() {
+ return sourceBinding.getVariableQName();
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ /**
+ * Test whether this is a global variable or parameter
+ * @return true if this is global
+ */
+
+ public boolean isGlobal() {
+ return isTopLevel();
+ // might be called before the "global" field is initialized
+ }
+
+ /**
+ * Check that the variable is not already declared, and allocate a slot number
+ * @param decl the declaration being validated. A single XSLVariableDeclaration object may represent
+ * multiple declarations if it appears in a stylesheet module that is included/imported more than once
+ */
+
+ public void validate(Declaration decl) throws XPathException {
+ sourceBinding.validate();
+ if (sourceBinding.hasProperty(SourceBinding.STATIC) && !isXslt30Processor()) {
+ compileError("Static variables and parameters require XSLT 3.0 to be enabled");
+ }
+ }
+
+ /**
+ * Hook to allow additional validation of a parent element immediately after its
+ * children have been validated.
+ */
+
+ public void postValidate() throws XPathException {
+ sourceBinding.postValidate();
+ }
+
+
+ protected void setReferenceCount(GeneralVariable var) {
+ // overridden in subclass
+ }
+
+ /**
+ * Get the type of construct. This will be a constant in
+ * class {@link net.sf.saxon.trace.Location}. This method is part of the
+ * {@link net.sf.saxon.trace.InstructionInfo} interface
+ */
+
+ public int getConstructType() {
+ return StandardNames.XSL_VARIABLE;
+ }
+
+ public GeneralVariable getCompiledVariable() {
+ return compiledVariable;
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLGlobalParam.java b/sf/saxon/style/XSLGlobalParam.java
new file mode 100644
index 0000000..b9530f7
--- /dev/null
+++ b/sf/saxon/style/XSLGlobalParam.java
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.SuppliedParameterReference;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.GlobalParam;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceType;
+
+/**
+* An xsl:param element representing a global parameter (stylesheet parameter) in the stylesheet. <br>
+* The xsl:param element has mandatory attribute name and optional attributes
+ * select, required, as, ...
+*/
+
+public class XSLGlobalParam extends XSLGlobalVariable {
+
+ protected int getPermittedAttributes() {
+ return
+ SourceBinding.REQUIRED |
+ SourceBinding.SELECT |
+ SourceBinding.AS |
+ SourceBinding.STATIC;
+ }
+
+ /*@Nullable*/ Expression conversion = null;
+
+ public XSLGlobalParam() {
+ sourceBinding.setProperty(SourceBinding.PARAM, true);
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+
+ if (sourceBinding.hasProperty(SourceBinding.REQUIRED)) {
+ if (sourceBinding.getSelectExpression() != null) {
+ // NB, we do this test before setting the default select attribute
+ compileError("The select attribute should be omitted when required='yes'", "XTSE0010");
+ }
+ if (hasChildNodes()) {
+ compileError("A parameter specifying required='yes' must have empty content", "XTSE0010");
+ }
+ }
+
+ super.validate(decl);
+ }
+
+
+ /**
+ * Compile a global xsl:param element: this ensures space is available for local variables declared within
+ * this global variable
+ */
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+ if(sourceBinding.isStatic()) {
+ super.compileDeclaration(exec, decl);
+ }
+ if (!redundant) {
+ StructuredQName name = sourceBinding.getVariableQName();
+ int slot = exec.getGlobalVariableMap().allocateSlotNumber(name);
+ if (sourceBinding.getDeclaredType() != null) {
+ SuppliedParameterReference pref = new SuppliedParameterReference(slot);
+ pref.setLocationId(staticContext.getLocationMap().allocateLocationId(getSystemId(), getLineNumber()));
+ RoleLocator role = new RoleLocator(RoleLocator.PARAM, name.getDisplayName(), 0);
+ role.setErrorCode("XTTE0590");
+ conversion = TypeChecker.staticTypeCheck(
+ pref,
+ sourceBinding.getDeclaredType(),
+ false,
+ role, makeExpressionVisitor());
+ }
+
+ sourceBinding.handleSequenceConstructor(exec, decl);
+
+ GlobalParam binding = new GlobalParam();
+ binding.setExecutable(getPreparedStylesheet());
+ binding.setContainer(binding);
+ if (sourceBinding.hasProperty(SourceBinding.REQUIRED) /*|| sourceBinding.hasProperty(SourceBinding.IMPLICITLY_REQUIRED)*/ ) {
+ getPreparedStylesheet().addRequiredParam(sourceBinding.getVariableQName());
+ }
+ Expression select = sourceBinding.getSelectExpression();
+ if (select != null) {
+ select.setContainer(binding);
+ }
+ binding.setSelectExpression(select);
+ binding.setVariableQName(sourceBinding.getVariableQName());
+ initializeBinding(exec, decl, binding);
+
+ binding.setSlotNumber(slot);
+ binding.setRequiredType(getRequiredType());
+ binding.setRequiredParam(sourceBinding.hasProperty(SourceBinding.REQUIRED));
+ binding.setImplicitlyRequiredParam(sourceBinding.hasProperty(SourceBinding.IMPLICITLY_REQUIRED));
+ sourceBinding.fixupBinding(binding);
+ compiledVariable = binding;
+ }
+ }
+
+
+
+
+ /**
+ * Get the static type of the parameter. This is the declared type, because we cannot know
+ * the actual value in advance.
+ */
+
+ public SequenceType getRequiredType() {
+ SequenceType declaredType = sourceBinding.getDeclaredType();
+ if (declaredType!=null) {
+ return declaredType;
+ } else {
+ return SequenceType.ANY_SEQUENCE;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLGlobalVariable.java b/sf/saxon/style/XSLGlobalVariable.java
new file mode 100644
index 0000000..5194b2f
--- /dev/null
+++ b/sf/saxon/style/XSLGlobalVariable.java
@@ -0,0 +1,271 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.SequenceType;
+
+/**
+* Handler for xsl:variable elements appearing as a child of xsl:stylesheet. <br>
+* The xsl:variable element has mandatory attribute name and optional attribute select
+*/
+
+public class XSLGlobalVariable extends XSLGeneralVariable implements StylesheetProcedure {
+
+ SlotManager slotManager; // used to manage local variables declared inside this global variable
+
+ public XSLGlobalVariable() {
+ sourceBinding.setProperty(SourceBinding.GLOBAL, true);
+ }
+
+ protected int getPermittedAttributes() {
+ return
+ SourceBinding.ASSIGNABLE |
+ SourceBinding.SELECT |
+ SourceBinding.AS |
+ SourceBinding.STATIC;
+ }
+
+ private int state = 0;
+ // 0 = before prepareAttributes()
+ // 1 = during prepareAttributes()
+ // 2 = after prepareAttributes()
+
+ protected boolean redundant = false;
+
+ /**
+ * Ask whether this element contains a binding for a variable with a given name; and if it does,
+ * return the source binding information
+ * @param name the variable name
+ * @return the binding information if this element binds a variable of this name; otherwise null
+ */
+
+ public SourceBinding getBindingInformation(StructuredQName name) {
+ if (name.equals(sourceBinding.getVariableQName())) {
+ return sourceBinding;
+ } else {
+ return null;
+ }
+ }
+
+ public void prepareAttributes() throws XPathException {
+ if (state==2) return;
+ if (state==1) {
+ compileError("Circular reference to variable", "XTDE0640");
+ }
+ state = 1;
+ //System.err.println("Prepare attributes of $" + getVariableName());
+
+ sourceBinding.prepareAttributes(getPermittedAttributes());
+ state = 2;
+ }
+
+ @Override
+ protected void index(Declaration decl, PrincipalStylesheetModule top) throws XPathException {
+ top.indexVariableDeclaration(decl);
+ }
+
+ @Override
+ public void validate(Declaration decl) throws XPathException {
+ slotManager = getConfiguration().makeSlotManager();
+ super.validate(decl);
+ }
+
+ /**
+ * Ask whether the global variable is declared with assignable="yes"
+ * @return true if assignabl="yes" was specified
+ */
+
+ public boolean isAssignable() {
+ return sourceBinding.hasProperty(SourceBinding.ASSIGNABLE);
+ }
+
+ /**
+ * Determine whether this node is a declaration.
+ * @return true - a global variable is a declaration
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return false - a global variable is not an instruction
+ */
+
+ public boolean isInstruction() {
+ return false;
+ }
+
+ /**
+ * Get the static type of the variable. This is the declared type, unless the value
+ * is statically known and constant, in which case it is the actual type of the value.
+ */
+
+ public SequenceType getRequiredType() {
+ return sourceBinding.getInferredType(true);
+ }
+
+ @Override
+ public void fixupReferences() throws XPathException {
+ sourceBinding.fixupReferences();
+ super.fixupReferences();
+ }
+
+ /**
+ * Compile.
+ * This method ensures space is available for local variables declared within
+ * this global variable
+ */
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+
+ if (sourceBinding.getReferences().isEmpty() && !isAssignable()) {
+ redundant = true;
+ }
+
+ if (!redundant) {
+ sourceBinding.handleSequenceConstructor(exec, decl);
+ GlobalVariable inst = new GlobalVariable();
+ inst.setExecutable(exec);
+ Expression select = sourceBinding.getSelectExpression();
+ inst.setSelectExpression(select);
+ if (select != null) {
+ select.setContainer(inst);
+ }
+ inst.setVariableQName(sourceBinding.getVariableQName());
+ initializeBinding(exec, decl, inst);
+ inst.setAssignable(isAssignable());
+ int slot = exec.getGlobalVariableMap().allocateSlotNumber(sourceBinding.getVariableQName());
+ inst.setSlotNumber(slot);
+ inst.setRequiredType(getRequiredType());
+ sourceBinding.fixupBinding(inst);
+ inst.setContainer(inst);
+ compiledVariable = inst;
+ }
+
+ }
+
+ /**
+ * Initialize - common code called from the compile() method of all subclasses
+ * @param exec the executable
+ * @param decl this xsl:variable declaration
+ * @param var the representation of the variable declaration in the compiled executable
+ * @throws net.sf.saxon.trans.XPathException if an error is detected
+ */
+
+ protected void initializeBinding(Executable exec, Declaration decl, GeneralVariable var)
+ throws XPathException {
+
+ Expression select = var.getSelectExpression();
+ final GlobalVariable gvar = (GlobalVariable)var;
+ var.setContainer(gvar);
+ Expression exp2 = select;
+ if (exp2 != null) {
+ try {
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ exp2.setContainer(gvar);
+ exp2 = visitor.typeCheck(visitor.simplify(select), new ExpressionVisitor.ContextItemType(Type.ITEM_TYPE, true));
+ } catch (XPathException err) {
+ compileError(err);
+ }
+
+ // Add trace wrapper code if required
+ exp2 = makeTraceInstruction(this, exp2);
+
+ allocateSlots(exp2);
+ }
+ if (slotManager != null && slotManager.getNumberOfVariables() > 0) {
+ gvar.setContainsLocals(slotManager);
+ }
+ exec.registerGlobalVariable(gvar);
+ setReferenceCount(gvar);
+
+ if (exp2 != select) {
+ gvar.setSelectExpression(exp2);
+ }
+
+ }
+
+
+ /**
+ * Get the SlotManager associated with this stylesheet construct. The SlotManager contains the
+ * information needed to manage the local stack frames used by run-time instances of the code.
+ * @return the associated SlotManager object
+ */
+
+ public SlotManager getSlotManager() {
+ return slotManager;
+ }
+
+ /**
+ * Optimize the stylesheet construct
+ * @param declaration
+ */
+
+ public void optimize(Declaration declaration) throws XPathException {
+ if (!redundant && sourceBinding.getSelectExpression()!=null) {
+ Expression exp2 = sourceBinding.getSelectExpression();
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ Optimizer opt = getConfiguration().obtainOptimizer();
+ try {
+ if (opt.getOptimizationLevel() != Optimizer.NO_OPTIMIZATION) {
+ ExpressionTool.resetPropertiesWithinSubtree(exp2);
+ exp2 = exp2.optimize(visitor, new ExpressionVisitor.ContextItemType(AnyNodeTest.getInstance(), true));
+ }
+
+ } catch (XPathException err) {
+ err.maybeSetLocation(this);
+ compileError(err);
+ }
+
+ // Try to extract new global variables from the body of the variable declaration
+ // (but don't extract the whole body!)
+// if (opt.getOptimizationLevel() != Optimizer.NO_OPTIMIZATION) {
+// exp2 = opt.promoteExpressionsToGlobal(exp2, visitor, true);
+// }
+ // dropped because it doesn't seem to do much good - just splits up an expression
+ // into lots of small global variables.
+
+ allocateSlots(exp2);
+ if (slotManager != null && slotManager.getNumberOfVariables() > 0) {
+ ((GlobalVariable)compiledVariable).setContainsLocals(slotManager);
+ }
+
+ if (exp2 != sourceBinding.getSelectExpression()) {
+ //sourceBinding.setSelectExpresion(exp2);
+ compiledVariable.setSelectExpression(exp2);
+ }
+ }
+ }
+
+
+ /**
+ * Mark this global variable as redundant, typically because it is overridden by another global
+ * variable of the same name, or because there are no references to it
+ * @param redundant true if this variable is redundant, otherwise false
+ */
+
+ public void setRedundant(boolean redundant) {
+ this.redundant = redundant;
+ }
+
+
+
+}
+
diff --git a/sf/saxon/style/XSLIf.java b/sf/saxon/style/XSLIf.java
new file mode 100644
index 0000000..3374282
--- /dev/null
+++ b/sf/saxon/style/XSLIf.java
@@ -0,0 +1,146 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.Choose;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.BooleanValue;
+
+
+/**
+* Handler for xsl:if elements in stylesheet. <br>
+* The xsl:if element has a mandatory attribute test, a boolean expression.
+* The content is output if the test condition is true.
+*/
+
+public class XSLIf extends StyleElement {
+
+ /*@Nullable*/ private Expression test;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return getCommonChildItemType();
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+ test = prepareTestAttribute(this);
+ if (test==null) {
+ reportAbsence("test");
+ }
+ }
+
+ /**
+ * Process all the attributes, for an element where the only permitted attribute is "test"
+ * @param se the containing element
+ * @return the expression represented by the test attribute, or null if the attribute is absent
+ * @throws XPathException if an error is encountered
+ */
+
+ public static Expression prepareTestAttribute(StyleElement se) throws XPathException {
+
+ String testAtt=null;
+
+ AttributeCollection atts = se.getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.TEST)) {
+ testAtt = atts.getValue(a);
+ } else {
+ se.checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (testAtt==null) {
+ return null;
+ } else {
+ try {
+ return se.makeExpression(testAtt);
+ } catch (XPathException err) {
+ // already reported: prevent further errors
+ return Literal.makeLiteral(BooleanValue.FALSE);
+ }
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ test = typeCheck("test", test);
+ }
+
+ /**
+ * Mark tail-recursive calls on stylesheet functions. For most instructions, this does nothing.
+ */
+
+ public boolean markTailCalls() {
+ StyleElement last = getLastChildInstruction();
+ return last != null && last.markTailCalls();
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ if (test instanceof Literal) {
+ GroundedValue testVal = ((Literal)test).getValue();
+ // condition known statically, so we only need compile the code if true.
+ // This can happen with expressions such as test="function-available('abc')".
+ try {
+ if (testVal.effectiveBooleanValue()) {
+ return compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+// Block block = new Block();
+// block.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+// compileChildren(exec, block, true);
+// return block.simplify(getStaticContext());
+ } else {
+ return null;
+ }
+ } catch (XPathException err) {
+ // fall through to non-optimizing case
+ }
+ }
+
+ Expression action = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (action == null) {
+ return null;
+ }
+ Expression[] conditions = {test};
+ Expression[] actions = {action};
+
+ return new Choose(conditions, actions);
+ }
+
+
+}
\ No newline at end of file
diff --git a/sf/saxon/style/XSLImport.java b/sf/saxon/style/XSLImport.java
new file mode 100644
index 0000000..892e7b6
--- /dev/null
+++ b/sf/saxon/style/XSLImport.java
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+
+/**
+* xsl:import element in the stylesheet. <br>
+*/
+
+public class XSLImport extends XSLGeneralIncorporate {
+
+ /**
+ * isImport() returns true if this is an xsl:import statement rather than an xsl:include
+ */
+
+ public boolean isImport() {
+ return true;
+ }
+}
+
diff --git a/sf/saxon/style/XSLImportSchema.java b/sf/saxon/style/XSLImportSchema.java
new file mode 100644
index 0000000..13bb052
--- /dev/null
+++ b/sf/saxon/style/XSLImportSchema.java
@@ -0,0 +1,145 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.LicenseException;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.SchemaException;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.TransformerConfigurationException;
+
+
+/**
+* Compile-time representation of an xsl:import-schema declaration
+ * in a stylesheet
+*/
+
+public class XSLImportSchema extends StyleElement {
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+ String namespace = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.SCHEMA_LOCATION)) {
+ //
+ } else if (f.equals(StandardNames.NAMESPACE)) {
+ namespace = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if ("".equals(namespace)) {
+ compileError("The zero-length string is not a valid namespace URI. "+
+ "For a schema with no namespace, omit the namespace attribute");
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkTopLevel("XTSE0010");
+ }
+
+ protected void index(Declaration decl, PrincipalStylesheetModule top) throws XPathException {
+ try {
+ readSchema();
+ } catch (SchemaException e) {
+ throw XPathException.makeXPathException(e);
+ }
+ }
+
+ private void readSchema() throws SchemaException, XPathException {
+ try {
+ String schemaLoc = Whitespace.trim(getAttributeValue("", StandardNames.SCHEMA_LOCATION));
+ String namespace = Whitespace.trim(getAttributeValue("", StandardNames.NAMESPACE));
+ if (namespace==null) {
+ namespace = "";
+ } else {
+ namespace = namespace.trim();
+ }
+ PreparedStylesheet preparedStylesheet = getPreparedStylesheet();
+ Configuration config = preparedStylesheet.getConfiguration();
+ try {
+ config.checkLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XSLT, "xsl:import-schema");
+ } catch (LicenseException err) {
+ XPathException xe = new XPathException(err);
+ xe.setErrorCode("XTSE1650");
+ xe.setLocator(this);
+ throw xe;
+ }
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ NodeInfo inlineSchema = null;
+ while (true) {
+ Item child = kids.next();
+ if (child==null) {
+ break;
+ }
+ if (inlineSchema != null) {
+ compileError(getDisplayName() + " must not have more than one child element");
+ }
+ inlineSchema = (NodeInfo)child;
+ if (inlineSchema.getFingerprint() != StandardNames.XS_SCHEMA) {
+ compileError("The only child element permitted for " + getDisplayName() + " is xs:schema");
+ }
+ if (schemaLoc != null) {
+ compileError("The schema-location attribute must be absent if an inline schema is present");
+ }
+
+ namespace = config.readInlineSchema(inlineSchema, namespace,
+ preparedStylesheet.getCompilerInfo().getErrorListener());
+ getPrincipalStylesheetModule().addImportedSchema(namespace);
+ }
+ if (inlineSchema != null) {
+ return;
+ }
+ if (!config.isSchemaAvailable(namespace)) {
+ if (schemaLoc == null) {
+ compileWarning("No schema for this namespace is known, " +
+ "and no schema-location was supplied, so no schema has been imported",
+ SaxonErrorCode.SXWN9006);
+ return;
+ }
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ namespace = config.readSchema(pipe, getBaseURI(), schemaLoc, namespace);
+ }
+ getPrincipalStylesheetModule().addImportedSchema(namespace);
+ } catch (SchemaException err) {
+ compileError(err.getMessage(), "XTSE0220");
+ } catch (TransformerConfigurationException err) {
+ compileError(err.getMessage(), "XTSE0220");
+ }
+
+ }
+
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+ // No action. The effect of import-schema is compile-time only
+ }
+}
+
diff --git a/sf/saxon/style/XSLInclude.java b/sf/saxon/style/XSLInclude.java
new file mode 100644
index 0000000..731a23a
--- /dev/null
+++ b/sf/saxon/style/XSLInclude.java
@@ -0,0 +1,23 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+/**
+* xsl:include element in the stylesheet. <br>
+*/
+
+public class XSLInclude extends XSLGeneralIncorporate {
+
+ /**
+ * isImport() returns true if this is an xsl:import statement rather than an xsl:include
+ */
+
+ public boolean isImport() {
+ return false;
+ }
+}
diff --git a/sf/saxon/style/XSLKey.java b/sf/saxon/style/XSLKey.java
new file mode 100644
index 0000000..a5e01c4
--- /dev/null
+++ b/sf/saxon/style/XSLKey.java
@@ -0,0 +1,311 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.ItemTypePattern;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.KeyDefinition;
+import net.sf.saxon.trans.KeyManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ErrorType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+* Handler for xsl:key elements in stylesheet. <br>
+*/
+
+public class XSLKey extends StyleElement implements StylesheetProcedure {
+
+ private Pattern match;
+ private Expression use;
+ private String collationName;
+ private StructuredQName keyName;
+ SlotManager stackFrameMap;
+ private boolean rangeKey;
+ // needed if variables are used
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a sequence constructor
+ * @return true: yes, it may contain a sequence constructor
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ /**
+ * Get the Procedure object that looks after any local variables declared in the content constructor
+ */
+
+ public SlotManager getSlotManager() {
+ return stackFrameMap;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ String nameAtt = null;
+ String matchAtt = null;
+ String useAtt = null;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String uri = atts.getURI(a);
+ String local = atts.getLocalName(a);
+ if ("".equals(uri)) {
+ if (local.equals(StandardNames.NAME)) {
+ nameAtt = Whitespace.trim(atts.getValue(a)) ;
+ } else if (local.equals(StandardNames.USE)) {
+ useAtt = atts.getValue(a);
+ } else if (local.equals(StandardNames.MATCH)) {
+ matchAtt = atts.getValue(a);
+ } else if (local.equals(StandardNames.COLLATION)) {
+ collationName = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ } else if (local.equals("range-key") && uri.equals(NamespaceConstant.SAXON)) {
+ String rangeKeyAtt = Whitespace.trim(atts.getValue(a));
+ if ("yes".equals(rangeKeyAtt)) {
+ rangeKey = true;
+ } else if ("no".equals(rangeKeyAtt)) {
+ rangeKey = false;
+ } else {
+ compileError("saxon:range-key must be 'yes' or 'no'", "XTSE0020");
+ }
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (nameAtt==null) {
+ reportAbsence("name");
+ return;
+ }
+ try {
+ keyName = makeQName(nameAtt);
+ setObjectName(keyName);
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE0280");
+ } catch (XPathException err) {
+ compileError(err);
+ }
+
+ if (matchAtt==null) {
+ reportAbsence("match");
+ matchAtt = "*";
+ }
+ match = makePattern(matchAtt);
+ if (match == null) {
+ // error has been reported
+ match = new ItemTypePattern(ErrorType.getInstance());
+ }
+
+ if (useAtt!=null) {
+ use = makeExpression(useAtt);
+ }
+ }
+
+ public StructuredQName getKeyName() {
+ //We use null to mean "not yet evaluated"
+ try {
+ if (getObjectName()==null) {
+ // allow for forwards references
+ String nameAtt = getAttributeValue("", StandardNames.NAME);
+ if (nameAtt != null) {
+ setObjectName(makeQName(nameAtt));
+ }
+ }
+ return getObjectName();
+ } catch (NamespaceException err) {
+ return null; // the errors will be picked up later
+ } catch (XPathException err) {
+ return null;
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+
+ stackFrameMap = getConfiguration().makeSlotManager();
+ checkTopLevel("XTSE0010");
+ if (use!=null) {
+ // the value can be supplied as a content constructor in place of a use expression
+ if (hasChildNodes()) {
+ compileError("An xsl:key element with a @use attribute must be empty", "XTSE1205");
+ }
+ try {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:key/use", 0);
+ //role.setSourceLocator(new ExpressionLocation(this));
+ use = TypeChecker.staticTypeCheck(
+ use,
+ SequenceType.makeSequenceType(BuiltInAtomicType.ANY_ATOMIC, StaticProperty.ALLOWS_ZERO_OR_MORE),
+ false, role, makeExpressionVisitor());
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ } else {
+ if (!hasChildNodes()) {
+ compileError("An xsl:key element must either have a @use attribute or have content", "XTSE1205");
+ }
+ }
+ use = typeCheck("use", use);
+ match = typeCheck("match", match);
+
+ // Do a further check that the use expression makes sense in the context of the match pattern
+ if (use != null) {
+ use = makeExpressionVisitor().typeCheck(use, new ExpressionVisitor.ContextItemType(match.getItemType(), false));
+ }
+
+ if (collationName != null) {
+ URI collationURI;
+ try {
+ collationURI = new URI(collationName);
+ if (!collationURI.isAbsolute()) {
+ URI base = new URI(getBaseURI());
+ collationURI = base.resolve(collationURI);
+ collationName = collationURI.toString();
+ }
+ } catch (URISyntaxException err) {
+ compileError("Collation name '" + collationName + "' is not a valid URI");
+ //collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
+ }
+ } else {
+ collationName = getDefaultCollationName();
+ }
+ }
+
+ protected void index(Declaration decl, PrincipalStylesheetModule top) throws XPathException {
+ StructuredQName keyName = getKeyName();
+ if (keyName != null) {
+ top.getPreparedStylesheet().getKeyManager().preRegisterKeyDefinition(keyName);
+ }
+ }
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+ StaticContext env = getStaticContext();
+ Configuration config = env.getConfiguration();
+ StringCollator collator = null;
+ if (collationName != null) {
+ collator = getPrincipalStylesheetModule().findCollation(collationName, getBaseURI());
+ if (collator==null) {
+ compileError("The collation name " + Err.wrap(collationName, Err.URI) + " is not recognized", "XTSE1210");
+ collator = CodepointCollator.getInstance();
+ }
+ if (collator instanceof CodepointCollator) {
+ // if the user explicitly asks for the codepoint collation, treat it as if they hadn't asked
+ collator = null;
+ collationName = null;
+
+ } else if (!Configuration.getPlatform().canReturnCollationKeys(collator)) {
+ compileError("The collation used for xsl:key must be capable of generating collation keys", "XTSE1210");
+ }
+ }
+
+ if (use==null) {
+ Expression body = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+
+ try {
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ use = Atomizer.makeAtomizer(body);
+ use = visitor.simplify(use);
+ } catch (XPathException e) {
+ compileError(e);
+ }
+
+ try {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:key/use", 0);
+ //role.setSourceLocator(new ExpressionLocation(this));
+ use = TypeChecker.staticTypeCheck(
+ use,
+ SequenceType.makeSequenceType(BuiltInAtomicType.ANY_ATOMIC, StaticProperty.ALLOWS_ZERO_OR_MORE),
+ false, role, makeExpressionVisitor());
+ // Do a further check that the use expression makes sense in the context of the match pattern
+ assert match != null;
+ use = makeExpressionVisitor().typeCheck(use, new ExpressionVisitor.ContextItemType(match.getItemType(),false));
+
+
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ }
+
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ ExpressionVisitor.ContextItemType contextItemType = new ExpressionVisitor.ContextItemType(match.getItemType(), false);
+ use = use.optimize(visitor, contextItemType);
+ final TypeHierarchy th = config.getTypeHierarchy();
+ ItemType useItemType = use.getItemType(th);
+ if (useItemType == ErrorType.getInstance()) {
+ useItemType = BuiltInAtomicType.STRING; // corner case, prevents crashing
+ }
+ BuiltInAtomicType useType = (BuiltInAtomicType)useItemType.getPrimitiveItemType();
+ if (xPath10ModeIsEnabled()) {
+ if (!useType.equals(BuiltInAtomicType.STRING) && !useType.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ use = new AtomicSequenceConverter(use, BuiltInAtomicType.STRING);
+ ((AtomicSequenceConverter)use).allocateConverter(config, false);
+ useType = BuiltInAtomicType.STRING;
+ }
+ }
+ allocateSlots(use);
+ // first slot in pattern is reserved for current()
+ int nextFree = 0;
+ if ((match.getDependencies() & StaticProperty.DEPENDS_ON_CURRENT_ITEM) != 0) {
+ nextFree = 1;
+ }
+ int slots = match.allocateSlots(stackFrameMap, nextFree);
+ allocatePatternSlots(slots);
+
+ KeyManager km = getPreparedStylesheet().getKeyManager();
+ KeyDefinition keydef = new KeyDefinition(match, use, collationName, collator);
+ keydef.setRangeKey(rangeKey);
+ keydef.setIndexedItemType(useType);
+ keydef.setStackFrameMap(stackFrameMap);
+ keydef.setLocation(getSystemId(), getLineNumber());
+ keydef.setExecutable(getPreparedStylesheet());
+ keydef.setBackwardsCompatible(xPath10ModeIsEnabled());
+ try {
+ km.addKeyDefinition(keyName, keydef, exec.getConfiguration());
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ }
+
+ /**
+ * Optimize the stylesheet construct
+ * @param declaration this xsl:key declaration
+ */
+
+ public void optimize(Declaration declaration) throws XPathException {
+ // already done earlier
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/style/XSLLeafNodeConstructor.java b/sf/saxon/style/XSLLeafNodeConstructor.java
new file mode 100644
index 0000000..3117949
--- /dev/null
+++ b/sf/saxon/style/XSLLeafNodeConstructor.java
@@ -0,0 +1,170 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SimpleNodeConstructor;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+/**
+ * Common superclass for XSLT elements whose content template produces a text
+ * value: xsl:text, xsl:value-of, xsl:attribute, xsl:comment, xsl:namespace, and xsl:processing-instruction
+ */
+
+public abstract class XSLLeafNodeConstructor extends StyleElement {
+
+ //protected String stringValue = null;
+ /*@Nullable*/ protected Expression select = null;
+
+ /**
+ * Method for use by subclasses (processing-instruction and namespace) that take
+ * a name and a select attribute
+ * @return the expression defining the name attribute
+ * @throws XPathException if an error is detected
+ */
+
+ protected Expression prepareAttributesNameAndSelect() throws XPathException {
+
+ Expression name = null;
+ String nameAtt = null;
+ String selectAtt = null;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.NAME)) {
+ nameAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SELECT)) {
+ selectAtt = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (nameAtt==null) {
+ reportAbsence("name");
+ } else {
+ name = makeAttributeValueTemplate(nameAtt);
+ }
+
+ if (selectAtt!=null) {
+ select = makeExpression(selectAtt);
+ }
+
+ return name;
+ }
+
+ /**
+ * Determine whether this node is an instruction.
+ *
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ *
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (select != null && hasChildNodes()) {
+ String errorCode = getErrorCodeForSelectPlusContent();
+ compileError("An " + getDisplayName() + " element with a select attribute must be empty", errorCode);
+ }
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ NodeInfo first = kids.next();
+ if (select == null) {
+ if (first == null) {
+ // there are no child nodes and no select attribute
+ //stringValue = "";
+ select = new StringLiteral(StringValue.EMPTY_STRING);
+ } else {
+ if (kids.next() == null && !isExpandingText()) {
+ // there is exactly one child node
+ if (first.getNodeKind() == Type.TEXT) {
+ // it is a text node: optimize for this case
+ select = new StringLiteral(first.getStringValue());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the error code to be returned when the element has a select attribute but is not empty.
+ * @return the error code defined for this condition, for this particular instruction
+ */
+
+ protected abstract String getErrorCodeForSelectPlusContent();
+
+ protected void compileContent(Executable exec, Declaration decl, SimpleNodeConstructor inst, Expression separator) throws XPathException {
+ if (separator == null) {
+ separator = new StringLiteral(StringValue.SINGLE_SPACE);
+ }
+ try {
+ if (select == null) {
+ select = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ }
+ select = makeSimpleContentConstructor(select, separator, exec.getConfiguration());
+ inst.setSelect(select, exec.getConfiguration());
+
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ }
+
+ /**
+ * Construct an expression that implements the rules of "constructing simple content":
+ * given an expression to select the base sequence, and an expression to compute the separator,
+ * build an (unoptimized) expression to produce the value of the node as a string.
+ * @param select the expression that selects the base sequence
+ * @param separator the expression that computes the separator
+ * @param config the Saxon configuration
+ * @return an expression that returns a string containing the string value of the constructed node
+ */
+
+ public static Expression makeSimpleContentConstructor(Expression select, Expression separator, Configuration config) {
+ // Merge adjacent text nodes
+ select = AdjacentTextNodeMerger.makeAdjacentTextNodeMerger(select);
+ // Atomize the result
+ select = Atomizer.makeAtomizer(select);
+ // Convert each atomic value to a string
+ select = new AtomicSequenceConverter(select, BuiltInAtomicType.STRING);
+ ((AtomicSequenceConverter)select).allocateConverter(config, false);
+ // Join the resulting strings with a separator
+ if (select.getCardinality() != StaticProperty.EXACTLY_ONE) {
+ select = SystemFunctionCall.makeSystemFunction("string-join", new Expression[]{select, separator});
+ }
+ // All that's left for the instruction to do is to construct the right kind of node
+ return select;
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLLocalParam.java b/sf/saxon/style/XSLLocalParam.java
new file mode 100644
index 0000000..83721e8
--- /dev/null
+++ b/sf/saxon/style/XSLLocalParam.java
@@ -0,0 +1,215 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.SuppliedParameterReference;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.LocalParam;
+import net.sf.saxon.expr.instruct.LocalParamSetter;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* An xsl:param element representing a local parameter (template or function parameter) in the stylesheet. <br>
+* The xsl:param element has mandatory attribute name and optional attributes
+ * select, required, as, ...
+*/
+
+public class XSLLocalParam extends XSLGeneralVariable {
+
+ private int permittedAttributes =
+ SourceBinding.TUNNEL |
+ SourceBinding.REQUIRED |
+ SourceBinding.SELECT |
+ SourceBinding.AS;
+
+ /*@Nullable*/ Expression conversion = null;
+ private int slotNumber = -9876; // initial value designed solely to show up when debugging
+
+ /**
+ * Ask whether this element contains a binding for a variable with a given name; and if it does,
+ * return the source binding information
+ * @param name the variable name
+ * @return the binding information if this element binds a variable of this name; otherwise null
+ */
+
+ public SourceBinding getBindingInformation(StructuredQName name) {
+ if (name.equals(sourceBinding.getVariableQName())) {
+ return sourceBinding;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the slot number allocated to this variable (its position in the stackframe)
+ * @return the allocated slot number
+ */
+
+ public int getSlotNumber() {
+ return slotNumber;
+ }
+
+ @Override
+ public void prepareAttributes() throws XPathException {
+ sourceBinding.setProperty(SourceBinding.PARAM, true);
+ if (getParent() instanceof XSLFunction) {
+ permittedAttributes &= ~SourceBinding.SELECT;
+ sourceBinding.setProperty(SourceBinding.DISALLOWS_CONTENT, true);
+ }
+ sourceBinding.prepareAttributes(permittedAttributes);
+ if (sourceBinding.hasProperty(SourceBinding.TUNNEL) && !(getParent() instanceof XSLTemplate)) {
+ compileError("For attribute 'tunnel' within an " + getParent().getDisplayName() +
+ " parameter, the only permitted value is 'no'", "XTSE0020");
+ }
+
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+
+ StructuredQName name = sourceBinding.getVariableQName();
+
+ NodeInfo parent = getParent();
+
+ if (!((parent instanceof StyleElement) && ((StyleElement)parent).mayContainParam(null))) {
+ compileError("xsl:param must be immediately within a template, function or stylesheet", "XTSE0010");
+ }
+
+ if (hasChildNodes() && getParent() instanceof XSLFunction) {
+ compileError("Function parameters cannot have a default value", "XTSE0760");
+ }
+
+ AxisIterator preceding = iterateAxis(AxisInfo.PRECEDING_SIBLING);
+ while (true) {
+ NodeInfo node = preceding.next();
+ if (node == null) {
+ break;
+ }
+ if (node instanceof XSLLocalParam) {
+ if (name.equals(((XSLLocalParam) node).sourceBinding.getVariableQName())) {
+ compileError("The name of the parameter is not unique", "XTSE0580");
+ }
+ } else if (node instanceof StyleElement) {
+ compileError("xsl:param must not be preceded by other instructions", "XTSE0010");
+ } else {
+ // it must be a text node; allow it if all whitespace
+ if (!Whitespace.isWhite(node.getStringValueCS())) {
+ compileError("xsl:param must not be preceded by text", "XTSE0010");
+ }
+ }
+ }
+
+ SlotManager p = getContainingSlotManager();
+ if (p==null) {
+ compileError("Local variable must be declared within a template or function", "XTSE0010");
+ } else {
+ slotNumber = p.allocateSlotNumber(name);
+ }
+
+ if (sourceBinding.hasProperty(SourceBinding.REQUIRED)) {
+ if (sourceBinding.getSelectExpression() != null) {
+ // NB, we do this test before setting the default select attribute
+ compileError("The select attribute must be omitted when required='yes'", "XTSE0010");
+ }
+ if (hasChildNodes()) {
+ compileError("A parameter specifying required='yes' must have empty content", "XTSE0010");
+ }
+ }
+
+ super.validate(decl);
+ }
+
+ public boolean isTunnelParam() {
+ return sourceBinding.hasProperty(SourceBinding.TUNNEL);
+ }
+
+ public boolean isRequiredParam() {
+ return sourceBinding.hasProperty(SourceBinding.REQUIRED);
+ }
+
+ @Override
+ public void fixupReferences() throws XPathException {
+ sourceBinding.fixupReferences();
+ super.fixupReferences();
+ }
+
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ if (!"iterate".equals(getParent().getLocalPart()) &&
+ sourceBinding.getReferences().size() == 0 &&
+ !sourceBinding.hasProperty(SourceBinding.REQUIRED)) {
+ return null;
+ }
+ if (getParent() instanceof XSLFunction) {
+ // Do nothing. We did everything necessary while compiling the XSLFunction element.
+ return null;
+ } else {
+ SequenceType declaredType = getRequiredType();
+ StructuredQName name = sourceBinding.getVariableQName();
+ int slot = getSlotNumber();
+ if (declaredType != null) {
+ SuppliedParameterReference pref = new SuppliedParameterReference(slot);
+ pref.setLocationId(staticContext.getLocationMap().allocateLocationId(getSystemId(), getLineNumber()));
+ RoleLocator role = new RoleLocator(RoleLocator.PARAM, name.getDisplayName(), 0);
+ //role.setSourceLocator(new ExpressionLocation(this));
+ role.setErrorCode("XTTE0590");
+ conversion = TypeChecker.staticTypeCheck(
+ pref,
+ declaredType,
+ false,
+ role, makeExpressionVisitor());
+ }
+
+ PrincipalStylesheetModule psm = getPrincipalStylesheetModule();
+
+ sourceBinding.handleSequenceConstructor(exec, decl);
+
+ LocalParam binding = new LocalParam();
+ binding.setSelectExpression(sourceBinding.getSelectExpression());
+ binding.setConversion(conversion);
+ binding.setParameterId(psm.allocateUniqueParameterNumber(name));
+ binding.setVariableQName(name);
+ binding.setSlotNumber(slot);
+ binding.setRequiredType(getRequiredType());
+ binding.setRequiredParam(sourceBinding.hasProperty(SourceBinding.REQUIRED));
+ binding.setImplicitlyRequiredParam(sourceBinding.hasProperty(SourceBinding.IMPLICITLY_REQUIRED));
+ binding.setTunnel(sourceBinding.hasProperty(SourceBinding.TUNNEL));
+ sourceBinding.fixupBinding(binding);
+ compiledVariable = binding;
+ return new LocalParamSetter(binding);
+
+ }
+ }
+
+
+ /**
+ * Get the static type of the parameter. This is the declared type, because we cannot know
+ * the actual value in advance.
+ */
+
+ public SequenceType getRequiredType() {
+ SequenceType declaredType = sourceBinding.getDeclaredType();
+ if (declaredType!=null) {
+ return declaredType;
+ } else {
+ return SequenceType.ANY_SEQUENCE;
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLLocalVariable.java b/sf/saxon/style/XSLLocalVariable.java
new file mode 100644
index 0000000..9857710
--- /dev/null
+++ b/sf/saxon/style/XSLLocalVariable.java
@@ -0,0 +1,88 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceType;
+
+/**
+* Handler for xsl:variable elements acting as local variable declarations in a stylesheet. <br>
+* The xsl:variable element has mandatory attribute name and optional attribute select
+*/
+
+public class XSLLocalVariable extends XSLGeneralVariable {
+
+ private static int permittedAttributes =
+ SourceBinding.SELECT |
+ SourceBinding.AS;
+
+ /**
+ * Ask whether this element contains a binding for a variable with a given name; and if it does,
+ * return the source binding information
+ * @param name the variable name
+ * @return the binding information if this element binds a variable of this name; otherwise null
+ */
+
+ public SourceBinding getBindingInformation(StructuredQName name) {
+ if (name.equals(sourceBinding.getVariableQName())) {
+ return sourceBinding;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction (well, it can be, anyway)
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ @Override
+ public void prepareAttributes() throws XPathException {
+ sourceBinding.prepareAttributes(permittedAttributes);
+ }
+
+ /**
+ * Get the static type of the variable. This is the declared type, unless the value
+ * is statically known and constant, in which case it is the actual type of the value.
+ */
+
+ public SequenceType getRequiredType() {
+ return sourceBinding.getInferredType(true);
+ }
+
+ @Override
+ public void fixupReferences() throws XPathException {
+ sourceBinding.fixupReferences();
+ super.fixupReferences();
+ }
+
+ /**
+ * Process this local variable declaration by expanding any sequence constructor and setting
+ * the select expression to the result
+ * @param exec the executable
+ * @param decl the declaration being compiled
+ * @throws XPathException if an error occurs
+ */
+
+ public void compileLocalVariable(Executable exec, Declaration decl) throws XPathException {
+
+ //if (!sourceBinding.getReferences().isEmpty()) {
+ sourceBinding.handleSequenceConstructor(exec, decl);
+ //}
+
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLMatchingSubstring.java b/sf/saxon/style/XSLMatchingSubstring.java
new file mode 100644
index 0000000..28ecd14
--- /dev/null
+++ b/sf/saxon/style/XSLMatchingSubstring.java
@@ -0,0 +1,61 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+
+/**
+* Handler for xsl:matching-substring and xsl:non-matching-substring elements in stylesheet.
+* New at XSLT 2.0<BR>
+*/
+
+public class XSLMatchingSubstring extends StyleElement {
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return getCommonChildItemType();
+ }
+
+
+ public void prepareAttributes() throws XPathException {
+ AttributeCollection atts = getAttributeList();
+ for (int a=0; a<atts.getLength(); a++) {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (!(getParent() instanceof XSLAnalyzeString)) {
+ compileError(getDisplayName() + " must be immediately within xsl:analyze-string", "XTSE0010");
+ }
+ }
+
+ /*@NotNull*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ throw new UnsupportedOperationException("XSLMatchingSubstring#compile() should not be called");
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLMessage.java b/sf/saxon/style/XSLMessage.java
new file mode 100644
index 0000000..aa1f336
--- /dev/null
+++ b/sf/saxon/style/XSLMessage.java
@@ -0,0 +1,138 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.Block;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.Message;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NamespaceException;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Whitespace;
+
+
+/**
+* An xsl:message element in the stylesheet. <br>
+*/
+
+public final class XSLMessage extends StyleElement {
+
+ private Expression terminate = null;
+ private Expression select = null;
+ private Expression errorCode = null;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ String terminateAtt = null;
+ String selectAtt = null;
+ String errorCodeAtt = null;
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals("terminate")) {
+ terminateAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals("select")) {
+ selectAtt = atts.getValue(a);
+ } else if (f.equals("error-code")) {
+ errorCodeAtt = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+ if (selectAtt!=null) {
+ select = makeExpression(selectAtt);
+ }
+
+ if (errorCodeAtt != null) {
+ errorCode = makeAttributeValueTemplate(errorCodeAtt);
+ }
+
+ if (terminateAtt==null) {
+ terminateAtt = "no";
+ }
+
+ checkAttributeValue("terminate", terminateAtt, true, StyleElement.YES_NO);
+ terminate = makeAttributeValueTemplate(terminateAtt);
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ select = typeCheck("select", select);
+ terminate = typeCheck("terminate", terminate);
+ if (errorCode != null && !isXslt30Processor()) {
+ // we have a 2.0 processor
+ if (forwardsCompatibleModeIsEnabled()) {
+ compileWarning("xsl:message/@error-code is ignored in forwards-compatibility mode", "");
+ } else {
+ compileError("The xsl:message/@error-code attribute is not recognized by an XSLT 2.0 processor");
+ }
+ }
+ if (errorCode == null) {
+ errorCode = new StringLiteral("Q{http://www.w3.org/2005/xqt-errors}XTMM9000");
+ } else {
+ errorCode = typeCheck("error-code", errorCode);
+ }
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ Expression b = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (b != null) {
+ if (select == null) {
+ select = b;
+ } else {
+ select = Block.makeBlock(select, b);
+ select.setLocationId(
+ allocateLocationId(getSystemId(), getLineNumber()));
+ }
+ }
+ if (select == null) {
+ select = new StringLiteral("xsl:message (no content)");
+ }
+ if (errorCode instanceof StringLiteral) {
+ // resolve any QName prefix now
+ String code = ((StringLiteral)errorCode).getStringValue();
+ if (code.indexOf(":")>=0 && !code.startsWith("Q{")) {
+ try {
+ StructuredQName name = makeQName(code);
+ errorCode = new StringLiteral("Q" + name.getClarkName());
+ } catch (NamespaceException err) {
+ errorCode = new StringLiteral("Q{http://www.w3.org/2005/xqt-errors}XTMM9000");
+ }
+ }
+ }
+ Message inst = new Message(select, terminate, errorCode);
+ if (!(errorCode instanceof StringLiteral)) {
+ // evaluation of the error code may need the namespace context
+ inst.setNamespaceResolver(makeNamespaceContext());
+ }
+ return inst;
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/style/XSLNamespace.java b/sf/saxon/style/XSLNamespace.java
new file mode 100644
index 0000000..efae6b2
--- /dev/null
+++ b/sf/saxon/style/XSLNamespace.java
@@ -0,0 +1,90 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.NamespaceConstructor;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.StringValue;
+
+/**
+* An xsl:namespace element in the stylesheet. (XSLT 2.0)
+*/
+
+public class XSLNamespace extends XSLLeafNodeConstructor {
+
+ /*@Nullable*/ Expression name;
+
+ public void prepareAttributes() throws XPathException {
+ name = prepareAttributesNameAndSelect();
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ name = typeCheck("name", name);
+ select = typeCheck("select", select);
+ int countChildren = 0;
+ NodeInfo firstChild = null;
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLFallback) {
+ continue;
+ }
+ if (select != null) {
+ String errorCode = getErrorCodeForSelectPlusContent();
+ compileError("An " + getDisplayName() + " element with a select attribute must be empty", errorCode);
+ }
+ countChildren++;
+ if (firstChild == null) {
+ firstChild = child;
+ } else {
+ break;
+ }
+ }
+
+ if (select == null) {
+ if (countChildren == 0) {
+ // there are no child nodes and no select attribute
+ select = new StringLiteral(StringValue.EMPTY_STRING);
+ } else if (countChildren == 1) {
+ // there is exactly one child node
+ if (firstChild.getNodeKind() == Type.TEXT) {
+ // it is a text node: optimize for this case
+ select = new StringLiteral(firstChild.getStringValueCS());
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the error code to be returned when the element has a select attribute but is not empty.
+ *
+ * @return the error code defined for this condition, for this particular instruction
+ */
+
+ protected String getErrorCodeForSelectPlusContent() {
+ return "XTSE0910";
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ NamespaceConstructor inst = new NamespaceConstructor(name);
+ compileContent(exec, decl, inst, new StringLiteral(StringValue.SINGLE_SPACE));
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLNamespaceAlias.java b/sf/saxon/style/XSLNamespaceAlias.java
new file mode 100644
index 0000000..e073bce
--- /dev/null
+++ b/sf/saxon/style/XSLNamespaceAlias.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Whitespace;
+
+
+/**
+* An xsl:namespace-alias element in the stylesheet. <br>
+*/
+
+public class XSLNamespaceAlias extends StyleElement {
+
+ private String stylesheetURI;
+ private NamespaceBinding resultNamespaceBinding;
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ String stylesheetPrefix=null;
+ String resultPrefix=null;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.STYLESHEET_PREFIX)) {
+ stylesheetPrefix = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.RESULT_PREFIX)) {
+ resultPrefix = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+ if (stylesheetPrefix==null) {
+ reportAbsence("stylesheet-prefix");
+ return;
+ }
+ if (stylesheetPrefix.equals("#default")) {
+ stylesheetPrefix="";
+ }
+ if (resultPrefix==null) {
+ reportAbsence("result-prefix");
+ return;
+ }
+ if (resultPrefix.equals("#default")) {
+ resultPrefix="";
+ }
+ stylesheetURI = getURIForPrefix(stylesheetPrefix, true);
+ if (stylesheetURI == null) {
+ compileError("stylesheet-prefix " + stylesheetPrefix + " has not been declared", "XTSE0812");
+ // recovery action
+ stylesheetURI = "";
+ resultNamespaceBinding = NamespaceBinding.DEFAULT_UNDECLARATION;
+ return;
+ }
+ String resultURI = getURIForPrefix(resultPrefix, true);
+ if (resultURI == null) {
+ compileError("result-prefix " + resultPrefix + " has not been declared", "XTSE0812");
+ // recovery action
+ stylesheetURI = "";
+ resultURI = "";
+ }
+ resultNamespaceBinding = new NamespaceBinding(resultPrefix, resultURI);
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkTopLevel("XTSE0010");
+ }
+
+ /*@Nullable*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ return null;
+ }
+
+ protected void index(Declaration decl, PrincipalStylesheetModule top) throws XPathException {
+ top.addNamespaceAlias(decl);
+ }
+
+ public String getStylesheetURI() {
+ return stylesheetURI;
+ }
+
+ public NamespaceBinding getResultNamespaceBinding() {
+ return resultNamespaceBinding;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLNextMatch.java b/sf/saxon/style/XSLNextMatch.java
new file mode 100644
index 0000000..b4ca03c
--- /dev/null
+++ b/sf/saxon/style/XSLNextMatch.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.NextMatch;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* An xsl:next-match element in the stylesheet
+*/
+
+public class XSLNextMatch extends StyleElement {
+
+ private boolean useTailRecursion = false;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain an xsl:fallback
+ * instruction
+ */
+
+ public boolean mayContainFallback() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLWithParam || child instanceof XSLFallback) {
+ // OK;
+ } else if (child.getNodeKind() == Type.TEXT) {
+ // with xml:space=preserve, white space nodes may still be there
+ if (!Whitespace.isWhite(child.getStringValueCS())) {
+ compileError("No character data is allowed within xsl:next-match", "XTSE0010");
+ }
+ } else {
+ compileError("Child element " + child.getDisplayName() +
+ " is not allowed as a child of xsl:next-match", "XTSE0010");
+ }
+ }
+
+ }
+
+
+ /**
+ * Mark tail-recursive calls on templates and functions.
+ * For most instructions, this does nothing.
+ */
+
+ protected boolean markTailCalls() {
+ useTailRecursion = true;
+ return true;
+ }
+
+ /*@NotNull*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ NextMatch inst = new NextMatch(useTailRecursion);
+ inst.setActualParameters(getWithParamInstructions(exec, decl, false, inst),
+ getWithParamInstructions(exec, decl, true, inst));
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLNumber.java b/sf/saxon/style/XSLNumber.java
new file mode 100644
index 0000000..54f7149
--- /dev/null
+++ b/sf/saxon/style/XSLNumber.java
@@ -0,0 +1,287 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.NumberInstruction;
+import net.sf.saxon.expr.instruct.ValueOf;
+import net.sf.saxon.expr.number.NumberFormatter;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.lib.Numberer;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.StringConverter;
+import net.sf.saxon.type.ValidationFailure;
+import net.sf.saxon.value.*;
+
+/**
+* An xsl:number element in the stylesheet. <br>
+*/
+
+public class XSLNumber extends StyleElement {
+
+ private static final int SINGLE = 0;
+ private static final int MULTI = 1;
+ private static final int ANY = 2;
+ private static final int SIMPLE = 3;
+
+ private int level;
+ /*@Nullable*/ private Pattern count = null;
+ private Pattern from = null;
+ private Expression select = null;
+ private Expression value = null;
+ private Expression format = null;
+ private Expression groupSize = null;
+ private Expression groupSeparator = null;
+ private Expression letterValue = null;
+ private Expression lang = null;
+ private Expression ordinal = null;
+ private Expression startAt = null;
+ private NumberFormatter formatter = null;
+ private Numberer numberer = null;
+ private boolean hasVariablesInPatterns = false;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return NodeKindTest.TEXT;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ String selectAtt = null;
+ String valueAtt = null;
+ String countAtt = null;
+ String fromAtt = null;
+ String levelAtt = null;
+ String formatAtt = null;
+ String gsizeAtt = null;
+ String gsepAtt = null;
+ String langAtt = null;
+ String letterValueAtt = null;
+ String ordinalAtt = null;
+ String startAtAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.VALUE)) {
+ valueAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.COUNT)) {
+ countAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.FROM)) {
+ fromAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.LEVEL)) {
+ levelAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.FORMAT)) {
+ formatAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.LANG)) {
+ langAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.LETTER_VALUE)) {
+ letterValueAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.GROUPING_SIZE)) {
+ gsizeAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.GROUPING_SEPARATOR)) {
+ gsepAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.ORDINAL)) {
+ ordinalAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.START_AT)) {
+ startAtAtt = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt != null) {
+ select = makeExpression(selectAtt);
+ }
+
+ if (valueAtt!=null) {
+ value = makeExpression(valueAtt);
+ if (selectAtt != null) {
+ compileError("The select attribute and value attribute must not both be present", "XTSE0975");
+ }
+ if (countAtt != null) {
+ compileError("The count attribute and value attribute must not both be present", "XTSE0975");
+ }
+ if (fromAtt != null) {
+ compileError("The from attribute and value attribute must not both be present", "XTSE0975");
+ }
+ if (levelAtt != null) {
+ compileError("The level attribute and value attribute must not both be present", "XTSE0975");
+ }
+ }
+
+ if (countAtt!=null) {
+ count = makePattern(countAtt);
+ // the following test is a very crude way of testing if the pattern might
+ // contain variables, but it's good enough...
+ if (countAtt.indexOf('$')>=0) {
+ hasVariablesInPatterns = true;
+ }
+ }
+
+ if (fromAtt!=null) {
+ from = makePattern(fromAtt);
+ if (fromAtt.indexOf('$')>=0) {
+ hasVariablesInPatterns = true;
+ }
+ }
+
+ if (levelAtt==null) {
+ level = SINGLE;
+ } else if (levelAtt.equals("single")) {
+ level = SINGLE;
+ } else if (levelAtt.equals("multiple")) {
+ level = MULTI;
+ } else if (levelAtt.equals("any")) {
+ level = ANY;
+ } else {
+ compileError("Invalid value for level attribute", "XTSE0020");
+ }
+
+ if (level==SINGLE && from==null && count==null) {
+ level=SIMPLE;
+ }
+
+ if (formatAtt != null) {
+ format = makeAttributeValueTemplate(formatAtt);
+ if (format instanceof StringLiteral) {
+ formatter = new NumberFormatter();
+ formatter.prepare(((StringLiteral)format).getStringValue());
+ }
+ // else we'll need to allocate the formatter at run-time
+ } else {
+ formatter = new NumberFormatter();
+ formatter.prepare("1");
+ }
+
+ if (gsepAtt!=null && gsizeAtt!=null) {
+ // the spec says that if only one is specified, it is ignored
+ groupSize = makeAttributeValueTemplate(gsizeAtt);
+ groupSeparator = makeAttributeValueTemplate(gsepAtt);
+ }
+
+ if (langAtt==null) {
+ numberer = getConfiguration().makeNumberer(null, null);
+ } else {
+ lang = makeAttributeValueTemplate(langAtt);
+ if (lang instanceof StringLiteral) {
+ String language = ((StringLiteral)lang).getStringValue();
+ if (language.length() != 0) {
+ ValidationFailure vf = StringConverter.STRING_TO_LANGUAGE.validate(language);
+ if (vf != null) {
+ compileError("The lang attribute must be a valid language code", "XTDE0030");
+ lang = new StringLiteral(StringValue.EMPTY_STRING);
+ }
+ }
+ numberer = getConfiguration().makeNumberer(language, null);
+ } // else we allocate a numberer at run-time
+ }
+
+ if (letterValueAtt != null) {
+ letterValue = makeAttributeValueTemplate(letterValueAtt);
+ }
+
+ if (ordinalAtt != null) {
+ ordinal = makeAttributeValueTemplate(ordinalAtt);
+ }
+
+ if (startAtAtt != null) {
+ if (!getProcessorVersion().equals(DecimalValue.THREE)) {
+ compileWarning("xsl:number/@start-at is ignored as XSLT 3.0 is not enabled", "XTSE0010");
+ } else {
+ startAt = makeAttributeValueTemplate(startAtAtt);
+ }
+ } else {
+ startAt = Literal.makeLiteral(Int64Value.PLUS_ONE);
+ }
+
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkEmpty();
+
+ select = typeCheck("select", select);
+ value = typeCheck("value", value);
+ format = typeCheck("format", format);
+ groupSize = typeCheck("group-size", groupSize);
+ groupSeparator = typeCheck("group-separator", groupSeparator);
+ letterValue = typeCheck("letter-value", letterValue);
+ ordinal = typeCheck("ordinal", ordinal);
+ lang = typeCheck("lang", lang);
+ from = typeCheck("from", from);
+ count = typeCheck("count", count);
+ startAt = typeCheck("start-at", startAt);
+
+ if (select != null) {
+ try {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, "xsl:number/select", 0);
+ //role.setSourceLocator(new ExpressionLocation(this));
+ role.setErrorCode("XTTE1000");
+ select = TypeChecker.staticTypeCheck(select,
+ SequenceType.SINGLE_NODE,
+ false, role, makeExpressionVisitor());
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ }
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ NumberInstruction expr = new NumberInstruction (exec.getConfiguration(),
+ select,
+ level,
+ count,
+ from,
+ value,
+ format,
+ groupSize,
+ groupSeparator,
+ letterValue,
+ ordinal,
+ startAt,
+ lang,
+ formatter,
+ numberer,
+ hasVariablesInPatterns,
+ xPath10ModeIsEnabled());
+ int loc = getStaticContext().getLocationMap().allocateLocationId(getSystemId(), getLineNumber());
+ expr.setLocationId(loc);
+ ValueOf inst = new ValueOf(expr, false, false);
+ inst.setLocationId(allocateLocationId(getSystemId(), getLineNumber()));
+ inst.setIsNumberingInstruction();
+ return inst;
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/style/XSLOtherwise.java b/sf/saxon/style/XSLOtherwise.java
new file mode 100644
index 0000000..8ea5311
--- /dev/null
+++ b/sf/saxon/style/XSLOtherwise.java
@@ -0,0 +1,70 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+
+
+
+/**
+* Handler for xsl:otherwise elements in stylesheet. <br>
+*/
+
+public class XSLOtherwise extends StyleElement {
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return getCommonChildItemType();
+ }
+
+ public void prepareAttributes() throws XPathException {
+ AttributeCollection atts = getAttributeList();
+ for (int a=0; a<atts.getLength(); a++) {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (!(getParent() instanceof XSLChoose)) {
+ compileError("xsl:otherwise must be immediately within xsl:choose", "XTSE0010");
+ }
+ }
+
+ /**
+ * Mark tail-recursive calls on stylesheet functions. For most instructions, this does nothing.
+ */
+
+ public boolean markTailCalls() {
+ StyleElement last = getLastChildInstruction();
+ return last != null && last.markTailCalls();
+ }
+
+ /*@NotNull*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ throw new UnsupportedOperationException("XSLOtherwise#compile() should not be called");
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLOutput.java b/sf/saxon/style/XSLOutput.java
new file mode 100644
index 0000000..16b4501
--- /dev/null
+++ b/sf/saxon/style/XSLOutput.java
@@ -0,0 +1,428 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.OutputKeys;
+import java.util.*;
+
+/**
+* An xsl:output element in the stylesheet.
+*/
+
+public class XSLOutput extends StyleElement {
+
+ private StructuredQName outputFormatName;
+ /*@Nullable*/ private String method = null;
+ private String version = null;
+ private String htmlVersion = null;
+ private String indent = null;
+ private String encoding = null;
+ private String mediaType = null;
+ private String doctypeSystem = null;
+ private String doctypePublic = null;
+ private String omitDeclaration = null;
+ private String standalone = null;
+ private String cdataElements = null;
+ private String includeContentType = null;
+ private String nextInChain = null;
+ private String suppressIndentation = null;
+ private String doubleSpace = null;
+ private String representation = null;
+ private String indentSpaces = null;
+ private String lineLength = null;
+ private String byteOrderMark = null;
+ private String escapeURIAttributes = null;
+ private String normalizationForm = null;
+ private String recognizeBinary = null;
+ private String requireWellFormed = null;
+ private String undeclareNamespaces = null;
+ private String useCharacterMaps = null;
+ private String attributeOrder = null;
+ private HashMap<String, String> userAttributes = null;
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+ AttributeCollection atts = getAttributeList();
+ String nameAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getNodeName(a).getStructuredQName().getClarkName();
+ if (f.equals(StandardNames.NAME)) {
+ nameAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.METHOD)) {
+ method = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.VERSION)) {
+ version = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.HTML_VERSION)) {
+ htmlVersion = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.BYTE_ORDER_MARK)) {
+ byteOrderMark = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.ENCODING)) {
+ encoding = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.OMIT_XML_DECLARATION)) {
+ omitDeclaration = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.STANDALONE)) {
+ standalone = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.DOCTYPE_PUBLIC)) {
+ doctypePublic = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.DOCTYPE_SYSTEM)) {
+ doctypeSystem = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.CDATA_SECTION_ELEMENTS)) {
+ cdataElements = atts.getValue(a);
+ } else if (f.equals(StandardNames.INDENT)) {
+ indent = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.MEDIA_TYPE)) {
+ mediaType = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.INCLUDE_CONTENT_TYPE)) {
+ includeContentType = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.NORMALIZATION_FORM)) {
+ normalizationForm = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.ESCAPE_URI_ATTRIBUTES)) {
+ escapeURIAttributes = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.USE_CHARACTER_MAPS)) {
+ useCharacterMaps = atts.getValue(a);
+ } else if (f.equals(StandardNames.UNDECLARE_PREFIXES)) {
+ undeclareNamespaces = atts.getValue(a);
+ } else if (f.equals(StandardNames.SAXON_CHARACTER_REPRESENTATION)) {
+ representation = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SAXON_INDENT_SPACES)) {
+ indentSpaces = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SAXON_LINE_LENGTH)) {
+ lineLength = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SAXON_SUPPRESS_INDENTATION)) {
+ suppressIndentation = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SAXON_DOUBLE_SPACE)) {
+ doubleSpace = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SAXON_NEXT_IN_CHAIN)) {
+ nextInChain = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SAXON_RECOGNIZE_BINARY)) {
+ recognizeBinary = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SAXON_REQUIRE_WELL_FORMED)) {
+ requireWellFormed = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SAXON_ATTRIBUTE_ORDER)) {
+ attributeOrder = Whitespace.trim(atts.getValue(a));
+ } else {
+ String attributeURI = atts.getURI(a);
+ if ("".equals(attributeURI) ||
+ NamespaceConstant.XSLT.equals(attributeURI) ||
+ NamespaceConstant.SAXON.equals(attributeURI)) {
+ checkUnknownAttribute(atts.getNodeName(a));
+ } else {
+ String name = '{' + attributeURI + '}' + atts.getLocalName(a);
+ if (userAttributes==null) {
+ userAttributes = new HashMap<String, String>(5);
+ }
+ userAttributes.put(name, atts.getValue(a));
+ }
+ }
+ }
+ if (nameAtt!=null) {
+ try {
+ outputFormatName = makeQName(nameAtt);
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE1570");
+ } catch (XPathException err) {
+ compileError(err.getMessage(), "XTSE1570");
+ }
+ }
+ }
+
+ /**
+ * Get the name of the xsl:output declaration
+ * @return the name, as a structured QName; or null for an unnamed output declaration
+ */
+
+ public StructuredQName getFormatQName() {
+ return outputFormatName;
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkTopLevel("XTSE0010");
+ checkEmpty();
+ }
+
+ public void compileDeclaration(Executable exec, Declaration decl) {
+ // no action
+ }
+
+
+ /**
+ * Validate the properties,
+ * and return the values as additions to a supplied Properties object.
+ * @param details the Properties object to be populated with property values
+ * @param precedences a HashMap to be populated with information about the precedence
+ * of the property values: the key is the property name as a Clark name, the value
+ * is a boxed integer giving the property's import precedence
+ * @param thisPrecedence the precedence of thi instruction
+ * @throws net.sf.saxon.trans.XPathException if an error is found
+ */
+
+ protected void gatherOutputProperties(Properties details, HashMap<String, Integer> precedences, int thisPrecedence)
+ throws XPathException {
+
+ if (method != null) {
+ if ("xml".equals(method) || "html".equals(method) ||
+ "text".equals(method) || "xhtml".equals(method)) {
+ checkAndPut(OutputKeys.METHOD, method, details, precedences, thisPrecedence);
+ //details.put(OutputKeys.METHOD, method);
+ } else {
+ String[] parts;
+ try {
+ parts = getConfiguration().getNameChecker().getQNameParts(method);
+ String prefix = parts[0];
+ if (prefix.length() == 0) {
+ compileError("method must be xml, html, xhtml, or text, or a prefixed name", "XTSE1570");
+ } else {
+ String uri = getURIForPrefix(prefix, false);
+ if (uri == null) {
+ undeclaredNamespaceError(prefix, "XTSE0280");
+ }
+ checkAndPut(OutputKeys.METHOD, '{' + uri + '}' + parts[1], details, precedences, thisPrecedence);
+ //details.put(OutputKeys.METHOD, '{' + uri + '}' + parts[1] );
+ }
+ } catch (QNameException e) {
+ compileError("Invalid method name. " + e.getMessage(), "XTSE1570");
+ }
+ }
+ }
+
+ if (byteOrderMark != null) {
+ checkAndPut(SaxonOutputKeys.BYTE_ORDER_MARK, byteOrderMark, details, precedences, thisPrecedence);
+ }
+
+ if (version != null) {
+ checkAndPut(OutputKeys.VERSION, version, details, precedences, thisPrecedence);
+ }
+
+ if (htmlVersion != null) {
+ checkAndPut(SaxonOutputKeys.HTML_VERSION, htmlVersion, details, precedences, thisPrecedence);
+ }
+
+ if (indent != null) {
+ checkAndPut(OutputKeys.INDENT, indent, details, precedences, thisPrecedence);
+ }
+
+ if (indentSpaces != null) {
+ checkAndPut(SaxonOutputKeys.INDENT_SPACES, indentSpaces, details, precedences, thisPrecedence);
+ }
+
+ if (lineLength != null) {
+ checkAndPut(SaxonOutputKeys.LINE_LENGTH, lineLength, details, precedences, thisPrecedence);
+ }
+
+ if (suppressIndentation != null) {
+ String existing = details.getProperty(SaxonOutputKeys.SUPPRESS_INDENTATION);
+ if (existing==null) {
+ // support retained for backwards compatibility
+ existing = details.getProperty("{http://saxon.sf.net/}suppress-indentation");
+ }
+ if (existing==null) {
+ existing = "";
+ }
+ String s = SaxonOutputKeys.parseListOfNodeNames(
+ suppressIndentation, this, true, false, getConfiguration().getNameChecker(), "XTSE0280");
+ details.setProperty(SaxonOutputKeys.SUPPRESS_INDENTATION, existing+s);
+ }
+
+ if (doubleSpace != null) {
+ String existing = details.getProperty(SaxonOutputKeys.DOUBLE_SPACE);
+ if (existing==null) {
+ existing = "";
+ }
+ String s = SaxonOutputKeys.parseListOfNodeNames(
+ doubleSpace, this, true, false, getConfiguration().getNameChecker(), "XTSE0280");
+ details.setProperty(SaxonOutputKeys.DOUBLE_SPACE, existing+s);
+ }
+
+ if (encoding != null) {
+ checkAndPut(OutputKeys.ENCODING, encoding, details, precedences, thisPrecedence);
+ }
+
+ if (mediaType != null) {
+ checkAndPut(OutputKeys.MEDIA_TYPE, mediaType, details, precedences, thisPrecedence);
+ }
+
+ if (doctypeSystem != null) {
+ checkAndPut(OutputKeys.DOCTYPE_SYSTEM, doctypeSystem, details, precedences, thisPrecedence);
+ }
+
+ if (doctypePublic != null) {
+ checkAndPut(OutputKeys.DOCTYPE_PUBLIC, doctypePublic, details, precedences, thisPrecedence);
+ }
+
+ if (omitDeclaration != null) {
+ checkAndPut(OutputKeys.OMIT_XML_DECLARATION, omitDeclaration, details, precedences, thisPrecedence);
+ }
+
+ if (standalone != null) {
+ checkAndPut(OutputKeys.STANDALONE, standalone, details, precedences, thisPrecedence);
+ }
+
+ if (cdataElements != null) {
+ String existing = details.getProperty(OutputKeys.CDATA_SECTION_ELEMENTS);
+ if (existing==null) {
+ existing = "";
+ }
+ String s = SaxonOutputKeys.parseListOfNodeNames(
+ cdataElements, this, true, false, getConfiguration().getNameChecker(), "XTSE0280");
+ details.setProperty(OutputKeys.CDATA_SECTION_ELEMENTS, existing+s);
+ }
+
+ if (normalizationForm != null && !normalizationForm.equals("none")) {
+ checkAndPut(SaxonOutputKeys.NORMALIZATION_FORM, normalizationForm, details, precedences, thisPrecedence);
+ }
+
+ if (undeclareNamespaces != null) {
+ checkAndPut(SaxonOutputKeys.UNDECLARE_PREFIXES, undeclareNamespaces, details, precedences, thisPrecedence);
+ }
+
+ if (useCharacterMaps != null) {
+ String s = prepareCharacterMaps(this, useCharacterMaps, details);
+ details.setProperty(SaxonOutputKeys.USE_CHARACTER_MAPS, s);
+ }
+
+ if (representation != null) {
+ checkAndPut(SaxonOutputKeys.CHARACTER_REPRESENTATION, representation, details, precedences, thisPrecedence);
+ }
+
+ if (includeContentType != null) {
+ checkAndPut(SaxonOutputKeys.INCLUDE_CONTENT_TYPE, includeContentType, details, precedences, thisPrecedence);
+ }
+
+ if (escapeURIAttributes != null) {
+ checkAndPut(SaxonOutputKeys.ESCAPE_URI_ATTRIBUTES, escapeURIAttributes, details, precedences, thisPrecedence);
+ }
+
+ if (nextInChain != null) {
+ checkAndPut(SaxonOutputKeys.NEXT_IN_CHAIN, nextInChain, details, precedences, thisPrecedence);
+ checkAndPut(SaxonOutputKeys.NEXT_IN_CHAIN_BASE_URI, getSystemId(), details, precedences, thisPrecedence);
+ }
+
+ if (recognizeBinary != null) {
+ checkAndPut(SaxonOutputKeys.RECOGNIZE_BINARY, recognizeBinary, details, precedences, thisPrecedence);
+ }
+
+ if (requireWellFormed != null) {
+ checkAndPut(SaxonOutputKeys.REQUIRE_WELL_FORMED, requireWellFormed, details, precedences, thisPrecedence);
+ }
+
+ if (attributeOrder != null) {
+ String existing = details.getProperty(SaxonOutputKeys.ATTRIBUTE_ORDER);
+ if (existing==null) {
+ existing = "";
+ }
+ String s = SaxonOutputKeys.parseListOfNodeNames(
+ attributeOrder, this, false, false, getConfiguration().getNameChecker(), "XTSE0280");
+ details.setProperty(SaxonOutputKeys.ATTRIBUTE_ORDER, existing + " " + s);
+ }
+
+ // deal with user-defined attributes
+
+ if (userAttributes!=null) {
+ for (Map.Entry<String, String> e : userAttributes.entrySet()) {
+ details.setProperty(e.getKey(), e.getValue());
+ }
+ }
+
+ }
+
+ /**
+ * Add an output property to the list of properties after checking that it is consistent
+ * with other properties
+ * @param property the name of the property
+ * @param value the value of the ptoperty
+ * @param props the list of properties to be updated
+ * @param precedences the import precedence of each property
+ * @param thisPrecedence the import precedence of the declaration containing this value
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ private void checkAndPut(String property, String value, Properties props,
+ HashMap<String, Integer> precedences, int thisPrecedence)
+ throws XPathException {
+ try {
+ SaxonOutputKeys.checkOutputProperty(property, value, getConfiguration());
+ } catch (XPathException err) {
+ compileError(err.getMessage(), "XTSE0020");
+ return;
+ }
+ String old = props.getProperty(property);
+ if (old == null) {
+ props.setProperty(property, value);
+ precedences.put(property, thisPrecedence);
+ } else if (old.equals(value)) {
+ // do nothing
+ } else {
+ Integer oldPrec = precedences.get(property);
+ if (oldPrec == null) return; // shouldn't happen but ignore it
+ if (oldPrec > thisPrecedence) {
+ // ignore this value, the other has higher precedence
+ } else if (oldPrec == thisPrecedence) {
+ compileError("Conflicting values for output property " + property, "XTSE1560");
+ } else {
+ // this has higher precedence: can't happen
+ throw new IllegalStateException("Output properties must be processed in decreasing precedence order");
+ }
+ }
+ }
+
+ /**
+ * Process the use-character-maps attribute
+ * @param element the stylesheet element on which the use-character-maps attribute appears
+ * @param useCharacterMaps the value of the use-character-maps attribute
+ * @param details The existing output properties
+ * @return the augmented value of the use-character-maps attribute in Clark notation
+ * @throws XPathException if the value is invalid
+ */
+ public static String prepareCharacterMaps(StyleElement element,
+ String useCharacterMaps,
+ Properties details)
+ throws XPathException {
+ PrincipalStylesheetModule psm = element.getPrincipalStylesheetModule();
+ String existing = details.getProperty(SaxonOutputKeys.USE_CHARACTER_MAPS);
+ if (existing==null) {
+ existing = "";
+ }
+ String s = "";
+ StringTokenizer st = new StringTokenizer(useCharacterMaps, " \t\n\r", false);
+ while (st.hasMoreTokens()) {
+ String displayname = st.nextToken();
+ try {
+ StructuredQName qName = element.makeQName(displayname);
+ Declaration decl = psm.getCharacterMap(qName);
+ if (decl == null) {
+ element.compileError("No character-map named '" + displayname + "' has been defined", "XTSE1590");
+ }
+ s += " " + qName.getClarkName();
+ } catch (NamespaceException err) {
+ element.undeclaredNamespaceError(err.getPrefix(), "XTSE0280");
+ }
+ }
+ existing = s + existing;
+ return existing;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLOutputCharacter.java b/sf/saxon/style/XSLOutputCharacter.java
new file mode 100644
index 0000000..82829a2
--- /dev/null
+++ b/sf/saxon/style/XSLOutputCharacter.java
@@ -0,0 +1,93 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.trans.XPathException;
+
+
+/**
+* An xsl:output-character element in the stylesheet. <br>
+*/
+
+public class XSLOutputCharacter extends StyleElement {
+
+ private int codepoint = -1;
+ // the character to be substituted, as a Unicode codepoint (may be > 65535)
+ private String replacementString = null;
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.CHARACTER)) {
+ String s = atts.getValue(a);
+ switch (s.length()) {
+ case 0:
+ compileError("character attribute must not be zero-length", "XTSE0020");
+ codepoint = 256; // for error recovery
+ break;
+ case 1:
+ codepoint = s.charAt(0);
+ break;
+ case 2:
+ if (UTF16CharacterSet.isHighSurrogate(s.charAt(0)) &&
+ UTF16CharacterSet.isLowSurrogate(s.charAt(1))) {
+ codepoint = UTF16CharacterSet.combinePair(s.charAt(0), s.charAt(1));
+ } else {
+ compileError("character attribute must be a single XML character", "XTSE0020");
+ codepoint = 256; // for error recovery
+ }
+ break;
+ default:
+ compileError("character attribute must be a single XML character", "XTSE0020");
+ codepoint = 256; // for error recovery
+ }
+ } else if (f.equals(StandardNames.STRING)) {
+ replacementString = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+ if (codepoint==-1) {
+ reportAbsence("character");
+ return;
+ }
+
+ if (replacementString==null) {
+ reportAbsence("string");
+ }
+
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (!(getParent() instanceof XSLCharacterMap)) {
+ compileError("xsl:output-character may appear only as a child of xsl:character-map", "XTSE0010");
+ }
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ return null;
+ }
+
+ public int getCodePoint() {
+ return codepoint;
+ }
+
+ public String getReplacementString() {
+ return replacementString;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLPerformSort.java b/sf/saxon/style/XSLPerformSort.java
new file mode 100644
index 0000000..344987a
--- /dev/null
+++ b/sf/saxon/style/XSLPerformSort.java
@@ -0,0 +1,143 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.sort.SortExpression;
+import net.sf.saxon.expr.sort.SortKeyDefinition;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Whitespace;
+
+
+/**
+* Handler for xsl:perform-sort elements in stylesheet (XSLT 2.0). <br>
+*/
+
+public class XSLPerformSort extends StyleElement {
+
+ /*@Nullable*/ Expression select = null;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ if (select==null) {
+ return getCommonChildItemType();
+ } else {
+ final TypeHierarchy th = getConfiguration().getTypeHierarchy();
+ return select.getItemType(th);
+ }
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ /**
+ * Specify that xsl:sort is a permitted child
+ */
+
+ protected boolean isPermittedChild(StyleElement child) {
+ return (child instanceof XSLSort);
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ String selectAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt!=null) {
+ select = makeExpression(selectAtt);
+ }
+
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkSortComesFirst(true);
+
+ if (select != null) {
+ // if there is a select attribute, check that there are no children other than xsl:sort and xsl:fallback
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = (NodeInfo)kids.next();
+ if (child == null) {
+ break;
+ }
+ if (child instanceof XSLSort || child instanceof XSLFallback) {
+ // no action
+ } else if (child.getNodeKind() == Type.TEXT && !Whitespace.isWhite(child.getStringValueCS())) {
+ // with xml:space=preserve, white space nodes may still be there
+ compileError("Within xsl:perform-sort, significant text must not appear if there is a select attribute",
+ "XTSE1040");
+ } else {
+ ((StyleElement)child).compileError(
+ "Within xsl:perform-sort, child instructions are not allowed if there is a select attribute",
+ "XTSE1040");
+ }
+ }
+ }
+ select = typeCheck("select", select);
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ SortKeyDefinition[] sortKeys = makeSortKeys(decl);
+ if (select != null) {
+ return new SortExpression(select, sortKeys);
+ } else {
+ Expression body = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (body == null) {
+ body = Literal.makeEmptySequence();
+ }
+ try {
+ return new SortExpression(makeExpressionVisitor().simplify(body), sortKeys);
+ } catch (XPathException e) {
+ compileError(e);
+ return null;
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/sf/saxon/style/XSLPreserveSpace.java b/sf/saxon/style/XSLPreserveSpace.java
new file mode 100644
index 0000000..5f70aad
--- /dev/null
+++ b/sf/saxon/style/XSLPreserveSpace.java
@@ -0,0 +1,136 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.event.Stripper;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+
+import java.util.StringTokenizer;
+
+/**
+* An xsl:preserve-space or xsl:strip-space elements in stylesheet. <br>
+*/
+
+public class XSLPreserveSpace extends StyleElement {
+
+ private String elements;
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.ELEMENTS)) {
+ elements = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+ if (elements==null) {
+ reportAbsence("elements");
+ elements="*"; // for error recovery
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ checkEmpty();
+ checkTopLevel("XTSE0010");
+ }
+
+ public void compileDeclaration(Executable exec, /*@NotNull*/ Declaration decl) throws XPathException
+ {
+ Stripper.StripRuleTarget preserve =
+ (getFingerprint() == StandardNames.XSL_PRESERVE_SPACE ? Stripper.PRESERVE : Stripper.STRIP);
+ SpaceStrippingRule stripperRules = getPreparedStylesheet().getStripperRules();
+ if (!(stripperRules instanceof SelectedElementsSpaceStrippingRule)) {
+ stripperRules = new SelectedElementsSpaceStrippingRule(isXslt30Processor());
+ getPreparedStylesheet().setStripperRules(stripperRules);
+ }
+
+ SelectedElementsSpaceStrippingRule rules = (SelectedElementsSpaceStrippingRule)stripperRules;
+
+ // elements is a space-separated list of element names
+
+ StringTokenizer st = new StringTokenizer(elements, " \t\n\r", false);
+ try {
+ while (st.hasMoreTokens()) {
+ String s = st.nextToken();
+ NodeTest nt;
+ if (s.equals("*")) {
+ nt = NodeKindTest.ELEMENT;
+ rules.addRule(nt, preserve, decl.getModule(), decl.getSourceElement().getLineNumber());
+
+ } else if (s.endsWith(":*")) {
+ if (s.length()==2) {
+ compileError("No prefix before ':*'");
+ }
+ String prefix = s.substring(0, s.length()-2);
+ String uri = getURIForPrefix(prefix, false);
+ nt = new NamespaceTest(getNamePool(), Type.ELEMENT, uri);
+ rules.addRule(nt, preserve, decl.getModule(), decl.getSourceElement().getLineNumber());
+
+ } else if (s.startsWith("*:")) {
+ if (s.length()==2) {
+ compileError("No local name after '*:'");
+ }
+ String localname = s.substring(2);
+ nt = new LocalNameTest(getNamePool(), Type.ELEMENT, localname);
+ rules.addRule(nt, preserve, decl.getModule(), decl.getSourceElement().getLineNumber());
+
+ } else {
+ String prefix;
+ String localName;
+ String uri;
+ try {
+ String[] parts = getConfiguration().getNameChecker().getQNameParts(s);
+ prefix = parts[0];
+ if (parts[0].equals("")) {
+ uri = getDefaultXPathNamespace();
+ } else {
+ uri = getURIForPrefix(prefix, false);
+ if (uri == null) {
+ undeclaredNamespaceError(prefix, "XTSE0280");
+ }
+ }
+ localName = parts[1];
+ } catch (QNameException err) {
+ compileError("Element name " + s + " is not a valid QName", "XTSE0280");
+ return;
+ }
+ NamePool target = getNamePool();
+ int nameCode = target.allocate("", uri, localName);
+ nt = new NameTest(Type.ELEMENT, nameCode, getNamePool());
+ rules.addRule(nt, preserve, decl.getModule(), decl.getSourceElement().getLineNumber());
+ }
+
+ }
+ } catch (XPathException e) {
+ e.maybeSetLocation(this);
+ compileError(e);
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLProcessingInstruction.java b/sf/saxon/style/XSLProcessingInstruction.java
new file mode 100644
index 0000000..b416a65
--- /dev/null
+++ b/sf/saxon/style/XSLProcessingInstruction.java
@@ -0,0 +1,52 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.ProcessingInstruction;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+/**
+* An xsl:processing-instruction element in the stylesheet.
+*/
+
+public class XSLProcessingInstruction extends XSLLeafNodeConstructor {
+
+ Expression name;
+
+ public void prepareAttributes() throws XPathException {
+ name = prepareAttributesNameAndSelect();
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ name = typeCheck("name", name);
+ select = typeCheck("select", select);
+ super.validate(decl);
+ }
+
+ /**
+ * Get the error code to be returned when the element has a select attribute but is not empty.
+ *
+ * @return the error code defined for this condition, for this particular instruction
+ */
+
+ protected String getErrorCodeForSelectPlusContent() {
+ return "XTSE0880";
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ ProcessingInstruction inst = new ProcessingInstruction(name);
+ compileContent(exec, decl, inst, new StringLiteral(StringValue.SINGLE_SPACE));
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLResultDocument.java b/sf/saxon/style/XSLResultDocument.java
new file mode 100644
index 0000000..801face
--- /dev/null
+++ b/sf/saxon/style/XSLResultDocument.java
@@ -0,0 +1,345 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.ErrorExpression;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.StringLiteral;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.ResultDocument;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntHashSet;
+import net.sf.saxon.z.IntIterator;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.HashSet;
+import java.util.Properties;
+
+/**
+* An xsl:result-document element in the stylesheet. <BR>
+* The xsl:result-document element takes an attribute href="filename". The filename will
+* often contain parameters, e.g. {position()} to ensure that a different file is produced
+* for each element instance. <BR>
+* There is a further attribute "name" which determines the format of the
+* output file, it identifies the name of an xsl:output element containing the output
+* format details.
+*/
+
+public class XSLResultDocument extends StyleElement {
+
+ private static final HashSet<String> fans = new HashSet<String>(25); // formatting attribute names
+
+ static {
+ fans.add(StandardNames.METHOD);
+ fans.add(StandardNames.OUTPUT_VERSION);
+ fans.add(StandardNames.HTML_VERSION);
+ fans.add(StandardNames.BYTE_ORDER_MARK);
+ fans.add(StandardNames.INDENT);
+ fans.add(StandardNames.ENCODING);
+ fans.add(StandardNames.MEDIA_TYPE);
+ fans.add(StandardNames.DOCTYPE_SYSTEM);
+ fans.add(StandardNames.DOCTYPE_PUBLIC);
+ fans.add(StandardNames.OMIT_XML_DECLARATION);
+ fans.add(StandardNames.STANDALONE);
+ fans.add(StandardNames.CDATA_SECTION_ELEMENTS);
+ fans.add(StandardNames.INCLUDE_CONTENT_TYPE);
+ fans.add(StandardNames.ESCAPE_URI_ATTRIBUTES);
+ fans.add(StandardNames.UNDECLARE_PREFIXES);
+ fans.add(StandardNames.NORMALIZATION_FORM);
+ fans.add(StandardNames.SAXON_NEXT_IN_CHAIN);
+ fans.add(StandardNames.SAXON_CHARACTER_REPRESENTATION);
+ fans.add(StandardNames.SAXON_INDENT_SPACES);
+ fans.add(StandardNames.SAXON_REQUIRE_WELL_FORMED);
+ fans.add(StandardNames.SAXON_SUPPRESS_INDENTATION);
+ fans.add(StandardNames.SAXON_ATTRIBUTE_ORDER);
+ }
+
+ private Expression href;
+ private StructuredQName formatQName; // used when format is a literal string
+ private Expression formatExpression; // used when format is an AVT
+ private int validationAction = Validation.STRIP;
+ private SchemaType schemaType = null;
+ private IntHashMap<Expression> serializationAttributes = new IntHashMap<Expression>(10);
+ private boolean async = true;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction). Default implementation returns Type.ITEM, indicating
+ * that we don't know, it might be anything. Returns null in the case of an element
+ * such as xsl:sort or xsl:variable that can appear in a sequence constructor but
+ * contributes nothing to the result sequence.
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return null;
+ }
+
+ public void prepareAttributes() throws XPathException {
+ AttributeCollection atts = getAttributeList();
+
+ String formatAttribute = null;
+ String hrefAttribute = null;
+ String validationAtt = null;
+ String typeAtt = null;
+ String useCharacterMapsAtt = null;
+
+
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.FORMAT)) {
+ formatAttribute = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.HREF)) {
+ hrefAttribute = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.VALIDATION)) {
+ validationAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.TYPE)) {
+ typeAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.USE_CHARACTER_MAPS)) {
+ useCharacterMapsAtt = Whitespace.trim(atts.getValue(a));
+ } else if (fans.contains(f) || f.startsWith("{")) {
+ // this is a serialization attribute
+ String val = Whitespace.trim(atts.getValue(a));
+ Expression exp = makeAttributeValueTemplate(val);
+ serializationAttributes.put(atts.getNameCode(a) & NamePool.FP_MASK, exp);
+ } else if (atts.getLocalName(a).equals("asynchronous") && atts.getURI(a).equals(NamespaceConstant.SAXON)) {
+ String asyncAtt = Whitespace.trim(atts.getValue(a));
+ if (asyncAtt.equals("yes")) {
+ async = true;
+ } else if (asyncAtt.equals("no")) {
+ async = false;
+ } else {
+ compileError("The attribute 'saxon:asynchronous' must be set to 'yes' or 'no'", "XTSE0020");
+ }
+ if (getPreparedStylesheet().isCompileWithTracing()) {
+ async = false;
+ } else if (!"EE".equals(getConfiguration().getEditionCode())) {
+ compileWarning("saxon:asynchronous - ignored when not running Saxon-EE",
+ SaxonErrorCode.SXWN9013);
+ async = false;
+ }
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (hrefAttribute==null) {
+ //href = StringValue.EMPTY_STRING;
+ } else {
+ href = makeAttributeValueTemplate(hrefAttribute);
+ }
+
+ if (formatAttribute!=null) {
+ formatExpression = makeAttributeValueTemplate(formatAttribute);
+ if (formatExpression instanceof StringLiteral) {
+ try {
+ formatQName = makeQName(((StringLiteral)formatExpression).getStringValue());
+ formatExpression = null;
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE0280");
+ } catch (XPathException err) {
+ compileError(err.getMessage(), "XTDE1460");
+ }
+ }
+ }
+
+ if (validationAtt==null) {
+ validationAction = getContainingStylesheet().getDefaultValidation();
+ } else {
+ validationAction = Validation.getCode(validationAtt);
+ if (validationAction != Validation.STRIP && !getPreparedStylesheet().isSchemaAware()) {
+ validationAction = Validation.STRIP;
+ compileError("To perform validation, a schema-aware XSLT processor is needed", "XTSE1660");
+ }
+ if (validationAction == Validation.INVALID) {
+ compileError("Invalid value of @validation attribute", "XTSE0020");
+ }
+ }
+ if (typeAtt!=null) {
+ if (!getPreparedStylesheet().isSchemaAware()) {
+ compileError("The @type attribute is available only with a schema-aware XSLT processor", "XTSE1660");
+ }
+ schemaType = getSchemaType(typeAtt);
+ validationAction = Validation.BY_TYPE;
+ }
+
+ if (typeAtt != null && validationAtt != null) {
+ compileError("The @validation and @type attributes are mutually exclusive", "XTSE1505");
+ }
+
+ if (useCharacterMapsAtt != null) {
+ String s = XSLOutput.prepareCharacterMaps(this, useCharacterMapsAtt, new Properties());
+ serializationAttributes.put(
+ getNamePool().allocate("", "", StandardNames.USE_CHARACTER_MAPS),
+ new StringLiteral(s));
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (href != null && !getConfiguration().getBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS)) {
+ compileError("xsl:result-document is disabled when extension functions are disabled");
+ }
+ href = typeCheck("href", href);
+ formatExpression = typeCheck("format", formatExpression);
+
+ IntIterator it = serializationAttributes.keyIterator();
+ while (it.hasNext()) {
+ int fp = it.next();
+ String displayName = getNamePool().getDisplayName(fp);
+ final Expression exp1 = serializationAttributes.get(fp);
+ final Expression exp2 = typeCheck(displayName, exp1);
+ if (exp1 != exp2) {
+ serializationAttributes.put(fp, exp2);
+ }
+ }
+
+ getPreparedStylesheet().setCreatesSecondaryResult(true);
+
+ }
+
+ /*@Nullable*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+
+ // Check that the call is not within xsl:variable or xsl:function.
+ // This is a dynamic error, but worth detecting statically.
+ // In fact this is a bit of a fudge. If a function or variable is inlined, we sometimes don't detect
+ // XTDE1480 at run-time. Doing this static check improves our chances, though it won't catch all cases.
+ AxisIterator ai = iterateAxis(AxisInfo.ANCESTOR);
+ while (true) {
+ NodeInfo node = ai.next();
+ if (node == null) {
+ break;
+ }
+ if (node instanceof XSLGeneralVariable || node instanceof XSLFunction) {
+ issueWarning("An xsl:result-document instruction inside " + node.getDisplayName() +
+ " will always fail at run-time", this);
+ XPathException err = new XPathException("Call to xsl:result-document while in temporary output state", "XTDE1480");
+ err.setLocator(this);
+ return new ErrorExpression(err);
+ }
+ }
+
+ Properties globalProps;
+ if (formatExpression == null) {
+ try {
+ globalProps = getPrincipalStylesheetModule().gatherOutputProperties(formatQName);
+ } catch (XPathException err) {
+ compileError("Named output format has not been defined", "XTDE1460");
+ return null;
+ }
+ } else {
+ globalProps = new Properties();
+ getPrincipalStylesheetModule().setNeedsDynamicOutputProperties(true);
+ }
+
+ // If no serialization method was specified, we can work it out statically if the
+ // first contained instruction is a literal result element. This saves effort at run-time.
+
+ int key = getNamePool().allocate("", "", StandardNames.METHOD);
+ if (formatExpression == null &&
+ globalProps.getProperty("method") == null &&
+ serializationAttributes.get(key) == null) {
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ NodeInfo first = kids.next();
+ if (first instanceof LiteralResultElement) {
+ if (first.getFingerprint() == getNamePool().allocate("", NamespaceConstant.XHTML, "html")) {
+ globalProps.setProperty("method", "xhtml");
+ } else if (first.getLocalPart().equalsIgnoreCase("html") && first.getURI().length() == 0) {
+ globalProps.setProperty("method", "html");
+ } else {
+ globalProps.setProperty("method", "xml");
+ }
+ }
+ }
+
+ Properties localProps = new Properties();
+
+ IntHashSet fixed = new IntHashSet(10);
+ boolean needsNamespaceContext = (formatExpression != null);
+ NamespaceResolver namespaceResolver = getStaticContext().getNamespaceResolver();
+ for (IntIterator it=serializationAttributes.keyIterator(); it.hasNext();) {
+ int fp = it.next();
+ Expression exp = serializationAttributes.get(fp);
+ if (exp instanceof StringLiteral) {
+ String s = ((StringLiteral)exp).getStringValue();
+ String lname = getNamePool().getLocalName(fp);
+ String uri = getNamePool().getURI(fp);
+ try {
+
+ ResultDocument.setSerializationProperty(localProps, uri, lname, s,
+ namespaceResolver, false, exec.getConfiguration());
+ fixed.add(fp);
+ } catch (XPathException e) {
+ if (NamespaceConstant.SAXON.equals(e.getErrorCodeNamespace())) {
+ compileWarning(e.getMessage(), e.getErrorCodeQName());
+ } else {
+ compileError(e);
+ }
+ }
+ } else {
+ String lname = getNamePool().getLocalName(fp);
+ if (lname.equals("method") || lname.equals("cdata-section-elements") ||
+ lname.equals("suppress-indentation")) {
+ needsNamespaceContext = true;
+ }
+ }
+ }
+ for (IntIterator it=fixed.iterator(); it.hasNext();) {
+ serializationAttributes.remove(it.next());
+ }
+
+ ResultDocument inst = new ResultDocument(globalProps,
+ localProps,
+ href,
+ formatExpression,
+ getBaseURI(),
+ validationAction,
+ schemaType,
+ serializationAttributes,
+ (needsNamespaceContext ? namespaceResolver : null));
+
+ Expression b = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (b == null) {
+ b = Literal.makeLiteral(EmptySequence.getInstance());
+ }
+ inst.setContentExpression(b);
+ inst.setAsynchronous(async);
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLSequence.java b/sf/saxon/style/XSLSequence.java
new file mode 100644
index 0000000..17fac07
--- /dev/null
+++ b/sf/saxon/style/XSLSequence.java
@@ -0,0 +1,126 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+
+/**
+ * An xsl:sequence element in the stylesheet. <br>
+ * The xsl:sequence element takes attributes:<ul>
+ * <li>a mandatory attribute select="expression".</li>
+ * </ul>
+ */
+
+public final class XSLSequence extends StyleElement {
+
+ private Expression select;
+
+ /**
+ * Determine whether this node is an instruction.
+ * @return true - it is an instruction
+ */
+
+ public boolean isInstruction() {
+ return true;
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ if (select == null) {
+ return AnyItemType.getInstance();
+ }
+ final TypeHierarchy th = getConfiguration().getTypeHierarchy();
+ return select.getItemType(th);
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a sequence constructor
+ * @return in XSLT 2.0, false. In XSLT 3.0 true: yes, it may contain a sequence constructor
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return isXslt30Processor();
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain an xsl:fallback
+ * instruction
+ */
+
+ public boolean mayContainFallback() {
+ return true;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ String selectAtt = null;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt!=null) {
+ select = makeExpression(selectAtt);
+ } else if (!isXslt30Processor()) {
+ reportAbsence(StandardNames.SELECT);
+ select = Literal.makeEmptySequence();
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo child = kids.next();
+ if (child == null) break;
+ if (!(child instanceof XSLFallback)) {
+ if (isXslt30Processor()) {
+ if (select != null) {
+ compileError("An xsl:sequence element with a select attribute must be empty", "XTSE3185");
+ }
+ } else {
+ compileError("The only child node allowed for xsl:sequence is an xsl:fallback instruction", "XTSE0010");
+ }
+ break;
+ }
+ }
+ select = typeCheck("select", select);
+ }
+
+ /*@Nullable*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ if (select == null) {
+ select = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), false);
+ }
+ return select;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLSort.java b/sf/saxon/style/XSLSort.java
new file mode 100644
index 0000000..7d2ee9f
--- /dev/null
+++ b/sf/saxon/style/XSLSort.java
@@ -0,0 +1,50 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+
+/**
+* An xsl:sort element in the stylesheet. <br>
+*/
+
+public class XSLSort extends XSLSortOrMergeKey {
+
+
+
+ public void validate(Declaration decl) throws XPathException {
+
+ super.validate(decl);
+ stable = typeCheck("stable", stable);
+ sortKeyDefinition.setStable(stable);
+
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction). Default implementation returns Type.ITEM, indicating
+ * that we don't know, it might be anything. Returns null in the case of an element
+ * such as xsl:sort or xsl:variable that can appear in a sequence constructor but
+ * contributes nothing to the result sequence.
+ * @return the item type returned
+ */
+
+ /*@Nullable*/ protected ItemType getReturnedItemType() {
+ return null;
+ }
+
+
+ public Expression getStable() {
+ return stable;
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLSortOrMergeKey.java b/sf/saxon/style/XSLSortOrMergeKey.java
new file mode 100644
index 0000000..9ab985a
--- /dev/null
+++ b/sf/saxon/style/XSLSortOrMergeKey.java
@@ -0,0 +1,254 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.expr.sort.SortKeyDefinition;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.StringConverter;
+import net.sf.saxon.type.ValidationFailure;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public abstract class XSLSortOrMergeKey extends StyleElement {
+
+ protected SortKeyDefinition sortKeyDefinition;
+ protected Expression select;
+ protected Expression order;
+ protected Expression dataType = null;
+ protected Expression caseOrder;
+ protected Expression lang;
+ protected Expression collationName;
+ protected Expression stable;
+ protected boolean useDefaultCollation = true;
+
+
+ /**
+ * Determine whether this type of element is allowed to contain a sequence constructor
+ * @return true: yes, it may contain a sequence constructor
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ protected String getErrorCode(){
+ return "XTSE1015";
+ }
+
+ @Override
+ public void validate(Declaration decl) throws XPathException {
+ if (select != null && hasChildNodes()) {
+ compileError("An "+getDisplayName()+" element with a select attribute must be empty", getErrorCode());
+ }
+ if (select == null && !hasChildNodes()) {
+ select = new ContextItemExpression();
+ }
+
+ // Get the named or default collation
+
+ if (useDefaultCollation) {
+ collationName = new StringLiteral(getDefaultCollationName());
+ }
+
+ StringCollator stringCollator = null;
+ if (collationName instanceof StringLiteral) {
+ String collationString = ((StringLiteral)collationName).getStringValue();
+ try {
+ URI collationURI = new URI(collationString);
+ if (!collationURI.isAbsolute()) {
+ URI base = new URI(getBaseURI());
+ collationURI = base.resolve(collationURI);
+ collationString = collationURI.toString();
+ }
+ } catch (URISyntaxException err) {
+ compileError("Collation name '" + collationString + "' is not a valid URI");
+ collationString = NamespaceConstant.CODEPOINT_COLLATION_URI;
+ }
+ stringCollator = getPrincipalStylesheetModule().findCollation(collationString, getBaseURI());
+ if (stringCollator==null) {
+ compileError("Collation " + collationString + " has not been defined", "XTDE1035");
+ stringCollator = CodepointCollator.getInstance(); // for recovery paths
+ }
+ }
+
+ select = typeCheck("select", select);
+ order = typeCheck("order", order);
+ caseOrder = typeCheck("case-order", caseOrder);
+ lang = typeCheck("lang", lang);
+ dataType = typeCheck("data-type", dataType);
+ collationName = typeCheck("collation", collationName);
+
+
+ if (select != null) {
+ try {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.INSTRUCTION, getDisplayName()+"//select", 0);
+ //role.setSourceLocator(new ExpressionLocation(this));
+ select = TypeChecker.staticTypeCheck(select,
+ SequenceType.ATOMIC_SEQUENCE,
+ false, role, makeExpressionVisitor());
+ } catch (XPathException err) {
+ compileError(err);
+ }
+ }
+
+ sortKeyDefinition = new SortKeyDefinition();
+ sortKeyDefinition.setOrder(order);
+ sortKeyDefinition.setCaseOrder(caseOrder);
+ sortKeyDefinition.setLanguage(lang);
+ sortKeyDefinition.setSortKey(select, true);
+ sortKeyDefinition.setDataTypeExpression(dataType);
+ sortKeyDefinition.setCollationNameExpression(collationName);
+ sortKeyDefinition.setCollation(stringCollator);
+ sortKeyDefinition.setBaseURI(getBaseURI());
+ sortKeyDefinition.setStable(stable);
+ sortKeyDefinition.setBackwardsCompatible(xPath10ModeIsEnabled());
+ }
+
+ protected Expression getStable(){
+ return stable;
+ }
+
+ @Override
+ protected void prepareAttributes() throws XPathException {
+ AttributeCollection atts = getAttributeList();
+
+ String selectAtt = null;
+ String orderAtt = null;
+ String dataTypeAtt = null;
+ String caseOrderAtt = null;
+ String langAtt = null;
+ String collationAtt = null;
+ String stableAtt = null;
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.ORDER)) {
+ orderAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.DATA_TYPE)) {
+ dataTypeAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.CASE_ORDER)) {
+ caseOrderAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.LANG)) {
+ langAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.COLLATION)) {
+ collationAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.STABLE)) {
+ stableAtt = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt==null) {
+ //select = new ContextItemExpression();
+ } else {
+ select = makeExpression(selectAtt);
+ }
+
+ if (orderAtt == null) {
+ order = new StringLiteral("ascending");
+ } else {
+ checkAttributeValue("order", orderAtt, true, new String[]{"ascending", "descending"});
+ order = makeAttributeValueTemplate(orderAtt);
+ }
+
+ if (dataTypeAtt == null) {
+ dataType = null;
+ } else {
+ dataType = makeAttributeValueTemplate(dataTypeAtt);
+ }
+
+ if (caseOrderAtt == null) {
+ caseOrder = new StringLiteral("#default");
+ } else {
+ checkAttributeValue("case-order", caseOrderAtt, true, new String[]{"lower-first", "upper-first"});
+ caseOrder = makeAttributeValueTemplate(caseOrderAtt);
+ useDefaultCollation = false;
+ }
+
+ if (langAtt == null || langAtt.equals("")) {
+ lang = new StringLiteral(StringValue.EMPTY_STRING);
+ } else {
+ lang = makeAttributeValueTemplate(langAtt);
+ useDefaultCollation = false;
+ if (lang instanceof StringLiteral) {
+ String s = ((StringLiteral)lang).getStringValue();
+ if (s.length() != 0) {
+ ValidationFailure vf = StringConverter.STRING_TO_LANGUAGE.validate(s);
+ if (vf != null) {
+ compileError("The lang attribute must be a valid language code", "XTDE0030");
+ lang = new StringLiteral(StringValue.EMPTY_STRING);
+ }
+ }
+ }
+ }
+
+ if (stableAtt == null) {
+ stable = null;
+ } else {
+ checkAttributeValue("stable", stableAtt, true, StyleElement.YES_NO);
+ stable = makeAttributeValueTemplate(stableAtt);
+ }
+
+ if (collationAtt != null) {
+ collationName = makeAttributeValueTemplate(collationAtt);
+ useDefaultCollation = false;
+ }
+
+ }
+
+
+ /*@Nullable*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ if (select == null) {
+ Expression b = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (b == null) {
+ b = Literal.makeEmptySequence();
+ }
+ b.setContainer(this);
+ try {
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ Expression atomizedSortKey = Atomizer.makeAtomizer(b);
+ atomizedSortKey = visitor.simplify(atomizedSortKey);
+ ExpressionTool.copyLocationInfo(b, atomizedSortKey);
+ sortKeyDefinition.setSortKey(atomizedSortKey, true);
+ } catch (XPathException e) {
+ compileError(e);
+ }
+ }
+ // Simplify the sort key definition - this is especially important in the case where
+ // all aspects of the sort key are known statically.
+ sortKeyDefinition = sortKeyDefinition.simplify(makeExpressionVisitor());
+ // not an executable instruction
+ return null;
+ }
+
+ public SortKeyDefinition getSortKeyDefinition() {
+ return sortKeyDefinition;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLStylesheet.java b/sf/saxon/style/XSLStylesheet.java
new file mode 100644
index 0000000..cecab51
--- /dev/null
+++ b/sf/saxon/style/XSLStylesheet.java
@@ -0,0 +1,312 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.LocationMap;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.KeyManager;
+import net.sf.saxon.trans.RuleManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Whitespace;
+
+/**
+ * An xsl:stylesheet or xsl:transform element in the stylesheet. <br>
+ * Note this element represents a stylesheet module, not necessarily
+ * the whole stylesheet. However, much of the functionality (and the fields)
+ * are relevant only to the top-level module.
+ */
+
+public class XSLStylesheet extends StyleElement {
+
+ PreparedStylesheet exec;
+
+
+ // the PrincipalStylesheetModule object
+ private PrincipalStylesheetModule principalStylesheetModule;
+
+ public static final int ANNOTATION_UNSPECIFIED = 0;
+ public static final int ANNOTATION_STRIP = 1;
+ public static final int ANNOTATION_PRESERVE = 2;
+
+
+
+ // default validation
+ private int defaultValidation = Validation.STRIP;
+
+ // default mode (XSLT 3.0 only). Null means the unnamed mode is the default.
+ private StructuredQName defaultMode = null;
+
+ /**
+ * Get the owning PreparedStylesheet object.
+ * @return the owning PreparedStylesheet object. Exceptionally returns null during early construction.
+ */
+
+ public PreparedStylesheet getPreparedStylesheet() {
+ return (principalStylesheetModule==null ? null : principalStylesheetModule.getPreparedStylesheet());
+ }
+
+ public void setPrincipalStylesheetModule(PrincipalStylesheetModule module) {
+ this.principalStylesheetModule = module;
+ this.exec = module.getPreparedStylesheet();
+ }
+
+ public PrincipalStylesheetModule getPrincipalStylesheetModule() {
+ return principalStylesheetModule;
+ }
+
+ /**
+ * Get the run-time Executable object
+ */
+
+ /*@NotNull*/
+ public PreparedStylesheet getExecutable() {
+ return exec;
+ }
+
+ protected boolean mayContainParam(String attName) {
+ return true;
+ }
+
+ /**
+ * Get the locationMap object
+ * @return the LocationMap
+ */
+
+ public LocationMap getLocationMap() {
+ return exec.getLocationMap();
+ }
+
+ /**
+ * Get the RuleManager which handles template rules
+ * @return the template rule manager
+ */
+
+ public RuleManager getRuleManager() {
+ return exec.getRuleManager();
+ }
+
+ /**
+ * Get the default mode (XSLT 3.0 feature)
+ * @return the default mode name for this stylesheet module. A return value of null indicates either that
+ * no default mode was specified, or that default-mode="#unnamed" was specified.
+ */
+
+ public StructuredQName getDefaultMode() {
+ return defaultMode;
+ }
+
+ /**
+ * Get the KeyManager which handles key definitions
+ * @return the key manager
+ */
+
+ public KeyManager getKeyManager() {
+ if (exec.getKeyManager() == null) {
+ exec.setKeyManager(new KeyManager(getConfiguration()));
+ }
+ return exec.getKeyManager();
+ }
+
+ /**
+ * Prepare the attributes on the stylesheet element
+ */
+
+ public void prepareAttributes() throws XPathException {
+
+ String inputTypeAnnotationsAtt = null;
+ AttributeCollection atts = getAttributeList();
+ for (int a = 0; a < atts.getLength(); a++) {
+
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.VERSION)) {
+ // already processed
+ } else if (f.equals(StandardNames.ID)) {
+ //
+ } else if (f.equals(StandardNames.EXTENSION_ELEMENT_PREFIXES)) {
+ //
+ } else if (f.equals(StandardNames.EXCLUDE_RESULT_PREFIXES)) {
+ //
+ } else if (f.equals(StandardNames.DEFAULT_VALIDATION)) {
+ String val = Whitespace.trim(atts.getValue(a));
+ defaultValidation = Validation.getCode(val);
+ if (defaultValidation == Validation.INVALID) {
+ compileError("Invalid value for default-validation attribute. " +
+ "Permitted values are (strict, lax, preserve, strip)", "XTSE0020");
+ } else if (!getExecutable().isSchemaAware() && defaultValidation != Validation.STRIP) {
+ defaultValidation = Validation.STRIP;
+ compileError("default-validation='" + val + "' requires a schema-aware processor",
+ "XTSE1660");
+ }
+ } else if (f.equals(StandardNames.INPUT_TYPE_ANNOTATIONS)) {
+ inputTypeAnnotationsAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.DEFAULT_MODE)) {
+ if (!isXslt30Processor()) {
+ compileError("The @default-mode attribute requires XSLT 3.0");
+ }
+ String val = Whitespace.trim(atts.getValue(a));
+ if (!"#unnamed".equals(val)) {
+ try {
+ defaultMode = makeQName(atts.getValue(a));
+ } catch (NamespaceException err) {
+ throw new XPathException(err.getMessage(), "XTST0030");
+ }
+ }
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+ if (version == null) {
+ reportAbsence("version");
+ }
+
+ if (inputTypeAnnotationsAtt != null) {
+ if (inputTypeAnnotationsAtt.equals("strip")) {
+ //setInputTypeAnnotations(ANNOTATION_STRIP);
+ } else if (inputTypeAnnotationsAtt.equals("preserve")) {
+ //setInputTypeAnnotations(ANNOTATION_PRESERVE);
+ } else if (inputTypeAnnotationsAtt.equals("unspecified")) {
+ //
+ } else {
+ compileError("Invalid value for input-type-annotations attribute. " +
+ "Permitted values are (strip, preserve, unspecified)", "XTSE0020");
+ }
+ }
+
+ }
+
+
+ /**
+ * Get the value of the default validation attribute
+ * @return the value of the default-validation attribute, as a constant such
+ * as {@link Validation#STRIP}
+ */
+
+ public int getDefaultValidation() {
+ return defaultValidation;
+ }
+
+
+ /**
+ * Get the value of the input-type-annotations attribute, for this module alone.
+ * The value is an or-ed combination of the two bits
+ * {@link #ANNOTATION_STRIP} and {@link #ANNOTATION_PRESERVE}
+ * @return the value if the input-type-annotations attribute in this stylesheet module
+ * @throws net.sf.saxon.trans.XPathException if an error is detected
+ */
+
+ public int getInputTypeAnnotationsAttribute() throws XPathException {
+ String inputTypeAnnotationsAtt = getAttributeValue("", StandardNames.INPUT_TYPE_ANNOTATIONS);
+ if (inputTypeAnnotationsAtt != null) {
+ if (inputTypeAnnotationsAtt.equals("strip")) {
+ return ANNOTATION_STRIP;
+ } else if (inputTypeAnnotationsAtt.equals("preserve")) {
+ return ANNOTATION_PRESERVE;
+ } else if (inputTypeAnnotationsAtt.equals("unspecified")) {
+ return ANNOTATION_UNSPECIFIED;
+ } else {
+ compileError("Invalid value for input-type-annotations attribute. " +
+ "Permitted values are (strip, preserve, unspecified)", "XTSE0020");
+ }
+ }
+ return -1;
+ }
+
+
+
+
+
+ /**
+ * Validate this element
+ * @param decl Not used
+ */
+
+ public void validate(Declaration decl) throws XPathException {
+ if (validationError != null) {
+ compileError(validationError);
+ }
+ if (getParent().getNodeKind() != Type.DOCUMENT) {
+ compileError(getDisplayName() + " must be the outermost element", "XTSE0010");
+ }
+
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while(true) {
+ NodeInfo curr = kids.next();
+ if (curr == null) break;
+ if (curr.getNodeKind() == Type.TEXT ||
+ (curr instanceof StyleElement && ((StyleElement)curr).isDeclaration()) ||
+ curr instanceof DataElement) {
+ // all is well
+ } else if (!NamespaceConstant.XSLT.equals(curr.getURI()) && !"".equals(curr.getURI())) {
+ // elements in other namespaces are allowed and ignored
+ } else if (curr instanceof AbsentExtensionElement && ((StyleElement)curr).forwardsCompatibleModeIsEnabled()) {
+ // this is OK: an unknown XSLT element is allowed in forwards compatibility mode
+ } else if (NamespaceConstant.XSLT.equals(curr.getURI())) {
+ ((StyleElement)curr).compileError("Element " + curr.getDisplayName() +
+ " must not appear directly within " + getDisplayName(), "XTSE0010");
+ } else {
+ ((StyleElement)curr).compileError("Element " + curr.getDisplayName() +
+ " must not appear directly within " + getDisplayName() +
+ " because it is not in a namespace", "XTSE0130");
+ }
+ }
+ }
+
+
+
+
+
+
+
+ /**
+ * Process the attributes of every node in the stylesheet
+ */
+
+ public void processAllAttributes() throws XPathException {
+// processDefaultCollationAttribute();
+ prepareAttributes();
+ AxisIterator iter = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo node = (NodeInfo)iter.next();
+ if (node == null) {
+ break;
+ }
+ if (node instanceof StyleElement) {
+ try {
+ ((StyleElement) node).processAllAttributes();
+ } catch (XPathException err) {
+ ((StyleElement) node).compileError(err);
+ }
+ }
+ }
+ }
+
+
+ @Override
+ protected void index(Declaration decl, PrincipalStylesheetModule top) throws XPathException {
+ compileError("xsl:stylesheet can appear only as the outermost element", "XTSE0010");
+ }
+
+ /**
+ * Dummy compile() method to satisfy the interface
+ */
+
+ /*@Nullable*/ public Expression compile(Executable exec, Declaration decl) {
+ return null;
+ }
+
+
+
+}
+
diff --git a/sf/saxon/style/XSLTStaticContext.java b/sf/saxon/style/XSLTStaticContext.java
new file mode 100644
index 0000000..84a3750
--- /dev/null
+++ b/sf/saxon/style/XSLTStaticContext.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Extends the standard XPath static context with information that is available for
+ * XPath expressions invoked from XSLT
+ */
+
+public interface XSLTStaticContext extends StaticContext {
+
+ /**
+ * Determine if an extension element is available
+ * @throws net.sf.saxon.trans.XPathException if the name is invalid or the prefix is not declared
+ */
+
+ public boolean isElementAvailable(String qname) throws XPathException;
+
+}
+
+
diff --git a/sf/saxon/style/XSLTemplate.java b/sf/saxon/style/XSLTemplate.java
new file mode 100644
index 0000000..181eabb
--- /dev/null
+++ b/sf/saxon/style/XSLTemplate.java
@@ -0,0 +1,522 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.instruct.Template;
+import net.sf.saxon.expr.instruct.TraceExpression;
+import net.sf.saxon.expr.parser.*;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.trans.Mode;
+import net.sf.saxon.trans.RuleManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.ErrorType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.transform.TransformerException;
+import java.io.PrintStream;
+import java.util.StringTokenizer;
+
+/**
+* An xsl:template element in the style sheet.
+*/
+
+public final class XSLTemplate extends StyleElement implements StylesheetProcedure {
+
+ private String matchAtt = null;
+ private String modeAtt = null;
+ private String nameAtt = null;
+ private String priorityAtt = null;
+ private String asAtt = null;
+
+ private StructuredQName[] modeNames;
+ private String diagnosticId;
+ private Pattern match;
+ private boolean prioritySpecified;
+ private double priority;
+ private SlotManager stackFrameMap;
+ private Template compiledTemplate = new Template();
+ private SequenceType requiredType = null;
+ private boolean hasRequiredParams = false;
+ private boolean isTailRecursive = false;
+
+ /**
+ * Ask whether this node is a declaration, that is, a permitted child of xsl:stylesheet
+ * (including xsl:include and xsl:import).
+ * @return true for this element
+ */
+
+ @Override
+ public boolean isDeclaration() {
+ return true;
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ protected boolean mayContainParam(String attName) {
+ return true;
+ }
+
+ /**
+ * Specify that xsl:param is a permitted child
+ */
+
+ protected boolean isPermittedChild(StyleElement child) {
+ return (child instanceof XSLLocalParam);
+ }
+
+ /**
+ * Return the name of this template. Note that this may
+ * be called before prepareAttributes has been called.
+ * @return the name of the template as a Structured QName.
+ */
+
+ /*@Nullable*/ public StructuredQName getTemplateName() {
+
+ //We use null to mean "not yet evaluated"
+
+ try {
+ if (getObjectName()==null) {
+ // allow for forwards references
+ String nameAtt = getAttributeValue("", StandardNames.NAME);
+ if (nameAtt != null) {
+ setObjectName(makeQName(nameAtt));
+ }
+ }
+ return getObjectName();
+ } catch (NamespaceException err) {
+ return null; // the errors will be picked up later
+ } catch (XPathException err) {
+ return null;
+ }
+ }
+
+ /**
+ * Determine the type of item returned by this template
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ if (requiredType==null) {
+ return getCommonChildItemType();
+ } else {
+ return requiredType.getPrimaryType();
+ }
+ }
+
+
+ public void prepareAttributes() throws XPathException {
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.MODE)) {
+ modeAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.NAME)) {
+ nameAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.MATCH)) {
+ matchAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.PRIORITY)) {
+ priorityAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.AS)) {
+ asAtt = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+ try {
+ if (modeAtt==null) {
+ // XSLT 3.0 allows the default mode to be specified at stylesheet module level
+ StructuredQName defaultMode = getContainingStylesheet().getDefaultMode();
+ if (defaultMode == null) {
+ defaultMode = Mode.UNNAMED_MODE_NAME;
+ }
+ modeNames = new StructuredQName[1];
+ modeNames[0] = defaultMode;
+ } else {
+ if (matchAtt==null) {
+ compileError("The mode attribute must be absent if the match attribute is absent", "XTSE0500");
+ }
+ // mode is a space-separated list of mode names, or "#default", or "#all"
+
+ int count = 0;
+ boolean allModes = false;
+ StringTokenizer st = new StringTokenizer(modeAtt, " \t\n\r", false);
+ while (st.hasMoreTokens()) {
+ st.nextToken();
+ count++;
+ }
+
+ if (count==0) {
+ compileError("The mode attribute must not be empty", "XTSE0550");
+ }
+
+ modeNames = new StructuredQName[count];
+ count = 0;
+ st = new StringTokenizer(modeAtt, " \t\n\r", false);
+ while (st.hasMoreTokens()) {
+ String s = st.nextToken();
+ StructuredQName mname;
+ if ("#default".equals(s)) {
+ mname = getContainingStylesheet().getDefaultMode();
+ if (mname == null) {
+ mname = Mode.UNNAMED_MODE_NAME;
+ }
+ } else if ("#unnamed".equals(s) && isXslt30Processor()) {
+ mname = Mode.UNNAMED_MODE_NAME;
+ } else if ("#all".equals(s)) {
+ allModes = true;
+ mname = Mode.ALL_MODES;
+ } else {
+ mname = makeQName(s);
+ }
+ for (int e=0; e < count; e++) {
+ if (modeNames[e].equals(mname)) {
+ compileError("In the list of modes, the value " + s + " is duplicated", "XTSE0550");
+ }
+ }
+ modeNames[count++] = mname;
+ }
+ if (allModes && (count>1)) {
+ compileError("mode='#all' cannot be combined with other modes", "XTSE0550");
+ }
+ }
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE0280");
+ } catch (XPathException err) {
+ err.maybeSetErrorCode("XTSE0280");
+ if (err.getErrorCodeLocalPart().equals("XTSE0020")) {
+ err.setErrorCode("XTSE0550");
+ }
+ err.setIsStaticError(true);
+ compileError(err);
+ }
+
+ try{
+ if (nameAtt!=null) {
+ StructuredQName qName = makeQName(nameAtt);
+ setObjectName(qName);
+ diagnosticId = nameAtt;
+ }
+ } catch (NamespaceException err) {
+ compileError(err.getMessage(), "XTSE0280");
+ } catch (XPathException err) {
+ err.maybeSetErrorCode("XTSE0280");
+ err.setIsStaticError(true);
+ compileError(err);
+ }
+
+ prioritySpecified = (priorityAtt != null);
+ if (prioritySpecified) {
+ if (matchAtt==null) {
+ compileError("The priority attribute must be absent if the match attribute is absent", "XTSE0500");
+ }
+ try {
+ // it's got to be a valid decimal, but we want it as a double, so parse it twice
+ if (!DecimalValue.castableAsDecimal(priorityAtt)) {
+ compileError("Invalid numeric value for priority (" + priority + ')', "XTSE0530");
+ }
+ priority = Double.parseDouble(priorityAtt);
+ } catch (NumberFormatException err) {
+ // shouldn't happen
+ compileError("Invalid numeric value for priority (" + priority + ')', "XTSE0530");
+ }
+ }
+
+ if (matchAtt != null) {
+ match = makePattern(matchAtt);
+ if (diagnosticId == null) {
+ diagnosticId = "match=\"" + matchAtt + '\"';
+ if (modeAtt != null) {
+ diagnosticId += " mode=\"" + modeAtt + '\"';
+ }
+ }
+ }
+
+ if (match==null && nameAtt==null)
+ compileError("xsl:template must have a name or match attribute (or both)", "XTSE0500");
+
+ if (asAtt != null) {
+ requiredType = makeSequenceType(asAtt);
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ stackFrameMap = getConfiguration().makeSlotManager();
+ checkTopLevel("XTSE0010");
+
+ // the check for duplicates is now done in the buildIndexes() method of XSLStylesheet
+ if (match != null) {
+ match = typeCheck("match", match);
+ if (match.getItemType() instanceof ErrorType) {
+ try {
+ getConfiguration().getErrorListener().warning(
+ new TransformerException("Pattern will never match anything", this));
+ } catch (TransformerException e) {
+ //noinspection ThrowableResultOfMethodCallIgnored
+ compileError(XPathException.makeXPathException(e));
+ }
+ }
+ }
+
+ // See if there are any required parameters.
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ while(true) {
+ NodeInfo param = kids.next();
+ if (param == null) {
+ break;
+ }
+ if (param instanceof XSLLocalParam && ((XSLLocalParam)param).isRequiredParam()) {
+ hasRequiredParams = true;
+ break;
+ }
+ }
+
+ }
+
+
+ public void postValidate() throws XPathException {
+ isTailRecursive = markTailCalls();
+ }
+
+ protected void index(Declaration decl, PrincipalStylesheetModule top) throws XPathException {
+ top.indexNamedTemplate(decl);
+ }
+
+ /**
+ * Mark tail-recursive calls on templates and functions.
+ */
+
+ public boolean markTailCalls() {
+ StyleElement last = getLastChildInstruction();
+ return last != null && last.markTailCalls();
+ }
+
+ /**
+ * Compile: creates the executable form of the template
+ */
+
+ public void compileDeclaration(Executable exec, Declaration decl) throws XPathException {
+
+ Expression block = compileSequenceConstructor(exec, decl, iterateAxis(AxisInfo.CHILD), true);
+ if (block == null) {
+ block = Literal.makeEmptySequence();
+ }
+
+ compiledTemplate.setMatchPattern(match);
+ compiledTemplate.setBody(block);
+ compiledTemplate.setStackFrameMap(stackFrameMap);
+ compiledTemplate.setExecutable(getPreparedStylesheet());
+ compiledTemplate.setSystemId(getSystemId());
+ compiledTemplate.setLineNumber(getLineNumber());
+ compiledTemplate.setHasRequiredParams(hasRequiredParams);
+ compiledTemplate.setRequiredType(requiredType);
+ compiledTemplate.setModeNames(modeNames);
+
+ Expression exp = null;
+ try {
+ exp = makeExpressionVisitor().simplify(block);
+ } catch (XPathException e) {
+ compileError(e);
+ }
+
+ try {
+ if (requiredType != null) {
+ RoleLocator role =
+ new RoleLocator(RoleLocator.TEMPLATE_RESULT, diagnosticId, 0);
+ //role.setSourceLocator(new ExpressionLocation(this));
+ role.setErrorCode("XTTE0505");
+ exp = TypeChecker.staticTypeCheck(exp, requiredType, false, role, makeExpressionVisitor());
+ }
+ } catch (XPathException err) {
+ compileError(err);
+ }
+
+ compiledTemplate.setBody(exp);
+ compiledTemplate.setTemplateName(getObjectName());
+
+ if (getConfiguration().isCompileWithTracing()) {
+ // Add trace wrapper code if required
+ exp = makeTraceInstruction(this, exp);
+ if (exp instanceof TraceExpression) {
+ ((TraceExpression)exp).setProperty("match", matchAtt);
+ ((TraceExpression)exp).setProperty("mode", modeAtt);
+ }
+ compiledTemplate.setBody(exp);
+ }
+ }
+
+ /**
+ * Registers the template rule with each Mode that it belongs to.
+ * @param declaration Associates this template with a stylesheet module (in principle an xsl:template
+ * element can be in a document that is imported more than once; these are separate declarations)
+ * @throws XPathException if a failure occurs
+ */
+
+ public void register(Declaration declaration) throws XPathException {
+ if (match != null) {
+ StylesheetModule module = declaration.getModule();
+ // first slot in pattern is reserved for current()
+ int nextFree = 0;
+ if ((match.getDependencies() & StaticProperty.DEPENDS_ON_CURRENT_ITEM) != 0) {
+ nextFree = 1;
+ }
+ int slots = match.allocateSlots(getSlotManager(), nextFree);
+ RuleManager mgr = getPreparedStylesheet().getRuleManager();
+ for (StructuredQName nc : modeNames) {
+ Mode mode = mgr.getMode(nc, true);
+ if (prioritySpecified) {
+ mgr.setTemplateRule(match, compiledTemplate, mode, module, priority);
+ } else {
+ mgr.setTemplateRule(match, compiledTemplate, mode, module, Double.NaN);
+ }
+ mode.allocatePatternSlots(slots);
+ if (mode.isStreamable()) {
+ compiledTemplate.setDeclaredStreamable(true);
+ }
+ }
+
+ allocatePatternSlots(slots);
+ }
+ }
+
+
+ /**
+ * This method is a bit of a misnomer, because it does more than invoke optimization of the template body.
+ * In particular, it also registers the template rule with each Mode that it belongs to.
+ * @throws XPathException
+ * @param declaration Associates this template with a stylesheet module (in principle an xsl:template
+ * element can be in a document that is imported more than once; these are separate declarations)
+ */
+
+ public void optimize(Declaration declaration) throws XPathException {
+ ItemType contextItemType = Type.ITEM_TYPE;
+ ExpressionVisitor.ContextItemType cit;
+ if (getObjectName() == null) {
+ // the template can't be called by name, so the context item must match the match pattern
+ contextItemType = match.getItemType();
+ cit = new ExpressionVisitor.ContextItemType(contextItemType, false);
+ } else {
+ cit = new ExpressionVisitor.ContextItemType(contextItemType, true);
+ }
+
+ Expression exp = compiledTemplate.getBody();
+ ExpressionTool.resetPropertiesWithinSubtree(exp);
+ ExpressionVisitor visitor = makeExpressionVisitor();
+ visitor.setOptimizeForStreaming(compiledTemplate.isDeclaredStreamable());
+ Optimizer opt = getConfiguration().obtainOptimizer();
+ try {
+ // We've already done the typecheck of each XPath expression, but it's worth doing again at this
+ // level because we have more information now.
+ Expression exp2 = visitor.typeCheck(exp, cit);
+ if (opt.getOptimizationLevel() != Optimizer.NO_OPTIMIZATION) {
+ exp2 = visitor.optimize(exp2, cit);
+ }
+ if (exp != exp2) {
+ compiledTemplate.setBody(exp2);
+ exp = exp2;
+ }
+ } catch (XPathException e) {
+ compileError(e);
+ }
+
+ // Try to extract new global variables from the body of the template
+// ExpressionPresenter presenter = ExpressionPresenter.make(getConfiguration());
+// exp.explain(presenter);
+// presenter.close();
+ // TODO: it might be better to extract global variables much earlier
+ if (opt.getOptimizationLevel() != Optimizer.NO_OPTIMIZATION && !getConfiguration().isCompileWithTracing()) {
+ Expression exp2 = opt.promoteExpressionsToGlobal(exp, visitor);
+ if (exp2 != null) {
+ // Try another optimization pass: extracting global variables can identify things that are indexable
+ compiledTemplate.setBody(visitor.optimize(exp2, cit));
+ exp = exp2;
+ }
+ }
+
+ allocateSlots(exp);
+
+ // Generate byte code if appropriate
+
+ if (getConfiguration().isGenerateByteCode(Configuration.XSLT) && !isTailRecursive && !compiledTemplate.isDeclaredStreamable()) {
+ try {
+ Expression cbody = opt.compileToByteCode(exp, (nameAtt==null ? matchAtt : nameAtt), Expression.PROCESS_METHOD);
+ if (cbody != null) {
+ compiledTemplate.setBody(cbody);
+ exp = cbody;
+ }
+ } catch (Exception e) {
+ System.err.println("Failed while compiling function " + (nameAtt==null ? matchAtt : nameAtt));
+ e.printStackTrace();
+ throw new XPathException(e);
+ }
+ }
+
+ opt.checkStreamability(this, compiledTemplate);
+
+ if (isExplaining()) {
+ PrintStream err = getConfiguration().getStandardErrorOutput();
+ err.println("Optimized expression tree for template at line " +
+ getLineNumber() + " in " + getSystemId() + ':');
+ exp.explain(err);
+ }
+
+ }
+
+
+
+ /**
+ * Get associated Procedure (for details of stack frame)
+ */
+
+ public SlotManager getSlotManager() {
+ return stackFrameMap;
+ }
+
+
+
+ /**
+ * Get the compiled template
+ * @return the compiled template
+ */
+
+ public Template getCompiledTemplate() {
+ return compiledTemplate;
+ }
+
+ /**
+ * Get the type of construct. This will be a constant in
+ * class {@link net.sf.saxon.trace.Location}. This method is part of the {@link net.sf.saxon.trace.InstructionInfo} interface
+ */
+
+ public int getConstructType() {
+ return StandardNames.XSL_TEMPLATE;
+ }
+
+
+}
+
diff --git a/sf/saxon/style/XSLText.java b/sf/saxon/style/XSLText.java
new file mode 100644
index 0000000..82601fb
--- /dev/null
+++ b/sf/saxon/style/XSLText.java
@@ -0,0 +1,108 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.ValueOf;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+/**
+* Handler for xsl:text elements in stylesheet. <BR>
+*/
+
+public class XSLText extends XSLLeafNodeConstructor {
+
+ private boolean disable = false;
+ private StringValue value;
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return NodeKindTest.TEXT;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ String disableAtt = null;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.DISABLE_OUTPUT_ESCAPING)) {
+ disableAtt = Whitespace.trim(atts.getValue(a));
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (disableAtt != null) {
+ if (disableAtt.equals("yes")) {
+ disable = true;
+ } else if (disableAtt.equals("no")) {
+ disable = false;
+ } else {
+ compileError("disable-output-escaping attribute must be either 'yes' or 'no'", "XTSE0020");
+ }
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+
+ AxisIterator kids = iterateAxis(AxisInfo.CHILD);
+ value = StringValue.EMPTY_STRING;
+ while(true) {
+ Item child = kids.next();
+ if (child == null) {
+ break;
+ } else if (child instanceof StyleElement) {
+ ((StyleElement)child).compileError("xsl:text must not contain child elements", "XTSE0010");
+ return;
+ } else {
+ value = StringValue.makeStringValue(child.getStringValueCS());
+ //continue;
+ }
+ }
+
+ String expandText = getAttributeValue("", "expand-text");
+ if (expandText != null && "yes".equals(Whitespace.trim(expandText))) {
+ compileError("xsl:text must not specify expand-text=`yes'", "XTSE0020");
+ }
+ super.validate(decl);
+ }
+
+ /**
+ * Get the error code to be returned when the element has a select attribute but is not empty.
+ * @return the error code defined for this condition, for this particular instruction
+ */
+
+ /*@Nullable*/ protected String getErrorCodeForSelectPlusContent() {
+ return null; // not applicable
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ return new ValueOf(Literal.makeLiteral(value), disable, false);
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLValueOf.java b/sf/saxon/style/XSLValueOf.java
new file mode 100644
index 0000000..ef276c0
--- /dev/null
+++ b/sf/saxon/style/XSLValueOf.java
@@ -0,0 +1,140 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.ValueOf;
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.Cardinality;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+
+/**
+* An xsl:value-of element in the stylesheet. <br>
+* The xsl:value-of element takes attributes:<ul>
+* <li>a mandatory attribute select="expression".
+* This must be a valid String expression</li>
+* <li>an optional disable-output-escaping attribute, value "yes" or "no"</li>
+* <li>an optional separator attribute</li>
+* </ul>
+*/
+
+public final class XSLValueOf extends XSLLeafNodeConstructor {
+
+ private boolean disable = false;
+ /*@Nullable*/ private Expression separator;
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return NodeKindTest.TEXT;
+ }
+
+ public void prepareAttributes() throws XPathException {
+
+ String selectAtt = null;
+ String disableAtt = null;
+ String separatorAtt = null;
+
+ AttributeCollection atts = getAttributeList();
+
+ for (int a=0; a<atts.getLength(); a++) {
+ String f = atts.getQName(a);
+ if (f.equals(StandardNames.DISABLE_OUTPUT_ESCAPING)) {
+ disableAtt = Whitespace.trim(atts.getValue(a));
+ } else if (f.equals(StandardNames.SELECT)) {
+ selectAtt = atts.getValue(a);
+ } else if (f.equals(StandardNames.SEPARATOR)) {
+ separatorAtt = atts.getValue(a);
+ } else {
+ checkUnknownAttribute(atts.getNodeName(a));
+ }
+ }
+
+ if (selectAtt!=null) {
+ select = makeExpression(selectAtt);
+ }
+
+ if (separatorAtt != null) {
+ separator = makeAttributeValueTemplate(separatorAtt);
+ }
+
+ if (disableAtt != null) {
+ if (disableAtt.equals("yes")) {
+ disable = true;
+ } else if (disableAtt.equals("no")) {
+ disable = false;
+ } else {
+ compileError("disable-output-escaping attribute must be either 'yes' or 'no'", "XTSE0020");
+ }
+ }
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (select == null && iterateAxis(AxisInfo.CHILD).next() == null && !isXslt30Processor()) {
+ compileError("In XSLT 2.0, the xsl:value-of element must either have a select attribute, or have non-empty content",
+ "XTSE0870");
+ }
+ super.validate(decl);
+ select = typeCheck("select", select);
+ separator = typeCheck("separator", separator);
+ }
+
+ /**
+ * Get the error code to be returned when the element has a select attribute but is not empty.
+ *
+ * @return the error code defined for this condition, for this particular instruction
+ */
+
+ protected String getErrorCodeForSelectPlusContent() {
+ return "XTSE0870";
+ }
+
+ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ final TypeHierarchy th = getConfiguration().getTypeHierarchy();
+ if (separator == null && select != null && xPath10ModeIsEnabled()) {
+ if (!select.getItemType(th).isPlainType()) {
+ select = Atomizer.makeAtomizer(select);
+ select = makeExpressionVisitor().simplify(select);
+ }
+ if (Cardinality.allowsMany(select.getCardinality())) {
+ select = FirstItemExpression.makeFirstItemExpression(select);
+ }
+ if (!th.isSubType(select.getItemType(th), BuiltInAtomicType.STRING)) {
+ select = new AtomicSequenceConverter(select, BuiltInAtomicType.STRING);
+ ((AtomicSequenceConverter)select).allocateConverter(getConfiguration(), false);
+ }
+ } else {
+ if (separator == null) {
+ if (select == null) {
+ separator = new StringLiteral(StringValue.EMPTY_STRING);
+ } else {
+ separator = new StringLiteral(StringValue.SINGLE_SPACE);
+ }
+ }
+ }
+ ValueOf inst = new ValueOf(select, disable, false);
+ compileContent(exec, decl, inst, separator);
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/XSLWhen.java b/sf/saxon/style/XSLWhen.java
new file mode 100644
index 0000000..44f7d8b
--- /dev/null
+++ b/sf/saxon/style/XSLWhen.java
@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+
+
+/**
+* Handler for xsl:when elements in stylesheet. <br>
+* The xsl:while element has a mandatory attribute test, a boolean expression.
+*/
+
+public class XSLWhen extends StyleElement {
+
+ private Expression test;
+
+ public Expression getCondition() {
+ return test;
+ }
+
+ /**
+ * Determine the type of item returned by this instruction (only relevant if
+ * it is an instruction).
+ * @return the item type returned
+ */
+
+ protected ItemType getReturnedItemType() {
+ return getCommonChildItemType();
+ }
+
+ public void prepareAttributes() throws XPathException {
+ test = XSLIf.prepareTestAttribute(this);
+ if (test==null) {
+ reportAbsence("test");
+ }
+ }
+
+ /**
+ * Determine whether this type of element is allowed to contain a template-body
+ * @return true: yes, it may contain a template-body
+ */
+
+ public boolean mayContainSequenceConstructor() {
+ return true;
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ if (!(getParent() instanceof XSLChoose)) {
+ compileError("xsl:when must be immediately within xsl:choose", "XTSE0010");
+ }
+ test = typeCheck("test", test);
+ }
+
+ /**
+ * Mark tail-recursive calls on stylesheet functions. For most instructions, this does nothing.
+ */
+
+ public boolean markTailCalls() {
+ StyleElement last = getLastChildInstruction();
+ return last != null && last.markTailCalls();
+ }
+
+ /*@Nullable*/ public Expression compile(Executable exec, Declaration decl) throws XPathException {
+ return null;
+ // compilation is handled from the xsl:choose element
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/style/XSLWithParam.java b/sf/saxon/style/XSLWithParam.java
new file mode 100644
index 0000000..458e272
--- /dev/null
+++ b/sf/saxon/style/XSLWithParam.java
@@ -0,0 +1,83 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.style;
+
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.WithParam;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.value.SequenceType;
+
+/**
+* An xsl:with-param element in the stylesheet. <br>
+* The xsl:with-param element has mandatory attribute name and optional attribute select
+*/
+
+public class XSLWithParam extends XSLGeneralVariable {
+
+ private int allowedAttributes =
+ SourceBinding.SELECT | SourceBinding.AS | SourceBinding.TUNNEL;
+
+ @Override
+ protected void prepareAttributes() throws XPathException {
+ sourceBinding.prepareAttributes(allowedAttributes);
+ }
+
+ public boolean isTunnelParam() {
+ return sourceBinding.hasProperty(SourceBinding.TUNNEL);
+ }
+
+ public void validate(Declaration decl) throws XPathException {
+ super.validate(decl);
+
+ // Check for duplicate parameter names
+
+ AxisIterator iter = iterateAxis(AxisInfo.PRECEDING_SIBLING);
+ while (true) {
+ Item prev = iter.next();
+ if (prev == null) {
+ break;
+ }
+ if (prev instanceof XSLWithParam) {
+ if (sourceBinding.getVariableQName().equals(((XSLWithParam)prev).sourceBinding.getVariableQName())) {
+ compileError("Duplicate parameter name", "XTSE0670");
+ }
+ }
+ }
+
+ // Register that the stylesheet uses tunnel parameters
+ if (sourceBinding.hasProperty(SourceBinding.TUNNEL)) {
+ getPreparedStylesheet().setUsesTunnelParameters();
+ }
+
+ }
+
+ public void checkAgainstRequiredType(SequenceType required) throws XPathException {
+ sourceBinding.checkAgainstRequiredType(required);
+ }
+
+ /*@NotNull*/ public WithParam compileWithParam(Executable exec, Declaration decl) throws XPathException {
+ PrincipalStylesheetModule psm = getPrincipalStylesheetModule();
+
+ sourceBinding.handleSequenceConstructor(exec, decl);
+
+ WithParam inst = new WithParam();
+ //inst.adoptChildExpression(select);
+ inst.setSelectExpression(sourceBinding.getSelectExpression());
+ inst.setParameterId(psm.allocateUniqueParameterNumber(sourceBinding.getVariableQName()));
+ inst.setVariableQName(sourceBinding.getVariableQName());
+ inst.setTunnel(sourceBinding.hasProperty(SourceBinding.TUNNEL));
+ inst.setRequiredType(sourceBinding.getInferredType(true));
+ //initializeBinding(exec, decl, inst);
+ return inst;
+ }
+
+}
+
diff --git a/sf/saxon/style/package.html b/sf/saxon/style/package.html
new file mode 100644
index 0000000..6fd1bbe
--- /dev/null
+++ b/sf/saxon/style/package.html
@@ -0,0 +1,48 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview: net.sf.saxon.style</title>
+
+</head>
+
+
+<body>
+
+<p>This package provides classes used during the compilation of an XSLT stylesheet.
+The instances of these classes are discarded once compilation is complete, and they play no role
+in executing the transformation at run-time, except when tracing and debugging are invoked.</p>
+
+<p>The class <b>StyleElement</b> represents an element node on the stylesheet tree. Subclasses
+of StyleElement represent individual stylesheet elements, and are generally named according to
+the XSLT element name, for example XSLApplyTemplates, XSLChoose. The class <b>XSLStylesheet</b>
+is used for the <code>xsl:stylesheet</code> element in each stylesheet module, and in particular for the
+<code>xsl:stylesheet</code> element in the principal stylesheet module.</p>
+
+<p>During construction of the stylesheet tree, the class <b>StyleNodeFactory</b> is nominated to
+the <b>Builder</b> as the factory class responsible for creating element nodes on the tree. It is
+this class that decides which subclass of StyleElement to use for each element appearing in the
+stylesheet. For extension elements, the decision is delegated to a user-created
+<b>ExtensionElementFactory</b>.</p>
+
+<p>Each class provides a number of methods supporting the various phases of processing. The sequence
+of events sometimes varies slightly, but in general the first phase is done by <code>prepareAttributes</code>,
+which performs local validation of the attributes of each instruction. The second phase is represented
+by the <code>validate</code> method, which does global validation, fixup of references, and type checking.
+The third phase is done by the <code>compile</code> method, which generates <code>Instruction</code> and
+<code>Expression</code> objects. Further processing (local and global optimization) is then done on these
+Instruction objects, and is no longer the responsibility of this package.</p>
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/sxpath/AbstractStaticContext.java b/sf/saxon/sxpath/AbstractStaticContext.java
new file mode 100644
index 0000000..5b88e40
--- /dev/null
+++ b/sf/saxon/sxpath/AbstractStaticContext.java
@@ -0,0 +1,477 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.sxpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.expr.CollationMap;
+import net.sf.saxon.expr.EarlyEvaluationContext;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.LocationMap;
+import net.sf.saxon.functions.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.DecimalFormatManager;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.DecimalValue;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+import java.util.List;
+
+/**
+ * An abstract and configurable implementation of the StaticContext interface,
+ * which defines the static context of an XPath expression.
+ *
+ * <p>This class implements those parts of the functionality of a static context
+ * that tend to be common to most implementations: simple-valued properties such
+ * as base URI and default element namespace; availability of the standard
+ * function library; and support for collations.</p>
+*/
+
+public abstract class AbstractStaticContext implements StaticContext {
+
+ private String baseURI = null;
+ private Configuration config;
+ private LocationMap locationMap = new LocationMap();
+ private CollationMap collationMap;
+ private FunctionLibraryList libraryList = new FunctionLibraryList();
+ private String defaultFunctionNamespace = NamespaceConstant.FN;
+ private String defaultElementNamespace = NamespaceConstant.NULL;
+ private DecimalFormatManager decimalFormatManager = null;
+ private boolean backwardsCompatible = false;
+ private DecimalValue xpathLanguageLevel = DecimalValue.TWO;
+ private boolean schemaAware = false;
+ protected boolean usingDefaultFunctionLibrary;
+
+ /**
+ * Set the Configuration. This is protected so it can be used only by subclasses;
+ * the configuration will normally be set at construction time
+ * @param config the configuration
+ */
+
+ protected void setConfiguration(Configuration config) {
+ this.config = config;
+ collationMap = new CollationMap(config.getCollationMap());
+ }
+
+ /**
+ * Get the system configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Set a collation map to override use of the default collation map from the configuration
+ * @param collationMap the collation map to be used
+ */
+
+ public void setCollationMap(CollationMap collationMap) {
+ this.collationMap = collationMap;
+ }
+
+ /**
+ * Get the collation map in use
+ * @return the collation map
+ */
+
+ public CollationMap getCollationMap() {
+ return collationMap;
+ }
+
+ /**
+ * Say whether this static context is schema-aware
+ * @param aware true if this static context is schema-aware
+ */
+
+ public void setSchemaAware(boolean aware) {
+ schemaAware = aware;
+ }
+
+ /**
+ * Ask whether this static context is schema-aware
+ * @return true if this context is schema-aware
+ */
+
+ public boolean isSchemaAware() {
+ return schemaAware;
+ }
+
+ /**
+ * Initialize the default function library for XPath.
+ * This can be overridden using setFunctionLibrary().
+ */
+
+ protected final void setDefaultFunctionLibrary() {
+ FunctionLibraryList lib = new FunctionLibraryList();
+ int features = (getXPathLanguageLevel().equals(DecimalValue.THREE) ?
+ (StandardFunction.CORE | StandardFunction.XPATH30) :
+ (StandardFunction.CORE));
+ lib.addFunctionLibrary(
+ SystemFunctionLibrary.getSystemFunctionLibrary(features));
+ lib.addFunctionLibrary(getConfiguration().getVendorFunctionLibrary());
+ lib.addFunctionLibrary(new ConstructorFunctionLibrary(getConfiguration()));
+ lib.addFunctionLibrary(config.getIntegratedFunctionLibrary());
+ config.addExtensionBinders(lib);
+ setFunctionLibrary(lib);
+ }
+
+ /**
+ * Add a function library to the list of function libraries
+ * @param library the function library to be added
+ */
+
+ protected final void addFunctionLibrary(FunctionLibrary library) {
+ //FunctionLibrary libraryList = executable.getFunctionLibrary();
+ //if (libraryList instanceof FunctionLibraryList) {
+ ((FunctionLibraryList)libraryList).addFunctionLibrary(library);
+ //} else {
+ // throw new IllegalStateException("Registered function library cannot be extended");
+ //}
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ *
+ * @return the value {@link net.sf.saxon.Configuration#XPATH}
+ */
+
+ public int getHostLanguage() {
+ return Configuration.XPATH;
+ }
+
+ /**
+ * Construct a dynamic context for early evaluation of constant subexpressions
+ */
+
+ public XPathContext makeEarlyEvaluationContext() {
+ return new EarlyEvaluationContext(getConfiguration(), collationMap);
+ }
+
+
+ public LocationMap getLocationMap() {
+ return locationMap;
+ }
+
+ /**
+ * Set the location map, which is used for translating location identifiers into URIs and line
+ * numbers
+ * @param locationMap the location map to be used
+ */
+
+ public void setLocationMap(LocationMap locationMap) {
+ this.locationMap = locationMap;
+ }
+
+ /**
+ * Set the base URI in the static context
+ * @param baseURI the base URI of the expression; the value null is allowed to indicate that the base URI is not available.
+ */
+
+ public void setBaseURI(String baseURI) {
+ this.baseURI = baseURI;
+ }
+
+ /**
+ * Get the Base URI, for resolving any relative URI's used
+ * in the expression. Used by the document() function, resolve-uri(), etc.
+ * @return "" if no base URI has been set
+ */
+
+ public String getBaseURI() {
+ return baseURI==null ? "" : baseURI;
+ }
+
+ /**
+ * Get the function library containing all the in-scope functions available in this static
+ * context. This method is called by the XPath parser when binding a function call in the
+ * XPath expression to an implementation of the function.
+ */
+
+ public FunctionLibrary getFunctionLibrary() {
+ return libraryList;
+ }
+
+ /**
+ * Set the function library to be used
+ * @param lib the function library
+ */
+
+ public void setFunctionLibrary(FunctionLibraryList lib) {
+ libraryList = lib;
+ usingDefaultFunctionLibrary = false;
+ }
+
+ /**
+ * Declare a named collation
+ * @param name The name of the collation (technically, a URI)
+ * @param comparator The StringCollator used to implement the collating sequence
+ * @param isDefault True if this is to be used as the default collation
+ */
+
+ public void declareCollation(String name, StringCollator comparator, boolean isDefault) {
+ CollationMap collations = collationMap;
+ collations.setNamedCollation(name, comparator);
+ if (isDefault) {
+ collations.setDefaultCollationName(name);
+ }
+ }
+
+
+ /**
+ * Get a named collation.
+ * @return the collation identified by the given name, as set previously using declareCollation.
+ * Return null if no collation with this name is found.
+ */
+
+ public StringCollator getCollation(String name) {
+ return collationMap.getNamedCollation(name);
+ }
+
+ /**
+ * Get the name of the default collation.
+ * @return the name of the default collation; or the name of the codepoint collation
+ * if no default collation has been defined
+ */
+
+ public String getDefaultCollationName() {
+ return collationMap.getDefaultCollationName();
+ }
+
+ /**
+ * Get the NamePool used for compiling expressions
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ /**
+ * Issue a compile-time warning. This method is used during XPath expression compilation to
+ * output warning conditions. The default implementation writes the message to the
+ * error listener registered with the Configuration.
+ */
+
+ public void issueWarning(String s, SourceLocator locator) {
+ try {
+ config.getErrorListener().warning(new XPathException(s));
+ } catch (TransformerException e) {
+ config.getStandardErrorOutput().println("Warning: " + s);
+ }
+ }
+
+ /**
+ * Get the system ID of the container of the expression. Used to construct error messages.
+ * @return "" always
+ */
+
+ public String getSystemId() {
+ return "";
+ }
+
+
+ /**
+ * Get the line number of the expression within that container.
+ * Used to construct error messages.
+ * @return -1 always
+ */
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+
+ /**
+ * Get the default namespace URI for elements and types
+ * Return NamespaceConstant.NULL (that is, the zero-length string) for the non-namespace
+ * @return the default namespace for elements and type
+ */
+
+ public String getDefaultElementNamespace() {
+ return defaultElementNamespace;
+ }
+
+ /**
+ * Set the default namespace for elements and types
+ * @param uri the namespace to be used for unprefixed element and type names.
+ * The value "" (or NamespaceConstant.NULL) represents the non-namespace
+ */
+
+ public void setDefaultElementNamespace(String uri) {
+ defaultElementNamespace = uri;
+ }
+
+ /**
+ * Set the default function namespace
+ * @param uri the namespace to be used for unprefixed function names.
+ * The value "" (or NamespaceConstant.NULL) represents the non-namespace
+ */
+
+ public void setDefaultFunctionNamespace(String uri) {
+ defaultFunctionNamespace = uri;
+ }
+
+ /**
+ * Get the default function namespace.
+ * The value "" (or NamespaceConstant.NULL) represents the non-namespace
+ * @return the default namesapce for functions
+ */
+
+ public String getDefaultFunctionNamespace() {
+ return defaultFunctionNamespace;
+ }
+
+ /**
+ * Set the XPath language level supported, as a string.
+ * The current levels supported are 2.0, and 3.0. The default is 2.0.
+ * If running XQuery 1.0, the value is "2.0"; if running XQuery 3.0, it is 3.0.
+ * @param level the XPath language level
+ * @since 9.3
+ */
+
+ public void setXPathLanguageLevel(DecimalValue level) {
+ xpathLanguageLevel = level;
+ if (usingDefaultFunctionLibrary) {
+ FunctionLibraryList liblist = (FunctionLibraryList)getFunctionLibrary();
+ List<FunctionLibrary> list = liblist.getLibraryList();
+ for (int i=0; i<list.size(); i++) {
+ if (list.get(i) instanceof SystemFunctionLibrary) {
+ int features = (getXPathLanguageLevel().equals(DecimalValue.THREE) ?
+ (StandardFunction.CORE | StandardFunction.XPATH30) :
+ (StandardFunction.CORE));
+ list.set(i, SystemFunctionLibrary.getSystemFunctionLibrary(features));
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the XPath language level supported, as a string.
+ * The current levels supported are "2.0", and "3.0". The default is "2.0".
+ * If running XQuery 1.0, the value is "2.0"; if running XQuery 3.0, it is "3.0".
+ * @return the XPath language level
+ * @since 9.3
+ */
+
+ public DecimalValue getXPathLanguageLevel() {
+ return xpathLanguageLevel;
+ }
+
+ /**
+ * Set XPath 1.0 backwards compatibility mode on or off
+ * @param option true if XPath 1.0 compatibility mode is to be set to true;
+ * otherwise false
+ */
+
+ public void setBackwardsCompatibilityMode(boolean option) {
+ backwardsCompatible = option;
+ }
+
+ /**
+ * Determine whether Backwards Compatible Mode is used
+ * @return true if XPath 1.0 compatibility mode is to be set to true;
+ * otherwise false
+ */
+
+ public boolean isInBackwardsCompatibleMode() {
+ return backwardsCompatible;
+ }
+
+ /**
+ * Determine whether a built-in type is available in this context. This method caters for differences
+ * between host languages as to which set of types are built in.
+ *
+ * @param type the supposedly built-in type. This will always be a type in the XS namespace.
+ * @return true if this type can be used in this static context
+ */
+
+ public boolean isAllowedBuiltInType(BuiltInAtomicType type) {
+ return type.getFingerprint() != StandardNames.XS_DATE_TIME_STAMP ||
+ config.getXsdVersion() == Configuration.XSD11;
+ }
+
+ /**
+ * Set the DecimalFormatManager used to resolve the names of decimal formats used in calls
+ * to the format-number() function.
+ * @param manager the decimal format manager for this static context, or null if no named decimal
+ * formats are available in this environment.
+ */
+
+ public void setDecimalFormatManager(DecimalFormatManager manager) {
+ this.decimalFormatManager = manager;
+ }
+
+ /**
+ * Get the required type of the context item. If no type has been explicitly declared for the context
+ * item, an instance of AnyItemType (representing the type item()) is returned.
+ * @return the required type of the context item
+ * @since 9.3
+ */
+
+ public ItemType getRequiredContextItemType() {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Get a DecimalFormatManager to resolve the names of decimal formats used in calls
+ * to the format-number() function.
+ * @return the decimal format manager for this static context; a newly created empty
+ * DecimalFormatManager if none has been supplied
+ * @since 9.2
+ */
+
+ public DecimalFormatManager getDecimalFormatManager() {
+ if (decimalFormatManager == null) {
+ decimalFormatManager = new DecimalFormatManager();
+ }
+ return decimalFormatManager;
+ }
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ * @return the LocationProvider that translates location identifiers into URIs and line numbers
+ */
+
+ public LocationProvider getLocationProvider() {
+ return locationMap;
+ }
+
+ /**
+ * Return the public identifier.
+ * <p/>
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup that
+ * triggered the event appears.</p>
+ *
+ * @return null (always).
+ * @see #getSystemId
+ */
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Return the character position where the current document event ends.
+ * @return -1 (no column number is available).
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return -1;
+ }
+
+}
+
diff --git a/sf/saxon/sxpath/DedicatedStaticContext.java b/sf/saxon/sxpath/DedicatedStaticContext.java
new file mode 100644
index 0000000..39b1689
--- /dev/null
+++ b/sf/saxon/sxpath/DedicatedStaticContext.java
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.sxpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.functions.FunctionLibraryList;
+import net.sf.saxon.om.StructuredQName;
+
+import java.util.HashMap;
+
+/**
+ * This implementation of the XPath static context is mainly used when XPath expressions are hosted
+ * in other Saxon processing contexts, for example in xsl:evaluate, or in XSD assertions
+ */
+public class DedicatedStaticContext extends IndependentContext implements Container {
+
+ private Executable executable;
+
+ public DedicatedStaticContext(Configuration config) {
+ super(config);
+ }
+
+ /**
+ * Create a DedicatedStaticContext as a copy of an IndependentContext
+ * @param ic the IndependentContext to be copied
+ */
+
+ public DedicatedStaticContext(IndependentContext ic) {
+ super(ic.getConfiguration());
+ setBaseURI(ic.getBaseURI());
+ setLocationMap(ic.getLocationMap());
+ setCollationMap(ic.getCollationMap());
+ setDefaultElementNamespace(ic.getDefaultElementNamespace());
+ setDefaultFunctionNamespace(ic.getDefaultFunctionNamespace());
+ setBackwardsCompatibilityMode(ic.isInBackwardsCompatibleMode());
+ setSchemaAware(ic.isSchemaAware());
+ namespaces = new HashMap<String, String>(ic.namespaces);
+ variables = new HashMap<StructuredQName, XPathVariable>(10);
+ FunctionLibraryList libList = (FunctionLibraryList)ic.getFunctionLibrary();
+ if (libList != null) {
+ setFunctionLibrary((FunctionLibraryList)libList.copy());
+ }
+ setImportedSchemaNamespaces(ic.importedSchemaNamespaces);
+ externalResolver = ic.externalResolver;
+ autoDeclare = ic.autoDeclare;
+ setXPathLanguageLevel(ic.getXPathLanguageLevel());
+ requiredContextItemType = ic.requiredContextItemType;
+ if (ic instanceof DedicatedStaticContext) {
+ setExecutable(((DedicatedStaticContext)ic).getExecutable());
+ }
+ }
+
+ public void setExecutable(Executable exec) {
+ executable = exec;
+ }
+
+ /*@Nullable*/ public Executable getExecutable() {
+ return executable;
+ }
+}
+
diff --git a/sf/saxon/sxpath/IndependentContext.java b/sf/saxon/sxpath/IndependentContext.java
new file mode 100644
index 0000000..5fc8505
--- /dev/null
+++ b/sf/saxon/sxpath/IndependentContext.java
@@ -0,0 +1,463 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.sxpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.VariableReference;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.QNameValue;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+* An IndependentContext provides a context for parsing an XPath expression appearing
+* in a context other than a stylesheet.
+ *
+ * <p>This class is used in a number of places where freestanding XPath expressions occur.
+ * These include the native Saxon XPath API, the .NET XPath API, XPath expressions used
+ * in XML Schema identity constraints, and XPath expressions supplied to saxon:evaluate().
+ * It is not used by the JAXP XPath API (though it shares code with that API through
+ * the common superclass AbstractStaticContext).</p>
+ *
+ * <p>This class currently provides no mechanism for binding user-defined functions.</p>
+*/
+
+public class IndependentContext extends AbstractStaticContext
+ implements XPathStaticContext, NamespaceResolver, Serializable {
+
+ protected HashMap<String, String> namespaces = new HashMap<String, String>(10);
+ protected HashMap<StructuredQName, XPathVariable> variables = new HashMap<StructuredQName, XPathVariable>(20);
+ protected NamespaceResolver externalResolver = null;
+ protected ItemType requiredContextItemType = AnyItemType.getInstance();
+ protected Set importedSchemaNamespaces = new HashSet();
+ protected boolean autoDeclare = false;
+
+
+ /**
+ * Create an IndependentContext along with a new (non-schema-aware) Saxon Configuration
+ */
+
+ public IndependentContext() {
+ this(new Configuration());
+ }
+
+ /**
+ * Create an IndependentContext using a specific Configuration
+ * @param config the Saxon configuration to be used
+ */
+
+ public IndependentContext(Configuration config) {
+ setConfiguration(config);
+ clearNamespaces();
+ setDefaultFunctionLibrary();
+ usingDefaultFunctionLibrary = true;
+ }
+
+ /**
+ * Get the granularity of the container.
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 1;
+ }
+
+ /**
+ * Declare a namespace whose prefix can be used in expressions
+ * @param prefix The namespace prefix. Must not be null. Supplying "" sets the
+ * default element namespace.
+ * @param uri The namespace URI. Must not be null.
+ */
+
+ public void declareNamespace(String prefix, String uri) {
+ if (prefix==null) {
+ throw new NullPointerException("Null prefix supplied to declareNamespace()");
+ }
+ if (uri==null) {
+ throw new NullPointerException("Null namespace URI supplied to declareNamespace()");
+ }
+ if ("".equals(prefix)) {
+ setDefaultElementNamespace(uri);
+ } else {
+ namespaces.put(prefix, uri);
+ }
+ }
+
+ /**
+ * Set the default namespace for elements and types
+ * @param uri the namespace to be used for unprefixed element and type names.
+ * The value "" (or NamespaceConstant.NULL) represents the non-namespace
+ */
+
+ @Override
+ public void setDefaultElementNamespace(String uri) {
+ super.setDefaultElementNamespace(uri);
+ namespaces.put("", uri);
+ }
+
+ /**
+ * Clear all the declared namespaces, except for the standard ones (xml, xslt, saxon, xdt).
+ * This also resets the default element namespace to the "null" namespace
+ */
+
+ public void clearNamespaces() {
+ namespaces.clear();
+ declareNamespace("xml", NamespaceConstant.XML);
+ declareNamespace("xsl", NamespaceConstant.XSLT);
+ declareNamespace("saxon", NamespaceConstant.SAXON);
+ declareNamespace("xs", NamespaceConstant.SCHEMA);
+ declareNamespace("", "");
+ }
+
+ /**
+ * Clear all the declared namespaces, including the standard ones (xml, xslt, saxon).
+ * Leave only the XML namespace and the default namespace (xmlns="").
+ * This also resets the default element namespace to the "null" namespace.
+ */
+
+ public void clearAllNamespaces() {
+ namespaces.clear();
+ declareNamespace("xml", NamespaceConstant.XML);
+ declareNamespace("", "");
+ }
+
+ /**
+ * Declares all the namespaces that are in-scope for a given node, removing all previous
+ * namespace declarations.
+ * In addition, the standard namespaces (xml, xslt, saxon) are declared. This method also
+ * sets the default element namespace to be the same as the default namespace for this node.
+ * @param node The node whose in-scope namespaces are to be used as the context namespaces.
+ * If the node is an attribute, text node, etc, then the namespaces of its parent element are used.
+ */
+
+ public void setNamespaces(NodeInfo node) {
+ namespaces.clear();
+ int kind = node.getNodeKind();
+ if (kind == Type.ATTRIBUTE || kind == Type.TEXT ||
+ kind == Type.COMMENT || kind == Type.PROCESSING_INSTRUCTION ||
+ kind == Type.NAMESPACE) {
+ node = node.getParent();
+ }
+ if (node == null) {
+ return;
+ }
+
+ AxisIterator iter = node.iterateAxis(AxisInfo.NAMESPACE);
+ while (true) {
+ NodeInfo ns = (NodeInfo)iter.next();
+ if (ns == null) {
+ return;
+ }
+ String prefix = ns.getLocalPart();
+ if ("".equals(prefix)) {
+ setDefaultElementNamespace(ns.getStringValue());
+ } else {
+ declareNamespace(ns.getLocalPart(), ns.getStringValue());
+ }
+ }
+ }
+
+ /**
+ * Set an external namespace resolver. If this is set, then all resolution of namespace
+ * prefixes is delegated to the external namespace resolver, and namespaces declared
+ * individually on this IndependentContext object are ignored.
+ * @param resolver the external NamespaceResolver
+ */
+
+ public void setNamespaceResolver(NamespaceResolver resolver) {
+ externalResolver = resolver;
+ }
+
+ /**
+ * Say whether undeclared variables are allowed. By default, they are not allowed. When
+ * undeclared variables are allowed, it is not necessary to predeclare the variables that
+ * may be used in the XPath expression; instead, a variable is automatically declared when a reference
+ * to the variable is encountered within the expression.
+ * @param allow true if undeclared variables are allowed, false if they are not allowed.
+ * @since 9.2
+ */
+
+ public void setAllowUndeclaredVariables(boolean allow) {
+ autoDeclare = allow;
+ }
+
+ /**
+ * Ask whether undeclared variables are allowed. By default, they are not allowed. When
+ * undeclared variables are allowed, it is not necessary to predeclare the variables that
+ * may be used in the XPath expression; instead, a variable is automatically declared when a reference
+ * to the variable is encountered within the expression.
+ * @return true if undeclared variables are allowed, false if they are not allowed.
+ * @since 9.2
+ */
+
+ public boolean isAllowUndeclaredVariables() {
+ return autoDeclare;
+ }
+ /**
+ * Declare a variable. A variable must be declared before an expression referring
+ * to it is compiled. The initial value of the variable will be the empty sequence
+ * @param qname The name of the variable
+ * @return an XPathVariable object representing information about the variable that has been
+ * declared.
+ */
+
+ public XPathVariable declareVariable(QNameValue qname) {
+ return declareVariable(qname.toStructuredQName());
+ }
+
+ /**
+ * Declare a variable. A variable must be declared before an expression referring
+ * to it is compiled. The initial value of the variable will be the empty sequence
+ * @param namespaceURI The namespace URI of the name of the variable. Supply "" to represent
+ * names in no namespace (null is also accepted)
+ * @param localName The local part of the name of the variable (an NCName)
+ * @return an XPathVariable object representing information about the variable that has been
+ * declared.
+ */
+
+ public XPathVariable declareVariable(String namespaceURI, String localName) {
+ StructuredQName qName = new StructuredQName("", namespaceURI, localName);
+ return declareVariable(qName);
+ }
+
+ /**
+ * Declare a variable. A variable must be declared before an expression referring
+ * to it is compiled. The initial value of the variable will be the empty sequence
+ * @param qName the name of the variable.
+ * @return an XPathVariable object representing information about the variable that has been
+ * declared.
+ * @since 9.2
+ */
+
+
+ public XPathVariable declareVariable(StructuredQName qName) {
+ XPathVariable var = variables.get(qName);
+ if (var != null) {
+ return var;
+ } else {
+ var = XPathVariable.make(qName);
+ int slot = variables.size();
+ var.setSlotNumber(slot);
+ variables.put(qName, var);
+ return var;
+ }
+ }
+
+ /**
+ * Get an iterator over all the variables that have been declared, either explicitly by an
+ * application call on declareVariable(), or implicitly if the option <code>allowUndeclaredVariables</code>
+ * is set.
+ * @return an iterator; the objects returned by this iterator will be instances of XPathVariable
+ * @since 9.2
+ */
+
+ public Iterator<XPathVariable> iterateExternalVariables() {
+ return variables.values().iterator();
+ }
+
+ /**
+ * Get the declared variable with a given name, if there is one
+ * @param qName the name of the required variable
+ * @return the explicitly or implicitly declared variable with this name if it exists,
+ * or null otherwise
+ * @since 9.2
+ */
+
+ public XPathVariable getExternalVariable(StructuredQName qName) {
+ return variables.get(qName);
+ }
+
+ /**
+ * Get the slot number allocated to a particular variable
+ * @param qname the name of the variable
+ * @return the slot number, or -1 if the variable has not been declared
+ */
+
+ public int getSlotNumber(QNameValue qname) {
+ StructuredQName sq = qname.toStructuredQName();
+ XPathVariable var = variables.get(sq);
+ if (var == null) {
+ return -1;
+ }
+ return var.getLocalSlotNumber();
+ }
+
+ /**
+ * Get the URI for a prefix, using the declared namespaces as
+ * the context for namespace resolution. The default namespace is NOT used
+ * when the prefix is empty.
+ * This method is provided for use by the XPath parser.
+ * @param prefix The prefix
+ * @throws net.sf.saxon.trans.XPathException if the prefix is not declared; the
+ * associated error code should be XPST0081
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(String prefix) throws XPathException {
+ String uri = getURIForPrefix(prefix, false);
+ if (uri==null) {
+ throw new XPathException("Prefix " + prefix + " has not been declared", "XPST0081");
+ }
+ return uri;
+ }
+
+ public NamespaceResolver getNamespaceResolver() {
+ if (externalResolver != null) {
+ return externalResolver;
+ } else {
+ return this;
+ }
+ }
+
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ * @param prefix the namespace prefix
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is ""
+ * @return the uri for the namespace, or null if the prefix is not in scope.
+ * Return "" if the prefix maps to the null namespace.
+ */
+
+ public String getURIForPrefix(String prefix, boolean useDefault) {
+ if (externalResolver != null) {
+ return externalResolver.getURIForPrefix(prefix, useDefault);
+ }
+ if (prefix.length() == 0) {
+ return useDefault ? getDefaultElementNamespace() : "";
+ } else {
+ return namespaces.get(prefix);
+ }
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ if (externalResolver != null) {
+ return externalResolver.iteratePrefixes();
+ } else {
+ return namespaces.keySet().iterator();
+ }
+ }
+
+ /**
+ * Bind a variable used in an XPath Expression to the XSLVariable element in which it is declared.
+ * This method is provided for use by the XPath parser, and it should not be called by the user of
+ * the API, or overridden, unless variables are to be declared using a mechanism other than the
+ * declareVariable method of this class.
+ * @param qName the name of the variable
+ * @return the resulting variable reference
+ */
+
+ public Expression bindVariable(StructuredQName qName) throws XPathException {
+ XPathVariable var = variables.get(qName);
+ if (var==null) {
+ if (autoDeclare) {
+ return new VariableReference(declareVariable(qName));
+ } else {
+ throw new XPathException("Undeclared variable in XPath expression: $" + qName.getClarkName(), "XPST0008");
+ }
+ } else {
+ return new VariableReference(var);
+ }
+ }
+
+ /**
+ * Get a Stack Frame Map containing definitions of all the declared variables. This will return a newly
+ * created object that the caller is free to modify by adding additional variables, without affecting
+ * the static context itself.
+ */
+
+ public SlotManager getStackFrameMap() {
+ SlotManager map = getConfiguration().makeSlotManager();
+ XPathVariable[] va = new XPathVariable[variables.size()];
+
+ for (Iterator<XPathVariable> v = variables.values().iterator(); v.hasNext();) {
+ XPathVariable var = v.next();
+ va[var.getLocalSlotNumber()] = var;
+ }
+ for (int i=0; i<va.length; i++) {
+ map.allocateSlotNumber(va[i].getVariableQName());
+ }
+ return map;
+ }
+
+
+
+ public boolean isImportedSchema(String namespace) {
+ return importedSchemaNamespaces.contains(namespace);
+ }
+
+ /**
+ * Get the set of imported schemas
+ * @return a Set, the set of URIs representing the names of imported schemas
+ */
+
+ public Set<String> getImportedSchemaNamespaces() {
+ return importedSchemaNamespaces;
+ }
+
+ /**
+ * Register the set of imported schema namespaces
+ * @param namespaces the set of namespaces for which schema components are available in the
+ * static context
+ */
+
+ public void setImportedSchemaNamespaces(Set namespaces) {
+ importedSchemaNamespaces = namespaces;
+ if (!namespaces.isEmpty()) {
+ setSchemaAware(true);
+ }
+ }
+
+ /**
+ * Declare the static type of the context item. If this type is declared, and if a context item
+ * is supplied when the query is invoked, then the context item must conform to this type (no
+ * type conversion will take place to force it into this type).
+ * @param type the required type of the context item
+ * @since 9.3
+ */
+
+ public void setRequiredContextItemType(ItemType type) {
+ requiredContextItemType = type;
+ }
+
+ /**
+ * Get the required type of the context item. If no type has been explicitly declared for the context
+ * item, an instance of AnyItemType (representing the type item()) is returned.
+ * @return the required type of the context item
+ * @since 9.3
+ */
+
+ public ItemType getRequiredContextItemType() {
+ return requiredContextItemType;
+ }
+
+
+}
+
diff --git a/sf/saxon/sxpath/SimpleContainer.java b/sf/saxon/sxpath/SimpleContainer.java
new file mode 100644
index 0000000..bfa96b7
--- /dev/null
+++ b/sf/saxon/sxpath/SimpleContainer.java
@@ -0,0 +1,151 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.sxpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.instruct.Executable;
+
+/**
+ * A simple container for standalone XPath expressions
+ */
+public class SimpleContainer implements Container {
+
+ private Executable executable;
+ private String systemId = null;
+ private int lineNumber = -1;
+
+ public SimpleContainer(Executable exec) {
+ this.executable = exec;
+ }
+
+ /**
+ * Set location information if available
+ * @param systemId the system Id
+ * @param lineNumber the line number
+ */
+
+ public void setLocation(String systemId, int lineNumber) {
+ this.systemId = systemId;
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Get the Executable (representing a complete stylesheet or query) of which this Container forms part
+ *
+ * @return the executable
+ */
+ public Executable getExecutable() {
+ return executable;
+ }
+
+ /**
+ * Get the LocationProvider allowing location identifiers to be resolved.
+ *
+ * @return the location provider
+ */
+ public LocationProvider getLocationProvider() {
+ return executable.getLocationMap();
+ }
+
+ /**
+ * Get the host language (XSLT, XQuery, XPath) used to implement the code in this container
+ *
+ * @return typically {@link net.sf.saxon.Configuration#XSLT} or {@link net.sf.saxon.Configuration#XQUERY}
+ */
+ public int getHostLanguage() {
+ return Configuration.XPATH;
+ }
+
+ /**
+ * Get the granularity of the container. During successive phases of compilation, growing
+ * expression trees are rooted in containers of increasing granularity. The granularity
+ * of the container is used to avoid "repotting" a tree more frequently than is required,
+ * as this requires a complete traversal of the tree which can take a measurable time.
+ *
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+ public int getContainerGranularity() {
+ return 1;
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ * <p/>
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup that
+ * triggered the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Return the system identifier for the current document event.
+ * <p/>
+ * <p>The return value is the system identifier of the document
+ * entity or of the external parsed entity in which the markup that
+ * triggered the event appears.</p>
+ * <p/>
+ * <p>If the system identifier is a URL, the parser must resolve it
+ * fully before passing it to the application.</p>
+ *
+ * @return A string containing the system identifier, or null
+ * if none is available.
+ * @see #getPublicId
+ */
+ /*@Nullable*/ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Return the line number where the current document event ends.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of error
+ * reporting; it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.</p>
+ * <p/>
+ * <p>The return value is an approximation of the line number
+ * in the document entity or external parsed entity where the
+ * markup that triggered the event appears.</p>
+ *
+ * @return The line number, or -1 if none is available.
+ * @see #getColumnNumber
+ */
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Return the character position where the current document event ends.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of error
+ * reporting; it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.</p>
+ * <p/>
+ * <p>The return value is an approximation of the column number
+ * in the document entity or external parsed entity where the
+ * markup that triggered the event appears.</p>
+ *
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return -1;
+ }
+}
+
diff --git a/sf/saxon/sxpath/XPathDynamicContext.java b/sf/saxon/sxpath/XPathDynamicContext.java
new file mode 100644
index 0000000..a943732
--- /dev/null
+++ b/sf/saxon/sxpath/XPathDynamicContext.java
@@ -0,0 +1,272 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.sxpath;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.TypeChecker;
+import net.sf.saxon.lib.CollectionURIResolver;
+import net.sf.saxon.lib.UnparsedTextURIResolver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.SequenceType;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.URIResolver;
+
+/**
+ * This object represents the dynamic XPath execution context for use in the free-standing Saxon XPath API.
+ * The dynamic context holds the context item and the values of external variables used by the XPath expression.
+ *
+ * <p>This object is always created via the method
+ * {@link net.sf.saxon.sxpath.XPathExpression#createDynamicContext(net.sf.saxon.om.Item)}</p>
+ */
+public class XPathDynamicContext {
+
+ private ItemType contextItemType;
+ private XPathContextMajor contextObject;
+ private SlotManager stackFrameMap;
+
+ protected XPathDynamicContext(ItemType contextItemType, XPathContextMajor contextObject, SlotManager stackFrameMap) {
+ this.contextItemType = contextItemType;
+ this.contextObject = contextObject;
+ this.stackFrameMap = stackFrameMap;
+ }
+
+ /**
+ * Set the context item to a node derived from a supplied Source object. This may be
+ * any implementation of the Source interface recognized by Saxon. Note that the
+ * Saxon {@link NodeInfo} interface, representing a node in a tree, is one such
+ * implementation; others include {@link javax.xml.transform.stream.StreamSource},
+ * {@link javax.xml.transform.sax.SAXSource}, and {@link javax.xml.transform.dom.DOMSource}
+ *
+ * @param source The source object representing the node that will be used as the context item
+ * @throws XPathException if a failure occurs reading or parsing a Source object to build an input tree,
+ * or if the source is a document that was built under the wrong configuration
+ */
+
+ public void setContextNode(Source source) throws XPathException {
+ NodeInfo origin;
+ if (source instanceof NodeInfo) {
+ origin = (NodeInfo)source;
+ if (!origin.getConfiguration().isCompatible(contextObject.getConfiguration())) {
+ throw new XPathException(
+ "Supplied node must be built using the same or a compatible Configuration",
+ SaxonErrorCode.SXXP0004);
+ }
+ } else {
+ origin = contextObject.getConfiguration().buildDocument(source);
+ }
+ setContextItem(origin);
+ }
+
+ /**
+ * Set the context item for evaluation of the XPath Expression
+ * @param item the context item
+ * @throws XPathException if the node is in a document that was built under the wrong configuration
+ */
+
+ public void setContextItem(Item item) throws XPathException {
+ if (item instanceof NodeInfo) {
+ if (!((NodeInfo)item).getConfiguration().isCompatible(contextObject.getConfiguration())) {
+ throw new XPathException(
+ "Supplied node must be built using the same or a compatible Configuration",
+ SaxonErrorCode.SXXP0004);
+ }
+ }
+ if (!contextItemType.matchesItem(item, false, contextObject.getConfiguration())) {
+ throw new XPathException("Supplied context item does not match required context item type " +
+ contextItemType.toString());
+ }
+ UnfailingIterator iter = SingletonIterator.makeIterator(item);
+ iter.next();
+ contextObject.setCurrentIterator(iter);
+ }
+
+ /**
+ * Get the context item
+ * @return the context item if there is one, or null otherwise
+ */
+
+ public Item getContextItem() {
+ return contextObject.getContextItem();
+ }
+
+ /**
+ * Set the value of an external variable used within the XPath expression
+ * @param variable the object representing the variable, as returned by the
+ * {@link net.sf.saxon.sxpath.XPathEvaluator#declareVariable(String, String)} method.
+ * Note that setting the value of a variable does not modify the {@link XPathVariable}
+ * object itself, which means that this method is thread-safe.
+ * @param value The value of the variable.
+ * @throws XPathException if the supplied value does not conform to the required type of the
+ * variable; or if the supplied value contains a node that does not belong to this Configuration
+ * (or another Configuration that shares the same namePool)
+ */
+
+ public void setVariable(XPathVariable variable, Sequence value) throws XPathException {
+ SequenceType requiredType = variable.getRequiredType();
+ if (requiredType != SequenceType.ANY_SEQUENCE) {
+ XPathException err = TypeChecker.testConformance(value, requiredType, contextObject);
+ if (err != null) {
+ throw err;
+ }
+ }
+ SequenceIterator iter = value.iterate();
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ if (item instanceof NodeInfo) {
+ if (!((NodeInfo)item).getConfiguration().isCompatible(contextObject.getConfiguration())) {
+ throw new XPathException(
+ "Supplied node must be built using the same or a compatible Configuration",
+ SaxonErrorCode.SXXP0004);
+ }
+ }
+ }
+ int slot = variable.getLocalSlotNumber();
+ StructuredQName expectedName = (slot >= stackFrameMap.getNumberOfVariables() ? null :
+ stackFrameMap.getVariableMap().get(slot));
+ if (!variable.getVariableQName().equals(expectedName)) {
+ throw new XPathException(
+ "Supplied XPathVariable is bound to the wrong slot: perhaps it was created using a different static context");
+ }
+ contextObject.setLocalVariable(slot, value);
+ }
+
+ /**
+ * Set an object that will be used to resolve URIs used in
+ * document(), etc.
+ *
+ * @param resolver An object that implements the URIResolver interface, or
+ * null.
+ * @since 9.2
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ contextObject.getController().setURIResolver(resolver);
+ }
+
+ /**
+ * Get the URI resolver.
+ *
+ * @return the user-supplied URI resolver if there is one, or null otherwise.
+ * @since 9.2
+ */
+
+ public URIResolver getURIResolver() {
+ return contextObject.getController().getURIResolver();
+ }
+
+ /**
+ * Set the CollectionURIResolver used for resolving collection URIs.
+ * Defaults to the CollectionURIResolver registered with the Configuration
+ * @param resolver the resolver for references to collections
+ * @since 9.4
+ */
+
+ public void setCollectionURIResolver(CollectionURIResolver resolver) {
+ contextObject.getController().setCollectionURIResolver(resolver);
+ }
+
+ /**
+ * Get the CollectionURIResolver used for resolving references to collections.
+ * If none has been set on the Controller, returns the
+ * CollectionURIResolver registered with the Configuration
+ * @return the resolver for references to collections
+ * @since 9.4
+ */
+
+ public CollectionURIResolver getCollectionURIResolver() {
+ return contextObject.getController().getCollectionURIResolver();
+ }
+
+
+ /**
+ * Set the error listener.
+ *
+ * @param listener the ErrorListener to be used
+ * @since 9.2
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ contextObject.getController().setErrorListener(listener);
+ }
+
+ /**
+ * Get the error listener.
+ *
+ * @return the ErrorListener in use
+ * @since 9.2
+ */
+
+ public ErrorListener getErrorListener() {
+ return contextObject.getController().getErrorListener();
+ }
+
+
+ /**
+ * For system use: get the wrapped XPathContext object
+ * @return the underlying XPathContext object
+ */
+
+ public XPathContext getXPathContextObject() {
+ return contextObject;
+ }
+
+ /**
+ * Set an object that will be used to resolve URIs used in
+ * fn:unparsed-text() and related functions.
+ *
+ * @param resolver An object that implements the UnparsedTextURIResolver interface, or
+ * null.
+ * @since 9.5
+ */
+
+ public void setUnparsedTextURIResolver(UnparsedTextURIResolver resolver) {
+ contextObject.getController().setUnparsedTextURIResolver(resolver);
+ }
+
+ /**
+ * Get the URI resolver for unparsed text.
+ *
+ * @return the user-supplied unparsed text URI resolver if there is one, or the
+ * system-defined one otherwise
+ * @since 9.5
+ */
+
+ public UnparsedTextURIResolver getUnparsedTextURIResolver() {
+ return contextObject.getController().getUnparsedTextURIResolver();
+ }
+
+ /**
+ * Check that all external variables have been given a value
+ * @param stackFrameMap describes the stack frame
+ * @param numberOfExternals the number of variables that need to be supplied
+ * @throws XPathException if required variables have not been given a value
+ */
+
+ protected void checkExternalVariables(/*@NotNull*/ SlotManager stackFrameMap, int numberOfExternals) throws XPathException {
+ Sequence[] stack = contextObject.getStackFrame().getStackFrameValues();
+ for (int i=0; i<numberOfExternals; i++) {
+ if (stack[i] == null) {
+ StructuredQName qname = stackFrameMap.getVariableMap().get(i);
+ throw new XPathException("No value has been supplied for variable $" + qname.getDisplayName());
+ }
+ }
+ }
+}
+
diff --git a/sf/saxon/sxpath/XPathEvaluator.java b/sf/saxon/sxpath/XPathEvaluator.java
new file mode 100644
index 0000000..8d7e94e
--- /dev/null
+++ b/sf/saxon/sxpath/XPathEvaluator.java
@@ -0,0 +1,227 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.sxpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.functions.*;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.pattern.Pattern;
+import net.sf.saxon.pattern.PatternSponsor;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.DecimalValue;
+
+/**
+ * This class provides a native Saxon API for free-standing evaluation of XPath expressions. Unlike the
+ * JAXP API offered by {@link net.sf.saxon.xpath.XPathEvaluator} it exposes Saxon classes and interfaces
+ * and thus provides a more strongly-typed interface with greater control over the detailed behaviour.
+ *
+ * <p>However, the preferred API for user applications calling XPath is the s9api {@link net.sf.saxon.s9api.XPathCompiler}
+ * interface.</p>
+ *
+ * @author Michael H. Kay
+ * @since 8.4
+ */
+
+public class XPathEvaluator {
+
+ private XPathStaticContext staticContext;
+
+ /**
+ * Default constructor. Creates an XPathEvaluator with a default configuration and name pool.
+ * Note that any source documents used by an XPath expression under this XPathEvaluator must
+ * be built using the {@link Configuration} that is implicitly created by this constructor,
+ * which is accessible using the {@link #getConfiguration} method.
+ */
+
+ public XPathEvaluator() {
+ this(new Configuration());
+ }
+
+ /**
+ * Construct an XPathEvaluator with a specified configuration.
+ * @param config the configuration to be used. If the XPathEvaluator is
+ * to be used to run schema-aware XPath expressions this must be an instance
+ * of {@link com.saxonica.config.EnterpriseConfiguration}
+ */
+ public XPathEvaluator(Configuration config) {
+ staticContext = new IndependentContext(config);
+ }
+
+ /**
+ * Get the Configuration in use.
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return staticContext.getConfiguration();
+ }
+
+ /**
+ * Declare a variable, making it available for use within a subsequently-compiled XPath expression.
+ * The variable may subsequently be assigned a value using the method
+ * {@link XPathDynamicContext#setVariable(XPathVariable, net.sf.saxon.om.Sequence)}.
+ * Any external variable used in an XPath expression must be declared before the XPath expression
+ * is compiled.
+ * @param uri The namespace URI of the variable name. Use "" for the null namespace.
+ * @param localName The local part of the variable name.
+ * @return an object representing the variable
+ */
+
+ public XPathVariable declareVariable(String uri, String localName) {
+ return staticContext.declareVariable(uri, localName);
+ }
+
+ /**
+ * Set the static context for compiling XPath expressions. This provides more detailed control over the
+ * environment in which the expression is compiled, for example it allows namespace prefixes to
+ * be declared, variables to be bound and functions to be defined. For most purposes, the static
+ * context can be defined by providing and tailoring an instance of the {@link IndependentContext} class.
+ * Until this method is called, a default static context is used, in which no namespaces are defined
+ * other than the standard ones (xml, xslt, and saxon), and no variables or functions (other than the
+ * core XPath functions) are available.
+ * @param context the XPath static context
+ * <p>Setting a new static context clears any variables and namespaces that have previously been declared.</p>
+ */
+
+ public void setStaticContext(XPathStaticContext context) {
+ staticContext = context;
+ }
+
+ /**
+ * Get the current static context. This will always return a value; if no static context has been
+ * supplied by the user, the system will have created its own. A system-created static context
+ * will always be an instance of {@link IndependentContext}
+ * @return the static context object
+ */
+
+ public XPathStaticContext getStaticContext() {
+ return staticContext;
+ }
+
+ /**
+ * Prepare (compile) an XPath expression for subsequent evaluation.
+ * @param expression The XPath expression to be compiled, supplied as a string.
+ * @return an XPathExpression object representing the prepared expression
+ * @throws XPathException if the syntax of the expression is wrong, or if it references namespaces,
+ * variables, or functions that have not been declared.
+ */
+
+ public XPathExpression createExpression(String expression) throws XPathException {
+ Configuration config = getConfiguration();
+ Executable exec = new Executable(config);
+ SimpleContainer container = new SimpleContainer(exec);
+ boolean allowXPath30 = staticContext.getXPathLanguageLevel().equals(DecimalValue.THREE);
+ exec.setSchemaAware(staticContext.isSchemaAware());
+ exec.setHostLanguage(Configuration.XPATH, allowXPath30);
+
+ // Make the function library that's available at run-time (e.g. for saxon:evaluate() and function-lookup()).
+ // This includes all user-defined functions regardless of which module they are in
+
+ FunctionLibrary userlib = exec.getFunctionLibrary();
+ FunctionLibraryList lib = new FunctionLibraryList();
+ int permittedFunctions = StandardFunction.CORE;
+ if (allowXPath30) {
+ permittedFunctions |= StandardFunction.XPATH30;
+ }
+ lib.addFunctionLibrary(
+ SystemFunctionLibrary.getSystemFunctionLibrary(permittedFunctions));
+ lib.addFunctionLibrary(config.getVendorFunctionLibrary());
+ lib.addFunctionLibrary(new ConstructorFunctionLibrary(config));
+ lib.addFunctionLibrary(config.getIntegratedFunctionLibrary());
+ config.addExtensionBinders(lib);
+ if(userlib != null) {
+ lib.addFunctionLibrary(userlib);
+ }
+ exec.setFunctionLibrary(lib);
+
+
+ Expression exp = ExpressionTool.make(expression, staticContext, container, 0, -1, 1, null);
+ exp.setContainer(container);
+ ExpressionVisitor visitor = ExpressionVisitor.make(staticContext, exec);
+ visitor.setExecutable(exec);
+ ItemType contextItemType = staticContext.getRequiredContextItemType();
+ ExpressionVisitor.ContextItemType cit = new ExpressionVisitor.ContextItemType(contextItemType, true);
+ exp = visitor.typeCheck(exp, cit);
+ exp = visitor.optimize(exp, cit);
+ exp.setContainer(container); // might have lost its container during optimization
+ SlotManager map = staticContext.getStackFrameMap();
+ int numberOfExternalVariables = map.getNumberOfVariables();
+ ExpressionTool.allocateSlots(exp, numberOfExternalVariables, map);
+ XPathExpression xpe = new XPathExpression(this, exp);
+ xpe.setStackFrameMap(map, numberOfExternalVariables);
+ return xpe;
+ }
+
+ /**
+ * Prepare (compile) an XSLT pattern for subsequent evaluation. The result is an XPathExpression
+ * object representing a (pseudo-) expression that when evaluated returns a boolean result: true
+ * if the context node matches the pattern, false if it does not.
+ * @param pattern the XSLT pattern to be compiled, supplied as a string
+ * @return an XPathExpression object representing the pattern, wrapped as an expression
+ * @throws XPathException if the syntax of the expression is wrong, or if it references namespaces,
+ * variables, or functions that have not been declared.
+ * @since 9.1
+ */
+
+ /*@NotNull*/ public XPathExpression createPattern(String pattern) throws XPathException {
+ Executable exec = new Executable(getConfiguration());
+ Pattern pat = Pattern.make(pattern, staticContext, exec);
+ ExpressionVisitor visitor = ExpressionVisitor.make(staticContext, exec);
+ pat.analyze(visitor, new ExpressionVisitor.ContextItemType(Type.NODE_TYPE, true));
+ SlotManager map = staticContext.getStackFrameMap();
+ int slots = map.getNumberOfVariables();
+ slots = pat.allocateSlots(map, slots);
+ PatternSponsor sponsor = new PatternSponsor(pat);
+ XPathExpression xpe = new XPathExpression(this, sponsor);
+ xpe.setStackFrameMap(map, slots);
+ return xpe;
+ }
+
+ /**
+ * Set the external namespace resolver to be used. The NamespaceResolver is stored
+ * as part of the static context. It overrides any namespaces declared directly
+ * using declareNamespace on the staticContext object
+ * @param namespaceResolver The namespace resolver, which maintains a mapping of prefixes to URIs.
+ * Any namespace prefix used in the XPath expression is resolved using this namespaceResolver.
+ */
+
+ public void setNamespaceResolver(NamespaceResolver namespaceResolver) {
+ staticContext.setNamespaceResolver(namespaceResolver);
+ }
+
+ /**
+ * Get the external namespace resolver, if one has been set using {@link #setNamespaceResolver}
+ * @return the namespace resolver supplied by the user if set, or a system-defined namespace resolver
+ * otherwise. By default, the {@link IndependentContext} object used as the {@link XPathStaticContext}
+ * also acts as the {@link NamespaceResolver}.
+ */
+
+ public NamespaceResolver getNamespaceResolver() {
+ return staticContext.getNamespaceResolver();
+ }
+
+ /**
+ * Set the default namespace for elements and types
+ * @param uri The namespace to be used to qualify unprefixed element names and type names appearing
+ * in the XPath expression.
+ */
+
+ public void setDefaultElementNamespace(String uri) {
+ staticContext.setDefaultElementNamespace(uri);
+ }
+
+
+}
+
diff --git a/sf/saxon/sxpath/XPathExpression.java b/sf/saxon/sxpath/XPathExpression.java
new file mode 100644
index 0000000..462c91b
--- /dev/null
+++ b/sf/saxon/sxpath/XPathExpression.java
@@ -0,0 +1,322 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.sxpath;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.PJConverter;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.SequenceExtent;
+
+import javax.xml.transform.Source;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class is a representation of an XPath Expression for use with the {@link XPathEvaluator} class.
+ * It is modelled on the XPath API defined in JAXP 1.3, but is cut down to remove any dependencies on JAXP 1.3,
+ * making it suitable for use on vanilla JDK 1.4 installations.
+ *
+ * @author Michael H. Kay
+ */
+
+
+public class XPathExpression implements Serializable {
+
+ private XPathEvaluator evaluator;
+ private Expression expression;
+ private SlotManager stackFrameMap;
+ private int numberOfExternalVariables;
+
+ /**
+ * The constructor is protected, to ensure that instances can only be
+ * created using the createExpression() method of XPathEvaluator
+ * @param evaluator the creating XPathEvaluator
+ * @param exp the internal representation of the compiled expression
+ */
+
+ protected XPathExpression(XPathEvaluator evaluator, Expression exp) {
+ expression = exp;
+ this.evaluator = evaluator;
+ }
+
+ /**
+ * Define the number of slots needed for local variables within the expression
+ * @param map the stack frame map identifying all the variables used by the expression, both
+ * those declared internally, and references to variables declared externally
+ * @param numberOfExternalVariables the number of slots in the stack frame allocated to
+ * externally-declared variables. These must be assigned a value before execution starts
+ */
+
+ protected void setStackFrameMap(SlotManager map, int numberOfExternalVariables) {
+ stackFrameMap = map;
+ this.numberOfExternalVariables = numberOfExternalVariables;
+ }
+
+ /**
+ * Create a dynamic context suitable for evaluating this expression, without supplying a context
+ * item
+ * @return an XPathDynamicContext object representing a suitable dynamic context. This will
+ * be initialized with a stack frame suitable for holding the variables used by the
+ * expression.
+ */
+
+ public XPathDynamicContext createDynamicContext() {
+ XPathContextMajor context = new XPathContextMajor(null, expression.getExecutable());
+ context.openStackFrame(stackFrameMap);
+ return new XPathDynamicContext(
+ evaluator.getStaticContext().getRequiredContextItemType(), context, stackFrameMap);
+ }
+
+
+ /**
+ * Create a dynamic context suitable for evaluating this expression
+ * @param contextItem the initial context item, which may be null if no
+ * context item is required, or if it is to be supplied later
+ * @return an XPathDynamicContext object representing a suitable dynamic context. This will
+ * be initialized with a stack frame suitable for holding the variables used by the
+ * expression.
+ * @throws XPathException if the context item does not match the required context item type
+ * set up in the static context
+ */
+
+ public XPathDynamicContext createDynamicContext(Item contextItem) throws XPathException {
+ checkContextItemType(contextItem);
+ XPathContextMajor context = new XPathContextMajor(contextItem, expression.getExecutable());
+ context.openStackFrame(stackFrameMap);
+ return new XPathDynamicContext(
+ evaluator.getStaticContext().getRequiredContextItemType(), context, stackFrameMap);
+ }
+
+ /**
+ * Create a dynamic context suitable for evaluating this expression within an environment
+ * represented by an existing controller. The dynamic context will inherit properties of the
+ * Controller such as the current error listener, URI resolver, document pool, and implicit
+ * timezone.
+ * @param controller an existing controller. May be null, in which case the method is
+ * equivalent to calling {@link #createDynamicContext(net.sf.saxon.om.Item)}
+ * @param contextItem the initial context item, which may be null if no
+ * context item is required, or if it is to be supplied later
+ * @return an XPathDynamicContext object representing a suitable dynamic context. This will
+ * be initialized with a stack frame suitable for holding the variables used by the
+ * expression.
+ * @throws XPathException if the context item does not match the required context item type
+ * set up in the static context
+ * @since 9.2
+ */
+
+ public XPathDynamicContext createDynamicContext(Controller controller, Item contextItem)
+ throws XPathException {
+ checkContextItemType(contextItem);
+ if (controller == null) {
+ return createDynamicContext(contextItem);
+ } else {
+ XPathContextMajor context = controller.newXPathContext();
+ context.openStackFrame(stackFrameMap);
+ XPathDynamicContext dc = new XPathDynamicContext(
+ evaluator.getStaticContext().getRequiredContextItemType(), context, stackFrameMap);
+ if (contextItem != null) {
+ dc.setContextItem(contextItem);
+ }
+ return dc;
+ }
+ }
+
+ private void checkContextItemType(Item contextItem) throws XPathException {
+ if (contextItem != null) {
+ ItemType type = evaluator.getStaticContext().getRequiredContextItemType();
+ if (!type.matchesItem(contextItem, false, evaluator.getConfiguration())) {
+ throw new XPathException("Supplied context item does not match required context item type " +
+ type.toString());
+ }
+ }
+ }
+
+ /**
+ * Execute the expression, returning the result as a {@link SequenceIterator}, whose members will be instances
+ * of the class {@link net.sf.saxon.om.Item}
+ *
+ * <p>Note that if evaluation of the expression fails with a dynamic error, this will generally
+ * be reported in the form of an exception thrown by the next() method of the {@link SequenceIterator}
+ * @param context the XPath dynamic context
+ * @return an iterator over the result of the expression
+ * @throws XPathException if a dynamic error occurs during the expression evaluation (though in many
+ * cases, the error will only be detected when the iterator is actually used).
+ */
+
+ public SequenceIterator<? extends Item> iterate(XPathDynamicContext context) throws XPathException {
+ context.checkExternalVariables(stackFrameMap, numberOfExternalVariables);
+ return expression.iterate(context.getXPathContextObject());
+ }
+
+ /**
+ * Execute the expression, returning the result as a List, whose members will be instances
+ * of the class {@link net.sf.saxon.om.Item}
+ * @param context the XPath dynamic context
+ * @return a list of items representing the result of the expression
+ * @throws XPathException if a dynamic error occurs while evaluation the expression
+ */
+
+ public List<Item> evaluate(XPathDynamicContext context) throws XPathException {
+ SequenceIterator iter = expression.iterate(context.getXPathContextObject());
+ List<Item> list = new ArrayList<Item>(20);
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ list.add(item);
+ }
+ return list;
+ }
+
+ /**
+ * Execute the expression, returning the result as a single {@link net.sf.saxon.om.Item}
+ * If the result of the expression is a sequence containing more than one item, items after
+ * the first are discarded. If the result is an empty sequence, the method returns null.
+ * @param context the XPath dynamic context
+ * @return the first item in the result of the expression, or null to indicate that the result
+ * is an empty sequence
+ * @throws XPathException if a dynamic error occurs during the expression evaluation
+ */
+
+
+ /*@Nullable*/ public Item evaluateSingle(XPathDynamicContext context) throws XPathException {
+ return expression.iterate(context.getXPathContextObject()).next();
+ }
+
+ /**
+ * Evaluate the expression, returning its effective boolean value
+ * @param context the XPath dynamic context
+ * @return the effective boolean value of the result, as defined by the fn:boolean() function
+ * @throws XPathException if a dynamic error occurs, or if the result is a value for which
+ * no effective boolean value is defined
+ */
+
+ public boolean effectiveBooleanValue(XPathDynamicContext context) throws XPathException {
+ return expression.effectiveBooleanValue(context.getXPathContextObject());
+ }
+
+
+ /**
+ * Execute a prepared XPath expression, returning the results as a List in which items have been converted
+ * to the appropriate Java object.
+ *
+ * @param source the document or other node against which the XPath expression
+ * will be evaluated. This may be a Saxon NodeInfo object, representing a node in an
+ * existing tree, or it may be any kind of JAXP Source object such as a StreamSource
+ * SAXSource or DOMSource. For the way in which the source document is built, see
+ * {@link net.sf.saxon.Configuration#buildDocument}
+ * @return The results of the expression, as a List. The List represents the sequence
+ * of items returned by the expression. Each item in the list will either be an instance
+ * of net.sf.saxon.om.NodeInfo, representing a node, or a Java object representing an atomic value.
+ * @throws XPathException if a dynamic error occurs during the expression evaluation
+ */
+
+ public List<Object> evaluate(Source source) throws XPathException {
+ NodeInfo origin;
+ if (source instanceof NodeInfo) {
+ origin = (NodeInfo)source;
+ } else {
+ origin = evaluator.getConfiguration().buildDocument(source);
+ }
+ XPathDynamicContext dynamicContext = createDynamicContext(origin);
+ SequenceIterator<? extends Item> iter = iterate(dynamicContext);
+ SequenceExtent<? extends Item> extent = new SequenceExtent(iter);
+ //List result = (List)extent.convertToJava(List.class, dynamicContext.getXPathContextObject());
+ List<Object> result = (List)PJConverter.ToCollection.INSTANCE.convert(
+ extent, List.class, dynamicContext.getXPathContextObject());
+ if (result == null) {
+ result = Collections.EMPTY_LIST;
+ }
+ return result;
+ }
+
+ /**
+ * Execute a prepared XPath expression, returning the first item in the result.
+ * This is useful where it is known that the expression will only return
+ * a singleton value (for example, a single node, or a boolean).
+ * @param source the document or other node against which the XPath expression
+ * will be evaluated. This may be a Saxon NodeInfo object, representing a node in an
+ * existing tree, or it may be any kind of JAXP Source object such as a StreamSource
+ * SAXSource or DOMSource. For the way in which the source document is built, see
+ * {@link net.sf.saxon.Configuration#buildDocument}
+ *
+ * @return The first item in the sequence returned by the expression. If the expression
+ * returns an empty sequence, this method returns null. Otherwise, it returns the first
+ * item in the result sequence, represented as a Java object using the same mapping as for
+ * the evaluate() method
+ * @throws XPathException if a dynamic error occurs during the expression evaluation
+ */
+
+ public Object evaluateSingle(Source source) throws XPathException {
+ NodeInfo origin;
+ if (source instanceof NodeInfo) {
+ origin = (NodeInfo)source;
+ } else {
+ origin = evaluator.getConfiguration().buildDocument(source);
+ }
+ XPathDynamicContext context = createDynamicContext(origin);
+ SequenceIterator iterator = iterate(context);
+ Item item = iterator.next();
+ if (item == null) {
+ return null;
+ } else {
+ return SequenceTool.convertToJava(item);
+ }
+ }
+
+ /**
+ * Get a raw iterator over the results of the expression. This returns results without
+ * any conversion of the returned items to "native" Java classes. This method is intended
+ * for use by applications that need to process the results of the expression using
+ * internal Saxon interfaces.
+ * @param source the document or other node against which the XPath expression
+ * will be evaluated. This may be a Saxon NodeInfo object, representing a node in an
+ * existing tree, or it may be any kind of JAXP Source object such as a StreamSource
+ * SAXSource or DOMSource. For the way in which the source document is built, see
+ * {@link net.sf.saxon.Configuration#buildDocument}
+ * @return an iterator over the results of the expression
+ * @throws XPathException if a dynamic error occurs during the expression evaluation
+ * @deprecated since 8.9 - use {@link #iterate}
+ */
+ /*@Nullable*/
+ public SequenceIterator rawIterator(Source source) throws XPathException {
+ NodeInfo origin;
+ if (source instanceof NodeInfo) {
+ origin = (NodeInfo)source;
+ } else {
+ origin = evaluator.getConfiguration().buildDocument(source);
+ }
+ XPathDynamicContext context = createDynamicContext(origin);
+ return iterate(context);
+ }
+
+ /**
+ * Low-level method to get the internal Saxon expression object. This exposes a wide range of
+ * internal methods that may be needed by specialized applications, and allows greater control
+ * over the dynamic context for evaluating the expression.
+ *
+ * @return the underlying Saxon expression object.
+ */
+
+ public Expression getInternalExpression() {
+ return expression;
+ }
+
+}
+
diff --git a/sf/saxon/sxpath/XPathStaticContext.java b/sf/saxon/sxpath/XPathStaticContext.java
new file mode 100644
index 0000000..aecf5ea
--- /dev/null
+++ b/sf/saxon/sxpath/XPathStaticContext.java
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.sxpath;
+
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.value.QNameValue;
+
+/**
+ * This interface defines methods that must be provided when Saxon's free-standing XPath API is used.
+ * The default implementation of this interface is {@link net.sf.saxon.sxpath.IndependentContext}, and
+ * that implementation should be adequate for most purposes; but for extra customization, a user-written
+ * implementation of this interface may be used instead.
+ */
+public interface XPathStaticContext extends StaticContext {
+
+ /**
+ * Set the default namespace for elements and types
+ * @param uri The namespace to be used to qualify unprefixed element names and type names appearing
+ * in the XPath expression.
+ */
+
+ public void setDefaultElementNamespace(String uri);
+
+ /**
+ * Set an external namespace resolver. If this is set, then all resolution of namespace
+ * prefixes is delegated to the external namespace resolver, and namespaces declared
+ * individually on this IndependentContext object are ignored.
+ * @param resolver the external namespace resolver
+ */
+
+ public void setNamespaceResolver(NamespaceResolver resolver);
+
+ /**
+ * Declare a variable. A variable must be declared before an expression referring
+ * to it is compiled. The initial value of the variable will be the empty sequence.
+ * This method backs up the {@link XPathEvaluator#declareVariable} method.
+ * @param qname The name of the variable
+ * @return a Variable object representing information about the variable that has been
+ * declared.
+ */
+
+ public XPathVariable declareVariable(QNameValue qname);
+
+ /**
+ * Declare a variable. A variable must be declared before an expression referring
+ * to it is compiled. The initial value of the variable will be the empty sequence.
+ * This method backs up the {@link XPathEvaluator#declareVariable} method.
+ * @param namespaceURI The namespace URI of the name of the variable. Supply "" to represent
+ * names in no namespace (null is also accepted)
+ * @param localName The local part of the name of the variable (an NCName)
+ * @return an XPathVariable object representing information about the variable that has been
+ * declared.
+ */
+
+ public XPathVariable declareVariable(String namespaceURI, String localName);
+
+ /**
+ * Get a Stack Frame Map containing definitions of all the declared variables. This will return a newly
+ * created object that the caller is free to modify by adding additional variables, without affecting
+ * the static context itself.
+ * @return a SlotManager object holding details of the allocation of variables on the stack frame.
+ */
+
+ public SlotManager getStackFrameMap();
+
+
+}
+
diff --git a/sf/saxon/sxpath/XPathVariable.java b/sf/saxon/sxpath/XPathVariable.java
new file mode 100644
index 0000000..5b94b0f
--- /dev/null
+++ b/sf/saxon/sxpath/XPathVariable.java
@@ -0,0 +1,179 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.sxpath;
+
+import net.sf.saxon.expr.Binding;
+import net.sf.saxon.expr.BindingReference;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.SequenceType;
+
+
+/**
+ * An object representing an XPath variable for use in the standalone XPath API. The object
+ * can only be created by calling the declareVariable method of class {@link IndependentContext}.
+ * Note that once declared, this object is thread-safe: it does not hold the actual variable
+ * value, which means it can be used with any number of evaluations of a given XPath expression,
+ * in series or in parallel.
+ *
+ * <p>A variable can be given a value by calling
+ * {@link XPathDynamicContext#setVariable(XPathVariable, net.sf.saxon.om.Sequence)}.
+ * Note that the value of the variable is not held in the XPathVariable object, but in the
+ * XPathDynamicContext, which means that the XPathVariable itself can be used in multiple threads.
+*/
+
+public final class XPathVariable implements Binding {
+
+ private StructuredQName name;
+ private SequenceType requiredType = SequenceType.ANY_SEQUENCE;
+ private Sequence defaultValue;
+ private int slotNumber;
+
+ /**
+ * Private constructor: for use only by the protected factory method make()
+ */
+
+ private XPathVariable() {}
+
+ /**
+ * Factory method, for use by the declareVariable method of class IndependentContext
+ * @param name the name of the variable to create
+ * @return the constructed XPathVariable
+ */
+
+ protected static XPathVariable make(StructuredQName name) {
+ XPathVariable v = new XPathVariable();
+ v.name = name;
+ return v;
+ }
+
+ /**
+ * Ask whether the binding is local or global. A global binding is one that has a fixed
+ * value for the life of a query or transformation; any other binding is local. An XPath
+ * variable is treated as a local variable (largely because it is held on the stack frame)
+ * @return false (always)
+ */
+
+ public boolean isGlobal() {
+ return false;
+ }
+
+ /**
+ * Test whether it is permitted to assign to the variable using the saxon:assign
+ * extension element. This will only be for an XSLT global variable where the extra
+ * attribute saxon:assignable="yes" is present.
+ * @return false (always)
+ */
+
+ public final boolean isAssignable() {
+ return false;
+ }
+
+ /**
+ * Set the required type of this variable. If no required type is specified,
+ * the type <code>item()*</code> is assumed.
+ * @param requiredType the required type
+ */
+
+ public void setRequiredType(SequenceType requiredType) {
+ this.requiredType = requiredType;
+ }
+
+ /**
+ * Get the required type of this variable. If no required type has been specified,
+ * the type <code>item()*</code> is returned.
+ * @return the required type of the variable
+ */
+
+ public SequenceType getRequiredType() {
+ return requiredType;
+ }
+
+ /**
+ * If the variable is bound to an integer, get the minimum and maximum possible values.
+ * Return null if unknown or not applicable
+ */
+ /*@Nullable*/ public IntegerValue[] getIntegerBoundsForVariable() {
+ return null;
+ }
+
+ /**
+ * Set the slot number allocated to this variable. This method is for internal use.
+ * @param slotNumber the slot number to be allocated
+ */
+
+ public void setSlotNumber(int slotNumber) {
+ this.slotNumber = slotNumber;
+ }
+
+ /**
+ * If this is a local variable held on the local stack frame, return the corresponding slot number.
+ * In other cases, return -1.
+ */
+
+ public int getLocalSlotNumber() {
+ return slotNumber;
+ }
+
+ /**
+ * Get the name of the variable as a QNameValue.
+ * @return the name of the variable, as a QNameValue
+ */
+
+ public StructuredQName getVariableQName() {
+ return name;
+ }
+
+ public void addReference(boolean isLoopingReference) {
+
+ }
+
+ /**
+ * Method called by the XPath expression parser to register a reference to this variable.
+ * This method should not be called by users of the API.
+ */
+
+ public void registerReference(BindingReference ref) {
+ ref.setStaticType(requiredType, null, 0);
+ ref.fixup(this);
+ }
+
+ /**
+ * Set a default value for the variable, to be used if no specific value is
+ * supplied when the expression is evaluated
+ * @param defaultValue the default value for the variable
+ */
+
+ public void setDefaultValue(Sequence defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Get the default value of the variable
+ * @return the default value if one has been registered, or null otherwise
+ */
+
+ public Sequence getDefaultValue() {
+ return defaultValue;
+ }
+
+ /**
+ * Get the value of the variable. This method is used by the XPath execution engine
+ * to retrieve the value. Note that the value is not held within the variable itself,
+ * but within the dynamic context.
+ * @param context The dynamic evaluation context
+ * @return The value of the variable
+ */
+
+ public Sequence evaluateVariable(XPathContext context) {
+ return context.evaluateLocalVariable(slotNumber);
+ }
+}
+
diff --git a/sf/saxon/sxpath/package.html b/sf/saxon/sxpath/package.html
new file mode 100644
index 0000000..bf1ff9a
--- /dev/null
+++ b/sf/saxon/sxpath/package.html
@@ -0,0 +1,32 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.sxpath</title>
+</head>
+
+<body>
+
+<p>This package provides an alternative API for executing XPath expressions
+directly from a Java application: unlike the API in package <code>net.sf.saxon.xpath</code>, these
+interfaces are not dependent on JAXP 1.3. The API can be used either in a free-standing
+Java application (that is, where there is no XSLT stylesheet), or it can be
+used from within Java extension functions called from XPath expressions within
+a stylesheet.</p>
+
+<p>The preferred API for executing XPath expressions in Saxon is the <code>s9api</code>
+package. This package is older, and provides a lower-level interface that offers access to more
+internal details, but is not so convenient to use.</p>
+
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+12 June 2009</i></p>
+</body>
+</html>
diff --git a/sf/saxon/trace/AbstractTraceListener.java b/sf/saxon/trace/AbstractTraceListener.java
new file mode 100644
index 0000000..07ebacc
--- /dev/null
+++ b/sf/saxon/trace/AbstractTraceListener.java
@@ -0,0 +1,223 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.Version;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.CodeInjector;
+import net.sf.saxon.lib.StandardErrorListener;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.value.Whitespace;
+
+import java.io.PrintStream;
+import java.util.Iterator;
+
+/**
+ * This is the standard trace listener used when the -T option is specified on the command line.
+ * There are two variants, represented by subclasses: one for XSLT, and one for XQuery. The two variants
+ * differ in that they present the trace output in terms of constructs used in the relevant host language.
+ */
+
+public abstract class AbstractTraceListener implements TraceListener {
+ private int indent = 0;
+ private PrintStream out = System.err;
+ /*@NotNull*/ private static StringBuffer spaceBuffer = new StringBuffer(" ");
+
+ /**
+ * Get the associated CodeInjector to be used at compile time to generate the tracing calls
+ */
+
+ public CodeInjector getCodeInjector() {
+ return new TraceCodeInjector();
+ }
+
+ /**
+ * Called at start
+ */
+
+ public void open(Controller controller) {
+ out.println("<trace " +
+ "saxon-version=\"" + Version.getProductVersion()+ "\" " +
+ getOpeningAttributes() + '>');
+ indent++;
+ }
+
+ protected abstract String getOpeningAttributes();
+
+ /**
+ * Called at end
+ */
+
+ public void close() {
+ indent--;
+ out.println("</trace>");
+ }
+
+ /**
+ * Called when an instruction in the stylesheet gets processed
+ */
+
+ public void enter(/*@NotNull*/ InstructionInfo info, XPathContext context) {
+ int infotype = info.getConstructType();
+ StructuredQName qName = info.getObjectName();
+ String tag = tag(infotype);
+ if (tag==null) {
+ // this TraceListener ignores some events to reduce the volume of output
+ return;
+ }
+ String file = StandardErrorListener.abbreviatePath(info.getSystemId());
+ String msg = AbstractTraceListener.spaces(indent) + '<' + tag;
+ String name = (String)info.getProperty("name");
+ if (name!=null) {
+ msg += " name=\"" + escape(name) + '"';
+ } else if (qName != null) {
+ msg += " name=\"" + escape(qName.getDisplayName()) + '"';
+ }
+ Iterator props = info.getProperties();
+ while (props.hasNext()) {
+ String prop = (String)props.next();
+ Object val = info.getProperty(prop);
+ if (prop.startsWith("{")) {
+ // It's a QName in Clark notation: we'll strip off the namespace
+ int rcurly = prop.indexOf('}');
+ if (rcurly > 0) {
+ prop = prop.substring(rcurly+1);
+ }
+ }
+ if (val != null && !prop.equals("name") && !prop.equals("expression")) {
+ msg += ' ' + prop + "=\"" + escape(val.toString()) + '"';
+ }
+ }
+
+ msg += " line=\"" + info.getLineNumber() + '"';
+
+ int col = info.getColumnNumber();
+ if (col >= 0) {
+ msg += " column=\"" + info.getColumnNumber() + '"';
+ }
+
+ msg += " module=\"" + escape(file) + "\">";
+ out.println(msg);
+ indent++;
+ }
+
+ /**
+ * Escape a string for XML output (in an attribute delimited by double quotes).
+ * This method also collapses whitespace (since the value may be an XPath expression that
+ * was originally written over several lines).
+ */
+
+ public String escape(/*@Nullable*/ String in) {
+ if (in==null) {
+ return "";
+ }
+ CharSequence collapsed = Whitespace.collapseWhitespace(in);
+ FastStringBuffer sb = new FastStringBuffer(collapsed.length() + 10);
+ for (int i=0; i<collapsed.length(); i++) {
+ char c = collapsed.charAt(i);
+ if (c=='<') {
+ sb.append("<");
+ } else if (c=='>') {
+ sb.append(">");
+ } else if (c=='&') {
+ sb.append("&");
+ } else if (c=='\"') {
+ sb.append(""");
+ } else if (c=='\n') {
+ sb.append("
");
+ } else if (c=='\r') {
+ sb.append("
");
+ } else if (c=='\t') {
+ sb.append("	");
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Called after an instruction of the stylesheet got processed
+ */
+
+ public void leave(/*@NotNull*/ InstructionInfo info) {
+ int infotype = info.getConstructType();
+ String tag = tag(infotype);
+ if (tag==null) {
+ // this TraceListener ignores some events to reduce the volume of output
+ return;
+ }
+ indent--;
+ out.println(AbstractTraceListener.spaces(indent) + "</" + tag + '>');
+ }
+
+ protected abstract String tag(int construct);
+
+ /**
+ * Called when an item becomes the context item
+ */
+
+ public void startCurrentItem(Item item) {
+ if (item instanceof NodeInfo) {
+ NodeInfo curr = (NodeInfo) item;
+ out.println(AbstractTraceListener.spaces(indent) + "<source node=\"" + Navigator.getPath(curr)
+ + "\" line=\"" + curr.getLineNumber()
+ + "\" file=\"" + StandardErrorListener.abbreviatePath(curr.getSystemId())
+ + "\">");
+ }
+ indent++;
+ }
+
+ /**
+ * Called after a node of the source tree got processed
+ */
+
+ public void endCurrentItem(Item item) {
+ indent--;
+ if (item instanceof NodeInfo) {
+ NodeInfo curr = (NodeInfo) item;
+ out.println(AbstractTraceListener.spaces(indent) + "</source><!-- " +
+ Navigator.getPath(curr) + " -->");
+ }
+ }
+
+ /**
+ * Get n spaces
+ */
+
+ private static String spaces(int n) {
+ while (spaceBuffer.length() < n) {
+ spaceBuffer.append(AbstractTraceListener.spaceBuffer);
+ }
+ return spaceBuffer.substring(0, n);
+ }
+
+ /**
+ * Set the output destination (default is System.err)
+ * @param stream the output destination for tracing output
+ */
+
+ public void setOutputDestination(PrintStream stream) {
+ out = stream;
+ }
+
+ /**
+ * Get the output destination
+ */
+
+ public PrintStream getOutputDestination() {
+ return out;
+ }
+}
+
diff --git a/sf/saxon/trace/ContextStackFrame.java b/sf/saxon/trace/ContextStackFrame.java
new file mode 100644
index 0000000..39dd46e
--- /dev/null
+++ b/sf/saxon/trace/ContextStackFrame.java
@@ -0,0 +1,314 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.instruct.*;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.KeyDefinition;
+import net.sf.saxon.tree.util.Navigator;
+
+import java.io.PrintStream;
+
+/**
+ * An entry on the context stack. A new entry is created every time the context changes. This is a
+ * representation of the stack created on request; it does not hold live data.
+ */
+public abstract class ContextStackFrame {
+
+ private String moduleUri;
+ private int lineNumber;
+ private Container container;
+ private Item contextItem;
+
+ /**
+ * Set the system ID representing the location of the instruction that caused this new context
+ * to be created
+ * @param uri the system ID (base URI/module URI) of the module containing the instruction
+ */
+
+ public void setSystemId(String uri) {
+ this.moduleUri = uri;
+ }
+
+ /**
+ * Get the system ID representing the location of the instruction that caused this new context
+ * to be created
+ * @return the system ID (base URI/module URI) of the module containing the instruction
+ */
+
+ public String getSystemId() {
+ return moduleUri;
+ }
+
+ /**
+ * Set the line number of the location of the instruction that caused this new context
+ * to be created
+ * @param lineNumber the line number of the instruction within its containing module
+ */
+
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Get the line number of the location of the instruction that caused this new context
+ * to be created
+ * @return the line number of the instruction within its containing module
+ */
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Set the container of the instruction that caused this new context to be created. This will
+ * generally be an object such as an XSLT Template or a user-defined function
+ * @param container the container of the instruction
+ */
+
+ public void setContainer(Container container) {
+ this.container = container;
+ }
+
+ /**
+ * Get the container of the instruction that caused this new context to be created. This will
+ * generally be an object such as an XSLT Template or a user-defined function
+ * @return the container of the instruction in the expression tree
+ */
+
+ public Container getContainer() {
+ return container;
+ }
+
+ /**
+ * Set the value of the context item at this level of the context stack
+ * @param contextItem the context item as it was when this new context was created
+ */
+
+ public void setContextItem(Item contextItem) {
+ this.contextItem = contextItem;
+ }
+
+ /**
+ * Get the value of the context item at this level of the context stack
+ * @return the context item as it was when this new context was created
+ */
+
+ public Item getContextItem() {
+ return contextItem;
+ }
+
+ /**
+ * Display a representation of the stack frame on the specified output stream
+ * @param out the output stream
+ */
+
+ public abstract void print(PrintStream out);
+
+ /**
+ * Show the location of a call (for use by subclasses)
+ * @return typically "(" + systemId() + "#" + lineNumber() + ")"
+ */
+
+ protected String showLocation() {
+ if (getSystemId() == null) {
+ return "";
+ }
+ int line = getLineNumber();
+ if (line == -1 || line == 0xfffff) {
+ return "(" + getSystemId() + ")";
+ } else {
+ return "(" + getSystemId() + "#" + getLineNumber() + ")";
+ }
+ }
+
+ /**
+ * Subclass of ContextStackFrame representing the outermost stack frame,
+ * for the calling application
+ */
+
+ public static class CallingApplication extends ContextStackFrame {
+ public void print(PrintStream out) {
+ //out.println(" (called from external application)");
+ }
+ }
+
+ /**
+ * Subclass of ContextStackFrame representing a built-in template rule in XSLT
+ */
+
+ public static class BuiltInTemplateRule extends ContextStackFrame {
+ public void print(PrintStream out) {
+ out.println(" in built-in template rule");
+ }
+ }
+
+ /**
+ * Subclass of ContextStackFrame representing a call to a user-defined function
+ * either in XSLT or XQuery
+ */
+
+ public static class FunctionCall extends ContextStackFrame {
+
+ StructuredQName functionName;
+
+ /**
+ * Get the name of the function being called
+ * @return the name of the function being called
+ */
+ public StructuredQName getFunctionName() {
+ return functionName;
+ }
+
+ /**
+ * Set the name of the function being called
+ * @param functionName the name of the function being called
+ */
+ public void setFunctionName(StructuredQName functionName) {
+ this.functionName = functionName;
+ }
+
+ /**
+ * Display a representation of the stack frame on the specified output stream
+ * @param out the output stream
+ */
+
+ public void print(PrintStream out) {
+ out.println(" at " + (functionName==null ? "(anonymous)" : functionName.getDisplayName()) + "() " + showLocation());
+ }
+ }
+
+ /**
+ * Subclass of ContextStackFrame representing an xsl:apply-templates call in XSLT
+ */
+
+ public static class ApplyTemplates extends ContextStackFrame {
+
+ /**
+ * Display a representation of the stack frame on the specified output stream
+ * @param out the output stream
+ */
+
+ public void print(PrintStream out) {
+ out.println(" at xsl:apply-templates " + showLocation());
+ Item node = getContextItem();
+ if (node instanceof NodeInfo) {
+ out.println(" processing " + Navigator.getPath((NodeInfo)node));
+ }
+ }
+ }
+
+ /**
+ * Subclass of ContextStackFrame representing an xsl:call-template instruction in XSLT
+ */
+
+ public static class CallTemplate extends ContextStackFrame {
+
+ /**
+ * Get the name of the template being called
+ * @return the name of the template being called. Note this may be null in the case of the
+ * extension instruction saxon:call-template
+ */
+ public StructuredQName getTemplateName() {
+ return templateName;
+ }
+
+ /**
+ * Set the name of the template being called
+ * @param templateName the name of the template being called.
+ */
+ public void setTemplateName(StructuredQName templateName) {
+ this.templateName = templateName;
+ }
+
+ StructuredQName templateName;
+
+ /**
+ * Display a representation of the stack frame on the specified output stream
+ * @param out the output stream
+ */
+
+ public void print(PrintStream out) {
+ String name = templateName == null ? "??" : templateName.getDisplayName();
+ out.println(" at xsl:call-template name=\"" + name + "\" " + showLocation());
+ }
+ }
+
+ /**
+ * Subclass of ContextStackFrame representing the evaluation of a variable (typically a global variable)
+ */
+
+ public static class VariableEvaluation extends ContextStackFrame {
+
+ /**
+ * Get the name of the variable
+ * @return the name of the variable
+ */
+ public StructuredQName getVariableName() {
+ return variableName;
+ }
+
+ /**
+ * Set the name of the variable
+ * @param variableName the name of the variable
+ */
+ public void setVariableName(StructuredQName variableName) {
+ this.variableName = variableName;
+ }
+
+ StructuredQName variableName;
+
+ /**
+ * Display a representation of the stack frame on the specified output stream
+ * @param out the output stream
+ */
+
+ public void print(PrintStream out) {
+ out.println(" in " + displayContainer(getContainer()) + " " + showLocation());
+ }
+ }
+
+ private static String displayContainer(/*@NotNull*/ Container container) {
+ if (container instanceof Procedure) {
+ StructuredQName name = ((Procedure)container).getObjectName();
+ String objectName = (name==null ? "" : name.getDisplayName());
+ if (container instanceof Template) {
+ if (name == null) {
+ //NamePool pool = container.getExecutable().getConfiguration().getNamePool();
+ return "template match=\"" + ((Template)container).getMatchPattern().toString() + "\"";
+ } else {
+ return "template name=\"" + objectName + "\"";
+ }
+ } else if (container instanceof UserFunction) {
+ return "function " + objectName + "()";
+ } else if (container instanceof AttributeSet) {
+ return "attribute-set " + objectName;
+ } else if (container instanceof KeyDefinition) {
+ return "key " + objectName;
+ }
+ } else if (container instanceof GlobalVariable) {
+ StructuredQName qName = ((GlobalVariable)container).getVariableQName();
+ if (NamespaceConstant.SAXON_GENERATED_GLOBAL.equals(qName.getURI())) {
+ return "optimizer-created global variable";
+ } else {
+ return "variable " + qName.getDisplayName();
+ }
+ } else {
+ return "";
+ }
+ return "";
+ }
+
+
+
+}
+
diff --git a/sf/saxon/trace/ContextStackIterator.java b/sf/saxon/trace/ContextStackIterator.java
new file mode 100644
index 0000000..e9c0e01
--- /dev/null
+++ b/sf/saxon/trace/ContextStackIterator.java
@@ -0,0 +1,153 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.expr.UserFunctionCall;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.expr.instruct.ApplyTemplates;
+import net.sf.saxon.expr.instruct.CallTemplate;
+import net.sf.saxon.expr.instruct.GeneralVariable;
+import net.sf.saxon.om.StandardNames;
+
+import java.util.Iterator;
+
+/**
+ * This class provides a representation of the current runtime call stack, as represented by the stack
+ * of XPathContext objects.
+ */
+public class ContextStackIterator implements Iterator<ContextStackFrame> {
+
+ private XPathContextMajor next;
+
+ /**
+ * Create an iterator over the stack of XPath dynamic context objects, starting with the top-most
+ * stackframe and working down. The objects returned by this iterator will be of class {@link ContextStackFrame}.
+ * Note that only "major" context objects are considered - those that have a stack frame of their own.
+ * @param context the current context
+ */
+
+ public ContextStackIterator(XPathContext context) {
+ if (!(context instanceof XPathContextMajor)) {
+ context = getMajorCaller(context);
+ }
+ next = (XPathContextMajor)context;
+ }
+
+ /**
+ * Returns <tt>true</tt> if the iteration has more elements. (In other
+ * words, returns <tt>true</tt> if <tt>next</tt> would return an element
+ * rather than throwing an exception.)
+ *
+ * @return <tt>true</tt> if the iterator has more elements.
+ */
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ /**
+ * Returns the next element in the iteration. Calling this method
+ * repeatedly until the {@link #hasNext()} method returns false will
+ * return each element in the underlying collection exactly once.
+ *
+ * @return the next element in the iteration, which will always be an instance
+ * of {@link ContextStackFrame}
+ * @throws java.util.NoSuchElementException
+ * iteration has no more elements.
+ */
+ /*@Nullable*/ public ContextStackFrame next() {
+ XPathContextMajor context = next;
+ if (context == null) {
+ return null;
+ }
+ int construct = context.getOriginatingConstructType();
+ Object origin = context.getOrigin();
+
+ if (construct == Location.CONTROLLER) {
+ next = getMajorCaller(context);
+ return new ContextStackFrame.CallingApplication();
+ } else if (construct == Location.BUILT_IN_TEMPLATE) {
+ next = getMajorCaller(context);
+ return new ContextStackFrame.BuiltInTemplateRule();
+ }
+ if (construct == Location.FUNCTION_CALL) {
+ ContextStackFrame.FunctionCall sf = new ContextStackFrame.FunctionCall();
+ UserFunctionCall ufc = (UserFunctionCall)origin;
+ sf.setSystemId(ufc.getSystemId());
+ sf.setLineNumber(ufc.getLineNumber());
+ sf.setContainer(ufc.getContainer());
+ sf.setFunctionName(ufc.getFunctionName());
+ sf.setContextItem(context.getContextItem());
+ next = getMajorCaller(context);
+ return sf;
+ } else if (construct == StandardNames.XSL_APPLY_TEMPLATES) {
+ ContextStackFrame.ApplyTemplates sf = new ContextStackFrame.ApplyTemplates();
+ ApplyTemplates loc = (ApplyTemplates)origin;
+ sf.setSystemId(loc.getSystemId());
+ sf.setLineNumber(loc.getLineNumber());
+ sf.setContainer(loc.getContainer());
+ sf.setContextItem(context.getContextItem());
+ next = getMajorCaller(context);
+ return sf;
+ } else if (construct == StandardNames.XSL_CALL_TEMPLATE) {
+ ContextStackFrame.CallTemplate sf = new ContextStackFrame.CallTemplate();
+ CallTemplate loc = (CallTemplate)origin;
+ sf.setSystemId(loc.getSystemId());
+ sf.setLineNumber(loc.getLineNumber());
+ sf.setContainer(loc.getContainer());
+ sf.setTemplateName(loc.getObjectName());
+ sf.setContextItem(context.getContextItem());
+ next = getMajorCaller(context);
+ return sf;
+ } else if (construct == StandardNames.XSL_VARIABLE) {
+ ContextStackFrame.VariableEvaluation sf = new ContextStackFrame.VariableEvaluation();
+ GeneralVariable var = ((GeneralVariable)origin);
+ sf.setSystemId(var.getSystemId());
+ sf.setLineNumber(var.getLineNumber());
+ sf.setContainer(var.getContainer());
+ sf.setContextItem(context.getContextItem());
+ sf.setVariableName(var.getVariableQName());
+ next = getMajorCaller(context);
+ return sf;
+ } else {
+ //other context changes are not considered significant enough to report
+ //out.println(" In unidentified location " + construct);
+ next = getMajorCaller(context);
+ ContextStackFrame csf = next();
+ if (csf == null) {
+ // we can't return null, because hasNext() returned true...
+ return new ContextStackFrame.CallingApplication();
+ } else {
+ return csf;
+ }
+ }
+
+ }
+
+ private static XPathContextMajor getMajorCaller(XPathContext context) {
+ XPathContext caller = context.getCaller();
+ while (!(caller == null || caller instanceof XPathContextMajor)) {
+ caller = caller.getCaller();
+ }
+ return (XPathContextMajor)caller;
+ }
+
+ /**
+ * Removes from the underlying collection the last element returned by the
+ * iterator (optional operation).
+ *
+ * @throws UnsupportedOperationException as the <tt>remove</tt>
+ * operation is not supported by this Iterator.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+
+}
+
diff --git a/sf/saxon/trace/ExpressionPresenter.java b/sf/saxon/trace/ExpressionPresenter.java
new file mode 100644
index 0000000..6e579d9
--- /dev/null
+++ b/sf/saxon/trace/ExpressionPresenter.java
@@ -0,0 +1,239 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.SaxonOutputKeys;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NoNamespaceName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.type.Untyped;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.stream.StreamResult;
+import java.io.OutputStream;
+import java.util.Properties;
+
+/**
+ * This class handles the display of an abstract expression tree in an XML format
+ * with some slight resemblence to XQueryX
+ */
+public class ExpressionPresenter {
+
+ private Configuration config;
+ /*@Nullable*/ private Receiver receiver;
+ int depth = 0;
+ boolean inStartTag = false;
+
+ /**
+ * Make an ExpressionPresenter that writes indented output to the standard error output
+ * destination of the Configuration
+ * @param config the Saxon configuration
+ */
+
+ public ExpressionPresenter(/*@NotNull*/ Configuration config) {
+ this(config, config.getStandardErrorOutput());
+ }
+
+ /**
+ * Make an ExpressionPresenter that writes indented output to a specified output stream
+ * @param config the Saxon configuration
+ * @param out the output stream
+ */
+
+ public ExpressionPresenter(/*@NotNull*/ Configuration config, OutputStream out) {
+ Properties props = makeDefaultProperties();
+ try {
+ receiver = config.getSerializerFactory().getReceiver(
+ new StreamResult(out),
+ config.makePipelineConfiguration(),
+ props);
+ } catch (XPathException err) {
+ err.printStackTrace();
+ throw new InternalError(err.getMessage());
+ }
+ this.config = config;
+ try {
+ receiver.open();
+ receiver.startDocument(0);
+ } catch (XPathException err) {
+ err.printStackTrace();
+ throw new InternalError(err.getMessage());
+ }
+ }
+
+ /**
+ * Make an ExpressionPresenter for a given Configuration using a user-supplied Receiver
+ * to accept the output
+ * @param config the Configuration
+ * @param receiver the user-supplied Receiver
+ */
+
+ public ExpressionPresenter(Configuration config, /*@NotNull*/ Receiver receiver) {
+ this.config = config;
+ this.receiver = receiver;
+ try {
+ receiver.open();
+ receiver.startDocument(0);
+ } catch (XPathException err) {
+ err.printStackTrace();
+ throw new InternalError(err.getMessage());
+ }
+ }
+
+ /**
+ * Make a receiver, using default output properties, with serialized output going
+ * to a specified OutputStream
+ * @param config the Configuration
+ * @param out the OutputStream
+ * @return a Receiver that directs serialized output to this output stream
+ * @throws XPathException
+ */
+
+ /*@Nullable*/ public static Receiver defaultDestination(/*@NotNull*/ Configuration config, OutputStream out) throws XPathException {
+ Properties props = makeDefaultProperties();
+ return config.getSerializerFactory().getReceiver(
+ new StreamResult(out),
+ config.makePipelineConfiguration(),
+ props);
+ }
+
+
+ /**
+ * Make a Properties object containing defaulted serialization attributes for the expression tree
+ * @return a default set of properties
+ */
+
+ /*@NotNull*/ public static Properties makeDefaultProperties() {
+ Properties props = new Properties();
+ props.setProperty(OutputKeys.METHOD, "xml");
+ props.setProperty(OutputKeys.INDENT, "yes");
+ props.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ props.setProperty(SaxonOutputKeys.INDENT_SPACES, "2");
+ return props;
+ }
+
+ /**
+ * Start an element
+ * @param name the name of the element
+ * @return the depth of the tree before this element: for diagnostics, this can be compared
+ * with the value returned by endElement
+ */
+
+ public int startElement(String name) {
+ try {
+ if (inStartTag) {
+ receiver.startContent();
+ inStartTag = false;
+ }
+ receiver.startElement(new NoNamespaceName(name), Untyped.getInstance(), 0, 0);
+ } catch (XPathException err) {
+ err.printStackTrace();
+ throw new InternalError(err.getMessage());
+ }
+ inStartTag = true;
+ return depth++;
+ }
+
+ /**
+ * Output an attribute node
+ * @param name the name of the attribute
+ * @param value the value of the attribute
+ */
+
+ public void emitAttribute(String name, String value) {
+ try {
+ receiver.attribute(new NoNamespaceName(name), BuiltInAtomicType.UNTYPED_ATOMIC, value, 0, 0);
+ } catch (XPathException err) {
+ err.printStackTrace();
+ throw new InternalError(err.getMessage());
+ }
+ }
+
+ /**
+ * End an element in the expression tree
+ * @return the depth of the tree after ending this element. For diagnostics, this can be compared with the
+ * value returned by startElement()
+ */
+
+ public int endElement() {
+ try {
+ if (inStartTag) {
+ receiver.startContent();
+ inStartTag = false;
+ }
+ receiver.endElement();
+ } catch (XPathException err) {
+ err.printStackTrace();
+ throw new InternalError(err.getMessage());
+ }
+ return --depth;
+ }
+
+ /**
+ * Start a child element in the output
+ * @param name the name of the child element
+ */
+
+ public void startSubsidiaryElement(String name) {
+ startElement(name);
+ }
+
+ /**
+ * End a child element in the output
+ */
+
+ public void endSubsidiaryElement() {
+ endElement();
+ }
+
+ /**
+ * Close the output
+ */
+
+ public void close() {
+ try {
+ receiver.endDocument();
+ receiver.close();
+ } catch (XPathException err) {
+ err.printStackTrace();
+ throw new InternalError(err.getMessage());
+ }
+ }
+
+ /**
+ * Get the Saxon configuration
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the name pool
+ * @return the name pool
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ /**
+ * Get the type hierarchy cache
+ * @return the type hierarchy cache
+ */
+
+ public TypeHierarchy getTypeHierarchy() {
+ return config.getTypeHierarchy();
+ }
+}
+
diff --git a/sf/saxon/trace/InstructionInfo.java b/sf/saxon/trace/InstructionInfo.java
new file mode 100644
index 0000000..d85d9a8
--- /dev/null
+++ b/sf/saxon/trace/InstructionInfo.java
@@ -0,0 +1,81 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.event.SaxonLocator;
+import net.sf.saxon.om.StructuredQName;
+
+import java.util.Iterator;
+
+
+/**
+* Information about an instruction in the stylesheet or a construct in a Query, made
+* available at run-time to a TraceListener
+*/
+
+public interface InstructionInfo extends SaxonLocator {
+
+ /**
+ * Get the type of construct. This will either be the fingerprint of a standard XSLT instruction name
+ * (values in {@link net.sf.saxon.om.StandardNames}: all less than 1024)
+ * or it will be a constant in class {@link Location}.
+ * @return an integer identifying the kind of construct
+ */
+
+ public int getConstructType();
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ * @return the QName of the object declared or manipulated by this instruction or expression
+ */
+
+ /*@Nullable*/ public StructuredQName getObjectName();
+
+ /**
+ * Get the system identifier (URI) of the source stylesheet or query module containing
+ * the instruction. This will generally be an absolute URI. If the system
+ * identifier is not known, the method may return null. In some cases, for example
+ * where XML external entities are used, the correct system identifier is not
+ * always retained.
+ * @return the URI of the containing module
+ */
+
+ /*@Nullable*/ public String getSystemId();
+
+ /**
+ * Get the line number of the instruction in the source stylesheet module.
+ * If this is not known, or if the instruction is an artificial one that does
+ * not relate to anything in the source code, the value returned may be -1.
+ * @return the line number of the expression within the containing module
+ */
+
+ public int getLineNumber();
+
+ /**
+ * Get the value of a particular property of the instruction. Properties
+ * of XSLT instructions are generally known by the name of the stylesheet attribute
+ * that defines them.
+ * @param name The name of the required property
+ * @return The value of the requested property, or null if the property is not available
+ */
+
+ /*@Nullable*/ public Object getProperty(String name);
+
+ /**
+ * Get an iterator over all the properties available. The values returned by the iterator
+ * will be of type String, and each string can be supplied as input to the getProperty()
+ * method to retrieve the value of the property. The iterator may return properties whose
+ * value is null.
+ * @return an iterator over the properties.
+ */
+
+ public Iterator<String> getProperties();
+
+}
+
diff --git a/sf/saxon/trace/Location.java b/sf/saxon/trace/Location.java
new file mode 100644
index 0000000..b3d14fe
--- /dev/null
+++ b/sf/saxon/trace/Location.java
@@ -0,0 +1,239 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+/**
+ * This class holds constants identifying different kinds of location in a source stylesheet or query.
+ * These constants are used in the getConstructType() method of class InstructionInfo. Some of these
+ * locations represent points where the dynamic context changes, and they are therefore recorded as
+ * such on the context stack. Some of the locations represent points in the evaluation of a stylesheet
+ * (or query or XPath expression) that are notified to the trace listener. Some fulfil both roles.
+ *
+ * <p>Any constant used in {@link net.sf.saxon.om.StandardNames} can be used as a Location. Such
+ * names are generally used to identify XSLT instructions. They are also used for equivalent constructs
+ * in XQuery, for example XSL_ELEMENT is used for a computed element constructor in XQuery. The constants
+ * in StandardNames are all in the range 0-1023, so constants defined in this class are outside this
+ * range.</p>
+ *
+ * <p>The constants in this file are annotated with Q to indicate they can appear in XQuery trace output,
+ * T to indicate they can appear in XSLT trace output, and/or C to indicate that they can appear on the
+ * dynamic context stack.</p>
+ */
+public class Location {
+
+ /**
+ * The outer system environment, identified as the caller of a user query or stylesheet.
+ * Usage:C
+ */
+ public static final int CONTROLLER = 2000;
+
+
+ /**
+ * An XSLT instruction. The name of the instruction (which may be an extension instruction) can
+ * be obtained using the fingerprint property. Usage:T
+ */
+ public static final int EXTENSION_INSTRUCTION = 2005;
+
+ /**
+ * An XSLT literal result element, or an XQuery direct element constructor. Usage:QT
+ */
+ public static final int LITERAL_RESULT_ELEMENT = 2006;
+
+ /**
+ * An attribute of an XSLT literal result element or of an XQuery direct element constructor.
+ * Usage: QT
+ */
+ public static final int LITERAL_RESULT_ATTRIBUTE = 2007;
+
+ /**
+ * An XSLT user-written template rule or named template. Usage: TC
+ */
+ public static final int TEMPLATE = 2008;
+
+ /**
+ * An XPath function call to a user-defined function.
+ * The "expression" property references the actual expression, of class ComputedExpression.
+ * The "target" property references the function being called, of class UserFunction.
+ * Usage: QTC
+ */
+ public static final int FUNCTION_CALL = 2009;
+
+ /**
+ * An XSLT built-in template rule. Usage: TC
+ */
+ public static final int BUILT_IN_TEMPLATE = 2010;
+
+ /**
+ * Entry point to a top-level XPath expression within an XSLT stylesheet.
+ * Usage: TC
+ */
+
+ public static final int XPATH_IN_XSLT = 2011;
+
+ /**
+ * An XPath or XQuery "for" clause. Usage: Q
+ */
+
+ public static final int FOR_EXPRESSION = 2012;
+
+ /**
+ * An XQuery "let" clause, or an XSLT local variable (which compiles into a LET clause).
+ * Usage: Q,T
+ */
+
+ public static final int LET_EXPRESSION = 2013;
+
+ /**
+ * An XPath or XQuery "return" clause. Usage: Q
+ */
+
+ public static final int RETURN_EXPRESSION = 2014;
+
+
+ /**
+ * An XPath or XQuery "if" expression. Usage: Q
+ */
+
+ public static final int IF_EXPRESSION = 2015;
+
+ /**
+ * An XPath or XQuery "then" clause. Usage: Q
+ */
+
+ public static final int THEN_EXPRESSION = 2016;
+
+ /**
+ * An XPath or XQuery "else" clause. Usage: Q
+ */
+
+ public static final int ELSE_EXPRESSION = 2017;
+
+ /**
+ * A WHERE clause in a FLWOR expression. Usage: Q
+ */
+
+ public static final int WHERE_CLAUSE = 2018;
+
+ /**
+ * An order-by clause in a FLWOR expression. Usage: Q
+ */
+
+ public static final int ORDER_BY_CLAUSE = 2019;
+
+ /**
+ * An XPath or XQuery "typeswitch" expression. Usage: Q
+ */
+
+ public static final int TYPESWITCH_EXPRESSION = 2020;
+
+ /**
+ * CASE clause within "typeswitch". Usage: Q
+ */
+
+ public static final int CASE_EXPRESSION = 2021;
+
+ /**
+ * DEFAULT clause within "typeswitch". Usage: Q
+ */
+
+ public static final int DEFAULT_EXPRESSION = 2022;
+
+ /**
+ * An XPath or XQuery "validate" expression. Usage: Q
+ */
+
+ public static final int VALIDATE_EXPRESSION = 2023;
+
+ /**
+ * An XPath or XQuery path expression. Usage: C
+ */
+
+ public static final int PATH_EXPRESSION = 2025;
+
+ /**
+ * An XPath or XQuery "switch" expression. Usage: Q
+ */
+
+ public static final int SWITCH_EXPRESSION = 2026;
+
+ /**
+ * An XQuery Update copy-modify (transform) expression. Usage: Q
+ */
+
+ public static final int COPY_MODIFY_EXPRESSION = 2027;
+
+ /**
+ * An XQuery Update insert expression. Usage: Q
+ */
+
+ public static final int INSERT_EXPRESSION = 2028;
+
+ /**
+ * An XPath or XQuery replace expression. Usage: Q
+ */
+
+ public static final int REPLACE_EXPRESSION = 2029;
+
+ /**
+ * An XPath or XQuery delete expression. Usage: Q
+ */
+
+ public static final int DELETE_EXPRESSION = 2030;
+
+ /**
+ * An XPath or XQuery rename expression. Usage: Q
+ */
+
+ public static final int RENAME_EXPRESSION = 2031;
+
+
+ /**
+ * An explicit call of the fn:trace() function. Usage: QT
+ */
+
+ public static final int TRACE_CALL = 2041;
+
+ /**
+ * An XPath expression constructed dynamically using saxon:evaluate (or saxon:expression).
+ * Usage: QTC
+ */
+
+ public static final int SAXON_EVALUATE = 2051;
+
+ /**
+ * Lazy evaluation of an expression (this code is used to identify a context created as a saved
+ * copy of an existing context, to be stored in a Closure). Usage: C
+ */
+
+ public static final int LAZY_EVALUATION = 2063;
+
+ /**
+ * A function declaration in XSLT or XQuery
+ */
+
+ public static final int FUNCTION = 2065;
+
+ /**
+ * XPath expression, otherwise unclassified. The "expression" property references the actual expression,
+ * of class ComputedExpression. Used in fallback cases only.
+ */
+ public static final int XPATH_EXPRESSION = 2098;
+
+ /**
+ * Unclassified location. Used in fallback cases only.
+ */
+ public static final int UNCLASSIFIED = 2099;
+
+ /**
+ * Values of the form CLAUSE_BASE + n represent FLWOR clauses
+ */
+ public static final int CLAUSE_BASE = 3000;
+
+ private Location() {
+ }
+}
diff --git a/sf/saxon/trace/TimingCodeInjector.java b/sf/saxon/trace/TimingCodeInjector.java
new file mode 100644
index 0000000..14742b6
--- /dev/null
+++ b/sf/saxon/trace/TimingCodeInjector.java
@@ -0,0 +1,45 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.flwor.Clause;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+
+/**
+ * A code injector that wraps the body of a template or function in a TraceExpression, which causes
+ * the TimingTraceListener to be notified at the start and end of the function/template evaluation
+ */
+public class TimingCodeInjector extends TraceCodeInjector {
+
+ /**
+ * If tracing, wrap an expression in a trace instruction
+ *
+ * @param exp the expression to be wrapped
+ * @param env the static context
+ * @param construct integer constant identifying the kind of construct
+ * @param qName the name of the construct (if applicable)
+ * @return the expression that does the tracing
+ */
+
+ public Expression inject(Expression exp, StaticContext env, int construct, StructuredQName qName) {
+ if (construct == StandardNames.XSL_FUNCTION || construct == StandardNames.XSL_TEMPLATE) {
+ return super.inject(exp, env, construct, qName);
+ } else {
+ return exp;
+ }
+ }
+
+ public Clause injectClause(Clause target, StaticContext env, Container container) {
+ return null;
+ }
+}
+
diff --git a/sf/saxon/trace/TimingTraceListener.java b/sf/saxon/trace/TimingTraceListener.java
new file mode 100644
index 0000000..9e5fede
--- /dev/null
+++ b/sf/saxon/trace/TimingTraceListener.java
@@ -0,0 +1,287 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.PreparedStylesheet;
+import net.sf.saxon.event.StreamWriterToReceiver;
+import net.sf.saxon.event.TransformerReceiver;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.CompilerInfo;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.Stack;
+
+/**
+ * A trace listener that records timing information for templates and functions, outputting this
+ * information as an HTML report to a specified destination when the transformation completes.
+ */
+
+public class TimingTraceListener implements TraceListener {
+
+ private static class InstructionDetails {
+ public InstructionInfo instruct;
+ public long gross;
+ public long net;
+ public long count;
+ }
+
+
+ PrintStream out = System.err;
+ private long t_total;
+ /*@NotNull*/ private Stack<InstructionDetails> instructs = new Stack();
+ /*@NotNull*/ HashMap<InstructionInfo, InstructionDetails> instructMap = new HashMap<InstructionInfo, InstructionDetails>();
+ /*@Nullable*/ private Configuration config = null;
+
+ /**
+ * Set the PrintStream to which the output will be written.
+ *
+ * @param stream the PrintStream to be used for output. By default, the output is written
+ * to System.err.
+ * @throws XPathException
+ * @throws XMLStreamException
+ */
+
+ public void setOutputDestination(PrintStream stream) {
+ out = stream;
+ }
+
+ /**
+ * Called at start
+ */
+
+ public void open(/*@NotNull*/ Controller controller) {
+ config = controller.getConfiguration();
+ t_total = System.nanoTime();
+ }
+
+ /**
+ * Called at end. This method builds the XML out and analyzed html output
+ */
+
+ public void close() {
+ t_total = System.nanoTime() - t_total;
+ try {
+ PreparedStylesheet sheet = this.getStyleSheet();
+ Controller controller = (Controller) sheet.newTransformer();
+
+ Properties props = new Properties();
+ props.setProperty("method", "html");
+ props.setProperty("indent", "yes");
+ controller.setOutputProperties(props);
+ controller.setTraceListener(null);
+ TransformerReceiver tr = new TransformerReceiver(controller);
+ tr.open();
+ tr.setResult(new StreamResult(out));
+ XMLStreamWriter writer = new StreamWriterToReceiver(tr);
+ writer.writeStartDocument();
+
+ writer.writeStartElement("trace");
+ writer.writeAttribute("t-total", Double.toString((double) t_total / 1000000));
+ for (InstructionDetails ins : instructMap.values()) {
+ writer.writeStartElement("fn");
+ String name = "UNKNOWN";
+ if (ins.instruct.getObjectName() != null) {
+ name = ins.instruct.getObjectName().getDisplayName();
+ writer.writeAttribute("name", name);
+ } else {
+ if (ins.instruct.getProperty("name") != null) {
+ name = ins.instruct.getProperty("name").toString();
+ writer.writeAttribute("name", name);
+ }
+ }
+ if (ins.instruct.getProperty("match") != null) {
+ name = ins.instruct.getProperty("match").toString();
+ writer.writeAttribute("match", name);
+ }
+ writer.writeAttribute("construct", (ins.instruct.getConstructType() == StandardNames.XSL_FUNCTION ? "function" : "template"));
+ String file = ins.instruct.getSystemId();
+ if (file != null) {
+ if (file.length() > 15) {
+ file = "*" + file.substring(file.length() - 14);
+ }
+ writer.writeAttribute("file", "\"" + file + "\"");
+ }
+ writer.writeAttribute("count", Long.toString(ins.count));
+ writer.writeAttribute("t-sum-net", Double.toString((double) ins.net / 1000000));
+ writer.writeAttribute("t-avg-net", Double.toString((ins.net / (double) ins.count) / 1000000));
+ writer.writeAttribute("t-sum", Double.toString((double) ins.gross / 1000000));
+ writer.writeAttribute("t-avg", Double.toString((ins.gross / (double) ins.count) / 1000000));
+ writer.writeAttribute("line", Long.toString(ins.instruct.getLineNumber()));
+ writer.writeEndElement();
+ }
+ writer.writeEndElement();
+ writer.writeEndDocument();
+ writer.close();
+ } catch (TransformerConfigurationException e) {
+ System.err.println("Unable to transform timing profile information: " + e.getMessage());
+ } catch (TransformerException e) {
+ System.err.println("Unable to render timing profile information: " + e.getMessage());
+ } catch (XMLStreamException e) {
+ System.err.println("Unable to generate timing profile information: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Called when an instruction in the stylesheet gets processed
+ */
+
+ public void enter(/*@NotNull*/ InstructionInfo instruction, XPathContext context) {
+ int loc = instruction.getConstructType();
+ if (loc == StandardNames.XSL_FUNCTION || loc == StandardNames.XSL_TEMPLATE) {
+ long start = System.nanoTime();
+ InstructionDetails instructDetails = new InstructionDetails();
+ instructDetails.instruct = instruction;
+ instructDetails.gross = start;
+ instructs.add(instructDetails);
+ }
+ }
+
+ /**
+ * Called after an instruction of the stylesheet got processed
+ */
+
+ public void leave(/*@NotNull*/ InstructionInfo instruction) {
+ int loc = instruction.getConstructType();
+ if (loc == StandardNames.XSL_FUNCTION || loc == StandardNames.XSL_TEMPLATE) {
+ InstructionDetails instruct = instructs.peek();
+ long duration = System.nanoTime() - instruct.gross;
+ long net = duration - instruct.net;
+ instruct.net = net;
+ instruct.gross = duration;
+ InstructionDetails foundInstructDetails = instructMap.get(instruction);
+ if (foundInstructDetails == null) {
+ instruct.count = 1;
+ instructMap.put(instruction, instruct);
+ } else {
+ foundInstructDetails.count = foundInstructDetails.count + 1;
+ foundInstructDetails.gross = foundInstructDetails.gross + instruct.gross;
+ foundInstructDetails.net = foundInstructDetails.net + instruct.net;
+ }
+ instructs.pop();
+ if (instructs.size() > 0) {
+ InstructionDetails parentInstruct = instructs.peek();
+ parentInstruct.net = parentInstruct.net + duration;
+ }
+ }
+ }
+
+ /**
+ * Called when an item becomes current
+ */
+
+ public void startCurrentItem(Item item) {
+ }
+
+ /**
+ * Called after a node of the source tree got processed
+ */
+
+ public void endCurrentItem(Item item) {
+ }
+
+
+ /**
+ * Prepare Stylesheet to render the analyzed XML data out.
+ * This method can be overridden in a subclass to produce the output in a different format.
+ */
+ /*@NotNull*/ public PreparedStylesheet getStyleSheet() throws TransformerConfigurationException {
+
+ String xsl = "<?xml version='1.0' encoding='UTF-8'?>" +
+ "<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' " +
+ "xmlns:xs='http://www.w3.org/2001/XMLSchema' exclude-result-prefixes='xs' version='2.0'>" +
+ "<xsl:template match='*'>" +
+ "<html>" +
+ "<head>" +
+ "<title>Analysis of Stylesheet Execution Time</title>" +
+ "</head>" +
+ "<body>" +
+ "<h1>Analysis of Stylesheet Execution Time</h1>" +
+ "<p>Total time: <xsl:value-of select='format-number(@t-total, \"#0.000\")'/> milliseconds</p>" +
+ "<h2>Time spent in each template or function:</h2>" +
+ "<p>The table below is ordered by the total net time spent in the template or" +
+ " function. Gross time means the time including called templates and functions;" +
+ " net time means time excluding time spent in called templates and functions.</p>" +
+ "<table border='border' cellpadding='10'>" +
+ " <thead>" +
+ " <tr>" +
+ " <th>file</th>" +
+ " <th>line</th>" +
+ " <th>instruction</th>" +
+ " <th>count</th>" +
+ " <th>average time (gross)</th>" +
+ " <th>total time (gross)</th>" +
+ " <th>average time (net)</th>" +
+ " <th>total time (net)</th>" +
+ "</tr>" +
+ "</thead>" +
+ "<tbody>" +
+ " <xsl:for-each select='fn'>" +
+ /*"<xsl:sort select='@file'/>" +
+ "<xsl:sort select='@line'/>" +
+ "<xsl:sort select='@name'/>" +
+ "<xsl:sort select='@match'/>" +*/
+ " <xsl:sort select='number(@t-sum-net)' order='descending'/>" +
+ " <tr>" +
+ " <td>" +
+ " <xsl:value-of select='@file'/>" +
+ " </td>" +
+ " <td>" +
+ " <xsl:value-of select='@line'/>" +
+ " </td>" +
+ " <td>" +
+ " <xsl:value-of select='@construct, @name, @match'/>" +
+ " </td>" +
+ "<td align='right'>" +
+ " <xsl:value-of select='@count'/>" +
+ "</td>" +
+ " <td align='right'>" +
+ " <xsl:value-of select=\"format-number(@t-avg, '#0.000')\"/>" +
+ " </td>" +
+ " <td align='right'>" +
+ " <xsl:value-of select=\"format-number(@t-sum, '#0.000')\"/>" +
+ "</td>" +
+ " <td align='right'>" +
+ " <xsl:value-of select=\"format-number(@t-avg-net, '#0.000')\"/>" +
+ "</td>" +
+ "<td align='right'>" +
+ " <xsl:value-of select=\"format-number(@t-sum-net, '#0.000')\"/>" +
+ " </td>" +
+ "</tr>" +
+ " </xsl:for-each>" +
+ "</tbody>" +
+ "</table>" +
+ "</body>" +
+ "</html>" +
+ "</xsl:template>" +
+ "</xsl:stylesheet>";
+
+ Source styleSource = new StreamSource(new StringReader(xsl));
+ CompilerInfo compilerInfo = config.getDefaultXsltCompilerInfo();
+ compilerInfo.setCodeInjector(null);
+ return PreparedStylesheet.compile(styleSource, config, compilerInfo);
+
+ }
+
+}
+
diff --git a/sf/saxon/trace/TraceCodeInjector.java b/sf/saxon/trace/TraceCodeInjector.java
new file mode 100644
index 0000000..ce929ae
--- /dev/null
+++ b/sf/saxon/trace/TraceCodeInjector.java
@@ -0,0 +1,62 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.flwor.Clause;
+import net.sf.saxon.expr.flwor.TraceClause;
+import net.sf.saxon.expr.instruct.TraceExpression;
+import net.sf.saxon.expr.parser.CodeInjector;
+import net.sf.saxon.om.StructuredQName;
+
+/**
+ * A code injector that wraps every expression (other than a literal) in a TraceExpression, which causes
+ * a TraceListener to be notified when the expression is evaluated
+ */
+public class TraceCodeInjector implements CodeInjector {
+
+ /**
+ * If tracing, wrap an expression in a trace instruction
+ *
+ * @param exp the expression to be wrapped
+ * @param env the static context
+ * @param construct integer constant identifying the kind of construct
+ * @param qName the name of the construct (if applicable)
+ * @return the expression that does the tracing
+ */
+
+ public Expression inject(Expression exp, /*@NotNull*/ StaticContext env, int construct, StructuredQName qName) {
+ if (exp instanceof Literal) {
+ return exp;
+ }
+ TraceExpression trace = new TraceExpression(exp);
+ //ExpressionTool.copyLocationInfo(exp, trace);
+ trace.setNamespaceResolver(env.getNamespaceResolver());
+ trace.setConstructType(construct);
+ trace.setObjectName(qName);
+ //trace.setObjectNameCode(objectNameCode);
+ return trace;
+ }
+
+ /**
+ * If tracing, add a clause to a FLWOR expression that can be used to monitor requests for
+ * tuples to be processed
+ * @param target the clause whose evaluation is to be traced (or otherwise monitored)
+ * @param env the static context of the containing FLWOR expression
+ * @param container the container of the containing FLWOR Expression
+ * @return the new clause to do the tracing; or null if no tracing is required at this point
+ */
+
+ public Clause injectClause(Clause target, StaticContext env, Container container) {
+ return new TraceClause(target, env.getNamespaceResolver(), container);
+ }
+}
+
diff --git a/sf/saxon/trace/TraceEventMulticaster.java b/sf/saxon/trace/TraceEventMulticaster.java
new file mode 100644
index 0000000..eef8383
--- /dev/null
+++ b/sf/saxon/trace/TraceEventMulticaster.java
@@ -0,0 +1,184 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.Item;
+
+import java.io.PrintStream;
+import java.util.EventListener;
+
+/**
+ * A class which implements efficient and thread-safe multi-cast event
+ * dispatching for the TraceListener evants.
+ */
+public class TraceEventMulticaster implements TraceListener {
+
+ protected final EventListener a, b;
+
+ /**
+ * Creates an event multicaster instance which chains listener-a
+ * with listener-b.
+ *
+ * @param a listener-a
+ * @param b listener-b
+ */
+ protected TraceEventMulticaster(EventListener a, EventListener b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ public void setOutputDestination(PrintStream stream) {
+ ((TraceListener)a).setOutputDestination(stream);
+ ((TraceListener)b).setOutputDestination(stream);
+ }
+
+ /**
+ * Removes a listener from this multicaster and returns the
+ * resulting multicast listener.
+ *
+ * @param oldl the listener to be removed
+ */
+ /*@Nullable*/ protected EventListener remove(EventListener oldl) {
+ if (oldl == a) {
+ return b;
+ }
+ if (oldl == b) {
+ return a;
+ }
+ EventListener a2 = removeInternal(a, oldl);
+ EventListener b2 = removeInternal(b, oldl);
+ if (a2 == a && b2 == b) {
+ return this; // it's not here
+ }
+ return addInternal(a2, b2);
+ }
+
+ /**
+ * Called at start
+ */
+
+ public void open(Controller controller) {
+ ((TraceListener)a).open(controller);
+ ((TraceListener)b).open(controller);
+ }
+
+ /**
+ * Called at end
+ */
+
+ public void close() {
+ ((TraceListener)a).close();
+ ((TraceListener)b).close();
+ }
+
+
+ /**
+ * Called when an element of the stylesheet gets processed
+ */
+ public void enter(InstructionInfo element, XPathContext context) {
+ ((TraceListener)a).enter(element, context);
+ ((TraceListener)b).enter(element, context);
+ }
+
+ /**
+ * Called after an element of the stylesheet got processed
+ */
+ public void leave(InstructionInfo element) {
+ ((TraceListener)a).leave(element);
+ ((TraceListener)b).leave(element);
+ }
+
+ /**
+ * Called when an item becomes current
+ */
+ public void startCurrentItem(Item item) {
+ ((TraceListener)a).startCurrentItem(item);
+ ((TraceListener)b).startCurrentItem(item);
+ }
+
+ /**
+ * Called when an item ceases to be the current item
+ */
+ public void endCurrentItem(Item item) {
+ ((TraceListener)a).endCurrentItem(item);
+ ((TraceListener)b).endCurrentItem(item);
+ }
+
+ /**
+ * Adds trace-listener-a with trace-listener-b and
+ * returns the resulting multicast listener.
+ *
+ * @param a trace-listener-a
+ * @param b trace-listener-b
+ */
+ public static TraceListener add(TraceListener a, TraceListener b) {
+ return (TraceListener)addInternal(a, b);
+ }
+
+ /**
+ * Removes the old trace-listener from trace-listener-l and
+ * returns the resulting multicast listener.
+ *
+ * @param l trace-listener-l
+ * @param oldl the trace-listener being removed
+ */
+ public static TraceListener remove(TraceListener l, TraceListener oldl) {
+ return (TraceListener)removeInternal(l, oldl);
+ }
+
+ /**
+ * Returns the resulting multicast listener from adding listener-a
+ * and listener-b together.
+ * If listener-a is null, it returns listener-b;
+ * If listener-b is null, it returns listener-a
+ * If neither are null, then it creates and returns
+ * a new EventMulticaster instance which chains a with b.
+ *
+ * @param a event listener-a
+ * @param b event listener-b
+ */
+ protected static EventListener addInternal(EventListener a, EventListener b) {
+ if (a == null) {
+ return b;
+ }
+ if (b == null) {
+ return a;
+ }
+ return new TraceEventMulticaster(a, b);
+ }
+
+ /**
+ * Returns the resulting multicast listener after removing the
+ * old listener from listener-l.
+ * If listener-l equals the old listener OR listener-l is null,
+ * returns null.
+ * Else if listener-l is an instance of SaxonEventMulticaster,
+ * then it removes the old listener from it.
+ * Else, returns listener l.
+ *
+ * @param l the listener being removed from
+ * @param oldl the listener being removed
+ */
+
+ protected static EventListener removeInternal(EventListener l, EventListener oldl) {
+ if (l == oldl || l == null) {
+ return null;
+ } else if (l instanceof TraceEventMulticaster) {
+ return ((TraceEventMulticaster)l).remove(oldl);
+ } else {
+ return l; // it's not here
+ }
+ }
+
+}
+
+// * Modified by Michael Kay, Saxonica Limited to remove "throws SAXException" from all methods
+
diff --git a/sf/saxon/trace/XQueryTraceListener.java b/sf/saxon/trace/XQueryTraceListener.java
new file mode 100644
index 0000000..9449c5c
--- /dev/null
+++ b/sf/saxon/trace/XQueryTraceListener.java
@@ -0,0 +1,118 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.expr.flwor.Clause;
+import net.sf.saxon.om.StandardNames;
+
+/**
+ * A Simple trace listener for XQuery that writes messages (by default) to System.err
+ */
+
+public class XQueryTraceListener extends AbstractTraceListener {
+
+ /**
+ * Generate attributes to be included in the opening trace element
+ */
+
+ /*@NotNull*/ protected String getOpeningAttributes() {
+ return "";
+ }
+
+ /**
+ * Get the trace element tagname to be used for a particular construct. Return null for
+ * trace events that are ignored by this trace listener.
+ */
+
+ /*@Nullable*/ protected String tag(int construct) {
+ switch (construct) {
+ case StandardNames.XSL_FUNCTION:
+ return "function";
+ case StandardNames.XSL_VARIABLE:
+ return "variable";
+ case StandardNames.XSL_ELEMENT:
+ return "element";
+ case StandardNames.XSL_ATTRIBUTE:
+ return "attribute";
+ case StandardNames.XSL_COMMENT:
+ return "comment";
+ case StandardNames.XSL_DOCUMENT:
+ return "document";
+ case StandardNames.XSL_PROCESSING_INSTRUCTION:
+ return "processing-instruction";
+ case StandardNames.XSL_TEXT:
+ return "text";
+ case StandardNames.XSL_NAMESPACE:
+ return "namespace";
+ case Location.LITERAL_RESULT_ELEMENT:
+ return "element";
+ case Location.LITERAL_RESULT_ATTRIBUTE:
+ return "attribute";
+ case Location.FUNCTION_CALL:
+ //return "function-call";
+ return null;
+ case Location.FOR_EXPRESSION:
+ return "for";
+ case Location.LET_EXPRESSION:
+ return "let";
+ case Location.WHERE_CLAUSE:
+ return "where";
+ case Location.ORDER_BY_CLAUSE:
+ return "sort";
+ case Location.RETURN_EXPRESSION:
+ return "return";
+ case Location.COPY_MODIFY_EXPRESSION:
+ return "modify";
+ case Location.INSERT_EXPRESSION:
+ return "insert";
+ case Location.DELETE_EXPRESSION:
+ return "delete";
+ case Location.REPLACE_EXPRESSION:
+ return "replace";
+ case Location.RENAME_EXPRESSION:
+ return "rename";
+ case Location.TYPESWITCH_EXPRESSION:
+ return "typeswitch";
+ case Location.VALIDATE_EXPRESSION:
+ return "validate";
+ case Location.IF_EXPRESSION:
+ return "if";
+ case Location.THEN_EXPRESSION:
+ return "then";
+ case Location.ELSE_EXPRESSION:
+ return "else";
+ case Location.CASE_EXPRESSION:
+ return "case";
+ case Location.SWITCH_EXPRESSION:
+ return "switch";
+ case Location.DEFAULT_EXPRESSION:
+ return "default";
+ case Location.TRACE_CALL:
+ return "user-trace";
+ case Location.CLAUSE_BASE + Clause.COUNT:
+ return "count";
+ case Location.CLAUSE_BASE + Clause.FOR:
+ return "for";
+ case Location.CLAUSE_BASE + Clause.LET:
+ return "let";
+ case Location.CLAUSE_BASE + Clause.GROUPBYCLAUSE:
+ return "group-by";
+ case Location.CLAUSE_BASE + Clause.ORDERBYCLAUSE:
+ return "order-by";
+ case Location.CLAUSE_BASE + Clause.WHERE:
+ return "where";
+ case Location.CLAUSE_BASE + Clause.WINDOW:
+ return "window";
+ default:
+ //return "Other";
+ return null;
+ }
+ }
+
+}
+
diff --git a/sf/saxon/trace/XSLTTraceCodeInjector.java b/sf/saxon/trace/XSLTTraceCodeInjector.java
new file mode 100644
index 0000000..494f782
--- /dev/null
+++ b/sf/saxon/trace/XSLTTraceCodeInjector.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.om.StructuredQName;
+
+/**
+ * A code injector that wraps every expression (other than a literal) in a TraceExpression, which causes
+ * a TraceListener to be notified when the expression is evaluated
+ */
+public class XSLTTraceCodeInjector extends TraceCodeInjector {
+
+ /**
+ * If tracing, wrap an expression in a trace instruction
+ *
+ * @param exp the expression to be wrapped
+ * @param env the static context
+ * @param construct integer constant identifying the kind of construct
+ * @param qName the name of the construct (if applicable)
+ * @return the expression that does the tracing
+ */
+
+ public Expression inject(Expression exp, /*@NotNull*/ StaticContext env, int construct, StructuredQName qName) {
+ if (XSLTTraceListener.tagName(construct) != null) {
+ return super.inject(exp, env, construct, qName);
+ } else {
+ return exp;
+ }
+ }
+}
+
diff --git a/sf/saxon/trace/XSLTTraceListener.java b/sf/saxon/trace/XSLTTraceListener.java
new file mode 100644
index 0000000..44aaf7d
--- /dev/null
+++ b/sf/saxon/trace/XSLTTraceListener.java
@@ -0,0 +1,64 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trace;
+
+import net.sf.saxon.expr.parser.CodeInjector;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.StandardNames;
+
+/**
+ * A Simple trace listener for XSLT that writes messages (by default) to System.err
+ */
+
+public class XSLTTraceListener extends AbstractTraceListener {
+
+ @Override
+ public CodeInjector getCodeInjector() {
+ return new XSLTTraceCodeInjector();
+ }
+
+ /**
+ * Generate attributes to be included in the opening trace element
+ */
+
+ protected String getOpeningAttributes() {
+ return "xmlns:xsl=\"" + NamespaceConstant.XSLT + '\"';
+ }
+
+ /**
+ * Get the trace element tagname to be used for a particular construct. Return null for
+ * trace events that are ignored by this trace listener.
+ */
+
+ /*@Nullable*/ protected String tag(int construct) {
+ return tagName(construct);
+ }
+
+ public static String tagName(int construct) {
+ if (construct < 1024) {
+ return StandardNames.getDisplayName(construct);
+ }
+ switch (construct) {
+ case Location.LITERAL_RESULT_ELEMENT:
+ return "LRE";
+ case Location.LITERAL_RESULT_ATTRIBUTE:
+ return "ATTR";
+ case Location.LET_EXPRESSION:
+ return "xsl:variable";
+ case Location.EXTENSION_INSTRUCTION:
+ return "extension-instruction";
+ case Location.TRACE_CALL:
+ return "user-trace";
+ default:
+ return null;
+ }
+ }
+
+}
+
+
diff --git a/sf/saxon/trace/package.html b/sf/saxon/trace/package.html
new file mode 100644
index 0000000..3a908fa
--- /dev/null
+++ b/sf/saxon/trace/package.html
@@ -0,0 +1,33 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.trace</title>
+</head>
+
+<body>
+
+<p>This package provides an interface to Saxon tracing and debugging capabilities.</p>
+
+<p>The package was originally created by Edwin Glaser.</p>
+
+<p>The package includes three tracing modules that can be optionally selected:
+<code>XSLTTraceListener</code>, <code>XQueryTraceListener</code>, and
+<code>TimedTraceListener</code>. These all receive notification of the same events,
+but select and format the events in different ways to meet different requirements.
+Other events are notified through the <code>TraceListener</code> interface that
+are ignored by tracing applications, but may be of interest to debuggers.</p>
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/trans/BuiltInRuleSet.java b/sf/saxon/trans/BuiltInRuleSet.java
new file mode 100644
index 0000000..e1f6b91
--- /dev/null
+++ b/sf/saxon/trans/BuiltInRuleSet.java
@@ -0,0 +1,56 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.ParameterSet;
+import net.sf.saxon.om.Item;
+
+import java.io.Serializable;
+
+/**
+ * Defines a set of built-in template rules (rules for use when no user-defined template
+ * rules match a given node)
+ */
+public interface BuiltInRuleSet extends Serializable {
+
+ /**
+ * Perform the built-in template action for a given item.
+ *
+ * @param item the item to be processed
+ * @param parameters the parameters supplied to apply-templates
+ * @param tunnelParams the tunnel parameters to be passed through
+ * @param context the dynamic evaluation context
+ * @param locationId location of the instruction (apply-templates, apply-imports etc) that caused
+ * the built-in template to be invoked
+ * @throws XPathException if any dynamic error occurs
+ */
+
+ public void process( Item item,
+ ParameterSet parameters,
+ ParameterSet tunnelParams,
+ XPathContext context,
+ int locationId) throws XPathException;
+
+ /**
+ * Get the default action for unmatched nodes
+ * @param nodeKind the node kind
+ * @return the default action for unmatched element nodes: one of DEEP_COPY, APPLY_TEMPLATES, DEEP_SKIP, FAIL
+ */
+
+ public int getDefaultAction(int nodeKind);
+
+ public static final int DEEP_COPY = 1;
+ public static final int DEEP_SKIP = 3;
+ public static final int FAIL = 4;
+ public static final int SHALLOW_COPY = 5;
+ public static final int SHALLOW_SKIP = 6;
+ public static final int TEXT_COPY = 7;
+
+}
+
diff --git a/sf/saxon/trans/CommandLineOptions.java b/sf/saxon/trans/CommandLineOptions.java
new file mode 100644
index 0000000..deab0c8
--- /dev/null
+++ b/sf/saxon/trans/CommandLineOptions.java
@@ -0,0 +1,748 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.expr.instruct.GlobalParameterSet;
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.Initializer;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.sxpath.XPathDynamicContext;
+import net.sf.saxon.sxpath.XPathEvaluator;
+import net.sf.saxon.sxpath.XPathExpression;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.DayTimeDurationValue;
+import net.sf.saxon.value.NumericValue;
+import net.sf.saxon.value.SequenceExtent;
+import net.sf.saxon.value.UntypedAtomicValue;
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+import java.io.File;
+import java.math.BigDecimal;
+import java.text.Collator;
+import java.util.*;
+
+/**
+ * This is a helper class for classes such as net.sf.saxon.Transform and net.sf.saxon.Query that process
+ * command line options
+ */
+public class CommandLineOptions {
+
+ public static final int TYPE_BOOLEAN = 1;
+ public static final int TYPE_FILENAME = 2;
+ public static final int TYPE_CLASSNAME = 3;
+ public static final int TYPE_ENUMERATION = 4;
+ public static final int TYPE_INTEGER = 5;
+ public static final int TYPE_QNAME = 6;
+ public static final int TYPE_FILENAME_LIST = 7;
+ public static final int TYPE_DATETIME = 8;
+ public static final int TYPE_STRING = 9;
+ public static final int TYPE_INTEGER_PAIR = 10;
+
+ public static final int VALUE_REQUIRED = 1<<8;
+ public static final int VALUE_PROHIBITED = 2<<8;
+
+ /*@NotNull*/ HashMap<String, Integer> recognizedOptions = new HashMap<String, Integer>();
+ /*@NotNull*/ HashMap<String, String> optionHelp = new HashMap<String, String>();
+ /*@NotNull*/ Properties namedOptions = new Properties();
+ /*@NotNull*/ Properties configOptions = new Properties();
+ /*@NotNull*/ Map<String, Set<String>> permittedValues = new HashMap<String, Set<String>>();
+ /*@NotNull*/ Map<String, String> defaultValues = new HashMap<String, String>();
+ /*@NotNull*/ List<String> positionalOptions = new ArrayList<String>();
+ /*@NotNull*/ Properties paramValues = new Properties();
+ /*@NotNull*/ Properties paramExpressions = new Properties();
+ /*@NotNull*/ Properties paramFiles = new Properties();
+ /*@NotNull*/ Properties serializationParams = new Properties();
+
+ /**
+ * Set the permitted options.
+ * @param option A permitted option.
+ * @param optionProperties of this option, for example whether it is mandatory
+ * @param helpText message to be output if the user needs help concerning this option
+ */
+
+ public void addRecognizedOption(String option, int optionProperties, String helpText) {
+ recognizedOptions.put(option, optionProperties);
+ optionHelp.put(option, helpText);
+ if ((optionProperties & 0xff) == TYPE_BOOLEAN) {
+ setPermittedValues(option, new String[]{"on", "off"}, "on");
+ }
+ }
+
+ /**
+ * Set the permitted values for an option
+ * @param option the option keyword
+ * @param values the set of permitted values
+ * @param defaultValue the default value if the option is supplied but no value is given. May be null if no
+ * default is defined.
+ */
+
+ public void setPermittedValues(String option, String[] values, /*@Nullable*/ String defaultValue) {
+ Set<String> valueSet = new HashSet<String>();
+ valueSet.addAll(Arrays.asList(values));
+ permittedValues.put(option, valueSet);
+ if (defaultValue != null) {
+ defaultValues.put(option, defaultValue);
+ }
+ }
+
+ /**
+ * Display a list of the values permitted for an option with type enumeration
+ * @param permittedValues the set of permitted values
+ * @return the set of values as a string, pipe-separated
+ */
+
+ private static String displayPermittedValues(/*@NotNull*/ Set<String> permittedValues) {
+ FastStringBuffer sb = new FastStringBuffer(20);
+ for (String val: permittedValues) {
+ if ("".equals(val)) {
+ sb.append("\"\"");
+ } else {
+ sb.append(val);
+ }
+ sb.append('|');
+ }
+ sb.setLength(sb.length()-1);
+ return sb.toString();
+ }
+
+ /**
+ * Set the options actually present on the command line
+ * @param args the options supplied on the command line
+ * @throws XPathException if an unrecognized or invalid option is found
+ */
+
+ public void setActualOptions(/*@NotNull*/ String[] args) throws XPathException {
+ for (String arg : args) {
+ if ("-".equals(arg)) {
+ positionalOptions.add(args[1]);
+ } else if (arg.charAt(0) == '-') {
+ String option;
+ String value = "";
+ if (arg.length() > 5 && arg.charAt(1) == '-') {
+ // --featureKey:value format
+ int colon = arg.lastIndexOf(':');
+ if (colon > 0 && colon < arg.length() - 1) {
+ option = arg.substring(2, colon);
+ value = arg.substring(colon + 1);
+ configOptions.setProperty(option, value);
+ } else if (colon > 0 && colon == arg.length() - 1) {
+ option = arg.substring(2, colon);
+ configOptions.setProperty(option, "");
+ } else {
+ option = arg.substring(2);
+ configOptions.setProperty(option, "true");
+ }
+ } else {
+ int colon = arg.indexOf(':');
+ if (colon > 0 && colon < arg.length() - 1) {
+ option = arg.substring(1, colon);
+ value = arg.substring(colon + 1);
+ } else {
+ option = arg.substring(1);
+ }
+ if (recognizedOptions.get(option) == null) {
+ throw new XPathException("Command line option -" + option +
+ " is not recognized. Options available: " + displayPermittedOptions());
+ }
+ if (namedOptions.getProperty(option) != null) {
+ throw new XPathException("Command line option -" + option + " appears more than once");
+ } else if ("?".equals(value)) {
+ displayOptionHelp(option);
+ throw new XPathException("No processing requested");
+ } else {
+ if ("".equals(value)) {
+ int prop = recognizedOptions.get(option);
+ if ((prop & VALUE_REQUIRED) != 0) {
+ String msg = "Command line option -" + option + " requires a value";
+ if (permittedValues.get(option) != null) {
+ msg += ": permitted values are " + displayPermittedValues(permittedValues.get(option));
+ }
+ throw new XPathException(msg);
+ }
+ String defaultValue = defaultValues.get(option);
+ if (defaultValue != null) {
+ value = defaultValue;
+ }
+ } else {
+ int prop = recognizedOptions.get(option);
+ if ((prop & VALUE_PROHIBITED) != 0) {
+ String msg = "Command line option -" + option + " does not expect a value";
+ throw new XPathException(msg);
+ }
+ }
+ Set<String> permitted = permittedValues.get(option);
+ if (permitted != null && !permitted.contains(value)) {
+ throw new XPathException("Bad option value " + arg +
+ ": permitted values are " + displayPermittedValues(permitted));
+ }
+ namedOptions.setProperty(option, value);
+ }
+ }
+ } else {
+ // handle keyword=value options
+ int eq = arg.indexOf('=');
+ if (eq >= 1) {
+ String keyword = arg.substring(0, eq);
+ String value = "";
+ if (eq < arg.length() - 1) {
+ value = arg.substring(eq + 1);
+ }
+ char ch = arg.charAt(0);
+ if (ch == '!' && eq >= 2) {
+ serializationParams.setProperty(keyword.substring(1), value);
+ } else if (ch == '?' && eq >= 2) {
+ paramExpressions.setProperty(keyword.substring(1), value);
+ } else if (ch == '+' && eq >= 2) {
+ paramFiles.setProperty(keyword.substring(1), value);
+ } else {
+ paramValues.setProperty(keyword, value);
+ }
+ } else {
+ positionalOptions.add(arg);
+ }
+ }
+ }
+ }
+
+ /**
+ * Test whether there is any keyword=value option present
+ * @return true if there are any keyword=value options
+ */
+
+ public boolean definesParameterValues() {
+ return !serializationParams.isEmpty() ||
+ !paramExpressions.isEmpty() ||
+ !paramFiles.isEmpty() ||
+ !paramValues.isEmpty();
+ }
+
+ /**
+ * Prescan the command line arguments to see if any of them imply use of a schema-aware processor
+ * @return true if a schema-aware processor is needed
+ */
+
+ public boolean testIfSchemaAware() {
+ return getOptionValue("sa") != null ||
+ getOptionValue("outval") != null ||
+ getOptionValue("val") != null ||
+ getOptionValue("vlax") != null ||
+ getOptionValue("xsd") != null ||
+ getOptionValue("xsdversion") != null;
+ }
+
+ /**
+ * Apply options to the Configuration
+ * @param config the Configuration
+ * @throws javax.xml.transform.TransformerException if invalid options are present
+ */
+
+ public void applyToConfiguration(/*@NotNull*/ final Configuration config) throws TransformerException {
+
+ for (Enumeration e = configOptions.propertyNames(); e.hasMoreElements();) {
+ String name = (String)e.nextElement();
+ String value = configOptions.getProperty(name);
+ try {
+ config.setConfigurationProperty("http://saxon.sf.net/feature/" + name, value);
+ } catch (IllegalArgumentException err) {
+ throw new XPathException(err.getMessage());
+ }
+ }
+
+ String value = getOptionValue("catalog");
+ if (value != null) {
+ if (getOptionValue("r") != null) {
+ throw new XPathException("Cannot use -catalog and -r together");
+ }
+ if (getOptionValue("x") != null) {
+ throw new XPathException("Cannot use -catalog and -x together");
+ }
+ if (getOptionValue("y") != null) {
+ throw new XPathException("Cannot use -catalog and -y together");
+ }
+ try {
+ config.getClass("org.apache.xml.resolver.CatalogManager", false, null);
+ XmlCatalogResolver.setCatalog(value, config, getOptionValue("t") != null);
+ } catch (XPathException err) {
+ throw new XPathException("Failed to load Apache catalog resolver library", err);
+ }
+ }
+
+ value = getOptionValue("cr");
+ if (value != null) {
+ Object resolver = config.getInstance(value, null);
+ config.setConfigurationProperty(FeatureKeys.COLLECTION_URI_RESOLVER, resolver);
+ }
+
+ value = getOptionValue("dtd");
+ if (value != null) {
+ if ("on".equals(value)) {
+ config.getParseOptions().setDTDValidationMode(Validation.STRICT);
+ } else if ("off".equals(value)) {
+ config.getParseOptions().setDTDValidationMode(Validation.SKIP);
+ } else if ("recover".equals(value)) {
+ config.getParseOptions().setDTDValidationMode(Validation.LAX);
+ }
+ }
+
+ value = getOptionValue("expand");
+ if (value != null) {
+ config.getParseOptions().setExpandAttributeDefaults("on".equals(value));
+ }
+
+ value = getOptionValue("ext");
+ if (value != null) {
+ config.setBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS,
+ "on".equals(value));
+ }
+
+ value = getOptionValue("l");
+ if (value != null) {
+ config.setBooleanProperty(FeatureKeys.LINE_NUMBERING,
+ "on".equals(value));
+ }
+
+ value = getOptionValue("m");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.MESSAGE_EMITTER_CLASS, value);
+ }
+
+ value = getOptionValue("opt");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.OPTIMIZATION_LEVEL, value);
+ }
+
+ value = getOptionValue("or");
+ if (value != null) {
+ Object resolver = config.getInstance(value, null);
+ config.setConfigurationProperty(FeatureKeys.OUTPUT_URI_RESOLVER, resolver);
+ }
+
+ value = getOptionValue("outval");
+ if (value != null) {
+ Boolean isRecover = "recover".equals(value);
+ config.setConfigurationProperty(FeatureKeys.VALIDATION_WARNINGS, isRecover);
+ config.setConfigurationProperty(FeatureKeys.VALIDATION_COMMENTS, isRecover);
+ }
+
+ value = getOptionValue("r");
+ if (value != null) {
+ config.setURIResolver(config.makeURIResolver(value));
+ }
+
+ value = getOptionValue("strip");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.STRIP_WHITESPACE, value);
+ }
+
+ value = getOptionValue("TJ");
+ if (value != null) {
+ config.setBooleanProperty(FeatureKeys.TRACE_EXTERNAL_FUNCTIONS,
+ "on".equals(value));
+ }
+
+ value = getOptionValue("tree");
+ if (value != null) {
+ if ("linked".equals(value)) {
+ config.setTreeModel(Builder.LINKED_TREE);
+ } else if ("tiny".equals(value)) {
+ config.setTreeModel(Builder.TINY_TREE);
+ } else if ("tinyc".equals(value)) {
+ config.setTreeModel(Builder.TINY_TREE_CONDENSED);
+ }
+ }
+
+ value = getOptionValue("val");
+ if (value != null) {
+ if ("strict".equals(value)) {
+ config.setConfigurationProperty(FeatureKeys.SCHEMA_VALIDATION, Validation.STRICT);
+ } else if ("lax".equals(value)) {
+ config.setConfigurationProperty(FeatureKeys.SCHEMA_VALIDATION, Validation.LAX);
+ }
+ }
+
+ value = getOptionValue("versionmsg");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.VERSION_WARNING,
+ "on".equals(value));
+ }
+
+ value = getOptionValue("warnings");
+ if (value != null) {
+ if ("silent".equals(value)) {
+ config.setConfigurationProperty(FeatureKeys.RECOVERY_POLICY,
+ Configuration.RECOVER_SILENTLY);
+ } else if ("recover".equals(value)) {
+ config.setConfigurationProperty(FeatureKeys.RECOVERY_POLICY,
+ Configuration.RECOVER_WITH_WARNINGS);
+ } else if ("fatal".equals(value)) {
+ config.setConfigurationProperty(FeatureKeys.RECOVERY_POLICY,
+ Configuration.DO_NOT_RECOVER);
+ }
+ }
+
+ value = getOptionValue("x");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.SOURCE_PARSER_CLASS, value);
+ }
+
+ value = getOptionValue("xi");
+ if (value != null) {
+ config.setBooleanProperty(FeatureKeys.XINCLUDE,
+ "on".equals(value));
+ }
+
+ value = getOptionValue("xmlversion");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.XML_VERSION, value);
+ }
+
+ value = getOptionValue("xsdversion");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.XSD_VERSION, value);
+ }
+
+ value = getOptionValue("xsiloc");
+ if (value != null) {
+ config.setBooleanProperty(FeatureKeys.USE_XSI_SCHEMA_LOCATION,
+ "on".equals(value));
+ }
+
+ value = getOptionValue("xsltversion");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.XSLT_VERSION, value);
+ }
+
+ value = getOptionValue("y");
+ if (value != null) {
+ config.setConfigurationProperty(FeatureKeys.STYLE_PARSER_CLASS, value);
+ }
+
+ // The init option must be done last
+
+ value = getOptionValue("init");
+ if (value != null) {
+ Initializer initializer = (Initializer)config.getInstance(value, null);
+ initializer.initialize(config);
+ }
+
+ }
+
+ /**
+ * Display the list the permitted options
+ * @return the list of permitted options, as a string
+ */
+
+ public String displayPermittedOptions() {
+ String[] options = new String[recognizedOptions.size()];
+ options = new ArrayList<String>(recognizedOptions.keySet()).toArray(options);
+ Arrays.sort(options, Collator.getInstance());
+ FastStringBuffer sb = new FastStringBuffer(100);
+ for (String opt : options) {
+ sb.append(" -");
+ sb.append(opt);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Display help for a specific option on the System.err output (in response to -opt:?)
+ * @param option: the option for which help is required
+ */
+
+ private void displayOptionHelp(String option) {
+ System.err.println("Help for -" + option + " option");
+ int prop = recognizedOptions.get(option);
+ if ((prop & VALUE_PROHIBITED) == 0) {
+ switch ((prop & 0xff)) {
+ case TYPE_BOOLEAN:
+ System.err.println("Value: on|off");
+ break;
+ case TYPE_INTEGER:
+ System.err.println("Value: integer");
+ break;
+ case TYPE_FILENAME:
+ System.err.println("Value: file name");
+ break;
+ case TYPE_FILENAME_LIST:
+ System.err.println("Value: list of file names, semicolon-separated");
+ break;
+ case TYPE_CLASSNAME:
+ System.err.println("Value: Java fully-qualified class name");
+ break;
+ case TYPE_QNAME:
+ System.err.println("Value: QName in Clark notation ({uri}local)");
+ break;
+ case TYPE_STRING:
+ System.err.println("Value: string");
+ break;
+ case TYPE_INTEGER_PAIR:
+ System.err.println("Value: int,int");
+ break;
+ case TYPE_ENUMERATION:
+ String message = "Value: one of ";
+ message += displayPermittedValues(permittedValues.get(option));
+ System.err.println(message);
+ break;
+ default:
+ break;
+ }
+ }
+ System.err.println("Meaning: " + optionHelp.get(option));
+ }
+
+ /**
+ * Get the value of a named option. Returns null if the option was not present on the command line.
+ * Returns "" if the option was present but with no value ("-x" or "-x:").
+ * @param option the option keyword
+ * @return the option value, or null if not specified.
+ */
+
+ public String getOptionValue(String option) {
+ return namedOptions.getProperty(option);
+ }
+
+ /**
+ * Get the options specified positionally, that is, without a leading "-"
+ * @return the list of positional options
+ */
+
+ /*@NotNull*/ public List<String> getPositionalOptions() {
+ return positionalOptions;
+ }
+
+ /**
+ * Apply requested parameters to a controller, a query context, or a set of output properties, as appropriate
+ * @param config the Saxon configuration
+ * @param params The GlobalParameterSet object into which the parameters are copied
+ * @param outputProperties the serialization properties. May be null.
+ * @throws javax.xml.transform.TransformerException if invalid options are found
+ */
+
+ public void setParams(/*@NotNull*/ Configuration config, GlobalParameterSet params, /*@Nullable*/ Properties outputProperties)
+ throws TransformerException {
+ boolean useURLs = "on".equals(getOptionValue("u"));
+ for (Enumeration e = paramValues.propertyNames(); e.hasMoreElements();) {
+ String name = (String)e.nextElement();
+ String value = paramValues.getProperty(name);
+ params.put(StructuredQName.fromClarkName(name), value);
+ }
+ for (Enumeration e = paramFiles.propertyNames(); e.hasMoreElements();) {
+ String name = (String)e.nextElement();
+ String value = paramFiles.getProperty(name);
+ List<Source> sourceList = new ArrayList<Source>();
+ boolean isDirectory = loadDocuments(value, useURLs, config, true, sourceList);
+ params.put(StructuredQName.fromClarkName(name), isDirectory ? sourceList : sourceList.get(0));
+ }
+ for (Enumeration e = paramExpressions.propertyNames(); e.hasMoreElements();) {
+ String name = (String)e.nextElement();
+ String value = paramExpressions.getProperty(name);
+ // parameters starting with "?" are taken as XPath expressions
+ XPathEvaluator xpe = new XPathEvaluator(config);
+ XPathExpression expr = xpe.createExpression(value);
+ XPathDynamicContext context = expr.createDynamicContext();
+ Sequence val = SequenceExtent.makeSequenceExtent(expr.iterate(context));
+ params.put(StructuredQName.fromClarkName(name), val);
+ }
+ for (Enumeration e = serializationParams.propertyNames(); e.hasMoreElements();) {
+ String name = (String)e.nextElement();
+ String value = serializationParams.getProperty(name);
+ // parameters starting with "!" are taken as output properties
+ // Allow the prefix "!saxon:" instead of "!{http://saxon.sf.net}"
+ if (name.startsWith("saxon:")) {
+ name = "{" + NamespaceConstant.SAXON + "}" + name.substring(6);
+ }
+ if (outputProperties != null) {
+ outputProperties.setProperty(name, value);
+ }
+ }
+ }
+
+ /**
+ * Apply XSLT 3.0 static parameters to a compilerInfo
+ * @param config the Saxon configuration
+ * @param compiler The CompilerInfo object into which the parameters are copied
+ * @throws javax.xml.transform.TransformerException if invalid options are found
+ */
+
+ public void applyStaticParams(Configuration config, CompilerInfo compiler)
+ throws TransformerException {
+ for (Enumeration e = paramValues.propertyNames(); e.hasMoreElements();) {
+ String name = (String)e.nextElement();
+ String value = paramValues.getProperty(name);
+ compiler.setParameter(StructuredQName.fromClarkName(name), new UntypedAtomicValue(value));
+ }
+ for (Enumeration e = paramExpressions.propertyNames(); e.hasMoreElements();) {
+ String name = (String)e.nextElement();
+ String value = paramExpressions.getProperty(name);
+ // parameters starting with "?" are taken as XPath expressions
+ XPathEvaluator xpe = new XPathEvaluator(config);
+ XPathExpression expr = xpe.createExpression(value);
+ XPathDynamicContext context = expr.createDynamicContext();
+ Sequence val = SequenceExtent.makeSequenceExtent(expr.iterate(context));
+ compiler.setParameter(StructuredQName.fromClarkName(name), val);
+ }
+
+ }
+
+
+ /**
+ * Load a document, or all the documents in a directory, given a filename or URL
+ * @param sourceFileName the name of the source file or directory
+ * @param useURLs true if the filename argument is to be treated as a URI
+ * @param config the Saxon configuration
+ * @param useSAXSource true if the method should use a SAXSource rather than a StreamSource
+ * @param sources an empty list which the method will populate.
+ * If sourceFileName represents a single source document, a corresponding Source is
+ * added to the list. If sourceFileName represents a directory, multiple Source
+ * objects, one for each file in the directory, are added to the list
+ * @return true if the supplied sourceFileName was found to be a directory
+ * @throws javax.xml.transform.TransformerException if access to documents fails
+ */
+
+ /*@Nullable*/ public static boolean loadDocuments(/*@NotNull*/ String sourceFileName, boolean useURLs,
+ /*@NotNull*/ Configuration config, boolean useSAXSource, List<Source> sources)
+ throws TransformerException {
+
+ Source sourceInput;
+ XMLReader parser;
+ if (useURLs || sourceFileName.startsWith("http:") || sourceFileName.startsWith("file:")) {
+ sourceInput = config.getURIResolver().resolve(sourceFileName, null);
+ if (sourceInput == null) {
+ sourceInput = config.getSystemURIResolver().resolve(sourceFileName, null);
+ }
+ sources.add(sourceInput);
+ return false;
+ } else if (sourceFileName.equals("-")) {
+ // take input from stdin
+ if (useSAXSource) {
+ parser = config.getSourceParser();
+ sourceInput = new SAXSource(parser, new InputSource(System.in));
+ } else {
+ sourceInput = new StreamSource(System.in);
+ }
+ sources.add(sourceInput);
+ return false;
+ } else {
+ File sourceFile = new File(sourceFileName);
+ if (!sourceFile.exists()) {
+ throw new TransformerException("Source file " + sourceFile + " does not exist");
+ }
+ if (sourceFile.isDirectory()) {
+ parser = config.getSourceParser();
+ String[] files = sourceFile.list();
+ for (String file1 : files) {
+ File file = new File(sourceFile, file1);
+ if (!file.isDirectory()) {
+ if (useSAXSource) {
+ InputSource eis = new InputSource(file.toURI().toString());
+ sourceInput = new SAXSource(parser, eis);
+ // it's safe to use the same parser for each document, as they
+ // will be processed one at a time.
+ } else {
+ sourceInput = new StreamSource(file.toURI().toString());
+ }
+ sources.add(sourceInput);
+ }
+ }
+ return true;
+ } else {
+ if (useSAXSource) {
+ InputSource eis = new InputSource(sourceFile.toURI().toString());
+ sourceInput = new SAXSource(config.getSourceParser(), eis);
+ } else {
+ sourceInput = new StreamSource(sourceFile.toURI().toString());
+ }
+ sources.add(sourceInput);
+ return false;
+ }
+ }
+ }
+
+ public static void loadAdditionalSchemas(/*@NotNull*/ Configuration config, String additionalSchemas)
+ throws TransformerException {
+ StringTokenizer st = new StringTokenizer(additionalSchemas, ";");
+ while (st.hasMoreTokens()) {
+ String schema = st.nextToken();
+ File schemaFile = new File(schema);
+ if (!schemaFile.exists()) {
+ throw new TransformerException("Schema document " + schema + " not found");
+ }
+ config.addSchemaSource(new StreamSource(schemaFile));
+ }
+ }
+
+ /*@NotNull*/ public static String showExecutionTime(long millisecs) {
+ if (millisecs < 1000) {
+ return millisecs + "ms";
+ } else {
+ try {
+ DayTimeDurationValue d = new DayTimeDurationValue(1, 0, 0, 0, millisecs/1000, ((int)millisecs%1000)*1000);
+ long days = ((NumericValue)d.getComponent(Component.DAY)).longValue();
+ long hours = ((NumericValue)d.getComponent(Component.HOURS)).longValue();
+ long minutes = ((NumericValue)d.getComponent(Component.MINUTES)).longValue();
+ BigDecimal seconds = ((NumericValue)d.getComponent(Component.SECONDS)).getDecimalValue();
+ FastStringBuffer fsb = new FastStringBuffer(256);
+ if (days > 0) {
+ fsb.append(days + "days ");
+ }
+ if (hours > 0) {
+ fsb.append(hours + "h ");
+ }
+ if (minutes > 0) {
+ fsb.append(minutes + "m ");
+ }
+ fsb.append(seconds + "s");
+ return fsb.toString() + " (" + millisecs + "ms)";
+ } catch (XPathException e) {
+ return millisecs + "ms";
+ }
+
+ }
+ }
+
+ /*@NotNull*/ public static String showExecutionTimeNano(long nanosecs) {
+ if (nanosecs < 1e9) {
+ return (nanosecs/1e6) + "ms";
+ } else {
+ try {
+ DayTimeDurationValue d = new DayTimeDurationValue(1, 0, 0, 0, nanosecs/1000000000L, (int)(nanosecs%1000));
+ long days = ((NumericValue)d.getComponent(Component.DAY)).longValue();
+ long hours = ((NumericValue)d.getComponent(Component.HOURS)).longValue();
+ long minutes = ((NumericValue)d.getComponent(Component.MINUTES)).longValue();
+ BigDecimal seconds = ((NumericValue)d.getComponent(Component.SECONDS)).getDecimalValue();
+ FastStringBuffer fsb = new FastStringBuffer(256);
+ if (days > 0) {
+ fsb.append(days + "days ");
+ }
+ if (hours > 0) {
+ fsb.append(hours + "h ");
+ }
+ if (minutes > 0) {
+ fsb.append(minutes + "m ");
+ }
+ fsb.append(seconds + "s");
+ return fsb.toString() + " (" + nanosecs/1e6 + "ms)";
+ } catch (XPathException e) {
+ return nanosecs/1e6 + "ms";
+ }
+
+ }
+ }
+}
+
diff --git a/sf/saxon/trans/CompilerInfo.java b/sf/saxon/trans/CompilerInfo.java
new file mode 100644
index 0000000..3c51af6
--- /dev/null
+++ b/sf/saxon/trans/CompilerInfo.java
@@ -0,0 +1,427 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.CollationMap;
+import net.sf.saxon.expr.instruct.GlobalParameterSet;
+import net.sf.saxon.expr.parser.CodeInjector;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.lib.OutputURIResolver;
+import net.sf.saxon.lib.StandardOutputResolver;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.value.DecimalValue;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.URIResolver;
+import java.io.Serializable;
+
+/**
+ * This class exists to hold information associated with a specific XSLT compilation episode.
+ * In JAXP, the URIResolver and ErrorListener used during XSLT compilation are those defined in the
+ * TransformerFactory. The .NET API and the s9api API, however, allow finer granularity,
+ * and this class exists to support that.
+ */
+
+public class CompilerInfo implements Serializable {
+
+ private transient URIResolver uriResolver;
+ private transient OutputURIResolver outputURIResolver = StandardOutputResolver.getInstance();
+ private transient ErrorListener errorListener;
+ private CodeInjector codeInjector;
+ private int recoveryPolicy = Configuration.RECOVER_WITH_WARNINGS;
+ private boolean schemaAware;
+ private boolean versionWarning;
+ private String messageReceiverClassName = "net.sf.saxon.serialize.MessageEmitter";
+ private StructuredQName defaultInitialMode;
+ private StructuredQName defaultInitialTemplate;
+ private DecimalValue xsltVersion = DecimalValue.ZERO; // indicates selection based on xsl:stylesheet/@version
+ private FunctionLibrary extensionFunctionLibrary;
+ private GlobalParameterSet variableList = new GlobalParameterSet();
+ private CollationMap collationMap;
+
+
+ /**
+ * Create an empty CompilerInfo object with default settings
+ */
+
+ public CompilerInfo() {}
+
+ /**
+ * Create a CompilerInfo object as a copy of another CompilerInfo object
+ * @param info the existing CompilerInfo object
+ * @since 9.2
+ */
+
+ public CompilerInfo(CompilerInfo info) {
+ uriResolver = info.uriResolver;
+ outputURIResolver = info.outputURIResolver;
+ errorListener = info.errorListener;
+ codeInjector = info.codeInjector;
+ recoveryPolicy = info.recoveryPolicy;
+ schemaAware = info.schemaAware;
+ versionWarning = info.versionWarning;
+ messageReceiverClassName = info.messageReceiverClassName;
+ defaultInitialMode = info.defaultInitialMode;
+ defaultInitialTemplate = info.defaultInitialTemplate;
+ xsltVersion = info.xsltVersion;
+ }
+
+ /**
+ * Set the URI Resolver to be used in this compilation episode.
+ * @param resolver The URIResolver to be used. This is used to dereference URIs encountered in constructs
+ * such as xsl:include, xsl:import, and xsl:import-schema.
+ * @since 8.7
+ */
+
+ public void setURIResolver(URIResolver resolver) {
+ uriResolver = resolver;
+ }
+
+ public void setParameter(StructuredQName name, Sequence seq){
+ variableList.put(name, seq);
+ }
+
+ public GlobalParameterSet getParameters(){
+ return variableList;
+ }
+
+ /**
+ * Get the URI Resolver being used in this compilation episode.
+ * @return resolver The URIResolver in use. This is used to dereference URIs encountered in constructs
+ * such as xsl:include, xsl:import, and xsl:import-schema.
+ * @since 8.7
+ */
+
+ public URIResolver getURIResolver() {
+ return uriResolver;
+ }
+
+ /**
+ * Get the OutputURIResolver that will be used to resolve URIs used in the
+ * href attribute of the xsl:result-document instruction.
+ *
+ * @return the OutputURIResolver. If none has been supplied explicitly, the
+ * default OutputURIResolver is returned.
+ * @since 9.2
+ */
+
+ public OutputURIResolver getOutputURIResolver() {
+ return outputURIResolver;
+ }
+
+ /**
+ * Set the OutputURIResolver that will be used to resolve URIs used in the
+ * href attribute of the xsl:result-document instruction.
+ *
+ * @param outputURIResolver the OutputURIResolver to be used.
+ * @since 9.2
+ */
+
+ public void setOutputURIResolver(OutputURIResolver outputURIResolver) {
+ this.outputURIResolver = outputURIResolver;
+ }
+
+
+ /**
+ * Set the ErrorListener to be used during this compilation episode
+ * @param listener The error listener to be used. This is notified of all errors detected during the
+ * compilation.
+ * @since 8.7
+ */
+
+ public void setErrorListener(ErrorListener listener) {
+ errorListener = listener;
+ }
+
+ /**
+ * Get the ErrorListener being used during this compilation episode
+ * @return listener The error listener in use. This is notified of all errors detected during the
+ * compilation.
+ * @since 8.7
+ */
+
+ public ErrorListener getErrorListener() {
+ return errorListener;
+ }
+
+ /**
+ * Get the name of the class that will be instantiated to create a MessageEmitter,
+ * to process the output of xsl:message instructions in XSLT.
+ *
+ * @return the full class name of the message emitter class.
+ * @since 9.2
+ */
+
+ public String getMessageReceiverClassName() {
+ return messageReceiverClassName;
+ }
+
+ /**
+ * Set the name of the class that will be instantiated to create a MessageEmitter,
+ * to process the output of xsl:message instructions in XSLT.
+ *
+ * @param messageReceiverClassName the message emitter class. This
+ * must implement net.sf.saxon.event.Emitter.
+ * @since 9.2
+ */
+
+ public void setMessageReceiverClassName(String messageReceiverClassName) {
+ this.messageReceiverClassName = messageReceiverClassName;
+ }
+
+ /**
+ * Set information about named collations and the default collation
+ * @param map the collation map to be used
+ * @since 9.5
+ */
+
+ public void setCollationMap(CollationMap map) {
+ this.collationMap = map;
+ }
+
+ /**
+ * Get information about named collations and the default collation
+ * @return the collation map to be used
+ * @since 9.5
+ */
+
+ public CollationMap getCollationMap() {
+ return this.collationMap;
+ }
+
+ /**
+ * Set whether trace hooks are to be included in the compiled code. To use tracing, it is necessary
+ * both to compile the code with trace hooks included, and to supply a TraceListener at run-time
+ * @param injector the code injector used to insert trace or debugging hooks, or null to clear any
+ * existing entry
+ * @since 9.4
+ */
+
+ public void setCodeInjector(/*@Nullable*/ CodeInjector injector) {
+ codeInjector = injector;
+ }
+
+ /**
+ * Get the registered CodeInjector, if any
+ * @return the code injector used to insert trace or debugging hooks, or null if absent
+ */
+
+ public CodeInjector getCodeInjector() {
+ return codeInjector;
+ }
+
+ /**
+ * Determine whether trace hooks are included in the compiled code.
+ * @return true if trace hooks are included, false if not.
+ * @since 8.9
+ */
+
+ public boolean isCompileWithTracing() {
+ return codeInjector != null;
+ }
+
+ /**
+ * Set the policy for handling recoverable errrors. Note that for some errors the decision can be
+ * made at run-time, but for the "ambiguous template match" error, the decision is (since 9.2)
+ * fixed at compile time.
+ * @param policy the recovery policy to be used. The options are {@link Configuration#RECOVER_SILENTLY},
+ * {@link Configuration#RECOVER_WITH_WARNINGS}, or {@link Configuration#DO_NOT_RECOVER}.
+ * @since 9.2
+ */
+
+ public void setRecoveryPolicy(int policy) {
+ recoveryPolicy = policy;
+ }
+
+ /**
+ * Get the policy for handling recoverable errors. Note that for some errors the decision can be
+ * made at run-time, but for the "ambiguous template match" error, the decision is (since 9.2)
+ * fixed at compile time.
+ *
+ * @return the current policy.
+ * @since 9.2
+ */
+
+ public int getRecoveryPolicy() {
+ return recoveryPolicy;
+ }
+
+ /**
+ * Ask whether a warning is to be output when the stylesheet version does not match the processor version.
+ * In the case of stylesheet version="1.0", the XSLT specification requires such a warning unless the user disables it.
+ *
+ * @return true if these messages are to be output.
+ * @since 9.2
+ */
+
+ public boolean isVersionWarning() {
+ return versionWarning;
+ }
+
+ /**
+ * Say whether a warning is to be output when the stylesheet version does not match the processor version.
+ * In the case of stylesheet version="1.0", the XSLT specification requires such a warning unless the user disables it.
+ *
+ * @param warn true if these messages are to be output.
+ * @since 9.2
+ */
+
+ public void setVersionWarning(boolean warn) {
+ versionWarning = warn;
+ }
+
+ /**
+ * Say that the stylesheet must be compiled to be schema-aware, even if it contains no
+ * xsl:import-schema declarations. Normally a stylesheet is treated as schema-aware
+ * only if it contains one or more xsl:import-schema declarations. If it is not schema-aware,
+ * then all input documents must be untyped, and validation of temporary trees is disallowed
+ * (though validation of the final result tree is permitted). Setting the argument to true
+ * means that schema-aware code will be compiled regardless.
+ * @param schemaAware If true, the stylesheet will be compiled with schema-awareness
+ * enabled even if it contains no xsl:import-schema declarations. If false, the stylesheet
+ * is treated as schema-aware only if it contains one or more xsl:import-schema declarations
+ * @since 9.2
+ */
+
+ public void setSchemaAware(boolean schemaAware) {
+ this.schemaAware = schemaAware;
+ }
+
+ /**
+ * Ask whether schema-awareness has been requested by means of a call on
+ * {@link #setSchemaAware}
+ * @return true if schema-awareness has been requested
+ */
+
+ public boolean isSchemaAware() {
+ return schemaAware;
+ }
+
+ /**
+ * Set the default initial template name for a stylesheet compiled using this CompilerInfo.
+ * This is only a default; it can be overridden when the stylesheet is executed
+ * @param initialTemplate the name of the default initial template, or null if there is
+ * no default. No error occurs (until run-time) if the stylesheet does not contain a template
+ * with this name.
+ * @since 9.3
+ */
+
+ public void setDefaultInitialTemplate(StructuredQName initialTemplate) {
+ defaultInitialTemplate = initialTemplate;
+ }
+
+ /**
+ * Get the default initial template name for a stylesheet compiled using this CompilerInfo.
+ * This is only a default; it can be overridden when the stylesheet is executed
+ * @return the name of the default initial template, or null if there is
+ * no default, as set using {@link #setDefaultInitialTemplate}
+ * @since 9.3
+ */
+
+ public StructuredQName getDefaultInitialTemplate() {
+ return defaultInitialTemplate;
+ }
+
+ /**
+ * Set the default initial mode name for a stylesheet compiled using this CompilerInfo.
+ * This is only a default; it can be overridden when the stylesheet is executed
+ * @param initialMode the name of the default initial mode, or null if there is
+ * no default. No error occurs (until run-time) if the stylesheet does not contain a mode
+ * with this name.
+ * @since 9.3
+ */
+
+ public void setDefaultInitialMode(StructuredQName initialMode) {
+ defaultInitialMode = initialMode;
+ }
+
+ /**
+ * Get the default initial mode name for a stylesheet compiled using this CompilerInfo.
+ * This is only a default; it can be overridden when the stylesheet is executed
+ * @return the name of the default initial mode, or null if there is
+ * no default, as set using {@link #setDefaultInitialMode}
+ * @since 9.3
+ */
+
+ public StructuredQName getDefaultInitialMode() {
+ return defaultInitialMode;
+ }
+
+ /**
+ * Set the version of XSLT to be supported by this processor. This determines the version
+ * of the XSLT specification to which the processor conforms. This does not have to match
+ * the version attribute in the stylesheet; for example if the processor version is 2.0
+ * and the stylesheet is version 3.0, then the stylesheet will be processed using the rules
+ * for a 2.0 processor in forwards compatibility mode, rather than the rules for a 3.01
+ * processor.
+ * <p>The value 0.0 (which is the default) indicates that the processor version is to be
+ * taken from the version attribute of the xsl:stylesheet element.</p>
+ * <p><i>XSLT 2.1 features are supported only in Saxon-EE. Setting the version to 3.0
+ * here will not fail if the wrong edition is in use, but use of XSLT 3.0 features will
+ * fail subsequently.</i></p>
+ * @param version must be numerically equal to 0.0, 2.0 or 3.0
+ * @throws IllegalArgumentException if the version is invalid
+ * @since 9.3
+ */
+
+ public void setXsltVersion(/*@NotNull*/ DecimalValue version) {
+ if (!version.equals(DecimalValue.TWO) && !version.equals(DecimalValue.THREE)
+ && !version.equals(DecimalValue.ZERO)) {
+ throw new IllegalArgumentException("XSLT version must be 0.0, 2.0 or 3.0");
+ }
+ xsltVersion = version;
+ if (version.equals(DecimalValue.THREE)) {
+ setRecoveryPolicy(Configuration.RECOVER_SILENTLY);
+ }
+ }
+
+ /**
+ * Get the version of XSLT supported by this processor
+ * @return {@link net.sf.saxon.value.DecimalValue#TWO} or {@link net.sf.saxon.value.DecimalValue#THREE},
+ * or zero indicating that the processor versino is taken from the version attribute of the xsl:stylesheet element.)
+ * @since 9.3
+ */
+
+ public DecimalValue getXsltVersion() {
+ return xsltVersion;
+ }
+
+ /**
+ * Set a library of extension functions. The functions in this library will be available
+ * in all modules of the stylesheet. The function library will be searched after language-defined
+ * libraries (such as built-in functions, user-defined XQuery functions, and constructor
+ * functions) but before extension functions defined at the Configuration level.
+ * @param library the function library to be added (replacing any that has previously been set).
+ * May be null to clear a previously-set library
+ * @since 9.4
+ */
+
+ public void setExtensionFunctionLibrary(/*@Nullable*/ FunctionLibrary library) {
+ this.extensionFunctionLibrary = library;
+ }
+
+ /**
+ * Get any function library that was previously set using
+ * {@link #setExtensionFunctionLibrary(net.sf.saxon.functions.FunctionLibrary)}.
+ * @return the extension function library, or null if none has been set.
+ *
+ * @since 9.4
+ */
+
+
+ /*@Nullable*/ public FunctionLibrary getExtensionFunctionLibrary() {
+ return extensionFunctionLibrary;
+ }
+
+
+
+
+}
+
diff --git a/sf/saxon/trans/ConfigurationReader.java b/sf/saxon/trans/ConfigurationReader.java
new file mode 100644
index 0000000..ca67169
--- /dev/null
+++ b/sf/saxon/trans/ConfigurationReader.java
@@ -0,0 +1,582 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.instruct.ResultDocument;
+import net.sf.saxon.expr.parser.ExpressionLocation;
+import net.sf.saxon.functions.ResolveURI;
+import net.sf.saxon.lib.*;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.SchemaException;
+import org.xml.sax.*;
+
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamSource;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * Class used to read a config.xml file and transfer all settings from the file to the Configuration
+ */
+
+public class ConfigurationReader implements ContentHandler, NamespaceResolver {
+
+ private int level = 0;
+ private String section = null;
+ private FastStringBuffer buffer = new FastStringBuffer(100);
+ protected Configuration config;
+ private ClassLoader classLoader = null;
+ private List<XPathException> errors = new ArrayList<XPathException>();
+ private Locator locator;
+ private Stack<List<String[]>> namespaceStack = new Stack<List<String[]>>();
+
+ public ConfigurationReader() {
+ }
+
+ /**
+ * Set the ClassLoader to be used for dynamic loading of the configuration, and for dynamic loading
+ * of other classes used within the configuration. By default the class loader of this class is used.
+ * @param classLoader the ClassLoader to be used
+ */
+
+ public void setClassLoader(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ /**
+ * Create a Configuration based on the contents of this configuration file
+ * @param source the Source of the configuration file
+ * @return the constructed Configuration
+ * @throws XPathException
+ */
+
+ public Configuration makeConfiguration(Source source) throws XPathException {
+ InputSource is;
+ XMLReader parser = null;
+ if (source instanceof SAXSource) {
+ parser = ((SAXSource)source).getXMLReader();
+ is = ((SAXSource)source).getInputSource();
+ } else if (source instanceof StreamSource) {
+ is = new InputSource(source.getSystemId());
+ is.setCharacterStream(((StreamSource)source).getReader());
+ is.setByteStream(((StreamSource)source).getInputStream());
+ } else {
+ throw new XPathException("Source for configuration file must be a StreamSource or SAXSource");
+ }
+ if (parser == null) {
+ // Don't use the parser from the pool, it might be validating
+ parser = Configuration.getPlatform().loadParser();
+ try {
+ parser.setFeature("http://xml.org/sax/features/namespaces", true);
+ parser.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
+ } catch (SAXNotRecognizedException e) {
+ throw new TransformerFactoryConfigurationError(e);
+ } catch (SAXNotSupportedException e) {
+ throw new TransformerFactoryConfigurationError(e);
+ }
+ }
+ try {
+ parser.setContentHandler(this);
+ parser.parse(is);
+ } catch (IOException e) {
+ throw new XPathException("Failed to read config file", e);
+ } catch (SAXException e) {
+ throw new XPathException("Failed to parse config file", e);
+ }
+
+ if (!errors.isEmpty()) {
+ ErrorListener listener;
+ if (config == null) {
+ listener = new StandardErrorListener();
+ } else {
+ listener = config.getErrorListener();
+ }
+ try {
+ for (XPathException err: errors) {
+ listener.warning(err);
+ }
+ } catch (TransformerException e) {
+ //
+ }
+ throw errors.get(0);
+ }
+ return config;
+ }
+
+ public void setDocumentLocator(Locator locator) {
+ this.locator = locator;
+ }
+
+ public void startDocument() throws SAXException {
+ namespaceStack.push(new ArrayList<String[]>());
+ }
+
+ public void endDocument() throws SAXException {
+ namespaceStack.pop();
+ }
+
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ namespaceStack.peek().add(new String[]{prefix, uri});
+ }
+
+ public void endPrefixMapping(String prefix) throws SAXException {
+
+ }
+
+ public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+ buffer.setLength(0);
+ if (NamespaceConstant.SAXON_CONFIGURATION.equals(uri)) {
+ if (level == 0) {
+ if (!"configuration".equals(localName)) {
+ error(localName, null, null, "configuration");
+ }
+ String edition = atts.getValue("edition");
+ if (edition == null) {
+ edition = "HE";
+ }
+ if (edition.equals("HE")) {
+ config = new Configuration();
+ } else if (edition.equals("PE")) {
+ config = Configuration.makeLicensedConfiguration(classLoader, "com.saxonica.config.ProfessionalConfiguration");
+ } else if (edition.equals("EE")) {
+ config = Configuration.makeLicensedConfiguration(classLoader, "com.saxonica.config.EnterpriseConfiguration");
+ } else {
+ error("configuration", "edition", edition, "HE|PE|EE");
+ config = new Configuration();
+ }
+ config.getDynamicLoader().setClassLoader(classLoader);
+ }
+ if (level == 1) {
+ section = localName;
+ if ("global".equals(localName)) {
+ readGlobalElement(atts);
+ } else if ("serialization".equals(localName)) {
+ readSerializationElement(atts, config.getNameChecker());
+ } else if ("xquery".equals(localName)) {
+ readXQueryElement(atts);
+ } else if ("xslt".equals(localName)) {
+ readXsltElement(atts);
+ } else if ("xsd".equals(localName)) {
+ readXsdElement(atts);
+ } else if ("resources".equals(localName)) {
+ // no action until later
+ } else if ("collations".equals(localName)) {
+ // no action until later
+ } else if ("localizations".equals(localName)) {
+ readLocalizationsElement(atts);
+ } else {
+ error(localName, null, null, null);
+ }
+ } else if (level==2) {
+ if ("resources".equals(section)) {
+ // no action until endElement()
+ } else if ("collations".equals(section)) {
+ if (!"collation".equals(localName)) {
+ error(localName, null, null, "collation");
+ } else {
+ readCollation(atts);
+ }
+ } else if ("localizations".equals(section)) {
+ if (!"localization".equals(localName)) {
+ error(localName, null, null, "localization");
+ } else {
+ readLocalization(atts);
+ }
+ } else if ("xslt".equals(section)) {
+ if (!"extensionElement".equals(localName)) {
+ error(localName, null, null, "extensionElement");
+ } else {
+ readExtensionElement(atts);
+ }
+ }
+ }
+ } else {
+ errors.add(new XPathException("Configuration elements must be in namespace " + NamespaceConstant.SAXON_CONFIGURATION));
+ }
+ level++;
+ namespaceStack.push(new ArrayList<String[]>());
+ }
+
+ private void readGlobalElement(Attributes atts) {
+ Properties props = new Properties();
+ for (int i=0; i<atts.getLength(); i++) {
+ String name=atts.getLocalName(i);
+ String value = atts.getValue(i);
+ if (value.length() != 0 && atts.getURI(i).length() == 0) {
+ props.put(name, value);
+ }
+ }
+ props.put("#element", "global");
+ applyProperty(props, "allowExternalFunctions", FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS);
+ applyProperty(props, "allowMultiThreading", FeatureKeys.ALLOW_MULTITHREADING);
+ applyProperty(props, "allowOldJavaUriFormat", FeatureKeys.ALLOW_OLD_JAVA_URI_FORMAT);
+ applyProperty(props, "allowStreamabilityExtensions", FeatureKeys.ALLOW_STREAMABILITY_EXTENSIONS);
+ applyProperty(props, "collationUriResolver", FeatureKeys.COLLATION_URI_RESOLVER_CLASS);
+ applyProperty(props, "collectionUriResolver", FeatureKeys.COLLECTION_URI_RESOLVER_CLASS);
+ applyProperty(props, "compileWithTracing", FeatureKeys.COMPILE_WITH_TRACING);
+ applyProperty(props, "debugByteCode", FeatureKeys.DEBUG_BYTE_CODE);
+ applyProperty(props, "debugByteCodeDirectory", FeatureKeys.DEBUG_BYTE_CODE_DIR);
+ applyProperty(props, "defaultCollation", FeatureKeys.DEFAULT_COLLATION);
+ applyProperty(props, "defaultCollection", FeatureKeys.DEFAULT_COLLECTION);
+ applyProperty(props, "displayByteCode", FeatureKeys.DISPLAY_BYTE_CODE);
+ applyProperty(props, "dtdValidation", FeatureKeys.DTD_VALIDATION);
+ applyProperty(props, "dtdValidationRecoverable", FeatureKeys.DTD_VALIDATION_RECOVERABLE);
+ applyProperty(props, "entityResolver", FeatureKeys.ENTITY_RESOLVER_CLASS);
+ applyProperty(props, "errorListener", FeatureKeys.ERROR_LISTENER_CLASS);
+ applyProperty(props, "environmentVariableResolver", FeatureKeys.ENVIRONMENT_VARIABLE_RESOLVER_CLASS);
+ applyProperty(props, "expandAttributeDefaults", FeatureKeys.EXPAND_ATTRIBUTE_DEFAULTS);
+ applyProperty(props, "generateByteCode", FeatureKeys.GENERATE_BYTE_CODE);
+ applyProperty(props, "ignoreSAXSourceParser", FeatureKeys.IGNORE_SAX_SOURCE_PARSER);
+ applyProperty(props, "lazyConstructionMode", FeatureKeys.LAZY_CONSTRUCTION_MODE);
+ applyProperty(props, "lineNumbering", FeatureKeys.LINE_NUMBERING);
+ applyProperty(props, "optimizationLevel", FeatureKeys.OPTIMIZATION_LEVEL);
+ applyProperty(props, "parser", FeatureKeys.SOURCE_PARSER_CLASS);
+ applyProperty(props, "preEvaluateDoc", FeatureKeys.PRE_EVALUATE_DOC_FUNCTION);
+ applyProperty(props, "preferJaxpParser", FeatureKeys.PREFER_JAXP_PARSER);
+ applyProperty(props, "recognizeUriQueryParameters", FeatureKeys.RECOGNIZE_URI_QUERY_PARAMETERS);
+ applyProperty(props, "schemaValidation", FeatureKeys.SCHEMA_VALIDATION_MODE);
+ applyProperty(props, "serializerFactory", FeatureKeys.SERIALIZER_FACTORY_CLASS);
+ applyProperty(props, "sourceResolver", FeatureKeys.SOURCE_RESOLVER_CLASS);
+ applyProperty(props, "standardErrorOutputFile", FeatureKeys.STANDARD_ERROR_OUTPUT_FILE);
+ applyProperty(props, "stripSpace", FeatureKeys.STRIP_WHITESPACE);
+ applyProperty(props, "styleParser", FeatureKeys.STYLE_PARSER_CLASS);
+ applyProperty(props, "suppressEvaluationExpiryWarning", FeatureKeys.SUPPRESS_EVALUATION_EXPIRY_WARNING);
+ applyProperty(props, "timing", FeatureKeys.TIMING);
+ applyProperty(props, "traceExternalFunctions", FeatureKeys.TRACE_EXTERNAL_FUNCTIONS);
+ applyProperty(props, "traceListener", FeatureKeys.TRACE_LISTENER_CLASS);
+ applyProperty(props, "traceOptimizerDecisions", FeatureKeys.TRACE_OPTIMIZER_DECISIONS);
+ applyProperty(props, "treeModel", FeatureKeys.TREE_MODEL_NAME);
+ applyProperty(props, "uriResolver", FeatureKeys.URI_RESOLVER_CLASS);
+ applyProperty(props, "usePiDisableOutputEscaping", FeatureKeys.USE_PI_DISABLE_OUTPUT_ESCAPING);
+ applyProperty(props, "useTypedValueCache", FeatureKeys.USE_TYPED_VALUE_CACHE);
+ applyProperty(props, "validationComments", FeatureKeys.VALIDATION_COMMENTS);
+ applyProperty(props, "validationWarnings", FeatureKeys.VALIDATION_WARNINGS);
+ applyProperty(props, "versionOfXml", FeatureKeys.XML_VERSION);
+ applyProperty(props, "xInclude", FeatureKeys.XINCLUDE);
+ }
+
+ private void applyProperty(Properties props, String propertyName, String featureKey) {
+ String value = props.getProperty(propertyName);
+ if (value != null) {
+ try {
+ config.setConfigurationProperty(featureKey, value);
+ } catch (IllegalArgumentException e) {
+ error(props.getProperty("#element"), propertyName, value, e.getMessage());
+ }
+ }
+ }
+
+ private void readSerializationElement(Attributes atts, NameChecker checker) {
+ Properties props = new Properties();
+ for (int i=0; i<atts.getLength(); i++) {
+ String uri=atts.getURI(i);
+ String name=atts.getLocalName(i);
+ String value = atts.getValue(i);
+ if (value.length() == 0) {
+ continue;
+ }
+ try {
+ ResultDocument.setSerializationProperty(props,
+ uri, name, value, this, false, config);
+ } catch (XPathException e) {
+ errors.add(e);
+ }
+ }
+ config.setDefaultSerializationProperties(props);
+ }
+
+ private void readCollation(Attributes atts) {
+ Properties props = new Properties();
+ String collationUri = null;
+ for (int i=0; i<atts.getLength(); i++) {
+ if (atts.getURI(i).length() == 0) {
+ String name=atts.getLocalName(i);
+ String value = atts.getValue(i);
+ if (value.length() == 0) {
+ continue;
+ }
+ if ("uri".equals(name)) {
+ collationUri = value;
+ } else {
+ props.put(name, value);
+ }
+ }
+ }
+ if (collationUri == null) {
+ errors.add(new XPathException("collation specified with no uri"));
+ }
+ StringCollator collator = null;
+ try {
+ collator = Configuration.getPlatform().makeCollation(config, props, collationUri);
+ } catch (XPathException e) {
+ errors.add(e);
+ }
+ config.getCollationMap().setNamedCollation(collationUri, collator);
+
+ }
+
+ private void readLocalizationsElement(Attributes atts) {
+ for (int i=0; i<atts.getLength(); i++) {
+ if (atts.getURI(i).length() == 0) {
+ String name=atts.getLocalName(i);
+ String value = atts.getValue(i);
+ if ("defaultLanguage".equals(name) && value.length() > 0) {
+ config.setConfigurationProperty(FeatureKeys.DEFAULT_LANGUAGE, value);
+ }
+ if ("defaultCountry".equals(name) && value.length() > 0) {
+ config.setConfigurationProperty(FeatureKeys.DEFAULT_COUNTRY, value);
+ }
+ }
+ }
+ }
+
+ private void readLocalization(Attributes atts) {
+ String lang = null;
+ Properties properties = new Properties();
+ for (int i=0; i<atts.getLength(); i++) {
+ if (atts.getURI(i).length() == 0) {
+ String name=atts.getLocalName(i);
+ String value = atts.getValue(i);
+ if ("lang".equals(name) && value.length() > 0) {
+ lang = value;
+ } else if (value.length() > 0) {
+ properties.setProperty(name, value);
+ }
+ }
+ }
+ if (lang != null) {
+ LocalizerFactory factory = config.getLocalizerFactory();
+ if (factory != null) {
+ factory.setLanguageProperties(lang, properties);
+ }
+ }
+ }
+
+ /**
+ * Process details of XSLT extension elements. Overridden in Saxon-PE configuration reader
+ * @param atts the attributes of the extensionElement element in the configuration file
+ */
+
+ protected void readExtensionElement(Attributes atts) {
+ XPathException err = new XPathException("Extension elements are not available in Saxon-HE");
+ err.setLocator(ExpressionLocation.makeFromSax(locator));
+ errors.add(err);
+ }
+
+ private void readXQueryElement(Attributes atts) {
+ Properties props = new Properties();
+ for (int i=0; i<atts.getLength(); i++) {
+ String name=atts.getLocalName(i);
+ String value = atts.getValue(i);
+ if (value.length() != 0 && atts.getURI(i).length() == 0) {
+ props.put(name, value);
+ }
+ }
+ props.put("#element", "global");
+ applyProperty(props, "allowUpdate", FeatureKeys.XQUERY_ALLOW_UPDATE);
+ applyProperty(props, "constructionMode", FeatureKeys.XQUERY_CONSTRUCTION_MODE);
+ applyProperty(props, "defaultElementNamespace", FeatureKeys.XQUERY_DEFAULT_ELEMENT_NAMESPACE);
+ applyProperty(props, "defaultFunctionNamespace", FeatureKeys.XQUERY_DEFAULT_FUNCTION_NAMESPACE);
+ applyProperty(props, "emptyLeast", FeatureKeys.XQUERY_EMPTY_LEAST);
+ applyProperty(props, "inheritNamespaces", FeatureKeys.XQUERY_INHERIT_NAMESPACES);
+ applyProperty(props, "moduleUriResolver", FeatureKeys.MODULE_URI_RESOLVER_CLASS);
+ applyProperty(props, "preserveBoundarySpace", FeatureKeys.XQUERY_PRESERVE_BOUNDARY_SPACE);
+ applyProperty(props, "preserveNamespaces", FeatureKeys.XQUERY_PRESERVE_NAMESPACES);
+ applyProperty(props, "requiredContextItemType", FeatureKeys.XQUERY_REQUIRED_CONTEXT_ITEM_TYPE);
+ applyProperty(props, "schemaAware", FeatureKeys.XQUERY_SCHEMA_AWARE);
+ applyProperty(props, "staticErrorListener", FeatureKeys.XQUERY_STATIC_ERROR_LISTENER_CLASS);
+ applyProperty(props, "version", FeatureKeys.XQUERY_VERSION);
+ }
+
+ private void readXsltElement(Attributes atts) {
+ Properties props = new Properties();
+ for (int i=0; i<atts.getLength(); i++) {
+ String name=atts.getLocalName(i);
+ String value = atts.getValue(i);
+ if (value.length() != 0 && atts.getURI(i).length() == 0) {
+ props.put(name, value);
+ }
+ }
+ props.put("#element", "global");
+ applyProperty(props, "initialMode", FeatureKeys.XSLT_INITIAL_MODE);
+ applyProperty(props, "initialTemplate", FeatureKeys.XSLT_INITIAL_TEMPLATE);
+ applyProperty(props, "messageEmitter", FeatureKeys.MESSAGE_EMITTER_CLASS);
+ applyProperty(props, "outputUriResolver", FeatureKeys.OUTPUT_URI_RESOLVER_CLASS);
+ applyProperty(props, "recoveryPolicy", FeatureKeys.RECOVERY_POLICY_NAME);
+ applyProperty(props, "schemaAware", FeatureKeys.XSLT_SCHEMA_AWARE);
+ applyProperty(props, "staticErrorListener", FeatureKeys.XSLT_STATIC_ERROR_LISTENER_CLASS);
+ applyProperty(props, "staticUriResolver", FeatureKeys.XSLT_STATIC_URI_RESOLVER_CLASS);
+ applyProperty(props, "styleParser", FeatureKeys.STYLE_PARSER_CLASS);
+ applyProperty(props, "version", FeatureKeys.XSLT_VERSION);
+ applyProperty(props, "versionWarning", FeatureKeys.VERSION_WARNING);
+ }
+
+ private void readXsdElement(Attributes atts) {
+ Properties props = new Properties();
+ for (int i=0; i<atts.getLength(); i++) {
+ String name=atts.getLocalName(i);
+ String value = atts.getValue(i);
+ if (value.length() != 0 && atts.getURI(i).length() == 0) {
+ props.put(name, value);
+ }
+ }
+ props.put("#element", "global");
+ applyProperty(props, "assertionsCanSeeComments", FeatureKeys.ASSERTIONS_CAN_SEE_COMMENTS);
+ applyProperty(props, "multipleSchemaImports", FeatureKeys.MULTIPLE_SCHEMA_IMPORTS);
+ applyProperty(props, "occurrenceLimits", FeatureKeys.OCCURRENCE_LIMITS);
+ applyProperty(props, "schemaUriResolver", FeatureKeys.SCHEMA_URI_RESOLVER_CLASS);
+ applyProperty(props, "useXsiSchemaLocation", FeatureKeys.USE_XSI_SCHEMA_LOCATION);
+ applyProperty(props, "version", FeatureKeys.XSD_VERSION);
+ }
+
+ private void error(String element, String attribute, String actual, String required) {
+ XPathException err;
+ if (attribute == null) {
+ err = new XPathException("Invalid configuration element " + element);
+ } else {
+ err = new XPathException("Invalid configuration property " +
+ element + "/@" + attribute + ". Supplied value '" + actual + "'. " + required);
+ }
+ err.setLocator(ExpressionLocation.makeFromSax(locator));
+ errors.add(err);
+ }
+
+ protected void errorClass(String element, String attribute, String actual, Class required, Exception cause) {
+ XPathException err = new XPathException("Invalid configuration property " +
+ element + (attribute == null ? "" : "/@" + attribute) +
+ ". Supplied value '" + actual +
+ "', required value is the name of a class that implements '" + required.getName() + "'", cause);
+ err.setLocator(ExpressionLocation.makeFromSax(locator));
+ errors.add(err);
+ }
+
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (level == 3 && "resources".equals(section)) {
+ String content = buffer.toString();
+ if (content.length() != 0) {
+ if ("externalObjectModel".equals(localName)) {
+ try {
+ ExternalObjectModel model = (ExternalObjectModel)config.getInstance(content, null);
+ config.registerExternalObjectModel(model);
+ } catch (XPathException e) {
+ errorClass("externalObjectModel", null, content, ExternalObjectModel.class, e);
+ } catch (ClassCastException cce) {
+ errorClass("externalObjectModel", null, content, ExternalObjectModel.class, cce);
+ }
+ } else if ("extensionFunction".equals(localName)) {
+ try {
+ ExtensionFunctionDefinition model = (ExtensionFunctionDefinition)config.getInstance(content, null);
+ config.registerExtensionFunction(model);
+ } catch (XPathException e) {
+ errorClass("extensionFunction", null, content, ExtensionFunctionDefinition.class, e);
+ } catch (ClassCastException cce) {
+ errorClass("extensionFunction", null, content, ExtensionFunctionDefinition.class, cce);
+ } catch (IllegalArgumentException iae) {
+ errorClass("extensionFunction", null, content, ExtensionFunctionDefinition.class, iae);
+ }
+ } else if ("schemaDocument".equals(localName)) {
+ try {
+ Source source = getInputSource(content);
+ config.addSchemaSource(source);
+ } catch (SchemaException e) {
+ errors.add(XPathException.makeXPathException(e));
+ } catch (XPathException e) {
+ errors.add(e);
+ }
+ } else if ("schemaComponentModel".equals(localName)) {
+ try {
+ Source source = getInputSource(content);
+ config.importComponents(source);
+ } catch (XPathException e) {
+ errors.add(e);
+ }
+ } else {
+ error(localName, null, null, null);
+ }
+ }
+ }
+ level--;
+ buffer.setLength(0);
+ namespaceStack.pop();
+ }
+
+ private Source getInputSource(String href) throws XPathException {
+ try {
+ String base = locator.getSystemId();
+ URI abs = ResolveURI.makeAbsolute(href, base);
+ return new StreamSource(abs.toString());
+ } catch (URISyntaxException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ public void characters(char ch[], int start, int length) throws SAXException {
+ buffer.append(ch, start, length);
+ }
+
+ public void ignorableWhitespace(char ch[], int start, int length) throws SAXException {
+
+ }
+
+ public void processingInstruction(String target, String data) throws SAXException {
+
+ }
+
+ public void skippedEntity(String name) throws SAXException {
+
+ }
+
+ /////////////////////////////////////////////////////
+ // Implement NamespaceResolver
+ /////////////////////////////////////////////////////
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ * @param prefix the namespace prefix. May be the zero-length string, indicating
+ * that there is no prefix. This indicates either the default namespace or the
+ * null namespace, depending on the value of useDefault.
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is "". If false, the method returns "" when the prefix is "".
+ * @return the uri for the namespace, or null if the prefix is not in scope.
+ * The "null namespace" is represented by the pseudo-URI "".
+ */
+
+ public String getURIForPrefix(String prefix, boolean useDefault) {
+ for (int i=namespaceStack.size()-1; i>=0; i--) {
+ List<String[]> list = namespaceStack.get(i);
+ for (String[] pair : list) {
+ if (pair[0].equals(prefix)) {
+ return pair[1];
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ /*@Nullable*/ public Iterator<String> iteratePrefixes() {
+ // Not used
+ return null;
+ }
+}
+
diff --git a/sf/saxon/trans/DecimalFormatManager.java b/sf/saxon/trans/DecimalFormatManager.java
new file mode 100644
index 0000000..e473bb3
--- /dev/null
+++ b/sf/saxon/trans/DecimalFormatManager.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.om.StructuredQName;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * DecimalFormatManager manages the collection of named and unnamed decimal formats, for use by the
+ * format-number() function.
+ *
+ * <p>In XSLT, there is a single set of decimal formats shared by the whole stylesheet. In XQuery 3.0, however,
+ * each query module has its own set of decimal formats. The DecimalFormatManager to use is therefore linked
+ * from the format-number() call on the expression tree.</p>
+ * @author Michael H. Kay
+ */
+
+public class DecimalFormatManager implements Serializable {
+
+ private DecimalSymbols defaultDFS;
+ private HashMap<StructuredQName, DecimalSymbols> formatTable; // table for named decimal formats
+
+ /**
+ * create a DecimalFormatManager and initialise variables
+ */
+
+ public DecimalFormatManager() {
+ formatTable = new HashMap<StructuredQName, DecimalSymbols>(10);
+ defaultDFS = new DecimalSymbols();
+ }
+
+ /**
+ * Get the default decimal-format.
+ * @return the default (unnamed) decimal format
+ */
+
+ public DecimalSymbols getDefaultDecimalFormat() {
+ return defaultDFS;
+ }
+
+ /**
+ * Get a named decimal-format registered using setNamedDecimalFormat
+ * @param qName The name of the decimal format
+ * @return the DecimalSymbols object corresponding to the given name, if any
+ * or null if not set.
+ */
+
+ /*@Nullable*/ public DecimalSymbols getNamedDecimalFormat(StructuredQName qName) {
+ DecimalSymbols ds = formatTable.get(qName);
+ if (ds == null) {
+ return null;
+ // following two lines had been added to the code since 9.4, but they break XSLT test error089
+// ds = new DecimalSymbols();
+// formatTable.put(qName, ds);
+ }
+ return ds;
+ }
+
+ /**
+ * Get a named decimal-format registered using setNamedDecimalFormat if it exists;
+ * create it if it does not
+ * @param qName The name of the decimal format
+ * @return the DecimalSymbols object corresponding to the given name, if it exists,
+ * or a newly created DecimalSymbols object otherwise
+ */
+
+ public DecimalSymbols obtainNamedDecimalFormat(StructuredQName qName) {
+ DecimalSymbols ds = formatTable.get(qName);
+ if (ds == null) {
+ ds = new DecimalSymbols();
+ formatTable.put(qName, ds);
+ }
+ return ds;
+ }
+
+ /**
+ * Check the consistency of all DecimalSymbols objects owned by this DecimalFormatManager
+ * @throws XPathException if any inconsistencies are found
+ */
+
+ public void checkConsistency() throws XPathException {
+ defaultDFS.checkConsistency(null);
+ for (Map.Entry<StructuredQName, DecimalSymbols> entry : formatTable.entrySet()) {
+ entry.getValue().checkConsistency(entry.getKey());
+ }
+ }
+
+}
+
diff --git a/sf/saxon/trans/DecimalSymbols.java b/sf/saxon/trans/DecimalSymbols.java
new file mode 100644
index 0000000..2005792
--- /dev/null
+++ b/sf/saxon/trans/DecimalSymbols.java
@@ -0,0 +1,476 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.z.IntHashMap;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * This class is modelled on Java's DecimalFormatSymbols, but it allows the use of any
+ * Unicode character to represent symbols such as the decimal point and the grouping
+ * separator, whereas DecimalFormatSymbols restricts these to a char (1-65535).
+ */
+public class DecimalSymbols implements Serializable {
+
+ public static final int DECIMAL_SEPARATOR = 0;
+ public static final int GROUPING_SEPARATOR = 1;
+ public static final int DIGIT = 2;
+ public static final int MINUS_SIGN = 3;
+ public static final int PERCENT = 4;
+ public static final int PER_MILLE = 5;
+ public static final int ZERO_DIGIT = 6;
+ public static final int PATTERN_SEPARATOR = 7;
+ public static final int INFINITY = 8;
+ public static final int NAN = 9;
+
+ private static final int ERR_NOT_SINGLE_CHAR = 0;
+ private static final int ERR_NOT_UNICODE_DIGIT = 1;
+ private static final int ERR_SAME_CHAR_IN_TWO_ROLES = 2;
+ private static final int ERR_TWO_VALUES_FOR_SAME_PROPERTY = 3;
+
+ private static String[] XSLT_CODES = {"XTSE0020", "XTSE1295", "XTSE1300", "XTSE1290"};
+ private static String[] XQUERY_CODES = {"XQST0097", "XQST0097", "XQST0098", "XQST0114"};
+ private String[] errorCodes = XSLT_CODES;
+
+ private int[] intValues = new int[8];
+ private String infinityValue;
+ private String NaNValue;
+
+ private int[] precedences = new int[10];
+ private boolean[] inconsistent = new boolean[10];
+
+ private final static String[] propertyNames = {
+ StandardNames.DECIMAL_SEPARATOR,
+ StandardNames.GROUPING_SEPARATOR,
+ StandardNames.DIGIT,
+ StandardNames.MINUS_SIGN,
+ StandardNames.PERCENT,
+ StandardNames.PER_MILLE,
+ StandardNames.ZERO_DIGIT,
+ StandardNames.PATTERN_SEPARATOR,
+ StandardNames.INFINITY,
+ StandardNames.NAN
+ };
+
+ /**
+ * Create a DecimalSymbols object with default values for all properties
+ */
+
+ public DecimalSymbols() {
+ intValues[DECIMAL_SEPARATOR] = '.';
+ intValues[GROUPING_SEPARATOR] = ',';
+ intValues[DIGIT] = '#';
+ intValues[MINUS_SIGN] = '-';
+ intValues[PERCENT] = '%';
+ intValues[PER_MILLE] = '\u2030';
+ intValues[ZERO_DIGIT] = '0';
+ intValues[PATTERN_SEPARATOR] = ';';
+ infinityValue = "Infinity";
+ NaNValue = "NaN";
+ Arrays.fill(precedences, Integer.MIN_VALUE);
+ }
+
+ public void setHostLanguage(int language) {
+ if (language == Configuration.XQUERY) {
+ errorCodes = XQUERY_CODES;
+ } else {
+ errorCodes = XSLT_CODES;
+ }
+ }
+
+ /**
+ * Get the decimal separator value
+ * @return the decimal separator value that has been explicitly set, or its default ('.')
+ */
+
+ public int getDecimalSeparator() {
+ return intValues[DECIMAL_SEPARATOR];
+ }
+
+ /**
+ * Get the grouping separator value
+ * @return the grouping separator value that has been explicitly set, or its default (',')
+ */
+
+ public int getGroupingSeparator() {
+ return intValues[GROUPING_SEPARATOR];
+ }
+
+ /**
+ * Get the digit symbol value
+ * @return the digit symbol value that has been explicitly set, or its default ('#')
+ */
+
+ public int getDigit() {
+ return intValues[DIGIT];
+ }
+
+ /**
+ * Get the minus sign value
+ * @return the minus sign value that has been explicitly set, or its default ('-')
+ */
+
+ public int getMinusSign() {
+ return intValues[MINUS_SIGN];
+ }
+
+ /**
+ * Get the percent symbol value
+ * @return the percent symbol value that has been explicitly set, or its default ('%')
+ */
+
+ public int getPercent() {
+ return intValues[PERCENT];
+ }
+
+ /**
+ * Get the per-mille symbol value
+ * @return the per-mille symbol value that has been explicitly set, or its default
+ */
+
+ public int getPerMille() {
+ return intValues[PER_MILLE];
+ }
+
+ /**
+ * Get the zero digit symbol value
+ * @return the zero digit symbol value that has been explicitly set, or its default ('0')
+ */
+
+ public int getZeroDigit() {
+ return intValues[ZERO_DIGIT];
+ }
+
+ /**
+ * Get the pattern separator value
+ * @return the pattern separator value that has been explicitly set, or its default (';')
+ */
+
+ public int getPatternSeparator() {
+ return intValues[PATTERN_SEPARATOR];
+ }
+
+ /**
+ * Get the infinity symbol value
+ * @return the infinity symbol value that has been explicitly set, or its default ('Infinity')
+ */
+
+ public String getInfinity() {
+ return infinityValue;
+ }
+
+ /**
+ * Get the NaN symbol value
+ * @return the NaN symbol value that has been explicitly set, or its default ('NaN')
+ */
+
+ public String getNaN() {
+ return NaNValue;
+ }
+
+ /**
+ * Set the character to be used as the decimal separator
+ * @param value the character to be used
+ * @throws XPathException if the value is not a single Unicode character (a surrogate pair is permitted)
+ */
+
+ public void setDecimalSeparator(String value) throws XPathException {
+ setProperty(DECIMAL_SEPARATOR, value, 0);
+ }
+
+ /**
+ * Set the character to be used as the grouping separator
+ * @param value the character to be used
+ * @throws XPathException if the value is not a single Unicode character (a surrogate pair is permitted)
+ */
+
+ public void setGroupingSeparator(String value) throws XPathException {
+ setProperty(GROUPING_SEPARATOR, value, 0);
+ }
+
+ /**
+ * Set the character to be used as the digit symbol (default is '#')
+ * @param value the character to be used
+ * @throws XPathException if the value is not a single Unicode character (a surrogate pair is permitted)
+ */
+
+ public void setDigit(String value) throws XPathException {
+ setProperty(DIGIT, value, 0);
+ }
+
+ /**
+ * Set the character to be used as the minus sign
+ * @param value the character to be used
+ * @throws XPathException if the value is not a single Unicode character (a surrogate pair is permitted)
+ */
+
+ public void setMinusSign(String value) throws XPathException {
+ setProperty(MINUS_SIGN, value, 0);
+ }
+
+ /**
+ * Set the character to be used as the percent sign
+ * @param value the character to be used
+ * @throws XPathException if the value is not a single Unicode character (a surrogate pair is permitted)
+ */
+
+ public void setPercent(String value) throws XPathException {
+ setProperty(PERCENT, value, 0);
+ }
+
+ /**
+ * Set the character to be used as the per-mille sign
+ * @param value the character to be used
+ * @throws XPathException if the value is not a single Unicode character (a surrogate pair is permitted)
+ */
+
+ public void setPerMille(String value) throws XPathException {
+ setProperty(PER_MILLE, value, 0);
+ }
+
+ /**
+ * Set the character to be used as the zero digit (which determines the digit family used in the output)
+ * @param value the character to be used
+ * @throws XPathException if the value is not a single Unicode character (a surrogate pair is permitted),
+ * or if it is not a character classified in Unicode as a digit with numeric value zero
+ */
+
+ public void setZeroDigit(String value) throws XPathException {
+ setProperty(ZERO_DIGIT, value, 0);
+ }
+
+ /**
+ * Set the character to be used as the pattern separator (default ';')
+ * @param value the character to be used
+ * @throws XPathException if the value is not a single Unicode character (a surrogate pair is permitted)
+ */
+
+ public void setPatternSeparator(String value) throws XPathException {
+ setProperty(PATTERN_SEPARATOR, value, 0);
+ }
+
+ /**
+ * Set the string to be used to represent infinity
+ * @param value the string to be used
+ * @throws XPathException - should not happen
+ */
+
+ public void setInfinity(String value) throws XPathException {
+ setProperty(INFINITY, value, 0);
+ }
+
+ /**
+ * Set the string to be used to represent NaN
+ * @param value the string to be used
+ * @throws XPathException - should not happen
+ */
+
+ public void setNaN(String value) throws XPathException {
+ setProperty(NAN, value, 0);
+ }
+
+ /**
+ * Set the value of a property
+ *
+ * @param key the integer key of the property to be set
+ * @param value the value of the property as a string (in many cases, this must be a single character)
+ * @param precedence the precedence of the property value
+ * @throws XPathException if the property is invalid.
+ * This method does not check the consistency of different properties. If two different values are supplied
+ * for the same property at the same precedence, the method does not complain, but notes the fact, and if
+ * the inconsistency is not subsequently cleared by supplying another value at a higher precedence, the
+ * error is reported when the checkConsistency() method is subsequently called.
+ */
+
+ public void setProperty(int key, String value, int precedence) throws XPathException {
+ String name = propertyNames[key];
+ if (key <= PATTERN_SEPARATOR) {
+ int intValue = singleChar(name, value);
+ if (precedence > precedences[key]) {
+ intValues[key] = intValue;
+ precedences[key] = precedence;
+ inconsistent[key] = false;
+ } else if (precedence == precedences[key]) {
+ if (intValue != intValues[key]) {
+ inconsistent[key] = true;
+ }
+ } else {
+ // ignore the new value
+ }
+ if (key == ZERO_DIGIT && !isValidZeroDigit(intValue)) {
+ throw new XPathException("The value of the zero-digit attribute must be a Unicode digit with value zero",
+ errorCodes[ERR_NOT_UNICODE_DIGIT]);
+ }
+ } else if (key == INFINITY) {
+ if (precedence > precedences[key]) {
+ infinityValue = value;
+ precedences[key] = precedence;
+ inconsistent[key] = false;
+ } else if (precedence == precedences[key]) {
+ if (!infinityValue.equals(value)) {
+ inconsistent[key] = true;
+ }
+ }
+ } else if (key == NAN) {
+ if (precedence > precedences[key]) {
+ NaNValue = value;
+ precedences[key] = precedence;
+ inconsistent[key] = false;
+ } else if (precedence == precedences[key]) {
+ if (!NaNValue.equals(value)) {
+ inconsistent[key] = false;
+ }
+ }
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ }
+
+ /**
+ * Get the Unicode codepoint corresponding to a String, which must represent a single Unicode character
+ * @param name the name of the property, for use in error messages
+ * @param value the input string, representing a single Unicode character, perhaps as a surrogate pair
+ * @return the corresponding Unicode codepoint
+ * @throws XPathException if the supplied string is not a single character
+ */
+ private int singleChar(String name, String value) throws XPathException {
+ UnicodeString us = UnicodeString.makeUnicodeString(value);
+ if (us.length() != 1) {
+ XPathException err = new XPathException("Attribute " + name + " should be a single character",
+ errorCodes[ERR_NOT_SINGLE_CHAR]);
+ err.setIsStaticError(true);
+ throw err;
+ }
+ return us.charAt(0);
+ }
+
+
+ /**
+ * Check that no character is used in more than one role
+ * @param name the name of the decimal format (null for the unnamed decimal format)
+ * @throws XPathException if the same character is used in conflicting rules, for example as decimal separator
+ * and also as grouping separator
+ */
+
+ public void checkConsistency(StructuredQName name) throws XPathException {
+
+ for (int i=0; i<10; i++) {
+ if (inconsistent[i]) {
+ XPathException err = new XPathException(
+ "Inconsistency in " +
+ (name==null ? "unnamed decimal format. " : "decimal format " + name.getDisplayName() + ". ") +
+ "There are two inconsistent values for decimal-format property " + propertyNames[i] +
+ " at the same import precedence");
+ err.setErrorCode(errorCodes[ERR_TWO_VALUES_FOR_SAME_PROPERTY]);
+ err.setIsStaticError(true);
+ throw err;
+ }
+ }
+
+ IntHashMap<String> map = new IntHashMap<String>(20);
+ map.put(getDecimalSeparator(), StandardNames.DECIMAL_SEPARATOR);
+
+ if (map.get(getGroupingSeparator()) != null) {
+ duplicate(StandardNames.GROUPING_SEPARATOR, map.get(getGroupingSeparator()), name);
+ }
+ map.put(getGroupingSeparator(), StandardNames.GROUPING_SEPARATOR);
+
+ if (map.get(getPercent()) != null) {
+ duplicate(StandardNames.PERCENT, map.get(getPercent()), name);
+ }
+ map.put(getPercent(), StandardNames.PERCENT);
+
+ if (map.get(getPerMille()) != null) {
+ duplicate(StandardNames.PER_MILLE, map.get(getPerMille()), name);
+ }
+ map.put(getPerMille(), StandardNames.PER_MILLE);
+
+ if (map.get(getZeroDigit()) != null) {
+ duplicate(StandardNames.ZERO_DIGIT, map.get(getZeroDigit()), name);
+ }
+ map.put(getZeroDigit(), StandardNames.ZERO_DIGIT);
+
+ if (map.get(getDigit()) != null) {
+ duplicate(StandardNames.DIGIT, map.get(getDigit()), name);
+ }
+ map.put(getDigit(), StandardNames.DIGIT);
+
+ if (map.get(getPatternSeparator()) != null) {
+ duplicate(StandardNames.PATTERN_SEPARATOR, map.get(getPatternSeparator()), name);
+ }
+ //map.put(patternSeparator, StandardNames.PATTERN_SEPARATOR);
+ }
+
+ /**
+ * Report that a character is used in more than one role
+ * @param role1 the first role
+ * @param role2 the second role
+ * @param name the name of the decimal format (null for the unnamed decimal format)
+ * @throws XPathException (always)
+ */
+
+ private void duplicate(String role1, String role2, StructuredQName name) throws XPathException {
+ XPathException err = new XPathException(
+ "Inconsistent properties in " +
+ (name == null ? "unnamed decimal format. " : "decimal format " + name.getDisplayName() + ". ") +
+ "The same character is used as the " + role1 + " and as the " + role2);
+ err.setErrorCode(errorCodes[ERR_SAME_CHAR_IN_TWO_ROLES]);
+ throw err;
+ }
+
+ /**
+ * Check that the character declared as a zero-digit is indeed a valid zero-digit
+ * @param zeroDigit the value to be checked
+ * @return false if it is not a valid zero-digit
+ */
+
+ public static boolean isValidZeroDigit(int zeroDigit) {
+ return (Arrays.binarySearch(zeroDigits, zeroDigit) >= 0);
+ }
+
+ /*@NotNull*/ static int[] zeroDigits = {0x0030, 0x0660, 0x06f0, 0x0966, 0x09e6, 0x0a66, 0x0ae6, 0x0b66, 0x0be6, 0x0c66,
+ 0x0ce6, 0x0d66, 0x0e50, 0x0ed0, 0x0f20, 0x1040, 0x17e0, 0x1810, 0x1946, 0x19d0,
+ 0xff10, 0x104a0, 0x1d7ce, 0x1d7d8, 0x1d7e2, 0x1d7ec, 0x1d7f6};
+
+ /**
+ * Test if two sets of decimal format symbols are the same
+ * @param obj the other set of symbols
+ * @return true if the same characters/strings are assigned to each role in both sets of symbols.
+ * The precedences are not compared.
+ */
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DecimalSymbols)) {
+ return false;
+ }
+ DecimalSymbols o = (DecimalSymbols)obj;
+ return getDecimalSeparator() == o.getDecimalSeparator() &&
+ getGroupingSeparator() == o.getGroupingSeparator() &&
+ getDigit() == o.getDigit() &&
+ getMinusSign() == o.getMinusSign() &&
+ getPercent() == o.getPercent() &&
+ getPerMille() == o.getPerMille() &&
+ getZeroDigit() == o.getZeroDigit() &&
+ getPatternSeparator() == o.getPatternSeparator() &&
+ getInfinity().equals(o.getInfinity()) &&
+ getNaN().equals(o.getNaN());
+ }
+
+ public int hashCode() {
+ return getDecimalSeparator() + (37*getGroupingSeparator()) + (41*getDigit());
+ }
+
+}
+
diff --git a/sf/saxon/trans/DynamicLoader.java b/sf/saxon/trans/DynamicLoader.java
new file mode 100644
index 0000000..94d86d8
--- /dev/null
+++ b/sf/saxon/trans/DynamicLoader.java
@@ -0,0 +1,195 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.serialize.MessageEmitter;
+
+import java.io.PrintStream;
+import java.util.HashMap;
+
+/**
+ * Utility class used to perform dynamic loading of user-hook implementations
+ */
+public class DynamicLoader {
+
+ private ClassLoader classLoader;
+
+ protected HashMap<String, Class> knownClasses = new HashMap<String, Class>(20);
+
+ public DynamicLoader() {
+ registerKnownClasses();
+ }
+
+ /**
+ * Register classes that might be dynamically loaded even though they are contained
+ * within Saxon itself. This typically occurs for default implementations of classes that
+ * can be substituted or subclassed by the user.
+ */
+
+ protected void registerKnownClasses() {
+ knownClasses.put("net.sf.saxon.serialize.MessageEmitter", MessageEmitter.class);
+ //knownClasses.put("net.sf.saxon.java.JavaPlatform", JavaPlatform.class); // not available on .NET
+ knownClasses.put("net.sf.saxon.Configuration", Configuration.class);
+ }
+
+ /**
+ * Set a ClassLoader to be used when loading external classes. Examples of classes that are
+ * loaded include SAX parsers, localization modules for formatting numbers and dates,
+ * extension functions, external object models. In an environment such as Eclipse that uses
+ * its own ClassLoader, this ClassLoader should be nominated to ensure that any class loaded
+ * by Saxon is identical to a class of the same name loaded by the external environment.
+ * <p>
+ * @param loader the ClassLoader to be used in this configuration
+ */
+
+ public void setClassLoader(ClassLoader loader) {
+ classLoader = loader;
+ }
+
+ /**
+ * Get the ClassLoader supplied using the method {@link #setClassLoader}.
+ * If none has been supplied, return null.
+ * <p>
+ * @return the ClassLoader used in this configuration
+ */
+
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ /**
+ * Load a class using the class name provided.
+ * Note that the method does not check that the object is of the right class.
+ * <p>
+ * This method is intended for internal use only.
+ *
+ * @param className A string containing the name of the
+ * class, for example "com.microstar.sax.LarkDriver"
+ * @param traceOut if diagnostic tracing is required, the destination for the output; otherwise null
+ * @param classLoader The ClassLoader to be used to load the class. If this is null, then
+ * the classLoader used will be the first one available of: the classLoader registered
+ * with the Configuration using {@link #setClassLoader}; the context class loader for
+ * the current thread; or failing that, the class loader invoked implicitly by a call
+ * of Class.forName() (which is the ClassLoader that was used to load the Configuration
+ * object itself).
+ * @return an instance of the class named, or null if it is not
+ * loadable.
+ * @throws XPathException if the class cannot be loaded.
+ *
+ */
+
+ public Class getClass(String className, /*@Nullable*/ PrintStream traceOut, /*@Nullable*/ ClassLoader classLoader) throws XPathException {
+ Class known = knownClasses.get(className);
+ if (known != null) {
+ return known;
+ }
+
+ boolean tracing = traceOut != null;
+ if (tracing) {
+ traceOut.println("Loading " + className);
+ }
+
+ try {
+ ClassLoader loader = classLoader;
+ if (loader == null) {
+ loader = this.classLoader;
+ }
+ if (loader == null) {
+ loader = Thread.currentThread().getContextClassLoader();
+ }
+ if (loader != null) {
+ try {
+ return loader.loadClass(className);
+ } catch (Throwable ex) {
+ // Catching Exception is not enough; Java sometimes throws a NoClassDefFoundError
+ return Class.forName(className);
+ }
+ } else {
+ return Class.forName(className);
+ }
+ }
+ catch (Throwable e) {
+ if (tracing) {
+ // The exception is often masked, especially when calling extension
+ // functions
+ traceOut.println("The class " + className + " could not be loaded: " + e.getMessage());
+ }
+ throw new XPathException("Failed to load " + className, e );
+ }
+
+ }
+
+ /**
+ * Instantiate a class using the class name provided.
+ * Note that the method does not check that the object is of the right class.
+ * <p>
+ * This method is intended for internal use only.
+ *
+ * @param className A string containing the name of the
+ * class, for example "com.microstar.sax.LarkDriver"
+ * @param classLoader The ClassLoader to be used to load the class. If this is null, then
+ * the classLoader used will be the first one available of: the classLoader registered
+ * with the Configuration using {@link #setClassLoader}; the context class loader for
+ * the current thread; or failing that, the class loader invoked implicitly by a call
+ * of Class.forName() (which is the ClassLoader that was used to load the Configuration
+ * object itself).
+ * @return an instance of the class named, or null if it is not
+ * loadable.
+ * @throws XPathException if the class cannot be loaded.
+ *
+ */
+
+ public Object getInstance(String className, /*@Nullable*/ ClassLoader classLoader) throws XPathException {
+ Class theclass = getClass(className, null, classLoader);
+ try {
+ return theclass.newInstance();
+ } catch (Exception err) {
+ throw new XPathException("Failed to instantiate class " + className +
+ " (does it have a public zero-argument constructor?)", err);
+ }
+ }
+
+ /**
+ * Instantiate a class using the class name provided, with the option of tracing
+ * Note that the method does not check that the object is of the right class.
+ * <p>
+ * This method is intended for internal use only.
+ *
+ * @param className A string containing the name of the
+ * class, for example "com.microstar.sax.LarkDriver"
+ * @param traceOut if attempts to load classes are to be traced, then the destination
+ * for the trace output; otherwise null
+ * @param classLoader The ClassLoader to be used to load the class. If this is null, then
+ * the classLoader used will be the first one available of: the classLoader registered
+ * with the Configuration using {@link #setClassLoader}; the context class loader for
+ * the current thread; or failing that, the class loader invoked implicitly by a call
+ * of Class.forName() (which is the ClassLoader that was used to load the Configuration
+ * object itself).
+ * @return an instance of the class named, or null if it is not
+ * loadable.
+ * @throws XPathException if the class cannot be loaded.
+ *
+ */
+
+ public Object getInstance(String className, PrintStream traceOut, /*@Nullable*/ ClassLoader classLoader) throws XPathException {
+ Class theclass = getClass(className, traceOut, classLoader);
+ try {
+ return theclass.newInstance();
+ } catch (NoClassDefFoundError err) {
+ throw new XPathException("Failed to load instance of class " + className, err);
+ } catch (Exception err) {
+ throw new XPathException("Failed to instantiate class " + className, err);
+ }
+ }
+
+
+
+
+}
+
diff --git a/sf/saxon/trans/Err.java b/sf/saxon/trans/Err.java
new file mode 100644
index 0000000..cd37cd5
--- /dev/null
+++ b/sf/saxon/trans/Err.java
@@ -0,0 +1,188 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Instruction;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+/**
+ * Class containing utility methods for handling error messages
+ */
+
+public class Err {
+
+ public static final int ELEMENT = 1;
+ public static final int ATTRIBUTE = 2;
+ public static final int FUNCTION = 3;
+ public static final int VALUE = 4;
+ public static final int VARIABLE = 5;
+ public static final int GENERAL = 6;
+ public static final int URI = 7;
+
+ /**
+ * Add delimiters to represent variable information within an error message
+ * @param cs the variable information to be delimited
+ * @return the delimited variable information
+ */
+ public static String wrap(CharSequence cs) {
+ return wrap(cs, GENERAL);
+ }
+
+ /**
+ * Add delimiters to represent variable information within an error message
+ * @param cs the variable information to be delimited
+ * @param valueType the type of value, e.g. element name or attribute name
+ * @return the delimited variable information
+ */
+ public static String wrap(/*@Nullable*/ CharSequence cs, int valueType) {
+ if (cs == null) {
+ return "(NULL)";
+ }
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ int len = cs.length();
+ for (int i=0; i<len; i++) {
+ char c = cs.charAt(i);
+ switch (c) {
+ case '\n':
+ sb.append("\\n");
+ break;
+ case '\t':
+ sb.append("\\t");
+ break;
+ case '\r':
+ sb.append("\\r");
+ break;
+// case '\\':
+// sb.append("\\\\");
+// break;
+ default:
+ if (c < 32 || c > 255) {
+ sb.append("\\u");
+ String hex = Integer.toHexString(c);
+ while (hex.length() < 4) {
+ hex = "0" + hex;
+ }
+ sb.append(hex);
+ } else {
+ sb.append(c);
+ }
+ }
+ }
+ String s;
+ if (len > 30) {
+ if ((valueType == ELEMENT || valueType == ATTRIBUTE) && sb.charAt(0) == '{') {
+ StructuredQName qn = StructuredQName.fromClarkName(sb.toString());
+ String uri = abbreviateURI(qn.getURI());
+ s = "Q{" + uri + "}" + qn.getLocalPart();
+ } else if (valueType == URI) {
+ s = abbreviateURI(sb.toString());
+ } else {
+ s = sb.toString().substring(0, 30) + "...";
+ }
+ } else {
+ s = sb.toString();
+ }
+ switch (valueType) {
+ case ELEMENT:
+ return "<" + s + ">";
+ case ATTRIBUTE:
+ return "@" + s;
+ case FUNCTION:
+ return s + "()";
+ case VARIABLE:
+ return "$" + s;
+ case VALUE:
+ return "\"" + s + "\"";
+ default:
+ return "{" + s + "}";
+ }
+ }
+
+ /**
+ * Create a string representation of an item for use in an error message
+ */
+
+ public static CharSequence depict(Item item) {
+ if (item instanceof AtomicValue) {
+ CharSequence cs = item.getStringValueCS();
+ if (item instanceof StringValue) {
+ return '\"' + truncate30(cs).toString() + '\"';
+ } else {
+ return truncate30(cs);
+ }
+ } else if (item instanceof NodeInfo) {
+ NodeInfo node = (NodeInfo)item;
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT:
+ return "document-node()";
+ case Type.ELEMENT:
+ return "<" + node.getDisplayName() + "/>";
+ case Type.ATTRIBUTE:
+ return "@" + node.getDisplayName();
+ case Type.TEXT:
+ return "text(\"" + truncate30(node.getStringValue()) + "\")";
+ case Type.COMMENT:
+ return "<!--" + truncate30(node.getStringValue()) + "-->";
+ case Type.PROCESSING_INSTRUCTION:
+ return "<?" + node.getDisplayName() + "?>";
+ case Type.NAMESPACE:
+ String prefix = node.getLocalPart();
+ return "xmlns" + (prefix.equals("") ? "" : ":" + prefix) + "=\"" + node.getStringValue() + '"';
+ default:
+ return "";
+ }
+ } else {
+ return "function item";
+ }
+ }
+
+ private static CharSequence truncate30(CharSequence cs) {
+ if (cs.length() <= 30) {
+ return Whitespace.collapseWhitespace(cs);
+ } else {
+ return Whitespace.collapseWhitespace(cs.subSequence(0, 30)).toString() + "...";
+ }
+ }
+
+ /**
+ * Abbreviate a URI for use in error messages
+ * @param uri the full URI
+ * @return the URI, truncated at the last slash or to the last 15 characters, with a leading ellipsis, as appropriate
+ */
+
+ public static String abbreviateURI(String uri) {
+ int lastSlash = uri.lastIndexOf('/');
+ if (lastSlash < 0) {
+ if (uri.length() > 15) {
+ uri = "..." + uri.substring(uri.length()-15);
+ }
+ return uri;
+ } else {
+ return "..." + uri.substring(lastSlash);
+ }
+ }
+
+ public static String wrap(Expression exp) {
+ if (ExpressionTool.expressionSize(exp) < 10 && !(exp instanceof Instruction)) {
+ return "{" + exp.toString() + "}";
+ } else {
+ return exp.getExpressionName();
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/sf/saxon/trans/IAccumulatorManager.java b/sf/saxon/trans/IAccumulatorManager.java
new file mode 100644
index 0000000..1b5b92d
--- /dev/null
+++ b/sf/saxon/trans/IAccumulatorManager.java
@@ -0,0 +1,26 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.functions.FunctionLibrary;
+
+/**
+ * Definition of the class that manages accumulator functions. The concrete implementation class
+ * is part of the Saxon-EE streaming module.
+ */
+
+public interface IAccumulatorManager {
+
+ /**
+ * Get the library of accumulator functions
+ * @return the list of functions in the form of a function library
+ */
+
+ public FunctionLibrary getAccumulatorFunctionLibrary();
+}
+
diff --git a/sf/saxon/trans/KeyDefinition.java b/sf/saxon/trans/KeyDefinition.java
new file mode 100644
index 0000000..2ad1f4f
--- /dev/null
+++ b/sf/saxon/trans/KeyDefinition.java
@@ -0,0 +1,255 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Procedure;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.pattern.PatternFinder;
+import net.sf.saxon.type.BuiltInAtomicType;
+
+import java.io.Serializable;
+
+/**
+ * Corresponds to a single xsl:key declaration.<P>
+ * @author Michael H. Kay
+ */
+
+public class KeyDefinition extends Procedure implements Serializable {
+
+ private PatternFinder match; // the match pattern
+ private Expression use;
+ private BuiltInAtomicType useType; // the type of the values returned by the atomized use expression
+ private StringCollator collation; // the collating sequence, when type=string
+ private String collationName; // the collation URI
+ private boolean backwardsCompatible = false;
+ private boolean strictComparison = false;
+ private boolean convertUntypedToOther = false;
+ private boolean rangeKey = false;
+
+ /**
+ * Constructor to create a key definition
+ * @param match the pattern in the xsl:key match attribute
+ * @param use the expression in the xsl:key use attribute, or the expression that results from compiling
+ * the xsl:key contained instructions. Note that a KeyDefinition constructed by the XSLT or XQuery parser will
+ * always use an Expression here; however, a KeyDefinition constructed at run-time by a compiled stylesheet
+ * or XQuery might use a simple ExpressionEvaluator that lacks all the compile-time information associated
+ * with an Expression
+ * @param collationName the name of the collation being used
+ * @param collation the actual collation. This must be one that supports generation of collation keys.
+ */
+
+ public KeyDefinition(PatternFinder match, Expression use, String collationName, StringCollator collation) {
+ setHostLanguage(Configuration.XSLT);
+ this.match = match;
+ this.use = use;
+ if (use instanceof Expression) {
+ setBody(use);
+ }
+ this.collation = collation;
+ this.collationName = collationName;
+ }
+
+ /**
+ * Say whether this key is a range key, that is, a key capable of returning
+ * (a) the values in a selected range, and (b) the keys in order
+ * @param rangeKey true if this is a range key; false if not
+ */
+
+ public void setRangeKey(boolean rangeKey) {
+ this.rangeKey = rangeKey;
+ }
+
+ /**
+ * Ask whether this key is a range key, that is, a key capable of returning
+ * (a) the values in a selected range, and (b) the keys in order
+ * @return true if this is a range key; false if not
+ */
+
+ public boolean isRangeKey() {
+ return rangeKey;
+ }
+
+ /**
+ * Set the primitive item type of the values returned by the use expression
+ * @param itemType the primitive type of the indexed values
+ */
+
+ public void setIndexedItemType(BuiltInAtomicType itemType) {
+ useType = itemType;
+ }
+
+ /**
+ * Get the primitive item type of the values returned by the use expression
+ * @return the primitive item type of the indexed values
+ */
+
+ public BuiltInAtomicType getIndexedItemType() {
+ if (useType == null) {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ } else {
+ return useType;
+ }
+ }
+
+ /**
+ * Set backwards compatibility mode. The key definition is backwards compatible if ANY of the xsl:key
+ * declarations has version="1.0" in scope.
+ * @param bc set to true if running in XSLT 2.0 backwards compatibility mode
+ */
+
+ public void setBackwardsCompatible(boolean bc) {
+ backwardsCompatible = bc;
+ }
+
+ /**
+ * Test backwards compatibility mode
+ * @return true if running in XSLT backwards compatibility mode
+ */
+
+ public boolean isBackwardsCompatible() {
+ return backwardsCompatible;
+ }
+
+ /**
+ * Set whether strict comparison is needed. Strict comparison treats non-comparable values as an
+ * error rather than a no-match. This is used for internal keys that support value comparisons in
+ * Saxon-EE, it is not used for user-defined XSLT keys.
+ * @param strict true if strict comparison is needed
+ */
+
+ public void setStrictComparison(boolean strict) {
+ strictComparison = strict;
+ }
+
+ /**
+ * Get whether strict comparison is needed. Strict comparison treats non-comparable values as an
+ * error rather than a no-match. This is used for internal keys that support value comparisons in
+ * Saxon-EE, it is not used for user-defined XSLT keys.
+ * @return true if strict comparison is needed.
+ */
+
+ public boolean isStrictComparison() {
+ return strictComparison;
+ }
+
+ /**
+ * Indicate that untypedAtomic values should be converted to the type of the other operand,
+ * rather than to strings. This is used for indexes constructed internally by Saxon-EE to
+ * support filter expressions that use the "=" operator, as distinct from "eq".
+ * @param convertToOther true if comparisons follow the semantics of the "=" operator rather than
+ * the "eq" operator
+ */
+
+ public void setConvertUntypedToOther(boolean convertToOther) {
+ convertUntypedToOther = convertToOther;
+ }
+
+ /**
+ * Determine whether untypedAtomic values are converted to the type of the other operand.
+ * @return true if comparisons follow the semantics of the "=" operator rather than
+ * the "eq" operator
+ */
+
+ public boolean isConvertUntypedToOther() {
+ return convertUntypedToOther;
+ }
+
+ /**
+ * Set the map of local variables needed while evaluating the "use" expression
+ */
+
+ public void setStackFrameMap(SlotManager map) {
+ if (map != null && map.getNumberOfVariables() > 0) {
+ super.setStackFrameMap(map);
+ }
+ }
+
+ /**
+ * Set the system Id and line number of the source xsl:key definition
+ * @param systemId the URI of the module containing the key definition
+ * @param lineNumber the line number of the key definition
+ */
+
+ public void setLocation(String systemId, int lineNumber) {
+ setSystemId(systemId);
+ setLineNumber(lineNumber);
+ }
+
+ /**
+ * Get the match pattern for the key definition
+ * @return the pattern specified in the "match" attribute of the xsl:key declaration
+ */
+
+ public PatternFinder getMatch() {
+ return match;
+ }
+
+ /**
+ * Set the body of the key (the use expression). This is held redundantly as an Expression and
+ * as a SequenceIterable (not sure why!)
+ * @param body the use expression of the key
+ */
+
+ public void setBody(Expression body) {
+ super.setBody(body);
+ use = body;
+ }
+
+ /**
+ * Get the use expression for the key definition
+ * @return the expression specified in the "use" attribute of the xsl:key declaration
+ */
+
+ public Expression getUse() {
+ return use;
+ }
+
+ /**
+ * Get the collation name for this key definition.
+ * @return the collation name (the collation URI)
+ */
+
+ public String getCollationName() {
+ return collationName;
+ }
+
+ /**
+ * Get the collation.
+ * @return the collation
+ */
+
+ public StringCollator getCollation() {
+ return collation;
+ }
+
+ /**
+ * Get the type of construct. This will either be the fingerprint of a standard XSLT instruction name
+ * (values in {@link net.sf.saxon.om.StandardNames}: all less than 1024)
+ * or it will be a constant in class {@link net.sf.saxon.trace.Location}.
+ */
+
+ public int getConstructType() {
+ return StandardNames.XSL_KEY;
+ }
+
+ /**
+ * Get a name identifying the object of the expression, for example a function name, template name,
+ * variable name, key name, element name, etc. This is used only where the name is known statically.
+ *
+ */
+
+ /*@Nullable*/ public StructuredQName getObjectName() {
+ return null;
+ }
+}
+
diff --git a/sf/saxon/trans/KeyDefinitionSet.java b/sf/saxon/trans/KeyDefinitionSet.java
new file mode 100644
index 0000000..dfa183e
--- /dev/null
+++ b/sf/saxon/trans/KeyDefinitionSet.java
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.om.StructuredQName;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A set of xsl:key definitions in a stylesheet that share the same name
+ */
+
+public class KeyDefinitionSet implements Serializable {
+
+ StructuredQName keyName;
+ int keySetNumber; // unique among the KeyDefinitionSets within a KeyManager
+ List<KeyDefinition> keyDefinitions;
+ String collationName;
+ boolean backwardsCompatible; // true if any of the keys is backwards compatible
+ boolean rangeKey; // true if any of the keys is a range key
+
+ /**
+ * Create a key definition set for keys sharing a given name
+ * @param keyName the name of the key definitions in this set
+ * @param keySetNumber a unique number identifying this key definition set
+ */
+
+ public KeyDefinitionSet(StructuredQName keyName, int keySetNumber) {
+ this.keyName = keyName;
+ this.keySetNumber = keySetNumber;
+ keyDefinitions = new ArrayList(3);
+ }
+
+ /**
+ * Add a key definition to this set of key definitions. The caller is responsible for ensuring that
+ * all key definitions in a key definition set have the same name
+ * @param keyDef the key definition to be added
+ * @throws XPathException if the key definition uses a different collation from others in the set
+ */
+
+ public void addKeyDefinition(/*@NotNull*/ KeyDefinition keyDef) throws XPathException {
+ if (keyDefinitions.isEmpty()) {
+ collationName = keyDef.getCollationName();
+ } else {
+ if ((collationName == null && keyDef.getCollationName() != null) ||
+ (collationName != null && !collationName.equals(keyDef.getCollationName()))) {
+ XPathException err = new XPathException("All keys with the same name must use the same collation");
+ err.setErrorCode("XTSE1220");
+ throw err;
+ }
+ // ignore this key definition if it is a duplicate of another already present
+ List<KeyDefinition> v = getKeyDefinitions();
+ for (KeyDefinition other : v) {
+ if (keyDef.getMatch().equals(other.getMatch()) &&
+ keyDef.getBody().equals(other.getBody())) {
+ return;
+ }
+ }
+ }
+ if (keyDef.isBackwardsCompatible()) {
+ backwardsCompatible = true;
+ }
+ if (keyDef.isRangeKey()) {
+ rangeKey = true;
+ }
+ keyDefinitions.add(keyDef);
+ }
+
+ /**
+ * Get the name of the key definitions in this set (they all share the same name)
+ * @return the name of these key definitions
+ */
+
+ public StructuredQName getKeyName() {
+ return keyName;
+ }
+
+ /**
+ * Get the name of the collation used for this key
+ * @return the collation name (a URI)
+ */
+
+ public String getCollationName() {
+ return collationName;
+ }
+
+ /**
+ * Get the KeySet number. This uniquely identifies the KeyDefinitionSet within a KeyManager
+ * @return the unique number
+ */
+
+ public int getKeySetNumber() {
+ return keySetNumber;
+ }
+
+ /**
+ * Get the key definitions in this set
+ * @return the key definitions in this set
+ */
+
+ public List<KeyDefinition> getKeyDefinitions() {
+ return keyDefinitions;
+ }
+
+ /**
+ * Ask if the keys are to be evaluated in backwards compatible mode
+ * @return true if backwards compatibility is in force for at least one of the keys in the set
+ */
+
+ public boolean isBackwardsCompatible() {
+ return backwardsCompatible;
+ }
+
+ /**
+ * Ask if this is a range key
+ * @return true if any of the keys in the set is defined as a range key
+ */
+
+ public boolean isRangeKey() {
+ return rangeKey;
+ }
+}
+
diff --git a/sf/saxon/trans/KeyManager.java b/sf/saxon/trans/KeyManager.java
new file mode 100644
index 0000000..aa5ed4e
--- /dev/null
+++ b/sf/saxon/trans/KeyManager.java
@@ -0,0 +1,728 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.sort.LocalOrderComparer;
+import net.sf.saxon.functions.StringFn;
+import net.sf.saxon.functions.SystemFunctionCall;
+import net.sf.saxon.functions.Tokenize;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.IdrefTest;
+import net.sf.saxon.pattern.PatternFinder;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.tree.iter.*;
+import net.sf.saxon.tree.iter.ListIterator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.DoubleValue;
+import net.sf.saxon.value.NumericValue;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+import java.io.Serializable;
+import java.lang.ref.WeakReference;
+import java.util.*;
+
+/**
+ * KeyManager manages the set of key definitions in a stylesheet, and the indexes
+ * associated with these key definitions. It handles xsl:sort-key as well as xsl:key
+ * definitions.
+ *
+ * <p>The memory management in this class is subtle, with extensive use of weak references.
+ * The idea is that an index should continue to exist in memory so long as both the compiled
+ * stylesheet and the source document exist in memory: if either is removed, the index should
+ * go too. The document itself holds no reference to the index. The compiled stylesheet (which
+ * owns the KeyManager) holds a weak reference to the index. The index, of course, holds strong
+ * references to the nodes in the document. The Controller holds a strong reference to the
+ * list of indexes used for each document, so that indexes remain in memory for the duration
+ * of a transformation even if the documents themselves are garbage collected.</p>
+ *
+ * <p>Potentially there is a need for more than one index for a given key name, depending
+ * on the primitive type of the value provided to the key() function. An index is built
+ * corresponding to the type of the requested value; if subsequently the key() function is
+ * called with the same name and a different type of value, then a new index is built.</p>
+ *
+ * <p>For XSLT-defined keys, equality matching follows the rules of the eq operator, which means
+ * that untypedAtomic values are treated as strings. In backwards compatibility mode, <i>all</i>
+ * values are converted to strings.</p>
+ *
+ * <p>This class is also used for internal indexes constructed (a) to support the idref() function,
+ * and (b) (in Saxon-EE only) to support filter expressions of the form /a/b/c[d=e], where the
+ * path expression being filtered must be a single-document context-free path rooted at a document node,
+ * where exactly one of d and e must be dependent on the focus, and where certain other conditions apply
+ * such as the filter predicate not being positional. The operator in this case may be either "=" or "eq".
+ * If it is "eq", then the semantics are very similar to xsl:key indexes, except that use of non-comparable
+ * types gives an error rather than a non-match. If the operator is "=", however, then the rules for
+ * handling untypedAtomic values are different: these must be converted to the type of the other operand.
+ * In this situation the following rules apply. Assume that the predicate is [use=value], where use is
+ * dependent on the focus (the indexed value), and value is the sought value.</p>
+ *
+ * <ul>
+ * <li>If value is a type other than untypedAtomic, say T, then we build an index for type T, in which any
+ * untypedAtomic values that arise in evaluating "use" are converted to type T. A conversion failure results
+ * in an error. A value of a type that is not comparable to T also results in an error.</li>
+ * <li>If value is untypedAtomic, then we build an index for every type actually encountered in evaluating
+ * the use expression (treating untypedAtomic as string), and then search each of these indexes. (Note that
+ * it is not an error if the use expression returns a mixture of say numbers and dates, provided that the
+ * sought value is untypedAtomic).</li>
+ * </ul>
+ *
+ * @author Michael H. Kay
+ */
+
+public class KeyManager implements Serializable {
+
+ private HashMap<StructuredQName, KeyDefinitionSet> keyMap;
+ // one entry for each named key; the entry contains
+ // a KeyDefinitionSet holding the key definitions with that name
+ private transient WeakHashMap<DocumentInfo, WeakReference<HashMap<Long, Object>>> docIndexes;
+ // one entry for each document that is in memory;
+ // the entry contains a HashMap mapping the fingerprint of
+ // the key name plus the primitive item type
+ // to the HashMap that is the actual index
+ // of key/value pairs.
+
+ /**
+ * Create a KeyManager and initialise variables
+ * @param config the Saxon configuration
+ */
+
+ public KeyManager(Configuration config) {
+ keyMap = new HashMap<StructuredQName, KeyDefinitionSet>(10);
+ docIndexes = new WeakHashMap<DocumentInfo, WeakReference<HashMap<Long, Object>>>(10);
+ // Create a key definition for the idref() function
+ registerIdrefKey(config);
+ }
+
+ /**
+ * An internal key definition is used to support the idref() function. The key definition
+ * is equivalent to xsl:key match="element(*, xs:IDREF) | element(*, IDREFS) |
+ * attribute(*, xs:IDREF) | attribute(*, IDREFS)" use="tokenize(string(.))". This method creates this
+ * key definition.
+ * @param config The configuration. This is needed because the patterns that are
+ * generated need access to schema information.
+ */
+
+ private void registerIdrefKey(Configuration config) {
+ PatternFinder idref = IdrefTest.getInstance();
+ StringFn sf = (StringFn) SystemFunctionCall.makeSystemFunction(
+ "string", new Expression[]{new ContextItemExpression()});
+ StringLiteral regex = new StringLiteral("\\s+");
+ Tokenize use = (Tokenize) SystemFunctionCall.makeSystemFunction("tokenize", new Expression[]{sf, regex});
+ KeyDefinition key = new KeyDefinition(idref, use, null, null);
+ key.setIndexedItemType(BuiltInAtomicType.STRING);
+ try {
+ addKeyDefinition(StandardNames.getStructuredQName(StandardNames.XS_IDREFS), key, config);
+ } catch (XPathException err) {
+ throw new AssertionError(err); // shouldn't happen
+ }
+ }
+
+ /**
+ * Pre-register a key definition. This simply registers that a key with a given name exists,
+ * without providing any details.
+ * @param keyName the name of the key to be pre-registered
+ */
+
+ public void preRegisterKeyDefinition(StructuredQName keyName) {
+ KeyDefinitionSet keySet = keyMap.get(keyName);
+ if (keySet==null) {
+ keySet = new KeyDefinitionSet(keyName, keyMap.size());
+ keyMap.put(keyName, keySet);
+ }
+ }
+
+ /**
+ * Register a key definition. Note that multiple key definitions with the same name are
+ * allowed
+ * @param keyName Structured QName representing the name of the key
+ * @param keydef The details of the key's definition
+ * @param config The configuration
+ * @throws XPathException if this key definition is inconsistent with existing key definitions having the same name
+ */
+
+ public void addKeyDefinition(StructuredQName keyName, KeyDefinition keydef, Configuration config) throws XPathException {
+ KeyDefinitionSet keySet = keyMap.get(keyName);
+ if (keySet==null) {
+ keySet = new KeyDefinitionSet(keyName, keyMap.size());
+ keyMap.put(keyName, keySet);
+ }
+ keySet.addKeyDefinition(keydef);
+
+ boolean backwardsCompatible = keySet.isBackwardsCompatible();
+
+ if (backwardsCompatible) {
+ // In backwards compatibility mode, convert all the use-expression results to sequences of strings
+ List<KeyDefinition> v = keySet.getKeyDefinitions();
+ for (KeyDefinition kd : v) {
+ kd.setBackwardsCompatible(true);
+ if (!kd.getBody().getItemType(config.getTypeHierarchy()).equals(BuiltInAtomicType.STRING)) {
+ Expression exp = new AtomicSequenceConverter(kd.getBody(), BuiltInAtomicType.STRING);
+ ((AtomicSequenceConverter) exp).allocateConverter(config, false);
+ kd.setBody(exp);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Get all the key definitions that match a particular name
+ * @param qName The name of the required key
+ * @return The set of key definitions of the named key if there are any, or null otherwise.
+ */
+
+ public KeyDefinitionSet getKeyDefinitionSet(StructuredQName qName) {
+ return keyMap.get(qName);
+ }
+
+ /**
+ * Build the index for a particular document for a named key
+ * @param keySet The set of key definitions with this name
+ * @param itemType the type of the values to be indexed.
+ * @param foundItemTypes Optional (may be null). If supplied, a set that is to be populated with
+ * the set of primitive types actually found among the "use" values.
+ * @param doc The source document in question
+ * @param context The dynamic context
+ * @return the index in question, as a Map mapping a key value onto a ArrayList of nodes
+ * @throws XPathException if a dynamic error is encountered
+ */
+
+ private synchronized Map<Object, List<NodeInfo>> buildIndex(KeyDefinitionSet keySet,
+ BuiltInAtomicType itemType,
+ Set<BuiltInAtomicType> foundItemTypes,
+ DocumentInfo doc,
+ XPathContext context) throws XPathException {
+
+ List<KeyDefinition> definitions = keySet.getKeyDefinitions();
+ Map<Object, List<NodeInfo>> index =
+ keySet.isRangeKey() ?
+ new TreeMap<Object, List<NodeInfo>>() :
+ new HashMap<Object, List<NodeInfo>>(100);
+
+ // There may be multiple xsl:key definitions with the same name. Index them all.
+ for (int k=0; k<definitions.size(); k++) {
+ constructIndex( doc, index, definitions.get(k), itemType, foundItemTypes, context, k == 0);
+ }
+
+ return index;
+
+ }
+
+ /**
+ * Process one key definition to add entries to an index
+ * @param doc the document to be indexed
+ * @param index the index to be built
+ * @param keydef the key definition used to build the index
+ * @param soughtItemType the primitive type of the value that the user is searching for on the call
+ * to the key() function that triggered this index to be built
+ * @param foundItemTypes Optional (may be null): if supplied, a Set to be populated with the set of
+ * primitive types actually found for the use expression
+ * @param context the XPath dynamic evaluation context
+ * @param isFirst true if this is the first index to be built for this key
+ * @throws XPathException if a dynamic error is encountered
+ */
+
+ private void constructIndex( DocumentInfo doc,
+ Map<Object, List<NodeInfo>> index,
+ KeyDefinition keydef,
+ BuiltInAtomicType soughtItemType,
+ Set<BuiltInAtomicType> foundItemTypes,
+ XPathContext context,
+ boolean isFirst) throws XPathException {
+ //System.err.println("build index for doc " + doc.getDocumentNumber());
+ PatternFinder match = keydef.getMatch();
+
+ //NodeInfo curr;
+ XPathContextMajor xc = context.newContext();
+ xc.setOrigin(keydef);
+
+ // The use expression (or sequence constructor) may contain local variables.
+ SlotManager map = keydef.getStackFrameMap();
+ if (map != null) {
+ xc.openStackFrame(map);
+ }
+
+ SequenceIterator iter = match.selectNodes(doc, xc);
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ processKeyNode((NodeInfo)item, soughtItemType, foundItemTypes, keydef, index, xc, isFirst);
+ }
+ }
+
+ /**
+ * Process one matching node, adding entries to the index if appropriate
+ * @param curr the node being processed
+ * @param soughtItemType the primitive item type of the argument to the key() function that triggered
+ * this index to be built
+ * @param foundItemTypes Optional (may be null): if supplied, a Set to be populated with the set of
+ * primitive types actually found for the use expression
+ * @param keydef the key definition
+ * @param index the index being constructed
+ * @param xc the context for evaluating expressions
+ * @param isFirst indicates whether this is the first key definition with a given key name (which means
+ * no sort of the resulting key entries is required)
+ * @throws XPathException if a dynamic error is encountered
+ */
+
+ private void processKeyNode( NodeInfo curr,
+ BuiltInAtomicType soughtItemType,
+ Set<BuiltInAtomicType> foundItemTypes,
+ KeyDefinition keydef,
+ Map<Object, List<NodeInfo>> index,
+ XPathContext xc,
+ boolean isFirst) throws XPathException {
+
+
+ // Make the node we are testing the context node,
+ // with context position and context size set to 1
+
+ AxisIterator si = SingleNodeIterator.makeIterator(curr);
+ si.next(); // need to position iterator at first node
+
+ xc.setCurrentIterator(si);
+
+ StringCollator collation = keydef.getCollation();
+ final ConversionRules rules = xc.getConfiguration().getConversionRules();
+
+ // Evaluate the "use" expression against this context node
+
+ Expression use = keydef.getUse();
+ SequenceIterator useval = use.iterate(xc);
+ while (true) {
+ AtomicValue item = (AtomicValue)useval.next();
+ if (item == null) {
+ break;
+ }
+ BuiltInAtomicType actualItemType = item.getPrimitiveType();
+ if (foundItemTypes != null) {
+ foundItemTypes.add(actualItemType);
+ }
+ if (!Type.isGuaranteedComparable(actualItemType, soughtItemType, false)) {
+ // the types aren't comparable
+ if (keydef.isStrictComparison()) {
+ XPathException de = new XPathException("Cannot compare " + soughtItemType +
+ " to " + actualItemType + " using 'eq'");
+ de.setErrorCode("XPTY0004");
+ throw de;
+ } else if (keydef.isConvertUntypedToOther() &&
+ actualItemType.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ item = Converter.convert(item, soughtItemType, rules).asAtomic();
+ } else if (keydef.isConvertUntypedToOther() &&
+ soughtItemType.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ // index the item as is
+ } else {
+ // simply ignore this key value
+ continue;
+ }
+ }
+ Object val;
+
+ if (soughtItemType.equals(BuiltInAtomicType.UNTYPED_ATOMIC) ||
+ soughtItemType.equals(BuiltInAtomicType.STRING) ||
+ soughtItemType.equals(BuiltInAtomicType.ANY_URI)) {
+ // If the supplied key value is untyped atomic, we build an index using the
+ // actual type returned by the use expression
+ // If the supplied key value is a string, there is no match unless the use expression
+ // returns a string or an untyped atomic value
+ if (collation == null) {
+ val = item.getStringValue();
+ } else {
+ val = collation.getCollationKey(item.getStringValue());
+ }
+ } else {
+ // Ignore NaN values
+ if (item.isNaN()) {
+ break;
+ }
+ try {
+ AtomicValue av = Converter.convert(item, soughtItemType, rules).asAtomic();
+ val = av.getXPathComparable(false, collation, xc);
+ } catch (XPathException err) {
+ // ignore values that can't be converted to the required type
+ break;
+ }
+ }
+
+ List<NodeInfo> nodes = index.get(val);
+ if (nodes==null) {
+ // this is the first node with this key value
+ nodes = new ArrayList<NodeInfo>(4);
+ index.put(val, nodes);
+ nodes.add(curr);
+ } else {
+ // this is not the first node with this key value.
+ // add the node to the list of nodes for this key,
+ // unless it's already there
+ if (isFirst) {
+ // if this is the first index definition that we're processing,
+ // then this node must be after all existing nodes in document
+ // order, or the same node as the last existing node
+ if (nodes.get(nodes.size()-1)!=curr) {
+ nodes.add(curr);
+ }
+ } else {
+ // otherwise, we need to insert the node at the correct
+ // position in document order. This code does an insertion sort:
+ // not ideal for performance, but it's very unusual to have more than
+ // one key definition for a key.
+ LocalOrderComparer comparer = LocalOrderComparer.getInstance();
+ boolean found = false;
+ for (int i=0; i<nodes.size(); i++) {
+ int d = comparer.compare(curr, nodes.get(i));
+ if (d<=0) {
+ if (d==0) {
+ // node already in list; do nothing
+ } else {
+ // add the node at this position
+ nodes.add(i, curr);
+ }
+ found = true;
+ break;
+ }
+ // else continue round the loop
+ }
+ // if we're still here, add the new node at the end
+ if (!found) {
+ nodes.add(curr);
+ }
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Get the nodes with a given key value. This method is called from XQuery compiled code
+ * @param keyName key name used in the call to the key() function
+ * @param doc The source document in question
+ * @param soughtValue The required key value
+ * @param context The dynamic context, needed only the first time when the key is being built
+ * @return an iteration of the selected nodes, always in document order with no duplicates
+ */
+
+ /*@Nullable*/ public SequenceIterator selectByKey(
+ StructuredQName keyName,
+ DocumentInfo doc,
+ AtomicValue soughtValue,
+ XPathContext context) throws XPathException {
+ KeyDefinitionSet keyDef = getKeyDefinitionSet(keyName);
+ if (keyDef == null) {
+ throw new XPathException("Key " + keyName.getDisplayName() + " has not been defined");
+ }
+ return selectByKey(keyDef, doc, soughtValue, context);
+ }
+
+ /**
+ * Get the nodes with a given key value
+ * @param keySet The set of key definitions identified by the key name used in the call to the key() function
+ * @param doc The source document in question
+ * @param soughtValue The required key value
+ * @param context The dynamic context, needed only the first time when the key is being built
+ * @return an iteration of the selected nodes, always in document order with no duplicates
+ */
+
+ public SequenceIterator<NodeInfo> selectByKey(
+ KeyDefinitionSet keySet,
+ DocumentInfo doc,
+ AtomicValue soughtValue,
+ XPathContext context) throws XPathException {
+
+ //System.err.println("*********** USING KEY ************");
+ if (soughtValue == null) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ List definitions = keySet.getKeyDefinitions();
+// if (definitions == null) {
+// throw new XPathException("Key " + context.getNamePool().getDisplayName(keyNameFingerprint) +
+// " has not been defined", "XTDE1260", context);
+// }
+ KeyDefinition definition = (KeyDefinition)definitions.get(0);
+ // the itemType and collation and BC mode will be the same for all keys with the same name
+ StringCollator collation = definition.getCollation();
+
+ if (keySet.isBackwardsCompatible()) {
+ // if backwards compatibility is in force, treat all values as strings
+ final ConversionRules rules = context.getConfiguration().getConversionRules();
+ soughtValue = Converter.convert(soughtValue, BuiltInAtomicType.STRING, rules).asAtomic();
+ } else {
+ // If the key value is numeric, promote it to a double
+ // TODO: this could result in two decimals comparing equal because they convert to the same double
+
+ BuiltInAtomicType itemType = soughtValue.getPrimitiveType();
+ if (itemType.equals(BuiltInAtomicType.INTEGER) ||
+ itemType.equals(BuiltInAtomicType.DECIMAL) ||
+ itemType.equals(BuiltInAtomicType.FLOAT)) {
+ soughtValue = new DoubleValue(((NumericValue)soughtValue).getDoubleValue());
+ }
+ }
+
+ // If the sought value is untypedAtomic and the equality matching mode is
+ // "convertUntypedToOther", then we construct and search one index for each
+ // primitive atomic type that could occur in the result of the "use" expression,
+ // and merge the results. We rely on the fact that in this case, there will only
+ // be one key definition.
+
+ // NOTE: This is much more elaborate than it needs to be. The option convertUntypedToOther
+ // is used for an index used to support a general comparison. This reports an error if two
+ // non-comparable values are compared. We could report an error immediately if foundItemTypes
+ // includes a type that is not comparable to the soughtValue. In practice we only need a maximum
+ // of two indexes: one for the sought item type, and one for untypedAtomic.
+
+ HashSet<BuiltInAtomicType> foundItemTypes = null;
+ AtomicValue value = soughtValue;
+ if (soughtValue instanceof UntypedAtomicValue && definition.isConvertUntypedToOther()) {
+ // We try string first, but at the same time as building an index for strings,
+ // we collect details of the other types actually encountered for the use expression
+ BuiltInAtomicType useType = definition.getIndexedItemType();
+ if (useType.equals(BuiltInAtomicType.ANY_ATOMIC)) {
+ foundItemTypes = new HashSet<BuiltInAtomicType>(10);
+ useType = BuiltInAtomicType.STRING;
+ }
+ final ConversionRules rules = context.getConfiguration().getConversionRules();
+ value = Converter.convert(soughtValue, useType, rules).asAtomic();
+ }
+
+ // No special action needed for anyURI to string promotion (it just seems to work: tests idky44, 45)
+
+ int keySetNumber = keySet.getKeySetNumber();
+ BuiltInAtomicType itemType = value.getPrimitiveType();
+ Map index;
+ index = obtainIndex(keySet, doc, context, foundItemTypes, itemType);
+
+ if (foundItemTypes == null) {
+ ArrayList nodes = (ArrayList)index.get(getCollationKey(value, itemType, collation, context));
+ if (nodes==null) {
+ return EmptyIterator.getInstance();
+ } else {
+ return new net.sf.saxon.tree.iter.ListIterator(nodes);
+ }
+ } else {
+ // we need to search the indexes for all possible types, and combine the results.
+ SequenceIterator result = null;
+ WeakReference<HashMap<Long, Object>> ref = docIndexes.get(doc);
+ if (ref != null) {
+ HashMap<Long, Object> indexList = ref.get();
+ if (indexList != null) {
+ for (long key : indexList.keySet()) {
+ if (((key >> 32)) == keySetNumber) {
+ int typefp = (int) key;
+
+ BuiltInAtomicType type = (BuiltInAtomicType) BuiltInType.getSchemaType(typefp);
+
+ Object indexObject2 = getIndex(doc, keySetNumber, type);
+ if (indexObject2 instanceof String) {
+ // index is under construction
+ XPathException de = new XPathException("Key definition is circular");
+ de.setXPathContext(context);
+ de.setErrorCode("XTDE0640");
+ throw de;
+ }
+ HashMap index2 = (HashMap) indexObject2;
+ // NOTE: we've been known to encounter a null index2 here, but it doesn't seem possible
+ if (!index2.isEmpty()) {
+ final ConversionRules rules = context.getConfiguration().getConversionRules();
+ value = Converter.convert(soughtValue, type, rules).asAtomic();
+ ArrayList nodes = (ArrayList) index2.get(getCollationKey(value, type, collation, context));
+ if (nodes != null) {
+ if (result == null) {
+ result = new ListIterator(nodes);
+ } else {
+ result = new UnionEnumeration(result, new ListIterator(nodes), LocalOrderComparer.getInstance());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (result == null) {
+ return EmptyIterator.getInstance();
+ } else {
+ return result;
+ }
+ }
+ }
+
+ public Map obtainIndex(KeyDefinitionSet keySet, DocumentInfo doc, XPathContext context, HashSet<BuiltInAtomicType> foundItemTypes, BuiltInAtomicType itemType) throws XPathException {
+ Map index;
+ int keySetNumber = keySet.getKeySetNumber();
+ synchronized (doc) {
+ // Need to synchronize to prevent two threads that use the same stylesheet indexing the same source
+ // document simultaneously. We could synchronize on either the key definition or the document
+ // (ideally we would use the combination of the two), but the document is less likely to cause
+ // unnecessary contention: it's more likely that an index definition applies to large numbers of
+ // documents than that a document has large numbers of indexes.
+ Object indexObject = getIndex(doc, keySetNumber, itemType);
+ if (indexObject instanceof String) {
+ // index is under construction
+ XPathException de = new XPathException("Key definition is circular");
+ de.setXPathContext(context);
+ de.setErrorCode("XTDE0640");
+ throw de;
+ }
+ index = (Map)indexObject;
+
+ // If the index does not yet exist, then create it.
+ if (index==null) {
+ // Mark the index as being under construction, in case the definition is circular
+ putIndex(doc, keySetNumber, itemType, "Under Construction", context);
+ index = buildIndex(keySet, itemType, foundItemTypes, doc, context);
+ putIndex(doc, keySetNumber, itemType, index, context);
+ if (foundItemTypes != null) {
+ // build indexes for each item type actually found
+ for (BuiltInAtomicType t : foundItemTypes) {
+ if (!t.equals(BuiltInAtomicType.STRING)) {
+ putIndex(doc, keySetNumber, t, "Under Construction", context);
+ index = buildIndex(keySet, t, null, doc, context);
+ putIndex(doc, keySetNumber, t, index, context);
+ }
+ }
+ }
+ }
+ }
+ return index;
+ }
+
+ private static Object getCollationKey(AtomicValue value, BuiltInAtomicType itemType,
+ StringCollator collation, XPathContext context) throws XPathException {
+ Object val;
+ if (itemType.equals(BuiltInAtomicType.STRING) ||
+ itemType.equals(BuiltInAtomicType.UNTYPED_ATOMIC) ||
+ itemType.equals(BuiltInAtomicType.ANY_URI)) {
+ if (collation==null) {
+ val = value.getStringValue();
+ } else {
+ val = collation.getCollationKey(value.getStringValue());
+ }
+ } else {
+ val = value.getXPathComparable(false, collation, context);
+ }
+ return val;
+ }
+
+ /**
+ * Save the index associated with a particular key, a particular item type,
+ * and a particular document. This
+ * needs to be done in such a way that the index is discarded by the garbage collector
+ * if the document is discarded. We therefore use a WeakHashMap indexed on the DocumentInfo,
+ * which returns HashMap giving the index for each key fingerprint. This index is itself another
+ * HashMap.
+ * The methods need to be synchronized because several concurrent transformations (which share
+ * the same KeyManager) may be creating indexes for the same or different documents at the same
+ * time.
+ * @param doc the document being indexed
+ * @param keyFingerprint represents the name of the key definition
+ * @param itemType the primitive type of the values being indexed
+ * @param index the index being saved
+ * @param context the dynamic evaluation context
+ */
+
+ private synchronized void putIndex(DocumentInfo doc, int keyFingerprint,
+ AtomicType itemType, Object index, XPathContext context) {
+ if (docIndexes==null) {
+ // it's transient, so it will be null when reloading a compiled stylesheet
+ docIndexes = new WeakHashMap<DocumentInfo, WeakReference<HashMap<Long, Object>>>(10);
+ }
+ WeakReference<HashMap<Long, Object>> indexRef = docIndexes.get(doc);
+ HashMap<Long, Object> indexList;
+ if (indexRef==null || indexRef.get()==null) {
+ indexList = new HashMap<Long, Object>(10);
+ // Ensure there is a firm reference to the indexList for the duration of a transformation
+ // But for keys associated with temporary trees, or documents that have been discarded from
+ // the document pool, keep the reference within the document node itself.
+ Controller controller = context.getController();
+ if (controller.getDocumentPool().contains(doc)) {
+ context.getController().setUserData(doc, "saxon:key-index-list", indexList);
+ } else {
+ doc.setUserData("saxon:key-index-list", indexList);
+ }
+ docIndexes.put(doc, new WeakReference<HashMap<Long, Object>>(indexList));
+ } else {
+ indexList = indexRef.get();
+ }
+ indexList.put(Long.valueOf(((long)keyFingerprint)<<32 | itemType.getFingerprint()), index);
+ }
+
+ /**
+ * Get the index associated with a particular key, a particular source document,
+ * and a particular primitive item type
+ * @param doc the document whose index is required
+ * @param keyFingerprint the name of the key definition
+ * @param itemType the primitive item type of the values being indexed
+ * @return either an index (as a HashMap), or the String "under construction", or null
+ */
+
+ private synchronized Object getIndex(DocumentInfo doc, int keyFingerprint, AtomicType itemType) {
+ if (docIndexes==null) {
+ // it's transient, so it will be null when reloading a compiled stylesheet
+ docIndexes = new WeakHashMap<DocumentInfo, WeakReference<HashMap<Long, Object>>>(10);
+ }
+ WeakReference<HashMap<Long, Object>> ref = docIndexes.get(doc);
+ if (ref==null) return null;
+ HashMap<Long, Object> indexList = ref.get();
+ if (indexList==null) return null;
+ return indexList.get(Long.valueOf(((long)keyFingerprint)<<32 | itemType.getFingerprint()));
+ }
+
+ /**
+ * Clear all the indexes for a given document. This is currently done whenever updates
+ * are applied to the document, because updates can potentially invalidate the indexes.
+ * @param doc the document whose indexes are to be invalidated
+ */
+
+ public synchronized void clearDocumentIndexes(DocumentInfo doc) {
+ docIndexes.remove(doc);
+ }
+
+ /**
+ * Get the number of distinctly-named key definitions
+ * @return the number of key definition sets (where the key definitions in one set share the same name)
+ */
+
+ public int getNumberOfKeyDefinitions() {
+ return keyMap.size();
+ }
+
+ /**
+ * Diagnostic output explaining the keys
+ * @param out the expression presenter that will display the information
+ */
+
+ public void explainKeys(ExpressionPresenter out) {
+ if (keyMap.size() < 2) {
+ // don't bother with IDREFS if it's the only index
+ return;
+ }
+ out.startElement("keys");
+ for (Map.Entry<StructuredQName, KeyDefinitionSet> e : keyMap.entrySet()) {
+ StructuredQName qName = e.getKey();
+ List<KeyDefinition> list = e.getValue().getKeyDefinitions();
+ for (KeyDefinition kd : list) {
+ out.startElement("key");
+ out.emitAttribute("name", qName.getDisplayName());
+ out.emitAttribute("match", kd.getMatch().toString());
+ if (kd.isRangeKey()) {
+ out.emitAttribute("range", "true");
+ }
+ kd.getUse().explain(out);
+ out.endElement();
+ }
+ }
+ out.endElement();
+ }
+}
+
diff --git a/sf/saxon/trans/LicenseException.java b/sf/saxon/trans/LicenseException.java
new file mode 100644
index 0000000..d92c15d
--- /dev/null
+++ b/sf/saxon/trans/LicenseException.java
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+/**
+ * Exception thrown when there are problems with the license file
+ */
+public class LicenseException extends RuntimeException {
+
+ private int reason;
+
+ public static final int EXPIRED = 1;
+ public static final int INVALID = 2;
+ public static final int NOT_FOUND = 3;
+ public static final int WRONG_FEATURES = 4;
+ public static final int CANNOT_READ = 5;
+ public static final int WRONG_CONFIGURATION = 6;
+
+ public LicenseException(String message, int reason) {
+ super(message);
+ this.reason = reason;
+ }
+
+ public void setReason(int reason) {
+ this.reason = reason;
+ }
+
+ public int getReason() {
+ return reason;
+ }
+}
diff --git a/sf/saxon/trans/Mode.java b/sf/saxon/trans/Mode.java
new file mode 100644
index 0000000..d1f075c
--- /dev/null
+++ b/sf/saxon/trans/Mode.java
@@ -0,0 +1,1139 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import com.saxonica.stream.TemplateInversion;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.expr.instruct.ParameterSet;
+import net.sf.saxon.expr.instruct.TailCall;
+import net.sf.saxon.expr.instruct.Template;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.sort.GenericSorter;
+import net.sf.saxon.expr.sort.Sortable;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.TraceListener;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.style.StylesheetModule;
+import net.sf.saxon.trace.ExpressionPresenter;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.Whitespace;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.z.IntIterator;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A Mode is a collection of rules; the selection of a rule to apply to a given element
+ * is determined by a Pattern.
+ *
+ * @author Michael H. Kay
+ */
+
+public class Mode implements Serializable {
+
+ // TODO:PERF the data structure does not cater well for a stylesheet making heavy use of
+ // match="schema-element(X)". We should probably expand the substitution group.
+
+ public static final int UNNAMED_MODE = -1;
+ public static final int NAMED_MODE = -3;
+ public static final int ACCUMULATOR = -5;
+
+ public static final StructuredQName ALL_MODES =
+ new StructuredQName("saxon", NamespaceConstant.SAXON, "_omniMode");
+ public static final StructuredQName UNNAMED_MODE_NAME =
+ new StructuredQName("saxon", NamespaceConstant.SAXON, "_defaultMode");
+
+ private BuiltInRuleSet builtInRuleSet = TextOnlyCopyRuleSet.getInstance();
+
+ private Rule genericRuleChain = null;
+ private Rule atomicValueRuleChain = null;
+ private Rule functionItemRuleChain = null;
+ private Rule documentRuleChain = null;
+ private Rule textRuleChain = null;
+ private Rule commentRuleChain = null;
+ private Rule processingInstructionRuleChain = null;
+ private Rule namespaceRuleChain = null;
+ private Rule unnamedElementRuleChain = null;
+ private Rule unnamedAttributeRuleChain = null;
+ private IntHashMap<Rule> namedElementRuleChains = new IntHashMap<Rule>(32);
+ private IntHashMap<Rule> namedAttributeRuleChains = new IntHashMap<Rule>(8);
+
+ private Rule mostRecentRule;
+ private int mostRecentModuleHash;
+ private boolean isDefault;
+ private boolean streamable;
+ private boolean hasRules = false;
+ private StructuredQName modeName;
+ private int stackFrameSlotsNeeded = 0;
+ private int recoveryPolicy = Configuration.RECOVER_WITH_WARNINGS; // since 9.2 fixed at compile time
+
+ /**
+ * Default constructor - creates a Mode containing no rules
+ * @param usage one of {@link #UNNAMED_MODE}, {@link #NAMED_MODE}
+ * @param modeName the name of the mode
+ */
+
+ public Mode(int usage, StructuredQName modeName) {
+ isDefault = (usage == UNNAMED_MODE);
+ this.modeName = modeName;
+ }
+
+ /**
+ * Construct a new Mode, copying the contents of an existing Mode
+ *
+ * @param omniMode the existing mode. May be null, in which case it is not copied
+ * @param modeName the name of the new mode to be created
+ */
+
+ public Mode(Mode omniMode, StructuredQName modeName) {
+ isDefault = false;
+ this.modeName = modeName;
+ if (omniMode != null) {
+ // TODO: add atomic values and function items
+ genericRuleChain =
+ omniMode.genericRuleChain==null ? null : new Rule(omniMode.genericRuleChain);
+ documentRuleChain =
+ omniMode.documentRuleChain==null ? null : new Rule(omniMode.documentRuleChain);
+ textRuleChain =
+ omniMode.textRuleChain==null ? null : new Rule(omniMode.textRuleChain);
+ commentRuleChain =
+ omniMode.commentRuleChain==null ? null : new Rule(omniMode.commentRuleChain);
+ processingInstructionRuleChain =
+ omniMode.processingInstructionRuleChain==null ? null : new Rule(omniMode.processingInstructionRuleChain);
+ namespaceRuleChain =
+ omniMode.namespaceRuleChain==null ? null : new Rule(omniMode.namespaceRuleChain);
+ unnamedElementRuleChain =
+ omniMode.unnamedElementRuleChain==null ? null : new Rule(omniMode.unnamedElementRuleChain);
+ unnamedAttributeRuleChain =
+ omniMode.unnamedAttributeRuleChain==null ? null : new Rule(omniMode.unnamedAttributeRuleChain);
+
+ namedElementRuleChains = new IntHashMap<Rule>(omniMode.namedElementRuleChains.size());
+ // TODO: this looks as if it is only copying the first rule on each chain
+ IntIterator ii = omniMode.namedElementRuleChains.keyIterator();
+ while (ii.hasNext()) {
+ int fp = ii.next();
+ Rule r = omniMode.namedElementRuleChains.get(fp);
+ namedElementRuleChains.put(fp, new Rule(r));
+ }
+ ii = omniMode.namedAttributeRuleChains.keyIterator();
+ while (ii.hasNext()) {
+ int fp = ii.next();
+ Rule r = omniMode.namedAttributeRuleChains.get(fp);
+ namedAttributeRuleChains.put(fp, new Rule(r));
+ }
+ mostRecentRule = omniMode.mostRecentRule;
+ mostRecentModuleHash = omniMode.mostRecentModuleHash;
+ }
+ }
+
+ /**
+ * Set the built-in template rules to be used with this Mode in the case where there is no
+ * explicit template rule
+ * @param defaultRules the built-in rule set
+ */
+
+ public void setBuiltInRuleSet(BuiltInRuleSet defaultRules) {
+ this.builtInRuleSet = defaultRules;
+ hasRules = true; // if mode is explicitly declared, treat it as containing rules
+ }
+
+ /**
+ * Get the built-in template rules to be used with this Mode in the case where there is no
+ * explicit template rule
+ * @return the built-in rule set, defaulting to the TextOnlyCopyRuleSet if no other rule set has
+ * been supplied
+ */
+
+ public BuiltInRuleSet getBuiltInRuleSet() {
+ return this.builtInRuleSet;
+ }
+
+ /**
+ * Determine if this is the default mode
+ * @return true if this is the default (unnamed) mode
+ */
+
+ public boolean isDefaultMode() {
+ return isDefault;
+ }
+
+ /**
+ * Get the name of the mode (for diagnostics only)
+ * @return the mode name. Null for the default (unnamed) mode
+ */
+
+ public StructuredQName getModeName() {
+ return modeName;
+ }
+
+ /**
+ * Ask whether there are any template rules in this mode
+ * (a mode could exist merely because it is referenced in apply-templates)
+ * @return true if no template rules exist in this mode
+ */
+
+ public boolean isEmpty() {
+ return !hasRules;
+ }
+
+ /**
+ * Set the policy for handling recoverable errrors. Note that for some errors the decision can be
+ * made at run-time, but for the "ambiguous template match" error, the decision is (since 9.2)
+ * fixed at compile time.
+ * @param policy the recovery policy to be used. The options are {@link Configuration#RECOVER_SILENTLY},
+ * {@link Configuration#RECOVER_WITH_WARNINGS}, or {@link Configuration#DO_NOT_RECOVER}.
+ */
+
+ public void setRecoveryPolicy(int policy) {
+ recoveryPolicy = policy;
+ }
+
+ /**
+ * Get the policy for handling recoverable errors. Note that for some errors the decision can be
+ * made at run-time, but for the "ambiguous template match" error, the decision is (since 9.2)
+ * fixed at compile time.
+ *
+ * @return the current policy.
+ */
+
+ public int getRecoveryPolicy() {
+ return recoveryPolicy;
+ }
+
+ /**
+ * Say that this mode is (or is not) streamable
+ * @param streamable true if this mode is a streamable mode
+ */
+
+ public void setStreamable(boolean streamable) {
+ this.streamable = streamable;
+ }
+
+ /**
+ * Ask whether this mode is streamable
+ * @return true if this mode is streamable
+ */
+
+ public boolean isStreamable() {
+ return streamable;
+ }
+
+ /**
+ * Get the "explicit namespaces" matched by this mode. Returns a set containing all the namespaces
+ * matched by specific template rules in this mode
+ */
+
+ public Set<String> getExplicitNamespaces(NamePool pool) {
+ Set<String> namespaces = new HashSet<String>();
+ IntIterator ii = namedElementRuleChains.keyIterator();
+ while (ii.hasNext()) {
+ int fp = ii.next();
+ namespaces.add(pool.getURI(fp));
+ }
+ return namespaces;
+ }
+
+
+ /**
+ * Add a rule to the Mode.
+ *
+ * @param pattern a Pattern
+ * @param action the Object to return from getRule() when the supplied node matches this Pattern
+ * @param module the stylesheet module containing the rule
+ * @param priority the priority of the rule
+ * @param explicitMode true if adding a template rule for a specific (default or named) mode;
+ * false if adding a rule because it applies to all modes
+ */
+
+ public void addRule(Pattern pattern, RuleTarget action,
+ StylesheetModule module, double priority, boolean explicitMode) {
+
+ if (explicitMode) {
+ hasRules = true;
+ }
+
+ // Ignore a pattern that will never match, e.g. "@comment"
+
+ if (pattern.getItemType() instanceof ErrorType) {
+ return;
+ }
+
+ // for fast lookup, we maintain one list for each element name for patterns that can only
+ // match elements of a given name, one list for each node type for patterns that can only
+ // match one kind of non-element node, and one generic list.
+ // Each list is sorted in precedence/priority order so we find the highest-priority rule first
+
+ // This logic is designed to ensure that when a UnionPattern contains multiple branches
+ // with the same priority, next-match doesn't select the same template twice (override20_047)
+ int moduleHash = module.hashCode();
+ int sequence;
+ if (mostRecentRule == null) {
+ sequence = 0;
+ } else if (action == mostRecentRule.getAction() && moduleHash == mostRecentModuleHash) {
+ sequence = mostRecentRule.getSequence();
+ } else {
+ sequence = mostRecentRule.getSequence() + 1;
+ }
+ int precedence = module.getPrecedence();
+ int minImportPrecedence = module.getMinImportPrecedence();
+ Rule newRule = new Rule(pattern, action, precedence, minImportPrecedence, priority, sequence);
+ if (pattern instanceof ItemTypePattern) {
+ ItemType test = pattern.getItemType();
+ if (test instanceof AnyNodeTest) {
+ newRule.setAlwaysMatches(true);
+ } else if (test instanceof NodeKindTest) {
+ newRule.setAlwaysMatches(true);
+ } else if (test instanceof NameTest) {
+ int kind = test.getPrimitiveType();
+ if (kind == Type.ELEMENT || kind == Type.ATTRIBUTE) {
+ newRule.setAlwaysMatches(true);
+ }
+ }
+ }
+ mostRecentRule = newRule;
+ mostRecentModuleHash = moduleHash;
+
+ int kind = pattern.getNodeKind();
+ switch (kind) {
+ case Type.ELEMENT: {
+ int fp = pattern.getFingerprint();
+ if (fp == -1) {
+ unnamedElementRuleChain = addRuleToList(newRule, unnamedElementRuleChain);
+ } else {
+ Rule chain = namedElementRuleChains.get(fp);
+ namedElementRuleChains.put(fp, addRuleToList(newRule, chain));
+ }
+ break;
+ }
+ case Type.ATTRIBUTE: {
+ int fp = pattern.getFingerprint();
+ if (fp == -1) {
+ unnamedAttributeRuleChain = addRuleToList(newRule, unnamedAttributeRuleChain);
+ } else {
+ Rule chain = namedAttributeRuleChains.get(fp);
+ namedAttributeRuleChains.put(fp, addRuleToList(newRule, chain));
+ }
+ break;
+ }
+ case Type.NODE:
+ genericRuleChain = addRuleToList(newRule, genericRuleChain);
+ break;
+ case Type.DOCUMENT:
+ documentRuleChain = addRuleToList(newRule, documentRuleChain);
+ break;
+ case Type.TEXT:
+ textRuleChain = addRuleToList(newRule, textRuleChain);
+ break;
+ case Type.COMMENT:
+ commentRuleChain = addRuleToList(newRule, commentRuleChain);
+ break;
+ case Type.PROCESSING_INSTRUCTION:
+ processingInstructionRuleChain = addRuleToList(newRule, processingInstructionRuleChain);
+ break;
+ case Type.NAMESPACE:
+ namespaceRuleChain = addRuleToList(newRule, namespaceRuleChain);
+ break;
+ default:
+ if (pattern instanceof ItemTypePattern || pattern instanceof PatternWithPredicate) {
+ ItemType type = pattern.getItemType();
+ if (type instanceof AtomicType) {
+ atomicValueRuleChain = addRuleToList(newRule, atomicValueRuleChain);
+ } else if (type instanceof FunctionItemType) {
+ functionItemRuleChain = addRuleToList(newRule, functionItemRuleChain);
+ } else {
+ throw new UnsupportedOperationException("XSLT 3.0 '~' syntax not recognized with node type or external object types");
+ }
+ } else if (pattern instanceof BooleanExpressionPattern) {
+ genericRuleChain = addRuleToList(newRule, genericRuleChain);
+ } else {
+ throw new UnsupportedOperationException("Unrecognized pattern");
+ }
+ }
+
+ }
+
+ /**
+ * Insert a new rule into this list before others of the same precedence/priority
+ * @param newRule the new rule to be added into the list
+ * @param list the Rule at the head of the list, or null if the list is empty
+ * @return the new head of the list (which might be the old head, or the new rule if it
+ * was inserted at the start)
+ */
+
+
+ private Rule addRuleToList(Rule newRule, Rule list) {
+ if (list == null) {
+ return newRule;
+ }
+ int precedence = newRule.getPrecedence();
+ double priority = newRule.getPriority();
+ Rule rule = list;
+ Rule prev = null;
+ while (rule != null) {
+ if ((rule.getPrecedence() < precedence) ||
+ (rule.getPrecedence() == precedence && rule.getPriority() <= priority)) {
+ newRule.setNext(rule);
+ if (prev == null) {
+ return newRule;
+ } else {
+ prev.setNext(newRule);
+ }
+ break;
+ } else {
+ prev = rule;
+ rule = rule.getNext();
+ }
+ }
+ if (rule == null) {
+ prev.setNext(newRule);
+ newRule.setNext(null);
+ }
+ return list;
+ }
+
+ /**
+ * Specify how many slots for local variables are required by a particular pattern
+ * @param slots the number of slots needed
+ */
+
+ public void allocatePatternSlots(int slots) {
+ stackFrameSlotsNeeded = Math.max(stackFrameSlotsNeeded, slots);
+ }
+
+ /**
+ * Make a new XPath context for evaluating patterns if there is any possibility that the
+ * pattern uses local variables
+ *
+ * @param context The existing XPath context
+ * @return a new XPath context
+ */
+
+ private XPathContext makeNewContext(XPathContext context) {
+ XPathContextMajor c2 = context.newContext();
+ c2.setOriginatingConstructType(Location.CONTROLLER);
+ c2.openStackFrame(stackFrameSlotsNeeded);
+ return c2;
+ }
+
+ /**
+ * Get the rule corresponding to a given Node, by finding the best Pattern match.
+ *
+ * @param item the item to be matched
+ * @param context the XPath dynamic evaluation context
+ * @return the best matching rule, if any (otherwise null).
+ * @throws XPathException if an error occurs matching a pattern
+ */
+
+ public Rule getRule(Item item, XPathContext context) throws XPathException {
+
+ // If there are match patterns in the stylesheet that use local variables, we need to allocate
+ // a new stack frame for evaluating the match patterns. We base this on the match pattern with
+ // the highest number of range variables, so we can reuse the same stack frame for all rules
+ // that we test against. If no patterns use range variables, we don't bother allocating a new
+ // stack frame.
+
+ // Note, this method isn't functionally necessary; we could call the 3-argument version
+ // with a filter that always returns true. But this is the common path for apply-templates,
+ // and we want to squeeze every drop of performance from it.
+
+ if (stackFrameSlotsNeeded > 0) {
+ context = makeNewContext(context);
+ }
+
+ // search the specific list for this node type / node name
+
+ Rule unnamedNodeChain;
+ Rule bestRule = null;
+
+ if (item instanceof NodeInfo) {
+ NodeInfo node = (NodeInfo)item;
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT:
+ unnamedNodeChain = documentRuleChain;
+ break;
+
+ case Type.ELEMENT: {
+ unnamedNodeChain = unnamedElementRuleChain;
+ Rule namedNodeChain = namedElementRuleChains.get(node.getFingerprint());
+ if (namedNodeChain != null) {
+ bestRule = searchRuleChain(node, context, null, namedNodeChain);
+ }
+ break;
+ }
+ case Type.ATTRIBUTE: {
+ unnamedNodeChain = unnamedAttributeRuleChain;
+ Rule namedNodeChain = namedAttributeRuleChains.get(node.getFingerprint());
+ if (namedNodeChain != null) {
+ bestRule = searchRuleChain(node, context, null, namedNodeChain);
+ }
+ break;
+ }
+ case Type.TEXT:
+ unnamedNodeChain = textRuleChain;
+ break;
+ case Type.COMMENT:
+ unnamedNodeChain = commentRuleChain;
+ break;
+ case Type.PROCESSING_INSTRUCTION:
+ unnamedNodeChain = processingInstructionRuleChain;
+ break;
+ case Type.NAMESPACE:
+ unnamedNodeChain = namespaceRuleChain;
+ break;
+ default:
+ throw new AssertionError("Unknown node kind");
+ }
+
+ // search the list for unnamed nodes of a particular kind
+
+ if (unnamedNodeChain != null) {
+ bestRule = searchRuleChain(node, context, bestRule, unnamedNodeChain);
+ }
+
+ // Search the list for rules for nodes of unknown node kind
+
+ if (genericRuleChain != null) {
+ bestRule = searchRuleChain(node, context, bestRule, genericRuleChain);
+ }
+
+ } else if (item instanceof AtomicValue) {
+ if (atomicValueRuleChain != null) {
+ bestRule = searchRuleChain(item, context, bestRule, atomicValueRuleChain);
+ }
+ if (genericRuleChain != null) {
+ bestRule = searchRuleChain(item, context, bestRule, genericRuleChain);
+ }
+
+ } else if (item instanceof FunctionItem) {
+ if (functionItemRuleChain != null) {
+ bestRule = searchRuleChain(item, context, bestRule, functionItemRuleChain);
+ }
+ if (genericRuleChain != null) {
+ bestRule = searchRuleChain(item, context, bestRule, genericRuleChain);
+ }
+ }
+
+ return bestRule;
+ }
+
+ /**
+ * Search a chain of rules
+ * @param item the item being matched
+ * @param context XPath dynamic context
+ * @param bestRule the best rule so far in terms of precedence and priority (may be null)
+ * @param head the rule at the head of the chain to be searched
+ * @return the best match rule found in the chain, or the previous best rule, or null
+ * @throws XPathException if an error occurs matching a pattern
+ */
+
+ private Rule searchRuleChain(Item item, XPathContext context, /*@Nullable*/ Rule bestRule, Rule head) throws XPathException {
+ while (head != null) {
+ if (bestRule != null) {
+ int rank = head.compareRank(bestRule);
+ if (rank < 0) {
+ // if we already have a match, and the precedence or priority of this
+ // rule is lower, quit the search
+ break;
+ } else if (rank == 0) {
+ // this rule has the same precedence and priority as the matching rule already found
+ if (head.isAlwaysMatches() || head.getPattern().matches(item, context)) {
+ reportAmbiguity(item, bestRule, head, context);
+ // choose whichever one comes last (assuming the error wasn't fatal)
+ bestRule = (bestRule.getSequence() > head.getSequence() ? bestRule : head);
+ break;
+ } else {
+ // keep searching other rules of the same precedence and priority
+ }
+ } else {
+ // this rule has higher rank than the matching rule already found
+ if (head.isAlwaysMatches() || head.getPattern().matches(item, context)) {
+ bestRule = head;
+ }
+ }
+ } else if (head.isAlwaysMatches() || head.getPattern().matches(item, context)) {
+ bestRule = head;
+ if (recoveryPolicy == Configuration.RECOVER_SILENTLY) {
+ break; // choose the first match; rules within a chain are in order of rank
+ }
+ }
+ head = head.getNext();
+ }
+ return bestRule;
+ }
+
+ /**
+ * Get the rule corresponding to a given item, by finding the best Pattern match.
+ *
+ * @param item the item to be matched
+ * @param context the XPath dynamic evaluation context
+ * @param filter a filter to select which rules should be considered
+ * @return the best matching rule, if any (otherwise null).
+ * @throws XPathException if an error occurs
+ */
+
+
+ /*@Nullable*/ public Rule getRule(Item item, XPathContext context, RuleFilter filter) throws XPathException {
+
+ // If there are match patterns in the stylesheet that use local variables, we need to allocate
+ // a new stack frame for evaluating the match patterns. We base this on the match pattern with
+ // the highest number of range variables, so we can reuse the same stack frame for all rules
+ // that we test against. If no patterns use range variables, we don't bother allocating a new
+ // stack frame.
+
+ if (stackFrameSlotsNeeded > 0) {
+ context = makeNewContext(context);
+ }
+
+ // search the specific list for this node type / node name
+
+ Rule bestRule = null;
+ Rule unnamedNodeChain;
+
+
+ // Search the list for unnamed nodes of a particular kind
+
+ if (item instanceof NodeInfo) {
+ NodeInfo node = (NodeInfo)item;
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT:
+ unnamedNodeChain = documentRuleChain;
+ break;
+ case Type.ELEMENT: {
+ unnamedNodeChain = unnamedElementRuleChain;
+ Rule namedNodeChain = namedElementRuleChains.get(node.getFingerprint());
+ bestRule = searchRuleChain(item, context, null, namedNodeChain, filter);
+ break;
+ }
+ case Type.ATTRIBUTE: {
+ unnamedNodeChain = unnamedAttributeRuleChain;
+ Rule namedNodeChain = namedAttributeRuleChains.get(node.getFingerprint());
+ bestRule = searchRuleChain(item, context, null, namedNodeChain, filter);
+ break;
+ }
+ case Type.TEXT:
+ unnamedNodeChain = textRuleChain;
+ break;
+ case Type.COMMENT:
+ unnamedNodeChain = commentRuleChain;
+ break;
+ case Type.PROCESSING_INSTRUCTION:
+ unnamedNodeChain = processingInstructionRuleChain;
+ break;
+ case Type.NAMESPACE:
+ unnamedNodeChain = namespaceRuleChain;
+ break;
+ default:
+ throw new AssertionError("Unknown node kind");
+ }
+
+ bestRule = searchRuleChain(item, context, bestRule, unnamedNodeChain, filter);
+
+ // Search the list for rules for nodes of unknown node kind
+
+ return searchRuleChain(item, context, bestRule, genericRuleChain, filter);
+
+ } else if (item instanceof AtomicValue) {
+ if (atomicValueRuleChain != null) {
+ bestRule = searchRuleChain(item, context, bestRule, atomicValueRuleChain, filter);
+ }
+ if (genericRuleChain != null) {
+ bestRule = searchRuleChain(item, context, bestRule, genericRuleChain, filter);
+ }
+ return bestRule;
+
+ } else if (item instanceof FunctionItem) {
+ if (functionItemRuleChain != null) {
+ bestRule = searchRuleChain(item, context, bestRule, functionItemRuleChain, filter);
+ }
+ if (genericRuleChain != null) {
+ bestRule = searchRuleChain(item, context, bestRule, genericRuleChain, filter);
+ }
+ return bestRule;
+
+ } else {
+ return null;
+ }
+
+ }
+
+ /**
+ * Search a chain of rules
+ * @param item the item being matched
+ * @param context XPath dynamic context
+ * @param bestRule the best rule so far in terms of precedence and priority (may be null)
+ * @param head the rule at the head of the chain to be searched
+ * @param filter filter used to select which rules are candidates to be searched
+ * @return the best match rule found in the chain, or the previous best rule, or null
+ * @throws XPathException if an error occurs while matching a pattern
+ */
+
+ private Rule searchRuleChain(Item item, XPathContext context,
+ Rule/*@Nullable*/ bestRule, Rule head, RuleFilter filter) throws XPathException {
+ while (head != null) {
+ if (filter.testRule(head)) {
+ if (bestRule != null) {
+ int rank = head.compareRank(bestRule);
+ if (rank < 0) {
+ // if we already have a match, and the precedence or priority of this
+ // rule is lower, quit the search
+ break;
+ } else if (rank == 0) {
+ // this rule has the same precedence and priority as the matching rule already found
+ if (head.isAlwaysMatches() || head.getPattern().matches(item, context)) {
+ reportAmbiguity(item, bestRule, head, context);
+ // choose whichever one comes last (assuming the error wasn't fatal)
+ bestRule = (bestRule.getSequence() > head.getSequence() ? bestRule : head);
+ break;
+ } else {
+ // keep searching other rules of the same precedence and priority
+ }
+ } else {
+ // this rule has higher rank than the matching rule already found
+ if (head.isAlwaysMatches() || head.getPattern().matches(item, context)) {
+ bestRule = head;
+ }
+ }
+ } else if (head.isAlwaysMatches() || head.getPattern().matches(item, context)) {
+ bestRule = head;
+ if (recoveryPolicy == Configuration.RECOVER_SILENTLY) {
+ break; // choose the first match; rules within a chain are in order of rank
+ }
+ }
+ }
+ head = head.getNext();
+ }
+ return bestRule;
+ }
+
+
+ /**
+ * Get the rule corresponding to a given Node, by finding the best Pattern match, subject to a minimum
+ * and maximum precedence. (This supports xsl:apply-imports)
+ *
+ * @param item the item to be matched
+ * @param min the minimum import precedence
+ * @param max the maximum import precedence
+ * @param context the XPath dynamic evaluation context
+ * @return the Rule registered for that node, if any (otherwise null).
+ * @throws XPathException if an error occurs evaluating match patterns
+ */
+
+ public Rule getRule(Item item, final int min, final int max, XPathContext context) throws XPathException {
+ RuleFilter filter = new RuleFilter() {
+ public boolean testRule(Rule r) {
+ int p = r.getPrecedence();
+ return p >= min && p <= max;
+ }
+ };
+ return getRule(item, context, filter);
+ }
+
+ /**
+ * Get the rule corresponding to a given Node, by finding the next-best Pattern match
+ * after the specified object.
+ *
+ * @param item the NodeInfo referring to the node to be matched
+ * @param currentRule the current rule; we are looking for the next match after the current rule
+ * @param context the XPath dynamic evaluation context
+ * @return the object (e.g. a NodeHandler) registered for that element, if any (otherwise null).
+ * @throws XPathException if an error occurs matching a pattern
+ */
+
+ public Rule getNextMatchRule(Item item, final Rule currentRule, XPathContext context) throws XPathException {
+ RuleFilter filter = new RuleFilter() {
+ public boolean testRule(Rule r) {
+ int comp = r.compareRank(currentRule);
+ return comp < 0 || (comp == 0 && r.getSequence() < currentRule.getSequence());
+ }
+ };
+ return getRule(item, context, filter);
+ }
+
+ /**
+ * Report an ambiguity, that is, the situation where two rules of the same
+ * precedence and priority match the same node
+ *
+ * @param item The item that matches two or more rules
+ * @param r1 The first rule that the node matches
+ * @param r2 The second rule that the node matches
+ * @param c The context for the transformation
+ * @throws XPathException if the system is configured to treat ambiguous template matching as a
+ * non-recoverable error
+ */
+
+ private void reportAmbiguity(Item item, Rule r1, Rule r2, XPathContext c)
+ throws XPathException {
+ // don't report an error if the conflict is between two branches of the same Union pattern
+ if (r1.getAction() == r2.getAction() && r1.getSequence() == r2.getSequence()) {
+ return;
+ }
+ String path;
+ String errorCode = "XTRE0540";
+
+ if (item instanceof NodeInfo) {
+ path = Navigator.getPath(((NodeInfo)item));
+ } else {
+ path = item.getStringValue();
+ }
+
+ Pattern pat1 = r1.getPattern();
+ Pattern pat2 = r2.getPattern();
+
+ String message;
+ if (r1.getAction() == r2.getAction()) {
+ message = "Ambiguous rule match for " + path + ". " +
+ "Matches \"" + showPattern(pat1) + "\" on line " + pat1.getLineNumber() + " of " + pat1.getSystemId() +
+ ", a rule which appears in the stylesheet more than once, because the containing module was included more than once";
+ } else {
+ message = "Ambiguous rule match for " + path + '\n' +
+ "Matches both \"" + showPattern(pat1) + "\" on line " + pat1.getLineNumber() + " of " + pat1.getSystemId() +
+ "\nand \"" + showPattern(pat2) + "\" on line " + pat2.getLineNumber() + " of " + pat2.getSystemId();
+ }
+
+ XPathException err = new XPathException(message, errorCode);
+ if (recoveryPolicy == Configuration.DO_NOT_RECOVER) {
+ throw err;
+ } else {
+ c.getController().recoverableError(err);
+ }
+ }
+
+ private static String showPattern(Pattern p) {
+ // Complex patterns can be laid out with lots of whitespace, which looks messy in the error message
+ return Whitespace.collapseWhitespace(p.toString()).toString();
+ }
+
+ /**
+ * Walk over all the rules, applying a specified action to each one.
+ * @param action an action that is to be applied to all the rules in this Mode
+ * @throws XPathException if an error occurs processing any of the rules
+ */
+
+ public void processRules(RuleAction action) throws XPathException {
+ processRuleChain(documentRuleChain, action);
+ processRuleChain(unnamedElementRuleChain, action);
+ IntIterator ii = namedElementRuleChains.keyIterator();
+ while (ii.hasNext()) {
+ Rule r = namedElementRuleChains.get(ii.next());
+ processRuleChain(r, action);
+ }
+ processRuleChain(unnamedAttributeRuleChain, action);
+ ii = namedAttributeRuleChains.keyIterator();
+ while (ii.hasNext()) {
+ Rule r = namedAttributeRuleChains.get(ii.next());
+ processRuleChain(r, action);
+ }
+ processRuleChain(textRuleChain, action);
+ processRuleChain(commentRuleChain, action);
+ processRuleChain(processingInstructionRuleChain, action);
+ processRuleChain(namespaceRuleChain, action);
+ processRuleChain(genericRuleChain, action);
+ processRuleChain(atomicValueRuleChain, action);
+ processRuleChain(functionItemRuleChain, action);
+ }
+
+ private void processRuleChain(Rule r, RuleAction action) throws XPathException {
+ while (r != null) {
+ action.processRule(r);
+ r = r.getNext();
+ }
+ }
+
+ /**
+ * For a streamable mode, invert all the templates to generate streamable code
+ * @param opt the optimizer (always a Saxon-EE optimizer)
+ * @throws XPathException if there is a non-streamable template in the mode
+ */
+
+ public void invertStreamableTemplates(final Optimizer opt) throws XPathException {
+ if (streamable) {
+ RuleAction action = new RuleAction() {
+ public void processRule(Rule r) throws XPathException {
+ ItemType test = r.getPattern().getItemType();
+ int kind = test.getPrimitiveType();
+ if (kind == Type.DOCUMENT || kind == Type.ELEMENT || kind == Type.NODE) {
+ Template t = (Template)r.getAction();
+ RuleTarget inverse = opt.makeInversion(r.getPattern(), t, (NodeTest)test);
+ r.setAction(inverse);
+ } else {
+ // invert the template to check that it's streamable; but then use the original
+ Template t = (Template)r.getAction();
+ opt.makeInversion(r.getPattern(), t, (NodeTest)test);
+ }
+ }
+ };
+ processRules(action);
+ }
+ }
+
+ /**
+ * Explain all template rules in this mode by showing their
+ * expression tree represented in XML.
+ * @param presenter used to display the expression tree
+ */
+
+ public void explainTemplateRules(final ExpressionPresenter presenter) {
+ RuleAction action = new RuleAction() {
+ public void processRule(Rule r) {
+ RuleTarget target = r.getAction();
+ Template template = null;
+ if (target instanceof Template) {
+ template = (Template)target;
+ }
+ //#ifdefined STREAM
+ else if (target instanceof TemplateInversion) {
+ template = ((TemplateInversion)target).getOriginalTemplate();
+ }
+ //#endif
+ int s = presenter.startElement("templateRule");
+ presenter.emitAttribute("match", r.getPattern().toString());
+ presenter.emitAttribute("precedence", r.getPrecedence()+"");
+ presenter.emitAttribute("priority", r.getPriority()+"");
+ //#ifdefined STREAM
+ if (template != null && isStreamable()) {
+ presenter.emitAttribute("W3C_streamable", Boolean.toString(template.isActuallyStreamable(false, null)));
+ }
+ //#endif
+ target.explain(presenter);
+ int e = presenter.endElement();
+ if (s != e) {
+ throw new IllegalStateException(
+ "tree unbalanced in template at line " +
+ (template != null ?
+ (template.getLineNumber() + " of " + template.getSystemId()) : ""));
+ }
+ }
+ };
+ try {
+ processRules(action);
+ } catch (XPathException err) {
+ // can't happen, and doesn't matter if it does
+ }
+ }
+
+ /**
+ * Compute a rank for each rule, as a combination of the precedence and priority, to allow
+ * rapid comparison.
+ * @throws XPathException if an error occurs processing the rules
+ */
+
+ public void computeRankings() throws XPathException {
+ final RuleSorter sorter = new RuleSorter();
+ RuleAction addToSorter = new RuleAction() {
+ public void processRule(Rule r) {
+ sorter.addRule(r);
+ }
+ };
+ // add all the rules in this Mode to the sorter
+ processRules(addToSorter);
+ // now allocate ranks to all the modes
+ sorter.allocateRanks();
+ }
+
+ /**
+ * Process selected nodes using the handlers registered for this mode.
+ *
+ * @exception net.sf.saxon.trans.XPathException if any dynamic error occurs
+ * @param parameters A ParameterSet containing the parameters to
+ * the handler/template being invoked. Specify null if there are no
+ * parameters.
+ * @param tunnelParameters A ParameterSet containing the parameters to
+ * the handler/template being invoked. Specify null if there are no
+ * parameters.
+ * @param context A newly-created context object (this must be freshly created by the caller,
+ * as it will be modified by this method). The nodes to be processed are those
+ * selected by the currentIterator in this context object. There is also a precondition
+ * that this mode must be the current mode in this context object.
+ * @param locationId location of this apply-templates instruction in the stylesheet
+ * @return a TailCall returned by the last template to be invoked, or null,
+ * indicating that there are no outstanding tail calls.
+ */
+
+ /*@Nullable*/ public TailCall applyTemplates(
+ ParameterSet parameters,
+ ParameterSet tunnelParameters,
+ XPathContextMajor context,
+ int locationId)
+ throws XPathException {
+ Controller controller = context.getController();
+ PipelineConfiguration pipe = context.getReceiver().getPipelineConfiguration();
+ SequenceIterator iterator = context.getCurrentIterator();
+ TailCall tc = null;
+
+ // Iterate over this sequence
+
+ if (controller.isTracing()) {
+
+ while(true) {
+
+ Item item = iterator.next();
+ if (item == null) {
+ break;
+ }
+ // process any tail calls returned from previous nodes
+ while (tc != null) {
+ tc = tc.processLeavingTail();
+ }
+
+ pipe.pushCurrentAppliedItem(item);
+
+ // find the template rule for this node
+ Rule rule = getRule(item, context);
+
+ if (rule==null) { // Use the default action for the node
+ // No need to open a new stack frame!
+ getBuiltInRuleSet().process(item, parameters, tunnelParameters, context, locationId);
+
+ } else {
+ Template template = (Template)rule.getAction();
+ TraceListener traceListener = controller.getTraceListener();
+ assert traceListener != null;
+ context.setLocalParameters(parameters);
+ context.setTunnelParameters(tunnelParameters);
+ context.openStackFrame(template.getStackFrameMap());
+ context.setOriginatingConstructType(Location.TEMPLATE);
+ context.setCurrentTemplateRule(rule);
+ traceListener.startCurrentItem(item);
+ tc = template.applyLeavingTail(context);
+ traceListener.endCurrentItem(item);
+ }
+
+ pipe.popCurrentAppliedItem();
+ }
+
+ } else { // not tracing
+
+ boolean lookahead = (iterator.getProperties() & SequenceIterator.LOOKAHEAD) != 0;
+ Template previousTemplate = null;
+ while(true) {
+
+ // process any tail calls returned from previous nodes. We need to do this before changing
+ // the context. If we have a LookaheadIterator, we can tell whether we're positioned at the
+ // end without changing the current position, and we can then return the last tail call to
+ // the caller and execute it further down the stack, reducing the risk of running out of stack
+ // space. In other cases, we need to execute the outstanding tail calls before moving the iterator
+
+ if (tc != null) {
+ if (lookahead && !((LookaheadIterator)iterator).hasNext()) {
+ break;
+ }
+ do {
+ tc = tc.processLeavingTail();
+ } while (tc != null);
+ }
+
+ Item item = iterator.next();
+ if (item == null) {
+ break;
+ }
+
+ pipe.pushCurrentAppliedItem(item);
+
+ // find the template rule for this node
+
+ Rule rule = getRule(item, context);
+
+ if (rule==null) { // Use the default action for the node
+ // No need to open a new stack frame!
+ getBuiltInRuleSet().process(item, parameters, tunnelParameters, context, locationId);
+
+ } else {
+ Template template = (Template)rule.getAction();
+ if (template != previousTemplate) {
+ // Reuse the previous stackframe unless it's a different template rule
+ previousTemplate = template;
+ context.openStackFrame(template.getStackFrameMap());
+ context.setLocalParameters(parameters);
+ context.setTunnelParameters(tunnelParameters);
+ }
+ context.setCurrentTemplateRule(rule);
+ tc = template.applyLeavingTail(context);
+ }
+
+ pipe.popCurrentAppliedItem();
+ }
+ }
+ // return the TailCall returned from the last node processed
+ return tc;
+ }
+
+ /**
+ * Supporting class used at compile time to sort all the rules into precedence/priority
+ * order and allocate a rank to each one, so that at run-time, comparing two rules to see
+ * which has higher precedence/priority is a simple integer subtraction.
+ */
+
+ private static class RuleSorter implements Sortable {
+ public ArrayList<Rule> rules = new ArrayList<Rule>(100);
+ public void addRule(Rule rule) {
+ rules.add(rule);
+ }
+
+ public int compare(int a, int b) {
+ return rules.get(a).compareComputedRank(rules.get(b));
+ }
+
+ public void swap(int a, int b) {
+ Rule temp = rules.get(a);
+ rules.set(a, rules.get(b));
+ rules.set(b, temp);
+ }
+
+ public void allocateRanks() {
+ GenericSorter.quickSort(0, rules.size(), this);
+ int rank = 0;
+ for (int i=0; i<rules.size(); i++) {
+ if ( i> 0 && rules.get(i-1).compareComputedRank(rules.get(i)) != 0) {
+ rank++;
+ }
+ rules.get(i).setRank(rank);
+ }
+ }
+ }
+
+ /**
+ * Interface for helper classes used to filter a chain of rules
+ */
+
+ private static interface RuleFilter {
+ /**
+ * Test a rule to see whether it should be included
+ * @param r the rule to be tested
+ * @return true if the rule qualifies
+ */
+ public boolean testRule(Rule r);
+ }
+
+ /**
+ * Interface for helper classes used to process all the rules in the Mode
+ */
+
+ private static interface RuleAction {
+ /**
+ * Process a given rule
+ * @param r the rule to be processed
+ * @throws XPathException if an error occurs, for example a dynamic error in evaluating a pattern
+ */
+ public void processRule(Rule r) throws XPathException;
+ }
+
+
+}
+
diff --git a/sf/saxon/trans/NoDynamicContextException.java b/sf/saxon/trans/NoDynamicContextException.java
new file mode 100644
index 0000000..b3970bc
--- /dev/null
+++ b/sf/saxon/trans/NoDynamicContextException.java
@@ -0,0 +1,26 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+/**
+ * This exception class is used when early (compile-time) evaluation of an expression
+ * is attempted, and the expression requires knowledge of the current dateTime or implicit
+ * timezone. This exception should be caught internally, and should result in evaluation
+ * of the expression being deferred until run-time
+ */
+public class NoDynamicContextException extends XPathException {
+
+ /**
+ * Create a NoDynamicContextException
+ * @param message the error message
+ */
+
+ public NoDynamicContextException(String message) {
+ super("Dynamic context missing: " + message);
+ }
+}
diff --git a/sf/saxon/trans/NonDelegatingURIResolver.java b/sf/saxon/trans/NonDelegatingURIResolver.java
new file mode 100644
index 0000000..0bd2b21
--- /dev/null
+++ b/sf/saxon/trans/NonDelegatingURIResolver.java
@@ -0,0 +1,24 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import javax.xml.transform.URIResolver;
+
+/**
+ * This is a marker interface: if a URIResolver implements this interface and returns null from
+ * its resolve() method, then the standard URI resolver will not be invoked.
+ * <p>
+ * The main use case for this is to support protocols that the standard Java java.net.URL class
+ * does not recognize. In the case of doc-available(), we want to return false, rather than throwing
+ * an exception in such cases.
+ */
+
+public interface NonDelegatingURIResolver extends URIResolver {
+ // marker interface only
+}
+
diff --git a/sf/saxon/trans/Rule.java b/sf/saxon/trans/Rule.java
new file mode 100644
index 0000000..e22725c
--- /dev/null
+++ b/sf/saxon/trans/Rule.java
@@ -0,0 +1,167 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.pattern.Pattern;
+
+import java.io.Serializable;
+
+/**
+ * Rule: a template rule, or a strip-space rule used to support the implementation
+ */
+
+public final class Rule implements Serializable {
+ /*@NotNull*/
+ private Pattern pattern; // The pattern that fires this rule
+ /*@NotNull*/
+ private RuleTarget action; // The action associated with this rule (usually a Template)
+ private int precedence; // The import precedence
+ private int minImportPrecedence;// The minimum import precedence to be considered by xsl:apply-imports
+ private double priority; // The priority of the rule
+
+ /*@Nullable*/ private Rule next; // The next rule after this one in the chain of rules
+ private int sequence; // The relative position of this rule, its position in declaration order
+ private boolean alwaysMatches; // True if the pattern does not need to be tested, because the rule
+ // is on a rule-chain such that the pattern is necessarily satisfied
+ private int rank; // Indicates the relative precedence/priority of a rule within a mode;
+ // used for quick comparison
+ /**
+ * Create a Rule.
+ *
+ * @param p the pattern that this rule matches
+ * @param o the object invoked by this rule (usually a Template)
+ * @param prec the precedence of the rule
+ * @param min the minumum import precedence for xsl:apply-imports
+ * @param prio the priority of the rule
+ * @param seq a sequence number for ordering of rules
+ */
+
+ public Rule(/*@NotNull*/ Pattern p, /*@NotNull*/ RuleTarget o, int prec, int min, double prio, int seq) {
+ pattern = p;
+ action = o;
+ precedence = prec;
+ minImportPrecedence = min;
+ priority = prio;
+ next = null;
+ sequence = seq;
+ }
+
+ /**
+ * Copy a rule, including the chain of rules linked to it
+ * @param r the rule to be copied
+ */
+
+ public Rule(Rule r) {
+ pattern = r.pattern;
+ action = r.action;
+ precedence = r.precedence;
+ minImportPrecedence = r.minImportPrecedence;
+ priority = r.priority;
+ sequence = r.sequence;
+ if (r.next == null) {
+ next = null;
+ } else {
+ next = new Rule(r.next);
+ }
+ }
+
+ public int getSequence() {
+ return sequence;
+ }
+
+ public void setAction(/*@NotNull*/ RuleTarget action) {
+ this.action = action;
+ }
+
+ /*@NotNull*/
+ public RuleTarget getAction() {
+ return action;
+ }
+
+ /*@Nullable*/
+ public Rule getNext() {
+ return next;
+ }
+
+ public void setNext( /*@Nullable*/Rule next) {
+ this.next = next;
+ }
+
+ /*@NotNull*/
+ public Pattern getPattern() {
+ return pattern;
+ }
+
+ public int getPrecedence() {
+ return precedence;
+ }
+
+ public int getMinImportPrecedence() {
+ return minImportPrecedence;
+ }
+
+ public double getPriority() {
+ return priority;
+ }
+
+ public void setAlwaysMatches(boolean matches) {
+ alwaysMatches = matches;
+ }
+
+ public boolean isAlwaysMatches() {
+ return alwaysMatches;
+ }
+
+ public void setRank(int rank) {
+ this.rank = rank;
+ }
+
+ public int getRank() {
+ return rank;
+ }
+
+
+
+ /**
+ * Rules have an ordering, based on their precedence and priority. This method compares
+ * them using the precomputed rank value.
+ * @param other Another rule whose ordering rank is to be compared with this one
+ * @return <0 if this rule has lower rank, that is if it has lower precedence or equal
+ * precedence and lower priority. 0 if the two rules have equal precedence and
+ * priority. >0 if this rule has higher rank in precedence/priority order
+ */
+
+ public int compareRank(Rule other) {
+ return rank - other.rank;
+ }
+
+ /**
+ * Rules have an ordering, based on their precedence and priority.
+ * @param other Another rule whose ordering rank is to be compared with this one
+ * @return <0 if this rule has lower rank, that is if it has lower precedence or equal
+ * precedence and lower priority. 0 if the two rules have equal precedence and
+ * priority. >0 if this rule has higher rank in precedence/priority order
+ */
+
+ public int compareComputedRank(Rule other) {
+ if (precedence == other.precedence) {
+ if (priority == other.priority) {
+ return 0;
+ } else if (priority < other.priority) {
+ return -1;
+ } else {
+ return +1;
+ }
+ } else if (precedence < other.precedence) {
+ return -1;
+ } else {
+ return +1;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/trans/RuleManager.java b/sf/saxon/trans/RuleManager.java
new file mode 100644
index 0000000..2a13319
--- /dev/null
+++ b/sf/saxon/trans/RuleManager.java
@@ -0,0 +1,284 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.Template;
+import net.sf.saxon.expr.parser.Optimizer;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.style.StylesheetModule;
+import net.sf.saxon.trace.ExpressionPresenter;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+
+/**
+ * <B>RuleManager</B> maintains a set of template rules, one set for each mode
+ * @version 10 December 1999: carved out of the old Controller class
+ * @author Michael H. Kay
+ */
+
+public final class RuleManager implements Serializable {
+
+ private Mode unnamedMode; // node handlers with default mode
+ private HashMap<StructuredQName, Mode> modes;
+ // tables of node handlers for non-default modes
+ private Mode omniMode = null; // node handlers that specify mode="all"
+ private int recoveryPolicy;
+
+ /**
+ * create a RuleManager and initialise variables.
+ */
+
+ public RuleManager() {
+ resetHandlers();
+ }
+
+ /**
+ * Set the policy for handling recoverable errrors. Note that for some errors the decision can be
+ * made at run-time, but for the "ambiguous template match" error, the decision is (since 9.2)
+ * fixed at compile time.
+ * @param policy the recovery policy to be used. The options are {@link net.sf.saxon.Configuration#RECOVER_SILENTLY},
+ * {@link net.sf.saxon.Configuration#RECOVER_WITH_WARNINGS}, or {@link net.sf.saxon.Configuration#DO_NOT_RECOVER}.
+ * @since 9.2
+ */
+
+ public void setRecoveryPolicy(int policy) {
+ recoveryPolicy = policy;
+ unnamedMode.setRecoveryPolicy(policy);
+ }
+
+ /**
+ * Get the policy for handling recoverable errors. Note that for some errors the decision can be
+ * made at run-time, but for the "ambiguous template match" error, the decision is (since 9.2)
+ * fixed at compile time.
+ *
+ * @return the current policy.
+ * @since 9.2
+ */
+
+ public int getRecoveryPolicy() {
+ return recoveryPolicy;
+ }
+
+ /**
+ * Get all registered modes
+ * @return a collection containing all registered modes excluding the unnamed mode
+ */
+
+ public Collection<Mode> getAllNamedModes() {
+ return modes.values();
+ }
+
+ /**
+ * Set up a new table of handlers.
+ */
+
+ public void resetHandlers() {
+ unnamedMode = new Mode(Mode.UNNAMED_MODE, Mode.UNNAMED_MODE_NAME);
+ unnamedMode.setRecoveryPolicy(recoveryPolicy);
+ modes = new HashMap<StructuredQName, Mode>(5);
+ }
+
+ /**
+ * Get the mode object for the unnamed mode
+ * @return the unnamed mode
+ */
+
+ /*@NotNull*/
+ public Mode getUnnamedMode() {
+ return unnamedMode;
+ }
+
+ /**
+ * Get the Mode object for a named mode. If there is not one already registered.
+ * a new Mode is created.
+ * @param modeName The name of the mode. Supply null to get the default
+ * mode or Mode.ALL_MODES to get the Mode object containing "mode=all" rules
+ * @param createIfAbsent if true, then if the mode does not already exist it will be created.
+ * If false, then if the mode does not already exist the method returns null.
+ * @return the Mode with this name
+ */
+
+
+ /*@Nullable*/ public Mode getMode(StructuredQName modeName, boolean createIfAbsent) {
+ if (modeName == null || modeName.equals(Mode.UNNAMED_MODE_NAME)) {
+ return unnamedMode;
+ }
+ if (modeName.equals(Mode.ALL_MODES)) {
+ if (omniMode==null) {
+ omniMode = new Mode(Mode.NAMED_MODE, modeName);
+ omniMode.setRecoveryPolicy(recoveryPolicy);
+ }
+ return omniMode;
+ }
+ //Integer modekey = new Integer(modeNameCode & 0xfffff);
+ Mode m = modes.get(modeName);
+ if (m == null && createIfAbsent) {
+ m = new Mode(omniMode, modeName);
+ m.setRecoveryPolicy(recoveryPolicy);
+ modes.put(modeName, m);
+ // when creating a specific mode, copy all the rules currently held
+ // in the omniMode, as these apply to all modes
+ }
+ return m;
+ }
+
+ /**
+ * Register a template for a particular pattern.
+ * @param pattern Must be a valid Pattern.
+ * @param eh The Template to be used
+ * @param mode The processing mode to which this template applies
+ * @param module The stylesheet module containing the template rule
+ * @param priority The priority of the rule: if an element matches several patterns, the
+ * one with highest priority is used
+ * @see Pattern
+ */
+
+ public void setTemplateRule(Pattern pattern, Template eh,
+ Mode mode, StylesheetModule module, double priority) {
+
+ // for a union pattern, register the parts separately
+ // Note: technically this is only necessary if using default priorities and if the priorities
+ // of the two halves are different. However, splitting increases the chance that the pattern
+ // can be matched by hashing on the element name, so we do it always
+ if (pattern instanceof UnionPattern) {
+ UnionPattern up = (UnionPattern)pattern;
+ Pattern p1 = up.getLHS();
+ Pattern p2 = up.getRHS();
+// Expression currentSetter = up.getVariableBindingExpression();
+// if (currentSetter != null) {
+// p1.setVariableBindingExpression(currentSetter);
+// p2.setVariableBindingExpression(currentSetter);
+// }
+
+ setTemplateRule(p1, eh, mode, module, priority);
+ setTemplateRule(p2, eh, mode, module, priority);
+ return;
+ }
+ // some union patterns end up as a CombinedNodeTest. Need to split these.
+ // (Same reasoning as above)
+ if (pattern instanceof ItemTypePattern &&
+ pattern.getItemType() instanceof CombinedNodeTest &&
+ ((CombinedNodeTest)pattern.getItemType()).getOperator() == Token.UNION) {
+ CombinedNodeTest cnt = (CombinedNodeTest)pattern.getItemType();
+ NodeTest[] nt = cnt.getComponentNodeTests();
+ setTemplateRule(new ItemTypePattern(nt[0]), eh, mode, module, priority);
+ setTemplateRule(new ItemTypePattern(nt[1]), eh, mode, module, priority);
+ return;
+ }
+ if (Double.isNaN(priority)) {
+ priority = pattern.getDefaultPriority();
+ }
+
+ mode.addRule(pattern, eh, module, priority, true);
+
+ // if adding a rule to the omniMode (mode='all') add it to all
+ // the other modes as well
+
+ if (mode==omniMode) {
+ unnamedMode.addRule(pattern, eh, module, priority, false);
+ for (Mode m : modes.values()) {
+ m.addRule(pattern, eh, module, priority, false);
+ }
+ }
+ }
+
+ /**
+ * Get the template rule matching a given item whose import precedence
+ * is in a particular range. This is used to support the xsl:apply-imports function
+ * @param item The item to be matched
+ * @param mode The mode for which a rule is required
+ * @param min The minimum import precedence that the rule must have
+ * @param max The maximum import precedence that the rule must have
+ * @param c The Controller for the transformation
+ * @return The template rule to be invoked
+ * @throws XPathException if an error occurs matching a pattern
+ */
+
+ public Rule getTemplateRule (Item item, Mode mode, int min, int max, XPathContext c)
+ throws XPathException {
+ if (mode==null) {
+ mode = unnamedMode;
+ }
+ return mode.getRule(item, min, max, c);
+ }
+
+ /**
+ * Get the next-match handler after the current one
+ * @param node The node to be matched
+ * @param mode The processing mode
+ * @param currentRule The current template rule
+ * @param c The dynamic context for the transformation
+ * @return The template rule to be executed
+ * @throws XPathException if an error occurs while matching a pattern
+ */
+
+ public Rule getNextMatchHandler(Item node, Mode/*@Nullable*/ mode, Rule currentRule, XPathContext c)
+ throws XPathException {
+ if (mode==null) {
+ mode = unnamedMode;
+ }
+ return mode.getNextMatchRule(node, currentRule, c);
+ }
+
+ /**
+ * Allocate rankings to the rules within each mode. This method must be called when all
+ * the rules in each mode are known
+ * @throws XPathException if an error occurs
+ */
+
+ public void computeRankings() throws XPathException {
+ unnamedMode.computeRankings();
+ for (Mode mode : modes.values()) {
+ mode.computeRankings();
+ }
+ }
+
+ /**
+ * Invert streamable templates in all streamable modes
+ * @param opt the optimizer (Always a Saxon-EE optimizer)
+ * @throws XPathException if the templates are not streamable
+ */
+
+ public void invertStreamableTemplates(Optimizer opt) throws XPathException {
+ unnamedMode.invertStreamableTemplates(opt);
+ for (Mode mode : modes.values()) {
+ mode.invertStreamableTemplates(opt);
+ }
+ }
+
+ /**
+ * Explain (that is, output the expression tree) all template rules
+ * @param presenter the object used to present the output
+ */
+
+ public void explainTemplateRules(ExpressionPresenter presenter) {
+ presenter.startElement("templateRules");
+ unnamedMode.explainTemplateRules(presenter);
+ for (Mode mode : modes.values()) {
+ int s = presenter.startElement("mode");
+ if (!mode.isDefaultMode()) {
+ presenter.emitAttribute("name", mode.getModeName().getDisplayName());
+ }
+ presenter.emitAttribute("streamable", Boolean.toString(mode.isStreamable()));
+ mode.explainTemplateRules(presenter);
+ int e = presenter.endElement();
+ if (s != e) {
+ throw new IllegalStateException("tree unbalanced");
+ }
+ }
+
+ presenter.endElement();
+ }
+}
+
diff --git a/sf/saxon/trans/RuleTarget.java b/sf/saxon/trans/RuleTarget.java
new file mode 100644
index 0000000..112e174
--- /dev/null
+++ b/sf/saxon/trans/RuleTarget.java
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.trace.ExpressionPresenter;
+
+import java.io.Serializable;
+
+/**
+ * The target of a rule, typically a Template.
+ */
+public interface RuleTarget extends Serializable {
+
+ /**
+ * Output diagnostic explanation to an ExpressionPresenter
+ */
+
+ public void explain(ExpressionPresenter presenter);
+}
+
diff --git a/sf/saxon/trans/SaxonErrorCode.java b/sf/saxon/trans/SaxonErrorCode.java
new file mode 100644
index 0000000..7838839
--- /dev/null
+++ b/sf/saxon/trans/SaxonErrorCode.java
@@ -0,0 +1,355 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+/**
+ * The class acts as a register of Saxon-specific error codes.
+ * <p>
+ * Technically, these codes should be in their own namespace. At present, however, they share the
+ * same namespace as system-defined error codes.
+ */
+public class SaxonErrorCode {
+
+ /**
+ * SXLM0001: stylesheet or query appears to be looping/recursing indefinitely
+ */
+
+ public static final String SXLM0001 = "SXLM0001";
+
+ /**
+ * SXCH0002: cannot supply output to ContentHandler because it is not well-formed
+ */
+
+ public static final String SXCH0002 = "SXCH0002";
+
+ /**
+ * SXCH0003: error reported by the ContentHandler (SAXResult) to which the result tree was sent
+ */
+
+ public static final String SXCH0003 = "SXCH0003";
+
+ /**
+ * SXCH0004: cannot load user-supplied ContentHandler
+ */
+
+ public static final String SXCH0004 = "SXCH0004";
+
+ /**
+ * SXSE0001: cannot use character maps in an environment with no Controller
+ */
+
+ public static final String SXSE0001 = "SXSE0001";
+
+ /**
+ * SXSE0002: cannot use output property saxon:supply-source-locator unless tracing was enabled at compile time
+ */
+
+ public static final String SXSE0002 = "SXSE0002";
+
+ /**
+ * SXXP0003: error reported by XML parser while parsing source document
+ */
+
+ public static final String SXXP0003 = "SXXP0003";
+
+ /**
+ * SXXP0004: externally supplied node belongs to the wrong Configuration
+ */
+
+ public static final String SXXP0004 = "SXXP0004";
+
+ /**
+ * SXXP0005: namespace of source document doesn't match namespace of the template rules in the stylesheet
+ */
+
+ public static final String SXXP0005 = "SXXP0005";
+
+ /**
+ * SXXP0006: resource limits exceeded
+ */
+
+ public static final String SXXP0006 = "SXXP0006";
+
+ /**
+ * SXXF0001: first argument to saxon:eval must be an expression prepared using saxon:expression
+ */
+
+ public static final String SXXF0001 = "SXXF0001";
+
+ /**
+ * SXXF0002: undeclared namespace prefix used in saxon:script
+ */
+
+ public static final String SXXF0002 = "SXXF0002";
+
+ /**
+ * SXSQ0001: value of argument to SQL instruction is not a JDBC Connection object
+ */
+
+ public static final String SXSQ0001 = "SXSQ0001";
+
+ /**
+ * SXSQ0002: failed to close JDBC Connection
+ */
+
+ public static final String SXSQ0002 = "SXSQ0002";
+
+ /**
+ * SXSQ0003: failed to open JDBC Connection
+ */
+
+ public static final String SXSQ0003 = "SXSQ0003";
+
+ /**
+ * SXSQ0004: SQL Insert/Update/Delete action failed
+ */
+
+ public static final String SXSQ0004 = "SXSQ0004";
+
+ /**
+ * SXJE0001: cannot convert xs:boolean to the required Java type
+ */
+
+ public static final String SXJE0001 = "SXJE0001";
+
+ /**
+ * SXJE0002: cannot convert xs:double to the required Java type
+ */
+
+ public static final String SXJE0002 = "SXJE0002";
+
+ /**
+ * SXJE0003: cannot convert xs:duration to the required Java type
+ */
+
+ public static final String SXJE0003 = "SXJE0003";
+
+ /**
+ * SXJE0004: cannot convert xs:float to the required Java type
+ */
+
+ public static final String SXJE0004 = "SXJE0004";
+
+ /**
+ * SXJE0005: cannot convert xs:string to Java char unless the length is exactly one
+ */
+
+ public static final String SXJE0005 = "SXJE0005";
+
+ /**
+ * SXJE0006: cannot convert xs:string to the required Java type
+ */
+
+ public static final String SXJE0006 = "SXJE0006";
+
+ /**
+ * SXJE0007: cannot convert xs:dayTimeDuration to the required Java type
+ */
+
+ public static final String SXJE0007 = "SXJE0007";
+
+ /**
+ * SXJE0008: cannot convert xs:yearMonthDuration to the required Java type
+ */
+
+ public static final String SXJE0008 = "SXJE0008";
+
+ /**
+ * SXJE0009: cannot atomize an external Object
+ */
+
+ public static final String SXJE0009 = "SXJE0009";
+
+ /**
+ * SXJE0021: cannot convert XPath value to the type required by the signature of an extension function
+ */
+
+ public static final String SXJE0021 = "SXJE0021";
+
+ /**
+ * SXJE0022: cannot convert XPath value to the type required by the signature of an extension function,
+ * the XPath value is a sequence of more than one item but the Java type is a singleton
+ */
+
+ public static final String SXJE0022 = "SXJE0022";
+
+ /**
+ * SXJE0023: cannot convert XPath item to the member type of a Java array
+ */
+
+ public static final String SXJE0023 = "SXJE0023";
+
+ /**
+ * SXJE0051: supplied Java List/Array contains a member that cannot be converted to an Item
+ */
+
+ public static final String SXJE0051 = "SXJE0051";
+
+ /**
+ * SXJE0052: exception thrown by extension function
+ */
+
+ public static final String SXJE0052 = "SXJE0052";
+
+ /**
+ * SXST0060: Template in a streaming mode is not streamable
+ */
+
+ public static final String SXST0060 = "SXST0060";
+
+ /**
+ * SXST0061: Requested initial mode is streamable; must supply SAXSource or StreamSource
+ */
+
+ public static final String SXST0061 = "SXST0061";
+
+ /**
+ * SXST0062: Dynamic error: disallowed navigation from a streaming mode. Used when the streaming
+ * analysis was optimistic, and the optimism proved unfounded.
+ */
+
+ public static final String SXST0062 = "SXST0062";
+
+ /**
+ * SXST0063: A streamable template returns nodes from the streamed input document
+ */
+
+ public static final String SXST0063 = "SXST0063";
+
+ /**
+ * SXST0064: A pattern in a streamable template uses predicates that reposition the input stream
+ */
+
+ public static final String SXST0064 = "SXST0064";
+
+ /**
+ * SXST0065: Cannot use tracing with streaming templates
+ */
+
+ public static final String SXST0065 = "SXST0065";
+
+ /**
+ * SXST0066: Within a conditional expression in a streamed template, the condition cannot read the streamed input
+ */
+
+ public static final String SXST0066 = "SXST0066";
+
+ /**
+ * SXST0067: Within a streamed template, cannot call last()
+ */
+
+ public static final String SXST0067 = "SXST0067";
+
+ /**
+ * SXST0068: Parameters of a streamed template, cannot consume the input stream
+ */
+
+ public static final String SXST0068 = "SXST0068";
+
+ /**
+ * SXST0069: A parameter to a streamed template must not reference a node in the streamed document
+ */
+
+ public static final String SXST0069 = "SXST0069";
+
+ /**
+ * SXST0070: Body of for-each must not select from streamed document unless the select expression does so
+ */
+
+ public static final String SXST0070 = "SXST0070";
+
+ /**
+ * SXST0071: Cannot navigate upwards and then downwards when streaming
+ */
+
+ public static final String SXST0071 = "SXST0071";
+
+
+
+ /**
+ * SXUP0081: attempt to update a non-updateable node
+ */
+
+ public static final String SXUP0081 = "SXUP0081";
+
+
+ /**
+ * SXWN9001: a variable declaration with no following siblings has no effect
+ */
+
+ public static final String SXWN9001 = "SXWN9001";
+
+ /**
+ * SXWN9002: saxon:indent-spaces must be a positive integer
+ */
+
+ public static final String SXWN9002 = "SXWN9002";
+
+ /**
+ * SXWN9003: saxon:require-well-formed must be "yes" or "no"
+ */
+
+ public static final String SXWN9003 = "SXWN9003";
+
+ /**
+ * SXWN9004: saxon:next-in-chain cannot be specified dynamically
+ */
+
+ public static final String SXWN9004 = "SXWN9004";
+
+ /**
+ * SXWN9005: The 'default' attribute of saxon:collation no longer has any effect
+ */
+
+ public static final String SXWN9005 = "SXWN9005";
+
+ /**
+ * SXWN9006: No schema-location was specified, and no schema with the requested target namespace
+ * is known, so the schema import was ignored
+ */
+
+ public static final String SXWN9006 = "SXWN9006";
+
+ /**
+ * SXWN9008: Saxon extension element not recognized because namespace not declared
+ * in extension-element-prefixes
+ */
+
+ public static final String SXWN9008 = "SXWN9008";
+
+ /**
+ * SXWN9009: an empty xsl:for-each or xsl:for-each-group has no effect
+ */
+
+ public static final String SXWN9009 = "SXWN9009";
+
+ /**
+ * SXWN9010: saxon:recognize-binary must be "yes" or "no"
+ */
+
+ public static final String SXWN9010 = "SXWN9010";
+
+ /**
+ * SXWN9011: saxon:memo-function ignored under Saxon-HE
+ */
+
+ public static final String SXWN9011 = "SXWN9011";
+
+ /**
+ * SXWN9012: saxon:threads ignored when compiling with trace enabled
+ */
+
+ public static final String SXWN9012 = "SXWN9012";
+
+ /**
+ * SXWN9013: saxon:threads ignored when not running under Saxon-EE
+ */
+
+ /*@NotNull*/ public static final String SXWN9013 = "SXWN9013";
+}
+
diff --git a/sf/saxon/trans/ShallowSkipRuleSet.java b/sf/saxon/trans/ShallowSkipRuleSet.java
new file mode 100644
index 0000000..04c4e5e
--- /dev/null
+++ b/sf/saxon/trans/ShallowSkipRuleSet.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.expr.instruct.ParameterSet;
+import net.sf.saxon.expr.instruct.TailCall;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.type.Type;
+
+/**
+ * A built-in set of template rules that ignores the current node and does an apply-templates
+ * to its children.
+ */
+public class ShallowSkipRuleSet implements BuiltInRuleSet {
+
+ private static ShallowSkipRuleSet THE_INSTANCE = new ShallowSkipRuleSet();
+
+ /**
+ * Get the singleton instance of this class
+ * @return the singleton instance
+ */
+
+ public static ShallowSkipRuleSet getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private ShallowSkipRuleSet() {}
+
+ /**
+ * Perform the built-in template action for a given item.
+ * @param item the item to be processed
+ * @param parameters the parameters supplied to apply-templates
+ * @param tunnelParams the tunnel parameters to be passed through
+ * @param context the dynamic evaluation context
+ * @param locationId location of the instruction (apply-templates, apply-imports etc) that caused
+ * the built-in template to be invoked
+ * @throws XPathException
+ * if any dynamic error occurs
+ */
+
+ public void process(Item item, ParameterSet parameters,
+ ParameterSet tunnelParams, /*@NotNull*/ XPathContext context,
+ int locationId) throws XPathException {
+ if (item instanceof NodeInfo) {
+ NodeInfo node = (NodeInfo)item;
+ switch(node.getNodeKind()) {
+ case Type.DOCUMENT:
+ case Type.ELEMENT:
+ SequenceIterator iter = node.iterateAxis(AxisInfo.CHILD);
+ XPathContextMajor c2 = context.newContext();
+ c2.setOriginatingConstructType(Location.BUILT_IN_TEMPLATE);
+ c2.setCurrentIterator(iter);
+ TailCall tc = c2.getCurrentMode().applyTemplates(parameters, tunnelParams, c2, locationId);
+ while (tc != null) {
+ tc = tc.processLeavingTail();
+ }
+ return;
+ case Type.TEXT:
+ case Type.ATTRIBUTE:
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ case Type.NAMESPACE:
+ // no action
+ }
+ } else {
+ // no action (e.g. for atomic values and function items
+ }
+ }
+
+ /**
+ * Get the default action for unmatched nodes
+ *
+ * @param nodeKind the node kind
+ * @return the default action for unmatched nodes: one of DEEP_COPY, APPLY_TEMPLATES, DEEP_SKIP, FAIL
+ */
+ public int getDefaultAction(int nodeKind) {
+ switch (nodeKind) {
+ case Type.DOCUMENT:
+ case Type.ELEMENT:
+ return SHALLOW_SKIP;
+ default:
+ return DEEP_SKIP;
+ }
+ }
+}
+
diff --git a/sf/saxon/trans/TextOnlyCopyRuleSet.java b/sf/saxon/trans/TextOnlyCopyRuleSet.java
new file mode 100644
index 0000000..e03a611
--- /dev/null
+++ b/sf/saxon/trans/TextOnlyCopyRuleSet.java
@@ -0,0 +1,108 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.expr.instruct.ParameterSet;
+import net.sf.saxon.expr.instruct.TailCall;
+import net.sf.saxon.om.AxisInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * The built-in rule set used for 1.0 and 2.0, which for document and element nodes does an apply-templates
+ * to children, and for text nodes and attribute nodes copies the node.
+ */
+public class TextOnlyCopyRuleSet implements BuiltInRuleSet {
+
+ private static TextOnlyCopyRuleSet THE_INSTANCE = new TextOnlyCopyRuleSet();
+
+ /**
+ * Get the singleton instance of this class
+ * @return the singleton instance
+ */
+
+ public static TextOnlyCopyRuleSet getInstance() {
+ return THE_INSTANCE;
+ }
+
+ private TextOnlyCopyRuleSet() {}
+
+ /**
+ * Perform the built-in template action for a given item.
+ * @param item the item to be processed
+ * @param parameters the parameters supplied to apply-templates
+ * @param tunnelParams the tunnel parameters to be passed through
+ * @param context the dynamic evaluation context
+ * @param locationId location of the instruction (apply-templates, apply-imports etc) that caused
+ * the built-in template to be invoked
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs
+ */
+
+ public void process(Item item, ParameterSet parameters,
+ ParameterSet tunnelParams, /*@NotNull*/ XPathContext context,
+ int locationId) throws XPathException {
+ if (item instanceof NodeInfo) {
+ NodeInfo node = (NodeInfo)item;
+ switch(node.getNodeKind()) {
+ case Type.DOCUMENT:
+ case Type.ELEMENT:
+ SequenceIterator iter = node.iterateAxis(AxisInfo.CHILD);
+ XPathContextMajor c2 = context.newContext();
+ c2.setOriginatingConstructType(Location.BUILT_IN_TEMPLATE);
+ c2.setCurrentIterator(iter);
+ TailCall tc = c2.getCurrentMode().applyTemplates(parameters, tunnelParams, c2, locationId);
+ while (tc != null) {
+ tc = tc.processLeavingTail();
+ }
+ return;
+ case Type.TEXT:
+ // NOTE: I tried changing this to use the text node's copy() method, but
+ // performance was worse
+ case Type.ATTRIBUTE:
+ context.getReceiver().characters(item.getStringValueCS(), locationId, 0);
+ return;
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ case Type.NAMESPACE:
+ // no action
+ }
+ } else if (item instanceof AtomicValue) {
+ context.getReceiver().characters(item.getStringValueCS(), locationId, 0);
+ } else {
+ // no action (e.g. for function items
+ }
+ }
+
+ /**
+ * Get the default action for unmatched nodes
+ *
+ * @param nodeKind the node kind
+ * @return the default action for unmatched nodes: one of DEEP_COPY, SHALLOW_SKIP, DEEP_SKIP, FAIL, etc
+ */
+ public int getDefaultAction(int nodeKind) {
+ switch (nodeKind) {
+ case Type.DOCUMENT:
+ case Type.ELEMENT:
+ return SHALLOW_SKIP;
+ case Type.TEXT:
+ return DEEP_COPY;
+ case Type.ATTRIBUTE:
+ return TEXT_COPY;
+ default:
+ return DEEP_SKIP;
+ }
+ }
+}
+
diff --git a/sf/saxon/trans/Timer.java b/sf/saxon/trans/Timer.java
new file mode 100644
index 0000000..b9ac90e
--- /dev/null
+++ b/sf/saxon/trans/Timer.java
@@ -0,0 +1,34 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+/**
+ * Utility class for collecting and reporting timing information, used only under diagnostic control
+ */
+public class Timer {
+
+ private long start;
+ private long prev;
+
+ public Timer() {
+ start = System.currentTimeMillis();
+ prev = start;
+ }
+
+ public void report(String label) {
+ long time = System.currentTimeMillis();
+ System.err.println(label + " " + (time - prev) + "ms");
+ prev = time;
+ }
+
+ public void reportCumulative(String label) {
+ long time = System.currentTimeMillis();
+ System.err.println(label + " " + (time - start) + "ms");
+ prev = time;
+ }
+}
diff --git a/sf/saxon/trans/UncheckedXPathException.java b/sf/saxon/trans/UncheckedXPathException.java
new file mode 100644
index 0000000..cd2f648
--- /dev/null
+++ b/sf/saxon/trans/UncheckedXPathException.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+/**
+ * When tree construction is deferred, innocuous methods such as NodeInfo#getLocalName() may
+ * trigger a dynamic error. Rather than make all such methods on NodeInfo throw a checked XPathException,
+ * we instead throw an UncheckedXPathException, which is a simple wrapper for an XPathException.
+ * Appropriate places in the client code must check for this condition and translate it back into an
+ * XPathException.
+ */
+
+public class UncheckedXPathException extends RuntimeException {
+
+ private XPathException cause;
+
+ public UncheckedXPathException(XPathException cause) {
+ this.cause = cause;
+ }
+
+ public XPathException getXPathException() {
+ return cause;
+ }
+}
diff --git a/sf/saxon/trans/XPathException.java b/sf/saxon/trans/XPathException.java
new file mode 100644
index 0000000..fe9be10
--- /dev/null
+++ b/sf/saxon/trans/XPathException.java
@@ -0,0 +1,413 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerException;
+
+/**
+ * XPathException is used to indicate an error in an XPath expression.
+ * It will generally be either a StaticError or a DynamicError;
+ * ValidationExceptions (arising from schema validation) form a third category
+*/
+
+public class XPathException extends TransformerException {
+
+ private boolean isTypeError = false;
+ private boolean isStaticError = false;
+ private boolean isGlobalError = false;
+ /*@Nullable*/ private String locationText = null;
+ /*@Nullable*/ private StructuredQName errorCode;
+ private Sequence errorObject;
+ private boolean hasBeenReported = false;
+ transient XPathContext context;
+ // declared transient because a compiled stylesheet might contain a "deferred action" dynamic error
+ // and the EarlyEvaluationContext links back to the source stylesheet.
+
+ /**
+ * Create an XPathException with an error message
+ * @param message the message explaining what is wrong. This should not include location information.
+ */
+
+ public XPathException(String message) {
+ super(message);
+ }
+
+ /**
+ * Create an XPathException that wraps another exception
+ * @param err the wrapped error or exception
+ */
+
+ public XPathException(Throwable err) {
+ super(err);
+ }
+
+ /**
+ * Create an XPathException that supplies an error message and wraps an underlying exception
+ * @param message the error message (which should generally explain what Saxon was doing when the
+ * underlying exception occurred)
+ * @param err the underlying exception (the cause of this exception)
+ */
+
+ public XPathException(String message, Throwable err) {
+ super(message, err);
+ }
+
+ /**
+ * Create an XPathException that supplies an error message and supplies location information
+ * @param message the error message
+ * @param loc indicates where in the user-written query or stylesheet (or sometimes in a source
+ * document) the error occurred
+ */
+
+ public XPathException(String message, SourceLocator loc) {
+ super(message, loc);
+ }
+
+ /**
+ * Create an XPathException that supplies an error message and wraps an underlying exception
+ * and supplies location information
+ * @param message the error message (which should generally explain what Saxon was doing when the
+ * underlying exception occurred)
+ * @param loc indicates where in the user-written query or stylesheet (or sometimes in a source
+ * document) the error occurred
+ * @param err the underlying exception (the cause of this exception)
+ */
+
+ public XPathException(String message, SourceLocator loc, Throwable err) {
+ super(message, loc, err);
+ }
+
+ /**
+ * Create an XPathException that supplies an error message and an error code
+ * @param message the error message
+ * @param errorCode the error code - an eight-character code, which is taken to be in the standard
+ * system error code namespace
+ */
+
+ public XPathException(String message, String errorCode) {
+ super(message);
+ setErrorCode(errorCode);
+ }
+
+ /**
+ * Create an XPathException that supplies an error message and an error code and provides the
+ * dynamic context
+ * @param message the error message
+ * @param errorCode the error code - an eight-character code, which is taken to be in the standard
+ * system error code namespace
+ * @param context the dynamic evaluation context
+ */
+
+ public XPathException(String message, String errorCode, XPathContext context) {
+ super(message);
+ setErrorCode(errorCode);
+ setXPathContext(context);
+ }
+
+ /**
+ * Create an XPathException from a JAXP TransformerException. If the TransformerException is an XPathException,
+ * or if its cause is an XPathException, that XPathException is returned unchanged; otherwise the
+ * TransformerException is wrapped.
+ * @param err the supplied JAXP TransformerException
+ * @return an XPathException obtained from the supplied TransformerException
+ */
+
+ /*@NotNull*/ public static XPathException makeXPathException(/*@NotNull*/ TransformerException err) {
+ if (err instanceof XPathException) {
+ return (XPathException)err;
+ } else if (err.getException() instanceof XPathException) {
+ return (XPathException)err.getException();
+ } else {
+ return new XPathException(err);
+ }
+ }
+
+ /**
+ * Force an exception to a static error
+ * @return this exception, marked as a static error
+ */
+
+ /*@NotNull*/ public XPathException makeStatic() {
+ setIsStaticError(true);
+ return this;
+ }
+
+ /**
+ * Set dynamic context information in the exception object
+ * @param context the dynamic context at the time the exception occurred
+ */
+
+ public void setXPathContext(XPathContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Get the dynamic context at the time the exception occurred
+ * @return the dynamic context if known; otherwise null
+ */
+
+ public XPathContext getXPathContext() {
+ return context;
+ }
+
+ /**
+ * Set additional location text. This gives extra information about the position of the error
+ * in textual form. Where XPath is embedded within a host language such as XSLT, the
+ * formal location information identifies the location of the error in the XSLT module,
+ * while this string locates the error within a specific XPath expression. The information
+ * is typically used only for static errors.
+ * @param text additional information about the location of the error, designed to be output
+ * as a prefix to the error message if desired. (It is not concatenated with the message, because
+ * it may be superfluous in an IDE environment.)
+ */
+
+ public void setAdditionalLocationText(String text) {
+ locationText = text;
+ }
+
+ /**
+ * Get the additional location text, if any. This gives extra information about the position of the error
+ * in textual form. Where XPath is embedded within a host language such as XSLT, the
+ * formal location information identifies the location of the error in the XSLT module,
+ * while this string locates the error within a specific XPath expression. The information
+ * is typically used only for static errors.
+ * @return additional information about the location of the error, designed to be output
+ * as a prefix to the error message if desired. (It is not concatenated with the message, because
+ * it may be superfluous in an IDE environment.)
+ */
+
+ /*@Nullable*/ public String getAdditionalLocationText() {
+ return locationText;
+ }
+
+ /**
+ * Mark this exception to indicate that it represents (or does not represent) a static error
+ * @param is true if this exception is a static error
+ */
+
+ public void setIsStaticError(boolean is) {
+ isStaticError = is;
+ }
+
+ /**
+ * Ask whether this exception represents a static error
+ * @return true if this exception is a static error
+ */
+
+ public boolean isStaticError() {
+ return isStaticError;
+ }
+
+ /**
+ * Mark this exception to indicate that it represents (or does not represent) a type error
+ * @param is true if this exception is a type error
+ */
+
+ public void setIsTypeError(boolean is) {
+ isTypeError = is;
+ }
+
+ /**
+ * Ask whether this exception represents a type error
+ * @return true if this exception is a type error
+ */
+
+ public boolean isTypeError() {
+ return isTypeError;
+ }
+
+ /**
+ * Mark this exception to indicate that it originated while evaluating a global
+ * variable reference, and is therefore to be reported regardless of the try/catch
+ * context surrounding the variable reference
+ * @param is true if this exception is a global variable error
+ */
+
+ public void setIsGlobalError(boolean is) {
+ isGlobalError = is;
+ }
+
+ /**
+ * Ask whether this exception originated while evaluating a global
+ * variable reference, and is therefore to be reported regardless of the try/catch
+ * context surrounding the variable reference
+ * @return true if this exception is a global variable error
+ */
+
+ public boolean isGlobalError() {
+ return isGlobalError;
+ }
+
+
+ /**
+ * Set the error code. The error code is a QName; this method sets the local part of the name,
+ * setting the namespace of the error code to the standard system namespace {@link net.sf.saxon.lib.NamespaceConstant#ERR}
+ * @param code The local part of the name of the error code
+ */
+
+ public void setErrorCode(/*@Nullable*/ String code) {
+ if (code != null) {
+ errorCode = new StructuredQName("err", NamespaceConstant.ERR, code);
+ }
+ }
+
+ /**
+ * Set the error code, provided it has not already been set.
+ * The error code is a QName; this method sets the local part of the name,
+ * setting the namespace of the error code to the standard system namespace {@link NamespaceConstant#ERR}
+ * @param code The local part of the name of the error code
+ */
+
+ public void maybeSetErrorCode(/*@Nullable*/ String code) {
+ if (errorCode == null && code != null) {
+ errorCode = new StructuredQName("err", NamespaceConstant.ERR, code);
+ }
+ }
+
+ /**
+ * Set the error code. The error code is a QName; this method sets both parts of the name.
+ * @param code The error code as a QName
+ */
+
+ public void setErrorCodeQName(/*@Nullable*/ StructuredQName code) {
+ errorCode = code;
+ }
+
+ /**
+ * Get the error code as a QName
+ * @return the error code as a QName
+ */
+
+ /*@Nullable*/ public StructuredQName getErrorCodeQName() {
+ return errorCode;
+ }
+
+ /**
+ * Get the local part of the name of the error code
+ * @return the local part of the name of the error code
+ */
+
+ /*@Nullable*/ public String getErrorCodeLocalPart() {
+ return (errorCode == null ? null : errorCode.getLocalPart());
+ }
+
+ /**
+ * Get the namespace URI part of the name of the error code
+ * @return the namespace URI part of the name of the error code
+ */
+
+ /*@Nullable*/ public String getErrorCodeNamespace() {
+ return (errorCode == null ? null : errorCode.getURI());
+ }
+
+ /**
+ * Set the error object associated with this error. This is used by the standard XPath fn:error() function
+ * @param value the error object, as supplied to the fn:error() function
+ */
+
+ public void setErrorObject(Sequence value) {
+ errorObject = value;
+ }
+
+ /**
+ * Get the error object associated with this error. This is used by the standard XPath fn:error() function
+ * @return the error object, as supplied to the fn:error() function
+ */
+
+ public Sequence getErrorObject() {
+ return errorObject;
+ }
+
+ /**
+ * Mark this error to indicate that it has already been reported to the error listener, and should not be
+ * reported again
+ * @param reported true if the error has been reported to the error listener
+ */
+
+ public void setHasBeenReported(boolean reported) {
+ hasBeenReported = reported;
+ }
+
+ /**
+ * Ask whether this error is marked to indicate that it has already been reported to the error listener,
+ * and should not be reported again
+ * @return true if this error has already been reported
+ */
+
+ public boolean hasBeenReported() {
+ return hasBeenReported;
+ }
+
+ /**
+ * Set the location of a message, only if it is not already set
+ * @param locator the current location (or null)
+ */
+
+ public void maybeSetLocation(/*@Nullable*/ SourceLocator locator) {
+ if ((getLocator() == null || getLocator().getLineNumber() == -1) && locator != null) {
+ setLocator(locator);
+ }
+ }
+
+ /**
+ * Set the context of a message, only if it is not already set
+ * @param context the current XPath context (or null)
+ */
+
+ public void maybeSetContext(XPathContext context) {
+ if (getXPathContext() == null) {
+ setXPathContext(context);
+ }
+ }
+
+ /**
+ * Tests whether this is a dynamic error that may be reported statically if it is detected statically
+ * @return true if the error can be reported statically
+ */
+
+ public boolean isReportableStatically() {
+ if (isStaticError() || isTypeError()) {
+ return true;
+ }
+ StructuredQName err = errorCode;
+ if (err != null && err.getURI().equals(NamespaceConstant.ERR)) {
+ String local = err.getLocalPart();
+ return local.equals("XTDE1260") ||
+ local.equals("XTDE1280") ||
+ local.equals("XTDE1390") ||
+ local.equals("XTDE1400") ||
+ local.equals("XTDE1428") ||
+ local.equals("XTDE1440") ||
+ local.equals("XTDE1460");
+ }
+ return false;
+ }
+
+ /**
+ * Subclass of XPathException used to report circularities
+ */
+
+ public static class Circularity extends XPathException {
+
+ /**
+ * Create an exception indicating that a circularity was detected
+ * @param message the error message
+ */
+ public Circularity(String message) {
+ super(message);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/trans/XmlCatalogResolver.java b/sf/saxon/trans/XmlCatalogResolver.java
new file mode 100644
index 0000000..9c53697
--- /dev/null
+++ b/sf/saxon/trans/XmlCatalogResolver.java
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.trans;
+
+
+import net.sf.saxon.Configuration;
+import org.apache.xml.resolver.CatalogManager;
+import org.apache.xml.resolver.helpers.Debug;
+
+import javax.xml.transform.TransformerException;
+
+/**
+ * Provides the interface to the Apache catalog resolver. This is in a separate class to ensure that no failure
+ * occurs if the resolver code is not on the classpath, unless catalog resolution is explicitly requested.
+ */
+
+public class XmlCatalogResolver {
+
+ public static void setCatalog(String catalog, final Configuration config, boolean isTracing) throws XPathException {
+ System.setProperty("xml.catalog.files", catalog);
+ Configuration.getPlatform().setDefaultSAXParserFactory();
+ if (isTracing) {
+ // Customize the resolver to write messages to the Saxon logging destination
+ CatalogManager.getStaticManager().debug = new Debug() {
+ @Override
+ public void message(int level, String message) {
+ if (level <= getDebug()) {
+ config.getStandardErrorOutput().println(message);
+ }
+ }
+
+ @Override
+ public void message(int level, String message, String spec) {
+ if (level <= getDebug()) {
+ config.getStandardErrorOutput().println(message + ": " + spec);
+ }
+ }
+
+ @Override
+ public void message(int level, String message, String spec1, String spec2) {
+ if (level <= getDebug()) {
+ config.getStandardErrorOutput().println(message + ": " + spec1);
+ config.getStandardErrorOutput().println("\t" + spec2);
+ }
+ }
+ };
+ CatalogManager.getStaticManager().setVerbosity(2);
+ }
+ config.setSourceParserClass("org.apache.xml.resolver.tools.ResolvingXMLReader");
+ config.setStyleParserClass("org.apache.xml.resolver.tools.ResolvingXMLReader");
+ try {
+ config.setURIResolver(config.makeURIResolver("org.apache.xml.resolver.tools.CatalogResolver"));
+ } catch (TransformerException err) {
+ throw XPathException.makeXPathException(err);
+ }
+ }
+}
+
diff --git a/sf/saxon/trans/package.html b/sf/saxon/trans/package.html
new file mode 100644
index 0000000..e38eb22
--- /dev/null
+++ b/sf/saxon/trans/package.html
@@ -0,0 +1,29 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon</title>
+</head>
+
+<body>
+
+<p>This package provides a miscellaneous collection of
+helper classes for XSLT transformation. They are of no direct interest to user applications,
+except for the <code>XPathException</code> class, which is used to represent many
+errors in public methods.</p>
+
+
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/tree/NamespaceNode.java b/sf/saxon/tree/NamespaceNode.java
new file mode 100644
index 0000000..d43e2ce
--- /dev/null
+++ b/sf/saxon/tree/NamespaceNode.java
@@ -0,0 +1,676 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.*;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.NamespaceIterator;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.StringValue;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This class represents a namespace node; it is used in several tree models.
+ */
+
+public class NamespaceNode implements NodeInfo, FingerprintedNode {
+
+ NodeInfo element;
+ NamespaceBinding nsBinding;
+ int position;
+ int namecode;
+
+ /**
+ * Create a namespace node
+ *
+ * @param element the parent element of the namespace node
+ * @param nscode the namespace code, representing the prefix and URI of the namespace binding
+ * @param position maintains document order among namespace nodes for the same element
+ */
+
+ public NamespaceNode(NodeInfo element, NamespaceBinding nscode, int position) {
+ this.element = element;
+ this.nsBinding = nscode;
+ this.position = position;
+ namecode = -1; // evaluated lazily to avoid NamePool access
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns the item itself
+ * @return this item
+ */
+
+ public Item head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ * @return a singleton iterator that returns this item
+ */
+
+ public SequenceIterator iterate() {
+ return SingletonIterator.makeIterator(this);
+ }
+
+ /**
+ * Get the kind of node. This will be a value such as Type.ELEMENT or Type.ATTRIBUTE
+ *
+ * @return an integer identifying the kind of node. These integer values are the
+ * same as those used in the DOM
+ * @see net.sf.saxon.type.Type
+ */
+
+ public int getNodeKind() {
+ return Type.NAMESPACE;
+ }
+
+ /**
+ * Determine whether this is the same node as another node.
+ * Note: a.isSameNodeInfo(b) if and only if generateId(a)==generateId(b).
+ * This method has the same semantics as isSameNode() in DOM Level 3, but
+ * works on Saxon NodeInfo objects rather than DOM Node objects.
+ *
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(/*@NotNull*/ NodeInfo other) {
+ return other instanceof NamespaceNode &&
+ element.isSameNodeInfo(((NamespaceNode) other).element) &&
+ nsBinding.equals(((NamespaceNode) other).nsBinding);
+
+ }
+
+ /**
+ * The equals() method compares nodes for identity. It is defined to give the same result
+ * as isSameNodeInfo().
+ *
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ * @since 8.7 Previously, the effect of the equals() method was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics. It is safer to use isSameNodeInfo() for this reason.
+ * The equals() method has been defined because it is useful in contexts such as a Java Set or HashMap.
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof NamespaceNode && isSameNodeInfo((NodeInfo) other);
+ }
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ *
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode() {
+ return element.hashCode() ^ (position << 13);
+ }
+
+ /**
+ * Get the System ID for the node.
+ *
+ * @return the System Identifier of the entity in the source document
+ * containing the node, or null if not known. Note this is not the
+ * same as the base URI: the base URI can be modified by xml:base, but
+ * the system ID cannot.
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return element.getSystemId();
+ }
+
+ /**
+ * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
+ * in the node. This will be the same as the System ID unless xml:base has been used.
+ *
+ * @return the base URI of the node
+ */
+
+ /*@Nullable*/ public String getBaseURI() {
+ return null; // the base URI of a namespace node is the empty sequence
+ }
+
+ /**
+ * Get line number
+ *
+ * @return the line number of the node in its original source document; or
+ * -1 if not available
+ */
+
+ public int getLineNumber() {
+ return element.getLineNumber();
+ }
+
+ /**
+ * Get column number
+ *
+ * @return the column number of the node in its original source document; or
+ * -1 if not available
+ */
+
+ public int getColumnNumber() {
+ return element.getColumnNumber();
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return -1 if this node precedes the other node, +1 if it follows the
+ * other node, or 0 if they are the same node. (In this case,
+ * isSameNode() will always return true, and the two nodes will
+ * produce the same result for generateId())
+ */
+
+ public int compareOrder(/*@NotNull*/ NodeInfo other) {
+ if (other instanceof NamespaceNode && element.isSameNodeInfo(((NamespaceNode) other).element)) {
+ // alternative: return Integer.signum(position - ((NamespaceNodeI)other).position);
+ int c = position - ((NamespaceNode) other).position;
+ if (c == 0) {
+ return 0;
+ }
+ if (c < 0) {
+ return -1;
+ }
+ return +1;
+ } else if (element.isSameNodeInfo(other)) {
+ return +1;
+ } else {
+ return element.compareOrder(other);
+ }
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+ throw new UnsupportedOperationException("comparePosition(namespace)");
+ }
+
+ /**
+ * Return the string value of the node. The interpretation of this depends on the type
+ * of node. For a namespace node, it is the namespace URI.
+ *
+ * @return the string value of the node
+ */
+
+ public String getStringValue() {
+ return nsBinding.getURI();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ return getStringValue();
+ }
+
+ /**
+ * Get name code. The name code is a coded form of the node name: two nodes
+ * with the same name code have the same namespace URI, the same local name,
+ * and the same prefix. By masking the name code with &0xfffff, you get a
+ * fingerprint: two nodes with the same fingerprint have the same local name
+ * and namespace URI.
+ *
+ * @return an integer name code, which may be used to obtain the actual node
+ * name from the name pool
+ * @see net.sf.saxon.om.NamePool#allocate allocate
+ * @see net.sf.saxon.om.NamePool#getFingerprint getFingerprint
+ */
+
+ public int getNameCode() {
+ if (namecode == -1) {
+ if (nsBinding.getPrefix().length()==0) {
+ return -1;
+ } else {
+ namecode = element.getNamePool().allocate("", "", nsBinding.getPrefix());
+ }
+ }
+ return namecode;
+ }
+
+ /**
+ * Get fingerprint. The fingerprint is a coded form of the expanded name
+ * of the node: two nodes
+ * with the same name code have the same namespace URI and the same local name.
+ * A fingerprint of -1 should be returned for a node with no name.
+ *
+ * @return an integer fingerprint; two nodes with the same fingerprint have
+ * the same expanded QName
+ */
+
+ public int getFingerprint() {
+ if (nsBinding.getPrefix().length()==0) {
+ return -1;
+ }
+ return getNameCode() & NamePool.FP_MASK;
+ }
+
+ /**
+ * Get the local part of the name of this node. This is the name after the ":" if any.
+ *
+ * @return the local part of the name. For an unnamed node, returns "". Unlike the DOM
+ * interface, this returns the full name in the case of a non-namespaced name.
+ */
+
+ public String getLocalPart() {
+ return nsBinding.getPrefix();
+ }
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ *
+ * @return The URI of the namespace of this node. Since the name of a namespace
+ * node is always an NCName (the namespace prefix), this method always returns "".
+ */
+
+ /*@NotNull*/ public String getURI() {
+ return "";
+ }
+
+ /**
+ * Get the display name of this node. For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ *
+ * @return The display name of this node. For a node with no name, return
+ * an empty string.
+ */
+
+ public String getDisplayName() {
+ return getLocalPart();
+ }
+
+ /**
+ * Get the prefix of the name of the node. This is defined only for elements and attributes.
+ * If the node has no prefix, or for other kinds of node, return a zero-length string.
+ *
+ * @return The prefix of the name of the node.
+ */
+
+ /*@NotNull*/ public String getPrefix() {
+ return "";
+ }
+
+ /**
+ * Get the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return element.getConfiguration();
+ }
+
+ /**
+ * Get the NamePool that holds the namecode for this node
+ *
+ * @return the namepool
+ */
+
+ public NamePool getNamePool() {
+ return element.getNamePool();
+ }
+
+ /**
+ * Get the type annotation of this node, if any.
+ * Returns -1 for kinds of nodes that have no annotation, and for elements annotated as
+ * untyped, and attributes annotated as untypedAtomic.
+ *
+ * @return the type annotation of the node.
+ * @see net.sf.saxon.type.Type
+ */
+
+ public int getTypeAnnotation() {
+ return StandardNames.XS_STRING;
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ public SchemaType getSchemaType() {
+ return BuiltInAtomicType.STRING;
+ }
+
+ /**
+ * Get the NodeInfo object representing the parent of this node
+ *
+ * @return the parent of this node; null if this node has no parent
+ */
+
+ public NodeInfo getParent() {
+ return element;
+ }
+
+ /**
+ * Return an iteration over all the nodes reached by the given axis from this node
+ *
+ * @param axisNumber an integer identifying the axis; one of the constants
+ * defined in class net.sf.saxon.om.Axis
+ * @return an AxisIterator that scans the nodes reached by the axis in
+ * turn.
+ * @throws UnsupportedOperationException if the namespace axis is
+ * requested and this axis is not supported for this implementation.
+ * @see net.sf.saxon.om.AxisInfo
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber) {
+ return iterateAxis(axisNumber, AnyNodeTest.getInstance());
+ }
+
+ /**
+ * Return an iteration over all the nodes reached by the given axis from this node
+ * that match a given NodeTest
+ *
+ * @param axisNumber an integer identifying the axis; one of the constants
+ * defined in class net.sf.saxon.om.Axis
+ * @param nodeTest A pattern to be matched by the returned nodes; nodes
+ * that do not match this pattern are not included in the result
+ * @return a NodeEnumeration that scans the nodes reached by the axis in
+ * turn.
+ * @throws UnsupportedOperationException if the namespace axis is
+ * requested and this axis is not supported for this implementation.
+ * @see net.sf.saxon.om.AxisInfo
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber, /*@NotNull*/ NodeTest nodeTest) {
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR:
+ return element.iterateAxis(AxisInfo.ANCESTOR_OR_SELF, nodeTest);
+
+ case AxisInfo.ANCESTOR_OR_SELF:
+ if (nodeTest.matches(this)) {
+ return new PrependIterator(this, element.iterateAxis(AxisInfo.ANCESTOR_OR_SELF, nodeTest));
+ } else {
+ return element.iterateAxis(AxisInfo.ANCESTOR_OR_SELF, nodeTest);
+ }
+
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.CHILD:
+ case AxisInfo.DESCENDANT:
+ case AxisInfo.DESCENDANT_OR_SELF:
+ case AxisInfo.FOLLOWING_SIBLING:
+ case AxisInfo.NAMESPACE:
+ case AxisInfo.PRECEDING_SIBLING:
+ return EmptyAxisIterator.emptyAxisIterator();
+
+ case AxisInfo.FOLLOWING:
+ return new Navigator.AxisFilter(
+ new Navigator.FollowingEnumeration(this),
+ nodeTest);
+
+ case AxisInfo.PARENT:
+ return Navigator.filteredSingleton(element, nodeTest);
+
+ case AxisInfo.PRECEDING:
+ return new Navigator.AxisFilter(
+ new Navigator.PrecedingEnumeration(this, false),
+ nodeTest);
+
+ case AxisInfo.SELF:
+ return Navigator.filteredSingleton(this, nodeTest);
+
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ return new Navigator.AxisFilter(
+ new Navigator.PrecedingEnumeration(this, true),
+ nodeTest);
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) {
+ return null;
+ }
+
+ /**
+ * Get the value of the attribute with a given fingerprint.
+ *
+ * @param fp the fingerprint of the required attribute
+ * @return the string value of the attribute if present, or null if absent
+ */
+ public String getAttributeValue(int fp) {
+ return null;
+ }
+
+ /**
+ * Get the root node of the tree containing this node
+ *
+ * @return the NodeInfo representing the top-level ancestor of this node.
+ * This will not necessarily be a document node
+ */
+
+ public NodeInfo getRoot() {
+ return element.getRoot();
+ }
+
+ /**
+ * Get the root node, if it is a document node.
+ *
+ * @return the DocumentInfo representing the containing document. If this
+ * node is part of a tree that does not have a document node as its
+ * root, return null.
+ */
+
+ /*@Nullable*/ public DocumentInfo getDocumentRoot() {
+ return element.getDocumentRoot();
+ }
+
+ /**
+ * Determine whether the node has any children. <br />
+ * Note: the result is equivalent to <br />
+ * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
+ *
+ * @return True if the node has one or more children
+ */
+
+ public boolean hasChildNodes() {
+ return false;
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node.
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ *
+ * @param buffer buffer to hold a string that uniquely identifies this node, across all
+ * documents.
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ element.generateId(buffer);
+ buffer.append("n");
+ buffer.append(Integer.toString(position));
+ }
+
+ /**
+ * Get the document number of the document containing this node. For a free-standing
+ * orphan node, just return the hashcode.
+ */
+
+ public long getDocumentNumber() {
+ return element.getDocumentNumber();
+ }
+
+ /**
+ * Copy this node to a given outputter
+ *
+ * @param out the Receiver to which the node should be copied
+ * @param copyOptions a selection of the options defined in {@link net.sf.saxon.om.CopyOptions}
+ * @param locationId If non-zero, identifies the location of the instruction
+ * that requested this copy. If zero, indicates that the location information
+ * for the original node is to be copied; in this case the Receiver must be
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ out.namespace(nsBinding, ReceiverOptions.REJECT_DUPLICATES);
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ /*@Nullable*/ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return null;
+ }
+
+ /**
+ * Set the system identifier for this Source.
+ * <p/>
+ * <p>The system identifier is optional if the source does not
+ * get its data from a URL, but it may still be useful to provide one.
+ * The application can use a system identifier, for example, to resolve
+ * relative URIs and to include in error messages and warnings.</p>
+ *
+ * @param systemId The system identifier as a URL string.
+ */
+ public void setSystemId(String systemId) {
+ // no action: namespace nodes have the same base URI as their parent
+ }
+
+ /**
+ * Get the typed value. The result of this method will always be consistent with the method
+ * {@link net.sf.saxon.om.Item#getTypedValue()}. However, this method is often more convenient and may be
+ * more efficient, especially in the common case where the value is expected to be a singleton.
+ *
+ * @return the typed value. If requireSingleton is set to true, the result will always be an
+ * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize() throws XPathException {
+ return new StringValue(getStringValueCS());
+ }
+
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ return false;
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return false;
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return false;
+ }
+
+
+ /**
+ * Factory method to create an iterator over the in-scope namespace nodes of an element
+ * @param element the node whose namespaces are required
+ * @param test used to filter the returned nodes
+ * @return an iterator over the namespace nodes that satisfy the test
+ */
+
+ /*@NotNull*/ public static AxisIterator makeIterator(final NodeInfo element, /*@NotNull*/ NodeTest test) {
+ List<NamespaceNode> nodes = new ArrayList<NamespaceNode>();
+ Iterator<NamespaceBinding> bindings = NamespaceIterator.iterateNamespaces(element);
+ int position = 0;
+ while (bindings.hasNext()) {
+ NamespaceNode node = new NamespaceNode(element, bindings.next(), position++);
+ if (test.matches(node)) {
+ nodes.add(node);
+ }
+ }
+ NamespaceNode node = new NamespaceNode(element, NamespaceBinding.XML, position);
+ if (test.matches(node)) {
+ nodes.add(node);
+ }
+ return new AxisIteratorOverSequence<NamespaceNode>(new ListIterator<NamespaceNode>(nodes));
+ }
+}
+
diff --git a/sf/saxon/tree/iter/AdjacentTextNodeMergingIterator.java b/sf/saxon/tree/iter/AdjacentTextNodeMergingIterator.java
new file mode 100644
index 0000000..48e77b2
--- /dev/null
+++ b/sf/saxon/tree/iter/AdjacentTextNodeMergingIterator.java
@@ -0,0 +1,93 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.AdjacentTextNodeMerger;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Orphan;
+import net.sf.saxon.type.Type;
+
+/**
+ * AdjacentTextNodeMergingIterator is an iterator that eliminates zero-length text nodes
+ * and merges adjacent text nodes from the underlying iterator
+ */
+
+public class AdjacentTextNodeMergingIterator implements LookaheadIterator {
+
+ private SequenceIterator base;
+ /*@Nullable*/ private Item current;
+ /*@Nullable*/ private Item next;
+ private int position = 0;
+
+ public AdjacentTextNodeMergingIterator(/*@NotNull*/ SequenceIterator base) throws XPathException {
+ this.base = base;
+ next = base.next();
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ /*@Nullable*/ public Item next() throws XPathException {
+ current = next;
+ if (current == null) {
+ position = -1;
+ return null;
+ }
+ next = base.next();
+
+ if (AdjacentTextNodeMerger.isTextNode(current)) {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ fsb.append(current.getStringValueCS());
+ while (next != null && AdjacentTextNodeMerger.isTextNode(next)) {
+ fsb.append(next.getStringValueCS() /*.toString() */);
+ // NOTE: toString() shouldn't be necessary - added 2011-05-05 for bug workaround; removed again 2011-07-14
+ next = base.next();
+ }
+ if (fsb.length() == 0) {
+ return next();
+ } else {
+ Orphan o = new Orphan(((NodeInfo)current).getConfiguration());
+ o.setNodeKind(Type.TEXT);
+ o.setStringValue(fsb);
+ current = o;
+ position++;
+ return current;
+ }
+ } else {
+ position++;
+ return current;
+ }
+ }
+
+ /*@Nullable*/ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new AdjacentTextNodeMergingIterator(base.getAnother());
+ }
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+}
+
diff --git a/sf/saxon/tree/iter/ArrayIterator.java b/sf/saxon/tree/iter/ArrayIterator.java
new file mode 100644
index 0000000..de113a3
--- /dev/null
+++ b/sf/saxon/tree/iter/ArrayIterator.java
@@ -0,0 +1,252 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.value.SequenceExtent;
+
+/**
+ * ArrayIterator is used to enumerate items held in an array.
+ * The items are always held in the correct sorted order for the sequence.
+ *
+ * @author Michael H. Kay
+ */
+
+
+public class ArrayIterator<T extends Item> implements UnfailingIterator<T>,
+ ReversibleIterator<T>, LastPositionFinder<T>, LookaheadIterator<T>, GroundedIterator<T> {
+
+ protected T[] items;
+ private int index; // position in array of current item, zero-based
+ // set equal to end+1 when all the items required have been read.
+ protected int start; // position of first item to be returned, zero-based
+ protected int end; // position of first item that is NOT returned, zero-based
+ /*@Nullable*/ private T current = null;
+
+ /**
+ * Create an iterator over all the items in an array
+ *
+ * @param nodes the array (of any items, not necessarily nodes) to be
+ * processed by the iterator
+ */
+
+ public ArrayIterator(/*@NotNull*/ T[] nodes) {
+ items = nodes;
+ start = 0;
+ end = nodes.length;
+ index = 0;
+ }
+
+ /**
+ * Create an iterator over a range of an array. Note that the start position is zero-based
+ *
+ * @param items the array (of nodes or simple values) to be processed by
+ * the iterator
+ * @param start the position of the first item to be processed
+ * (numbering from zero). Must be between zero and nodes.length-1; if not,
+ * undefined exceptions are likely to occur.
+ * @param end position of first item that is NOT returned, zero-based. Must be
+ * beween 1 and nodes.length; if not, undefined exceptions are likely to occur.
+ */
+
+ public ArrayIterator(T[] items, int start, int end) {
+ this.items = items;
+ this.end = end;
+ this.start = start;
+ index = start;
+ }
+
+ /**
+ * Create a new ArrayIterator over the same items,
+ * with a different start point and end point
+ * @param min the start position (1-based) of the new ArrayIterator
+ * relative to the original
+ * @param max the end position (1-based) of the last item to be delivered
+ * by the new ArrayIterator, relative to the original. For example, min=2, max=3
+ * delivers the two items ($base[2], $base[3]). Set this to Integer.MAX_VALUE if
+ * there is no end limit.
+ * @return an iterator over the items between the min and max positions
+ */
+
+ public SequenceIterator<T> makeSliceIterator(int min, int max) {
+ T[] items = getArray();
+ int currentStart = getStartPosition();
+ int currentEnd = getEndPosition();
+ if (min < 1) {
+ min = 1;
+ }
+ int newStart = currentStart + (min-1);
+ if (newStart < currentStart) {
+ newStart = currentStart;
+ }
+ int newEnd = (max == Integer.MAX_VALUE ? currentEnd : newStart + (max - min + 1));
+ if (newEnd > currentEnd) {
+ newEnd = currentEnd;
+ }
+ if (newEnd <= newStart) {
+ return EmptyIterator.emptyIterator();
+ }
+ return new ArrayIterator<T>(items, newStart, newEnd);
+ }
+
+ /**
+ * Test whether there are any more items
+ * @return true if there are more items
+ */
+
+ public boolean hasNext() {
+ return index < end;
+ }
+
+ /**
+ * Get the next item in the array
+ * @return the next item in the array
+ */
+
+ /*@Nullable*/ public T next() {
+ if (index >= end) {
+ index = end+1;
+ current = null;
+ return null;
+ }
+ current = items[index++];
+ return current;
+ }
+
+ /**
+ * Get the current item in the array
+ *
+ * @return the item returned by the most recent call of next()
+ */
+ /*@Nullable*/ public T current() {
+ return current;
+ }
+
+ /**
+ * Get the position of the current item in the array
+ *
+ * @return the current position (starting at 1 for the first item)
+ */
+ public int position() {
+ if (index > end) {
+ return -1;
+ }
+ return index - start;
+ }
+
+ /**
+ * Get the number of items in the part of the array being processed
+ *
+ * @return the number of items; equivalently, the position of the last
+ * item
+ */
+ public int getLength() {
+ return end - start;
+ }
+
+ public void close() {
+
+ }
+
+ /**
+ * Get another iterator over the same items
+ *
+ * @return a new ArrayIterator
+ */
+ /*@NotNull*/
+ public ArrayIterator<T> getAnother() {
+ return new ArrayIterator<T>(items, start, end);
+ }
+
+ /**
+ * Get an iterator that processes the same items in reverse order
+ *
+ * @return a new ArrayIterator
+ */
+ public SequenceIterator<T> getReverseIterator() {
+ return new ReverseArrayIterator<T>(items, start, end);
+ }
+
+ /**
+ * Indicate that any nodes returned in the sequence will be atomized. This
+ * means that if it wishes to do so, the implementation can return the typed
+ * values of the nodes rather than the nodes themselves. The implementation
+ * is free to ignore this hint.
+ * @param atomizing true if the caller of this iterator will atomize any
+ * nodes that are returned, and is therefore willing to accept the typed
+ * value of the nodes instead of the nodes themselves.
+ */
+
+ //public void setIsAtomizing(boolean atomizing) {}
+
+ /**
+ * Get the underlying array
+ *
+ * @return the underlying array being processed by the iterator
+ */
+
+ public T[] getArray() {
+ return items;
+ }
+
+ /**
+ * Get the initial start position
+ *
+ * @return the start position of the iterator in the array (zero-based)
+ */
+
+ public int getStartPosition() {
+ return start;
+ }
+
+ /**
+ * Get the end position in the array
+ *
+ * @return the position in the array (zero-based) of the first item not included
+ * in the iteration
+ */
+
+ public int getEndPosition() {
+ return end;
+ }
+
+ /**
+ * Return a SequenceValue containing all the items in the sequence returned by this
+ * SequenceIterator
+ *
+ * @return the corresponding SequenceValue
+ */
+
+ /*@NotNull*/ public GroundedValue materialize() {
+ if (start==0 && end == items.length) {
+ return new SequenceExtent<T>(items);
+ } else {
+ SequenceExtent e = new SequenceExtent<T>(items);
+ return new SequenceExtent<T>(e, start, end-start);
+ }
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return GROUNDED | LAST_POSITION_FINDER | LOOKAHEAD;
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/AtomizingIterator.java b/sf/saxon/tree/iter/AtomizingIterator.java
new file mode 100644
index 0000000..7618c1d
--- /dev/null
+++ b/sf/saxon/tree/iter/AtomizingIterator.java
@@ -0,0 +1,119 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.*;
+
+/**
+ * AtomizingIterator returns the atomization of an underlying sequence supplied
+ * as an iterator. We use a specialist class rather than a general-purpose
+ * MappingIterator for performance, especially as the relationship of items
+ * in the result sequence to those in the base sequence is often one-to-one.
+ *
+ * This AtomizingIterator is capable of handling list-typed nodes whose atomized value
+ * is a sequence of more than one item. When it is known that all input will be untyped,
+ * an {@link UntypedAtomizingIterator} is used in preference.
+*/
+
+public class AtomizingIterator implements SequenceIterator<AtomicValue> {
+
+ private SequenceIterator base;
+ /*@Nullable*/ private AtomicSequence currentValue = null;
+ /*@Nullable*/ private AtomicValue current = null;
+ private int position = 0;
+ private int currentValuePosition = 1;
+ private int currentValueSize = 1;
+
+ /**
+ * Construct an AtomizingIterator that will atomize the values returned by the base iterator.
+ * @param base the base iterator
+ */
+
+ public AtomizingIterator(SequenceIterator base) {
+ this.base = base;
+ }
+
+ /*@Nullable*/ public AtomicValue next() throws XPathException {
+ while (true) {
+ if (currentValue != null) {
+ if (currentValuePosition < currentValueSize) {
+ current = (AtomicValue)currentValue.itemAt(currentValuePosition++);
+ position++;
+ return current;
+ } else {
+ currentValue = null;
+ }
+ }
+ Item nextSource = base.next();
+ if (nextSource != null) {
+ if (nextSource instanceof NodeInfo) {
+ AtomicSequence v = ((NodeInfo)nextSource).atomize();
+ if (v instanceof AtomicValue) {
+ current = (AtomicValue)v;
+ position++;
+ return (AtomicValue)v;
+ } else {
+ currentValue = v;
+ currentValuePosition = 0;
+ currentValueSize = currentValue.getLength();
+ // now go round the loop to get the first item from the atomized value
+ }
+ } else if (nextSource instanceof AtomicValue) {
+ return (AtomicValue)nextSource;
+ } else if (nextSource instanceof ObjectValue) {
+ return StringValue.makeStringValue(nextSource.getStringValue());
+ } else {
+ throw new XPathException("The typed value of a function item is not defined", "FOTY0013");
+ }
+ } else {
+ currentValue = null;
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+ }
+
+ /*@Nullable*/ public AtomicValue current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<AtomicValue> getAnother() throws XPathException {
+ return new AtomizingIterator(base.getAnother());
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED}, {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/AxisIterator.java b/sf/saxon/tree/iter/AxisIterator.java
new file mode 100644
index 0000000..d4c9231
--- /dev/null
+++ b/sf/saxon/tree/iter/AxisIterator.java
@@ -0,0 +1,77 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+
+
+/**
+ * A SequenceIterator is used to iterate over a sequence. An AxisIterator
+ * is a SequenceIterator that throws no exceptions, and that always returns
+ * nodes. The nodes should all be in the same document (though there are
+ * some cases, such as PrependIterator, where this is the responsibility of the
+ * user of the class and is not enforced.)
+ */
+
+public interface AxisIterator<T extends NodeInfo> extends UnfailingIterator<T> {
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ * @return true if there is a next node, false if the end of the sequence has been reached
+ */
+
+ boolean moveNext();
+
+ /*@Nullable*/ T next();
+
+ /*@Nullable*/ T current();
+
+ /*@NotNull*/
+ AxisIterator<T> getAnother();
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ * @return an iterator over an axis, starting at the current node
+ * @throws NullPointerException if there is no current node
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test);
+
+ /**
+ * Return the atomized value of the current node.
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ * @throws net.sf.saxon.trans.XPathException if the current node
+ * cannot be atomized, for example because it is an element node with
+ * element-only content.
+ */
+
+ public Sequence atomize() throws XPathException;
+
+ /**
+ * Return the string value of the current node.
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue();
+
+
+
+
+}
+
diff --git a/sf/saxon/tree/iter/AxisIteratorImpl.java b/sf/saxon/tree/iter/AxisIteratorImpl.java
new file mode 100644
index 0000000..cbe4eb7
--- /dev/null
+++ b/sf/saxon/tree/iter/AxisIteratorImpl.java
@@ -0,0 +1,114 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * A SequenceIterator is used to iterate over a sequence. An AxisIterator
+ * is a SequenceIterator that always iterates over a set of nodes, and that
+ * throws no exceptions; it also supports the ability
+ * to find the last() position, again with no exceptions.
+ * This class is an abstract implementation of AxisIterator that is used
+ * as a base class for many concrete implementations. The main functionality
+ * that it provides is maintaining the current position.
+ */
+
+public abstract class AxisIteratorImpl implements AxisIterator<NodeInfo> {
+
+ protected int position = 0;
+ /*@Nullable*/ protected NodeInfo current;
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return (next() != null);
+ }
+
+ /**
+ * Get the current node in the sequence.
+ * @return the node returned by the most recent call on next()
+ */
+
+ /*@Nullable*/ public NodeInfo current() {
+ return current;
+ }
+
+ /**
+ * Get the current position
+ * @return the position of the most recent node returned by next()
+ */
+
+ public final int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ //noinspection ConstantConditions
+ return current.iterateAxis(axis, test);
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ //noinspection ConstantConditions
+ return current.atomize();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ //noinspection ConstantConditions
+ return current.getStringValueCS();
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/AxisIteratorOverSequence.java b/sf/saxon/tree/iter/AxisIteratorOverSequence.java
new file mode 100644
index 0000000..8c0feb7
--- /dev/null
+++ b/sf/saxon/tree/iter/AxisIteratorOverSequence.java
@@ -0,0 +1,132 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class wraps any SequenceIterator as an AxisIterator. It must only be used if it
+ * is known that the underlying SequenceIterator will always return nodes, and will never
+ * throw an exception.
+ */
+
+public class AxisIteratorOverSequence<T extends NodeInfo> implements AxisIterator<T> {
+
+ private SequenceIterator<T> base;
+
+ public AxisIteratorOverSequence(SequenceIterator<T> base) {
+ this.base = base;
+ }
+
+ public T next() {
+ try {
+ return base.next();
+ } catch (XPathException err) {
+ throw new AssertionError(err);
+ }
+ }
+
+ /*@NotNull*/
+ public AxisIterator<T> getAnother() {
+ try {
+ return new AxisIteratorOverSequence<T>(base.getAnother());
+ } catch (XPathException err) {
+ throw new AssertionError(err);
+ }
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return (next() != null);
+ }
+
+ /**
+ * Get the current node in the sequence.
+ * @return the node returned by the most recent call on next()
+ */
+
+ /*@Nullable*/ public T current() {
+ return base.current();
+ }
+
+ /**
+ * Get the current position
+ * @return the position of the most recent node returned by next()
+ */
+
+ public final int position() {
+ return base.position();
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ //noinspection ConstantConditions
+ return current().iterateAxis(axis, test);
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ //noinspection ConstantConditions
+ return current().atomize();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ //noinspection ConstantConditions
+ return current().getStringValueCS();
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/EmptyAxisIterator.java b/sf/saxon/tree/iter/EmptyAxisIterator.java
new file mode 100644
index 0000000..8178d4f
--- /dev/null
+++ b/sf/saxon/tree/iter/EmptyAxisIterator.java
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NodeTest;
+
+/**
+* An AxisIterator over an empty sequence
+*/
+public class EmptyAxisIterator<T extends NodeInfo>
+ extends EmptyIterator<T> implements AxisIterator<T> {
+
+ /*@NotNull*/
+ private static EmptyAxisIterator<NodeInfo> theInstance =
+ new EmptyAxisIterator<NodeInfo>();
+
+
+ public static <T extends NodeInfo> net.sf.saxon.tree.iter.EmptyAxisIterator<T> emptyAxisIterator() {
+ return (EmptyAxisIterator<T>) theInstance;
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ * @throws NullPointerException if there is no current node
+ */
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ throw new NullPointerException();
+ }
+
+ /**
+ * Get another iterator over the same items, positioned at the start.
+ *
+ * @return another iterator over an empty sequence (in practice, it
+ * returns the same iterator each time)
+ */
+ /*@NotNull*/
+ @Override
+ public net.sf.saxon.tree.iter.EmptyAxisIterator<T> getAnother() {
+ return this;
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return false;
+ }
+}
+
diff --git a/sf/saxon/tree/iter/EmptyIterator.java b/sf/saxon/tree/iter/EmptyIterator.java
new file mode 100644
index 0000000..2d74fbf
--- /dev/null
+++ b/sf/saxon/tree/iter/EmptyIterator.java
@@ -0,0 +1,180 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.value.EmptySequence;
+
+/**
+ * EmptyIterator: an iterator over an empty sequence. Since such an iterator has no state,
+ * only one instance is required; therefore a singleton instance is available via the static
+ * getInstance() method.
+ */
+
+public class EmptyIterator<T extends Item> implements SequenceIterator<T>,
+ ReversibleIterator<T>, LastPositionFinder<T>, GroundedIterator<T>,
+ LookaheadIterator<T>, UnfailingIterator<T> {
+
+ /*@NotNull*/ private static EmptyIterator<Item> theInstance = new EmptyIterator<Item>();
+
+ /**
+ * Get an EmptyIterator, an iterator over an empty sequence.
+ * @return an EmptyIterator (in practice, this always returns the same
+ * one)
+ */
+ /*@NotNull*/ public static EmptyIterator getInstance() {
+ return theInstance;
+ }
+
+
+ public static <T extends Item> EmptyIterator<T> emptyIterator() {
+ return (EmptyIterator<T>) theInstance;
+ }
+
+ /**
+ * Protected constructor
+ */
+
+ protected EmptyIterator() {}
+
+
+
+
+ /**
+ * Get the next item.
+ * @return the next item. For the EmptyIterator this is always null.
+ */
+ /*@Nullable*/ public T next() {
+ return null;
+ }
+
+ /**
+ * Get the current item, that is, the item returned by the most recent call of next().
+ * @return the current item. For the EmptyIterator this is always null.
+ */
+ /*@Nullable*/ public T current() {
+ return null;
+ }
+
+ /**
+ * Get the position of the current item.
+ * @return the position of the current item. For the EmptyIterator this is always zero
+ * (whether or not the next() method has been called).
+ */
+ public int position() {
+ return 0;
+ }
+
+ /**
+ * Get the position of the last item in the sequence.
+ * @return the position of the last item in the sequence, always zero in
+ * this implementation
+ */
+ public int getLength() {
+ return 0;
+ }
+
+ public void close() {
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException always, because there is no current node
+ */
+
+ /*@NotNull*/ public Sequence atomize() {
+ throw new NullPointerException();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ /*@NotNull*/ public CharSequence getStringValue() {
+ throw new NullPointerException();
+ }
+
+ /**
+ * Get another iterator over the same items, positioned at the start.
+ * @return another iterator over an empty sequence (in practice, it
+ * returns the same iterator each time)
+ */
+ /*@NotNull*/ public EmptyIterator<T> getAnother() {
+ return this;
+ }
+
+ /**
+ * Indicate that any nodes returned in the sequence will be atomized. This
+ * means that if it wishes to do so, the implementation can return the typed
+ * values of the nodes rather than the nodes themselves. The implementation
+ * is free to ignore this hint.
+ * @param atomizing true if the caller of this iterator will atomize any
+ * nodes that are returned, and is therefore willing to accept the typed
+ * value of the nodes instead of the nodes themselves.
+ */
+
+ //public void setIsAtomizing(boolean atomizing) {}
+
+ /**
+ * Get another iterator over the same items, in reverse order.
+ * @return a reverse iterator over an empty sequence (in practice, it
+ * returns the same iterator each time)
+ */
+ /*@NotNull*/ public EmptyIterator<T> getReverseIterator() {
+ return this;
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return GROUNDED | LAST_POSITION_FINDER | LOOKAHEAD;
+ }
+
+ /**
+ * Return a Value containing all the items in the sequence returned by this
+ * SequenceIterator. This should be an "in-memory" value, not a Closure.
+ *
+ * @return the corresponding Value
+ */
+
+ public GroundedValue materialize() {
+ return EmptySequence.getInstance();
+ }
+
+ /**
+ * Determine whether there are more items to come. Note that this operation
+ * is stateless and it is not necessary (or usual) to call it before calling
+ * next(). It is used only when there is an explicit need to tell if we
+ * are at the last element.
+ *
+ * @return true if there are more nodes
+ */
+
+ public boolean hasNext() {
+ return false;
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/GroundedIterator.java b/sf/saxon/tree/iter/GroundedIterator.java
new file mode 100644
index 0000000..ac85de3
--- /dev/null
+++ b/sf/saxon/tree/iter/GroundedIterator.java
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This interface is an extension to the SequenceIterator interface; it represents
+ * a SequenceIterator that is based on an in-memory representation of a sequence,
+ * and that is therefore capable of returned a SequenceValue containing all the items
+ * in the sequence.
+ */
+
+public interface GroundedIterator<T extends Item> extends SequenceIterator<T> {
+
+ /**
+ * Return a GroundedValue containing all the items in the sequence returned by this
+ * SequenceIterator. This should be an "in-memory" value, not a Closure.
+ * @return the corresponding Value
+ */
+
+ public GroundedValue materialize() throws XPathException;
+}
+
diff --git a/sf/saxon/tree/iter/HomogeneityCheckerIterator.java b/sf/saxon/tree/iter/HomogeneityCheckerIterator.java
new file mode 100644
index 0000000..04ddfb0
--- /dev/null
+++ b/sf/saxon/tree/iter/HomogeneityCheckerIterator.java
@@ -0,0 +1,99 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.sort.DocumentOrderIterator;
+import net.sf.saxon.expr.sort.GlobalOrderComparer;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.transform.SourceLocator;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An iterator that returns the same items as its base iterator, checking to see that they are either
+ * all nodes, or all non-nodes; if they are all nodes, it delivers them in document order.
+ */
+
+public class HomogeneityCheckerIterator implements SequenceIterator {
+
+ /*@Nullable*/ SequenceIterator base = null;
+ SourceLocator loc;
+ int state;
+ // state = 0: initial state, will accept either nodes or atomic values
+ // state = +1: have seen a node, all further items must be nodes
+ // state = -1: have seen an atomic value or function item, all further items must be the same
+
+ public HomogeneityCheckerIterator(SequenceIterator base, SourceLocator loc) throws XPathException {
+ this.base = base;
+ this.loc = loc;
+ state = 0;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@Nullable*/ public Item current() {
+ return base.current();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ // if we've got two iterators over the items, it's enough for one of them to do the homogeneity checking
+ return base.getAnother();
+ }
+
+ /*@NotNull*/ private XPathException reportMixedItems() {
+ XPathException err = new XPathException("Cannot mix nodes and atomic values in the result of a path expression");
+ err.setErrorCode("XPTY0018");
+ err.setLocator(loc);
+ return err;
+ }
+
+ public int getProperties() {
+ return 0;
+ }
+
+ /*@Nullable*/ public Item next() throws XPathException {
+ Item item = base.next();
+ if (item == null) {
+ return null;
+ }
+ //first item in iterator
+ if (state == 0) {
+ if (item instanceof NodeInfo) {
+ List<Item> nodes = new ArrayList<Item>(50);
+ nodes.add(item);
+ while ((item = base.next()) != null) {
+ if (!(item instanceof NodeInfo)) {
+ throw reportMixedItems();
+ } else {
+ nodes.add(item);
+ }
+ }
+ base = new DocumentOrderIterator(new ListIterator(nodes), GlobalOrderComparer.getInstance());
+ state = 1; // first item is a node
+ return base.next();
+ } else {
+ state = -1; // first item is an atomic value or function item
+ }
+ } else if (state == -1 && item instanceof NodeInfo) {
+ throw reportMixedItems();
+ }
+ return item;
+ }
+
+ public int position() {
+ return base.position();
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/tree/iter/IteratorIterator.java b/sf/saxon/tree/iter/IteratorIterator.java
new file mode 100644
index 0000000..a638a14
--- /dev/null
+++ b/sf/saxon/tree/iter/IteratorIterator.java
@@ -0,0 +1,83 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.Item;
+
+import java.util.Iterator;
+
+/**
+* A SequenceIterator that wraps a Java Iterator. This is an abstract class, because the Java
+ * iterator does not hold enough information to support the getAnother() method, needed to
+ * implement the XPath last() function
+*/
+
+public abstract class IteratorIterator<T extends Item>
+ implements LookaheadIterator<T>, UnfailingIterator<T> {
+
+ private int position = 0;
+ /*@Nullable*/ private T current = null;
+ private Iterator<T> base;
+
+ /**
+ * Create a SequenceIterator over a given iterator
+ * @param base the base Iterator
+ */
+
+ public IteratorIterator(Iterator<T> base) {
+ this.base = base;
+ position = 0;
+ current = null;
+ }
+
+
+
+ public boolean hasNext() {
+ return base.hasNext();
+ }
+
+ /*@Nullable*/ public T next() {
+ if (base.hasNext()) {
+ current = base.next();
+ position++;
+ } else {
+ current = null;
+ position = -1;
+ }
+ return current;
+ }
+
+ /*@Nullable*/ public T current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/ public abstract UnfailingIterator<T> getAnother();
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/ListIterator.java b/sf/saxon/tree/iter/ListIterator.java
new file mode 100644
index 0000000..5ecf475
--- /dev/null
+++ b/sf/saxon/tree/iter/ListIterator.java
@@ -0,0 +1,126 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.SequenceExtent;
+import net.sf.saxon.value.SingletonItem;
+
+import java.util.List;
+
+/**
+ * Class ListIterator, iterates over a sequence of items held in a Java List
+*/
+
+public class ListIterator<T extends Item>
+ implements UnfailingIterator<T>, LastPositionFinder<T>, LookaheadIterator<T>, GroundedIterator<T> {
+
+ int index=0;
+ int length;
+ /*@Nullable*/ T current = null;
+ /*@NotNull*/ List<T> list;
+
+ /**
+ * Create a ListIterator over a given List
+ * @param list the list: all objects in the list must be instances of {@link Item}
+ */
+
+ public ListIterator(/*@NotNull*/ List<T> list) {
+ index = 0;
+ this.list = list;
+ this.length = list.size();
+ }
+
+ /**
+ * Create a ListIterator over the leading part of a given List
+ * @param list the list: all objects in the list must be instances of {@link Item}
+ * @param length the number of items to be included
+ */
+
+ public ListIterator(List<T> list, int length) {
+ index = 0;
+ this.list = list;
+ this.length = length;
+ }
+
+ public boolean hasNext() {
+ return index<length;
+ }
+
+ /*@Nullable*/ public T next() {
+ if (index >= length) {
+ current = null;
+ index = -1;
+ length = -1;
+ return null;
+ }
+ current = list.get(index++);
+ return current;
+ }
+
+ /*@Nullable*/ public T current() {
+ return current;
+ }
+
+ public int position() {
+ return index;
+ }
+
+ public void close() {
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ /*@NotNull*/
+ public ListIterator<T> getAnother() {
+ return new ListIterator<T>(list);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return GROUNDED | LAST_POSITION_FINDER | LOOKAHEAD;
+ }
+
+ /**
+ * Return a SequenceValue containing all the items in the sequence returned by this
+ * SequenceIterator
+ *
+ * @return the corresponding SequenceValue
+ */
+
+ /*@Nullable*/ public GroundedValue materialize() {
+ if (length == 0) {
+ return EmptySequence.getInstance();
+ } else if (length == 1) {
+ T item = list.get(0);
+ if (item instanceof GroundedValue) {
+ return (GroundedValue)item;
+ } else {
+ return new SingletonItem<T>(item);
+ }
+ } else {
+ return new SequenceExtent<T>(list);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/LookaheadIterator.java b/sf/saxon/tree/iter/LookaheadIterator.java
new file mode 100644
index 0000000..d614afc
--- /dev/null
+++ b/sf/saxon/tree/iter/LookaheadIterator.java
@@ -0,0 +1,38 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+
+
+/**
+ * A SequenceIterator is used to iterate over a sequence. A LookaheadIterator
+ * is one that supports a hasNext() method to determine if there are more nodes
+ * after the current node.
+ */
+
+public interface LookaheadIterator<T extends Item> extends SequenceIterator<T> {
+
+ /**
+ * Determine whether there are more items to come. Note that this operation
+ * is stateless and it is not necessary (or usual) to call it before calling
+ * next(). It is used only when there is an explicit need to tell if we
+ * are at the last element.
+ * <p/>
+ * This method must not be called unless the result of getProperties() on the iterator
+ * includes the bit setting {@link SequenceIterator#LOOKAHEAD}
+ *
+ * @return true if there are more items in the sequence
+ */
+
+ public boolean hasNext();
+
+
+}
+
diff --git a/sf/saxon/tree/iter/LookaheadIteratorImpl.java b/sf/saxon/tree/iter/LookaheadIteratorImpl.java
new file mode 100644
index 0000000..1dbf61e
--- /dev/null
+++ b/sf/saxon/tree/iter/LookaheadIteratorImpl.java
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * This class wraps any sequence iterator, turning it into a lookahead iterator,
+ * by looking ahead one item
+ */
+public class LookaheadIteratorImpl implements LookaheadIterator {
+
+ private SequenceIterator base;
+ /*@Nullable*/ private Item current;
+ /*@Nullable*/ private Item next;
+
+ private LookaheadIteratorImpl(/*@NotNull*/ SequenceIterator base) throws XPathException {
+ this.base = base;
+ next = base.next();
+ }
+
+ /*@NotNull*/ public static LookaheadIterator makeLookaheadIterator(/*@NotNull*/ SequenceIterator base) throws XPathException {
+ if ((base.getProperties() & SequenceIterator.LOOKAHEAD) != 0) {
+ return (LookaheadIterator)base;
+ } else {
+ return new LookaheadIteratorImpl(base);
+ }
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ /*@Nullable*/ public Item next() throws XPathException {
+ current = next;
+ if (next != null) {
+ next = base.next();
+ }
+ return current;
+ }
+
+ /*@Nullable*/ public Item current() {
+ return current;
+ }
+
+ public int position() {
+ int p = base.position();
+ return (p <= 0 ? p : p-1);
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator getAnother() throws XPathException {
+ return new LookaheadIteratorImpl(base.getAnother());
+ }
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+}
+
diff --git a/sf/saxon/tree/iter/ManualIterator.java b/sf/saxon/tree/iter/ManualIterator.java
new file mode 100644
index 0000000..c5cb1c4
--- /dev/null
+++ b/sf/saxon/tree/iter/ManualIterator.java
@@ -0,0 +1,137 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SingletonItem;
+
+
+/**
+ * ManualIterator: a pseudo-iterator used while streaming. It has a current node and a current position
+ * which are set manually. Calling last() is an error. Calling next() always returns null.
+*/
+
+public class ManualIterator implements UnfailingIterator,
+ ReversibleIterator, LastPositionFinder, GroundedIterator, LookaheadIterator {
+
+ private Item item;
+ private int position;
+ private LastPositionFinder lastPositionFinder;
+
+ public ManualIterator() {
+ item = null;
+ position = 0;
+ }
+
+ public ManualIterator(Item value, int position) {
+ this.item = value;
+ this.position = position;
+ }
+
+ public void setContextItem(Item value) {
+ this.item = value;
+ }
+
+ public void setLastPositionFinder(LastPositionFinder finder) {
+ this.lastPositionFinder = finder;
+ }
+
+ public void incrementPosition() {
+ position++;
+ }
+
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ /**
+ * Determine whether there are more items to come. Note that this operation
+ * is stateless and it is not necessary (or usual) to call it before calling
+ * next(). It is used only when there is an explicit need to tell if we
+ * are at the last element.
+ *
+ * @return true if there are more items
+ */
+
+ public boolean hasNext() {
+ return false;
+ }
+
+ public Item next() {
+ return null;
+ }
+
+ public Item current() {
+ return item;
+ }
+
+ /**
+ * Return the current position in the sequence.
+ * @return 0 before the first call on next(); 1 before the second call on next(); -1 after the second
+ * call on next().
+ */
+ public int position() {
+ return position;
+ }
+
+ public int getLength() throws XPathException {
+ if (lastPositionFinder == null) {
+ throw new UnsupportedOperationException("last() cannot be used when streaming");
+ } else {
+ return lastPositionFinder.getLength();
+ }
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/
+ public UnfailingIterator getAnother() {
+ return new ManualIterator(item, 1);
+ }
+
+ public UnfailingIterator getReverseIterator() {
+ return new ManualIterator(item, 1);
+ }
+
+ /**
+ * Return a Value containing all the items in the sequence returned by this
+ * SequenceIterator
+ *
+ * @return the corresponding Value. If the value is a closure or a function call package, it will be
+ * evaluated and expanded.
+ */
+
+ /*@Nullable*/ public GroundedValue materialize() {
+ if (item instanceof GroundedValue) {
+ return (GroundedValue)item;
+ } else {
+ return new SingletonItem(item);
+ }
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return GROUNDED | LAST_POSITION_FINDER | LOOKAHEAD;
+ }
+
+}
+
+// Copyright (c) 2009 Saxonica Limited. All rights reserved.
\ No newline at end of file
diff --git a/sf/saxon/tree/iter/NodeWrappingAxisIterator.java b/sf/saxon/tree/iter/NodeWrappingAxisIterator.java
new file mode 100644
index 0000000..223b775
--- /dev/null
+++ b/sf/saxon/tree/iter/NodeWrappingAxisIterator.java
@@ -0,0 +1,122 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+
+import java.util.Iterator;
+
+/**
+ * An AxisIterator that wraps a Java Iterator. This is an abstract class, because the Java
+ * iterator does not hold enough information to support the getAnother() method, needed to
+ * implement the XPath last() function
+*/
+
+public abstract class NodeWrappingAxisIterator<T extends NodeInfo, B>
+ implements AxisIterator<T>, LookaheadIterator<T>, UnfailingIterator<T> {
+
+
+ private int position = 0;
+ /*@Nullable*/ private T current = null;
+ private Iterator<B> base;
+ private NodeWrappingFunction<B, T> wrappingFunction ;
+
+
+ /**
+ * Create a SequenceIterator over a given iterator
+ * @param base the base Iterator
+ * @param wrappingFunction a function that wraps objects of type B in a Saxon NodeInfo
+ */
+
+ public NodeWrappingAxisIterator(Iterator<B> base, NodeWrappingFunction<B, T> wrappingFunction) {
+ this.base = base;
+ position = 0;
+ current = null;
+ this.wrappingFunction = wrappingFunction;
+ }
+
+ public Iterator<B> getBaseIterator() {
+ return base;
+ }
+
+ public NodeWrappingFunction<B, T> getNodeWrappingFunction() {
+ return wrappingFunction;
+ }
+
+ public boolean moveNext() {
+ return next() != null;
+ }
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ return current.iterateAxis(axis, test);
+ }
+
+ public Sequence atomize() throws XPathException {
+ return current.atomize();
+ }
+
+ public CharSequence getStringValue() {
+ return current.getStringValue();
+ }
+
+
+
+ public boolean hasNext() {
+ return base.hasNext();
+ }
+
+ /*@Nullable*/ public T next() {
+ while (base.hasNext()) {
+ B next = base.next();
+ if (!isIgnorable(next)) {
+ current = wrappingFunction.wrap(next);
+ position++;
+ return current;
+ }
+ }
+ current = null;
+ position = -1;
+ return current;
+ }
+
+ /*@Nullable*/ public T current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ public boolean isIgnorable(B node) {
+ return false;
+ }
+
+ /*@NotNull*/ public abstract AxisIterator<T> getAnother();
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/NodeWrappingFunction.java b/sf/saxon/tree/iter/NodeWrappingFunction.java
new file mode 100644
index 0000000..2d8f872
--- /dev/null
+++ b/sf/saxon/tree/iter/NodeWrappingFunction.java
@@ -0,0 +1,24 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.NodeInfo;
+
+/**
+ * Interface to a function that wraps nodes from an external object model in a Saxon NodeInfo
+ * representation
+ * @param <B> the type of the node in the external object model
+ * @param <T> the type of the Saxon node
+ *
+ */
+
+public interface NodeWrappingFunction<B, T extends NodeInfo> {
+
+ public T wrap(B node);
+}
+
diff --git a/sf/saxon/tree/iter/PrependIterator.java b/sf/saxon/tree/iter/PrependIterator.java
new file mode 100644
index 0000000..26b71ec
--- /dev/null
+++ b/sf/saxon/tree/iter/PrependIterator.java
@@ -0,0 +1,158 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * An iterator over nodes, that prepends a given node to the nodes
+ * returned by another iterator. Used to modify an iterator over axis A
+ * to one that iterates over A-OR-SELF.
+ */
+
+public class PrependIterator implements AxisIterator {
+
+ NodeInfo start;
+ AxisIterator base;
+ int position = 0;
+
+ public PrependIterator(NodeInfo start, AxisIterator base) {
+ this.start = start;
+ this.base = base;
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return (next() != null);
+ }
+
+
+ /**
+ * Get the next item in the sequence. <BR>
+ *
+ * @return the next Item. If there are no more nodes, return null.
+ */
+
+ /*@Nullable*/ public NodeInfo next() {
+ if (position == 0) {
+ position = 1;
+ return start;
+ }
+ NodeInfo n = base.next();
+ if (n == null) {
+ position = -1;
+ } else {
+ position++;
+ }
+ return n;
+ }
+
+ /**
+ * Get the current item in the sequence.
+ *
+ * @return the current item, that is, the item most recently returned by
+ * next()
+ */
+
+ /*@Nullable*/ public NodeInfo current() {
+ if (position() == 1) {
+ return start;
+ } else if (position < 1) {
+ return null;
+ } else {
+ return base.current();
+ }
+ }
+
+ /**
+ * Get the current position
+ *
+ * @return the position of the current item (the item most recently
+ * returned by next()), starting at 1 for the first node
+ */
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ return current().iterateAxis(axis, test);
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ return current().atomize();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ return ((NodeInfo)current()).getStringValueCS();
+ }
+
+ /**
+ * Get another iterator over the same sequence of items, positioned at the
+ * start of the sequence
+ *
+ * @return a new iterator over the same sequence
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new PrependIterator(start, base);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+
+}
+
diff --git a/sf/saxon/tree/iter/ReverseArrayIterator.java b/sf/saxon/tree/iter/ReverseArrayIterator.java
new file mode 100644
index 0000000..5c84d3b
--- /dev/null
+++ b/sf/saxon/tree/iter/ReverseArrayIterator.java
@@ -0,0 +1,117 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+
+
+/**
+ * ReverseArrayIterator is used to enumerate items held in an array in reverse order.
+ * @author Michael H. Kay
+ */
+
+
+public class ReverseArrayIterator<T extends Item> implements UnfailingIterator<T>,
+ ReversibleIterator<T>, LookaheadIterator<T>, LastPositionFinder<T> {
+
+ T[] items;
+ int index = 0;
+ int start;
+ int end; // item after the last to be output
+ /*@Nullable*/ T current = null;
+
+ /**
+ * Create an iterator a slice of an array
+ * @param items The array of items
+ * @param start The first item in the array to be be used (this will be the last
+ * one in the resulting iteration). Zero-based.
+ * @param end The item after the last one in the array to be used (this will be the
+ * first one to be returned by the iterator). Zero-based.
+ */
+
+ public ReverseArrayIterator(T[] items, int start, int end) {
+ this.items = items;
+ this.end = end;
+ this.start = start;
+ index = end - 1;
+ }
+
+ /**
+ * Determine whether there are more items to come. Note that this operation
+ * is stateless and it is not necessary (or usual) to call it before calling
+ * next(). It is used only when there is an explicit need to tell if we
+ * are at the last element.
+ *
+ * @return true if there are more items in the sequence
+ */
+
+ public boolean hasNext() {
+ return index >= start;
+ }
+
+ /*@Nullable*/ public T next() {
+ if (index >= start) {
+ current = items[index--];
+ return current;
+ } else {
+ current = null;
+ return null;
+ }
+ }
+
+ /*@Nullable*/ public T current() {
+ return current;
+ }
+
+ public int position() {
+ if (index < start-1) {
+ return -1; // position() returns -1 after next() returns null
+ }
+ return end - 1 - index;
+ }
+
+ public int getLength() {
+ return end - start;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/
+ public ReverseArrayIterator<T> getAnother() {
+ return new ReverseArrayIterator<T>(items, start, end);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LAST_POSITION_FINDER;
+ }
+
+ /**
+ * Get an iterator that processes the same items in reverse order.
+ * Since this iterator is processing the items backwards, this method
+ * returns an ArrayIterator that processes them forwards.
+ * @return a new ArrayIterator
+ */
+
+ public SequenceIterator<T> getReverseIterator() {
+ return new ArrayIterator<T>(items, start, end);
+ }
+}
+
diff --git a/sf/saxon/tree/iter/ReversibleIterator.java b/sf/saxon/tree/iter/ReversibleIterator.java
new file mode 100644
index 0000000..78828a1
--- /dev/null
+++ b/sf/saxon/tree/iter/ReversibleIterator.java
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+
+
+/**
+* A ReversibleIterator is an interface implemented by any SequenceIterator that is
+ * able to deliver items in reverse order (or to supply another iterator that can
+ * do so).
+*/
+
+public interface ReversibleIterator<T extends Item> extends SequenceIterator<T> {
+
+ /**
+ * Get a new SequenceIterator that returns the same items in reverse order.
+ * If this SequenceIterator is an AxisIterator, then the returned SequenceIterator
+ * must also be an AxisIterator.
+ * @return an iterator over the items in reverse order
+ */
+
+ public SequenceIterator<T> getReverseIterator();
+
+}
+
diff --git a/sf/saxon/tree/iter/SingleNodeIterator.java b/sf/saxon/tree/iter/SingleNodeIterator.java
new file mode 100644
index 0000000..75e2fed
--- /dev/null
+++ b/sf/saxon/tree/iter/SingleNodeIterator.java
@@ -0,0 +1,198 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SingletonItem;
+
+
+/**
+* SingletonIterator: an iterator over a sequence of zero or one values
+*/
+
+public class SingleNodeIterator implements AxisIterator, UnfailingIterator,
+ ReversibleIterator, LastPositionFinder, GroundedIterator, LookaheadIterator {
+
+ private NodeInfo item;
+ private int position = 0;
+
+ /**
+ * Private constructor: external classes should use the factory method
+ * @param value the item to iterate over
+ */
+
+ private SingleNodeIterator(NodeInfo value) {
+ this.item = value;
+ }
+
+ /**
+ * Factory method.
+ * @param item the item to iterate over
+ * @return a SingletonIterator over the supplied item, or an EmptyIterator
+ * if the supplied item is null.
+ */
+
+ /*@NotNull*/ public static AxisIterator makeIterator(/*@Nullable*/ NodeInfo item) {
+ if (item==null) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ } else {
+ return new SingleNodeIterator(item);
+ }
+ }
+
+ /**
+ * Determine whether there are more items to come. Note that this operation
+ * is stateless and it is not necessary (or usual) to call it before calling
+ * next(). It is used only when there is an explicit need to tell if we
+ * are at the last element.
+ *
+ * @return true if there are more items
+ */
+
+ public boolean hasNext() {
+ return position == 0;
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return next() != null;
+ }
+
+ /*@Nullable*/ public NodeInfo next() {
+ if (position == 0) {
+ position = 1;
+ return item;
+ } else if (position == 1) {
+ position = -1;
+ return null;
+ } else {
+ return null;
+ }
+ }
+
+ /*@Nullable*/ public NodeInfo current() {
+ if (position == 1) {
+ return item;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return the current position in the sequence.
+ * @return 0 before the first call on next(); 1 before the second call on next(); -1 after the second
+ * call on next().
+ */
+ public int position() {
+ return position;
+ }
+
+ public int getLength() {
+ return 1;
+ }
+
+ public void close() {
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ if (position == 1) {
+ return item.iterateAxis(axis, test);
+ } else {
+ throw new NullPointerException();
+ }
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ if (position == 1) {
+ return item.atomize();
+ } else {
+ throw new NullPointerException();
+ }
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ if (position == 1) {
+ return item.getStringValueCS();
+ } else {
+ throw new NullPointerException();
+ }
+ }
+
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new SingleNodeIterator(item);
+ }
+
+ /*@NotNull*/ public SequenceIterator getReverseIterator() {
+ return new SingleNodeIterator(item);
+ }
+
+ public Item getValue() {
+ return item;
+ }
+
+ /**
+ * Return a Value containing all the items in the sequence returned by this
+ * SequenceIterator
+ *
+ * @return the corresponding Value. If the value is a closure or a function call package, it will be
+ * evaluated and expanded.
+ */
+
+ /*@NotNull*/ public GroundedValue materialize() {
+ return new SingletonItem(item);
+ }
+
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return GROUNDED | LAST_POSITION_FINDER | LOOKAHEAD;
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/SingletonIterator.java b/sf/saxon/tree/iter/SingletonIterator.java
new file mode 100644
index 0000000..6a0efc9
--- /dev/null
+++ b/sf/saxon/tree/iter/SingletonIterator.java
@@ -0,0 +1,143 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.value.SingletonItem;
+
+
+/**
+* SingletonIterator: an iterator over a sequence of zero or one values
+*/
+
+public class SingletonIterator<T extends Item> implements SequenceIterator<T>, UnfailingIterator<T>,
+ ReversibleIterator<T>, LastPositionFinder<T>, GroundedIterator<T>, LookaheadIterator<T> {
+
+ private T item;
+ private int position = 0;
+
+ /**
+ * Private constructor: external classes should use the factory method
+ * @param value the item to iterate over
+ */
+
+ private SingletonIterator(T value) {
+ this.item = value;
+ }
+
+ /**
+ * Factory method.
+ * @param item the item to iterate over
+ * @return a SingletonIterator over the supplied item, or an EmptyIterator
+ * if the supplied item is null.
+ */
+
+ /*@NotNull*/ public static <T extends Item> UnfailingIterator<T> makeIterator(/*@Nullable*/ T item) {
+ if (item==null) {
+ return EmptyIterator.emptyIterator();
+ } else {
+ return new SingletonIterator<T>(item);
+ }
+ }
+
+ /**
+ * Determine whether there are more items to come. Note that this operation
+ * is stateless and it is not necessary (or usual) to call it before calling
+ * next(). It is used only when there is an explicit need to tell if we
+ * are at the last element.
+ *
+ * @return true if there are more items
+ */
+
+ public boolean hasNext() {
+ return position == 0;
+ }
+
+ /*@Nullable*/ public T next() {
+ if (position == 0) {
+ position = 1;
+ return item;
+ } else if (position == 1) {
+ position = -1;
+ return null;
+ } else {
+ return null;
+ }
+ }
+
+ /*@Nullable*/ public T current() {
+ if (position == 1) {
+ return item;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return the current position in the sequence.
+ * @return 0 before the first call on next(); 1 before the second call on next(); -1 after the second
+ * call on next().
+ */
+ public int position() {
+ return position;
+ }
+
+ public int getLength() {
+ return 1;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/ public SingletonIterator<T> getAnother() {
+ return new SingletonIterator<T>(item);
+ }
+
+ /*@NotNull*/ public SingletonIterator<T> getReverseIterator() {
+ return new SingletonIterator<T>(item);
+ }
+
+ public T getValue() {
+ return item;
+ }
+
+ /**
+ * Return a Value containing all the items in the sequence returned by this
+ * SequenceIterator
+ *
+ * @return the corresponding Value. If the value is a closure or a function call package, it will be
+ * evaluated and expanded.
+ */
+
+ /*@NotNull*/ public GroundedValue materialize() {
+ if (item instanceof GroundedValue) {
+ return (GroundedValue)item;
+ } else {
+ return new SingletonItem<T>(item);
+ }
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return GROUNDED | LAST_POSITION_FINDER | LOOKAHEAD;
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/TextLinesIterator.java b/sf/saxon/tree/iter/TextLinesIterator.java
new file mode 100644
index 0000000..b938d31
--- /dev/null
+++ b/sf/saxon/tree/iter/TextLinesIterator.java
@@ -0,0 +1,143 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.functions.UnparsedText;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+import javax.xml.transform.SourceLocator;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.net.URI;
+
+/**
+ * An iterator that iterates over a file line by line. It is abstract because it does not supply the
+ * getAnother() method
+*/
+public abstract class TextLinesIterator implements SequenceIterator<StringValue> {
+
+ protected LineNumberReader reader;
+ protected NameChecker checker;
+ StringValue current = null;
+ int position = 0;
+ protected SourceLocator location;
+ protected URI uri;
+
+ protected TextLinesIterator() {
+
+ }
+
+ /**
+ * Create a TextLinesIterator over a given reader
+ * @param reader the reader that reads the file
+ * @param checker checks that the characters in the file are legal XML characters
+ * @param location the location of the instruction being executed, for diagnostics
+ * @param uri the URI of the file being read, for diagnostics
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ public TextLinesIterator(LineNumberReader reader, SourceLocator location, URI uri, NameChecker checker) throws XPathException {
+ this.reader = reader;
+ this.location = location;
+ this.uri = uri;
+ this.checker = checker;
+ }
+
+// public TextLinesIterator(File file, String encoding) throws IOException {
+// this.file = file;
+// this.encoding = encoding;
+// this.reader = new LineNumberReader(new InputStreamReader(new FileInputStream(file), encoding));
+// this.checker = Name11Checker.getInstance();
+// }
+
+ /*@Nullable*/ public StringValue next() throws XPathException {
+ if (position < 0) {
+ // input already exhausted
+ return null;
+ }
+ try {
+ String s = reader.readLine();
+ if (s == null) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ if (position == 0 && s.startsWith("\ufeff")) {
+ // remove any BOM found at start of file
+ s = s.substring(1);
+ }
+ checkLine(checker, s);
+ current = new StringValue(s);
+ position++;
+ return current;
+ } catch (IOException err) {
+ XPathException e = UnparsedText.handleIOError(uri, err, null);
+ if (location != null) {
+ e.setLocator(location);
+ }
+ throw e;
+// } catch (Exception err) {
+// XPathException e = new XPathException(err.getMessage(), "XPST0001");
+// if (location != null) {
+// e.setLocator(location);
+// }
+// throw e;
+ }
+ }
+
+ /**
+ * The current line in the file
+ * @return returns StringValue: the current line in the file
+ */
+ /*@Nullable*/ public StringValue current() {
+ return current;
+ }
+
+ /**
+ * The line position currently being read
+ * @return returns the current line in the file
+ */
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ try {
+ reader.close();
+ } catch (IOException err) {
+ //
+ }
+ }
+
+
+ public int getProperties() {
+ return 0;
+ }
+
+ private void checkLine(/*@NotNull*/ NameChecker checker, /*@NotNull*/ String buffer) throws XPathException {
+ for (int c=0; c<buffer.length();) {
+ int ch32 = buffer.charAt(c++);
+ if (UTF16CharacterSet.isHighSurrogate(ch32)) {
+ char low = buffer.charAt(c++);
+ ch32 = UTF16CharacterSet.combinePair((char)ch32, low);
+ }
+ if (!checker.isValidChar(ch32)) {
+ XPathException err = new XPathException("The unparsed-text file contains a character that is illegal in XML (line=" +
+ position + " column=" + (c+1) + " value=hex " + Integer.toHexString(ch32) + ')');
+ err.setErrorCode("XTDE1190");
+ err.setLocator(location);
+ throw err;
+ }
+ }
+ }
+}
+
diff --git a/sf/saxon/tree/iter/UnfailingIterator.java b/sf/saxon/tree/iter/UnfailingIterator.java
new file mode 100644
index 0000000..a2e0433
--- /dev/null
+++ b/sf/saxon/tree/iter/UnfailingIterator.java
@@ -0,0 +1,58 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+
+
+/**
+ * A SequenceIterator is used to iterate over a sequence. An UnfailingIterator
+ * is a SequenceIterator that throws no checked exceptions.
+ */
+
+public interface UnfailingIterator<T extends Item> extends SequenceIterator<T> {
+
+ /**
+ * Get the next item in the sequence. <BR>
+ * @return the next Item. If there are no more nodes, return null.
+ */
+
+ /*@Nullable*/ T next();
+
+ /**
+ * Get the current item in the sequence.
+ *
+ * @return the current item, that is, the item most recently returned by
+ * next()
+ */
+
+ /*@Nullable*/ T current();
+
+ /**
+ * Get the current position
+ *
+ * @return the position of the current item (the item most recently
+ * returned by next()), starting at 1 for the first node
+ */
+
+ int position();
+
+ /**
+ * Get another iterator over the same sequence of items, positioned at the
+ * start of the sequence. It must be possible to call this method at any time, whether
+ * none, some, or all of the items in the original iterator have been read. The method
+ * is non-destructive: it does not change the state of the original iterator.
+ * @return a new iterator over the same sequence
+ */
+
+ /*@NotNull*/
+ UnfailingIterator<T> getAnother();
+
+}
+
diff --git a/sf/saxon/tree/iter/UnparsedTextIterator.java b/sf/saxon/tree/iter/UnparsedTextIterator.java
new file mode 100644
index 0000000..19020fc
--- /dev/null
+++ b/sf/saxon/tree/iter/UnparsedTextIterator.java
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.StringValue;
+
+import javax.xml.transform.SourceLocator;
+import java.io.LineNumberReader;
+import java.io.Reader;
+import java.net.URI;
+
+/**
+* Class UnparsedTextIterator, iterates over a file line by line
+*/
+public class UnparsedTextIterator extends TextLinesIterator {
+
+ XPathContext context;
+ String encoding = null;
+
+ /**
+ * Create a UnparsedTextIterator over a given file
+ * @param absoluteURI the URI identifying the file
+ * @param context the dynamic evaluation context
+ * @param encoding the expected encoding of the file
+ * @param location the location of the instruction being executed
+ * @throws XPathException if a dynamic error occurs
+ */
+
+ public UnparsedTextIterator(URI absoluteURI, /*@NotNull*/ XPathContext context, String encoding, SourceLocator location) throws XPathException {
+ Configuration config = context.getConfiguration();
+ Reader reader = context.getController().getUnparsedTextURIResolver().resolve(absoluteURI, encoding, config);
+ // TODO: does LineNumberReader use the correct definition of line endings?
+
+ this.reader = new LineNumberReader(reader);
+ this.uri = absoluteURI;
+ this.context = context;
+ this.checker = context.getConfiguration().getNameChecker();
+ this.encoding = encoding;
+ this.location = location;
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<StringValue> getAnother() throws XPathException {
+ return new UnparsedTextIterator(uri, context, encoding, location);
+ }
+
+}
+
diff --git a/sf/saxon/tree/iter/UntypedAtomizingIterator.java b/sf/saxon/tree/iter/UntypedAtomizingIterator.java
new file mode 100644
index 0000000..e7deee2
--- /dev/null
+++ b/sf/saxon/tree/iter/UntypedAtomizingIterator.java
@@ -0,0 +1,108 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.iter;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.*;
+
+/**
+ * AtomizingIterator returns the atomization of an underlying sequence supplied
+ * as an iterator. We use a specialist class rather than a general-purpose
+ * MappingIterator for performance, especially as the relationship of items
+ * in the result sequence to those in the base sequence is often one-to-one.
+ *
+ * This UntypedAtomizingIterator is used only when it is known that all nodes
+ * will be untyped, and that atomizing a node therefore always returns a singleton.
+ * However, it is not necessarily the case that the input sequence contains only
+ * nodes, and therefore the result sequence may contains atomic values that are
+ * not untyped.
+ *
+ * The parameter type B denotes the type of the items being atomized.
+*/
+
+public class UntypedAtomizingIterator<B extends Item> implements SequenceIterator<AtomicValue>,
+ LastPositionFinder<AtomicValue>, LookaheadIterator<AtomicValue> {
+
+ private SequenceIterator<B> base;
+ /*@Nullable*/ private AtomicValue current = null;
+ private int position = 0;
+
+ /**
+ * Construct an AtomizingIterator that will atomize the values returned by the base iterator.
+ * @param base the base iterator
+ */
+
+ public UntypedAtomizingIterator(SequenceIterator<B> base) {
+ this.base = base;
+ }
+
+ /*@Nullable*/ public AtomicValue next() throws XPathException {
+ Item nextSource = base.next();
+ if (nextSource != null) {
+ if (nextSource instanceof NodeInfo) {
+ current = (AtomicValue)((NodeInfo)nextSource).atomize();
+ position++;
+ return current;
+ } else if (nextSource instanceof AtomicValue) {
+ return (AtomicValue)nextSource;
+ } else if (nextSource instanceof ObjectValue) {
+ return StringValue.makeStringValue(nextSource.getStringValue());
+ } else {
+ throw new XPathException("The typed value of a function item is not defined", "FOTY0013");
+ }
+ } else {
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+
+ /*@Nullable*/ public AtomicValue current() {
+ return current;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /*@NotNull*/
+ public SequenceIterator<AtomicValue> getAnother() throws XPathException {
+ return new UntypedAtomizingIterator<B>(base.getAnother());
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link net.sf.saxon.om.SequenceIterator#GROUNDED}, {@link net.sf.saxon.om.SequenceIterator#LAST_POSITION_FINDER},
+ * and {@link net.sf.saxon.om.SequenceIterator#LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return base.getProperties() & (SequenceIterator.LAST_POSITION_FINDER | SequenceIterator.LOOKAHEAD);
+ }
+
+ public int getLength() throws XPathException {
+ return ((LastPositionFinder)base).getLength();
+ }
+
+ public boolean hasNext() {
+ return ((LookaheadIterator)base).hasNext();
+ }
+}
+
diff --git a/sf/saxon/tree/iter/package.html b/sf/saxon/tree/iter/package.html
new file mode 100644
index 0000000..bf483c6
--- /dev/null
+++ b/sf/saxon/tree/iter/package.html
@@ -0,0 +1,33 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.tree.iter</title>
+
+</head>
+ <body>
+ <p>This package defines implementations and subinterfaces of the interface SequenceIterator, which is
+ used to iterate over an XDM sequence.</p>
+
+ <p>The subinterfaces mostly represent iterators with additional capability: for example a LastPositionFinder
+ can determine the number of items in the sequence without reading to the end; a GroundedIterator can deliver
+ the original sequence as a list in memory; a LookaheadIterator is capable of one-item look-ahead. Note
+ that just because a class implements such an interface does not mean it necessarily has this capability;
+ it is necessary to check the properties of the specific iterator before assuming this.</p>
+
+
+
+
+ <p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+28 November 2011</i></p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/tree/linked/AncestorEnumeration.java b/sf/saxon/tree/linked/AncestorEnumeration.java
new file mode 100644
index 0000000..d183875
--- /dev/null
+++ b/sf/saxon/tree/linked/AncestorEnumeration.java
@@ -0,0 +1,37 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+
+final class AncestorEnumeration extends TreeEnumeration {
+
+ private boolean includeSelf;
+
+ public AncestorEnumeration(NodeImpl node, NodeTest nodeTest, boolean includeSelf) {
+ super(node, nodeTest);
+ this.includeSelf = includeSelf;
+ if (!includeSelf || !conforms(node)) {
+ advance();
+ }
+ }
+
+ protected void step() {
+ next = next.getParent();
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new AncestorEnumeration(start, nodeTest, includeSelf);
+ }
+}
+
diff --git a/sf/saxon/tree/linked/AttributeEnumeration.java b/sf/saxon/tree/linked/AttributeEnumeration.java
new file mode 100644
index 0000000..05b9858
--- /dev/null
+++ b/sf/saxon/tree/linked/AttributeEnumeration.java
@@ -0,0 +1,145 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.om.AttributeCollection;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorImpl;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.type.Type;
+
+/**
+* AttributeEnumeration is an enumeration of all the attribute nodes of an Element.
+*/
+
+final class AttributeEnumeration extends AxisIteratorImpl implements LookaheadIterator<NodeInfo> {
+
+ private ElementImpl element;
+ private AttributeCollectionImpl attributes;
+ private NodeTest nodeTest;
+ /*@Nullable*/ private NodeInfo next;
+ private int index;
+ private int length;
+
+ /**
+ * Constructor
+ * @param node: the element whose attributes are required. This may be any type of node,
+ * but if it is not an element the enumeration will be empty
+ * @param nodeTest: condition to be applied to the names of the attributes selected
+ */
+
+ public AttributeEnumeration(/*@NotNull*/ NodeImpl node, NodeTest nodeTest) {
+
+ this.nodeTest = nodeTest;
+
+ if (node.getNodeKind()==Type.ELEMENT) {
+ element = (ElementImpl)node;
+ attributes = (AttributeCollectionImpl)element.getAttributeList();
+ AttributeCollection attlist = element.getAttributeList();
+ index = 0;
+
+ if (nodeTest instanceof NameTest) {
+ NameTest test = (NameTest)nodeTest;
+ index = attlist.getIndexByFingerprint(test.getFingerprint());
+
+ if (index<0) {
+ next = null;
+ } else {
+ next = new AttributeImpl(element, index);
+ index = 0;
+ length = 0; // force iteration to select one node only
+ }
+
+ } else {
+ index = 0;
+ length = attlist.getLength();
+ advance();
+ }
+ }
+ else { // if it's not an element, or if we're not looking for attributes,
+ // then there's nothing to find
+ next = null;
+ index = 0;
+ length = 0;
+ }
+ }
+
+ /**
+ * Test if there are mode nodes still to come.
+ * ("elements" is used here in the sense of the Java enumeration class, not in the XML sense)
+ */
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ /**
+ * Get the next node in the iteration, or null if there are no more.
+ */
+
+ /*@Nullable*/ public NodeInfo next() {
+ if (next == null) {
+ current = null;
+ position = -1;
+ return null;
+ } else {
+ current = next;
+ position++;
+ advance();
+ return current;
+ }
+ }
+
+ /**
+ * Move to the next node in the enumeration.
+ */
+
+ private void advance() {
+ while (true) {
+ if (index >= length) {
+ next = null;
+ return;
+ } else if (attributes.isDeleted(index)) {
+ index++;
+ } else {
+ next = new AttributeImpl(element, index);
+ index++;
+ if (nodeTest.matches(next)) {
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new AttributeEnumeration(element, nodeTest);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+}
+
diff --git a/sf/saxon/tree/linked/AttributeImpl.java b/sf/saxon/tree/linked/AttributeImpl.java
new file mode 100644
index 0000000..b259fc5
--- /dev/null
+++ b/sf/saxon/tree/linked/AttributeImpl.java
@@ -0,0 +1,346 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.*;
+
+/**
+ * A node in the "linked" tree representing an attribute. Note that this is
+ * generated only "on demand", when the attribute is selected by a path expression.<P>
+ *
+ * <p>It is possible for multiple AttributeImpl objects to represent the same attribute node.
+ * The identity of an attribute node is determined by the identity of the element, and the index
+ * position of the attribute within the element. Index positions are not reused when an attribute
+ * is deleted, and are retained when an attribute is renamed.</p>
+ *
+ * <p>This object no longer caches information such as the name code and string value, because
+ * these would become invalid when the element node is modified.</p>
+ *
+ * @author Michael H. Kay
+ */
+
+final class AttributeImpl extends NodeImpl {
+
+ /**
+ * Construct an Attribute node for the n'th attribute of a given element
+ * @param element The element containing the relevant attribute
+ * @param index The index position of the attribute starting at zero
+ */
+
+ public AttributeImpl(ElementImpl element, int index) {
+ setRawParent(element);
+ setSiblingPosition(index);
+ }
+
+ /**
+ * Get the name code, which enables the name to be located in the name pool
+ */
+
+ public int getNameCode() {
+ if (getRawParent() == null || getSiblingPosition() == -1) {
+ // implies this node is deleted
+ return -1;
+ }
+ return ((ElementImpl)getRawParent()).getAttributeList().getNameCode(getSiblingPosition());
+ }
+
+ /**
+ * Get the fingerprint of the type annotation of this node, if any
+ */
+
+ public int getTypeAnnotation() {
+ return getSchemaType().getFingerprint();
+ }
+
+ /**
+ * Get the type annotation
+ * @return the type annotation of the node, if any
+ */
+
+ public SchemaType getSchemaType() {
+ return ((ElementImpl)getRawParent()).getAttributeList().getTypeAnnotation(getSiblingPosition());
+ }
+
+
+ /**
+ * Determine whether this node has the is-id property
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ if (getFingerprint() == StandardNames.XML_ID) {
+ return true;
+ }
+ TypeHierarchy th = getConfiguration().getTypeHierarchy();
+ return th.isIdCode(getTypeAnnotation());
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ AttributeCollection alist = ((ElementImpl) getRawParent()).getAttributeList();
+ if ((alist.getProperties(getSiblingPosition()) & ReceiverOptions.IS_IDREF) != 0) {
+ return true;
+ }
+ TypeHierarchy th = getConfiguration().getTypeHierarchy();
+ return th.isIdrefsCode(getTypeAnnotation());
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return false;
+ }
+
+ /**
+ * Determine whether this is the same node as another node
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(NodeInfo other) {
+ if (!(other instanceof AttributeImpl)) {
+ return false;
+ }
+ if (this==other) {
+ return true;
+ }
+ AttributeImpl otherAtt = (AttributeImpl)other;
+ return getRawParent().isSameNodeInfo(otherAtt.getRawParent()) && getSiblingPosition() == otherAtt.getSiblingPosition();
+ }
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode() {
+ return getRawParent().hashCode() ^ (getSiblingPosition()<<16);
+ }
+
+ /**
+ * Get the node sequence number (in document order). Sequence numbers are monotonic but not
+ * consecutive. In the current implementation, parent nodes (elements and roots) have a zero
+ * least-significant word, while namespaces, attributes, text nodes, comments, and PIs have
+ * the top word the same as their owner and the bottom half reflecting their relative position.
+ */
+
+ protected long getSequenceNumber() {
+ long parseq = getRawParent().getSequenceNumber();
+ return (parseq == -1L ? parseq : parseq + 0x8000 + getSiblingPosition());
+ // note the 0x8000 is to leave room for namespace nodes
+ }
+
+ /**
+ * Return the type of node.
+ * @return Node.ATTRIBUTE
+ */
+
+ public final int getNodeKind() {
+ return Type.ATTRIBUTE;
+ }
+
+ /**
+ * Return the character value of the node.
+ * @return the attribute value
+ */
+
+ public String getStringValue() {
+ return ((ElementImpl)getRawParent()).getAttributeList().getValue(getSiblingPosition());
+ }
+
+ /**
+ * Get next sibling - not defined for attributes
+ */
+
+ /*@Nullable*/ public NodeImpl getNextSibling() {
+ return null;
+ }
+
+ /**
+ * Get previous sibling - not defined for attributes
+ */
+
+ /*@Nullable*/ public NodeImpl getPreviousSibling() {
+ return null;
+ }
+
+ /**
+ * Get the previous node in document order (skipping attributes)
+ */
+
+ /*@NotNull*/ public NodeImpl getPreviousInDocument() {
+ return (NodeImpl)getParent();
+ }
+
+ /**
+ * Get the next node in document order (skipping attributes)
+ */
+
+ /*@Nullable*/ public NodeImpl getNextInDocument(NodeImpl anchor) {
+ if (anchor==this) return null;
+ return ((NodeImpl)getParent()).getNextInDocument(anchor);
+ }
+
+ /**
+ * Get sequential key. Returns key of owning element with the attribute index as a suffix
+ * @param buffer a buffer to which the generated ID will be written
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ getParent().generateId(buffer);
+ buffer.append('a');
+ buffer.append(Integer.toString(getSiblingPosition()));
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ SimpleType typeCode = (CopyOptions.includes(copyOptions, CopyOptions.TYPE_ANNOTATIONS) ?
+ ((SimpleType)getSchemaType()) : BuiltInAtomicType.UNTYPED_ATOMIC);
+ out.attribute(new NameOfNode(this), typeCode, getStringValue(), locationId, 0);
+ }
+
+ /**
+ * Delete this node (that is, detach it from its parent)
+ */
+
+ public void delete() {
+ if (getRawParent() != null) {
+ getRawParent().removeAttribute(this);
+ }
+ setRawParent(null);
+ setSiblingPosition(-1);
+ }
+
+ /**
+ * Test whether this MutableNodeInfo object represents a node that has been deleted.
+ * Generally, such a node is unusable, and any attempt to use it will result in an exception
+ * being thrown
+ * @return true if this node has been deleted
+ */
+
+ public boolean isDeleted() {
+ // Note that the attribute node may be represented by more than one object
+ return (getSiblingPosition() == -1 || getNameCode() == -1 || (getRawParent() != null && getRawParent().isDeleted()));
+ }
+
+ /**
+ * Replace this node with a given sequence of nodes
+ * @param replacement the replacement nodes (which for this version of the method must be attribute
+ * nodes - they may use any implementation of the NodeInfo interface).
+ * The target attribute node is deleted, and the replacement nodes are added to the
+ * parent element; if they have the same names as existing nodes, then the existing nodes will be
+ * overwritten.
+ * @param inherit set to true if new child elements are to inherit the in-scope namespaces
+ * of their new parent. Not used when replacing attribute nodes.
+ * @throws IllegalArgumentException if any of the replacement nodes is not an attribute
+ * @throws IllegalStateException if this node has been deleted or has no parent node
+ * or if two of the replacement nodes have the same name
+ */
+
+ public void replace(/*@NotNull*/ NodeInfo[] replacement, boolean inherit) {
+ if (isDeleted()) {
+ throw new IllegalStateException("Cannot replace a deleted node");
+ }
+ if (getParent()==null) {
+ throw new IllegalStateException("Cannot replace a parentless node");
+ }
+ ParentNodeImpl element = getRawParent();
+ delete();
+ for (NodeInfo n : replacement) {
+ if (n.getNodeKind() != Type.ATTRIBUTE) {
+ throw new IllegalArgumentException("Replacement nodes must be attributes");
+ }
+ element.addAttribute(new NameOfNode(n), BuiltInAtomicType.UNTYPED_ATOMIC, n.getStringValue(), 0);
+ }
+ }
+
+ /**
+ * Rename this node
+ *
+ * @param newNameCode the NamePool code of the new name
+ */
+
+ public void rename(NodeName newNameCode) {
+ // The attribute node itself is transient; we need to update the attribute collection held in the parent
+ if (getRawParent() != null) {
+ ((AttributeCollectionImpl)((ElementImpl)getRawParent()).getAttributeList()).renameAttribute(getSiblingPosition(), newNameCode);
+ String newURI = newNameCode.getURI();
+ if (newURI.length()!=0) {
+ // new attribute name is in a namespace
+ String newPrefix = newNameCode.getPrefix();
+ NamespaceBinding newBinding = new NamespaceBinding(newPrefix, newURI);
+ String oldURI = ((ElementImpl)getRawParent()).getURIForPrefix(newPrefix, false);
+ if (oldURI == null) {
+ getRawParent().addNamespace(newBinding, false);
+ } else if (!oldURI.equals(newURI)) {
+ throw new IllegalArgumentException(
+ "Namespace binding of new name conflicts with existing namespace binding");
+ }
+ }
+ }
+ }
+
+ public void replaceStringValue(CharSequence stringValue) {
+ if (getRawParent() != null) {
+ AttributeCollectionImpl atts = (AttributeCollectionImpl)((ElementImpl)getRawParent()).getAttributeList();
+ atts.replaceAttribute(getSiblingPosition(), stringValue);
+ }
+ }
+
+
+ /**
+ * Remove type information from this node (and its ancestors, recursively).
+ * This method implements the upd:removeType() primitive defined in the XQuery Update specification
+ *
+ */
+
+ public void removeTypeAnnotation() {
+ if (getRawParent() != null) {
+ AttributeCollectionImpl atts = (AttributeCollectionImpl)((ElementImpl)getRawParent()).getAttributeList();
+ atts.setTypeAnnotation(getSiblingPosition(), BuiltInAtomicType.UNTYPED_ATOMIC);
+ getRawParent().removeTypeAnnotation();
+ }
+ }
+
+
+ /**
+ * Set the type annotation on a node. This must only be called when the caller has verified (by validation)
+ * that the node is a valid instance of the specified type. The call is ignored if the node is not an element
+ * or attribute node.
+ *
+ * @param typeCode the type annotation (possibly including high bits set to indicate the isID, isIDREF, and
+ * isNilled properties)
+ */
+
+ public void setTypeAnnotation(int typeCode) {
+ if (getRawParent() != null) {
+ AttributeCollectionImpl atts = (AttributeCollectionImpl)((ElementImpl)getRawParent()).getAttributeList();
+ atts.setTypeAnnotation(getSiblingPosition(), (SimpleType)getConfiguration().getSchemaType(typeCode));
+ }
+ }
+}
+
diff --git a/sf/saxon/tree/linked/ChildEnumeration.java b/sf/saxon/tree/linked/ChildEnumeration.java
new file mode 100644
index 0000000..ad88918
--- /dev/null
+++ b/sf/saxon/tree/linked/ChildEnumeration.java
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+
+final class ChildEnumeration extends TreeEnumeration {
+
+ public ChildEnumeration(NodeImpl node, NodeTest nodeTest) {
+ super(node, nodeTest);
+ next = node.getFirstChild();
+ while (!conforms(next)) {
+ step();
+ }
+ }
+
+ protected void step() {
+ next = next.getNextSibling();
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new ChildEnumeration(start, nodeTest);
+ }
+}
+
diff --git a/sf/saxon/tree/linked/CommentImpl.java b/sf/saxon/tree/linked/CommentImpl.java
new file mode 100644
index 0000000..2da6e5e
--- /dev/null
+++ b/sf/saxon/tree/linked/CommentImpl.java
@@ -0,0 +1,66 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * CommentImpl is an implementation of a Comment node
+ * @author Michael H. Kay
+ */
+
+
+final class CommentImpl extends NodeImpl {
+
+ String comment;
+
+ public CommentImpl(String content) {
+ this.comment = content;
+ }
+
+ public final String getStringValue() {
+ return comment;
+ }
+
+ /**
+ * Get the typed value of this node.
+ * Returns the string value, as an instance of xs:string
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize() {
+ return new StringValue(getStringValue());
+ }
+
+ public final int getNodeKind() {
+ return Type.COMMENT;
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ out.comment(comment, locationId, 0);
+ }
+
+
+ /**
+ * Replace the string-value of this node
+ *
+ * @param stringValue the new string value
+ */
+
+ public void replaceStringValue(/*@NotNull*/ CharSequence stringValue) {
+ comment = stringValue.toString();
+ }
+}
+
diff --git a/sf/saxon/tree/linked/DocumentImpl.java b/sf/saxon/tree/linked/DocumentImpl.java
new file mode 100644
index 0000000..3314918
--- /dev/null
+++ b/sf/saxon/tree/linked/DocumentImpl.java
@@ -0,0 +1,701 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorOverSequence;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AnyType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.*;
+
+/**
+ * A node in the XML parse tree representing the Document itself (or equivalently, the root
+ * node of the Document).
+ * <p/>
+ * <p>A DocumentImpl object may either represent a real document node, or it may represent an imaginary
+ * container for a parentless element.</p>
+ *
+ * @author Michael H. Kay
+ */
+
+public final class DocumentImpl extends ParentNodeImpl implements DocumentInfo, MutableDocumentInfo {
+
+ //private static int nextDocumentNumber = 0;
+
+ private ElementImpl documentElement;
+
+
+ /*@Nullable*/ private HashMap<String, NodeInfo> idTable;
+ private long documentNumber;
+ private String baseURI;
+ private HashMap<String, String[]> entityTable;
+
+ /*@Nullable*/ private IntHashMap<List<NodeImpl>> elementList;
+ private HashMap<String, Object> userData;
+ private Configuration config;
+ private LineNumberMap lineNumberMap;
+ private SystemIdMap systemIdMap = new SystemIdMap();
+ private boolean imaginary;
+
+ /**
+ * Create a DocumentImpl
+ */
+
+ public DocumentImpl() {
+ setRawParent(null);
+ }
+
+ /**
+ * Set the Configuration that contains this document
+ *
+ * @param config the Saxon configuration
+ */
+
+ public void setConfiguration(/*@NotNull*/ Configuration config) {
+ this.config = config;
+ documentNumber = config.getDocumentNumberAllocator().allocateDocumentNumber();
+ }
+
+ /**
+ * Get the configuration previously set using setConfiguration
+ *
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the name pool used for the names in this document
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ /**
+ * Get a Builder suitable for building nodes that can be attached to this document.
+ *
+ * @return a new TreeBuilder
+ */
+
+ /*@NotNull*/
+ public Builder newBuilder() {
+ LinkedTreeBuilder builder = new LinkedTreeBuilder(config.makePipelineConfiguration());
+ builder.setAllocateSequenceNumbers(false);
+ return builder;
+ }
+
+ /**
+ * Set whether this is an imaginary document node
+ *
+ * @param imaginary if true, this is an imaginary node - the tree is really rooted at the topmost element
+ */
+
+ public void setImaginary(boolean imaginary) {
+ this.imaginary = imaginary;
+ }
+
+ /**
+ * Ask whether this is an imaginary document node
+ *
+ * @return true if this is an imaginary node - the tree is really rooted at the topmost element
+ */
+
+ public boolean isImaginary() {
+ return imaginary;
+ }
+
+ /**
+ * Ask whether the document contains any nodes whose type annotation is anything other than
+ * UNTYPED
+ *
+ * @return true if the document contains elements whose type is other than UNTYPED
+ */
+ public boolean isTyped() {
+ return documentElement.getTypeAnnotation() != StandardNames.XS_UNTYPED;
+ }
+
+ /**
+ * Get the unique document number
+ */
+
+ public long getDocumentNumber() {
+ return documentNumber;
+ }
+
+ /**
+ * Set the top-level element of the document (variously called the root element or the
+ * document element). Note that a DocumentImpl may represent the root of a result tree
+ * fragment, in which case there is no document element.
+ *
+ * @param e the top-level element
+ */
+
+ void setDocumentElement(ElementImpl e) {
+ documentElement = e;
+ }
+
+ /**
+ * Copy the system ID and line number map from another document
+ * (used when grafting a simplified stylesheet)
+ *
+ * @param original the document whose system ID and line number maps are to be grafted
+ * onto this tree
+ */
+
+ public void graftLocationMap(/*@NotNull*/ DocumentImpl original) {
+ systemIdMap = original.systemIdMap;
+ lineNumberMap = original.lineNumberMap;
+ }
+
+ /**
+ * Set the system id (base URI) of this node
+ */
+
+ public void setSystemId( String /*@Nullable*/uri) {
+ if (uri == null) {
+ uri = "";
+ }
+ systemIdMap.setSystemId(getRawSequenceNumber(), uri);
+ }
+
+ /**
+ * Get the system id of this root node
+ */
+
+ public String getSystemId() {
+ return systemIdMap.getSystemId(getRawSequenceNumber());
+ }
+
+ /**
+ * Set the base URI of this document node
+ *
+ * @param uri the new base URI
+ */
+
+ public void setBaseURI(String uri) {
+ baseURI = uri;
+ }
+
+ /**
+ * Get the base URI of this root node.
+ *
+ * @return the base URI
+ */
+
+ public String getBaseURI() {
+ if (baseURI != null) {
+ return baseURI;
+ }
+ return getSystemId();
+ }
+
+
+ /**
+ * Set the system id of an element in the document
+ *
+ * @param seq the sequence number of the element
+ * @param uri the system identifier (base URI) of the element
+ */
+
+ void setSystemId(int seq, String /*@Nullable*/uri) {
+ if (uri == null) {
+ uri = "";
+ }
+ systemIdMap.setSystemId(seq, uri);
+ }
+
+
+ /**
+ * Get the system id of an element in the document
+ *
+ * @param seq the sequence number of the element
+ * @return the systemId (base URI) of the element
+ */
+
+ String getSystemId(int seq) {
+ return systemIdMap.getSystemId(seq);
+ }
+
+
+ /**
+ * Set line numbering on
+ */
+
+ public void setLineNumbering() {
+ lineNumberMap = new LineNumberMap();
+ lineNumberMap.setLineAndColumn(getRawSequenceNumber(), 0, -1);
+ }
+
+ /**
+ * Set the line number for an element. Ignored if line numbering is off.
+ *
+ * @param sequence the sequence number of the element
+ * @param line the line number of the element
+ * @param column the column number of the element
+ */
+
+ void setLineAndColumn(int sequence, int line, int column) {
+ if (lineNumberMap != null && sequence >= 0) {
+ lineNumberMap.setLineAndColumn(sequence, line, column);
+ }
+ }
+
+ /**
+ * Get the line number for an element.
+ *
+ * @param sequence the sequence number of the element
+ * @return the line number for an element. Return -1 if line numbering is off, or if
+ * the element was added subsequent to document creation by use of XQuery update
+ */
+
+ int getLineNumber(int sequence) {
+ if (lineNumberMap != null && sequence >= 0) {
+ return lineNumberMap.getLineNumber(sequence);
+ }
+ return -1;
+ }
+
+ /**
+ * Get the column number for an element.
+ *
+ * @param sequence the sequence number of the element
+ * @return the column number for an element. Return -1 if line numbering is off, or if
+ * the element was added subsequent to document creation by use of XQuery update
+ */
+
+ int getColumnNumber(int sequence) {
+ if (lineNumberMap != null && sequence >= 0) {
+ return lineNumberMap.getColumnNumber(sequence);
+ }
+ return -1;
+ }
+
+
+ /**
+ * Get the line number of this root node.
+ *
+ * @return 0 always
+ */
+
+ public int getLineNumber() {
+ return 0;
+ }
+
+ /**
+ * Return the type of node.
+ *
+ * @return Type.DOCUMENT (always)
+ */
+
+ public final int getNodeKind() {
+ return Type.DOCUMENT;
+ }
+
+ /**
+ * Get next sibling - always null
+ *
+ * @return null
+ */
+
+
+ public /*@Nullable*/final NodeImpl getNextSibling() {
+ return null;
+ }
+
+ /**
+ * Get previous sibling - always null
+ *
+ * @return null
+ */
+
+ /*@Nullable*/
+ public final NodeImpl getPreviousSibling() {
+ return null;
+ }
+
+ /**
+ * Get the root (outermost) element.
+ *
+ * @return the Element node for the outermost element of the document.
+ */
+
+ public ElementImpl getDocumentElement() {
+ return documentElement;
+ }
+
+ /**
+ * Get the root node
+ *
+ * @return the NodeInfo representing the root of this tree
+ */
+
+ /*@NotNull*/
+ public NodeInfo getRoot() {
+ return this;
+ }
+
+ /**
+ * Get the root (document) node
+ *
+ * @return the DocumentInfo representing this document
+ */
+
+ /*@NotNull*/
+ public DocumentInfo getDocumentRoot() {
+ return this;
+ }
+
+ /**
+ * Get the physical root of the tree. This may be an imaginary document node: this method
+ * should be used only when control information held at the physical root is required
+ *
+ * @return the document node, which may be imaginary
+ */
+
+ /*@NotNull*/
+ public DocumentImpl getPhysicalRoot() {
+ return this;
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node
+ *
+ * @param buffer a buffer into which will be placed a string based on the document number
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ buffer.append('d');
+ buffer.append(Long.toString(documentNumber));
+ }
+
+ /**
+ * Get a list of all elements with a given name fingerprint
+ *
+ * @param fingerprint the fingerprint of the required element name
+ * @return an iterator over all the elements with this name
+ */
+
+ /*@NotNull*/
+ AxisIterator getAllElements(int fingerprint) {
+ if (elementList == null) {
+ elementList = new IntHashMap<List<NodeImpl>>(500);
+ }
+ IntHashMap<List<NodeImpl>> eList = elementList;
+ List<NodeImpl> list = eList.get(fingerprint);
+ if (list == null) {
+ list = new ArrayList<NodeImpl>(500);
+ NodeImpl next = getNextInDocument(this);
+ while (next != null) {
+ if (next.getNodeKind() == Type.ELEMENT &&
+ next.getFingerprint() == fingerprint) {
+ list.add(next);
+ }
+ next = next.getNextInDocument(this);
+ }
+ eList.put(fingerprint, list);
+ }
+ return new AxisIteratorOverSequence<NodeImpl>(
+ new net.sf.saxon.tree.iter.ListIterator<NodeImpl>(list));
+ }
+
+ /**
+ * Remove a node from any indexes when it is detached from the tree
+ *
+ * @param node the node to be removed from all indexes
+ */
+
+ public void deIndex(/*@NotNull*/ NodeImpl node) {
+ if (node instanceof ElementImpl) {
+ IntHashMap<List<NodeImpl>> eList = elementList;
+ if (eList != null) {
+ List<NodeImpl> list = eList.get(node.getFingerprint());
+ if (list == null) {
+ return;
+ }
+ list.remove(node);
+ }
+ if (node.isId()) {
+ deregisterID(node.getStringValue());
+ }
+ } else if (node instanceof AttributeImpl) {
+ if (node.isId()) {
+ deregisterID(node.getStringValue());
+ }
+ }
+ }
+
+ /**
+ * Index all the ID attributes. This is done the first time the id() function
+ * is used on this document, or the first time that id() is called after a sequence of updates
+ */
+
+ private void indexIDs() {
+ if (idTable != null) {
+ return; // ID's are already indexed
+ }
+ idTable = new HashMap<String, NodeInfo>(256);
+ NameChecker checker = getConfiguration().getNameChecker();
+
+ NodeImpl curr = this;
+ NodeImpl root = curr;
+ while (curr != null) {
+ if (curr.getNodeKind() == Type.ELEMENT) {
+ //noinspection ConstantConditions
+ ElementImpl e = (ElementImpl) curr;
+ if (e.isId()) {
+ registerID(e, Whitespace.trim(e.getStringValueCS()));
+ }
+ AttributeCollectionImpl atts = (AttributeCollectionImpl) e.getAttributeList();
+ for (int i = 0; i < atts.getLength(); i++) {
+ if (!atts.isDeleted(i) && atts.isId(i) && checker.isValidNCName(Whitespace.trim(atts.getValue(i)))) {
+ // don't index any invalid IDs - these can arise when using a non-validating parser
+ registerID(e, Whitespace.trim(atts.getValue(i)));
+ }
+ }
+ }
+ curr = curr.getNextInDocument(root);
+ }
+ }
+
+ /**
+ * Register a unique element ID. Does nothing if there is already an element with that ID.
+ *
+ * @param e The Element having a particular unique ID value
+ * @param id The unique ID value
+ */
+
+ protected void registerID(NodeInfo e, String id) {
+ // the XPath spec (5.2.1) says ignore the second ID if it's not unique
+ if (idTable == null) {
+ idTable = new HashMap<String, NodeInfo>(256);
+ }
+ HashMap<String, NodeInfo> table = idTable;
+ Object old = table.get(id);
+ if (old == null) {
+ table.put(id, e);
+ }
+ }
+
+ /**
+ * Get the element with a given ID.
+ *
+ * @param id The unique ID of the required element, previously registered using registerID()
+ * @param getParent true if the requirement is for the parent of the node with the given ID,
+ * not the node itself.
+ * @return The NodeInfo for the given ID if one has been registered, otherwise null.
+ */
+
+
+ public NodeInfo/*@Nullable*/ selectID(String id, boolean getParent) {
+ if (idTable == null) {
+ indexIDs();
+ }
+ assert idTable != null;
+ NodeInfo node = idTable.get(id);
+ if (node != null && getParent && node.isId() && node.getStringValue().equals(id)) {
+ node = node.getParent();
+ }
+ return node;
+ }
+
+ /**
+ * Remove the entry for a given ID (when nodes are deleted). Does nothing if the id value is not
+ * present in the index.
+ *
+ * @param id The id value
+ */
+
+ protected void deregisterID(String id) {
+ id = Whitespace.trim(id);
+ if (idTable != null) {
+ idTable.remove(id);
+ }
+ }
+
+ /**
+ * Set an unparsed entity URI associated with this document. For system use only, while
+ * building the document.
+ *
+ * @param name the entity name
+ * @param uri the system identifier of the unparsed entity
+ * @param publicId the public identifier of the unparsed entity
+ */
+
+ void setUnparsedEntity(String name, String uri, String publicId) {
+ // System.err.println("setUnparsedEntity( " + name + "," + uri + ")");
+ if (entityTable == null) {
+ entityTable = new HashMap<String, String[]>(10);
+ }
+ String[] ids = new String[2];
+ ids[0] = uri;
+ ids[1] = publicId;
+ entityTable.put(name, ids);
+ }
+
+ /**
+ * Get the list of unparsed entities defined in this document
+ *
+ * @return an Iterator, whose items are of type String, containing the names of all
+ * unparsed entities defined in this document. If there are no unparsed entities or if the
+ * information is not available then an empty iterator is returned
+ */
+
+ public Iterator<String> getUnparsedEntityNames() {
+ if (entityTable == null) {
+ List<String> ls = Collections.emptyList();
+ return ls.iterator();
+ } else {
+ return entityTable.keySet().iterator();
+ }
+ }
+
+ /**
+ * Get the unparsed entity with a given name
+ *
+ * @param name the name of the entity
+ * @return if the entity exists, return an array of two Strings, the first holding the system ID
+ * of the entity, the second holding the public ID if there is one, or null if not. If the entity
+ * does not exist, return null.
+ */
+
+ /*@Nullable*/
+ public String[] getUnparsedEntity(String name) {
+ if (entityTable == null) {
+ return null;
+ }
+ return entityTable.get(name);
+ }
+
+ /**
+ * Get the type annotation of this node, if any. By convention for a document node this is
+ * XS_ANY_TYPE if the document is validated, or XS_UNTYPED otherwise
+ *
+ * @return the type annotation, as the integer name code of the type name
+ */
+
+ public int getTypeAnnotation() {
+ return getSchemaType().getFingerprint();
+ }
+
+ /**
+ * Get the type annotation
+ *
+ * @return the type annotation of the base node
+ */
+
+ public SchemaType getSchemaType() {
+ if (documentElement == null || documentElement.getTypeAnnotation() == StandardNames.XS_UNTYPED) {
+ return Untyped.getInstance();
+ } else {
+ return AnyType.getInstance();
+ }
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ out.startDocument(CopyOptions.getStartDocumentProperties(copyOptions));
+
+ // copy any unparsed entities
+
+ for (Iterator<String> names = getUnparsedEntityNames(); names.hasNext(); ) {
+ String name = names.next();
+ String[] details = getUnparsedEntity(name);
+ assert details != null;
+ out.setUnparsedEntity(name, details[0], details[1]);
+ }
+
+ // copy the children
+
+ NodeImpl next = (NodeImpl) getFirstChild();
+ while (next != null) {
+ next.copy(out, copyOptions, locationId);
+ next = (NodeImpl) next.getNextSibling();
+ }
+
+ out.endDocument();
+ }
+
+
+ /**
+ * Replace the string-value of this node
+ *
+ * @param stringValue the new string value
+ */
+
+ public void replaceStringValue(CharSequence stringValue) {
+ throw new UnsupportedOperationException("Cannot replace the value of a document node");
+ }
+
+ /**
+ * This method is called before performing a batch of updates; it allows all cached data that
+ * might be invalidated by such updates to be cleared
+ */
+
+ public void resetIndexes() {
+ idTable = null;
+ elementList = null;
+ }
+
+ /**
+ * Set user data on the document node. The user data can be retrieved subsequently
+ * using {@link #getUserData}
+ *
+ * @param key A string giving the name of the property to be set. Clients are responsible
+ * for choosing a key that is likely to be unique. Must not be null. Keys used internally
+ * by Saxon are prefixed "saxon:".
+ * @param value The value to be set for the property. May be null, which effectively
+ * removes the existing value for the property.
+ */
+
+ public void setUserData(String key, Object value) {
+ /*@Nullable*/ if (userData == null) {
+ userData = new HashMap<String, Object>(4);
+ }
+ if (value == null) {
+ userData.remove(key);
+ } else {
+ userData.put(key, value);
+ }
+ }
+
+ /**
+ * Get user data held in the document node. This retrieves properties previously set using
+ * {@link #setUserData}
+ *
+ * @param key A string giving the name of the property to be retrieved.
+ * @return the value of the property, or null if the property has not been defined.
+ */
+
+ /*@Nullable*/
+ public Object getUserData(String key) {
+ if (userData == null) {
+ return null;
+ } else {
+ return userData.get(key);
+ }
+ }
+}
+
diff --git a/sf/saxon/tree/linked/ElementImpl.java b/sf/saxon/tree/linked/ElementImpl.java
new file mode 100644
index 0000000..1be412e
--- /dev/null
+++ b/sf/saxon/tree/linked/ElementImpl.java
@@ -0,0 +1,837 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.event.CopyInformee;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.NamespaceIterator;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * ElementImpl implements an element with no attributes or namespace declarations.<P>
+ * This class is an implementation of NodeInfo. For elements with attributes or
+ * namespace declarations, class ElementWithAttributes is used.
+ *
+ * @author Michael H. Kay
+ */
+
+
+public class ElementImpl extends ParentNodeImpl implements NamespaceResolver {
+
+ private int nameCode;
+ private int typeCode = StandardNames.XS_UNTYPED;
+ private AttributeCollection attributeList; // this excludes namespace attributes
+ /*@Nullable*/ private NamespaceBinding[] namespaceList = null; // list of namespace codes
+
+ /**
+ * Construct an empty ElementImpl
+ */
+
+ public ElementImpl() {
+ }
+
+ /**
+ * Set the name code. Used when creating a dummy element in the Stripper
+ *
+ * @param nameCode the integer name code representing the element name
+ */
+
+ public void setNameCode(int nameCode) {
+ this.nameCode = nameCode;
+ }
+
+ /**
+ * Set the attribute list
+ *
+ * @param atts the list of attributes of this element (not including namespace attributes)
+ */
+
+ public void setAttributeList(AttributeCollection atts) {
+ this.attributeList = atts;
+ }
+
+ /**
+ * Set the namespace list
+ *
+ * @param namespaces an integer array of namespace codes
+ */
+
+ public void setNamespaceList(NamespaceBinding[] namespaces) {
+ this.namespaceList = namespaces;
+ }
+
+ /**
+ * Initialise a new ElementImpl with an element name
+ *
+ * @param elemName Integer representing the element name, with namespaces resolved
+ * @param elementType the schema type of the element node
+ * @param atts The attribute list: always null
+ * @param parent The parent node
+ * @param sequenceNumber Integer identifying this element within the document
+ */
+
+ public void initialise(/*@NotNull*/ NodeName elemName, SchemaType elementType, AttributeCollectionImpl atts, /*@NotNull*/ NodeInfo parent,
+ int sequenceNumber) {
+ setNameCode(elemName.allocateNameCode(parent.getNamePool()));
+ this.typeCode = elementType.getFingerprint();
+ setRawParent((ParentNodeImpl) parent);
+ setRawSequenceNumber(sequenceNumber);
+ attributeList = atts;
+ }
+
+ /**
+ * Set location information for this node
+ *
+ * @param systemId the base URI
+ * @param line the line number if known
+ * @param column the column number if known
+ */
+
+ public void setLocation(String systemId, int line, int column) {
+ DocumentImpl root = getRawParent().getPhysicalRoot();
+ root.setLineAndColumn(getRawSequenceNumber(), line, column);
+ root.setSystemId(getRawSequenceNumber(), systemId);
+ }
+
+ /**
+ * Set the system ID of this node. This method is provided so that a NodeInfo
+ * implements the javax.xml.transform.Source interface, allowing a node to be
+ * used directly as the Source of a transformation
+ */
+
+ public void setSystemId(String uri) {
+ getPhysicalRoot().setSystemId(getRawSequenceNumber(), uri);
+ }
+
+ /**
+ * Get the root node
+ */
+
+ public NodeInfo getRoot() {
+ ParentNodeImpl up = getRawParent();
+ if (up == null || (up instanceof DocumentImpl && ((DocumentImpl) up).isImaginary())) {
+ return this;
+ } else {
+ return up.getRoot();
+ }
+ }
+
+ /**
+ * Get the root node, if it is a document node.
+ *
+ * @return the DocumentInfo representing the containing document. If this
+ * node is part of a tree that does not have a document node as its
+ * root, returns null.
+ * @since 8.4
+ */
+
+ /*@Nullable*/
+ public DocumentInfo getDocumentRoot() {
+ NodeInfo root = getRoot();
+ if (root instanceof DocumentInfo) {
+ return (DocumentInfo) root;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the system ID of the entity containing this element node.
+ */
+
+ /*@Nullable*/
+ public final String getSystemId() {
+ DocumentImpl root = getPhysicalRoot();
+ return (root == null ? null : root.getSystemId(getRawSequenceNumber()));
+ }
+
+ /**
+ * Get the base URI of this element node. This will be the same as the System ID unless
+ * xml:base has been used.
+ */
+
+ public String getBaseURI() {
+ return Navigator.getBaseURI(this);
+ }
+
+ /**
+ * Get the attribute list. Note that if the attribute list is empty, it should not be modified, as it
+ * will be shared by other elements. Instead, set a new attribute list.
+ *
+ * @return the list of attributes of this element (not including namespace attributes)
+ */
+
+ public AttributeCollection gsetAttributeCollection() {
+ return this.attributeList;
+ }
+
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return (typeCode & NodeInfo.IS_NILLED) != 0;
+ }
+
+ /**
+ * Set the type annotation on a node. This must only be called when the caller has verified (by validation)
+ * that the node is a valid instance of the specified type. The call is ignored if the node is not an element
+ * or attribute node.
+ *
+ * @param typeCode the type annotation (possibly including high bits set to indicate the isID, isIDREF, and
+ * isNilled properties)
+ */
+
+ public void setTypeAnnotation(int typeCode) {
+ if (typeCode == -1) {
+ typeCode = StandardNames.XS_UNTYPED;
+ }
+ this.typeCode = typeCode;
+ }
+
+ /**
+ * Get the fingerpint of the type annotation of this node, if any
+ *
+ * @return the type annotation, as the integer name code of the type name
+ */
+
+ public int getTypeAnnotation() {
+ return typeCode & NamePool.FP_MASK;
+ }
+
+ /**
+ * Get the type annotation
+ *
+ * @return the type annotation of the node
+ */
+
+ @Override
+ public SchemaType getSchemaType() {
+ if (typeCode == StandardNames.XS_UNTYPED) {
+ return Untyped.getInstance();
+ } else {
+ return getConfiguration().getSchemaType(typeCode & NamePool.FP_MASK);
+ }
+ }
+
+ /**
+ * Get the line number of the node within its source document entity
+ */
+
+ public int getLineNumber() {
+ DocumentImpl root = getPhysicalRoot();
+ if (root == null) {
+ return -1;
+ } else {
+ return root.getLineNumber(getRawSequenceNumber());
+ }
+ }
+
+ /**
+ * Get the line number of the node within its source document entity
+ */
+
+ public int getColumnNumber() {
+ DocumentImpl root = getPhysicalRoot();
+ if (root == null) {
+ return -1;
+ } else {
+ return root.getColumnNumber(getRawSequenceNumber());
+ }
+ }
+
+ /**
+ * Get the nameCode of the node. This is used to locate the name in the NamePool
+ */
+
+ public int getNameCode() {
+ return nameCode;
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node
+ *
+ * @param buffer to contain the generated ID
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ int sequence = getRawSequenceNumber();
+ if (sequence >= 0) {
+ getPhysicalRoot().generateId(buffer);
+ buffer.append("e");
+ buffer.append(Integer.toString(sequence));
+ } else {
+ getRawParent().generateId(buffer);
+ buffer.append("f");
+ buffer.append(Integer.toString(getSiblingPosition()));
+ }
+ }
+
+ /**
+ * Return the kind of node.
+ *
+ * @return Type.ELEMENT
+ */
+
+ public final int getNodeKind() {
+ return Type.ELEMENT;
+ }
+
+ /**
+ * Copy this node to a given outputter (supporting xsl:copy-of)
+ *
+ * @param out The outputter
+ * @param copyOptions
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+
+ SchemaType typeCode = (CopyOptions.includes(copyOptions, CopyOptions.TYPE_ANNOTATIONS) ?
+ getSchemaType() : Untyped.getInstance());
+ CopyInformee informee = (CopyInformee) out.getPipelineConfiguration().getComponent(CopyInformee.class.getName());
+ if (informee != null) {
+ locationId = informee.notifyElementNode(this);
+ }
+
+ out.startElement(new NameOfNode(this), typeCode, locationId, 0);
+
+ // output the namespaces
+
+ int childCopyOptions = copyOptions & ~CopyOptions.ALL_NAMESPACES;
+ if ((copyOptions & CopyOptions.LOCAL_NAMESPACES) != 0) {
+ NamespaceBinding[] localNamespaces = getDeclaredNamespaces(null);
+ for (NamespaceBinding ns : localNamespaces) {
+ if (ns == null) {
+ break;
+ }
+ out.namespace(ns, 0);
+ }
+ } else if ((copyOptions & CopyOptions.ALL_NAMESPACES) != 0) {
+ NamespaceIterator.sendNamespaces(this, out);
+ childCopyOptions |= CopyOptions.LOCAL_NAMESPACES;
+ }
+
+ // output the attributes
+
+ if (attributeList != null) {
+ for (int i = 0; i < attributeList.getLength(); i++) {
+ NodeName nc = attributeList.getNodeName(i);
+ if (nc != null) {
+ // if attribute hasn't been deleted
+ out.attribute(nc, BuiltInAtomicType.UNTYPED_ATOMIC, attributeList.getValue(i), 0, 0);
+ }
+ }
+ }
+
+ out.startContent();
+
+ // output the children
+
+ NodeImpl next = (NodeImpl) getFirstChild();
+ while (next != null) {
+ next.copy(out, childCopyOptions, locationId);
+ next = (NodeImpl) next.getNextSibling();
+ }
+
+ out.endElement();
+ }
+
+ /**
+ * Delete this node (that is, detach it from its parent)
+ */
+
+ public void delete() {
+ DocumentImpl root = getPhysicalRoot();
+ super.delete();
+ if (root != null) {
+ AxisIterator iter = iterateAxis(AxisInfo.DESCENDANT_OR_SELF, NodeKindTest.ELEMENT);
+ while (true) {
+ ElementImpl n = (ElementImpl) iter.next();
+ int atts = attributeList.getLength();
+ for (int index = 0; index < atts; index++) {
+ if (attributeList.isId(index)) {
+ root.deregisterID(attributeList.getValue(index));
+ }
+ }
+ if (n == null) {
+ break;
+ }
+ root.deIndex(n);
+ }
+ }
+ }
+
+ /**
+ * Rename this node
+ *
+ * @param newName the new name
+ */
+
+ public void rename(NodeName newName) {
+ String prefix = newName.getPrefix();
+ String uri = newName.getURI();
+ NamespaceBinding ns = new NamespaceBinding(prefix, uri);
+ String uc = getURIForPrefix(prefix, true);
+ if (uc == null) {
+ uc = "";
+ }
+ if (!uc.equals(uri)) {
+ if (uc.length() == 0) {
+ addNamespace(ns, true);
+ } else {
+ throw new IllegalArgumentException(
+ "Namespace binding of new name conflicts with existing namespace binding");
+ }
+ }
+ nameCode = getNamePool().allocate(prefix, uri, newName.getLocalPart());
+ }
+
+ /**
+ * Add a namespace binding (that is, a namespace node) to this element. This call has no effect if applied
+ * to a node other than an element.
+ *
+ * @param nscode The namespace code representing the (prefix, uri) pair of the namespace binding to be
+ * added. If the target element already has a namespace binding with this (prefix, uri) pair, the call has
+ * no effect. If the target element currently has a namespace binding with this prefix and a different URI, an
+ * exception is raised.
+ * @param inherit If true, the new namespace binding will be inherited by any children of the target element
+ * that do not already have a namespace binding for the specified prefix, recursively.
+ * If false, the new namespace binding will not be inherited.
+ * @throws IllegalArgumentException if the target element already has a namespace binding for this prefix,
+ * or if the namespace code represents a namespace undeclaration
+ */
+
+ public void addNamespace(/*@NotNull*/ NamespaceBinding nscode, boolean inherit) {
+ if ((nscode.getURI().length() == 0)) {
+ throw new IllegalArgumentException("Cannot add a namespace undeclaration");
+ }
+ addNamespaceInternal(nscode, true);
+
+ // The data model is such that namespaces are inherited by default. If inheritance is NOT requested,
+ // we must process the children to add namespace undeclarations
+ if (hasChildNodes() && !inherit) {
+ AxisIterator kids = iterateChildren(NodeKindTest.ELEMENT);
+ while (true) {
+ ElementImpl child = (ElementImpl) kids.next();
+ if (child == null) {
+ break;
+ }
+ child.addNamespaceInternal(nscode, false);
+ }
+ }
+ }
+
+ private void addNamespaceInternal(/*@NotNull*/ NamespaceBinding nscode, boolean externalCall) {
+ if (namespaceList == null) {
+ namespaceList = new NamespaceBinding[]{nscode};
+ } else {
+ NamespaceBinding[] nsList = namespaceList;
+ for (int i = 0; i < nsList.length; i++) {
+ if (nsList[i].equals(nscode)) {
+ return;
+ }
+ if ((nsList[i].getPrefix().equals(nscode.getPrefix()))) {
+ if ((nsList[i].getURI()).length() == 0) {
+ // this is an undeclaration; replace it with the new declaration
+ nsList[i] = nscode;
+ return;
+ } else if (externalCall) {
+ throw new IllegalArgumentException("New namespace conflicts with existing namespace binding");
+ } else {
+ return;
+ }
+ }
+ }
+ int len = nsList.length;
+ NamespaceBinding[] ns2 = new NamespaceBinding[len + 1];
+ System.arraycopy(nsList, 0, ns2, 0, len);
+ ns2[len] = nscode;
+ namespaceList = ns2;
+ }
+ }
+
+
+ /**
+ * Replace the string-value of this node
+ *
+ * @param stringValue the new string value
+ */
+
+ public void replaceStringValue(/*@NotNull*/ CharSequence stringValue) {
+ if (stringValue.length() == 0) {
+ setChildren(null);
+ } else {
+ TextImpl text = new TextImpl(stringValue.toString());
+ text.setRawParent(this);
+ setChildren(text);
+ }
+ }
+
+ /**
+ * Add an attribute to this element node.
+ * <p/>
+ * <p>If this node is not an element, or if the supplied node is not an attribute, the method
+ * takes no action. If the element already has an attribute with this name, the method
+ * throws an exception.</p>
+ * <p/>
+ * <p>This method does not perform any namespace fixup. It is the caller's responsibility
+ * to ensure that any namespace prefix used in the name of the attribute (or in its value
+ * if it has a namespace-sensitive type) is declared on this element.</p>
+ *
+ * @param nameCode the name of the new attribute
+ * @param attType the type annotation of the new attribute
+ * @param value the string value of the new attribute
+ * @param properties properties including IS_ID and IS_IDREF properties
+ * @throws IllegalStateException if the element already has an attribute with the given name.
+ */
+
+ public void addAttribute(/*@NotNull*/ NodeName nameCode, SimpleType attType, /*@NotNull*/ CharSequence value, int properties) {
+ if (attributeList == null || attributeList.getLength() == 0) {
+ attributeList = new AttributeCollectionImpl(getConfiguration());
+ }
+ AttributeCollectionImpl atts = (AttributeCollectionImpl) attributeList;
+ int index = atts.findByNodeName(nameCode);
+ if (index == -1) {
+ atts.addAttribute(nameCode, attType, value.toString(), 0, 0);
+ } else {
+ throw new IllegalStateException(
+ "Cannot add an attribute to an element as it already has an attribute with the specified name");
+ }
+ if (!nameCode.isInNamespace("")) {
+ // The new attribute name is in a namespace
+ NamespaceBinding binding = nameCode.getNamespaceBinding();
+ String prefix = binding.getPrefix();
+ String uc = getURIForPrefix(prefix, false);
+ if (uc == null) {
+ // The namespace is not already declared on the element
+ addNamespace(binding, true);
+ } else if (!uc.equals(binding.getURI())) {
+ throw new IllegalStateException(
+ "Namespace binding of new name conflicts with existing namespace binding");
+ }
+ }
+ if ((properties & ReceiverOptions.IS_ID) != 0) {
+ DocumentImpl root = getPhysicalRoot();
+ if (root != null) {
+ root.registerID(this, Whitespace.trim(value));
+ }
+ }
+ }
+
+ /**
+ * Remove an attribute from this element node
+ * <p/>
+ * <p>If this node is not an element, or if the specified node is not an attribute
+ * of this element, this method takes no action.</p>
+ * <p/>
+ * <p>The attribute object itself becomes unusable; any attempt to use this attribute object,
+ * or any other object representing the same node, is likely to result in an exception.</p>
+ *
+ * @param attribute the attribute node to be removed
+ */
+
+ public void removeAttribute(/*@NotNull*/ NodeInfo attribute) {
+ if (!(attribute instanceof AttributeImpl)) {
+ return; // no action
+ }
+ AttributeCollectionImpl atts = (AttributeCollectionImpl) getAttributeList();
+ int index = ((AttributeImpl) attribute).getSiblingPosition();
+ if (index >= 0 && atts.isId(index)) {
+ DocumentImpl root = getPhysicalRoot();
+ root.deregisterID(atts.getValue(index));
+ }
+ atts.removeAttribute(index);
+ ((AttributeImpl) attribute).setRawParent(null);
+ }
+
+
+ /**
+ * Remove type information from this node (and its ancestors, recursively).
+ * This method implements the upd:removeType() primitive defined in the XQuery Update specification
+ */
+
+ public void removeTypeAnnotation() {
+ int t = getTypeAnnotation();
+ if (t != StandardNames.XS_UNTYPED) {
+ typeCode = StandardNames.XS_ANY_TYPE;
+ getRawParent().removeTypeAnnotation();
+ }
+ }
+
+ /**
+ * Set the namespace declarations for the element
+ *
+ * @param namespaces the list of namespace codes
+ * @param namespacesUsed the number of entries in the list that are used
+ */
+
+ public void setNamespaceDeclarations(NamespaceBinding[] namespaces, int namespacesUsed) {
+ namespaceList = new NamespaceBinding[namespacesUsed];
+ System.arraycopy(namespaces, 0, namespaceList, 0, namespacesUsed);
+ }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ *
+ * @param prefix the namespace prefix. May be the zero-length string, indicating
+ * that there is no prefix. This indicates either the default namespace or the
+ * null namespace, depending on the value of useDefault.
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is "". If false, the method returns "" when the prefix is "".
+ * @return the uri for the namespace, or null if the prefix is not in scope.
+ * The "null namespace" is represented by the pseudo-URI "".
+ */
+
+ /*@Nullable*/
+ public String getURIForPrefix(/*@NotNull*/ String prefix, boolean useDefault) {
+ if (prefix.equals("xml")) {
+ return NamespaceConstant.XML;
+ }
+ if (prefix.length() == 0 && !useDefault) {
+ return NamespaceConstant.NULL;
+ }
+
+ if (namespaceList != null) {
+ for (NamespaceBinding aNamespaceList : namespaceList) {
+ if ((aNamespaceList.getPrefix().equals(prefix))) {
+ String uri = aNamespaceList.getURI();
+ return (uri.length() == 0 && prefix.length() != 0 ? null : uri);
+ }
+ }
+ }
+ NodeInfo next = getRawParent();
+ if (next.getNodeKind() == Type.DOCUMENT) {
+ // prefixCode==0 represents the empty namespace prefix ""
+ return (prefix.length() == 0 ? NamespaceConstant.NULL : null);
+ } else {
+ return ((ElementImpl) next).getURIForPrefix(prefix, useDefault);
+ }
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ /*@Nullable*/
+ public Iterator<String> iteratePrefixes() {
+ return new Iterator<String>() {
+ /*@Nullable*/ private NamePool pool = null;
+ private Iterator<NamespaceBinding> iter = NamespaceIterator.iterateNamespaces(ElementImpl.this);
+
+ public boolean hasNext() {
+ return (pool == null || iter.hasNext());
+ }
+
+ public String next() {
+ if (pool == null) {
+ pool = getNamePool();
+ return "xml";
+ } else {
+ return iter.next().getPrefix();
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+ };
+ }
+
+ /**
+ * Search the NamespaceList for a given URI, returning the corresponding prefix.
+ *
+ * @param uri The URI to be matched.
+ * @return The prefix corresponding to this URI. If not found, return null. If there is
+ * more than one prefix matching the URI, the first one found is returned. If the URI matches
+ * the default namespace, return an empty string.
+ */
+
+ /*@Nullable*/
+ public String getPrefixForURI(/*@NotNull*/ String uri) {
+ if (uri.equals(NamespaceConstant.XML)) {
+ return "xml";
+ }
+ for (Iterator<String> iter = iteratePrefixes(); iter.hasNext(); ) {
+ String prefix = iter.next();
+ if (uri.equals(getURIForPrefix(prefix, true))) {
+ return uri;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of NamespaceBinding objects representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to null.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return (namespaceList == null ? NamespaceBinding.EMPTY_ARRAY : namespaceList);
+ }
+
+ /**
+ * Ensure that a child element being inserted into a tree has the right namespace declarations.
+ * Redundant declarations should be removed. If the child is in the null namespace but the parent has a default
+ * namespace, xmlns="" should be added. If inherit is false, namespace undeclarations should be added for all
+ * namespaces that are declared on the parent but not on the child.
+ *
+ * @param inherit true if the child is to inherit the inscope namespaces of its new parent
+ */
+
+ protected void fixupInsertedNamespaces(boolean inherit) {
+ if (getRawParent().getNodeKind() == Type.DOCUMENT) {
+ return;
+ }
+
+ Set<NamespaceBinding> childNamespaces = new HashSet<NamespaceBinding>();
+ if (namespaceList != null) {
+ childNamespaces.addAll(Arrays.asList(namespaceList));
+ }
+
+ NamespaceResolver inscope = new InscopeNamespaceResolver(getRawParent());
+
+ // If the child is in the null namespace but the parent has a default namespace, xmlns="" should be added.
+
+ if (getURI().length() == 0 && inscope.getURIForPrefix("", true).length() != 0) {
+ childNamespaces.add(NamespaceBinding.DEFAULT_UNDECLARATION);
+ }
+
+ // Namespaces present on the parent but not on the child should be undeclared (if requested)
+
+ if (!inherit) {
+ Iterator it = inscope.iteratePrefixes();
+ while (it.hasNext()) {
+ String prefix = (String) it.next();
+ if (!prefix.equals("xml")) {
+ boolean found = false;
+ if (namespaceList != null) {
+ for (NamespaceBinding aNamespaceList : namespaceList) {
+ if ((aNamespaceList.getPrefix()).equals(prefix)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ childNamespaces.add(new NamespaceBinding(prefix, ""));
+ }
+ }
+ }
+ }
+
+ // Redundant namespaces should be removed
+
+ if (namespaceList != null) {
+ for (NamespaceBinding nscode : namespaceList) {
+ String prefix = nscode.getPrefix();
+ String uri = nscode.getURI();
+ String parentUri = inscope.getURIForPrefix(prefix, true);
+ if (parentUri != null && parentUri.equals(uri)) {
+ // the namespace declaration is redundant
+ childNamespaces.remove(nscode);
+ }
+ }
+ }
+ NamespaceBinding[] n2 = new NamespaceBinding[childNamespaces.size()];
+ int j = 0;
+ for (NamespaceBinding childNamespace : childNamespaces) {
+ n2[j++] = childNamespace;
+ }
+ namespaceList = n2;
+ }
+
+ /**
+ * Get the attribute list for this element.
+ *
+ * @return The attribute list. This will not include any
+ * namespace attributes. The attribute names will be in expanded form, with prefixes
+ * replaced by URIs
+ */
+
+ public AttributeCollection getAttributeList() {
+ return (attributeList == null ? AttributeCollectionImpl.EMPTY_ATTRIBUTE_COLLECTION : attributeList);
+ }
+
+ /**
+ * Get the namespace list for this element.
+ *
+ * @return The raw namespace list, as an array of name codes
+ */
+
+ /*@Nullable*/
+ public NamespaceBinding[] getNamespaceList() {
+ return namespaceList;
+ }
+
+ /**
+ * Get the value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name, or "" if the attribute is not in a namepsace
+ * @param localName the local part of the attribute name
+ * @return the attribute value if it exists or null if not
+ */
+
+ /*@Nullable*/
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String localName) {
+ return (attributeList == null ? null : attributeList.getValue(uri, localName));
+ }
+
+ /**
+ * Get the value of the attribute with a given fingerprint.
+ *
+ * @param fp the fingerprint of the required attribute
+ * @return the string value of the attribute if present, or null if absent
+ */
+ @Override
+ public String getAttributeValue(int fp) {
+ return (attributeList == null ? null : attributeList.getValueByFingerprint(fp));
+ }
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ // This is an approximation. For a union type, we check that the actual value is a valid NCName,
+ // but we don't check that it was validated against the member type of the union that is an ID type.
+ SchemaType type = getSchemaType();
+ return type.getFingerprint() == StandardNames.XS_ID ||
+ type.isIdType() && getConfiguration().getNameChecker().isValidNCName(getStringValueCS());
+ }
+}
+
diff --git a/sf/saxon/tree/linked/FollowingEnumeration.java b/sf/saxon/tree/linked/FollowingEnumeration.java
new file mode 100644
index 0000000..b7e269a
--- /dev/null
+++ b/sf/saxon/tree/linked/FollowingEnumeration.java
@@ -0,0 +1,49 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.Type;
+
+final class FollowingEnumeration extends TreeEnumeration {
+
+ private NodeImpl root;
+
+ public FollowingEnumeration(NodeImpl node, NodeTest nodeTest) {
+ super(node, nodeTest);
+ root = (DocumentImpl)node.getDocumentRoot();
+ // skip the descendant nodes if any
+ int type = node.getNodeKind();
+ if (type==Type.ATTRIBUTE || type==Type.NAMESPACE) {
+ next = node.getParent().getNextInDocument(root);
+ } else {
+ do {
+ next = node.getNextSibling();
+ if (next==null) node = node.getParent();
+ } while (next==null && node!=null);
+ }
+ while (!conforms(next)) {
+ step();
+ }
+ }
+
+ protected void step() {
+ next = next.getNextInDocument(root);
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new FollowingEnumeration(start, nodeTest);
+ }
+
+}
+
diff --git a/sf/saxon/tree/linked/FollowingSiblingEnumeration.java b/sf/saxon/tree/linked/FollowingSiblingEnumeration.java
new file mode 100644
index 0000000..ec31377
--- /dev/null
+++ b/sf/saxon/tree/linked/FollowingSiblingEnumeration.java
@@ -0,0 +1,32 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+
+final class FollowingSiblingEnumeration extends TreeEnumeration {
+
+ public FollowingSiblingEnumeration(NodeImpl node, NodeTest nodeTest) {
+ super(node, nodeTest);
+ advance();
+ }
+
+ protected void step() {
+ next = (NodeImpl)next.getNextSibling();
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new FollowingSiblingEnumeration(start, nodeTest);
+ }
+}
+
diff --git a/sf/saxon/tree/linked/LineNumberMap.java b/sf/saxon/tree/linked/LineNumberMap.java
new file mode 100644
index 0000000..9e00d41
--- /dev/null
+++ b/sf/saxon/tree/linked/LineNumberMap.java
@@ -0,0 +1,114 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import java.util.Arrays;
+
+/**
+ * Line numbers are not held in nodes in the tree, because they are not usually needed.
+ * This class provides a map from element sequence numbers to line numbers: it is
+ * linked to the root node of the tree.
+ *
+ * @author Michael H. Kay
+ */
+
+public class LineNumberMap {
+
+ private int[] sequenceNumbers;
+ private int[] lineNumbers;
+ private int[] columnNumbers;
+ private int allocated;
+
+ /**
+ * Create a LineNumberMap with an initial capacity of 200 nodes, which is expanded as necessary
+ */
+
+ public LineNumberMap() {
+ sequenceNumbers = new int[200];
+ lineNumbers = new int[200];
+ columnNumbers = new int[200];
+ allocated = 0;
+ }
+
+ /**
+ * Set the line number corresponding to a given sequence number
+ * @param sequence the sequence number of the node
+ * @param line the line number position of the node
+ * @param column the column position of the node
+ */
+
+ public void setLineAndColumn(int sequence, int line, int column) {
+ if (sequenceNumbers.length <= allocated + 1) {
+ int[] s = new int[allocated * 2];
+ int[] l = new int[allocated * 2];
+ int[] c = new int[allocated * 2];
+ System.arraycopy(sequenceNumbers, 0, s, 0, allocated);
+ System.arraycopy(lineNumbers, 0, l, 0, allocated);
+ System.arraycopy(columnNumbers, 0, c, 0, allocated);
+ sequenceNumbers = s;
+ lineNumbers = l;
+ columnNumbers = c;
+ }
+ sequenceNumbers[allocated] = sequence;
+ lineNumbers[allocated] = line;
+ columnNumbers[allocated] = column;
+ allocated++;
+ }
+
+ /**
+ * Get the line number corresponding to a given sequence number
+ * @param sequence the sequence number held in the node
+ * @return the corresponding line number
+ */
+
+ public int getLineNumber(int sequence) {
+ if (sequenceNumbers.length > allocated) {
+ condense();
+ }
+ int index = Arrays.binarySearch(sequenceNumbers, sequence);
+ if (index < 0) {
+ index = -index - 1;
+ if (index > lineNumbers.length - 1) {
+ index = lineNumbers.length - 1;
+ }
+ }
+ return lineNumbers[index];
+ }
+
+ /**
+ * Get the column number corresponding to a given sequence number
+ * @param sequence the sequence number held in the node
+ * @return the corresponding column number
+ */
+
+ public int getColumnNumber(int sequence) {
+ if (sequenceNumbers.length > allocated) {
+ condense();
+ }
+ int index = Arrays.binarySearch(sequenceNumbers, sequence);
+ if (index < 0) {
+ index = -index - 1;
+ }
+ return columnNumbers[index];
+ }
+
+ private synchronized void condense() {
+ int[] s = new int[allocated];
+ int[] l = new int[allocated];
+ int[] c = new int[allocated];
+ System.arraycopy(sequenceNumbers, 0, s, 0, allocated);
+ System.arraycopy(lineNumbers, 0, l, 0, allocated);
+ System.arraycopy(columnNumbers, 0, c, 0, allocated);
+ sequenceNumbers = s;
+ lineNumbers = l;
+ columnNumbers = c;
+ }
+
+
+}
+
diff --git a/sf/saxon/tree/linked/LinkedBuilderMonitor.java b/sf/saxon/tree/linked/LinkedBuilderMonitor.java
new file mode 100644
index 0000000..6029c80
--- /dev/null
+++ b/sf/saxon/tree/linked/LinkedBuilderMonitor.java
@@ -0,0 +1,105 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.event.BuilderMonitor;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+
+/**
+ * Monitor construction of a TinyTree. This allows a marker to be set during tree construction, in such a way
+ * that the node corresponding to the marker can be retrieved at the end of tree construction. This is used in the
+ * implementation of the XSLT 3.0 snapshot function.
+ */
+public class LinkedBuilderMonitor extends BuilderMonitor {
+
+ private LinkedTreeBuilder builder;
+ private int mark = -1;
+ /*@Nullable*/ private NodeInfo markedNode;
+
+ public LinkedBuilderMonitor(/*@NotNull*/ LinkedTreeBuilder builder) {
+ super(builder);
+ this.builder = builder;
+ }
+
+ public void markNextNode(int nodeKind) {
+ mark = nodeKind;
+ }
+
+ public void startDocument(int properties) throws XPathException {
+ super.startDocument(properties);
+ if (mark == Type.DOCUMENT) {
+ markedNode = builder.getCurrentParentNode();
+ }
+ mark = -1;
+ }
+
+
+ public void startContent() throws XPathException {
+ super.startContent();
+ if (mark == Type.ELEMENT) {
+ markedNode = builder.getCurrentParentNode();
+ }
+ mark = -1;
+ }
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ super.characters(chars, locationId, properties);
+ if (mark == Type.TEXT) {
+ markedNode = builder.getCurrentLeafNode();
+ }
+ mark = -1;
+ }
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ super.comment(chars, locationId, properties);
+ if (mark == Type.COMMENT) {
+ markedNode = builder.getCurrentLeafNode();
+ }
+ mark = -1;
+ }
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ super.processingInstruction(target, data, locationId, properties);
+ if (mark == Type.PROCESSING_INSTRUCTION) {
+ markedNode = builder.getCurrentLeafNode();
+ }
+ mark = -1;
+ }
+
+ public void attribute(/*@NotNull*/ NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ super.attribute(nameCode, typeCode, value, locationId, properties);
+ if (mark == Type.ATTRIBUTE) {
+ NodeInfo element = builder.getCurrentParentNode();
+ markedNode = (NodeInfo)element.iterateAxis(
+ AxisInfo.ATTRIBUTE, new NameTest(Type.ATTRIBUTE, nameCode, element.getNamePool())).next();
+ mark = -1;
+ }
+ }
+
+ public void namespace(/*@NotNull*/ NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ super.namespace(namespaceBinding, properties);
+ if (mark == Type.NAMESPACE) {
+ NodeInfo element = builder.getCurrentParentNode();
+ NamePool pool = element.getNamePool();
+ String prefix = namespaceBinding.getPrefix();
+ markedNode = (NodeInfo)element.iterateAxis(
+ AxisInfo.NAMESPACE, new NameTest(Type.NAMESPACE, "", prefix, pool)).next();
+ mark = -1;
+ }
+ }
+
+ /*@Nullable*/ public NodeInfo getMarkedNode() {
+ return markedNode;
+ }
+}
+
diff --git a/sf/saxon/tree/linked/LinkedTreeBuilder.java b/sf/saxon/tree/linked/LinkedTreeBuilder.java
new file mode 100644
index 0000000..45b4a53
--- /dev/null
+++ b/sf/saxon/tree/linked/LinkedTreeBuilder.java
@@ -0,0 +1,438 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.event.*;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+
+import java.util.ArrayList;
+
+
+/**
+ * The LinkedTreeBuilder class is responsible for taking a stream of Receiver events and constructing
+ * a Document tree using the linked tree implementation.
+ * @author Michael H. Kay
+ */
+
+public class LinkedTreeBuilder extends Builder
+
+{
+// private static AttributeCollectionImpl emptyAttributeCollection =
+// new AttributeCollectionImpl((Configuration)null);
+
+ /*@Nullable*/ private ParentNodeImpl currentNode;
+ private boolean contentStarted = false; // provides a minimal check on correct sequence of calls
+ private NodeFactory nodeFactory;
+ /*@NotNull*/ private int[] size = new int[100]; // stack of number of children for each open node
+ private int depth = 0;
+ private ArrayList<NodeImpl[]> arrays = new ArrayList<NodeImpl[]>(20); // reusable arrays for creating nodes
+ private NodeName elementNameCode;
+ private SchemaType elementType;
+ private boolean isNilled;
+ private int pendingLocationId;
+ /*@Nullable*/ private AttributeCollectionImpl attributes;
+ private NamespaceBinding[] namespaces;
+ private int namespacesUsed;
+ private boolean allocateSequenceNumbers = true;
+ private int nextNodeNumber = 1;
+ private static final NamespaceBinding[] EMPTY_NAMESPACE_LIST = new NamespaceBinding[0];
+
+ /**
+ * Create a Builder and initialise variables
+ * @param pipe the pipeline configuration
+ */
+
+ public LinkedTreeBuilder(PipelineConfiguration pipe) {
+ super(pipe);
+ nodeFactory = new DefaultNodeFactory();
+ // System.err.println("new TreeBuilder " + this);
+ }
+
+ /**
+ * Get the current root node. This will normally be a document node, but if the root of the tree
+ * is an element node, it can be an element.
+ * @return the root of the tree that is currently being built, or that has been most recently built
+ * using this builder
+ */
+
+ /*@Nullable*/ public NodeInfo getCurrentRoot() {
+ NodeInfo physicalRoot = currentRoot;
+ if (physicalRoot instanceof DocumentImpl && ((DocumentImpl)physicalRoot).isImaginary()) {
+ return ((DocumentImpl)physicalRoot).getDocumentElement();
+ } else {
+ return physicalRoot;
+ }
+ }
+
+ public void reset() {
+ super.reset();
+ currentNode = null;
+ nodeFactory = null;
+ depth = 0;
+ allocateSequenceNumbers = true;
+ nextNodeNumber = 1;
+ }
+
+ /**
+ * Set whether the builder should allocate sequence numbers to elements as they are added to the
+ * tree. This is normally done, because it provides a quick way of comparing document order. But
+ * nodes added using XQuery update are not sequence-numbered.
+ * @param allocate true if sequence numbers are to be allocated
+ */
+
+ public void setAllocateSequenceNumbers(boolean allocate) {
+ allocateSequenceNumbers = allocate;
+ }
+
+ /**
+ * Set the Node Factory to use. If none is specified, the Builder uses its own.
+ * @param factory the node factory to be used. This allows custom objects to be used to represent
+ * the elements in the tree.
+ */
+
+ public void setNodeFactory(NodeFactory factory) {
+ nodeFactory = factory;
+ }
+
+ /**
+ * Open the stream of Receiver events
+ */
+
+ public void open () {
+ started = true;
+ depth = 0;
+ size[depth] = 0;
+ super.open();
+ }
+
+
+ /**
+ * Start of a document node.
+ * This event is ignored: we simply add the contained elements to the current document
+ */
+
+ public void startDocument(int properties) throws XPathException {
+ DocumentImpl doc = new DocumentImpl();
+ currentRoot = doc;
+ doc.setSystemId(getSystemId());
+ doc.setBaseURI(getBaseURI());
+ doc.setConfiguration(config);
+ currentNode = doc;
+ depth = 0;
+ size[depth] = 0;
+ doc.setRawSequenceNumber(0);
+ if (lineNumbering) {
+ doc.setLineNumbering();
+ }
+ contentStarted = true;
+ }
+
+ /**
+ * Notify the end of the document
+ */
+
+ public void endDocument() throws XPathException {
+ //System.err.println("End document depth=" + depth);
+ currentNode.compact(size[depth]);
+ }
+
+ /**
+ * Close the stream of Receiver events
+ */
+
+ public void close () throws XPathException {
+ // System.err.println("TreeBuilder: " + this + " End document");
+ if (currentNode==null) {
+ return; // can be called twice on an error path
+ }
+ currentNode.compact(size[depth]);
+ currentNode = null;
+
+ // we're not going to use this Builder again so give the garbage collector
+ // something to play with
+ arrays = null;
+
+ super.close();
+ nodeFactory = null;
+ }
+
+ /**
+ * Notify the start of an element
+ */
+
+ public void startElement (NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ //System.err.println("LinkedTreeBuilder: " + this + " Start element depth=" + depth);
+ if (currentNode == null) {
+ startDocument(0);
+ ((DocumentImpl)currentRoot).setImaginary(true);
+ }
+ elementNameCode = nameCode;
+ elementType = typeCode;
+ isNilled = ((properties & ReceiverOptions.NILLED_ELEMENT) != 0);
+ pendingLocationId = locationId;
+ namespacesUsed = 0;
+ attributes = null;
+ contentStarted = false;
+ }
+
+ public void namespace (NamespaceBinding namespaceBinding, int properties) {
+ if (contentStarted) {
+ throw new IllegalStateException("namespace() called after startContent()");
+ }
+ if (namespaces==null) {
+ namespaces = new NamespaceBinding[5];
+ }
+ if (namespacesUsed == namespaces.length) {
+ NamespaceBinding[] ns2 = new NamespaceBinding[namespaces.length * 2];
+ System.arraycopy(namespaces, 0, ns2, 0, namespacesUsed);
+ namespaces = ns2;
+ }
+ namespaces[namespacesUsed++] = namespaceBinding;
+ }
+
+ public void attribute(NodeName attName, SimpleType typeCode, /*@NotNull*/ CharSequence value, int locationId, int properties)
+ throws XPathException {
+ properties &= ~ReceiverOptions.DISABLE_ESCAPING;
+ if (contentStarted) {
+ throw new IllegalStateException("attribute() called after startContent()");
+ }
+ if (attributes==null) {
+ attributes = new AttributeCollectionImpl(config);
+ }
+ attributes.addAttribute(attName, typeCode, value.toString(), locationId, properties);
+ }
+
+ public void startContent() throws XPathException {
+ // System.err.println("TreeBuilder: " + this + " startContent()");
+ if (contentStarted) {
+ throw new IllegalStateException("startContent() called more than once");
+ }
+ contentStarted = true;
+ if (attributes == null) {
+ attributes = AttributeCollectionImpl.EMPTY_ATTRIBUTE_COLLECTION;
+ } else {
+ attributes.compact();
+ }
+
+ NamespaceBinding[] nslist = namespaces;
+ if (nslist == null || namespacesUsed == 0) {
+ nslist = EMPTY_NAMESPACE_LIST;
+ }
+
+ ElementImpl elem = nodeFactory.makeElementNode(
+ currentNode, elementNameCode, elementType, isNilled,
+ attributes, nslist, namespacesUsed,
+ pipe,
+ pendingLocationId, (allocateSequenceNumbers ? nextNodeNumber++ : -1));
+
+ namespacesUsed = 0;
+ attributes = null;
+
+ // the initial array used for pointing to children will be discarded when the exact number
+ // of children in known. Therefore, it can be reused. So we allocate an initial array from
+ // a pool of reusable arrays. A nesting depth of >20 is so rare that we don't bother.
+
+ while (depth >= arrays.size()) {
+ arrays.add(new NodeImpl[20]);
+ }
+ elem.setChildren(arrays.get(depth));
+
+ currentNode.addChild(elem, size[depth]++);
+ if (depth >= size.length - 1) {
+ int[] newsize = new int[size.length * 2];
+ System.arraycopy(size, 0, newsize, 0, size.length);
+ size = newsize;
+ }
+ size[++depth] = 0;
+ namespacesUsed = 0;
+
+ if (currentNode instanceof DocumentInfo) {
+ ((DocumentImpl)currentNode).setDocumentElement(elem);
+ }
+
+ currentNode = elem;
+ }
+
+ /**
+ * Notify the end of an element
+ */
+
+ public void endElement () throws XPathException {
+ //System.err.println("End element depth=" + depth);
+ if (!contentStarted) {
+ throw new IllegalStateException("missing call on startContent()");
+ }
+ currentNode.compact(size[depth]);
+ depth--;
+ currentNode = (ParentNodeImpl)currentNode.getParent();
+ }
+
+ /**
+ * Notify a text node. Adjacent text nodes must have already been merged
+ */
+
+ public void characters (/*@NotNull*/ CharSequence chars, int locationId, int properties) throws XPathException {
+ // System.err.println("Characters: " + chars.toString() + " depth=" + depth);
+ if (!contentStarted) {
+ throw new IllegalStateException("missing call on startContent()");
+ }
+ if (chars.length()>0) {
+ NodeInfo prev = currentNode.getNthChild(size[depth]-1);
+ if (prev instanceof TextImpl) {
+ ((TextImpl)prev).appendStringValue(chars.toString());
+ } else {
+ TextImpl n = nodeFactory.makeTextNode(currentNode, chars);
+ //TextImpl n = new TextImpl(chars.toString());
+ currentNode.addChild(n, size[depth]++);
+ }
+ }
+ }
+
+ /**
+ * Notify a processing instruction
+ */
+
+ public void processingInstruction (String name, /*@NotNull*/ CharSequence remainder, int locationId, int properties) {
+ if (!contentStarted) {
+ throw new IllegalStateException("missing call on startContent()");
+ }
+ int nameCode = namePool.allocate("", "", name);
+ ProcInstImpl pi = new ProcInstImpl(nameCode, remainder.toString());
+ currentNode.addChild(pi, size[depth]++);
+ LocationProvider locator = pipe.getLocationProvider();
+ if (locator!=null) {
+ pi.setLocation(locator.getSystemId(locationId),
+ locator.getLineNumber(locationId));
+ }
+ }
+
+ /**
+ * Notify a comment
+ */
+
+ public void comment (/*@NotNull*/ CharSequence chars, int locationId, int properties) throws XPathException {
+ if (!contentStarted) {
+ throw new IllegalStateException("missing call on startContent()");
+ }
+ CommentImpl comment = new CommentImpl(chars.toString());
+ currentNode.addChild(comment, size[depth]++);
+ }
+
+ /**
+ * Get the current document or element node
+ * @return the most recently started document or element node (to which children are currently being added)
+ * In the case of elements, this is only available after startContent() has been called
+ */
+
+ /*@Nullable*/ public ParentNodeImpl getCurrentParentNode() {
+ return currentNode;
+ }
+
+ /**
+ * Get the current text, comment, or processing instruction node
+ * @return if any text, comment, or processing instruction nodes have been added to the current parent
+ * node, then return that text, comment, or PI; otherwise return null
+ */
+
+ /*@NotNull*/ public NodeImpl getCurrentLeafNode() {
+ return (NodeImpl)currentNode.getLastChild();
+ }
+
+
+ /**
+ * graftElement() allows an element node to be transferred from one tree to another.
+ * This is a dangerous internal interface which is used only to contruct a stylesheet
+ * tree from a stylesheet using the "literal result element as stylesheet" syntax.
+ * The supplied element is grafted onto the current element as its only child.
+ * @param element the element to be grafted in as a new child.
+ */
+
+ public void graftElement(ElementImpl element) throws XPathException {
+ currentNode.addChild(element, size[depth]++);
+ }
+
+ /**
+ * Set an unparsed entity URI for the document
+ */
+
+ public void setUnparsedEntity(String name, String uri, String publicId) {
+ ((DocumentImpl)currentRoot).setUnparsedEntity(name, uri, publicId);
+ }
+
+ /**
+ * Get a builder monitor for this builder. This must be called immediately after opening the builder,
+ * and all events to the builder must thenceforth be sent via the BuilderMonitor.
+ * @return a new BuilderMonitor appropriate to this kind of Builder; or null if the Builder does
+ * not provide this service
+ */
+
+ /*@NotNull*/ public BuilderMonitor getBuilderMonitor() {
+ return new LinkedBuilderMonitor(this);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // Inner class DefaultNodeFactory. This creates the nodes in the tree.
+ // It can be overridden, e.g. when building the stylesheet tree
+ //////////////////////////////////////////////////////////////////////////////
+
+ private static class DefaultNodeFactory implements NodeFactory {
+
+ /*@NotNull*/ public ElementImpl makeElementNode(
+ /*@NotNull*/ NodeInfo parent,
+ /*@NotNull*/ NodeName nameCode,
+ SchemaType elementType,
+ boolean isNilled,
+ AttributeCollectionImpl attlist,
+ NamespaceBinding[] namespaces,
+ int namespacesUsed,
+ /*@NotNull*/ PipelineConfiguration pipe,
+ int locationId,
+ int sequenceNumber)
+
+ {
+ ElementImpl e = new ElementImpl();
+ if (namespacesUsed > 0) {
+ e.setNamespaceDeclarations(namespaces, namespacesUsed);
+ }
+
+ e.initialise(nameCode, elementType, attlist, parent, sequenceNumber);
+ if (isNilled) {
+ e.setTypeAnnotation(e.getTypeAnnotation() | NodeInfo.IS_NILLED);
+ }
+ LocationProvider locator = pipe.getLocationProvider();
+ if (locator!=null) {
+ String baseURI = locator.getSystemId(locationId);
+ int lineNumber = locator.getLineNumber(locationId);
+ int columnNumber = locator.getColumnNumber(locationId);
+ e.setLocation(baseURI, lineNumber, columnNumber);
+ }
+ return e;
+ }
+
+ /**
+ * Make a text node
+ *
+ * @param content the content of the text node
+ * @return the constructed text node
+ */
+ public TextImpl makeTextNode(NodeInfo parent, CharSequence content) {
+ return new TextImpl(content.toString());
+ }
+ }
+
+
+
+}
+
diff --git a/sf/saxon/tree/linked/NodeFactory.java b/sf/saxon/tree/linked/NodeFactory.java
new file mode 100644
index 0000000..1e55f24
--- /dev/null
+++ b/sf/saxon/tree/linked/NodeFactory.java
@@ -0,0 +1,70 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.tree.util.AttributeCollectionImpl;
+import net.sf.saxon.type.SchemaType;
+
+import java.io.Serializable;
+
+
+/**
+ * Interface NodeFactory. <br>
+ * A Factory for nodes used to build a tree. <br>
+ * Currently only allows Element nodes to be user-constructed.
+ *
+ * @author Michael H. Kay
+ * @version 25 February 2000
+ */
+
+public interface NodeFactory extends Serializable {
+
+ /**
+ * Create an Element node
+ *
+ * @param parent The parent element
+ * @param nameCode The element name
+ * @param elementType The type annotation of the element
+ * @param isNilled true if the element is to be marked as nilled
+ * @param attlist The attribute collection, excluding any namespace attributes
+ * @param namespaces List of new namespace declarations for this element, as a sequence
+ * of namespace codes representing pairs of strings: (prefix1, uri1), (prefix2, uri2)...
+ * @param namespacesUsed the number of elemnts of the namespaces array actually used
+ * @param pipe The pipeline configuration (provides access to the error listener and the
+ * location provider)
+ * @param locationId Indicates the source document and line number containing the node
+ * @param sequenceNumber Sequence number to be assigned to represent document order.
+ */
+
+ /*@Nullable*/
+ public ElementImpl makeElementNode(
+ NodeInfo parent,
+ NodeName nameCode,
+ SchemaType elementType,
+ boolean isNilled,
+ AttributeCollectionImpl attlist,
+ NamespaceBinding[] namespaces,
+ int namespacesUsed,
+ PipelineConfiguration pipe,
+ int locationId,
+ int sequenceNumber);
+
+ /**
+ * Make a text node
+ * @param content the content of the text node
+ * @return the constructed text node
+ */
+
+ public TextImpl makeTextNode(NodeInfo parent, CharSequence content);
+
+}
+
diff --git a/sf/saxon/tree/linked/NodeImpl.java b/sf/saxon/tree/linked/NodeImpl.java
new file mode 100644
index 0000000..dcb1821
--- /dev/null
+++ b/sf/saxon/tree/linked/NodeImpl.java
@@ -0,0 +1,1042 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.NamespaceNode;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.EmptyAxisIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.tree.util.SteppingNavigator;
+import net.sf.saxon.tree.util.SteppingNode;
+import net.sf.saxon.tree.wrapper.SiblingCountingNode;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+import javax.xml.transform.SourceLocator;
+
+
+/**
+ * A node in the "linked" tree representing any kind of node except a namespace node.
+ * Specific node kinds are represented by concrete subclasses.
+ *
+ * @author Michael H. Kay
+ */
+
+public abstract class NodeImpl
+ implements MutableNodeInfo, FingerprintedNode, SteppingNode, SiblingCountingNode, SourceLocator {
+
+ /*@Nullable*/ private ParentNodeImpl parent;
+ private int index; // Set to -1 when the node is deleted
+ /**
+ * Chararacteristic letters to identify each type of node, indexed using the node type
+ * values. These are used as the initial letter of the result of generate-id()
+ */
+
+ /*@NotNull*/ public static final char[] NODE_LETTER =
+ {'x', 'e', 'a', 't', 'x', 'x', 'x', 'p', 'c', 'r', 'x', 'x', 'x', 'n'};
+
+ /**
+ * To implement {@link Sequence}, this method returns the item itself
+ * @return this item
+ */
+
+ public Item head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ * @return a singleton iterator that returns this item
+ */
+
+ public SequenceIterator iterate() {
+ return SingletonIterator.makeIterator(this);
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ return getStringValue();
+ }
+
+ /**
+ * Get the type annotation of this node, if any
+ * @return the type annotation, as the integer name code of the type name
+ */
+
+ public int getTypeAnnotation() {
+ return StandardNames.XS_UNTYPED;
+ }
+
+ /**
+ * Get the type annotation
+ * @return the type annotation of the base node
+ */
+
+ public SchemaType getSchemaType() {
+ return Untyped.getInstance();
+ }
+
+ /**
+ * Get the column number of the node.
+ * The default implementation returns -1, meaning unknown
+ */
+
+ public int getColumnNumber() {
+ if (parent == null) {
+ return -1;
+ } else {
+ return parent.getColumnNumber();
+ }
+ }
+
+ /**
+ * Get the public identifier of the document entity containing this node.
+ * The default implementation returns null, meaning unknown
+ */
+
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+
+ /**
+ * Get the document number of the document containing this node. For a free-standing
+ * orphan node, just return the hashcode.
+ */
+
+ public long getDocumentNumber() {
+ return getPhysicalRoot().getDocumentNumber();
+ }
+
+
+ /**
+ * Get the index position of this node among its siblings (starting from 0)
+ * @return 0 for the first child, 1 for the second child, etc. Returns -1 for a node
+ * that has been deleted.
+ */
+ public final int getSiblingPosition() {
+ return index;
+ }
+
+ /**
+ * Set the index position. For internal use only
+ * @param index the position of the node among its siblings, counting from zero.
+ */
+
+ protected final void setSiblingPosition(int index) {
+ this.index = index;
+ }
+
+ /**
+ * Get the typed value.
+ *
+ * @return the typed value. If requireSingleton is set to true, the result will always be an
+ * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize() throws XPathException {
+ SchemaType stype = getSchemaType();
+ if (stype == Untyped.getInstance() || stype == BuiltInAtomicType.UNTYPED_ATOMIC) {
+ return new UntypedAtomicValue(getStringValueCS());
+ } else {
+ return stype.atomize(this);
+ }
+ }
+
+ /**
+ * Set the system ID of this node. This method is provided so that a NodeInfo
+ * implements the javax.xml.transform.Source interface, allowing a node to be
+ * used directly as the Source of a transformation
+ */
+
+ public void setSystemId(String uri) {
+ // overridden in DocumentImpl and ElementImpl
+ NodeInfo p = getParent();
+ if (p != null) {
+ p.setSystemId(uri);
+ }
+ }
+
+ /**
+ * Determine whether this is the same node as another node
+ *
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(NodeInfo other) {
+ // default implementation: differs for attribute and namespace nodes
+ return this == other;
+ }
+
+ /**
+ * The equals() method compares nodes for identity. It is defined to give the same result
+ * as isSameNodeInfo().
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ * @since 8.7 Previously, the effect of the equals() method was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics. It is safer to use isSameNodeInfo() for this reason.
+ * The equals() method has been defined because it is useful in contexts such as a Java Set or HashMap.
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof NodeInfo && isSameNodeInfo((NodeInfo)other);
+ }
+
+ /**
+ * Get the nameCode of the node. This is used to locate the name in the NamePool
+ */
+
+ public int getNameCode() {
+ // default implementation: return -1 for an unnamed node
+ return -1;
+ }
+
+ /**
+ * Get the fingerprint of the node. This is used to compare whether two nodes
+ * have equivalent names. Return -1 for a node with no name.
+ */
+
+ public int getFingerprint() {
+ int nameCode = getNameCode();
+ if (nameCode == -1) {
+ return -1;
+ }
+ return nameCode & NamePool.FP_MASK;
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ long seq = getSequenceNumber();
+ if (seq == -1L) {
+ getPhysicalRoot().generateId(buffer);
+ buffer.append(NODE_LETTER[getNodeKind()]);
+ buffer.append(Long.toString(seq) + "h" + hashCode());
+ } else {
+ parent.generateId(buffer);
+ buffer.append(NODE_LETTER[getNodeKind()]);
+ buffer.append(Integer.toString(index));
+ }
+ }
+
+ /**
+ * Get the system ID for the node. Default implementation for child nodes.
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return parent.getSystemId();
+ }
+
+ /**
+ * Get the base URI for the node. Default implementation for child nodes.
+ */
+
+ public String getBaseURI() {
+ return parent.getBaseURI();
+ }
+
+ /**
+ * Get the node sequence number (in document order). Sequence numbers are monotonic but not
+ * consecutive. In the current implementation, parent nodes (elements and roots) have a zero
+ * least-significant word, while namespaces, attributes, text nodes, comments, and PIs have
+ * the top word the same as their owner and the bottom half reflecting their relative position.
+ * This is the default implementation for child nodes.
+ * For nodes added by XQuery Update, the sequence number is -1L
+ * @return the sequence number if there is one, or -1L otherwise.
+ */
+
+ protected long getSequenceNumber() {
+ NodeImpl prev = this;
+ for (int i = 0; ; i++) {
+ if (prev instanceof ParentNodeImpl) {
+ long prevseq = prev.getSequenceNumber();
+ return (prevseq == -1L ? prevseq : prevseq + 0x10000 + i);
+ // note the 0x10000 is to leave room for namespace and attribute nodes.
+ }
+ assert prev != null;
+ prev = prev.getPreviousInDocument();
+ }
+
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ *
+ * @param other The other node, whose position is to be compared with this node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public final int compareOrder(/*@NotNull*/ NodeInfo other) {
+ if (other instanceof NamespaceNode) {
+ return 0 - other.compareOrder(this);
+ }
+ long a = getSequenceNumber();
+ long b = ((NodeImpl)other).getSequenceNumber();
+ if (a == -1L || b == -1L) {
+ // Nodes added by XQuery Update do not have sequence numbers
+ return Navigator.compareOrder(this, ((NodeImpl)other));
+ }
+ if (a < b) {
+ return -1;
+ }
+ if (a > b) {
+ return +1;
+ }
+ return 0;
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+ return Navigator.comparePosition(this, other);
+ }
+
+ /**
+ * Get the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return getPhysicalRoot().getConfiguration();
+ }
+
+ /**
+ * Get the NamePool
+ */
+
+ public NamePool getNamePool() {
+ return getPhysicalRoot().getNamePool();
+ }
+
+ /**
+ * Get the prefix part of the name of this node. This is the name before the ":" if any.
+ *
+ * @return the prefix part of the name. For an unnamed node, return an empty string.
+ */
+
+ public String getPrefix() {
+ int nameCode = getNameCode();
+ if (nameCode == -1) {
+ return "";
+ }
+ if (!NamePool.isPrefixed(nameCode)) {
+ return "";
+ }
+ return getNamePool().getPrefix(nameCode);
+ }
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ *
+ * @return The URI of the namespace of this node. For the null namespace, return an
+ * empty string. For an unnamed node, return the empty string.
+ */
+
+ public String getURI() {
+ int nameCode = getNameCode();
+ if (nameCode == -1) {
+ return "";
+ }
+ return getNamePool().getURI(nameCode);
+ }
+
+ /**
+ * Get the display name of this node. For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ *
+ * @return The display name of this node.
+ * For a node with no name, return an empty string.
+ */
+
+ public String getDisplayName() {
+ int nameCode = getNameCode();
+ if (nameCode == -1) {
+ return "";
+ }
+ return getNamePool().getDisplayName(nameCode);
+ }
+
+ /**
+ * Get the local name of this node.
+ *
+ * @return The local name of this node.
+ * For a node with no name, return "",.
+ */
+
+ public String getLocalPart() {
+ int nameCode = getNameCode();
+ if (nameCode == -1) {
+ return "";
+ }
+ return getNamePool().getLocalName(nameCode);
+ }
+
+ /**
+ * Get the line number of the node within its source document entity
+ */
+
+ public int getLineNumber() {
+ return parent.getLineNumber();
+ }
+
+ /**
+ * Find the parent node of this node.
+ *
+ * @return The Node object describing the containing element or root node.
+ */
+
+ /*@Nullable*/ public final NodeImpl getParent() {
+ if (parent instanceof DocumentImpl && ((DocumentImpl)parent).isImaginary()) {
+ return null;
+ }
+ return parent;
+ }
+
+ /**
+ * Get the raw value of the parent pointer. This will usually be the same as the parent node
+ * in the XDM model, but in the case of a parentless element it will be a pointer to the "imaginary"
+ * document node which is not properly part of the tree.
+ * @return either the real parent of this node, or the "imaginary" parent present in the tree
+ * implementation to provide a root object for the tree
+ */
+
+ /*@Nullable*/ protected final ParentNodeImpl getRawParent() {
+ return parent;
+ }
+
+ /**
+ * Set the raw parent pointer
+ * @param parent the "raw" parent pointer: either the real parent, or a dummy parent
+ * added to ensure that the tree is properly rooted.
+ */
+
+ protected final void setRawParent(/*@Nullable*/ ParentNodeImpl parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Get the previous sibling of the node
+ *
+ * @return The previous sibling node. Returns null if the current node is the first
+ * child of its parent.
+ */
+
+ /*@Nullable*/ public NodeImpl getPreviousSibling() {
+ if (parent == null) {
+ return null;
+ }
+ return parent.getNthChild(index - 1);
+ }
+
+
+ /**
+ * Get next sibling node
+ *
+ * @return The next sibling node of the required type. Returns null if the current node is the last
+ * child of its parent.
+ */
+
+ /*@Nullable*/ public NodeImpl getNextSibling() {
+ if (parent == null) {
+ return null;
+ }
+ return parent.getNthChild(index + 1);
+ }
+
+ /**
+ * Get first child - default implementation used for leaf nodes
+ *
+ * @return null
+ */
+
+ /*@Nullable*/ public NodeImpl getFirstChild() {
+ return null;
+ }
+
+ /**
+ * Get last child - default implementation used for leaf nodes
+ *
+ * @return null
+ */
+
+ /*@Nullable*/ public NodeInfo getLastChild() {
+ return null;
+ }
+
+ /**
+ * Return an enumeration over the nodes reached by the given axis from this node
+ *
+ * @param axisNumber The axis to be iterated over
+ * @return an AxisIterator that scans the nodes reached by the axis in turn.
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber) {
+ // Fast path for child axis
+ if (axisNumber == AxisInfo.CHILD) {
+ if (this instanceof ParentNodeImpl) {
+ return ((ParentNodeImpl)this).iterateChildren(null);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ } else {
+ return iterateAxis(axisNumber, AnyNodeTest.getInstance());
+ }
+ }
+
+ /**
+ * Return an enumeration over the nodes reached by the given axis from this node
+ *
+ * @param axisNumber The axis to be iterated over
+ * @param nodeTest A pattern to be matched by the returned nodes
+ * @return an AxisIterator that scans the nodes reached by the axis in turn.
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber, /*@NotNull*/ NodeTest nodeTest) {
+
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR:
+ return new AncestorEnumeration(this, nodeTest, false);
+
+ case AxisInfo.ANCESTOR_OR_SELF:
+ return new AncestorEnumeration(this, nodeTest, true);
+
+ case AxisInfo.ATTRIBUTE:
+ if (getNodeKind() != Type.ELEMENT) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ return new AttributeEnumeration(this, nodeTest);
+
+ case AxisInfo.CHILD:
+ if (this instanceof ParentNodeImpl) {
+ return ((ParentNodeImpl)this).iterateChildren(nodeTest);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+
+ case AxisInfo.DESCENDANT:
+ if (getNodeKind() == Type.DOCUMENT &&
+ nodeTest instanceof NameTest &&
+ nodeTest.getPrimitiveType() == Type.ELEMENT) {
+ return ((DocumentImpl)this).getAllElements(nodeTest.getFingerprint());
+ } else if (hasChildNodes()) {
+ return new SteppingNavigator.DescendantAxisIterator(this, false, nodeTest);
+ //return new DescendantEnumeration(this, nodeTest, false);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+
+ case AxisInfo.DESCENDANT_OR_SELF:
+ return new SteppingNavigator.DescendantAxisIterator(this, true, nodeTest);
+ //return new DescendantEnumeration(this, nodeTest, true);
+
+ case AxisInfo.FOLLOWING:
+ return new FollowingEnumeration(this, nodeTest);
+
+ case AxisInfo.FOLLOWING_SIBLING:
+ return new FollowingSiblingEnumeration(this, nodeTest);
+
+ case AxisInfo.NAMESPACE:
+ if (getNodeKind() != Type.ELEMENT) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ return NamespaceNode.makeIterator(this, nodeTest);
+
+ case AxisInfo.PARENT:
+ NodeInfo parent = getParent();
+ if (parent == null) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ return Navigator.filteredSingleton(parent, nodeTest);
+
+ case AxisInfo.PRECEDING:
+ return new PrecedingEnumeration(this, nodeTest);
+
+ case AxisInfo.PRECEDING_SIBLING:
+ return new PrecedingSiblingEnumeration(this, nodeTest);
+
+ case AxisInfo.SELF:
+ return Navigator.filteredSingleton(this, nodeTest);
+
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ return new PrecedingOrAncestorEnumeration(this, nodeTest);
+
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+ /**
+ * Find the value of a given attribute of this node. <BR>
+ * This method is defined on all nodes to meet XSL requirements, but for nodes
+ * other than elements it will always return null.
+ * @param uri the namespace uri of an attribute
+ * @param localName the local name of an attribute
+ * @return the value of the attribute, if it exists, otherwise null
+ */
+
+ /*@Nullable*/ public String getAttributeValue( /*@NotNull*/ String uri, /*@NotNull*/ String localName ) {
+ return null;
+ }
+
+ /**
+ * Get the value of the attribute with a given fingerprint.
+ *
+ * @param fp the fingerprint of the required attribute
+ * @return the string value of the attribute if present, or null if absent
+ */
+ public String getAttributeValue(int fp) {
+ return null;
+ }
+
+ /**
+ * Get the root node
+ * @return the NodeInfo representing the logical root of the tree. For this tree implementation the
+ * root will either be a document node or an element node.
+ */
+
+ public NodeInfo getRoot() {
+ NodeInfo parent = getParent();
+ if (parent == null) {
+ return this;
+ } else {
+ return parent.getRoot();
+ }
+ }
+
+ /**
+ * Get the root (document) node
+ * @return the DocumentInfo representing the containing document. If this
+ * node is part of a tree that does not have a document node as its
+ * root, returns null.
+ */
+
+ /*@Nullable*/ public DocumentInfo getDocumentRoot() {
+ NodeInfo parent = getParent();
+ if (parent == null) {
+ return null;
+ } else {
+ return parent.getDocumentRoot();
+ }
+ }
+
+ /**
+ * Get the physical root of the tree. This may be an imaginary document node: this method
+ * should be used only when control information held at the physical root is required
+ * @return the document node, which may be imaginary. In the case of a node that has been detached
+ * from the tree by means of a delete() operation, this method returns null.
+ */
+
+ /*@Nullable*/ public DocumentImpl getPhysicalRoot() {
+ ParentNodeImpl up = parent;
+ while (up != null && !(up instanceof DocumentImpl)) {
+ up = up.getRawParent();
+ }
+ return (DocumentImpl)up;
+ }
+
+ /**
+ * Get the next node in document order
+ *
+ * @param anchor the scan stops when it reaches a node that is not a descendant of the specified
+ * anchor node
+ * @return the next node in the document, or null if there is no such node
+ */
+
+ /*@Nullable*/ public NodeImpl getNextInDocument(NodeImpl anchor) {
+ // find the first child node if there is one; otherwise the next sibling node
+ // if there is one; otherwise the next sibling of the parent, grandparent, etc, up to the anchor element.
+ // If this yields no result, return null.
+
+ NodeImpl next = getFirstChild();
+ if (next != null) {
+ return next;
+ }
+ if (this == anchor) {
+ return null;
+ }
+ next = getNextSibling();
+ if (next != null) {
+ return next;
+ }
+ NodeImpl parent = this;
+ while (true) {
+ parent = parent.getParent();
+ if (parent == null) {
+ return null;
+ }
+ if (parent == anchor) {
+ return null;
+ }
+ next = parent.getNextSibling();
+ if (next != null) {
+ return next;
+ }
+ }
+ }
+
+ public SteppingNode getSuccessorElement(SteppingNode anchor, String uri, String local) {
+ NodeImpl next = getNextInDocument((NodeImpl) anchor);
+ while (next != null && !(next.getNodeKind() == Type.ELEMENT &&
+ (uri == null || uri.equals(next.getURI())) &&
+ (local == null || local.equals(next.getLocalPart())))) {
+ next = next.getNextInDocument((NodeImpl) anchor);
+ }
+ return next;
+ }
+
+ /**
+ * Get the previous node in document order
+ *
+ * @return the previous node in the document, or null if there is no such node
+ */
+
+ /*@Nullable*/ public NodeImpl getPreviousInDocument() {
+
+ // finds the last child of the previous sibling if there is one;
+ // otherwise the previous sibling element if there is one;
+ // otherwise the parent, up to the anchor element.
+ // If this reaches the document root, return null.
+
+ NodeImpl prev = (NodeImpl)getPreviousSibling();
+ if (prev != null) {
+ return prev.getLastDescendantOrSelf();
+ }
+ return (NodeImpl)getParent();
+ }
+
+ /*@NotNull*/ private NodeImpl getLastDescendantOrSelf() {
+ NodeImpl last = (NodeImpl)getLastChild();
+ if (last == null) {
+ return this;
+ }
+ return last.getLastDescendantOrSelf();
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ /*@Nullable*/ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return null;
+ }
+ /**
+ * Copy nodes. Copying type annotations is not yet supported for this tree
+ * structure, so we simply map the new interface onto the old
+ */
+
+// public final void copy(Receiver out, int whichNamespaces, boolean copyAnnotations, int locationId)
+// throws XPathException {
+// copy(out, whichNamespaces);
+// }
+//
+// public abstract void copy(Receiver out, int whichNamespaces) throws XPathException;
+
+ // implement DOM Node methods
+
+ /**
+ * Determine whether the node has any children.
+ *
+ * @return <code>true</code> if the node has any children,
+ * <code>false</code> if the node has no children.
+ */
+
+ public boolean hasChildNodes() {
+ return getFirstChild() != null;
+ }
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ return false;
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return false;
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return false;
+ }
+
+
+ /**
+ * Set the type annotation on a node. This must only be called when the caller has verified (by validation)
+ * that the node is a valid instance of the specified type. The call is ignored if the node is not an element
+ * or attribute node.
+ *
+ * @param typeCode the type annotation (possibly including high bits set to indicate the isID, isIDREF, and
+ * isNilled properties)
+ */
+
+ public void setTypeAnnotation(int typeCode) {
+ // no action
+ }
+
+ /**
+ * Delete this node (that is, detach it from its parent)
+ */
+
+ public void delete() {
+ // Overridden for attribute nodes
+ if (parent != null) {
+ parent.removeChild(this);
+ DocumentImpl newRoot = new DocumentImpl();
+ newRoot.setConfiguration(getConfiguration());
+ newRoot.setImaginary(true);
+ parent = newRoot;
+ }
+ index = -1;
+ }
+
+ /**
+ * Test whether this MutableNodeInfo object represents a node that has been deleted.
+ * Generally, such a node is unusable, and any attempt to use it will result in an exception
+ * being thrown
+ * @return true if this node has been deleted
+ */
+
+ public boolean isDeleted() {
+ return (index == -1 || (parent != null && parent.isDeleted()));
+ }
+
+ /**
+ * Remove an attribute from this element node
+ *
+ * <p>If this node is not an element, or if the specified node is not an attribute
+ * of this element, this method takes no action.</p>
+ *
+ * <p>The attribute object itself becomes unusable; any attempt to use this attribute object,
+ * or any other object representing the same node, is likely to result in an exception.</p>
+ *
+ * @param attribute the attribute node to be removed
+ */
+
+ public void removeAttribute(NodeInfo attribute) {
+ // no action (overridden in subclasses)
+ }
+
+ /**
+ * Add an attribute to this element node.
+ *
+ * <p>If this node is not an element, or if the supplied node is not an attribute, the method
+ * takes no action. If the element already has an attribute with this name, the method
+ * throws an exception.</p>
+ *
+ * <p>This method does not perform any namespace fixup. It is the caller's responsibility
+ * to ensure that any namespace prefix used in the name of the attribute (or in its value
+ * if it has a namespace-sensitive type) is declared on this element.</p>
+ *
+ *
+ *
+ * @param name the name of the new attribute
+ * @param attType the type annotation of the new attribute
+ * @param value the string value of the new attribute
+ * @param properties properties including IS_ID and IS_IDREF properties
+ * @throws IllegalStateException if the element already has an attribute with the given name.
+ */
+
+ public void addAttribute(NodeName name, SimpleType attType, CharSequence value, int properties) {
+ // No action, unless this is an element node
+ }
+
+ /**
+ * Rename this node
+ * @param newNameCode the NamePool code of the new name
+ */
+
+ public void rename(NodeName newNameCode) {
+ // implemented for node kinds that have a name
+ }
+
+
+ public void addNamespace(NamespaceBinding nscode, boolean inherit) {
+ // implemented for element nodes only
+ }
+
+ /**
+ * Replace this node with a given sequence of nodes. This node is effectively deleted, and the replacement
+ * nodes are attached to the parent of this node in its place.
+ *
+ * <p>The supplied nodes will become children of this node's parent. Adjacent text nodes will be merged, and
+ * zero-length text nodes removed. The supplied nodes may be modified in situ, for example to change their
+ * parent property and to add namespace bindings, or they may be copied, at the discretion of
+ * the implementation.</p>
+ *
+ * @param replacement the replacement nodes. If this node is an attribute, the replacements
+ * must also be attributes; if this node is not an attribute, the replacements must not be attributes.
+ * source the nodes to be inserted. The implementation determines what implementation classes
+ * of node it will accept; this implementation will accept attribute, text, comment, and processing instruction
+ * nodes belonging to any implementation, but elements must be instances of {@link net.sf.saxon.tree.linked.ElementImpl}.
+ * The supplied nodes will be modified in situ, for example
+ * to change their parent property and to add namespace bindings, if they are instances of
+ * {@link net.sf.saxon.tree.linked.ElementImpl}; otherwise they will be copied. If the nodes are copied, then on return
+ * the supplied source array will contain the copy rather than the original.
+ * @param inherit true if the replacement nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ * @throws IllegalArgumentException if any of the replacement nodes is of the wrong kind. When replacing
+ * a child node, the replacement nodes must all be elements, text, comment, or PI nodes; when replacing
+ * an attribute, the replacement nodes must all be attributes.
+ * @throws IllegalStateException if this node is deleted or if it has no parent node.
+ * or if two replacement attributes have the same name.
+ */
+
+ public void replace(NodeInfo[] replacement, boolean inherit) {
+ if (isDeleted()) {
+ throw new IllegalStateException("Cannot replace a deleted node");
+ }
+ if (getParent()==null) {
+ throw new IllegalStateException("Cannot replace a parentless node");
+ }
+ assert parent != null;
+ parent.replaceChildrenAt(replacement, index, inherit);
+ parent = null;
+ index = -1; // mark the node as deleted
+ }
+
+ /**
+ * Insert a sequence of nodes as children of this node.
+ *
+ * <p>This method takes no action unless the target node is a document node or element node. It also
+ * takes no action in respect of any supplied nodes that are not elements, text nodes, comments, or
+ * processing instructions.</p>
+ *
+ * <p>The supplied nodes will form the new children. Adjacent text nodes will be merged, and
+ * zero-length text nodes removed. The supplied nodes may be modified in situ, for example to change their
+ * parent property and to add namespace bindings, or they may be copied, at the discretion of
+ * the implementation.</p>
+ *
+ * @param source the nodes to be inserted. The implementation determines what implementation classes
+ * of node it will accept; all implementations must accept nodes constructed using the Builder supplied
+ * by the {@link #newBuilder} method on this object. The supplied nodes may be modified in situ, for example
+ * to change their parent property and to add namespace bindings, but this depends on the implementation.
+ * The argument array may be modified as a result of the call.
+ * @param atStart true if the new nodes are to be inserted before existing children; false if they are
+ * to be inserted after existing children
+ * @param inherit true if the inserted nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ * @throws IllegalArgumentException if the supplied nodes use a node implementation that this
+ * implementation does not accept.
+ */
+
+ public void insertChildren(NodeInfo[] source, boolean atStart, boolean inherit) {
+ // No action: node is not a document or element node
+ }
+
+ /**
+ * Insert copies of a sequence of nodes as siblings of this node.
+ * <p/>
+ * <p>This method takes no action unless the target node is an element, text node, comment, or
+ * processing instruction, and one that has a parent node. It also
+ * takes no action in respect of any supplied nodes that are not elements, text nodes, comments, or
+ * processing instructions.</p>
+ * <p/>
+ * <p>The supplied nodes must use the same data model implementation as the tree into which they
+ * will be inserted.</p>
+ *
+ * @param source the nodes to be inserted
+ * @param before true if the new nodes are to be inserted before the target node; false if they are
+ * @param inherit true if the inserted nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ */
+
+ public void insertSiblings(NodeInfo[] source, boolean before, boolean inherit) {
+ if (parent == null) {
+ throw new IllegalStateException("Cannot add siblings if there is no parent");
+ }
+ parent.insertChildrenAt(source, (before ? index : index+1), inherit);
+ }
+
+
+ /**
+ * Remove type information from this node (and its ancestors, recursively).
+ * This method implements the upd:removeType() primitive defined in the XQuery Update specification
+ */
+
+ public void removeTypeAnnotation() {
+ // no action
+ }
+
+ /**
+ * Get a Builder suitable for building nodes that can be attached to this document.
+ * @return a new Builder that constructs nodes using the same object model implementation
+ * as this one, suitable for attachment to this tree
+ */
+
+ /*@NotNull*/ public Builder newBuilder() {
+ return getPhysicalRoot().newBuilder();
+ }
+}
+
diff --git a/sf/saxon/tree/linked/ParentNodeImpl.java b/sf/saxon/tree/linked/ParentNodeImpl.java
new file mode 100644
index 0000000..af2ce03
--- /dev/null
+++ b/sf/saxon/tree/linked/ParentNodeImpl.java
@@ -0,0 +1,476 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.*;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.Type;
+
+/**
+ * ParentNodeImpl is an implementation of a non-leaf node (specifically, an Element node
+ * or a Document node)
+ * @author Michael H. Kay
+ */
+
+
+abstract class ParentNodeImpl extends NodeImpl {
+
+ /*@Nullable*/ private Object children = null; // null for no children
+ // a NodeImpl for a single child
+ // a NodeImpl[] for >1 child
+
+ private int sequence; // sequence number allocated during original tree creation.
+ // set to -1 for nodes added subsequently by XQuery update
+
+ /**
+ * Get the node sequence number (in document order). Sequence numbers are monotonic but not
+ * consecutive. In the current implementation, parent nodes (elements and document nodes) have a zero
+ * least-significant word, while namespaces, attributes, text nodes, comments, and PIs have
+ * the top word the same as their owner and the bottom half reflecting their relative position.
+ * For nodes added by XQUery Update, the sequence number is -1L
+ * @return the sequence number if there is one, or -1L otherwise.
+ */
+
+ protected final long getSequenceNumber() {
+ return (getRawSequenceNumber() == -1 ? -1L : ((long)getRawSequenceNumber())<<32);
+ }
+
+ protected final int getRawSequenceNumber() {
+ return sequence;
+ }
+
+ protected final void setRawSequenceNumber(int seq) {
+ sequence = seq;
+ }
+
+ /**
+ * Set the children of this node
+ * @param children null if there are no children, a single NodeInfo if there is one child, an array of NodeInfo
+ * if there are multiple children
+ */
+
+ protected final void setChildren(Object children) {
+ this.children = children;
+ }
+
+ /**
+ * Determine if the node has any children.
+ */
+
+ public final boolean hasChildNodes() {
+ return (children!=null);
+ }
+
+ /**
+ * Determine how many children the node has
+ * @return the number of children of this parent node
+ */
+
+ public final int getNumberOfChildren() {
+ if (children == null) {
+ return 0;
+ } else if (children instanceof NodeImpl) {
+ return 1;
+ } else {
+ return ((NodeInfo[])children).length;
+ }
+ }
+
+ /**
+ * Get an enumeration of the children of this node
+ * @param test A NodeTest to be satisfied by the child nodes, or null
+ * if all child node are to be returned
+ * @return an iterator over the children of this node
+ */
+
+ protected final AxisIterator iterateChildren(/*@Nullable*/ NodeTest test) {
+ if (children==null) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ } else if (children instanceof NodeImpl) {
+ NodeImpl child = (NodeImpl)children;
+ if (test == null || test == AnyNodeTest.getInstance()) {
+ return SingleNodeIterator.makeIterator(child);
+ } else {
+ return Navigator.filteredSingleton(child, test);
+ }
+ } else {
+ if (test == null || test == AnyNodeTest.getInstance()) {
+ return new AxisIteratorOverSequence<NodeImpl>(
+ new ArrayIterator<NodeImpl>((NodeImpl[])children));
+ } else {
+ return new ChildEnumeration(this, test);
+ }
+ }
+ }
+
+
+ /**
+ * Get the first child node of the element
+ * @return the first child node of the required type, or null if there are no children
+ */
+
+ /*@Nullable*/ public final NodeImpl getFirstChild() {
+ if (children==null) return null;
+ if (children instanceof NodeImpl) return (NodeImpl)children;
+ return ((NodeImpl[])children)[0];
+ }
+
+ /**
+ * Get the last child node of the element
+ * @return the last child of the element, or null if there are no children
+ */
+
+ /*@Nullable*/ public final NodeImpl getLastChild() {
+ if (children==null) return null;
+ if (children instanceof NodeImpl) return (NodeImpl)children;
+ NodeImpl[] n = (NodeImpl[])children;
+ return n[n.length-1];
+ }
+
+ /**
+ * Get the nth child node of the element (numbering from 0)
+ * @param n identifies the required child
+ * @return the last child of the element, or null if there is no n'th child
+ */
+
+ /*@Nullable*/ protected final NodeImpl getNthChild(int n) {
+ if (children==null) return null;
+ if (children instanceof NodeImpl) {
+ return (n==0 ? (NodeImpl)children : null);
+ }
+ NodeImpl[] nodes = (NodeImpl[])children;
+ if (n<0 || n>=nodes.length) return null;
+ return nodes[n];
+ }
+
+ /**
+ * Remove a given child
+ * @param child the child to be removed
+ */
+
+ protected void removeChild(NodeImpl child) {
+ if (children == null) {
+ return;
+ }
+ if (children == child) {
+ children = null;
+ return;
+ }
+ NodeImpl[] nodes = (NodeImpl[])children;
+ for (int i=0; i<nodes.length; i++) {
+ if (nodes[i] == child) {
+ if (nodes.length == 2) {
+ children = nodes[1-i];
+ } else {
+ NodeImpl[] n2 = new NodeImpl[nodes.length - 1];
+ if (i > 0) {
+ System.arraycopy(nodes, 0, n2, 0, i);
+ }
+ if (i < nodes.length - 1) {
+ System.arraycopy(nodes, i+1, n2, i, nodes.length-i-1);
+ }
+ children = cleanUpChildren(n2);
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Tidy up the children of the node. Merge adjacent text nodes; remove zero-length text nodes;
+ * reallocate index numbers to each of the children
+ * @param children the existing children
+ * @return the replacement array of children
+ */
+
+ /*@NotNull*/ private NodeImpl[] cleanUpChildren(/*@NotNull*/ NodeImpl[] children) {
+ boolean prevText = false;
+ int j = 0;
+ NodeImpl[] c2 = new NodeImpl[children.length];
+ for (int i=0; i<children.length; i++) {
+ NodeImpl node = children[i];
+ if (node instanceof TextImpl) {
+ if (prevText) {
+ TextImpl prev = ((TextImpl)c2[j-1]);
+ prev.replaceStringValue(prev.getStringValue() + node.getStringValue());
+ } else if (node.getStringValue().length() > 0) {
+ prevText = true;
+ node.setSiblingPosition(j);
+ c2[j++] = node;
+ }
+ } else {
+ node.setSiblingPosition(j);
+ c2[j++] = node;
+ prevText = false;
+ }
+ }
+ if (j == c2.length) {
+ return c2;
+ } else {
+ NodeImpl[] c3 = new NodeImpl[j];
+ System.arraycopy(c2, 0, c3, 0, j);
+ return c3;
+ }
+ }
+
+
+ /**
+ * Return the string-value of the node, that is, the concatenation
+ * of the character content of all descendent elements and text nodes.
+ * @return the accumulated character content of the element, including descendant elements.
+ */
+
+ public String getStringValue() {
+ return getStringValueCS().toString();
+ }
+
+
+ public CharSequence getStringValueCS() {
+ FastStringBuffer sb = null;
+
+ NodeImpl next = (NodeImpl)getFirstChild();
+ while (next!=null) {
+ if (next instanceof TextImpl) {
+ if (sb==null) {
+ sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ }
+ sb.append(next.getStringValueCS());
+ }
+ next = next.getNextInDocument(this);
+ }
+ if (sb==null) return "";
+ return sb.condense();
+ }
+
+ /**
+ * Add a child node to this node. For system use only. Note: normalizing adjacent text nodes
+ * is the responsibility of the caller.
+ * @param node the node to be added as a child of this node. This must be an instance of
+ * {@link net.sf.saxon.tree.linked.NodeImpl}. It will be modified as a result of this call (by setting its
+ * parent property and sibling position)
+ * @param index the position where the child is to be added
+ */
+
+ protected synchronized void addChild(/*@NotNull*/ NodeImpl node, int index) {
+ NodeImpl[] c;
+ if (children == null) {
+ c = new NodeImpl[10];
+ } else if (children instanceof NodeImpl) {
+ c = new NodeImpl[10];
+ c[0] = (NodeImpl)children;
+ } else {
+ c = (NodeImpl[])children;
+ }
+ if (index >= c.length) {
+ NodeImpl[] kids = new NodeImpl[c.length * 2];
+ System.arraycopy(c, 0, kids, 0, c.length);
+ c = kids;
+ }
+ c[index] = node;
+ node.setRawParent(this);
+ node.setSiblingPosition(index);
+ children = c;
+ }
+
+
+ /**
+ * Insert a sequence of nodes as children of this node.
+ *
+ * <p>This method takes no action unless the target node is a document node or element node. It also
+ * takes no action in respect of any supplied nodes that are not elements, text nodes, comments, or
+ * processing instructions.</p>
+ *
+ * <p>The supplied nodes will form the new children. Adjacent text nodes will be merged, and
+ * zero-length text nodes removed. The supplied nodes may be modified in situ, for example to change their
+ * parent property and to add namespace bindings, or they may be copied, at the discretion of
+ * the implementation.</p>
+ *
+ * @param source the nodes to be inserted. The implementation determines what implementation classes
+ * of node it will accept; this implementation will accept text, comment, and processing instruction
+ * nodes belonging to any implementation, but elements must be instances of {@link net.sf.saxon.tree.linked.ElementImpl}.
+ * The supplied nodes will be modified in situ, for example
+ * to change their parent property and to add namespace bindings, if they are instances of
+ * {@link net.sf.saxon.tree.linked.ElementImpl}; otherwise they will be copied. If the nodes are copied, then on return
+ * the supplied source array will contain the copy rather than the original.
+ * @param atStart true if the new nodes are to be inserted before existing children; false if they are
+ * to be inserted after existing children
+ * @param inherit true if the inserted nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ * @throws IllegalArgumentException if the supplied nodes use a node implementation that this
+ * implementation does not accept.
+ */
+
+ public void insertChildren(/*@NotNull*/ NodeInfo[] source, boolean atStart, boolean inherit) {
+ if (atStart) {
+ insertChildrenAt(source, 0, inherit);
+ } else {
+ insertChildrenAt(source, getNumberOfChildren(), inherit);
+ }
+ }
+
+ /**
+ * Insert children before or after a given existing child
+ * @param source the children to be inserted. We allow any kind of text, comment, or processing instruction
+ * node, but element nodes must be instances of this NodeInfo implementation.
+ * @param index the position before which they are to be inserted: 0 indicates insertion before the
+ * first child, 1 insertion before the second child, and so on.
+ * @param inherit true if the inserted nodes are to inherit the namespaces that are in-scope for their
+ * new parent; false if such namespaces should be undeclared on the children
+ */
+
+ protected synchronized void insertChildrenAt(/*@NotNull*/ NodeInfo[] source, int index, boolean inherit) {
+ if (source.length == 0) {
+ return;
+ }
+ for (int i=0; i<source.length; i++) {
+ source[i] = convertForeignNode(source[i]);
+ NodeImpl child = (NodeImpl)source[i];
+ child.setRawParent(this);
+ if (child instanceof ElementImpl) {
+ // If the child has no xmlns="xxx" declaration, then add an xmlns="" to prevent false inheritance
+ // from the new parent
+ ((ElementImpl)child).fixupInsertedNamespaces(inherit);
+ }
+ }
+ if (children == null) {
+ if (source.length == 1) {
+ children = source[0];
+ } else {
+ NodeImpl[] n2 = new NodeImpl[source.length];
+ System.arraycopy(source, 0, n2, 0, source.length);
+ children = n2;
+ }
+ } else if (children instanceof NodeImpl) {
+ int adjacent = (index==0 ? 0 : source.length - 1);
+ if (children instanceof TextImpl && source[adjacent] instanceof TextImpl) {
+ if (index == 0) {
+ ((TextImpl)source[adjacent]).replaceStringValue(
+ source[adjacent].getStringValue() + ((TextImpl)children).getStringValue());
+ } else {
+ ((TextImpl)source[adjacent]).replaceStringValue(
+ ((TextImpl)children).getStringValue() + source[adjacent].getStringValue());
+ }
+ NodeImpl[] n2 = new NodeImpl[source.length];
+ System.arraycopy(source, 0, n2, 0, source.length);
+ children = n2;
+ } else {
+ NodeImpl[] n2 = new NodeImpl[source.length + 1];
+ if (index == 0) {
+ System.arraycopy(source, 0, n2, 0, source.length);
+ n2[source.length] = (NodeImpl)children;
+ } else {
+ n2[0] = (NodeImpl)children;
+ System.arraycopy(source, 0, n2, 1, source.length);
+ }
+ children = cleanUpChildren(n2);
+ }
+ } else {
+ NodeImpl[] n0 = (NodeImpl[])children;
+ NodeImpl[] n2 = new NodeImpl[n0.length + source.length];
+ System.arraycopy(n0, 0, n2, 0, index);
+ System.arraycopy(source, 0, n2, index, source.length);
+ System.arraycopy(n0, index, n2, index+source.length, n0.length - index);
+ children = cleanUpChildren(n2);
+ }
+ }
+
+ /*@NotNull*/ private NodeImpl convertForeignNode(/*@NotNull*/ NodeInfo source) {
+ if (!(source instanceof NodeImpl)) {
+ int kind = source.getNodeKind();
+ switch (kind) {
+ case Type.TEXT:
+ return new TextImpl(source.getStringValue());
+ case Type.COMMENT:
+ return new CommentImpl(source.getStringValue());
+ case Type.PROCESSING_INSTRUCTION:
+ return new ProcInstImpl(source.getNameCode(), source.getStringValue());
+ case Type.ELEMENT:
+ throw new IllegalArgumentException(
+ "Cannot insert an element node unless it is an instance of net.sf.saxon.om.tree.ElementImpl");
+ default:
+ throw new IllegalArgumentException(
+ "Cannot insert a node unless it is an element, comment, text node, or processing instruction");
+ }
+ }
+ return (NodeImpl)source;
+ }
+
+ /**
+ * Replace child at a given index by new children
+ * @param source the children to be inserted
+ * @param index the position at which they are to be inserted: 0 indicates replacement of the
+ * first child, replacement of the second child, and so on. The effect is undefined if index
+ * is out of range
+ * @param inherit set to true if the new child elements are to inherit the in-scope namespaces
+ * of their new parent
+ * @throws IllegalArgumentException if any of the replacement nodes is not an element, text,
+ * comment, or processing instruction node
+ */
+
+ protected synchronized void replaceChildrenAt(/*@NotNull*/ NodeInfo[] source, int index, boolean inherit) {
+ if (children == null) {
+ return;
+ }
+ for (int i=0; i<source.length; i++) {
+ source[i] = convertForeignNode(source[i]);
+ NodeImpl child = (NodeImpl)source[i];
+ child.setRawParent(this);
+ if (child instanceof ElementImpl) {
+ // If the child has no xmlns="xxx" declaration, then add an xmlns="" to prevent false inheritance
+ // from the new parent
+ ((ElementImpl)child).fixupInsertedNamespaces(inherit);
+ }
+ }
+ if (children instanceof NodeImpl) {
+ if (source.length == 0) {
+ children = null;
+ } else if (source.length == 1) {
+ children = source[0];
+ } else {
+ NodeImpl[] n2 = new NodeImpl[source.length];
+ System.arraycopy(source, 0, n2, 0, source.length);
+ children = cleanUpChildren(n2);
+ }
+ } else {
+ NodeImpl[] n0 = (NodeImpl[])children;
+ NodeImpl[] n2 = new NodeImpl[n0.length + source.length - 1];
+ System.arraycopy(n0, 0, n2, 0, index);
+ System.arraycopy(source, 0, n2, index, source.length);
+ System.arraycopy(n0, index + 1, n2, index+source.length, n0.length - index - 1);
+ children = cleanUpChildren(n2);
+ }
+ }
+
+
+ /**
+ * Compact the space used by this node
+ * @param size the number of actual children
+ */
+
+ public synchronized void compact(int size) {
+ if (size==0) {
+ children = null;
+ } else if (size==1) {
+ if (children instanceof NodeImpl[]) {
+ children = ((NodeImpl[])children)[0];
+ }
+ } else {
+ NodeImpl[] kids = new NodeImpl[size];
+ System.arraycopy(children, 0, kids, 0, size);
+ children = kids;
+ }
+ }
+
+
+
+}
+
diff --git a/sf/saxon/tree/linked/PrecedingEnumeration.java b/sf/saxon/tree/linked/PrecedingEnumeration.java
new file mode 100644
index 0000000..ef68b32
--- /dev/null
+++ b/sf/saxon/tree/linked/PrecedingEnumeration.java
@@ -0,0 +1,55 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+
+final class PrecedingEnumeration extends TreeEnumeration {
+
+ /*@Nullable*/ NodeImpl nextAncestor;
+
+ public PrecedingEnumeration(/*@NotNull*/ NodeImpl node, NodeTest nodeTest) {
+ super(node, nodeTest);
+
+ // we need to avoid returning ancestors of the starting node
+ nextAncestor = (NodeImpl)node.getParent();
+ advance();
+ }
+
+
+ /**
+ * Special code to skip the ancestors of the start node
+ */
+
+ protected boolean conforms(/*@Nullable*/ NodeImpl node) {
+ // ASSERT: we'll never test the root node, because it's always
+ // an ancestor, so nextAncestor will never be null.
+ if (node!=null) {
+ if (node.isSameNodeInfo(nextAncestor)) {
+ nextAncestor = (NodeImpl)nextAncestor.getParent();
+ return false;
+ }
+ }
+ return super.conforms(node);
+ }
+
+ protected void step() {
+ next = next.getPreviousInDocument();
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new PrecedingEnumeration(start, nodeTest);
+ }
+
+}
+
diff --git a/sf/saxon/tree/linked/PrecedingOrAncestorEnumeration.java b/sf/saxon/tree/linked/PrecedingOrAncestorEnumeration.java
new file mode 100644
index 0000000..09ac7a6
--- /dev/null
+++ b/sf/saxon/tree/linked/PrecedingOrAncestorEnumeration.java
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+
+/**
+* This axis cannot be requested directly in an XPath expression
+* but is used when evaluating xsl:number. It is provided because
+* taking the union of the two axes would be very inefficient
+*/
+
+final class PrecedingOrAncestorEnumeration extends TreeEnumeration {
+
+
+ public PrecedingOrAncestorEnumeration(NodeImpl node, NodeTest nodeTest) {
+ super(node, nodeTest);
+ advance();
+ }
+
+ protected void step() {
+ next = next.getPreviousInDocument();
+ }
+
+ /**
+ * Get another iterator over the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new PrecedingOrAncestorEnumeration(start, nodeTest);
+ }
+
+}
+
diff --git a/sf/saxon/tree/linked/PrecedingSiblingEnumeration.java b/sf/saxon/tree/linked/PrecedingSiblingEnumeration.java
new file mode 100644
index 0000000..4b75a83
--- /dev/null
+++ b/sf/saxon/tree/linked/PrecedingSiblingEnumeration.java
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+
+final class PrecedingSiblingEnumeration extends TreeEnumeration {
+
+ public PrecedingSiblingEnumeration(NodeImpl node, NodeTest nodeTest) {
+ super(node, nodeTest);
+ advance();
+ }
+
+ protected void step() {
+ next = next.getPreviousSibling();
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new PrecedingSiblingEnumeration(start, nodeTest);
+ }
+
+}
+
diff --git a/sf/saxon/tree/linked/ProcInstImpl.java b/sf/saxon/tree/linked/ProcInstImpl.java
new file mode 100644
index 0000000..817ddf2
--- /dev/null
+++ b/sf/saxon/tree/linked/ProcInstImpl.java
@@ -0,0 +1,118 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * ProcInstImpl is an implementation of ProcInstInfo used by the Propagator to construct
+ * its trees.
+ * @author Michael H. Kay
+ */
+
+
+class ProcInstImpl extends NodeImpl {
+
+ String content;
+ int nameCode;
+ String systemId;
+ int lineNumber = -1;
+
+ public ProcInstImpl(int nameCode, String content) {
+ this.nameCode = nameCode;
+ this.content = content;
+ }
+
+ /**
+ * Get the nameCode of the node. This is used to locate the name in the NamePool
+ */
+
+ public int getNameCode() {
+ return nameCode;
+ }
+
+ public String getStringValue() {
+ return content;
+ }
+
+ /**
+ * Get the typed value of this node.
+ * Returns the string value, as an instance of xs:string
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize() {
+ return new StringValue(getStringValue());
+ }
+
+ public final int getNodeKind() {
+ return Type.PROCESSING_INSTRUCTION;
+ }
+
+ /**
+ * Set the system ID and line number
+ * @param uri the system identifier
+ * @param lineNumber the line number
+ */
+
+ public void setLocation(String uri, int lineNumber) {
+ this.systemId = uri;
+ this.lineNumber = lineNumber;
+ }
+
+ /**
+ * Get the system ID for the entity containing this node.
+ * @return the system identifier
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Get the line number of the node within its source entity
+ */
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ out.processingInstruction(getLocalPart(), content, locationId, 0);
+ }
+
+ /**
+ * Rename this node
+ *
+ * @param newNameCode the NamePool code of the new name
+ */
+
+ public void rename(NodeName newNameCode) {
+ nameCode = getNamePool().allocate("", "", newNameCode.getLocalPart());
+ }
+
+
+ /**
+ * Replace the string-value of this node
+ *
+ * @param stringValue the new string value
+ */
+
+ public void replaceStringValue(/*@NotNull*/ CharSequence stringValue) {
+ content = stringValue.toString();
+ }
+}
+
diff --git a/sf/saxon/tree/linked/SystemIdMap.java b/sf/saxon/tree/linked/SystemIdMap.java
new file mode 100644
index 0000000..3b07020
--- /dev/null
+++ b/sf/saxon/tree/linked/SystemIdMap.java
@@ -0,0 +1,72 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+/**
+ * System IDs are not held in nodes in the tree, because they are usually the same
+ * for a whole document.
+ * This class provides a map from element sequence numbers to System IDs: it is
+ * linked to the root node of the tree.
+ * Note that the System ID is not necessarily the same as the Base URI. The System ID relates
+ * to the external entity in which a node was physically located; this provides a default for
+ * the Base URI, but this may be modified by specifying an xml:base attribute
+ *
+ * @author Michael H. Kay
+ */
+
+public class SystemIdMap {
+
+ private int[] sequenceNumbers;
+ private String[] uris;
+ private int allocated;
+
+ public SystemIdMap() {
+ sequenceNumbers = new int[4];
+ uris = new String[4];
+ allocated = 0;
+ }
+
+ /**
+ * Set the system ID corresponding to a given sequence number
+ */
+
+ public void setSystemId(int sequence, /*@NotNull*/ String uri) {
+ // ignore it if same as previous
+ if (allocated>0 && uri.equals(uris[allocated-1])) {
+ return;
+ }
+ if (sequenceNumbers.length <= allocated + 1) {
+ int[] s = new int[allocated * 2];
+ String[] u = new String[allocated * 2];
+ System.arraycopy(sequenceNumbers, 0, s, 0, allocated);
+ System.arraycopy(uris, 0, u, 0, allocated);
+ sequenceNumbers = s;
+ uris = u;
+ }
+ sequenceNumbers[allocated] = sequence;
+ uris[allocated] = uri;
+ allocated++;
+ }
+
+ /**
+ * Get the system ID corresponding to a given sequence number
+ */
+
+ /*@Nullable*/ public String getSystemId(int sequence) {
+ if (allocated==0) return null;
+ // could use a binary chop, but it's not important
+ for (int i=1; i<allocated; i++) {
+ if (sequenceNumbers[i] > sequence) {
+ return uris[i-1];
+ }
+ }
+ return uris[allocated-1];
+ }
+
+
+}
diff --git a/sf/saxon/tree/linked/TextImpl.java b/sf/saxon/tree/linked/TextImpl.java
new file mode 100644
index 0000000..3ff6cf5
--- /dev/null
+++ b/sf/saxon/tree/linked/TextImpl.java
@@ -0,0 +1,78 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+
+/**
+ * A node in the XML parse tree representing character content<P>
+ * @author Michael H. Kay
+ */
+
+public class TextImpl extends NodeImpl {
+
+ private String content;
+
+ public TextImpl(String content) {
+ this.content = content;
+ }
+
+ /**
+ * Append to the content of the text node
+ * @param content the new content to be appended
+ */
+
+ public void appendStringValue(String content) {
+ this.content = this.content + content;
+ }
+
+ /**
+ * Return the character value of the node.
+ * @return the string value of the node
+ */
+
+ public String getStringValue() {
+ return content;
+ }
+
+ /**
+ * Return the type of node.
+ * @return Type.TEXT
+ */
+
+ public final int getNodeKind() {
+ return Type.TEXT;
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ out.characters(content, locationId, 0);
+ }
+
+
+ /**
+ * Replace the string-value of this node
+ *
+ * @param stringValue the new string value
+ */
+
+ public void replaceStringValue(/*@NotNull*/ CharSequence stringValue) {
+ if (stringValue.length() == 0) {
+ delete();
+ } else {
+ content = stringValue.toString();
+ }
+ }
+
+}
+
diff --git a/sf/saxon/tree/linked/TreeEnumeration.java b/sf/saxon/tree/linked/TreeEnumeration.java
new file mode 100644
index 0000000..ecbb76b
--- /dev/null
+++ b/sf/saxon/tree/linked/TreeEnumeration.java
@@ -0,0 +1,184 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.linked;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+
+abstract class TreeEnumeration implements AxisIterator, LookaheadIterator {
+
+ protected NodeImpl start;
+ /*@Nullable*/ protected NodeImpl next;
+ protected NodeTest nodeTest;
+ /*@Nullable*/ protected NodeImpl current = null;
+ protected int position = 0;
+ //protected int last = -1;
+
+ /**
+ * Create an axis enumeration for a given type and name of node, from a given
+ * origin node
+ * @param origin the node from which the axis originates
+ * @param nodeTest test to be satisfied by the returned nodes, or null if all nodes
+ * are to be returned.
+ */
+
+ public TreeEnumeration(NodeImpl origin, NodeTest nodeTest) {
+ next = origin;
+ start = origin;
+ this.nodeTest = nodeTest;
+ }
+
+ /**
+ * Test whether a node conforms to the node type and name constraints.
+ * Note that this returns true if the supplied node is null, this is a way of
+ * terminating a loop.
+ * @param node the node to be tested
+ * @return true if the node matches the requested node type and name
+ */
+
+ protected boolean conforms(/*@Nullable*/ NodeImpl node) {
+ return node == null || nodeTest == null || nodeTest.matches(node);
+ }
+
+ /**
+ * Advance along the axis until a node is found that matches the required criteria
+ */
+
+ protected final void advance() {
+ do {
+ step();
+ } while (!conforms(next));
+ }
+
+ /**
+ * Advance one step along the axis: the resulting node might not meet the required
+ * criteria for inclusion
+ */
+
+ protected abstract void step();
+
+ /**
+ * Determine whether there are more items to come. Note that this operation
+ * is stateless and it is not necessary (or usual) to call it before calling
+ * next(). It is used only when there is an explicit need to tell if we
+ * are at the last element.
+ *
+ * @return true if there are more items in the sequence
+ */
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return (next() != null);
+ }
+
+
+ /**
+ * Return the next node in the sequence
+ */
+
+ /*@Nullable*/ public final NodeInfo next() {
+ if (next==null) {
+ current = null;
+ position = -1;
+ return null;
+ } else {
+ current = next;
+ position++;
+ advance();
+ return current;
+ }
+ }
+
+ /**
+ * Return the current Item
+ */
+
+ /*@Nullable*/ public final NodeInfo current() {
+ return current;
+ }
+
+ /**
+ * Return the current position
+ */
+
+ public final int position() {
+ return position;
+ }
+
+ /**
+ * Indicate that no more items are required from the iterator
+ */
+
+ public void close() {
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public AxisIterator iterateAxis(byte axis, /*@NotNull*/ NodeTest test) {
+ return current.iterateAxis(axis, test);
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ return current.atomize();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ return current.getStringValueCS();
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+
+}
+
diff --git a/sf/saxon/tree/linked/package.html b/sf/saxon/tree/linked/package.html
new file mode 100644
index 0000000..7e60e0d
--- /dev/null
+++ b/sf/saxon/tree/linked/package.html
@@ -0,0 +1,42 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.tree</title>
+</head>
+
+<body>
+
+<p>This package defines the implementation of the so-called "standard tree" structure. This
+structure can be used to represent both the source document and the stylesheet.
+It is no longer the default structure for source documents, but is always used for
+stylesheets and for schema documents, because it allows each element to be represented
+by a subclass of <code>Element</code> with application-specific functionality.</p>
+
+<p>The classes represent the various kinds of node on the tree. Most of them
+are not visible outside the package, with the notable exception of ElementImpl,
+which can be subclassed to contain properties for a particular kind of element.
+This capability is exploited especially in the stylesheet tree.</p>
+
+<p>As well as classes representing nodes, there are classes representing
+iterators over the various XPath axes, for example <code>ChildEnumeration</code>
+and <code>PrecedingEnumeration</code>.</p>
+
+<p>The <code>TreeBuilder</code> performs the work of constructing a tree, from a
+sequence of SAX-like <code>Receiver</code> events.</p>
+
+<p>The package also contains some helper classes such as <code>SystemIdMap</code>
+and <code>LineNumberMap</code> that are used also by the TinyTree implementation.</p>
+
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/tree/package.html b/sf/saxon/tree/package.html
new file mode 100644
index 0000000..7ebaacc
--- /dev/null
+++ b/sf/saxon/tree/package.html
@@ -0,0 +1,27 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.tree</title>
+
+</head>
+ <body>
+ <p>This package contains subpackages containing the code for different tree models. It also contains
+ the class {@link net.sf.saxon.tree.NamespaceNode} which is used by several tree models.</p>
+
+
+
+
+ <p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+28 November 2011</i></p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/tree/tiny/AncestorEnumeration.java b/sf/saxon/tree/tiny/AncestorEnumeration.java
new file mode 100644
index 0000000..d363049
--- /dev/null
+++ b/sf/saxon/tree/tiny/AncestorEnumeration.java
@@ -0,0 +1,67 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorImpl;
+
+/**
+* This class enumerates the ancestor:: or ancestor-or-self:: axes,
+* starting at a given node. The start node will never be the root.
+*/
+
+final class AncestorEnumeration extends AxisIteratorImpl {
+
+ private TinyNodeImpl startNode;
+ private NodeTest test;
+ private boolean includeSelf;
+
+ public AncestorEnumeration(TinyNodeImpl node, NodeTest nodeTest, boolean includeSelf) {
+ test = nodeTest;
+ startNode = node;
+ this.includeSelf = includeSelf;
+ current = startNode;
+ }
+
+ /*@Nullable*/ public NodeInfo next() {
+ if (position <= 0) {
+ if (position < 0) {
+ return null;
+ }
+ if (position==0 && includeSelf && test.matches(startNode)) {
+ current = startNode;
+ position = 1;
+ return current;
+ }
+ }
+ assert current != null;
+ NodeInfo node = current.getParent();
+ while (node != null && !test.matches(node)) {
+ node = node.getParent();
+ }
+ current = node;
+ if (node == null) {
+ position = -1;
+ } else {
+ position++;
+ }
+ return current;
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new AncestorEnumeration(startNode, test, includeSelf);
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/AppendableCharSequence.java b/sf/saxon/tree/tiny/AppendableCharSequence.java
new file mode 100644
index 0000000..002413a
--- /dev/null
+++ b/sf/saxon/tree/tiny/AppendableCharSequence.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+/**
+ * Defines a CharSequence to which characters can be appended
+ */
+public interface AppendableCharSequence extends CharSequence {
+
+ /**
+ * Append characters to this CharSequence
+ * @param chars the characters to be appended
+ */
+ void append(CharSequence chars);
+
+ /**
+ * Set the length. If this exceeds the current length, this method is a no-op.
+ * If this is less than the current length, characters beyond the specified point
+ * are deleted.
+ * @param length the new length
+ */
+
+ void setLength(int length);
+}
diff --git a/sf/saxon/tree/tiny/AttributeEnumeration.java b/sf/saxon/tree/tiny/AttributeEnumeration.java
new file mode 100644
index 0000000..7506730
--- /dev/null
+++ b/sf/saxon/tree/tiny/AttributeEnumeration.java
@@ -0,0 +1,148 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.om.CodedName;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorImpl;
+import net.sf.saxon.type.Type;
+
+/**
+* AttributeEnumeration is an iterator over all the attribute nodes of an Element.
+*/
+
+final class AttributeEnumeration extends AxisIteratorImpl {
+
+ private TinyTree tree;
+ private int element;
+ private NodeTest nodeTest;
+ private int index;
+ private int currentNodeNr;
+
+ /**
+ * Constructor. Note: this constructor will only be called if the relevant node
+ * is an element and if it has one or more attributes. Otherwise an EmptyEnumeration
+ * will be constructed instead.
+ * @param tree: the containing TinyTree
+ * @param element: the node number of the element whose attributes are required
+ * @param nodeTest: condition to be applied to the names of the attributes selected
+ */
+
+ AttributeEnumeration(/*@NotNull*/ TinyTree tree, int element, NodeTest nodeTest) {
+
+ this.nodeTest = nodeTest;
+ this.tree = tree;
+ this.element = element;
+ index = tree.alpha[element];
+ currentNodeNr = -1;
+ }
+
+ /**
+ * Move to the next node in the iteration.
+ */
+
+ public boolean moveNext() {
+ while (true) {
+ if (index >= tree.numberOfAttributes || tree.attParent[index] != element) {
+ index = Integer.MAX_VALUE;
+ current = null;
+ position = -1;
+ currentNodeNr = -1;
+ return false;
+ }
+ int typeCode = tree.getAttributeAnnotation(index);
+ if (nodeTest.matches(Type.ATTRIBUTE, new CodedName(tree.attCode[index], tree.getNamePool()), typeCode)) {
+ position++;
+ currentNodeNr = index++;
+ if (nodeTest instanceof NameTest) {
+ // there can only be one match, so abandon the search after this node
+ index = Integer.MAX_VALUE;
+ }
+ current = null;
+ return true;
+ }
+ index++;
+ }
+ }
+
+ /**
+ * Get the next item in the sequence. <BR>
+ *
+ * @return the next Item. If there are no more nodes, return null.
+ */
+
+ /*@Nullable*/ public NodeInfo next() {
+ if (moveNext()) {
+ current = tree.getAttributeNode(currentNodeNr);
+ } else {
+ current = null;
+ }
+ return current;
+ }
+
+ /**
+ * Get the current node in the sequence.
+ *
+ * @return the node returned by the most recent call on next(), or the node on which we positioned using
+ * moveNext()
+ */
+
+ /*@Nullable*/ public NodeInfo current() {
+ if (current == null) {
+ if (currentNodeNr == -1) {
+ return null;
+ } else {
+ current = tree.getAttributeNode(currentNodeNr);
+ }
+ }
+ return current;
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ if (currentNodeNr == -1) {
+ throw new NullPointerException();
+ }
+ return tree.getTypedValueOfAttribute(null, currentNodeNr);
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ if (currentNodeNr == -1) {
+ throw new NullPointerException();
+ }
+ return tree.attValue[currentNodeNr];
+ }
+
+ /**
+ * Get another iteration over the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new AttributeEnumeration(tree, element, nodeTest);
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/CharSlice.java b/sf/saxon/tree/tiny/CharSlice.java
new file mode 100644
index 0000000..115ca4f
--- /dev/null
+++ b/sf/saxon/tree/tiny/CharSlice.java
@@ -0,0 +1,228 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import java.io.Serializable;
+import java.io.Writer;
+
+/**
+ * This is an implementation of the JDK 1.4 CharSequence interface: it implements
+ * a CharSequence as a view of an array. The implementation relies on the array
+ * being immutable: as a minimum, the caller is required to ensure that the array
+ * contents will not change so long as the CharSlice remains in existence.
+ *
+ * This class should be more efficient than String because it avoids copying the
+ * characters unnecessarily.
+ *
+ * The methods in the class don't check their arguments. Incorrect arguments will
+ * generally result in exceptions from lower-level classes.
+ *
+ */
+public final class CharSlice implements CharSequence, Serializable {
+
+ private char[] array;
+ private int offset;
+ private int count;
+
+ /**
+ * Create a CharSlice that maps to the whole of a char[] array
+ * @param array the char[] array
+ */
+
+ public CharSlice(char[] array) {
+ this.array = array;
+ offset = 0;
+ count = array.length;
+ }
+
+ /**
+ * Create a CharSlice that maps to a section of a char[] array
+ * @param array the char[] array
+ * @param start position of the first character to be included
+ * @param length number of characters to be included
+ */
+
+ public CharSlice(char[] array, int start, int length) {
+ this.array = array;
+ offset = start;
+ count = length;
+ if (start + length > array.length) {
+ throw new IndexOutOfBoundsException("start(" + start +
+ ") + length(" + length + ") > size(" + array.length + ')');
+ }
+ }
+
+ /**
+ * Returns the length of this character sequence. The length is the number
+ * of 16-bit Unicode characters in the sequence. </p>
+ *
+ * @return the number of characters in this sequence
+ */
+ public int length() {
+ return count;
+ }
+
+ /**
+ * Set the length of this character sequence, without changing the array and start offset
+ * to which it is bound
+ * @param length the new length of the CharSlice (which must be less than the existing length,
+ * though this is not enforced)
+ */
+ public void setLength(int length) {
+ count = length;
+ }
+
+ /**
+ * Returns the character at the specified index. An index ranges from zero
+ * to <tt>length() - 1</tt>. The first character of the sequence is at
+ * index zero, the next at index one, and so on, as for array
+ * indexing. </p>
+ *
+ * @param index the index of the character to be returned
+ * @return the specified character
+ * @throws java.lang.IndexOutOfBoundsException
+ * if the <tt>index</tt> argument is negative or not less than
+ * <tt>length()</tt>
+ */
+ public char charAt(int index) {
+ return array[offset+index];
+ }
+
+ /**
+ * Returns a new character sequence that is a subsequence of this sequence.
+ * The subsequence starts with the character at the specified index and
+ * ends with the character at index <tt>end - 1</tt>. The length of the
+ * returned sequence is <tt>end - start</tt>, so if <tt>start == end</tt>
+ * then an empty sequence is returned. </p>
+ *
+ * @param start the start index, inclusive
+ * @param end the end index, exclusive
+ *
+ * @return the specified subsequence
+ *
+ * @throws java.lang.IndexOutOfBoundsException
+ * if <tt>start</tt> or <tt>end</tt> are negative,
+ * if <tt>end</tt> is greater than <tt>length()</tt>,
+ * or if <tt>start</tt> is greater than <tt>end</tt>
+ */
+ public CharSequence subSequence(int start, int end) {
+ return new CharSlice(array, offset+start, end-start);
+ }
+
+ /**
+ * Convert to a string
+ */
+
+ public String toString() {
+ return new String(array, offset, count);
+ }
+
+ /**
+ * Compare equality
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof CharSlice) {
+ CharSlice cs2 = (CharSlice)other;
+ if (count != cs2.count) {
+ return false;
+ }
+ int limit = offset + count;
+ for (int j=offset, k=cs2.offset; j<limit; ) {
+ if (array[j++] != cs2.array[k++]) {
+ return false;
+ }
+ }
+ return true;
+ } else if (other instanceof CharSequence) {
+ return count == ((CharSequence)other).length() && toString().equals(other.toString());
+ }
+ return false;
+ }
+
+ /**
+ * Generate a hash code
+ */
+
+ public int hashCode() {
+ // Same algorithm as String#hashCode(), but not cached
+ int end = offset+count;
+ int h = 0;
+ for (int i = offset; i < end; i++) {
+ h = 31 * h + array[i];
+ }
+ return h;
+ }
+
+ /**
+ * Get the index of a specific character in the sequence. Returns -1 if not found.
+ * This method mimics {@link String#indexOf}
+ * @param c the character to be found
+ * @return the position of the first occurrence of that character, or -1 if not found.
+ */
+
+ public int indexOf(char c) {
+ int end = offset+count;
+ for (int i = offset; i < end; i++) {
+ if (array[i] == c) {
+ return i-offset;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns a new character sequence that is a subsequence of this sequence.
+ * Unlike subSequence, this is guaranteed to return a String.
+ * @param start position of the first character to be included (relative to the
+ * start of the CharSlice, not the underlying array)
+ * @param end position of the first character <b>not</b> to be included (relative
+ * to the start of the CharSlice)
+ * @return the substring, as a String object
+ */
+
+ public String substring(int start, int end) {
+ return new String(array, offset+start, end-start);
+ }
+
+ /**
+ * Append the contents to another array at a given offset. The caller is responsible
+ * for ensuring that sufficient space is available.
+ * @param destination the array to which the characters will be copied
+ * @param destOffset the offset in the target array where the copy will start
+ */
+
+ public void copyTo(char[] destination, int destOffset) {
+ System.arraycopy(array, offset, destination, destOffset, count);
+ }
+
+ /**
+ * Append the contents to another array at a given offset. The caller is responsible
+ * for ensuring that sufficient space is available. Otherwise this behaves like String.getChars()
+ * @param start offset of first character to be copied
+ * @param end offset of the first character that is not copied
+ * @param destination the array to which the characters will be copied
+ * @param destOffset the offset in the target array where the copy will start
+ */
+
+ public void getChars(int start, int end, char[] destination, int destOffset) {
+ System.arraycopy(array, offset+start, destination, destOffset, end - start);
+ }
+
+ /**
+ * Write the value to a writer
+ * @param writer the writer to be written to
+ * @throws java.io.IOException if writing fails
+ */
+
+ public void write(/*@NotNull*/ Writer writer) throws java.io.IOException {
+ writer.write(array, offset, count);
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/CompressedWhitespace.java b/sf/saxon/tree/tiny/CompressedWhitespace.java
new file mode 100644
index 0000000..f890775
--- /dev/null
+++ b/sf/saxon/tree/tiny/CompressedWhitespace.java
@@ -0,0 +1,308 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.io.OutputStream;
+import java.io.Writer;
+
+/**
+ * This class provides a compressed representation of a sequence of whitespace characters. The representation
+ * is a sequence of bytes: in each byte the top two bits indicate which whitespace character is used
+ * (x9, xA, xD, or x20) and the bottom six bits indicate the number of such characters. A zero byte is a filler.
+ * We don't compress the sequence if it would occupy more than 8 bytes, because that's the space we've got available
+ * in the TinyTree arrays.
+ */
+
+public class CompressedWhitespace implements CharSequence {
+
+ /*@NotNull*/ private static char[] WHITE_CHARS = {0x09, 0x0A, 0x0D, 0x20};
+ /*@NotNull*/ private static int[] CODES =
+ {-1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, -1, 2, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3};
+
+ private long value;
+
+ public CompressedWhitespace(long compressedValue) {
+ value = compressedValue;
+ }
+
+ /**
+ * Attempt to compress a CharSequence
+ * @param in the CharSequence to be compressed
+ * @return the compressed sequence if it can be compressed; or the original CharSequence otherwise
+ */
+
+ /*@NotNull*/ public static CharSequence compress(/*@NotNull*/ CharSequence in) {
+ final int inlen = in.length();
+ if (inlen == 0) {
+ return in;
+ }
+ int runlength = 1;
+ int outlength = 0;
+ for (int i=0; i<inlen; i++) {
+ final char c = in.charAt(i);
+ if (c <= 32 && CODES[c] > 0) {
+ if (i == inlen-1 || c != in.charAt(i+1) || runlength == 63) {
+ runlength = 1;
+ outlength++;
+ if (outlength > 8) {
+ return in;
+ }
+ } else {
+ runlength++;
+ }
+ } else {
+ return in;
+ }
+ }
+ int ix = 0;
+ runlength = 1;
+ int[] out = new int[outlength];
+ for (int i=0; i<inlen; i++) {
+ final char c = in.charAt(i);
+ if (i == inlen-1 || c != in.charAt(i+1) || runlength == 63) {
+ out[ix++] = (CODES[c]<<6) | runlength;
+ runlength = 1;
+ } else {
+ runlength++;
+ }
+ }
+ long value = 0;
+ for (int i=0; i<outlength; i++) {
+ value = (value<<8) | out[i];
+ }
+ value <<= (8*(8-outlength));
+ return new CompressedWhitespace(value);
+ }
+
+ /**
+ * Uncompress the whitespace to a FastStringBuffer
+ * @param buffer the buffer to which the whitespace is to be appended. The parameter may be
+ * null, in which case a new buffer is created.
+ * @return the FastStringBuffer to which the whitespace has been appended. If a buffer was
+ * supplied in the argument, this will be the same buffer.
+ */
+
+ public FastStringBuffer uncompress(/*@Nullable*/ FastStringBuffer buffer) {
+ if (buffer == null) {
+ buffer = new FastStringBuffer(length());
+ }
+ uncompress(value, buffer);
+ return buffer;
+ }
+
+ public static void uncompress(long value, /*@NotNull*/ FastStringBuffer buffer) {
+ for (int s=56; s>=0; s-=8) {
+ byte b = (byte)((value >>>s) & 0xff);
+ if (b == 0) {
+ break;
+ }
+ char c = WHITE_CHARS[b>>>6 & 0x3];
+ int len = (b & 0x3f);
+ buffer.ensureCapacity(len);
+ for (int j=0; j<len; j++) {
+ buffer.append(c);
+ }
+ }
+ }
+
+ public long getCompressedValue() {
+ return value;
+ }
+
+ public int length() {
+ int count = 0;
+ long val = value;
+ for (int s=56; s>=0; s-=8) {
+ int c = (int)((val>>>s) & 0x3f);
+ if (c == 0) {
+ break;
+ }
+ count += c;
+ }
+ return count;
+ }
+
+ /**
+ * Returns the <code>char</code> value at the specified index. An index ranges from zero
+ * to <tt>length() - 1</tt>. The first <code>char</code> value of the sequence is at
+ * index zero, the next at index one, and so on, as for array
+ * indexing. </p>
+ * <p/>
+ * <p>If the <code>char</code> value specified by the index is a
+ * <a href="Character.html#unicode">surrogate</a>, the surrogate
+ * value is returned.
+ *
+ * @param index the index of the <code>char</code> value to be returned
+ * @return the specified <code>char</code> value
+ * @throws IndexOutOfBoundsException if the <tt>index</tt> argument is negative or not less than
+ * <tt>length()</tt>
+ */
+ public char charAt(int index) {
+ int count = 0;
+ final long val = value;
+ for (int s=56; s>=0; s-=8) {
+ byte b = (byte)((val>>>s) & 0xff);
+ if (b == 0) {
+ break;
+ }
+ count += (b & 0x3f);
+ if (count > index) {
+ return WHITE_CHARS[b>>>6 & 0x3];
+ }
+ }
+ throw new IndexOutOfBoundsException(index+"");
+ }
+
+ /**
+ * Returns a new <code>CharSequence</code> that is a subsequence of this sequence.
+ * The subsequence starts with the <code>char</code> value at the specified index and
+ * ends with the <code>char</code> value at index <tt>end - 1</tt>. The length
+ * (in <code>char</code>s) of the
+ * returned sequence is <tt>end - start</tt>, so if <tt>start == end</tt>
+ * then an empty sequence is returned. </p>
+ *
+ * @param start the start index, inclusive
+ * @param end the end index, exclusive
+ * @return the specified subsequence
+ * @throws IndexOutOfBoundsException if <tt>start</tt> or <tt>end</tt> are negative,
+ * if <tt>end</tt> is greater than <tt>length()</tt>,
+ * or if <tt>start</tt> is greater than <tt>end</tt>
+ */
+ public CharSequence subSequence(int start, int end) {
+ return uncompress(null).subSequence(start, end);
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ public boolean equals(/*@NotNull*/ Object obj) {
+ if (obj instanceof CompressedWhitespace) {
+ return value == ((CompressedWhitespace)obj).value;
+ }
+ return uncompress(null).equals(obj);
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+ public int hashCode() {
+ return uncompress(null).hashCode();
+ }
+
+ /**
+ * Returns a string representation of the object.
+ */
+ public String toString() {
+ return uncompress(null).toString();
+ }
+
+ /**
+ * Write the value to a Writer
+ * @param writer the writer to write to
+ * @throws java.io.IOException if an error occurs downstream
+ */
+
+ public void write(/*@NotNull*/ Writer writer) throws java.io.IOException {
+ final long val = value;
+ for (int s=56; s>=0; s-=8) {
+ final byte b = (byte)((val>>>s) & 0xff);
+ if (b == 0) {
+ break;
+ }
+ final char c = WHITE_CHARS[b>>>6 & 0x3];
+ final int len = (b & 0x3f);
+ for (int j=0; j<len; j++) {
+ writer.write(c);
+ }
+ }
+ }
+
+ /**
+ * Write the value to a Writer with escaping of special characters
+ * @param specialChars identifies which characters are considered special
+ * @param writer the writer to write to
+ * @throws java.io.IOException if an error occurs downstream
+ */
+
+ public void writeEscape(boolean[] specialChars, /*@NotNull*/ Writer writer) throws java.io.IOException {
+ final long val = value;
+ for (int s=56; s>=0; s-=8) {
+ final byte b = (byte)((val>>>s) & 0xff);
+ if (b == 0) {
+ break;
+ }
+ final char c = WHITE_CHARS[b>>>6 & 0x3];
+ final int len = (b & 0x3f);
+ if (specialChars[c]) {
+ String e = "";
+ if (c=='\n') {
+ e = "
";
+ } else if (c=='\r') {
+ e = "
";
+ } else if (c=='\t') {
+ e = "	";
+ }
+ for (int j=0; j<len; j++) {
+ writer.write(e);
+ }
+ } else {
+ for (int j=0; j<len; j++) {
+ writer.write(c);
+ }
+ }
+ }
+ }
+
+ /**
+ * Write the value to a UTF-8 OutputStream with escaping of special characters
+ * @param specialChars array of booleans indicating which characters need to be XML-escaped
+ * @param stream the output stream to write to
+ * @throws java.io.IOException if an error occurs downstream
+ */
+
+ public void writeEscape(boolean[] specialChars, /*@NotNull*/ OutputStream stream) throws java.io.IOException {
+ final long val = value;
+ for (int s=56; s>=0; s-=8) {
+ final byte b = (byte)((val>>>s) & 0xff);
+ if (b == 0) {
+ break;
+ }
+ final char c = WHITE_CHARS[b>>>6 & 0x3];
+ final int len = (b & 0x3f);
+ if (specialChars[c]) {
+ byte[] e;
+ if (c=='\n') {
+ e = ESCAPE_N;
+ } else if (c=='\r') {
+ e = ESCAPE_R;
+ } else {
+ e = ESCAPE_T;
+ }
+ for (int j=0; j<len; j++) {
+ stream.write(e);
+ }
+ } else {
+ for (int j=0; j<len; j++) {
+ stream.write(c);
+ }
+ }
+ }
+ }
+
+ /*@NotNull*/ private static byte[] ESCAPE_N = {'&', '#', 'x', 'A', ';'};
+ /*@NotNull*/ private static byte[] ESCAPE_R = {'&', '#', 'x', 'D', ';'};
+ /*@NotNull*/ private static byte[] ESCAPE_T = {'&', '#', 'x', '9', ';'};
+
+
+
+}
+
diff --git a/sf/saxon/tree/tiny/DescendantEnumeration.java b/sf/saxon/tree/tiny/DescendantEnumeration.java
new file mode 100644
index 0000000..f03471a
--- /dev/null
+++ b/sf/saxon/tree/tiny/DescendantEnumeration.java
@@ -0,0 +1,89 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorImpl;
+
+/**
+* This class supports both the descendant:: and descendant-or-self:: axes, which are
+* identical except for the route to the first candidate node.
+* It enumerates descendants of the specified node.
+* The calling code must ensure that the start node is not an attribute or namespace node.
+*/
+
+final class DescendantEnumeration extends AxisIteratorImpl {
+
+ private TinyTree tree;
+ private TinyNodeImpl startNode;
+ private boolean includeSelf;
+ private int nextNodeNr;
+ private int startDepth;
+ private NodeTest test;
+
+ /**
+ * Create an iterator over the descendant axis
+ * @param doc the containing TinyTree
+ * @param node the node whose descendants are required
+ * @param nodeTest test to be satisfied by each returned node
+ * @param includeSelf true if the start node is to be included
+ */
+
+ DescendantEnumeration(/*@NotNull*/ TinyTree doc, /*@NotNull*/ TinyNodeImpl node, NodeTest nodeTest, boolean includeSelf) {
+ tree = doc;
+ startNode = node;
+ this.includeSelf = includeSelf;
+ test = nodeTest;
+ nextNodeNr = node.nodeNr;
+ startDepth = doc.depth[nextNodeNr];
+ }
+
+ /*@Nullable*/ public NodeInfo next() {
+ if (position==0 && includeSelf && test.matches(startNode)) {
+ current = startNode;
+ position++;
+ return current;
+ }
+
+ do {
+ nextNodeNr++;
+ try {
+ if (tree.depth[nextNodeNr] <= startDepth) {
+ nextNodeNr = -1;
+ current = null;
+ position = -1;
+ return null;
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // this shouldn't happen. If it does happen, it means the tree wasn't properly closed
+ // during construction (there is no stopper node at the end). In this case, we'll recover
+ // by returning end-of sequence
+ //System.err.println("********* no stopper node **********");
+ nextNodeNr = -1;
+ current = null;
+ position = -1;
+ return null;
+ }
+ } while (!test.matches(tree, nextNodeNr));
+
+ position++;
+ current = tree.getNode(nextNodeNr);
+ return current;
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new DescendantEnumeration(tree, startNode, test, includeSelf);
+ }
+}
+
diff --git a/sf/saxon/tree/tiny/FollowingEnumeration.java b/sf/saxon/tree/tiny/FollowingEnumeration.java
new file mode 100644
index 0000000..04085d3
--- /dev/null
+++ b/sf/saxon/tree/tiny/FollowingEnumeration.java
@@ -0,0 +1,102 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorImpl;
+
+/**
+* Iterate over the following axis starting at a given node.
+* The start node must not be a namespace or attribute node.
+*/
+
+final class FollowingEnumeration extends AxisIteratorImpl {
+
+ private TinyTree tree;
+ private TinyNodeImpl startNode;
+ private NodeTest test;
+ private boolean includeDescendants;
+
+ /**
+ * Create an iterator over the following axis
+ * @param doc the containing TinyTree
+ * @param node the start node. If the actual start was an attribute or namespace node, this will
+ * be the parent element of that attribute or namespace
+ * @param nodeTest condition that all the returned nodes must satisfy
+ * @param includeDescendants true if descendants of the start node are to be included. This will
+ * be false if the actual start was an element node, true if it was an attribute or namespace node
+ * (since the children of their parent follow the attribute or namespace in document order).
+ */
+
+ public FollowingEnumeration(TinyTree doc, TinyNodeImpl node,
+ NodeTest nodeTest, boolean includeDescendants) {
+ tree = doc;
+ test = nodeTest;
+ startNode = node;
+ this.includeDescendants = includeDescendants;
+ }
+
+ /*@Nullable*/ public NodeInfo next() {
+ int nodeNr;
+ if (position <= 0) {
+ if (position < 0) {
+ // already at end
+ return null;
+ }
+ // first time call
+ nodeNr = startNode.nodeNr;
+
+ // skip the descendant nodes if any
+ if (includeDescendants) {
+ nodeNr++;
+ } else {
+ while (true) {
+ int nextSib = tree.next[nodeNr];
+ if (nextSib > nodeNr) {
+ nodeNr = nextSib;
+ break;
+ } else if (tree.depth[nextSib] == 0) {
+ current = null;
+ position = -1;
+ return null;
+ } else {
+ nodeNr = nextSib;
+ }
+ }
+ }
+ } else {
+ assert current != null;
+ nodeNr = ((TinyNodeImpl)current).nodeNr + 1;
+ }
+
+ while (true) {
+ if (tree.depth[nodeNr] == 0) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ if (test.matches(tree, nodeNr)) {
+ position++;
+ current = tree.getNode(nodeNr);
+ return current;
+ }
+ nodeNr++;
+ }
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new FollowingEnumeration(tree, startNode, test, includeDescendants);
+ }
+}
+
diff --git a/sf/saxon/tree/tiny/LargeStringBuffer.java b/sf/saxon/tree/tiny/LargeStringBuffer.java
new file mode 100644
index 0000000..fa806d4
--- /dev/null
+++ b/sf/saxon/tree/tiny/LargeStringBuffer.java
@@ -0,0 +1,316 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.io.Serializable;
+
+/**
+ * This is an implementation of the JDK 1.4 CharSequence interface: it implements
+ * a CharSequence as a list of arrays of characters (the individual arrays are known
+ * as segments). The segments have a fixed size of 65536 characters.
+ * <p/>
+ * This is more efficient than a buffer backed by a contiguous array of characters
+ * in cases where the size is likely to grow very large, and where substring operations
+ * are rare. As used within the TinyTree, extraction of the string value of a node
+ * requires character copying only in the case where the value crosses segment
+ * boundaries.
+ */
+
+public final class LargeStringBuffer implements AppendableCharSequence, Serializable {
+
+ private final static int BITS = 16;
+ private final static int SEGLEN = 1<<BITS;
+ private final static int MASK = SEGLEN - 1;
+
+ // Variant of LargeStringBuffer using fixed-length segments
+
+ private char[][] data;
+ private int length; // total length of the CharSequence
+ private int segmentsUsed;
+
+ /**
+ * Create an empty LargeStringBuffer with default space allocation
+ */
+
+ public LargeStringBuffer() {
+ data = new char[1][];
+ segmentsUsed = 0;
+ length = 0;
+ }
+
+ /**
+ * Expand the data structure. Note this only involves expanding the "index" (the list of
+ * segments), it does not cause any character data to be copied.
+ * @param seg the new segment to be added.
+ */
+
+ private void addSegment(char[] seg) {
+ int segs = data.length;
+ if (segmentsUsed + 1 > segs) {
+ char[][] d2 = new char[segs*2][];
+ System.arraycopy(data, 0, d2, 0, segmentsUsed);
+ data = d2;
+ }
+ data[segmentsUsed++] = seg;
+ }
+
+ /**
+ * Append a CharSequence to this LargeStringBuffer
+ * @param s the data to be appended
+ */
+
+ public void append(CharSequence s) {
+ // Although we provide variants of this method for different subtypes, Java decides which to use based
+ // on the static type of the operand. We want to use the right method based on the dynamic type, to avoid
+ // creating objects and copying strings unnecessarily. So we do a dynamic dispatch. (This is only necessary
+ // of course because the CharSequence class offers no getChars() method).
+
+ if (s instanceof CompressedWhitespace) {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ ((CompressedWhitespace)s).uncompress(fsb);
+ append(fsb);
+ return;
+ }
+
+ final int len = s.length();
+ char[] firstSeg;
+ int firstSegOffset = length & MASK;
+ if (firstSegOffset == 0) {
+ firstSeg = new char[SEGLEN];
+ addSegment(firstSeg);
+ } else {
+ firstSeg = data[length>>BITS];
+ }
+ int firstSegLen;
+ int fullSegments;
+ int lastSegLen;
+ if (len <= SEGLEN - firstSegOffset) {
+ // all fits in the current segment
+ firstSegLen = len;
+ fullSegments = 0;
+ lastSegLen = 0;
+ } else {
+ firstSegLen = SEGLEN - firstSegOffset;
+ fullSegments = (len - firstSegLen) >> BITS;
+ lastSegLen = (len - firstSegLen) & MASK;
+ }
+
+ if (s instanceof CharSlice) {
+ ((CharSlice)s).getChars(0, firstSegLen, firstSeg, firstSegOffset);
+ int start = firstSegLen;
+ for (int i=0; i<fullSegments; i++) {
+ char[] seg = new char[SEGLEN];
+ addSegment(seg);
+ ((CharSlice)s).getChars(start, start+SEGLEN, seg, 0);
+ start += SEGLEN;
+ }
+ if (lastSegLen > 0) {
+ char[] seg = new char[SEGLEN];
+ addSegment(seg);
+ ((CharSlice)s).getChars(start, len, seg, 0);
+ }
+ length += len;
+
+ } else if (s instanceof String) {
+ ((String)s).getChars(0, firstSegLen, firstSeg, firstSegOffset);
+ int start = firstSegLen;
+ for (int i=0; i<fullSegments; i++) {
+ char[] seg = new char[SEGLEN];
+ addSegment(seg);
+ ((String)s).getChars(start, start+SEGLEN, seg, 0);
+ start += SEGLEN;
+ }
+ if (lastSegLen > 0) {
+ char[] seg = new char[SEGLEN];
+ addSegment(seg);
+ ((String)s).getChars(start, len, seg, 0);
+ }
+ length += len;
+
+ } else if (s instanceof FastStringBuffer) {
+ ((FastStringBuffer)s).getChars(0, firstSegLen, firstSeg, firstSegOffset);
+ int start = firstSegLen;
+ for (int i=0; i<fullSegments; i++) {
+ char[] seg = new char[SEGLEN];
+ addSegment(seg);
+ ((FastStringBuffer)s).getChars(start, start+SEGLEN, seg, 0);
+ start += SEGLEN;
+ }
+ if (lastSegLen > 0) {
+ char[] seg = new char[SEGLEN];
+ addSegment(seg);
+ ((FastStringBuffer)s).getChars(start, len, seg, 0);
+ }
+ length += len;
+
+ } else {
+ append(s.toString());
+ }
+ }
+
+ /**
+ * Returns the length of this character sequence. The length is the number
+ * of 16-bit UTF-16 characters in the sequence. </p>
+ * @return the number of characters in this sequence
+ */
+ public int length() {
+ return length;
+ }
+
+ /**
+ * Set the length. If this exceeds the current length, this method is a no-op.
+ * If this is less than the current length, characters beyond the specified point
+ * are deleted.
+ * @param length the new length
+ */
+
+ public void setLength(int length) {
+ if (length < this.length) {
+ int usedInLastSegment = length & MASK;
+ this.length = length;
+ this.segmentsUsed = length / SEGLEN + (usedInLastSegment == 0 ? 0 : 1);
+ }
+ }
+
+ /**
+ * Returns the character at the specified index. An index ranges from zero
+ * to <tt>length() - 1</tt>. The first character of the sequence is at
+ * index zero, the next at index one, and so on, as for array
+ * indexing. </p>
+ * @param index the index of the character to be returned
+ * @return the specified character
+ * @throws IndexOutOfBoundsException if the <tt>index</tt> argument is negative or not less than
+ * <tt>length()</tt>
+ */
+ public char charAt(int index) {
+ if (index < 0 || index >= length) {
+ throw new IndexOutOfBoundsException(index + "");
+ }
+ return data[index>>BITS][index&MASK];
+ }
+
+ /**
+ * Returns a new character sequence that is a subsequence of this sequence.
+ * The subsequence starts with the character at the specified index and
+ * ends with the character at index <tt>end - 1</tt>. The length of the
+ * returned sequence is <tt>end - start</tt>, so if <tt>start == end</tt>
+ * then an empty sequence is returned. </p>
+ * @param start the start index, inclusive
+ * @param end the end index, exclusive
+ * @return the specified subsequence
+ * @throws IndexOutOfBoundsException if <tt>start</tt> or <tt>end</tt> are negative,
+ * if <tt>end</tt> is greater than <tt>length()</tt>,
+ * or if <tt>start</tt> is greater than <tt>end</tt>
+ */
+ /*@NotNull*/ public CharSequence subSequence(int start, int end) {
+ int firstSeg = start>>BITS;
+ int lastSeg = (end-1)>>BITS;
+ if (firstSeg == lastSeg) {
+ return new CharSlice(data[firstSeg], start & MASK, end - start);
+ } else {
+ FastStringBuffer fsb = new FastStringBuffer(end - start);
+ int firstSegLen = SEGLEN - (start & MASK);
+ fsb.append(data[firstSeg], start & MASK, firstSegLen);
+ int doneTo = start + firstSegLen;
+ while (true) {
+ firstSeg++;
+ if (doneTo + SEGLEN < end) {
+ fsb.append(data[firstSeg]);
+ doneTo += SEGLEN;
+ } else {
+ fsb.append(data[firstSeg], 0, end - doneTo);
+ break;
+ }
+ }
+ return fsb;
+ }
+ }
+
+ /**
+ * Convert to a string
+ */
+
+ public String toString() {
+ return subSequence(0, length).toString();
+ }
+
+ /**
+ * Compare equality
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof CharSequence && toString().equals(other.toString());
+ }
+
+ /**
+ * Generate a hash code
+ */
+
+ public int hashCode() {
+ // Same algorithm as String#hashCode(), but not cached
+ int h = 0;
+ for (char[] chars : data) {
+ for (int i = 0; i < SEGLEN; i++) {
+ h = 31 * h + chars[i];
+ }
+ }
+ return h;
+ }
+
+ /**
+ * Returns a new character sequence that is a subsequence of this sequence.
+ * Unlike subSequence, this is guaranteed to return a String.
+ * @param start index of the first character to be included
+ * @param end index of the character after the last one to be included
+ * @return the substring at the given position
+ */
+
+ public String substring(int start, int end) {
+ return subSequence(start, end).toString();
+ }
+
+ /**
+ * Write the value to a writer
+ * @param writer the writer to which the value is to be written
+ * @throws java.io.IOException if an error occurs downstream
+ */
+
+ public void write(java.io.Writer writer) throws java.io.IOException {
+ writer.write(toString());
+ }
+
+ /**
+ * Produce diagnostic dump
+ */
+
+// public void dumpDataStructure() {
+// System.err.println("** Segments:");
+// for (int s = 0; s < segments.size(); s++) {
+// System.err.println(" SEG " + s + " start offset " + startOffsets[s] + " length "
+// + ((FastStringBuffer)segments.get(s)).length());
+// }
+// }
+
+// public static void main(String[] args) {
+// LargeStringBuffer lsb = new LargeStringBuffer();
+// for (int i=0; i<30; i++) {
+// char[] chars = new char[i*5000];
+// Arrays.fill(chars, 'x');
+// lsb.append(new String(chars));
+// lsb.append("");
+// }
+// for (int i=0; i<lsb.length()-10000; i+=10000) {
+// System.out.println(i + ":" + lsb.subSequence(i, i+9999).length());
+// }
+// lsb.dumpDataStructure();
+// }
+
+}
+
diff --git a/sf/saxon/tree/tiny/PrecedingEnumeration.java b/sf/saxon/tree/tiny/PrecedingEnumeration.java
new file mode 100644
index 0000000..819c472
--- /dev/null
+++ b/sf/saxon/tree/tiny/PrecedingEnumeration.java
@@ -0,0 +1,90 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorImpl;
+
+/**
+* Enumerate all the nodes on the preceding axis from a given start node.
+* The calling code ensures that the start node is not a root, attribute,
+* or namespace node. As well as the standard XPath preceding axis, this
+* class also implements a Saxon-specific "preceding-or-ancestor" axis
+* which returns ancestor nodes as well as preceding nodes. This is used
+* when performing xsl:number level="any".
+*/
+
+final class PrecedingEnumeration extends AxisIteratorImpl {
+
+ private TinyTree tree;
+ private TinyNodeImpl startNode;
+ private NodeTest test;
+ private int nextAncestorDepth;
+ private boolean includeAncestors;
+
+ public PrecedingEnumeration(/*@NotNull*/ TinyTree doc, /*@NotNull*/ TinyNodeImpl node,
+ NodeTest nodeTest, boolean includeAncestors) {
+
+ this.includeAncestors = includeAncestors;
+ test = nodeTest;
+ tree = doc;
+ startNode = node;
+ current = startNode;
+ nextAncestorDepth = doc.depth[node.nodeNr] - 1;
+ }
+
+ /*@Nullable*/ public NodeInfo next() {
+ if (current == null) {
+ return null;
+ }
+ int nextNodeNr = ((TinyNodeImpl)current).nodeNr;
+ while (true) {
+ if (!includeAncestors) {
+ nextNodeNr--;
+ // skip over ancestor elements
+ while (nextAncestorDepth >= 0 && tree.depth[nextNodeNr] == nextAncestorDepth) {
+ if (nextAncestorDepth-- <= 0) { // bug 1121528
+ current = null;
+ position = -1;
+ return null;
+ }
+ nextNodeNr--;
+ }
+ } else {
+ if (tree.depth[nextNodeNr] == 0) {
+ current = null;
+ position = -1;
+ return null;
+ } else {
+ nextNodeNr--;
+ }
+ }
+ if (test.matches(tree, nextNodeNr)) {
+ position++;
+ current = tree.getNode(nextNodeNr);
+ return current;
+ }
+ if (tree.depth[nextNodeNr] == 0) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new PrecedingEnumeration(tree, startNode, test, includeAncestors);
+ }
+}
+
diff --git a/sf/saxon/tree/tiny/PrecedingSiblingEnumeration.java b/sf/saxon/tree/tiny/PrecedingSiblingEnumeration.java
new file mode 100644
index 0000000..285e182
--- /dev/null
+++ b/sf/saxon/tree/tiny/PrecedingSiblingEnumeration.java
@@ -0,0 +1,68 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorImpl;
+
+/**
+* This class supports the preceding-sibling axis.
+* The starting node must be an element, text node, comment, or processing instruction:
+* to ensure this, construct the enumeration using NodeInfo#getEnumeration()
+*/
+
+final class PrecedingSiblingEnumeration extends AxisIteratorImpl {
+
+ private TinyTree document;
+ private TinyNodeImpl startNode;
+ private int nextNodeNr;
+ private NodeTest test;
+ private TinyNodeImpl parentNode;
+
+ PrecedingSiblingEnumeration(TinyTree doc, /*@NotNull*/ TinyNodeImpl node, NodeTest nodeTest) {
+ document = doc;
+ document.ensurePriorIndex();
+ test = nodeTest;
+ startNode = node;
+ nextNodeNr = node.nodeNr;
+ parentNode = node.parent; // doesn't matter if this is null (unknown)
+ }
+
+ /*@Nullable*/ public NodeInfo next() {
+ if (nextNodeNr < 0) {
+ // This check is needed because an errant caller can call next() again after hitting the end of sequence
+ return null;
+ }
+ while (true) {
+ nextNodeNr = document.prior[nextNodeNr];
+ if (nextNodeNr < 0) {
+ current = null;
+ position = -1;
+ return null;
+ }
+ if (test.matches(document, nextNodeNr)) {
+ position++;
+ TinyNodeImpl next = document.getNode(nextNodeNr);
+ next.setParentNode(parentNode);
+ return (current = next);
+ }
+ }
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new PrecedingSiblingEnumeration(document, startNode, test);
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/SiblingEnumeration.java b/sf/saxon/tree/tiny/SiblingEnumeration.java
new file mode 100644
index 0000000..8b95f57
--- /dev/null
+++ b/sf/saxon/tree/tiny/SiblingEnumeration.java
@@ -0,0 +1,287 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorImpl;
+import net.sf.saxon.tree.iter.LookaheadIterator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+/**
+* This class supports both the child:: and following-sibling:: axes, which are
+* identical except for the route to the first candidate node.
+* It enumerates either the children or the following siblings of the specified node.
+* In the case of children, the specified node must always
+* be a node that has children: to ensure this, construct the enumeration
+* using NodeInfo#getEnumeration()
+*/
+
+final class SiblingEnumeration extends AxisIteratorImpl implements LookaheadIterator<NodeInfo> {
+
+ // NOTE: have experimented with a dedicated iterator for the child axis matching against
+ // elements only, by fingerprint - no measurable improvement obtained.
+
+ private TinyTree tree;
+ private int nextNodeNr;
+ /*@Nullable*/ private NodeTest test;
+ private TinyNodeImpl startNode;
+ private TinyNodeImpl parentNode;
+ private boolean getChildren;
+ private boolean needToAdvance = false;
+
+ /**
+ * Return an enumeration over children or siblings of the context node
+ * @param tree The TinyTree containing the context node
+ * @param node The context node, the start point for the iteration
+ * @param nodeTest Test that the selected nodes must satisfy, or null indicating
+ * that all nodes are selected
+ * @param getChildren True if children of the context node are to be returned, false
+ * if following siblings are required
+ */
+
+ SiblingEnumeration(/*@NotNull*/ TinyTree tree, /*@NotNull*/ TinyNodeImpl node,
+ /*@Nullable*/ NodeTest nodeTest, boolean getChildren) {
+ this.tree = tree;
+ test = nodeTest;
+ startNode = node;
+ this.getChildren = getChildren;
+ if (getChildren) { // child:: axis
+ parentNode = node;
+ // move to first child
+ // ASSERT: we don't invoke this code unless the node has children
+ nextNodeNr = node.nodeNr + 1;
+
+ } else { // following-sibling:: axis
+ parentNode = (TinyNodeImpl)node.getParent();
+ if (parentNode == null) {
+ nextNodeNr = -1;
+ } else {
+ // move to next sibling
+ nextNodeNr = tree.next[node.nodeNr];
+ while (tree.nodeKind[nextNodeNr] == Type.PARENT_POINTER) {
+ // skip dummy nodes
+ nextNodeNr = tree.next[nextNodeNr];
+ }
+ if (nextNodeNr < node.nodeNr) {
+ // if "next" pointer goes backwards, it's really an owner pointer from the last sibling
+ nextNodeNr = -1;
+ }
+ }
+ }
+
+ // check if this matches the conditions
+ if (nextNodeNr >= 0 && nodeTest != null) {
+ if (!nodeTest.matches(this.tree, nextNodeNr)) {
+ needToAdvance = true;
+ }
+ }
+ }
+
+ public boolean moveNext() {
+ // if needToAdvance == false, nextNodeNr already identifies the correct node.
+ current = null;
+ if (needToAdvance) {
+ final int thisNode = nextNodeNr;
+ final NodeTest nTest = test;
+ final int[] tNext = tree.next;
+ if (nTest==null) {
+ do {
+ nextNodeNr = tNext[nextNodeNr];
+ } while (tree.nodeKind[nextNodeNr] == Type.PARENT_POINTER);
+ } else {
+ do {
+ nextNodeNr = tNext[nextNodeNr];
+ } while ( nextNodeNr >= thisNode && !nTest.matches(tree, nextNodeNr));
+ }
+
+ if (nextNodeNr < thisNode) { // indicates we've got to the last sibling
+ nextNodeNr = -1;
+ needToAdvance = false;
+ current = null;
+ position = -1;
+ return false;
+ }
+ }
+
+ if (nextNodeNr == -1) {
+ return false;
+ }
+ needToAdvance = true;
+ position++;
+ return true;
+ }
+
+ /**
+ * Return the next node in the sequence
+ * @return the next node, or null if the end of the sequence is reached
+ */
+
+ /*@Nullable*/ public NodeInfo next() {
+ if (needToAdvance) {
+ final int thisNode = nextNodeNr;
+ final int[] tNext = tree.next;
+ final NodeTest nTest = test;
+ if (nTest==null) {
+ do {
+ nextNodeNr = tNext[nextNodeNr];
+ } while (tree.nodeKind[nextNodeNr] == Type.PARENT_POINTER);
+ } else {
+ do {
+ nextNodeNr = tNext[nextNodeNr];
+ } while ( nextNodeNr >= thisNode && !nTest.matches(tree, nextNodeNr));
+ }
+
+ if (nextNodeNr < thisNode) { // indicates we've got to the last sibling
+ nextNodeNr = -1;
+ needToAdvance = false;
+ current = null;
+ position = -1;
+ return null;
+ }
+ }
+
+ if (nextNodeNr == -1) {
+ return null;
+ }
+ needToAdvance = true;
+ position++;
+ TinyNodeImpl nextNode = tree.getNode(nextNodeNr);
+ nextNode.setParentNode(parentNode);
+ return (current = nextNode);
+ }
+
+ /**
+ * Get the current node in the sequence.
+ * @return the node returned by the most recent call on next(), or the one on which we have positioned
+ * using moveNext().
+ */
+
+ /*@Nullable*/ public NodeInfo current() {
+ if (current == null) {
+ if (nextNodeNr == -1) {
+ return null;
+ } else {
+ TinyNodeImpl nextNode = tree.getNode(nextNodeNr);
+ nextNode.setParentNode(parentNode);
+ return (current = nextNode);
+ }
+ }
+ return current;
+ }
+
+ /**
+ * Test whether there are any more nodes to come. This method is used only when testing whether the
+ * current item is the last in the sequence. It's not especially efficient, but is more efficient than
+ * the alternative strategy which involves counting how many nodes there are in the sequence.
+ * @return true if there are more items in the sequence
+ */
+
+ public boolean hasNext() {
+ int n = nextNodeNr;
+ if (needToAdvance) {
+ final NodeTest nTest = test;
+ final int[] tNext = tree.next;
+ if (nTest==null) {
+ do {
+ n = tNext[n];
+ } while (tree.nodeKind[n] == Type.PARENT_POINTER);
+ } else {
+ do {
+ n = tNext[n];
+ } while ( n >= nextNodeNr && !nTest.matches(tree, n));
+ }
+
+ if (n < nextNodeNr) { // indicates we've got to the last sibling
+ return false;
+ }
+ }
+
+ return (n != -1);
+ }
+
+ /**
+ * Return the atomized value of the current node. This is achieved in common cases without
+ * actually instantiating the NodeInfo object
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ int kind;
+ try {
+ kind = tree.nodeKind[nextNodeNr];
+ } catch (ArrayIndexOutOfBoundsException err) {
+ throw new NullPointerException();
+ }
+ switch (kind) {
+ case Type.TEXT: {
+ return new UntypedAtomicValue(TinyTextImpl.getStringValue(tree, nextNodeNr));
+ }
+ case Type.WHITESPACE_TEXT: {
+ return new UntypedAtomicValue(WhitespaceTextImpl.getStringValueCS(tree, nextNodeNr));
+ }
+ case Type.ELEMENT: {
+ return tree.getTypedValueOfElement(nextNodeNr);
+ }
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ return tree.getAtomizedValueOfUntypedNode(nextNodeNr);
+ default:
+ return current().atomize();
+ }
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ int kind;
+ try {
+ kind = tree.nodeKind[nextNodeNr];
+ } catch (ArrayIndexOutOfBoundsException err) {
+ throw new NullPointerException();
+ }
+ switch (kind) {
+ case Type.TEXT: {
+ return TinyTextImpl.getStringValue(tree, nextNodeNr);
+ }
+ case Type.WHITESPACE_TEXT: {
+ return WhitespaceTextImpl.getStringValueCS(tree, nextNodeNr);
+ }
+ case Type.ELEMENT: {
+ return TinyParentNodeImpl.getStringValueCS(tree, nextNodeNr);
+ }
+ default:
+ return current().getStringValueCS();
+ }
+ }
+
+ /**
+ * Get another enumeration of the same nodes
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new SiblingEnumeration(tree, startNode, test, getChildren);
+ }
+
+ public int getProperties() {
+ return LOOKAHEAD;
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyAttributeCollection.java b/sf/saxon/tree/tiny/TinyAttributeCollection.java
new file mode 100644
index 0000000..324042c
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyAttributeCollection.java
@@ -0,0 +1,272 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.om.*;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SimpleType;
+
+/**
+ * An implementation of the AttributeCollection interface based directly on the
+ * TinyTree data structure.
+ */
+
+public class TinyAttributeCollection implements AttributeCollection {
+
+ int element;
+ TinyTree tree;
+ int firstAttribute;
+
+ public TinyAttributeCollection(/*@NotNull*/ TinyTree tree, int element) {
+ this.tree = tree;
+ this.element = element;
+ firstAttribute = tree.alpha[element];
+ }
+
+ /**
+ * Return the number of attributes in the list.
+ *
+ * @return The number of attributes in the list.
+ */
+
+ public int getLength() {
+ int i = firstAttribute;
+ while (i < tree.numberOfAttributes && tree.attParent[i] == element) {
+ i++;
+ }
+ return i - firstAttribute;
+ }
+
+ /**
+ * Get the namecode of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The display name of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ public int getNameCode(int index) {
+ return tree.attCode[firstAttribute + index];
+ }
+
+ /**
+ * Get the node name of an attribute (by position)
+ *
+ * @param index The position of the attribute in the list
+ * @return The node name of the attribute, or null if there is no attribute in that position
+ */
+ /*@NotNull*/ public NodeName getNodeName(int index) {
+ return new CodedName(tree.attCode[firstAttribute + index], tree.getNamePool());
+ }
+
+ /**
+ * Get the type annotation of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The type annotation of the attribute
+ */
+
+ public SimpleType getTypeAnnotation(int index) {
+ if (tree.attTypeCode == null) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ return (SimpleType)tree.getConfiguration().getSchemaType(tree.getAttributeAnnotation(firstAttribute + index));
+ }
+
+ /**
+ * Get the locationID of an attribute (by position)
+ *
+ * @param index The position of the attribute in the list.
+ * @return The location identifier of the attribute. This can be supplied
+ * to a {@link net.sf.saxon.event.LocationProvider} in order to obtain the
+ * actual system identifier and line number of the relevant location
+ */
+
+ public int getLocationId(int index) {
+ return 0;
+ }
+
+ /**
+ * Get the systemId part of the location of an attribute, at a given index.
+ * <p/>
+ * <p>Attribute location information is not available from a SAX parser, so this method
+ * is not useful for getting the location of an attribute in a source document. However,
+ * in a Saxon result document, the location information represents the location in the
+ * stylesheet of the instruction used to generate this attribute, which is useful for
+ * debugging.</p>
+ *
+ * @param index the required attribute
+ * @return the systemId of the location of the attribute
+ */
+
+ public String getSystemId(int index) {
+ return tree.getSystemId(element);
+ }
+
+ /**
+ * Get the line number part of the location of an attribute, at a given index.
+ * <p/>
+ * <p>Attribute location information is not available from a SAX parser, so this method
+ * is not useful for getting the location of an attribute in a source document. However,
+ * in a Saxon result document, the location information represents the location in the
+ * stylesheet of the instruction used to generate this attribute, which is useful for
+ * debugging.</p>
+ *
+ * @param index the required attribute
+ * @return the line number of the location of the attribute
+ */
+
+ public int getLineNumber(int index) {
+ return -1;
+ }
+
+ /**
+ * Get the properties of an attribute (by position)
+ *
+ * @param index The position of the attribute in the list.
+ * @return The properties of the attribute. This is a set
+ * of bit-settings defined in class {@link net.sf.saxon.event.ReceiverOptions}. The
+ * most interesting of these is {{@link net.sf.saxon.event.ReceiverOptions#DEFAULTED_ATTRIBUTE},
+ * which indicates an attribute that was added to an element as a result of schema validation.
+ */
+
+ public int getProperties(int index) {
+ return 0;
+ }
+
+ /**
+ * Get the prefix of the name of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The prefix of the attribute name as a string, or null if there
+ * is no attribute at that position. Returns "" for an attribute that
+ * has no prefix.
+ */
+
+ public String getPrefix(int index) {
+ return tree.getNamePool().getPrefix(tree.attCode[firstAttribute + index]);
+ }
+
+ /**
+ * Get the lexical QName of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The lexical QName of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ public String getQName(int index) {
+ return tree.getNamePool().getDisplayName(tree.attCode[firstAttribute + index]);
+ }
+
+ /**
+ * Get the local name of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The local name of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ public String getLocalName(int index) {
+ return tree.getNamePool().getLocalName(tree.attCode[firstAttribute + index]);
+ }
+
+ /**
+ * Get the namespace URI of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The local name of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ public String getURI(int index) {
+ return tree.getNamePool().getURI(tree.attCode[firstAttribute + index]);
+ }
+
+ /**
+ * Get the index of an attribute (by name).
+ *
+ * @param uri The namespace uri of the attribute.
+ * @param localname The local name of the attribute.
+ * @return The index position of the attribute, or -1 if there is no attribute with this name
+ */
+
+ public int getIndex(String uri, String localname) {
+ int fingerprint = tree.getNamePool().getFingerprint(uri, localname);
+ return getIndexByFingerprint(fingerprint);
+ }
+
+ /**
+ * Get the index, given the fingerprint
+ * @param fingerprint the NamePool fingerprint of the required attribute name
+ * @return The index position of the attribute, or -1 if there is no attribute with this name
+ */
+
+ public int getIndexByFingerprint(int fingerprint) {
+ int i = firstAttribute;
+ while (tree.attParent[i] == element) {
+ if ((tree.attCode[i] & NamePool.FP_MASK) == fingerprint) {
+ return i - firstAttribute;
+ }
+ i++;
+ }
+ return -1;
+ }
+
+ /**
+ * Get the attribute value using its fingerprint
+ */
+
+ /*@Nullable*/ public String getValueByFingerprint(int fingerprint) {
+ return getValue(getIndexByFingerprint(fingerprint));
+ }
+
+ /**
+ * Get the value of an attribute (by name).
+ *
+ * @param uri The namespace uri of the attribute.
+ * @param localname The local name of the attribute.
+ * @return The index position of the attribute
+ */
+
+ /*@Nullable*/ public String getValue(String uri, String localname) {
+ return getValue(getIndex(uri, localname));
+ }
+
+ /**
+ * Get the value of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The attribute value as a string, or null if
+ * there is no attribute at that position.
+ */
+
+ /*@Nullable*/ public String getValue(int index) {
+ CharSequence cs = tree.attValue[firstAttribute + index];
+ return (cs==null ? null : cs.toString());
+ }
+
+ /**
+ * Determine whether a given attribute has the is-ID property set
+ */
+
+ public boolean isId(int index) {
+ return ((getTypeAnnotation(index).getFingerprint() == StandardNames.XS_ID) ||
+ ((tree.attCode[firstAttribute + index] & NamePool.FP_MASK) == StandardNames.XML_ID));
+ }
+
+ /**
+ * Determine whether a given attribute has the is-idref property set
+ */
+
+ public boolean isIdref(int index) {
+ return ((getTypeAnnotation(index).getFingerprint()) == StandardNames.XS_IDREF) ||
+ ((getTypeAnnotation(index).getFingerprint()) == StandardNames.XS_IDREFS);
+ }
+}
+
diff --git a/sf/saxon/tree/tiny/TinyAttributeImpl.java b/sf/saxon/tree/tiny/TinyAttributeImpl.java
new file mode 100644
index 0000000..1121ea1
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyAttributeImpl.java
@@ -0,0 +1,289 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+
+
+/**
+ * A node in the XML parse tree representing an attribute. Note that this is
+ * generated only "on demand", when the attribute is selected by a select pattern.<P>
+ * @author Michael H. Kay
+ */
+
+final class TinyAttributeImpl extends TinyNodeImpl {
+
+ public TinyAttributeImpl(TinyTree tree, int nodeNr) {
+ this.tree = tree;
+ this.nodeNr = nodeNr;
+ }
+
+
+ public void setSystemId(String uri) {
+ // no action: an attribute has the same base URI as its parent
+ }
+
+ /**
+ * Get the parent node
+ */
+
+ public NodeInfo getParent() {
+ return tree.getNode(tree.attParent[nodeNr]);
+ }
+
+ /**
+ * Get the root node of the tree (not necessarily a document node)
+ *
+ * @return the NodeInfo representing the root of this tree
+ */
+
+ public NodeInfo getRoot() {
+ NodeInfo parent = getParent();
+ if (parent == null) {
+ return this; // doesn't happen - parentless attributes are represented by the Orphan class
+ } else {
+ return parent.getRoot();
+ }
+ }
+
+ /**
+ * Get the node sequence number (in document order). Sequence numbers are monotonic but not
+ * consecutive. In this implementation, elements have a zero
+ * least-significant word, while attributes and namespaces use the same value in the top word as
+ * the containing element, and use the bottom word to hold
+ * a sequence number, which numbers namespaces first and then attributes.
+ */
+
+ protected long getSequenceNumber() {
+ //noinspection ConstantConditions
+ return
+ ((TinyNodeImpl)getParent()).getSequenceNumber()
+ + 0x8000 +
+ (nodeNr - tree.alpha[tree.attParent[nodeNr]]);
+ // note the 0x8000 is to leave room for namespace nodes
+ }
+
+ /**
+ * Return the type of node.
+ * @return Node.ATTRIBUTE
+ */
+
+ public final int getNodeKind() {
+ return Type.ATTRIBUTE;
+ }
+
+ /**
+ * Return the string value of the node.
+ * @return the attribute value
+ */
+
+ public CharSequence getStringValueCS() {
+ return tree.attValue[nodeNr];
+ }
+
+ /**
+ * Return the string value of the node.
+ * @return the attribute value
+ */
+
+ public String getStringValue() {
+ return tree.attValue[nodeNr].toString();
+ }
+
+ /**
+ * Get the fingerprint of the node, used for matching names
+ */
+
+ public int getFingerprint() {
+ return tree.attCode[nodeNr] & 0xfffff;
+ }
+
+ /**
+ * Get the name code of the node, used for finding names in the name pool
+ */
+
+ public int getNameCode() {
+ return tree.attCode[nodeNr];
+ }
+
+ /**
+ * Get the prefix part of the name of this node. This is the name before the ":" if any.
+ * @return the prefix part of the name. For an unnamed node, return null.
+ */
+
+ public String getPrefix() {
+ int code = tree.attCode[nodeNr];
+ if (!NamePool.isPrefixed(code)) {
+ return "";
+ }
+ return tree.getNamePool().getPrefix(code);
+ }
+
+ /**
+ * Get the display name of this node. For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ * @return The display name of this node.
+ * For a node with no name, return an empty string.
+ */
+
+ public String getDisplayName() {
+ return tree.getNamePool().getDisplayName(tree.attCode[nodeNr]);
+ }
+
+
+ /**
+ * Get the local name of this node.
+ * @return The local name of this node.
+ * For a node with no name, return an empty string.
+ */
+
+ public String getLocalPart() {
+ return tree.getNamePool().getLocalName(tree.attCode[nodeNr]);
+ }
+
+ /**
+ * Get the URI part of the name of this node.
+ * @return The URI of the namespace of this node. For the default namespace, return an
+ * empty string
+ */
+
+ public final String getURI() {
+ return tree.getNamePool().getURI(tree.attCode[nodeNr]);
+ }
+
+ /**
+ * Get the type annotation of this node
+ * Returns UNTYPED_ATOMIC if there is no type annotation
+ */
+
+ public int getTypeAnnotation() {
+ return tree.getAttributeAnnotation(nodeNr);
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ public SchemaType getSchemaType() {
+ if (tree.attTypeCode == null) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ return tree.getConfiguration().getSchemaType(tree.getAttributeAnnotation(nodeNr));
+ }
+
+ /**
+ * Get the typed value.
+ * @return the typed value. This will either be a single AtomicValue or a Value whose items are
+ * atomic values.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize() throws XPathException {
+ return tree.getTypedValueOfAttribute(this, nodeNr);
+ }
+
+ /**
+ * Generate id. Returns key of owning element with the attribute namecode as a suffix
+ * @param buffer Buffer to contain the generated ID value
+ */
+
+ public void generateId(FastStringBuffer buffer) {
+ getParent().generateId(buffer);
+ buffer.append("a");
+ buffer.append(Integer.toString(tree.attCode[nodeNr]));
+ // we previously used the attribute name. But this breaks the requirement
+ // that the result of generate-id consists entirely of alphanumeric ASCII
+ // characters
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ SimpleType typeCode = (CopyOptions.includes(copyOptions, CopyOptions.TYPE_ANNOTATIONS) ?
+ (SimpleType)getSchemaType() : BuiltInAtomicType.UNTYPED_ATOMIC);
+ out.attribute(new NameOfNode(this), typeCode, getStringValue(), locationId, 0);
+ }
+
+ /**
+ * Get the line number of the node within its source document entity
+ */
+
+ public int getLineNumber() {
+ return getParent().getLineNumber();
+ }
+
+ /**
+ * Get the column number of the node within its source document entity
+ */
+
+ public int getColumnNumber() {
+ return getParent().getColumnNumber();
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return false;
+ }
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ return tree.isIdAttribute(nodeNr);
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return tree.isIdrefAttribute(nodeNr);
+ }
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ *
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode() {
+ return (((int)(tree.getDocumentNumber() & 0x3ff)) << 20) ^ nodeNr ^ 7<<17;
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyBuilder.java b/sf/saxon/tree/tiny/TinyBuilder.java
new file mode 100644
index 0000000..f2b4277
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyBuilder.java
@@ -0,0 +1,486 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.event.*;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+
+
+/**
+ * The TinyBuilder class is responsible for taking a stream of SAX events and constructing
+ * a Document tree, using the "TinyTree" implementation.
+ *
+ * @author Michael H. Kay
+ */
+
+public class TinyBuilder extends Builder {
+
+ public static final int PARENT_POINTER_INTERVAL = 10;
+ // a lower value allocates more parent pointers which takes more space but reduces
+ // the length of parent searches
+
+ /*@Nullable*/ private TinyTree tree;
+
+ private int currentDepth = 0;
+ private int nodeNr = 0; // this is the local sequence within this document
+ private boolean ended = false;
+ /*@Nullable*/ private int[] sizeParams; // estimate of number of nodes, attributes, namespaces, characters
+
+ /**
+ * Create a TinyTree builder
+ * @param pipe information about the pipeline leading up to this Builder
+ */
+
+ public TinyBuilder(/*@NotNull*/ PipelineConfiguration pipe) {
+ super(pipe);
+ //System.err.println("TinyBuilder " + this);
+ }
+
+ /**
+ * Set the size parameters for the tree
+ * @param params an array of four integers giving the expected number of non-attribute nodes, the expected
+ * number of attributes, the expected number of namespace declarations, and the expected total length of
+ * character data
+ */
+
+ public void setSizeParameters(/*@Nullable*/ int[] params) {
+ sizeParams = params;
+ }
+
+ /**
+ * Get the size parameters for the tree
+ * @return an array of four integers giving the actual number of non-attribute nodes, the actual
+ * number of attributes, the actual number of namespace declarations, and the actual total length of
+ * character data. Return null if and only if the current tree is null.
+ */
+
+ /*@Nullable*/ public int[] getSizeParameters() {
+ if (tree != null) {
+ return new int[] {tree.numberOfNodes, tree.numberOfAttributes, tree.numberOfNamespaces,
+ tree.getCharacterBuffer().length()};
+ } else {
+ return null;
+ }
+ }
+
+ /*@NotNull*/ private int[] prevAtDepth = new int[100];
+ // this array is scaffolding used while constructing the tree, it is
+ // not present in the final tree. For each level of the tree, it records the
+ // node number of the most recent node at that level.
+
+ /*@NotNull*/ private int[] siblingsAtDepth = new int[100];
+ // more scaffolding. For each level of the tree, this array records the
+ // number of siblings processed at that level. When this exceeds a threshold value,
+ // a dummy node is inserted into the arrays to contain a parent pointer: this it to
+ // prevent excessively long searches for a parent node, which is normally found by
+ // scanning the siblings. The value is then reset to zero.
+
+ private boolean isIDElement = false;
+
+ /**
+ * Get the tree being built by this builder
+ * @return the TinyTree
+ */
+
+ /*@Nullable*/ public TinyTree getTree() {
+ return tree;
+ }
+
+ /**
+ * Get the current depth in the tree
+ * @return the current depth
+ */
+
+ public int getCurrentDepth() {
+ return currentDepth;
+ }
+
+ /**
+ * Open the event stream
+ */
+
+ public void open() {
+ //System.err.println("TinyBuilder " + this + " open; " + started);
+ if (started) {
+ // this happens when using an IdentityTransformer
+ return;
+ }
+ if (tree == null) {
+ if (sizeParams ==null) {
+ tree = new TinyTree(config);
+ } else {
+ tree = new TinyTree(config,
+ sizeParams[0], sizeParams[1], sizeParams[2], sizeParams[3]);
+ }
+ currentDepth = 0;
+ if (lineNumbering) {
+ tree.setLineNumbering();
+ }
+ }
+ super.open();
+ }
+
+ /**
+ * Write a document node to the tree
+ */
+
+ public void startDocument (int properties) throws XPathException {
+// if (currentDepth == 0 && tree.numberOfNodes != 0) {
+// System.err.println("**** FOREST DOCUMENT **** " + tree.numberOfNodes);
+// }
+// System.err.println("New doc: free memory: " + Runtime.getRuntime().freeMemory());
+// if (sizeParams != null) {
+// System.err.println("Size params: " + sizeParams[0] + ", " + sizeParams[1] + ", " + sizeParams[2] + ", " + sizeParams[3]);
+// }
+ if ((started && !ended) || currentDepth > 0) {
+ // this happens when using an IdentityTransformer, or when copying a document node to form
+ // the content of an element
+ return;
+ }
+ started = true;
+ ended = false;
+
+ TinyTree tt = tree;
+ assert tt != null;
+ currentRoot = new TinyDocumentImpl(tt);
+ TinyDocumentImpl doc = (TinyDocumentImpl)currentRoot;
+ doc.setSystemId(getSystemId());
+ doc.setBaseURI(getBaseURI());
+
+ currentDepth = 0;
+
+ int nodeNr = tt.addDocumentNode((TinyDocumentImpl)currentRoot);
+ prevAtDepth[0] = nodeNr;
+ prevAtDepth[1] = -1;
+ siblingsAtDepth[0] = 0;
+ siblingsAtDepth[1] = 0;
+ tt.next[nodeNr] = -1;
+
+ currentDepth++;
+ }
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void endDocument () throws XPathException {
+// System.err.println("TinyBuilder: " + this + " End document");
+
+ // Add a stopper node to ensure no-one walks off the end of the array; but
+ // decrement numberOfNodes so the next node will overwrite it
+ tree.addNode(Type.STOPPER, 0, 0, 0, -1);
+ tree.numberOfNodes--;
+
+ if (currentDepth > 1) return;
+ // happens when copying a document node as the child of an element
+
+ if (ended) return; // happens when using an IdentityTransformer
+ ended = true;
+
+ prevAtDepth[currentDepth] = -1;
+ currentDepth--;
+
+ }
+
+ public void reset() {
+ super.reset();
+ tree = null;
+ currentDepth = 0;
+ nodeNr = 0;
+ ended = false;
+ sizeParams = null;
+ }
+
+ public void close() throws XPathException {
+ TinyTree tt = tree;
+ if (tt != null) {
+ tt.addNode(Type.STOPPER, 0, 0, 0, -1);
+ tt.condense();
+ }
+ super.close();
+ }
+
+ /**
+ * Notify the start tag of an element
+ */
+
+ public void startElement (/*@NotNull*/ NodeName elemName, SchemaType type, int locationId, int properties) throws XPathException
+ {
+// if (currentDepth == 0 && tree.numberOfNodes != 0) {
+// System.err.println("**** FOREST ELEMENT **** trees=" + tree.rootIndexUsed );
+// }
+
+ // if the number of siblings exceeds a certain threshold, add a parent pointer, in the form
+ // of a pseudo-node
+
+ TinyTree tt = tree;
+ assert tt != null;
+
+ if (siblingsAtDepth[currentDepth] > PARENT_POINTER_INTERVAL) {
+ nodeNr = tt.addNode(Type.PARENT_POINTER, currentDepth, prevAtDepth[currentDepth-1], 0, 0);
+ int prev = prevAtDepth[currentDepth];
+ if (prev > 0) {
+ tt.next[prev] = nodeNr;
+ }
+ tt.next[nodeNr] = prevAtDepth[currentDepth-1];
+ prevAtDepth[currentDepth] = nodeNr;
+ siblingsAtDepth[currentDepth] = 0;
+ }
+
+ // now add the element node itself
+ int nameCode = elemName.allocateNameCode(namePool);
+ nodeNr = tt.addNode(Type.ELEMENT, currentDepth, -1, -1, nameCode);
+
+ isIDElement = ((properties & ReceiverOptions.IS_ID) != 0);
+ int typeCode = type.getFingerprint();
+ if (typeCode != StandardNames.XS_UNTYPED) {
+ if ((properties & ReceiverOptions.NILLED_ELEMENT) != 0) {
+ typeCode |= NodeInfo.IS_NILLED;
+ }
+ tt.setElementAnnotation(nodeNr, typeCode);
+ if (!isIDElement && config.getTypeHierarchy().isIdCode(typeCode)) {
+ isIDElement = true;
+ }
+ }
+
+ if (currentDepth == 0) {
+ prevAtDepth[0] = nodeNr;
+ prevAtDepth[1] = -1;
+ //tree.next[0] = -1;
+ currentRoot = tt.getNode(nodeNr);
+ } else {
+ int prev = prevAtDepth[currentDepth];
+ if (prev > 0) {
+ tt.next[prev] = nodeNr;
+ }
+ tt.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
+ prevAtDepth[currentDepth] = nodeNr;
+ siblingsAtDepth[currentDepth]++;
+ }
+ currentDepth++;
+
+ if (currentDepth == prevAtDepth.length) {
+ int[] p2 = new int[currentDepth*2];
+ System.arraycopy(prevAtDepth, 0, p2, 0, currentDepth);
+ prevAtDepth = p2;
+ p2 = new int[currentDepth*2];
+ System.arraycopy(siblingsAtDepth, 0, p2, 0, currentDepth);
+ siblingsAtDepth = p2;
+ }
+ prevAtDepth[currentDepth] = -1;
+ siblingsAtDepth[currentDepth] = 0;
+
+ LocationProvider locator = pipe.getLocationProvider();
+ if (locator instanceof SourceLocationProvider) {
+ tt.setSystemId(nodeNr, locator.getSystemId(locationId));
+ } else if (currentDepth == 1) {
+ tt.setSystemId(nodeNr, systemId);
+ }
+ if (lineNumbering) {
+ tt.setLineNumber(nodeNr, locator.getLineNumber(locationId), locator.getColumnNumber(locationId));
+ }
+ }
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ assert tree != null;
+ tree.addNamespace(nodeNr, namespaceBinding);
+ }
+
+ public void attribute(/*@NotNull*/ NodeName attName, SimpleType typeCode, CharSequence value, int locationId, int properties)
+ throws XPathException {
+ // System.err.println("attribute " + nameCode + "=" + value);
+ int nameCode = attName.allocateNameCode(namePool);
+ assert tree != null;
+ assert currentRoot != null;
+ tree.addAttribute(currentRoot, nodeNr, nameCode, typeCode.getFingerprint(), value, properties);
+ }
+
+ public void startContent() {
+ nodeNr++;
+ }
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void endElement() throws XPathException {
+ TinyTree tt = tree;
+ assert tt != null;
+// System.err.println("End element");
+ prevAtDepth[currentDepth] = -1;
+ siblingsAtDepth[currentDepth] = 0;
+ currentDepth--;
+ if (isIDElement) {
+ // we're relying on the fact that an ID element has no element children!
+ tt.indexIDElement(currentRoot, prevAtDepth[currentDepth], config.getNameChecker());
+ isIDElement = false;
+ }
+ }
+
+ /**
+ * Get the last completed element node. This is used during checking of schema assertions,
+ * which happens while the tree is still under construction. This method is called immediately after
+ * a call on endElement(), and it returns the element that has just ended.
+ * @return the last completed element node, that is, the element whose endElement event is the most recent
+ * endElement event to be reported, or null if there is no such element
+ */
+
+ public NodeInfo getLastCompletedElement() {
+ if (tree == null) {
+ return null;
+ }
+ return tree.getNode((currentDepth>=0 ? prevAtDepth[currentDepth] : 0));
+ // Note: reading an incomplete tree needs care if it constructs a prior index, etc.
+ }
+
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void characters(/*@NotNull*/ CharSequence chars, int locationId, int properties) throws XPathException
+ {
+ if (chars instanceof CompressedWhitespace &&
+ (properties & ReceiverOptions.WHOLE_TEXT_NODE) != 0) {
+ TinyTree tt = tree;
+ assert tt != null;
+ long lvalue = ((CompressedWhitespace)chars).getCompressedValue();
+ nodeNr = tt.addNode(Type.WHITESPACE_TEXT, currentDepth, (int)(lvalue>>32), (int)(lvalue), -1);
+
+ int prev = prevAtDepth[currentDepth];
+ if (prev > 0) {
+ tt.next[prev] = nodeNr;
+ }
+ tt.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
+ prevAtDepth[currentDepth] = nodeNr;
+ siblingsAtDepth[currentDepth]++;
+ return;
+ }
+
+ final int len = chars.length();
+ if (len>0) {
+ nodeNr = makeTextNode(chars, len);
+ }
+ }
+
+ /**
+ * Create a text node. Separate method so it can be overridden. If the current node
+ * on the tree is already a text node, the new text will be appended to it.
+ * @param chars the contents of the text node
+ * @param len the length of the text node
+ * @return the node number of the created text node, or the text node to which
+ * this text has been appended.
+ */
+
+ protected int makeTextNode(CharSequence chars, int len) {
+ TinyTree tt = tree;
+ assert tt != null;
+ int bufferStart = tt.getCharacterBuffer().length();
+ tt.appendChars(chars);
+ int n=tt.numberOfNodes-1;
+ if (tt.nodeKind[n] == Type.TEXT && tt.depth[n] == currentDepth) {
+ // merge this text node with the previous text node
+ tt.beta[n] += len;
+ } else {
+ nodeNr = tt.addNode(Type.TEXT, currentDepth, bufferStart, len, -1);
+
+ int prev = prevAtDepth[currentDepth];
+ if (prev > 0) {
+ tt.next[prev] = nodeNr;
+ }
+ tt.next[nodeNr] = prevAtDepth[currentDepth - 1];
+ prevAtDepth[currentDepth] = nodeNr;
+ siblingsAtDepth[currentDepth]++;
+ }
+ return nodeNr;
+ }
+
+ /**
+ * Callback interface for SAX: not for application use<BR>
+ */
+
+ public void processingInstruction (String piname, /*@NotNull*/ CharSequence remainder, int locationId, int properties) throws XPathException
+ {
+ TinyTree tt = tree;
+ assert tt != null;
+
+ if (tt.commentBuffer==null) {
+ tt.commentBuffer = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ }
+ int s = tt.commentBuffer.length();
+ tt.commentBuffer.append(remainder.toString());
+ int nameCode = namePool.allocate("", "", piname);
+
+ nodeNr = tt.addNode(Type.PROCESSING_INSTRUCTION, currentDepth, s, remainder.length(),
+ nameCode);
+
+ int prev = prevAtDepth[currentDepth];
+ if (prev > 0) {
+ tt.next[prev] = nodeNr;
+ }
+ tt.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
+ prevAtDepth[currentDepth] = nodeNr;
+ siblingsAtDepth[currentDepth]++;
+
+ LocationProvider locator = pipe.getLocationProvider();
+ if (locator instanceof SourceLocationProvider) {
+ tt.setSystemId(nodeNr, locator.getSystemId(locationId));
+ if (lineNumbering) {
+ tt.setLineNumber(nodeNr, locator.getLineNumber(locationId), locator.getColumnNumber(locationId));
+ }
+ }
+ }
+
+ /**
+ * Callback interface for SAX: not for application use
+ */
+
+ public void comment (/*@NotNull*/ CharSequence chars, int locationId, int properties) throws XPathException {
+
+ TinyTree tt = tree;
+ assert tt != null;
+
+ if (tt.commentBuffer==null) {
+ tt.commentBuffer = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ }
+ int s = tt.commentBuffer.length();
+ tt.commentBuffer.append(chars.toString());
+ nodeNr = tt.addNode(Type.COMMENT, currentDepth, s, chars.length(), -1);
+
+ int prev = prevAtDepth[currentDepth];
+ if (prev > 0) {
+ tt.next[prev] = nodeNr;
+ }
+ tt.next[nodeNr] = prevAtDepth[currentDepth - 1]; // *O* owner pointer in last sibling
+ prevAtDepth[currentDepth] = nodeNr;
+ siblingsAtDepth[currentDepth]++;
+
+ }
+
+ /**
+ * Set an unparsed entity in the document
+ */
+
+ public void setUnparsedEntity(String name, String uri, String publicId) {
+ assert currentRoot != null;
+ ((TinyDocumentImpl)currentRoot).setUnparsedEntity(name, uri, publicId);
+ }
+
+ /*@NotNull*/ public BuilderMonitor getBuilderMonitor() {
+ return new TinyBuilderMonitor(this);
+ }
+}
+
diff --git a/sf/saxon/tree/tiny/TinyBuilderCondensed.java b/sf/saxon/tree/tiny/TinyBuilderCondensed.java
new file mode 100644
index 0000000..1163302
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyBuilderCondensed.java
@@ -0,0 +1,105 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.om.NodeName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+
+/**
+ * Variant of the TinyBuilder to create a tiny tree in which multiple text nodes or attribute
+ * nodes sharing the same string value economize on space by only holding the value once.
+ */
+public class TinyBuilderCondensed extends TinyBuilder {
+
+ public TinyBuilderCondensed(PipelineConfiguration pipe) {
+ super(pipe);
+ }
+
+ // Keep a map from the hashcode of the string (calculated within this class) to the list
+ // of node numbers of text nodes having that hashcode
+
+ // Note from the specification of CharSequence:
+ // "It is therefore inappropriate to use arbitrary <tt>CharSequence</tt> instances as elements in a set or as keys in
+ // a map." We therefore take special care over the design of the map.
+ // We rely on the fact that all Saxon implementations of CharSequence have a hashCode() that is compatible with String.
+ // And we don't use equals() unless both CharSequences are of the same kind.
+
+ public IntHashMap<int[]> textValues = new IntHashMap<int[]>(100);
+
+
+ public void endElement() throws XPathException {
+ // When ending an element, consider whether the just-completed text node can be commoned-up with
+ // any other text nodes. (Don't bother if its more than 256 chars, as it's then likely to be unique)
+ // We do this at endElement() time because we need to make sure that adjacent text nodes are concatenated first.
+ TinyTree tree = getTree();
+ int n = tree.numberOfNodes-1;
+ if (tree.nodeKind[n] == Type.TEXT && tree.depth[n] == getCurrentDepth() && (tree.beta[n] - tree.alpha[n] <= 256)) {
+ CharSequence chars = TinyTextImpl.getStringValue(tree, n);
+ int hash = chars.hashCode();
+ int[] nodes = textValues.get(hash);
+ if (nodes != null) {
+ int used = nodes[0];
+ for (int i=1; i<used; i++) {
+ int nodeNr = nodes[i];
+ if (nodeNr == 0) {
+ break;
+ } else if (isEqual(chars, TinyTextImpl.getStringValue(tree, nodeNr))) {
+ // the latest text node is equal to some previous text node
+ int length = tree.alpha[n];
+ tree.alpha[n] = tree.alpha[nodeNr];
+ tree.beta[n] = tree.beta[nodeNr];
+ tree.getCharacterBuffer().setLength(length);
+ break;
+ }
+ }
+ } else {
+ nodes = new int[4];
+ nodes[0] = 1;
+ textValues.put(hash, nodes);
+ }
+ if (nodes[0] + 1 > nodes.length) {
+ int[] n2 = new int[nodes.length*2];
+ System.arraycopy(nodes, 0, n2, 0, nodes[0]);
+ textValues.put(hash, n2);
+ nodes = n2;
+ }
+ nodes[nodes[0]++] = n;
+ }
+ super.endElement();
+ }
+
+ /**
+ * For attribute nodes, the commoning-up of stored values is achieved simply by calling intern() on the
+ * string value of the attribute.
+ */
+
+ public void attribute(/*@NotNull*/ NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ super.attribute(nameCode, typeCode, value.toString().intern(), locationId, properties);
+ }
+
+ /**
+ * Test whether two CharSequences contain the same characters
+ * @param a the first CharSequence
+ * @param b the second CharSequence
+ * @return true if a and b contain the same characters
+ */
+
+ private static boolean isEqual(CharSequence a, /*@NotNull*/ CharSequence b) {
+ if (a.getClass() == b.getClass()) {
+ return a.equals(b);
+ } else {
+ return a.toString().equals(b.toString());
+ }
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyBuilderMonitor.java b/sf/saxon/tree/tiny/TinyBuilderMonitor.java
new file mode 100644
index 0000000..071f33a
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyBuilderMonitor.java
@@ -0,0 +1,115 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.event.BuilderMonitor;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.SimpleType;
+import net.sf.saxon.type.Type;
+
+/**
+ * Monitor construction of a TinyTree. This allows a marker to be set during tree construction, in such a way
+ * that the node corresponding to the marker can be retrieved at the end of tree construction. This is used in the
+ * implementation of the XSLT 3.0 snapshot function.
+ */
+public class TinyBuilderMonitor extends BuilderMonitor {
+
+ private TinyBuilder builder;
+ private int mark = -1;
+ private int markedNodeNr = -1;
+ private int markedAttribute = -1;
+ private int markedNamespace = -1;
+
+ public TinyBuilderMonitor(/*@NotNull*/ TinyBuilder builder) {
+ super(builder);
+ this.builder = builder;
+ }
+
+ public void markNextNode(int nodeKind) {
+ mark = nodeKind;
+ }
+
+ public void startDocument(int properties) throws XPathException {
+ if (mark == Type.DOCUMENT) {
+ markedNodeNr = builder.getTree().getNumberOfNodes();
+ }
+ mark = -1;
+ super.startDocument(properties);
+ }
+
+ public void startElement(NodeName nameCode, SchemaType typeCode, int locationId, int properties) throws XPathException {
+ if (mark == Type.ELEMENT) {
+ markedNodeNr = builder.getTree().getNumberOfNodes();
+ }
+ mark = -1;
+ super.startElement(nameCode, typeCode, locationId, properties);
+ }
+
+ public void characters(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (mark == Type.TEXT) {
+ markedNodeNr = builder.getTree().getNumberOfNodes();
+ }
+ mark = -1;
+ super.characters(chars, locationId, properties);
+ }
+
+ public void comment(CharSequence chars, int locationId, int properties) throws XPathException {
+ if (mark == Type.COMMENT) {
+ markedNodeNr = builder.getTree().getNumberOfNodes();
+ }
+ mark = -1;
+ super.comment(chars, locationId, properties);
+ }
+
+ public void processingInstruction(String target, CharSequence data, int locationId, int properties) throws XPathException {
+ if (mark == Type.PROCESSING_INSTRUCTION) {
+ markedNodeNr = builder.getTree().getNumberOfNodes();
+ }
+ mark = -1;
+ super.processingInstruction(target, data, locationId, properties);
+ }
+
+ public void attribute(NodeName nameCode, SimpleType typeCode, CharSequence value, int locationId, int properties) throws XPathException {
+ if (mark == Type.ATTRIBUTE) {
+ markedAttribute = builder.getTree().getNumberOfAttributes();
+ }
+ mark = -1;
+ super.attribute(nameCode, typeCode, value, locationId, properties);
+ }
+
+ public void namespace(NamespaceBinding namespaceBinding, int properties) throws XPathException {
+ if (mark == Type.NAMESPACE) {
+ markedNamespace = builder.getTree().getNumberOfNamespaces();
+ }
+ mark = -1;
+ super.namespace(namespaceBinding, properties);
+ }
+
+ /*@Nullable*/ public NodeInfo getMarkedNode() {
+ if (markedNodeNr != -1) {
+ return builder.getTree().getNode(markedNodeNr);
+ } else if (markedAttribute != -1) {
+ return builder.getTree().getAttributeNode(markedNodeNr);
+ } else if (markedNamespace != -1) {
+ NamespaceBinding nscode = builder.getTree().namespaceBinding[markedNamespace];
+ NamePool pool = builder.getConfiguration().getNamePool();
+ String prefix = nscode.getPrefix();
+ NodeInfo parent = builder.getTree().getNode(builder.getTree().namespaceParent[markedNamespace]);
+ NameTest test = new NameTest(Type.NAMESPACE, "", prefix, pool);
+ AxisIterator iter = parent.iterateAxis(AxisInfo.NAMESPACE, test);
+ return (NodeInfo)iter.next();
+ } else {
+ return null;
+ }
+ }
+}
+
diff --git a/sf/saxon/tree/tiny/TinyCommentImpl.java b/sf/saxon/tree/tiny/TinyCommentImpl.java
new file mode 100644
index 0000000..d66789d
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyCommentImpl.java
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.StringValue;
+
+
+/**
+ * TinyCommentImpl is an implementation of CommentInfo
+ * @author Michael H. Kay
+ */
+
+
+final class TinyCommentImpl extends TinyNodeImpl {
+
+ public TinyCommentImpl(TinyTree tree, int nodeNr) {
+ this.tree = tree;
+ this.nodeNr = nodeNr;
+ }
+
+ /**
+ * Get the XPath string value of the comment
+ */
+
+ public final String getStringValue() {
+ int start = tree.alpha[nodeNr];
+ int len = tree.beta[nodeNr];
+ if (len==0) return "";
+ char[] dest = new char[len];
+ tree.commentBuffer.getChars(start, start+len, dest, 0);
+ return new String(dest, 0, len);
+ }
+
+ /**
+ * Get the typed value of this node.
+ * Returns the string value, as an instance of xs:string
+ */
+
+ public AtomicSequence atomize() {
+ return new StringValue(getStringValue());
+ }
+
+
+ /**
+ * Get the node type
+ * @return Type.COMMENT
+ */
+
+ public final int getNodeKind() {
+ return Type.COMMENT;
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ out.comment(getStringValue(), 0, 0);
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyDocumentImpl.java b/sf/saxon/tree/tiny/TinyDocumentImpl.java
new file mode 100644
index 0000000..e01f5a2
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyDocumentImpl.java
@@ -0,0 +1,447 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.AxisIteratorOverSequence;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AnyType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+import java.util.*;
+
+
+/**
+ * A node in the XML parse tree representing the Document itself (or equivalently, the root
+ * node of the Document).<P>
+ */
+
+public final class TinyDocumentImpl extends TinyParentNodeImpl
+ implements DocumentInfo {
+
+ private HashMap<String, NodeInfo> idTable;
+ private IntHashMap<List<TinyElementImpl>> elementList;
+ private HashMap<String, String[]> entityTable;
+ private HashMap<String, Object> userData;
+ private String baseURI;
+
+
+
+ public TinyDocumentImpl(/*@NotNull*/ TinyTree tree) {
+ this.tree = tree;
+ nodeNr = tree.numberOfNodes;
+ }
+
+ /**
+ * Get the tree containing this node
+ */
+
+ public TinyTree getTree() {
+ return tree;
+ }
+
+ /**
+ * Get the configuration previously set using setConfiguration
+ */
+
+ public Configuration getConfiguration() {
+ return tree.getConfiguration();
+ }
+
+ /**
+ * Set the system id of this node
+ */
+
+ public void setSystemId(String uri) {
+ tree.setSystemId(nodeNr, uri);
+ }
+
+ /**
+ * Get the system id of this root node
+ */
+
+ public String getSystemId() {
+ return tree.getSystemId(nodeNr);
+ }
+
+ /**
+ * Set the base URI of this document node
+ * @param uri the base URI
+ */
+
+ public void setBaseURI(String uri) {
+ baseURI = uri;
+ }
+
+ /**
+ * Get the base URI of this root node.
+ */
+
+ public String getBaseURI() {
+ if (baseURI != null) {
+ return baseURI;
+ }
+ return getSystemId();
+ }
+
+ /**
+ * Get the line number of this root node.
+ * @return 0 always
+ */
+
+ public int getLineNumber() {
+ return 0;
+ }
+
+ /**
+ * Ask whether the document contains any nodes whose type annotation is anything other than
+ * UNTYPED
+ *
+ * @return true if the document contains elements whose type is other than UNTYPED
+ */
+ public boolean isTyped() {
+ return tree.getTypeCodeArray() != null;
+ }
+
+ /**
+ * Return the type of node.
+ * @return Type.DOCUMENT (always)
+ */
+
+ public final int getNodeKind() {
+ return Type.DOCUMENT;
+ }
+
+ /**
+ * Find the parent node of this node.
+ * @return The Node object describing the containing element or root node.
+ */
+
+ /*@Nullable*/ public NodeInfo getParent() {
+ return null;
+ }
+
+ /**
+ * Get the root node
+ * @return the NodeInfo that is the root of the tree - not necessarily a document node
+ */
+
+ /*@NotNull*/ public NodeInfo getRoot() {
+ return this;
+ }
+
+ /**
+ * Get the root (document) node
+ * @return the DocumentInfo representing the document node, or null if the
+ * root of the tree is not a document node
+ */
+
+ /*@NotNull*/ public DocumentInfo getDocumentRoot() {
+ return this;
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node
+ * @param buffer to contain an identifier based on the document number
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ buffer.append('d');
+ buffer.append(Long.toString(getDocumentNumber()));
+ }
+
+ /**
+ * Get the typed value.
+ * @return the typed value. This will either be a single AtomicValue or a Value whose items are
+ * atomic values.
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize() throws XPathException {
+ return new UntypedAtomicValue(getStringValueCS());
+ }
+
+ /**
+ * Get a list of all elements with a given name. This is implemented
+ * as a memo function: the first time it is called for a particular
+ * element type, it remembers the result for next time.
+ * @param fingerprint the fingerprint identifying the required element name
+ * @return an iterator over all elements with this name
+ */
+
+ /*@NotNull*/ AxisIterator getAllElements(int fingerprint) {
+ if (elementList==null) {
+ elementList = new IntHashMap<List<TinyElementImpl>>(20);
+ }
+ List<TinyElementImpl> list = elementList.get(fingerprint);
+ if (list==null) {
+ list = getElementList(fingerprint);
+ elementList.put(fingerprint, list);
+ }
+ return new AxisIteratorOverSequence<TinyElementImpl>(
+ new net.sf.saxon.tree.iter.ListIterator<TinyElementImpl>(list));
+ }
+
+ /**
+ * Get a list containing all the elements with a given element name
+ * @param fingerprint the fingerprint of the element name
+ * @return list a List containing the TinyElementImpl objects
+ */
+
+ /*@NotNull*/ List<TinyElementImpl> getElementList(int fingerprint) {
+ int size = tree.getNumberOfNodes()/20;
+ if (size > 100) {
+ size = 100;
+ }
+ if (size < 20) {
+ size = 20;
+ }
+ ArrayList<TinyElementImpl> list = new ArrayList<TinyElementImpl>(size);
+ int i = nodeNr+1;
+ try {
+ while (tree.depth[i] != 0) {
+ if (tree.nodeKind[i]==Type.ELEMENT &&
+ (tree.nameCode[i] & 0xfffff) == fingerprint) {
+ list.add((TinyElementImpl)tree.getNode(i));
+ }
+ i++;
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ // this shouldn't happen. If it does happen, it means the tree wasn't properly closed
+ // during construction (there is no stopper node at the end). In this case, we'll recover
+ return list;
+ }
+ list.trimToSize();
+ return list;
+ }
+
+ /**
+ * Register a unique element ID. Fails if there is already an element with that ID.
+ * @param e The NodeInfo (always an element) having a particular unique ID value
+ * @param id The unique ID value. The caller is responsible for checking that this
+ * is a valid NCName.
+ */
+
+ void registerID(NodeInfo e, String id) {
+ if (idTable==null) {
+ idTable = new HashMap<String, NodeInfo>(256);
+ }
+
+ // the XPath spec (5.2.1) says ignore the second ID if it's not unique
+ NodeInfo old = idTable.get(id);
+ if (old==null) {
+ idTable.put(id, e);
+ }
+
+ }
+
+ /**
+ * Get the element with a given ID.
+ * @param id The unique ID of the required element, previously registered using registerID()
+ * @param getParent true if the required element is the parent of the element of type ID
+ * @return The NodeInfo (always an Element) for the given ID if one has been registered,
+ * otherwise null.
+ */
+
+ /*@Nullable*/ public NodeInfo selectID(String id, boolean getParent) {
+ if (idTable==null) {
+ return null; // no ID values found
+ }
+ NodeInfo node = idTable.get(id);
+ if (node != null && getParent && node.isId() && node.getStringValue().equals(id)) {
+ node = node.getParent();
+ }
+ return node;
+ }
+
+ /**
+ * Set an unparsed entity URI associated with this document. For system use only, while
+ * building the document.
+ * @param name the name of the unparsed entity
+ * @param uri the system identifier of the unparsed entity
+ * @param publicId the public identifier of the unparsed entity
+ */
+
+ void setUnparsedEntity(String name, String uri, String publicId) {
+ if (entityTable==null) {
+ entityTable = new HashMap<String, String[]>(20);
+ }
+ String[] ids = new String[2];
+ ids[0] = uri;
+ ids[1] = publicId;
+ entityTable.put(name, ids);
+ }
+
+ /**
+ * Get the list of unparsed entities defined in this document
+ * @return an Iterator, whose items are of type String, containing the names of all
+ * unparsed entities defined in this document. If there are no unparsed entities or if the
+ * information is not available then an empty iterator is returned
+ */
+
+ public Iterator<String> getUnparsedEntityNames() {
+ if (entityTable == null) {
+ List<String> emptyList = Collections.emptyList();
+ return emptyList.iterator();
+ } else {
+ return entityTable.keySet().iterator();
+ }
+ }
+
+ /**
+ * Get the unparsed entity with a given nameID if there is one, or null if not. If the entity
+ * does not exist, return null.
+ * @param name the name of the entity
+ * @return if the entity exists, return an array of two Strings, the first holding the system ID
+ * of the entity, the second holding the public
+ */
+
+ /*@Nullable*/ public String[] getUnparsedEntity(String name) {
+ if (entityTable==null) {
+ return null;
+ }
+ return entityTable.get(name);
+ }
+
+ /**
+ * Get the type annotation of this node, if any.
+ * @return XS_UNTYPED if no validation has been done, XS_ANY_TYPE if the document has been validated
+ */
+
+ public int getTypeAnnotation() {
+ AxisIterator children = iterateAxis(AxisInfo.CHILD, NodeKindTest.ELEMENT);
+ NodeInfo node = children.next();
+ if (node == null || node.getTypeAnnotation() == StandardNames.XS_UNTYPED) {
+ return StandardNames.XS_UNTYPED;
+ } else {
+ return StandardNames.XS_ANY_TYPE;
+ }
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ public SchemaType getSchemaType() {
+ AxisIterator children = iterateAxis(AxisInfo.CHILD, NodeKindTest.ELEMENT);
+ NodeInfo node = children.next();
+ if (node == null || node.getSchemaType() == Untyped.getInstance()) {
+ return Untyped.getInstance();
+ } else {
+ return AnyType.getInstance();
+ }
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+
+ out.startDocument(CopyOptions.getStartDocumentProperties(copyOptions));
+
+ // copy any unparsed entities
+
+ if (entityTable != null) {
+ for (Map.Entry<String, String[]> entry : entityTable.entrySet()) {
+ String name = entry.getKey();
+ String[] details = entry.getValue();
+ String systemId = details[0];
+ String publicId = details[1];
+ out.setUnparsedEntity(name, systemId, publicId);
+ }
+ }
+
+ // output the children
+
+ AxisIterator children = iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo n = children.next();
+ if (n == null) {
+ break;
+ }
+ n.copy(out, copyOptions, locationId);
+ }
+
+ out.endDocument();
+ }
+
+ public void showSize() {
+ tree.showSize();
+ }
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ *
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode() {
+ // Chosen to give a hashcode that is likely (a) to be distinct from other documents, and (b) to
+ // be distinct from other nodes in the same document
+ return (int)tree.getDocumentNumber();
+ }
+
+ /**
+ * Set user data on the document node. The user data can be retrieved subsequently
+ * using {@link #getUserData}
+ * @param key A string giving the name of the property to be set. Clients are responsible
+ * for choosing a key that is likely to be unique. Must not be null. Keys used internally
+ * by Saxon are prefixed "saxon:".
+ * @param value The value to be set for the property. May be null, which effectively
+ * removes the existing value for the property.
+ */
+
+ public void setUserData(String key, /*@Nullable*/ Object value) {
+ if (userData == null) {
+ userData = new HashMap<String, Object>(4);
+ }
+ if (value == null) {
+ userData.remove(key);
+ } else {
+ userData.put(key, value);
+ }
+ }
+
+ /**
+ * Get user data held in the document node. This retrieves properties previously set using
+ * {@link #setUserData}
+ * @param key A string giving the name of the property to be retrieved.
+ * @return the value of the property, or null if the property has not been defined.
+ */
+
+ /*@Nullable*/ public Object getUserData(String key) {
+ if (userData == null) {
+ return null;
+ } else {
+ return userData.get(key);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyElementImpl.java b/sf/saxon/tree/tiny/TinyElementImpl.java
new file mode 100644
index 0000000..957d6a9
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyElementImpl.java
@@ -0,0 +1,547 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.CopyInformee;
+import net.sf.saxon.event.CopyNamespaceSensitiveException;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.NamespaceIterator;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.AtomicValue;
+
+
+/**
+ * A node in the XML parse tree representing an XML element.<P>
+ * This class is an implementation of NodeInfo. The object is a wrapper around
+ * one entry in the arrays maintained by the TinyTree. Note that the same node
+ * might be represented by different TinyElementImpl objects at different times.
+ * @author Michael H. Kay
+ */
+
+public final class TinyElementImpl extends TinyParentNodeImpl {
+
+ /**
+ * Constructor - create a tiny element node
+ * @param tree the Tinytree containing the node
+ * @param nodeNr the node number
+ */
+
+ public TinyElementImpl(TinyTree tree, int nodeNr) {
+ this.tree = tree;
+ this.nodeNr = nodeNr;
+ }
+
+ /**
+ * Return the type of node.
+ * @return Type.ELEMENT
+ */
+
+ public final int getNodeKind() {
+ return Type.ELEMENT;
+ }
+
+ /**
+ * Get the base URI of this element node. This will be the same as the System ID unless
+ * xml:base has been used.
+ */
+
+ public String getBaseURI() {
+ return Navigator.getBaseURI(this);
+ }
+
+ /**
+ * Get the type annotation of this node, if any
+ * Returns Type.UNTYPED_ANY if there is no type annotation
+ */
+
+ public int getTypeAnnotation() {
+ return tree.getTypeAnnotation(nodeNr);
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ @Override
+ public SchemaType getSchemaType() {
+ if (tree.typeCodeArray == null) {
+ return Untyped.getInstance();
+ }
+ return getConfiguration().getSchemaType(getTypeAnnotation());
+ }
+
+ /**
+ * Get the typed value.
+ *
+ * @return the typed value. It will be a Value representing a sequence whose items are atomic
+ * values.
+ */
+
+ public AtomicSequence atomize() throws XPathException {
+ return tree.getTypedValueOfElement(this);
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ /*@Nullable*/ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return getDeclaredNamespaces(tree, nodeNr, buffer);
+ }
+
+ /**
+ * Static method to get all namespace undeclarations and undeclarations defined on a given element,
+ * without instantiating the node object.
+ * @param tree The tree containing the given element node
+ * @param nodeNr The node number of the given element node within the tinyTree
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ /*@Nullable*/ public static NamespaceBinding[] getDeclaredNamespaces(/*@NotNull*/ TinyTree tree, int nodeNr, /*@Nullable*/ NamespaceBinding[] buffer) {
+ int ns = tree.beta[nodeNr]; // by convention
+ if (ns>0 ) {
+ int count = 0;
+ while (ns < tree.numberOfNamespaces &&
+ tree.namespaceParent[ns] == nodeNr ) {
+ count++;
+ ns++;
+ }
+ if (count == 0) {
+ return NamespaceBinding.EMPTY_ARRAY;
+ } else if (buffer != null && count <= buffer.length) {
+ System.arraycopy(tree.namespaceBinding, tree.beta[nodeNr], buffer, 0, count);
+ if (count < buffer.length) {
+ buffer[count] = null;
+ }
+ return buffer;
+ } else {
+ NamespaceBinding[] array = new NamespaceBinding[count];
+ System.arraycopy(tree.namespaceBinding, tree.beta[nodeNr], array, 0, count);
+ return array;
+ }
+ } else {
+ return NamespaceBinding.EMPTY_ARRAY;
+ }
+ }
+
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+
+ @Override
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) {
+ int a = tree.alpha[nodeNr];
+ if (a<0) return null;
+ NamePool pool = getNamePool();
+ while (a < tree.numberOfAttributes && tree.attParent[a] == nodeNr) {
+ int fp = tree.attCode[a] & NamePool.FP_MASK;
+ // Avoid allocating a name code for an ad-hoc request
+ if (pool.getLocalName(fp).equals(local) && pool.getURI(fp).equals(uri)) {
+ return tree.attValue[a].toString();
+ }
+ a++;
+ }
+ return null;
+ }
+
+ /**
+ * Get the value of the attribute with a given fingerprint.
+ *
+ * @param fp the fingerprint of the required attribute
+ * @return the string value of the attribute if present, or null if absent
+ */
+ @Override
+ public String getAttributeValue(int fp) {
+ int a = tree.alpha[nodeNr];
+ if (a<0) return null;
+ while (a < tree.numberOfAttributes && tree.attParent[a] == nodeNr) {
+ if (fp == (tree.attCode[a] & NamePool.FP_MASK)) {
+ return tree.attValue[a].toString();
+ }
+ a++;
+ }
+ return null;
+ }
+
+ /**
+ * Copy this node to a given receiver
+ * @param copyOptions
+ */
+
+ public void copy(/*@NotNull*/ Receiver receiver, int copyOptions, int locationId) throws XPathException {
+
+ // Based on an algorithm supplied by Ruud Diterwich
+
+ // Performance measurements show that this achieves no speed-up over the OLD version
+ // (in 7.4). So might as well switch back.
+
+ // control vars
+ short level = -1;
+ boolean closePending = false;
+ short startLevel = tree.depth[nodeNr];
+ boolean first = true;
+ boolean disallowNamespaceSensitiveContent =
+ ((copyOptions & CopyOptions.TYPE_ANNOTATIONS) != 0) &&
+ ((copyOptions & CopyOptions.SOME_NAMESPACES) == 0);
+
+ Configuration config = tree.getConfiguration();
+ NamePool pool = config.getNamePool();
+ int next = nodeNr;
+ CopyInformee informee = (CopyInformee)receiver.getPipelineConfiguration().getComponent(CopyInformee.class.getName());
+
+ // document.diagnosticDump();
+
+ do {
+
+ // determine node depth
+ short nodeLevel = tree.depth[next];
+
+ // extra close required?
+ if (closePending) {
+ level++;
+ }
+
+ // close former elements
+ for (; level > nodeLevel; level--) {
+ receiver.endElement();
+ }
+
+ // new node level
+ level = nodeLevel;
+
+ // output depends on node kind
+ switch (tree.nodeKind[next]) {
+ case Type.ELEMENT : {
+
+ // start element
+ final SchemaType typeCode = (CopyOptions.includes(copyOptions, CopyOptions.TYPE_ANNOTATIONS) ?
+ getConfiguration().getSchemaType(tree.getTypeAnnotation(next)): Untyped.getInstance());
+ if (disallowNamespaceSensitiveContent) {
+ try {
+ checkNotNamespaceSensitiveElement(config, typeCode, next);
+ } catch (CopyNamespaceSensitiveException e) {
+ int lang = receiver.getPipelineConfiguration().getHostLanguage();
+ e.setErrorCode((lang == Configuration.XSLT ? "XTTE0950" : "XQTY0086"));
+ throw e;
+ }
+ }
+ if (informee != null) {
+ locationId = informee.notifyElementNode(tree.getNode(next));
+ }
+ int nameCode = tree.nameCode[next];
+ String prefix = pool.getPrefix(nameCode);
+ String uri = pool.getURI(nameCode);
+ String local = pool.getLocalName(nameCode);
+ receiver.startElement(new FingerprintedQName(prefix, uri, local, nameCode),
+ typeCode,
+ locationId, (first ? 0 : ReceiverOptions.NAMESPACE_OK));
+
+ // there is an element to close
+ closePending = true;
+
+ // output namespaces
+ if ((copyOptions & CopyOptions.SOME_NAMESPACES)!=0 && tree.usesNamespaces) {
+ if (first) {
+ if ((copyOptions & CopyOptions.LOCAL_NAMESPACES) != 0) {
+ NamespaceBinding[] localNamespaces = getDeclaredNamespaces(null);
+ for (NamespaceBinding ns : localNamespaces) {
+ if (ns == null) {
+ break;
+ }
+ receiver.namespace(ns, 0);
+ }
+ } else if ((copyOptions & CopyOptions.ALL_NAMESPACES) != 0) {
+ NamespaceIterator.sendNamespaces(this, receiver);
+ }
+ } else {
+ int ns = tree.beta[next]; // by convention
+ if (ns>0 ) {
+ while (ns < tree.numberOfNamespaces &&
+ tree.namespaceParent[ns] == next ) {
+ NamespaceBinding nscode = tree.namespaceBinding[ns];
+ receiver.namespace(nscode, 0);
+ ns++;
+ }
+ }
+ }
+ }
+ first = false;
+
+ // output attributes
+
+ int att = tree.alpha[next];
+ if (att >= 0) {
+ while (att < tree.numberOfAttributes && tree.attParent[att] == next ) {
+ int attCode = tree.attCode[att];
+ SimpleType attType = (CopyOptions.includes(copyOptions, CopyOptions.TYPE_ANNOTATIONS) ?
+ (SimpleType)getConfiguration().getSchemaType(tree.getAttributeAnnotation(att)) :
+ BuiltInAtomicType.UNTYPED_ATOMIC);
+ if (disallowNamespaceSensitiveContent) {
+ try {
+ checkNotNamespaceSensitiveAttribute(config, attType, att);
+ } catch (CopyNamespaceSensitiveException e) {
+ int lang = receiver.getPipelineConfiguration().getHostLanguage();
+ e.setErrorCode((lang == Configuration.XSLT ? "XTTE0950" : "XQTY0086"));
+ throw e;
+ }
+ }
+ receiver.attribute(new CodedName(attCode, getNamePool()), attType, tree.attValue[att], locationId, 0);
+ att++;
+ }
+ }
+
+ // start content
+ receiver.startContent();
+ break;
+ }
+ case Type.TEXT: {
+
+ // don't close text nodes
+ closePending = false;
+
+ // output characters
+ final CharSequence value = TinyTextImpl.getStringValue(tree, next);
+ receiver.characters(value, locationId, ReceiverOptions.WHOLE_TEXT_NODE);
+ break;
+ }
+
+ case Type.WHITESPACE_TEXT: {
+
+ // don't close text nodes
+ closePending = false;
+
+ // output characters
+ final CharSequence value = WhitespaceTextImpl.getStringValueCS(tree, next);
+ receiver.characters(value, locationId, ReceiverOptions.WHOLE_TEXT_NODE);
+ break;
+ }
+
+ case Type.COMMENT : {
+
+ // don't close text nodes
+ closePending = false;
+
+ // output copy of comment
+ int start = tree.alpha[next];
+ int len = tree.beta[next];
+ if (len>0) {
+ receiver.comment(tree.commentBuffer.subSequence(start, start+len), locationId, 0);
+ } else {
+ receiver.comment("", 0, 0);
+ }
+ break;
+ }
+ case Type.PROCESSING_INSTRUCTION : {
+
+ // don't close text nodes
+ closePending = false;
+
+ // output copy of PI
+ NodeInfo pi = tree.getNode(next);
+ receiver.processingInstruction(pi.getLocalPart(), pi.getStringValue(), locationId, 0);
+ break;
+ }
+
+ case Type.PARENT_POINTER : {
+ closePending = false;
+ }
+ }
+
+ next++;
+
+ } while (next < tree.numberOfNodes && tree.depth[next] > startLevel);
+
+ // close all remaining elements
+ if (closePending) {
+ level++;
+ }
+ for (; level > startLevel; level--) {
+ receiver.endElement();
+ }
+ }
+
+ /**
+ * Check whether the content of an element is namespace-sensitive
+ * @param config the configuration
+ * @param type the type annotation of the node
+ * @param nodeNr the the node number of the elemente
+ * @throws XPathException
+ */
+
+ private void checkNotNamespaceSensitiveElement(/*@NotNull*/ Configuration config, SchemaType type, int nodeNr) throws XPathException {
+ if (type instanceof SimpleType && ((SimpleType)type).isNamespaceSensitive()) {
+ if (type.isAtomicType()) {
+ throw new CopyNamespaceSensitiveException(
+ "Cannot copy QName or NOTATION values without copying namespaces");
+ } else {
+ // For a union or list type, we need to check whether the actual value is namespace-sensitive
+ AtomicSequence value = tree.getTypedValueOfElement(nodeNr);
+ SequenceIterator iter = value.iterate();
+ while (true) {
+ AtomicValue val = (AtomicValue)iter.next();
+ if (val == null) {
+ return;
+ }
+ if (val.getPrimitiveType().isNamespaceSensitive()) {
+ throw new CopyNamespaceSensitiveException(
+ "Cannot copy QName or NOTATION values without copying namespaces");
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Check whether the content of an attribute is namespace-sensitive
+ * @param config the configuration
+ * @param type the type annotation of the node
+ * @param nodeNr the the node number of the elemente
+ * @throws XPathException
+ */
+
+ private void checkNotNamespaceSensitiveAttribute(/*@NotNull*/ Configuration config, SimpleType type, int nodeNr) throws XPathException {
+ if (type.isNamespaceSensitive()) {
+ if (type.isAtomicType()) {
+ throw new CopyNamespaceSensitiveException(
+ "Cannot copy QName or NOTATION values without copying namespaces");
+ } else {
+ // For a union or list type, we need to check whether the actual value is namespace-sensitive
+ AtomicSequence value = tree.getTypedValueOfAttribute(null, nodeNr);
+ SequenceIterator iter = value.iterate();
+ while (true) {
+ AtomicValue val = (AtomicValue)iter.next();
+ if (val == null) {
+ return;
+ }
+ if (val.getPrimitiveType().isNamespaceSensitive()) {
+ throw new CopyNamespaceSensitiveException(
+ "Cannot copy QName or NOTATION values without copying namespaces");
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ *
+ * @param prefix the namespace prefix. May be the zero-length string, indicating
+ * that there is no prefix. This indicates either the default namespace or the
+ * null namespace, depending on the value of useDefault.
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is "". If false, the method returns "" when the prefix is "".
+ * @return the uri for the namespace, or null if the prefix is not in scope.
+ * The "null namespace" is represented by the pseudo-URI "".
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(/*@Nullable*/ String prefix, boolean useDefault) {
+ if (!useDefault && (prefix==null || prefix.length()==0)) {
+ return "";
+ }
+ int ns = tree.beta[nodeNr]; // by convention
+ if (ns>0 ) {
+ while (ns < tree.numberOfNamespaces &&
+ tree.namespaceParent[ns] == nodeNr ) {
+ NamespaceBinding nscode = tree.namespaceBinding[ns];
+ if ((nscode.getPrefix().equals(prefix))) {
+ String uri = nscode.getURI();
+ if (uri.length()==0) {
+ // this is a namespace undeclaration, so the prefix is not in scope
+ if (prefix.length()==0) {
+ // the namespace xmlns="" is always in scope
+ return "";
+ } else {
+ return null;
+ }
+ } else {
+ return uri;
+ }
+ }
+ ns++;
+ }
+ }
+
+ // now search the namespaces defined on the ancestor nodes.
+
+ NodeInfo parent = getParent();
+ if (parent instanceof NamespaceResolver) {
+ return ((NamespaceResolver)parent).getURIForPrefix(prefix, useDefault);
+ }
+ return null;
+ }
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ // this looks very inefficient, but the method isn't actually used...
+ return tree.isIdElement(nodeNr);
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return tree.isIdrefElement(nodeNr);
+ }
+
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyNodeImpl.java b/sf/saxon/tree/tiny/TinyNodeImpl.java
new file mode 100644
index 0000000..9b7300e
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyNodeImpl.java
@@ -0,0 +1,797 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.tree.NamespaceNode;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.EmptyAxisIterator;
+import net.sf.saxon.tree.iter.PrependIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+
+import javax.xml.transform.SourceLocator;
+
+
+/**
+ * A node in a TinyTree representing an XML element, character content, or attribute.<P>
+ * This is the top-level class in the implementation class hierarchy; it essentially contains
+ * all those methods that can be defined using other primitive methods, without direct access
+ * to data.
+ *
+ * @author Michael H. Kay
+ */
+
+public abstract class TinyNodeImpl implements NodeInfo, FingerprintedNode, SourceLocator {
+
+ protected TinyTree tree;
+ protected int nodeNr;
+ /*@Nullable*/ protected TinyNodeImpl parent = null;
+
+ /**
+ * Chararacteristic letters to identify each type of node, indexed using the node type
+ * values. These are used as the initial letter of the result of generate-id()
+ */
+
+ /*@NotNull*/ public static final char[] NODE_LETTER =
+ {'x', 'e', 'a', 't', 'x', 'x', 'x', 'p', 'c', 'r', 'x', 'x', 'x', 'n'};
+
+ /**
+ * To implement {@link Sequence}, this method returns the item itself
+ * @return this item
+ */
+
+ public Item head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ * @return a singleton iterator that returns this item
+ */
+
+ public SequenceIterator iterate() {
+ return SingletonIterator.makeIterator(this);
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ return getStringValue();
+ }
+
+ /**
+ * Get the type annotation of this node, if any
+ */
+
+ public int getTypeAnnotation() {
+ return -1;
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ public SchemaType getSchemaType() {
+ return null;
+ }
+
+ /**
+ * Get the column number of the node.
+ * The default implementation returns -1, meaning unknown
+ */
+
+ public int getColumnNumber() {
+ return tree.getColumnNumber(nodeNr);
+ }
+
+ /**
+ * Get the public identifier of the document entity containing this node.
+ * The default implementation returns null, meaning unknown
+ */
+
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Set the system id of this node. <br />
+ * This method is present to ensure that
+ * the class implements the javax.xml.transform.Source interface, so a node can
+ * be used as the source of a transformation.
+ */
+
+ public void setSystemId(String uri) {
+ tree.setSystemId(nodeNr, uri);
+ }
+
+ /**
+ * Set the parent of this node. Providing this information is useful,
+ * if it is known, because otherwise getParent() has to search backwards
+ * through the document.
+ * @param parent the parent of this node
+ */
+
+ protected void setParentNode(TinyNodeImpl parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * Determine whether this is the same node as another node
+ *
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(/*@NotNull*/ NodeInfo other) {
+ return this == other ||
+ (other instanceof TinyNodeImpl &&
+ tree == ((TinyNodeImpl)other).tree &&
+ nodeNr == ((TinyNodeImpl)other).nodeNr &&
+ getNodeKind() == other.getNodeKind());
+ }
+
+ /**
+ * The equals() method compares nodes for identity. It is defined to give the same result
+ * as isSameNodeInfo().
+ *
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ * @since 8.7 Previously, the effect of the equals() method was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics. It is safer to use isSameNodeInfo() for this reason.
+ * The equals() method has been defined because it is useful in contexts such as a Java Set or HashMap.
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof NodeInfo && isSameNodeInfo((NodeInfo)other);
+ }
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ *
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode() {
+ return (((int)(tree.getDocumentNumber() & 0x3ff)) << 20) ^ nodeNr ^ (getNodeKind() << 14);
+ }
+
+ /**
+ * Get the system ID for the entity containing the node.
+ */
+
+ public String getSystemId() {
+ return tree.getSystemId(nodeNr);
+ }
+
+ /**
+ * Get the base URI for the node. Default implementation for child nodes gets
+ * the base URI of the parent node.
+ */
+
+ public String getBaseURI() {
+ return (getParent()).getBaseURI();
+ }
+
+ /**
+ * Get the line number of the node within its source document entity
+ */
+
+ public int getLineNumber() {
+ return tree.getLineNumber(nodeNr);
+ }
+
+ /**
+ * Get the node sequence number (in document order). Sequence numbers are monotonic but not
+ * consecutive. The sequence number must be unique within the document (not, as in
+ * previous releases, within the whole document collection).
+ * For document nodes, elements, text nodes, comment nodes, and PIs, the sequence number
+ * is a long with the sequential node number in the top half and zero in the bottom half.
+ * The bottom half is used only for attributes and namespace.
+ * @return the sequence number
+ */
+
+ protected long getSequenceNumber() {
+ return (long)nodeNr << 32;
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ *
+ * @param other The other node, whose position is to be compared with this node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public final int compareOrder(/*@NotNull*/ NodeInfo other) {
+ long a = getSequenceNumber();
+ if (other instanceof TinyNodeImpl) {
+ long b = ((TinyNodeImpl)other).getSequenceNumber();
+ if (a < b) {
+ return -1;
+ }
+ if (a > b) {
+ return +1;
+ }
+ return 0;
+ } else {
+ // it must be a namespace node
+ return 0 - other.compareOrder(this);
+ }
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+ if (other instanceof TinyNodeImpl && !(other instanceof TinyAttributeImpl)) {
+ TinyNodeImpl a = this;
+ TinyNodeImpl b = (TinyNodeImpl)other;
+ if (a.isSameNodeInfo(b)) {
+ return AxisInfo.SELF;
+ }
+ if (a.isAncestorOrSelf(b)) {
+ return AxisInfo.ANCESTOR;
+ }
+ if (b.isAncestorOrSelf(a)) {
+ return AxisInfo.DESCENDANT;
+ }
+ if (a.compareOrder(b) < 0) {
+ return AxisInfo.PRECEDING;
+ }
+ return AxisInfo.FOLLOWING;
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Get the fingerprint of the node, used for matching names
+ */
+
+ public int getFingerprint() {
+ int nc = getNameCode();
+ if (nc == -1) {
+ return -1;
+ }
+ return nc & 0xfffff;
+ }
+
+ /**
+ * Get the name code of the node, used for matching names
+ */
+
+ public int getNameCode() {
+ // overridden for attributes and namespace nodes.
+ return tree.nameCode[nodeNr];
+ }
+
+ /**
+ * Get the prefix part of the name of this node. This is the name before the ":" if any.
+ *
+ * @return the prefix part of the name. For an unnamed node, return "".
+ */
+
+ public String getPrefix() {
+ int code = tree.nameCode[nodeNr];
+ if (code < 0) {
+ return "";
+ }
+ if (!NamePool.isPrefixed(code)) {
+ return "";
+ }
+ return tree.getNamePool().getPrefix(code);
+ }
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ *
+ * @return The URI of the namespace of this node. For an unnamed node, or for
+ * an element or attribute in the default namespace, return an empty string.
+ */
+
+ public String getURI() {
+ int code = tree.nameCode[nodeNr];
+ if (code < 0) {
+ return "";
+ }
+ return tree.getNamePool().getURI(code);
+ }
+
+ /**
+ * Get the display name of this node (a lexical QName). For elements and attributes this is [prefix:]localname.
+ * The original prefix is retained. For unnamed nodes, the result is an empty string.
+ *
+ * @return The display name of this node.
+ * For a node with no name, return an empty string.
+ */
+
+ public String getDisplayName() {
+ int code = tree.nameCode[nodeNr];
+ if (code < 0) {
+ return "";
+ }
+ return tree.getNamePool().getDisplayName(code);
+ }
+
+ /**
+ * Get the local part of the name of this node.
+ *
+ * @return The local name of this node.
+ * For a node with no name, return "".
+ */
+
+ public String getLocalPart() {
+ int code = tree.nameCode[nodeNr];
+ if (code < 0) {
+ return "";
+ }
+ return tree.getNamePool().getLocalName(code);
+ }
+
+ /**
+ * Return an iterator over all the nodes reached by the given axis from this node
+ *
+ * @param axisNumber Identifies the required axis, eg. Axis.CHILD or Axis.PARENT
+ * @return a AxisIteratorImpl that scans the nodes reached by the axis in turn.
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber) {
+ // fast path for child axis
+ if (axisNumber == AxisInfo.CHILD) {
+ if (hasChildNodes()) {
+ return new SiblingEnumeration(tree, this, null, true);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ } else {
+ return iterateAxis(axisNumber, AnyNodeTest.getInstance());
+ }
+ }
+
+ /**
+ * Return an iterator over the nodes reached by the given axis from this node
+ *
+ * @param axisNumber Identifies the required axis, eg. Axis.CHILD or Axis.PARENT
+ * @param nodeTest A pattern to be matched by the returned nodes.
+ * @return a AxisIteratorImpl that scans the nodes reached by the axis in turn.
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber, /*@NotNull*/ NodeTest nodeTest) {
+
+ int type = getNodeKind();
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR:
+ return new AncestorEnumeration(this, nodeTest, false);
+
+ case AxisInfo.ANCESTOR_OR_SELF:
+ return new AncestorEnumeration(this, nodeTest, true);
+
+ case AxisInfo.ATTRIBUTE:
+ if (type != Type.ELEMENT) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ if (tree.alpha[nodeNr] < 0) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ return new AttributeEnumeration(tree, nodeNr, nodeTest);
+
+ case AxisInfo.CHILD:
+ if (hasChildNodes()) {
+ return new SiblingEnumeration(tree, this, nodeTest, true);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+
+ case AxisInfo.DESCENDANT:
+ if (type == Type.DOCUMENT &&
+ nodeTest instanceof NameTest &&
+ nodeTest.getPrimitiveType() == Type.ELEMENT) {
+ return ((TinyDocumentImpl)this).getAllElements(nodeTest.getFingerprint());
+ } else if (hasChildNodes()) {
+ return new DescendantEnumeration(tree, this, nodeTest, false);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+
+ case AxisInfo.DESCENDANT_OR_SELF:
+ if (hasChildNodes()) {
+ return new DescendantEnumeration(tree, this, nodeTest, true);
+ } else {
+ return Navigator.filteredSingleton(this, nodeTest);
+ }
+
+ case AxisInfo.FOLLOWING:
+ if (type == Type.ATTRIBUTE || type == Type.NAMESPACE) {
+ return new FollowingEnumeration(tree, (TinyNodeImpl)getParent(), nodeTest, true);
+ } else if (tree.depth[nodeNr] == 0) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ } else {
+ return new FollowingEnumeration(tree, this, nodeTest, false);
+ }
+
+ case AxisInfo.FOLLOWING_SIBLING:
+ if (type == Type.ATTRIBUTE || type == Type.NAMESPACE || tree.depth[nodeNr] == 0) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ } else {
+ return new SiblingEnumeration(tree, this, nodeTest, false);
+ }
+
+ case AxisInfo.NAMESPACE:
+ if (type != Type.ELEMENT) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ return NamespaceNode.makeIterator(this, nodeTest);
+
+ case AxisInfo.PARENT:
+ NodeInfo parent = getParent();
+ return Navigator.filteredSingleton(parent, nodeTest);
+
+ case AxisInfo.PRECEDING:
+ if (type == Type.ATTRIBUTE || type == Type.NAMESPACE) {
+ return new PrecedingEnumeration(tree, (TinyNodeImpl)getParent(), nodeTest, false);
+ } else if (tree.depth[nodeNr] == 0) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ } else {
+ return new PrecedingEnumeration(tree, this, nodeTest, false);
+ }
+
+ case AxisInfo.PRECEDING_SIBLING:
+ if (type == Type.ATTRIBUTE || type == Type.NAMESPACE || tree.depth[nodeNr] == 0) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ } else {
+ return new PrecedingSiblingEnumeration(tree, this, nodeTest);
+ }
+
+ case AxisInfo.SELF:
+ return Navigator.filteredSingleton(this, nodeTest);
+
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ if (type == Type.DOCUMENT) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ } else if (type == Type.ATTRIBUTE || type == Type.NAMESPACE) {
+ // See test numb32.
+ TinyNodeImpl el = (TinyNodeImpl)getParent();
+ return new PrependIterator(el, new PrecedingEnumeration(tree, el, nodeTest, true));
+ } else {
+ return new PrecedingEnumeration(tree, this, nodeTest, true);
+ }
+
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+ /**
+ * Find the parent node of this node.
+ *
+ * @return The Node object describing the containing element or root node.
+ */
+
+ /*@Nullable*/ public NodeInfo getParent() {
+ if (parent != null) {
+ return parent;
+ }
+ int p = getParentNodeNr(tree, nodeNr);
+ if (p == -1) {
+ parent = null;
+ } else {
+ parent = tree.getNode(p);
+ }
+ return parent;
+ }
+
+ /**
+ * Static method to get the parent of a given node, without instantiating the node as an object.
+ * The starting node is any node other than an attribute or namespace node.
+ *
+ * @param tree the tree containing the starting node
+ * @param nodeNr the node number of the starting node within the tree
+ * @return the node number of the parent node, or -1 if there is no parent.
+ */
+
+ static int getParentNodeNr(/*@NotNull*/ TinyTree tree, int nodeNr) {
+
+ if (tree.depth[nodeNr] == 0) {
+ return -1;
+ }
+
+ // follow the next-sibling pointers until we reach either a next sibling pointer that
+ // points backwards, or a parent-pointer pseudo-node
+ int p = tree.next[nodeNr];
+ while (p > nodeNr) {
+ if (tree.nodeKind[p] == Type.PARENT_POINTER) {
+ return tree.alpha[p];
+ }
+ p = tree.next[p];
+ }
+ return p;
+ }
+
+ /**
+ * Determine whether the node has any children.
+ *
+ * @return <code>true</code> if this node has any attributes,
+ * <code>false</code> otherwise.
+ */
+
+ public boolean hasChildNodes() {
+ // overridden in TinyParentNodeImpl
+ return false;
+ }
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) {
+ return null;
+ }
+
+ /**
+ * Get the value of the attribute with a given fingerprint.
+ *
+ * @param fp the fingerprint of the required attribute
+ * @return the string value of the attribute if present, or null if absent
+ */
+ public String getAttributeValue(int fp) {
+ return null;
+ }
+
+ /**
+ * Get the root node of the tree (not necessarily a document node)
+ *
+ * @return the NodeInfo representing the root of this tree
+ */
+
+ public NodeInfo getRoot() {
+ if (tree.depth[nodeNr] == 0) {
+ return this;
+ }
+ if (parent != null) {
+ return parent.getRoot();
+ }
+ return tree.getNode(tree.getRootNode(nodeNr));
+ }
+
+ /**
+ * Get the root (document) node
+ *
+ * @return the DocumentInfo representing the containing document
+ */
+
+ /*@Nullable*/ public DocumentInfo getDocumentRoot() {
+ NodeInfo root = getRoot();
+ if (root.getNodeKind() == Type.DOCUMENT) {
+ return (DocumentInfo)root;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return tree.getConfiguration();
+ }
+
+ /**
+ * Get the NamePool for the tree containing this node
+ *
+ * @return the NamePool
+ */
+
+ public NamePool getNamePool() {
+ return tree.getNamePool();
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ /*@Nullable*/ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return null;
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node
+ *
+ * @param buffer buffer, which on return will contain
+ * a character string that uniquely identifies this node.
+ *
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ buffer.append("d");
+ buffer.append(Long.toString(tree.getDocumentNumber()));
+ buffer.append(NODE_LETTER[getNodeKind()]);
+ buffer.append(Integer.toString(nodeNr));
+ }
+
+ /**
+ * Get the document number of the document containing this node
+ * (Needed when the document isn't a real node, for sorting free-standing elements)
+ */
+
+ public final long getDocumentNumber() {
+ return tree.getDocumentNumber();
+ }
+
+ /**
+ * Test if this node is an ancestor-or-self of another
+ *
+ * @param d the putative descendant-or-self node
+ * @return true if this node is an ancestor-or-self of d
+ */
+
+ public boolean isAncestorOrSelf(/*@NotNull*/ TinyNodeImpl d) {
+ // If it's a different tree, return false
+ if (tree != d.tree) return false;
+ int dn = d.nodeNr;
+ // If d is an attribute, then either "this" must be the same attribute, or "this" must
+ // be an ancestor-or-self of the parent of d.
+ if (d instanceof TinyAttributeImpl) {
+ if (this instanceof TinyAttributeImpl) {
+ return nodeNr == dn;
+ } else {
+ dn = tree.attParent[dn];
+ }
+ }
+ // If this is an attribute, return false (we've already handled the case where it's the same attribute)
+ if (this instanceof TinyAttributeImpl) return false;
+
+ // From now on, we know that both "this" and "dn" are nodes in the primary array
+
+ // If this node is later in document order, return false
+ if (nodeNr > dn) return false;
+
+ // If it's the same node, return true
+ if (nodeNr == dn) return true;
+
+ // We've dealt with the "self" case: to be an ancestor, it must be an element or document node
+ if (!(this instanceof TinyParentNodeImpl)) return false;
+
+ // If this node is deeper than the target node then it can't be an ancestor
+ if (tree.depth[nodeNr] >= tree.depth[dn]) return false;
+
+ // The following code will exit as soon as we find an ancestor that has a following-sibling:
+ // when that happens, we know it's an ancestor iff its following-sibling is beyond the node we're
+ // looking for. If the ancestor has no following sibling, we go up a level.
+
+ // The algorithm depends on the following assertion: if A is before D in document order, then
+ // either A is an ancestor of D, or some ancestor-or-self of A has a following-sibling that
+ // is before-or-equal to D in document order.
+
+ int n = nodeNr;
+ while (true) {
+ int nextSib = tree.next[n];
+ if (nextSib < 0 || nextSib > dn) {
+ return true;
+ } else if (tree.depth[nextSib] == 0) {
+ return true;
+ } else if (nextSib < n) {
+ n = nextSib;
+ // continue
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Determine whether this node has the is-id property
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ return false; // overridden for element and attribute nodes
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return false; // overridden for element and attribute nodes
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return tree.isNilled(nodeNr);
+ }
+
+ /**
+ * Get the TinyTree object containing this node
+ * @return the TinyTree. Note that this may also contain other unrelated trees
+ */
+
+ public TinyTree getTree() {
+ return tree;
+ }
+
+ /**
+ * Get the node number of this node within the TinyTree. This method is intended for internal use.
+ * @return the internal node number
+ */
+
+ public int getNodeNumber() {
+ return nodeNr;
+ }
+
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyParentNodeImpl.java b/sf/saxon/tree/tiny/TinyParentNodeImpl.java
new file mode 100644
index 0000000..cfe01fd
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyParentNodeImpl.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.Type;
+
+/**
+ * TinyParentNodeImpl is an implementation of a non-leaf node (specifically, an Element node
+ * or a Document node)
+ * @author Michael H. Kay
+ */
+
+
+public abstract class TinyParentNodeImpl extends TinyNodeImpl {
+
+ /**
+ * Determine if the node has children.
+ */
+
+ public final boolean hasChildNodes() {
+ return (nodeNr+1 < tree.numberOfNodes &&
+ tree.depth[nodeNr+1] > tree.depth[nodeNr]);
+ }
+
+ /**
+ * Return the string-value of the node, that is, the concatenation
+ * of the character content of all descendent elements and text nodes.
+ * @return the accumulated character content of the element, including descendant elements.
+ */
+
+ public final String getStringValue() {
+ return getStringValueCS(tree, nodeNr).toString();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public final CharSequence getStringValueCS() {
+ return getStringValueCS(tree, nodeNr);
+ }
+
+ /**
+ * Get the string value of a node. This static method allows the string value of a node
+ * to be obtained without instantiating the node as a Java object. The method also returns
+ * a CharSequence rather than a string, which means it can sometimes avoid copying the
+ * data.
+ * @param tree The containing document
+ * @param nodeNr identifies the node whose string value is required. This must be a
+ * document or element node. The caller is trusted to ensure this.
+ * @return the string value of the node, as a CharSequence
+ */
+
+ public static CharSequence getStringValueCS(/*@NotNull*/ TinyTree tree, int nodeNr) {
+ int level = tree.depth[nodeNr];
+
+ // note, we can't rely on the value being contiguously stored because of whitespace
+ // nodes: the data for these may still be present.
+
+ int next = nodeNr+1;
+
+ // we optimize two special cases: firstly, where the node has no children, and secondly,
+ // where it has a single text node as a child.
+
+ if (tree.depth[next] <= level) {
+ return "";
+ } else if (tree.nodeKind[next] == Type.TEXT && (next+1 >= tree.numberOfNodes || tree.depth[next+1] <= level)) {
+ //int length = tree.beta[next];
+ //int start = tree.alpha[next];
+ //return new CharSlice(tree.charBuffer, start, length);
+ //return tree.charBuffer.subSequence(start, start+length);
+ return TinyTextImpl.getStringValue(tree, next);
+ }
+
+ // now handle the general case
+
+ FastStringBuffer sb = null;
+ while (next < tree.numberOfNodes && tree.depth[next] > level) {
+ final byte kind = tree.nodeKind[next];
+ if (kind==Type.TEXT) {
+// int length = tree.beta[next];
+// int start = tree.alpha[next];
+ if (sb==null) {
+ sb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ }
+ //sb.append(tree.charBuffer, start, length);
+ //sb.append(tree.charBuffer.subSequence(start, start+length));
+ sb.append(TinyTextImpl.getStringValue(tree, next));
+ } else if (kind==Type.WHITESPACE_TEXT) {
+ if (sb==null) {
+ sb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ }
+ WhitespaceTextImpl.appendStringValue(tree, next, sb);
+ }
+ next++;
+ }
+ if (sb==null) return "";
+ return sb.condense();
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyProcInstImpl.java b/sf/saxon/tree/tiny/TinyProcInstImpl.java
new file mode 100644
index 0000000..6c6668e
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyProcInstImpl.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.StringValue;
+
+/**
+ * TProcInstImpl is an implementation of ProcInstInfo
+ * @author Michael H. Kay
+ * @version 16 July 1999
+ */
+
+
+final class TinyProcInstImpl extends TinyNodeImpl {
+
+ public TinyProcInstImpl(TinyTree tree, int nodeNr) {
+ this.tree = tree;
+ this.nodeNr = nodeNr;
+ }
+
+ public String getStringValue() {
+ int start = tree.alpha[nodeNr];
+ int len = tree.beta[nodeNr];
+ if (len==0) {
+ return ""; // need to special-case this for the Microsoft JVM
+ }
+ char[] dest = new char[len];
+ tree.commentBuffer.getChars(start, start+len, dest, 0);
+ return new String(dest, 0, len);
+ }
+
+ /**
+ * Get the typed value of this node.
+ * Returns the string value, as an instance of xs:string
+ */
+
+ public AtomicSequence atomize() {
+ return new StringValue(getStringValue());
+ }
+
+ public final int getNodeKind() {
+ return Type.PROCESSING_INSTRUCTION;
+ }
+
+ /**
+ * Get the base URI of this processing instruction node.
+ */
+
+ public String getBaseURI() {
+ return Navigator.getBaseURI(this);
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(Receiver out, int copyOptions, int locationId) throws XPathException {
+ out.processingInstruction(getDisplayName(), getStringValue(), 0, 0);
+ }
+
+ // DOM methods
+
+ /**
+ * The target of this processing instruction. XML defines this as being
+ * the first token following the markup that begins the processing
+ * instruction.
+ * @return the "target", or in XDM terms, the name of the processing instruction
+ */
+
+ public String getTarget() {
+ return getDisplayName();
+ }
+
+ /**
+ * The content of this processing instruction. This is from the first non
+ * white space character after the target to the character immediately
+ * preceding the <code>?></code> .
+ * @return the content of the processing instruction (in XDM this is the
+ * same as its string value)
+ */
+
+ /*@NotNull*/ public String getData() {
+ return getStringValue();
+ }
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyTextImpl.java b/sf/saxon/tree/tiny/TinyTextImpl.java
new file mode 100644
index 0000000..9269c31
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyTextImpl.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+/**
+ * A node in the XML parse tree representing character content
+ * @author Michael H. Kay
+ */
+
+public final class TinyTextImpl extends TinyNodeImpl {
+
+ /**
+ * Create a text node
+ * @param tree the tree to contain the node
+ * @param nodeNr the internal node number
+ */
+
+ public TinyTextImpl(TinyTree tree, int nodeNr) {
+ this.tree = tree;
+ this.nodeNr = nodeNr;
+ }
+
+ /**
+ * Return the character value of the node.
+ * @return the string value of the node
+ */
+
+ public String getStringValue() {
+ return getStringValueCS().toString();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ int start = tree.alpha[nodeNr];
+ int len = tree.beta[nodeNr];
+ return tree.charBuffer.subSequence(start, start+len);
+ }
+
+ /**
+ * Static method to get the string value of a text node without first constructing the node object
+ * @param tree the tree
+ * @param nodeNr the node number of the text node
+ * @return the string value of the text node
+ */
+
+ public static CharSequence getStringValue(TinyTree tree, int nodeNr) {
+ int start = tree.alpha[nodeNr];
+ int len = tree.beta[nodeNr];
+ return tree.charBuffer.subSequence(start, start+len);
+ }
+
+ /**
+ * Return the type of node.
+ * @return Type.TEXT
+ */
+
+ public final int getNodeKind() {
+ return Type.TEXT;
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(Receiver out, int copyOptions, int locationId) throws XPathException {
+ out.characters(getStringValueCS(), 0, 0);
+ }
+
+ /**
+ * Get the typed value. The result of this method will always be consistent with the method
+ * {@link net.sf.saxon.om.Item#getTypedValue()}. However, this method is often more convenient and may be
+ * more efficient, especially in the common case where the value is expected to be a singleton.
+ * @return the typed value. It will be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize() throws XPathException {
+ return new UntypedAtomicValue(getStringValueCS());
+ }
+}
+
diff --git a/sf/saxon/tree/tiny/TinyTree.java b/sf/saxon/tree/tiny/TinyTree.java
new file mode 100644
index 0000000..d0c6d78
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyTree.java
@@ -0,0 +1,1507 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.z.IntArraySet;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.linked.SystemIdMap;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+
+/**
+ * A data structure to hold the contents of a tree. As the name implies, this implementation
+ * of the data model is optimized for size, and for speed of creation: it minimizes the number
+ * of Java objects used.
+ *
+ * <p>It can be used to represent a tree that is rooted at a document node, or one that is rooted
+ * at an element node.</p>
+ */
+
+public final class TinyTree {
+
+ /*@NotNull*/
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ /*@NotNull*/
+ private Configuration config;
+
+ // List of top-level document nodes.
+ /*@NotNull*/
+ private ArrayList<TinyDocumentImpl> documentList = new ArrayList<TinyDocumentImpl>(5);
+
+ // The document number (really a tree number: it can identify a non-document root node
+ protected long documentNumber;
+
+ // the contents of the document
+
+ protected AppendableCharSequence charBuffer;
+ /*@Nullable*/
+ protected FastStringBuffer commentBuffer = null; // created when needed
+
+ protected int numberOfNodes = 0; // excluding attributes and namespaces
+
+ // The following arrays contain one entry for each node other than attribute
+ // and namespace nodes, arranged in document order.
+
+ // nodeKind indicates the kind of node, e.g. element, text, or comment
+ public byte[] nodeKind;
+
+ // depth is the depth of the node in the hierarchy, i.e. the number of ancestors
+ protected short[] depth;
+
+ // next is the node number of the next sibling
+ // - unless it points backwards, in which case it is the node number of the parent
+ protected int[] next;
+
+ // alpha holds a value that depends on the node kind. For text nodes, it is the offset
+ // into the text buffer. For comments and processing instructions, it is the offset into
+ // the comment buffer. For elements, it is the index of the first attribute node, or -1
+ // if this element has no attributes.
+ protected int[] alpha;
+
+ // beta holds a value that depends on the node kind. For text nodes, it is the length
+ // of the text. For comments and processing instructions, it is the length of the text.
+ // For elements, it is the index of the first namespace node, or -1
+ // if this element has no namespaces.
+ protected int[] beta;
+
+ // nameCode holds the name of the node, as an identifier resolved using the name pool
+ protected int[] nameCode;
+
+ // the prior array indexes preceding-siblings; it is constructed only when required
+ /*@Nullable*/
+ protected int[] prior = null;
+
+ // the typeCode array holds type codes for element nodes; it is constructed only
+ // if at least one element has a type other than untyped, or has an IDREF property.
+ // The array holds the type fingerprint, with bit TYPECODE_IDREF set if the value is an IDREF
+ /*@Nullable*/
+ protected int[] typeCodeArray = null;
+
+ // the typedValue array holds the typed values of element nodes if the typed value is anything
+ // other than string, untypedAtomic, or anyURI. This means it is only used for schema-validated
+ // documents. It is created lazily when the typed value of a node is first accessed.
+ /*@Nullable*/
+ protected AtomicSequence[] typedValueArray = null;
+
+ // boolean switch to disable the typed value caching
+ private boolean allowTypedValueCache = true;
+
+ public static final int TYPECODE_IDREF = 1<<29;
+
+ // the owner array gives fast access from a node to its parent; it is constructed
+ // only when required
+ // protected int[] parentIndex = null;
+
+ // the following arrays have one entry for each attribute.
+ protected int numberOfAttributes = 0;
+
+ // attParent is the index of the parent element node
+ protected int[] attParent;
+
+ // attCode is the nameCode representing the attribute name
+ protected int[] attCode;
+
+ // attValue is the string value of the attribute
+ protected CharSequence[] attValue;
+
+ // attTypedValue is the typed vlaue of the attribute, maintained only if the attribute type is
+ // something other than string, untypedAtomic, or anyURI. It is maintained lazily on first reference
+ // to the typed value
+ protected AtomicSequence[] attTypedValue;
+
+ // attTypeCode holds type annotations. The array is created only if any nodes have a type annotation
+ // or are marked as IDREF/IDREFS attributes. The bit TYPECODE_IDREF represents the is-idref property,
+ // while IS_DTD_TYPE is set if the type is DTD-derived.
+ /*@Nullable*/
+ protected int[] attTypeCode;
+
+ // The following arrays have one entry for each namespace declaration
+ protected int numberOfNamespaces = 0;
+
+ // namespaceParent is the index of the element node owning the namespace declaration
+ protected int[] namespaceParent;
+
+ // namespaceCode is the namespace binding, holding the prefix and URI
+ protected NamespaceBinding[] namespaceBinding;
+
+ // an array holding the offsets of all the level-0 (root) nodes, so that the root of a given
+ // node can be found efficiently
+ /*@NotNull*/ private int[] rootIndex = new int[5];
+ protected int rootIndexUsed = 0;
+
+ /*@Nullable*/
+ private int[] lineNumbers = null;
+ /*@Nullable*/
+ private int[] columnNumbers = null;
+ /*@Nullable*/
+ private SystemIdMap systemIdMap = null;
+
+ // a boolean that is set to true if the document declares a namespace other than the XML namespace
+ protected boolean usesNamespaces = false;
+
+ // We maintain statistics in static data, recording how large the trees created under this Java VM
+ // turned out to be. These figures are then used when allocating space for new trees, on the assumption
+ // that there is likely to be some uniformity. The statistics are initialized to an arbitrary value
+ // so that they can be used every time including the first time. The count of how many trees have been
+ // created so far is initialized artificially to 5, to provide some smoothing if the first real tree is
+ // atypically large or small.
+
+ private static int treesCreated = 5;
+ private static double averageNodes = 4000.0;
+ private static double averageAttributes = 100.0;
+ private static double averageNamespaces = 20.0;
+ private static double averageCharacters = 4000.0;
+
+ /**
+ * Create a TinyTree. The initial size is based on the average size of
+ * trees previously built in this session
+ * @param config the Saxon Configuration
+ */
+
+ public TinyTree(/*@NotNull*/ Configuration config) {
+ this(config,
+ (int)(averageNodes + 1),
+ (int)(averageAttributes + 1),
+ (int)(averageNamespaces + 1),
+ (int)(averageCharacters + 1));
+ }
+
+ /**
+ * Create a tree with a specified initial size
+ * @param config the Saxon configuration
+ * @param nodes the expected number of (non attribute or namespace) nodes
+ * @param attributes the expected number of attributes
+ * @param namespaces the expected number of namespace declarations
+ * @param characters the expected number of characters in the document (in text nodes)
+ */
+
+ public TinyTree(/*@NotNull*/ Configuration config, int nodes, int attributes, int namespaces, int characters) {
+ //System.err.println("TinyTree.new() (initial size " + nodes + ", treesCreated = " + treesCreated + ")");
+
+ nodeKind = new byte[nodes];
+ depth = new short[nodes];
+ next = new int[nodes];
+ alpha = new int[nodes];
+ beta = new int[nodes];
+ nameCode = new int[nodes];
+
+ numberOfAttributes = 0;
+ attParent = new int[attributes];
+ attCode = new int[attributes];
+ attValue = new String[attributes];
+
+ numberOfNamespaces = 0;
+ namespaceParent = new int[namespaces];
+ namespaceBinding = new NamespaceBinding[namespaces];
+
+ //charBuffer = new LargeStringBuffer(characters, 64000);
+ charBuffer = (characters > 65000 ? new LargeStringBuffer() : new FastStringBuffer(characters));
+
+ setConfiguration(config);
+ }
+
+ /**
+ * Set the Configuration that contains this document
+ * @param config the Saxon configuration
+ */
+
+ public void setConfiguration(/*@NotNull*/ Configuration config) {
+ this.config = config;
+ allowTypedValueCache = config.isLicensedFeature(Configuration.LicenseFeature.SCHEMA_VALIDATION) &&
+ config.getBooleanProperty(FeatureKeys.USE_TYPED_VALUE_CACHE);
+ addNamespace(0, NamespaceBinding.XML);
+ }
+
+ /**
+ * Get the configuration previously set using setConfiguration
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the name pool used for the names in this document
+ * @return the name pool
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ private void ensureNodeCapacity(short kind) {
+ if (nodeKind.length < numberOfNodes+1) {
+ //System.err.println("Number of nodes = " + numberOfNodes);
+ int k = (kind == Type.STOPPER ? numberOfNodes+1 : numberOfNodes*2);
+
+ byte[] nodeKind2 = new byte[k];
+ int[] next2 = new int[k];
+ short[] depth2 = new short[k];
+ int[] alpha2 = new int[k];
+ int[] beta2 = new int[k];
+ int[] nameCode2 = new int[k];
+
+ System.arraycopy(nodeKind, 0, nodeKind2, 0, numberOfNodes);
+ System.arraycopy(next, 0, next2, 0, numberOfNodes);
+ System.arraycopy(depth, 0, depth2, 0, numberOfNodes);
+ System.arraycopy(alpha, 0, alpha2, 0, numberOfNodes);
+ System.arraycopy(beta, 0, beta2, 0, numberOfNodes);
+ System.arraycopy(nameCode, 0, nameCode2, 0, numberOfNodes);
+
+ nodeKind = nodeKind2;
+ next = next2;
+ depth = depth2;
+ alpha = alpha2;
+ beta = beta2;
+ nameCode = nameCode2;
+
+ if (typeCodeArray != null) {
+ int[] typeCodeArray2 = new int[k];
+ System.arraycopy(typeCodeArray, 0, typeCodeArray2, 0, numberOfNodes);
+ typeCodeArray = typeCodeArray2;
+ }
+
+ if (typedValueArray != null) {
+ @SuppressWarnings({"unchecked"})
+ AtomicSequence[] typedValueArray2 = new AtomicSequence[k];
+ //noinspection SuspiciousSystemArraycopy
+ System.arraycopy(typedValueArray, 0, typedValueArray2, 0, numberOfNodes);
+ typedValueArray = typedValueArray2;
+ }
+
+ if (lineNumbers != null) {
+ int[] lines2 = new int[k];
+ System.arraycopy(lineNumbers, 0, lines2, 0, numberOfNodes);
+ lineNumbers = lines2;
+ int[] columns2 = new int[k];
+ System.arraycopy(columnNumbers, 0, columns2, 0, numberOfNodes);
+ columnNumbers = columns2;
+ }
+ }
+ }
+
+ private void ensureAttributeCapacity() {
+ if (attParent.length < numberOfAttributes+1) {
+ int k = numberOfAttributes*2;
+ if (k==0) {
+ k = 10;
+ }
+
+ int[] attParent2 = new int[k];
+ int[] attCode2 = new int[k];
+ CharSequence[] attValue2 = new String[k];
+
+ System.arraycopy(attParent, 0, attParent2, 0, numberOfAttributes);
+ System.arraycopy(attCode, 0, attCode2, 0, numberOfAttributes);
+ System.arraycopy(attValue, 0, attValue2, 0, numberOfAttributes);
+
+ attParent = attParent2;
+ attCode = attCode2;
+ attValue = attValue2;
+
+ if (attTypeCode != null) {
+ int[] attTypeCode2 = new int[k];
+ System.arraycopy(attTypeCode, 0, attTypeCode2, 0, numberOfAttributes);
+ attTypeCode = attTypeCode2;
+ }
+
+ if (attTypedValue != null) {
+ @SuppressWarnings({"unchecked"})
+ AtomicSequence[] attTypedValue2 = new AtomicSequence[k];
+ System.arraycopy(attTypedValue, 0, attTypedValue2, 0, numberOfAttributes);
+ attTypedValue = attTypedValue2;
+ }
+ }
+ }
+
+ private void ensureNamespaceCapacity() {
+ if (namespaceParent.length < numberOfNamespaces+1) {
+ int k = numberOfNamespaces*2;
+ if (k==0) {
+ k = 10;
+ }
+
+ int[] namespaceParent2 = new int[k];
+ NamespaceBinding[] namespaceCode2 = new NamespaceBinding[k];
+
+ System.arraycopy(namespaceParent, 0, namespaceParent2, 0, numberOfNamespaces);
+ System.arraycopy(namespaceBinding, 0, namespaceCode2, 0, numberOfNamespaces);
+
+ namespaceParent = namespaceParent2;
+ namespaceBinding = namespaceCode2;
+ }
+ }
+
+ /**
+ * Add a document node to the tree. The data structure can contain any number of document (or element) nodes
+ * as top-level nodes. The document node is retained in the documentList list, and its offset in that list
+ * is held in the alpha array for the relevant node number.
+ * @param doc the document node to be added
+ * @return the number of the node that was added
+ */
+
+ int addDocumentNode(TinyDocumentImpl doc) {
+ documentList.add(doc);
+ return addNode(Type.DOCUMENT, 0, documentList.size()-1, 0, -1);
+ }
+
+ /**
+ * Add a node to the tree
+ * @param kind The kind of the node. This must be a document, element, text, comment,
+ * or processing-instruction node (not an attribute or namespace)
+ * @param depth The depth in the tree
+ * @param alpha Pointer to attributes or text
+ * @param beta Pointer to namespaces or text
+ * @param nameCode The name of the node
+ * @return the node number of the node that was added
+ */
+ int addNode(short kind, int depth, int alpha, int beta, int nameCode) {
+ ensureNodeCapacity(kind);
+ nodeKind[numberOfNodes] = (byte)kind;
+ this.depth[numberOfNodes] = (short)depth;
+ this.alpha[numberOfNodes] = alpha;
+ this.beta[numberOfNodes] = beta;
+ this.nameCode[numberOfNodes] = nameCode;
+ next[numberOfNodes] = -1; // safety precaution
+
+ if (typeCodeArray != null) {
+ typeCodeArray[numberOfNodes] = StandardNames.XS_UNTYPED;
+ }
+
+ if (numberOfNodes == 0) {
+ documentNumber = config.getDocumentNumberAllocator().allocateDocumentNumber();
+ }
+
+ if (depth == 0 && kind != Type.STOPPER) {
+ if (rootIndexUsed == rootIndex.length) {
+ int[] r2 = new int[rootIndexUsed * 2];
+ System.arraycopy(rootIndex, 0, r2, 0, rootIndexUsed);
+ rootIndex = r2;
+ }
+ rootIndex[rootIndexUsed++] = numberOfNodes;
+ }
+ return numberOfNodes++;
+ }
+
+ /**
+ * Append character data to the current text node
+ * @param chars the character data to be appended
+ */
+
+ void appendChars(CharSequence chars) {
+ if (charBuffer instanceof FastStringBuffer && charBuffer.length() > 65000) {
+ LargeStringBuffer lsb = new LargeStringBuffer();
+ lsb.append(charBuffer);
+ charBuffer = lsb;
+ }
+ charBuffer.append(chars);
+ }
+
+ /**
+ * Create a new text node that is a copy of an existing text node
+ * @param depth the depth of the new node
+ * @param existingNodeNr the node to be copied
+ * @return the node number of the new node
+ */
+
+ public int addTextNodeCopy(int depth, int existingNodeNr) {
+ return addNode(Type.TEXT, depth, alpha[existingNodeNr], beta[existingNodeNr], -1);
+ }
+
+ /**
+ * Condense the tree: release unused memory. This is done after the full tree has been built.
+ * The method makes a pragmatic judgement as to whether it is worth reclaiming space; this is
+ * only done when the constructed tree is very small compared with the space allocated.
+ */
+
+ void condense() {
+ //System.err.println("TinyTree.condense() " + this + " roots " + rootIndexUsed + " nodes " + numberOfNodes + " capacity " + nodeKind.length);
+
+ // If there are already two trees in this forest, the chances are that more will be added. In this
+ // case we don't want to condense the arrays because we will only have to expand them again, which gets
+ // increasingly expensive as they grow larger.
+
+ if (rootIndexUsed > 1) {
+ return;
+ }
+ if (numberOfNodes * 3 < nodeKind.length ||
+ (nodeKind.length - numberOfNodes > 20000)) {
+
+ //System.err.println("-- copying node arrays");
+ int k = numberOfNodes + 1;
+
+ byte[] nodeKind2 = new byte[k];
+ int[] next2 = new int[k];
+ short[] depth2 = new short[k];
+ int[] alpha2 = new int[k];
+ int[] beta2 = new int[k];
+ int[] nameCode2 = new int[k];
+
+ System.arraycopy(nodeKind, 0, nodeKind2, 0, numberOfNodes);
+ System.arraycopy(next, 0, next2, 0, numberOfNodes);
+ System.arraycopy(depth, 0, depth2, 0, numberOfNodes);
+ System.arraycopy(alpha, 0, alpha2, 0, numberOfNodes);
+ System.arraycopy(beta, 0, beta2, 0, numberOfNodes);
+ System.arraycopy(nameCode, 0, nameCode2, 0, numberOfNodes);
+ if (typeCodeArray != null) {
+ int[] type2 = new int[k];
+ System.arraycopy(typeCodeArray, 0, type2, 0, numberOfNodes);
+ typeCodeArray = type2;
+ }
+ if (lineNumbers != null) {
+ int[] lines2 = new int[k];
+ System.arraycopy(lineNumbers, 0, lines2, 0, numberOfNodes);
+ lineNumbers = lines2;
+ int[] columns2 = new int[k];
+ System.arraycopy(columnNumbers, 0, columns2, 0, numberOfNodes);
+ columnNumbers = columns2;
+ }
+
+ nodeKind = nodeKind2;
+ next = next2;
+ depth = depth2;
+ alpha = alpha2;
+ beta = beta2;
+ nameCode = nameCode2;
+ }
+
+ if ((numberOfAttributes * 3 < attParent.length) ||
+ (attParent.length - numberOfAttributes > 1000)) {
+ int k = numberOfAttributes;
+
+ //System.err.println("-- copying attribute arrays");
+
+ if (k==0) {
+ attParent = IntArraySet.EMPTY_INT_ARRAY;
+ attCode = IntArraySet.EMPTY_INT_ARRAY;
+ attValue = EMPTY_STRING_ARRAY;
+ attTypeCode = null;
+ }
+
+ int[] attParent2 = new int[k];
+ int[] attCode2 = new int[k];
+ CharSequence[] attValue2 = new String[k];
+
+ System.arraycopy(attParent, 0, attParent2, 0, numberOfAttributes);
+ System.arraycopy(attCode, 0, attCode2, 0, numberOfAttributes);
+ System.arraycopy(attValue, 0, attValue2, 0, numberOfAttributes);
+
+ attParent = attParent2;
+ attCode = attCode2;
+ attValue = attValue2;
+
+ if (attTypeCode != null) {
+ int[] attTypeCode2 = new int[k];
+ System.arraycopy(attTypeCode, 0, attTypeCode2, 0, numberOfAttributes);
+ attTypeCode = attTypeCode2;
+ }
+ }
+
+ if (numberOfNamespaces * 3 < namespaceParent.length) {
+ int k = numberOfNamespaces;
+ int[] namespaceParent2 = new int[k];
+ NamespaceBinding[] namespaceCode2 = new NamespaceBinding[k];
+
+ //System.err.println("-- copying namespace arrays");
+
+ System.arraycopy(namespaceParent, 0, namespaceParent2, 0, numberOfNamespaces);
+ System.arraycopy(namespaceBinding, 0, namespaceCode2, 0, numberOfNamespaces);
+
+ namespaceParent = namespaceParent2;
+ namespaceBinding = namespaceCode2;
+ }
+
+ updateStatistics();
+// System.err.println("STATS: " + averageNodes + ", " + averageAttributes + ", "
+// + averageNamespaces + ", " + averageCharacters);
+
+// if (charBufferLength * 3 < charBuffer.length ||
+// charBuffer.length - charBufferLength > 10000) {
+// char[] c2 = new char[charBufferLength];
+// System.arraycopy(charBuffer, 0, c2, 0, charBufferLength);
+// charBuffer = c2;
+// }
+ }
+
+ /**
+ * Set the type annotation of an element node
+ * @param nodeNr the node whose type annotation is to be set
+ * @param typeCode the type annotation
+ */
+
+ void setElementAnnotation(int nodeNr, int typeCode) {
+ if (typeCode != StandardNames.XS_UNTYPED) {
+ if (typeCodeArray == null) {
+ typeCodeArray = new int[nodeKind.length];
+ Arrays.fill(typeCodeArray, 0, nodeKind.length, StandardNames.XS_UNTYPED);
+ }
+ assert typeCodeArray != null;
+ typeCodeArray[nodeNr] = typeCode;
+ }
+ }
+
+ /**
+ * Get the type annotation of a node. Applies only to document, element, text,
+ * processing instruction, and comment nodes.
+ * @param nodeNr the node whose type annotation is required
+ * @return the fingerprint of the type annotation for elements and attributes, otherwise undefined.
+ */
+
+ public int getTypeAnnotation(int nodeNr) {
+ if (typeCodeArray == null) {
+ return StandardNames.XS_UNTYPED;
+ }
+ return typeCodeArray[nodeNr] & NamePool.FP_MASK;
+ }
+
+ /**
+ * Get the typed value of an element node.
+ * @param element the element node
+ * @return the typed value of the node (a Value whose items are AtomicValue instances)
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs, for example if the node is
+ * an element annotated with a type that has element-only content
+ */
+
+ /*@Nullable*/ public AtomicSequence getTypedValueOfElement(/*@NotNull*/ TinyElementImpl element) throws XPathException {
+ int nodeNr = element.nodeNr;
+ if (typedValueArray == null || typedValueArray[nodeNr] == null) {
+ int annotation = getTypeAnnotation(nodeNr);
+ if (annotation == StandardNames.XS_UNTYPED || annotation == StandardNames.XS_UNTYPED_ATOMIC ||
+ annotation == StandardNames.XS_ANY_TYPE) {
+ CharSequence stringValue = TinyParentNodeImpl.getStringValueCS(this, nodeNr);
+ return new UntypedAtomicValue(stringValue);
+ } else if (annotation == StandardNames.XS_STRING) {
+ CharSequence stringValue = TinyParentNodeImpl.getStringValueCS(this, nodeNr);
+ return new StringValue(stringValue);
+ } else if (annotation == StandardNames.XS_ANY_URI) {
+ CharSequence stringValue = TinyParentNodeImpl.getStringValueCS(this, nodeNr);
+ return new AnyURIValue(stringValue);
+ } else {
+ SchemaType stype = getConfiguration().getSchemaType(annotation);
+ if (stype == null) {
+ String typeName;
+ try {
+ typeName = getNamePool().getDisplayName(annotation);
+ } catch (Exception err) {
+ typeName = annotation + "";
+ }
+ throw new XPathException("Unknown type annotation " +
+ Err.wrap(typeName) + " in document instance");
+ } else {
+ AtomicSequence value = stype.atomize(element);
+ if (allowTypedValueCache) {
+ if (typedValueArray == null) {
+ //noinspection unchecked
+ typedValueArray = new AtomicSequence[nodeKind.length];
+ }
+ typedValueArray[nodeNr] = value;
+ }
+ return value;
+ }
+ }
+ } else {
+ return typedValueArray[nodeNr];
+ }
+ }
+
+ /**
+ * Get the type value of an element node, given only the node number
+ * @param nodeNr the node number of the element node
+ * @return the typed value of the node
+ * @throws net.sf.saxon.trans.XPathException if the eement has no typed value
+ */
+
+ /*@Nullable*/ public AtomicSequence getTypedValueOfElement(int nodeNr) throws XPathException {
+ if (typedValueArray == null || typedValueArray[nodeNr] == null) {
+ int annotation = getTypeAnnotation(nodeNr);
+ if (annotation == StandardNames.XS_UNTYPED_ATOMIC || annotation == StandardNames.XS_UNTYPED) {
+ CharSequence stringValue = TinyParentNodeImpl.getStringValueCS(this, nodeNr);
+ return new UntypedAtomicValue(stringValue);
+ } else if (annotation == StandardNames.XS_STRING) {
+ CharSequence stringValue = TinyParentNodeImpl.getStringValueCS(this, nodeNr);
+ return new StringValue(stringValue);
+ } else if (annotation == StandardNames.XS_ANY_URI) {
+ CharSequence stringValue = TinyParentNodeImpl.getStringValueCS(this, nodeNr);
+ return new AnyURIValue(stringValue);
+ } else {
+ SchemaType stype = getConfiguration().getSchemaType(annotation);
+ if (stype == null) {
+ String typeName;
+ try {
+ typeName = getNamePool().getDisplayName(annotation);
+ } catch (Exception err) {
+ typeName = annotation + "";
+ }
+ throw new XPathException("Unknown type annotation " +
+ Err.wrap(typeName) + " in document instance");
+ } else {
+ TinyElementImpl element = new TinyElementImpl(this, nodeNr);
+ AtomicSequence value = stype.atomize(element);
+ if (allowTypedValueCache) {
+ if (typedValueArray == null) {
+ //noinspection unchecked
+ typedValueArray = new AtomicSequence[nodeKind.length];
+ }
+ typedValueArray[nodeNr] = value;
+ }
+ return value;
+ }
+ }
+ } else {
+ return typedValueArray[nodeNr];
+ }
+ }
+
+ /**
+ * Get the typed value of an attribute node. This method avoids
+ * materializing the attribute node if possible, but uses the attribute node
+ * supplied if it already exists.
+ * @param att the attribute node if available. If null is supplied, the attribute node
+ * will be materialized only if it is needed.
+ * @param nodeNr the node number of the attribute node
+ * @return the typed value of the node
+ * @throws net.sf.saxon.trans.XPathException if an error is found
+ */
+
+ public AtomicSequence getTypedValueOfAttribute(/*@Nullable*/ TinyAttributeImpl att, int nodeNr) throws XPathException {
+ if (attTypeCode == null) {
+ // it's an untyped tree
+ return new UntypedAtomicValue(attValue[nodeNr]);
+ }
+ if (attTypedValue == null || attTypedValue[nodeNr] == null) {
+ int annotation = getAttributeAnnotation(nodeNr);
+ if (annotation == StandardNames.XS_UNTYPED_ATOMIC) {
+ return new UntypedAtomicValue(attValue[nodeNr]);
+ } else if (annotation == StandardNames.XS_STRING) {
+ return new StringValue(attValue[nodeNr]);
+ } else if (annotation == StandardNames.XS_ANY_URI) {
+ return new AnyURIValue(attValue[nodeNr]);
+ } else {
+ SchemaType stype = getConfiguration().getSchemaType(annotation);
+ if (stype == null) {
+ String typeName;
+ try {
+ typeName = getNamePool().getDisplayName(annotation);
+ } catch (Exception err) {
+ typeName = annotation + "";
+ }
+ throw new XPathException("Unknown attribute type annotation " +
+ Err.wrap(typeName) + " in document instance");
+ } else {
+ if (att == null) {
+ att = new TinyAttributeImpl(this, nodeNr);
+ }
+ AtomicSequence value = stype.atomize(att);
+ if (allowTypedValueCache) {
+ if (attTypedValue == null) {
+ //noinspection unchecked
+ attTypedValue = new AtomicSequence[attParent.length];
+ }
+ attTypedValue[nodeNr] = value;
+ }
+ return value;
+ }
+ }
+ } else {
+ return attTypedValue[nodeNr];
+ }
+ }
+
+
+
+ /**
+ * Get the node kind of a given node, which must be a document, element,
+ * text, comment, or processing instruction node
+ * @param nodeNr the node number
+ * @return the node kind
+ */
+
+ public int getNodeKind(int nodeNr) {
+ int kind = nodeKind[nodeNr];
+ return (kind == Type.WHITESPACE_TEXT ? Type.TEXT : kind);
+ }
+
+ /**
+ * Get the nameCode for a given node, which must be a document, element,
+ * text, comment, or processing instruction node
+ * @param nodeNr the node number
+ * @return the name code
+ */
+
+ public int getNameCode(int nodeNr) {
+ return nameCode[nodeNr];
+ }
+
+ /**
+ * On demand, make an index for quick access to preceding-sibling nodes
+ */
+
+ void ensurePriorIndex() {
+ // TODO: avoid rebuilding the whole index in the second case, i.e. with a forest
+ if (prior==null || prior.length < numberOfNodes) {
+ makePriorIndex();
+ }
+ }
+
+ private synchronized void makePriorIndex() {
+ int[] p = new int[numberOfNodes];
+ Arrays.fill(p, 0, numberOfNodes, -1);
+ for (int i=0; i<numberOfNodes; i++) {
+ int nextNode = next[i];
+ if (nextNode > i) {
+ p[nextNode] = i;
+ }
+ }
+ prior = p;
+ }
+
+ /**
+ * Add an attribute node to the tree
+ * @param root the root of the tree to contain the attribute
+ * @param parent the parent element of the new attribute
+ * @param nameCode the name code of the attribute
+ * @param typeCode the type annotation of the attribute
+ * @param attValue the string value of the attribute
+ * @param properties any special properties of the attribute (bit-significant)
+ */
+
+
+ void addAttribute(/*@NotNull*/ NodeInfo root, int parent, int nameCode, int typeCode, CharSequence attValue, int properties) {
+ ensureAttributeCapacity();
+ attParent[numberOfAttributes] = parent;
+ attCode[numberOfAttributes] = nameCode;
+ this.attValue[numberOfAttributes] = attValue;
+
+ if (typeCode == -1) {
+ // this shouldn't happen any more
+ typeCode = StandardNames.XS_UNTYPED_ATOMIC;
+ }
+
+ if (typeCode != StandardNames.XS_UNTYPED_ATOMIC) {
+ initializeAttributeTypeCodes();
+ }
+
+ if (attTypeCode != null) {
+ attTypeCode[numberOfAttributes] = typeCode;
+ }
+
+ if (alpha[parent] == -1) {
+ alpha[parent] = numberOfAttributes;
+ }
+
+ if (root instanceof TinyDocumentImpl) {
+ boolean isID = false;
+ if ((properties & ReceiverOptions.IS_ID) != 0) {
+ isID = true;
+ } else if ((nameCode & NamePool.FP_MASK) == StandardNames.XML_ID) {
+ isID = true;
+ } else if (config.getTypeHierarchy().isIdCode(typeCode)) {
+ isID = true;
+ }
+ if (isID) {
+
+ // The attribute is marked as being an ID. But we don't trust it - it
+ // might come from a non-validating parser. Before adding it to the index, we
+ // check that it really is an ID.
+
+ String id = Whitespace.trim(attValue);
+
+ // Make an exception to our usual policy of storing the original string value.
+ // This is because xml:id processing applies whitespace trimming at an earlier stage
+ this.attValue[numberOfAttributes] = id;
+
+ if (root.getConfiguration().getNameChecker().isValidNCName(id)) {
+ NodeInfo e = getNode(parent);
+ ((TinyDocumentImpl)root).registerID(e, id);
+ } else if (attTypeCode != null) {
+ attTypeCode[numberOfAttributes] = StandardNames.XS_UNTYPED_ATOMIC;
+ }
+ }
+ if ((properties & ReceiverOptions.IS_IDREF) != 0) {
+ initializeAttributeTypeCodes();
+ assert attTypeCode != null;
+ attTypeCode[numberOfAttributes] = typeCode | TYPECODE_IDREF;
+ }
+ }
+
+ // Note: IDREF attributes are not indexed at this stage; that happens only if and when
+ // the idref() function is called.
+
+ // Note that an attTypes array will be created for all attributes if any IDREF value is reported.
+
+ numberOfAttributes++;
+ }
+
+ private void initializeAttributeTypeCodes() {
+ if (attTypeCode==null) {
+ // this is the first typed attribute;
+ // create an array for the types, and set all previous attributes to untyped
+ attTypeCode = new int[attParent.length];
+ Arrays.fill(attTypeCode, 0, numberOfAttributes, StandardNames.XS_UNTYPED_ATOMIC);
+// for (int i=0; i<numberOfAttributes; i++) {
+// attTypeCode[i] = StandardNames.XDT_UNTYPED_ATOMIC;
+// }
+ }
+ }
+
+ /**
+ * Index an element of type xs:ID
+ * @param root the root node of the document
+ * @param nodeNr the element of type xs:ID
+ * @param checker checks names against XML 1.0 or XML 1.1 rules
+ */
+
+ public void indexIDElement(/*@NotNull*/ NodeInfo root, int nodeNr, /*@NotNull*/ NameChecker checker) {
+ String id = Whitespace.trim(TinyParentNodeImpl.getStringValueCS(this, nodeNr));
+ if (root.getNodeKind() == Type.DOCUMENT && checker.isValidNCName(id)) {
+ NodeInfo e = getNode(nodeNr);
+ ((TinyDocumentImpl)root).registerID(e, id);
+ }
+ }
+
+ /**
+ * Add a namespace node to the current element
+ * @param parent the node number of the element
+ * @param binding namespace identifying the prefix and uri
+ */
+ void addNamespace(int parent, /*@NotNull*/ NamespaceBinding binding) {
+
+ ensureNamespaceCapacity();
+ namespaceParent[numberOfNamespaces] = parent;
+ namespaceBinding[numberOfNamespaces] = binding;
+
+ if (beta[parent] == -1) {
+ beta[parent] = numberOfNamespaces;
+ }
+ numberOfNamespaces++;
+ if (!binding.isXmlNamespace()) {
+ usesNamespaces = true;
+ }
+ }
+
+ /**
+ * Get the node at a given position in the tree
+ * @param nr the node number
+ * @return the node at the given position
+ */
+
+ public final TinyNodeImpl getNode(int nr) {
+
+ switch (nodeKind[nr]) {
+ case Type.DOCUMENT:
+ return documentList.get(alpha[nr]);
+ case Type.ELEMENT:
+ return new TinyElementImpl(this, nr);
+ case Type.TEXT:
+ return new TinyTextImpl(this, nr);
+ case Type.WHITESPACE_TEXT:
+ return new WhitespaceTextImpl(this, nr);
+ case Type.COMMENT:
+ return new TinyCommentImpl(this, nr);
+ case Type.PROCESSING_INSTRUCTION:
+ return new TinyProcInstImpl(this, nr);
+ case Type.PARENT_POINTER:
+ throw new IllegalArgumentException("Attempting to treat a parent pointer as a node");
+ default:
+ throw new IllegalStateException("Unknown node kind");
+ }
+ }
+
+ /**
+ * Get the typed value of a node whose type is known to be untypedAtomic.
+ * The node must be a document, element, text,
+ * comment, or processing-instruction node, and it must have no type annotation.
+ * This method gets the typed value
+ * of a numbered node without actually instantiating the NodeInfo object, as
+ * a performance optimization.
+ * @param nodeNr the node whose typed value is required
+ * @return the atomic value of the node
+ */
+
+ AtomicValue getAtomizedValueOfUntypedNode(int nodeNr) {
+ switch (nodeKind[nodeNr]) {
+ case Type.ELEMENT:
+ case Type.DOCUMENT:
+ int level = depth[nodeNr];
+ int next = nodeNr+1;
+
+ // we optimize two special cases: firstly, where the node has no children, and secondly,
+ // where it has a single text node as a child.
+
+ if (depth[next] <= level) {
+ return UntypedAtomicValue.ZERO_LENGTH_UNTYPED;
+ } else if (nodeKind[next] == Type.TEXT && depth[next+1] <= level) {
+ int length = beta[next];
+ int start = alpha[next];
+ return new UntypedAtomicValue(charBuffer.subSequence(start, start+length));
+ } else if (nodeKind[next] == Type.WHITESPACE_TEXT && depth[next+1] <= level) {
+ return new UntypedAtomicValue(WhitespaceTextImpl.getStringValueCS(this, next));
+ }
+
+ // Now handle the general case
+
+ FastStringBuffer sb = null;
+ while (next < numberOfNodes && depth[next] > level) {
+ if (nodeKind[next]==Type.TEXT) {
+ if (sb==null) {
+ sb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ }
+ sb.append(TinyTextImpl.getStringValue(this, next));
+ } else if (nodeKind[next]==Type.WHITESPACE_TEXT) {
+ if (sb==null) {
+ sb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ }
+ WhitespaceTextImpl.appendStringValue(this, next, sb);
+ }
+ next++;
+ }
+ if (sb==null) {
+ return UntypedAtomicValue.ZERO_LENGTH_UNTYPED;
+ } else {
+ return new UntypedAtomicValue(sb.condense());
+ }
+
+ case Type.TEXT:
+ return new UntypedAtomicValue(TinyTextImpl.getStringValue(this, nodeNr));
+ case Type.WHITESPACE_TEXT:
+ return new UntypedAtomicValue(WhitespaceTextImpl.getStringValueCS(this, nodeNr));
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ int start2 = alpha[nodeNr];
+ int len2 = beta[nodeNr];
+ if (len2==0) return UntypedAtomicValue.ZERO_LENGTH_UNTYPED;
+ char[] dest = new char[len2];
+ assert commentBuffer != null;
+ commentBuffer.getChars(start2, start2+len2, dest, 0);
+ return new StringValue(new CharSlice(dest, 0, len2));
+ default:
+ throw new IllegalStateException("Unknown node kind");
+ }
+ }
+
+ /**
+ * Make a (transient) attribute node from the array of attributes
+ * @param nr the node number of the attribute
+ * @return an attribute node
+ */
+
+ /*@NotNull*/ TinyAttributeImpl getAttributeNode(int nr) {
+ return new TinyAttributeImpl(this, nr);
+ }
+
+ /**
+ * Get the type annotation of an attribute node.
+ * @param nr the node number of the attribute
+ * @return the fingerprint of the type annotation, or Type.UNTYPED_ATOMIC if there is no annotation
+ */
+
+ int getAttributeAnnotation(int nr) {
+ if (attTypeCode == null) {
+ return StandardNames.XS_UNTYPED_ATOMIC;
+ } else {
+ return attTypeCode[nr] & ~TYPECODE_IDREF;
+ }
+ }
+
+ /**
+ * Determine whether an attribute is an IDREF/IDREFS attribute. (The represents the
+ * is-idref property in the data model)
+ * @param nr the node number of the attribute
+ * @return true if this is an IDREF/IDREFS attribute
+ */
+
+ public boolean isIdAttribute(int nr) {
+ if (attTypeCode == null) {
+ return false;
+ }
+ int tc = getAttributeAnnotation(nr);
+ if (tc == StandardNames.XS_UNTYPED_ATOMIC) {
+ return false;
+ } else if (tc == StandardNames.XS_ID) {
+ return true;
+ } else if (tc < 1024) {
+ return false;
+ } else {
+ final SchemaType type = getConfiguration().getSchemaType(tc);
+ assert type != null;
+ final TypeHierarchy th = getConfiguration().getTypeHierarchy();
+ if (type.isAtomicType()) {
+ return th.isSubType((AtomicType)type, BuiltInAtomicType.ID);
+ } else if (type instanceof ListType) {
+ // We are using the XSD 1.1 rules, which allow ID's in list and union types
+ SimpleType itemType = ((ListType)type).getItemType();
+ return itemType.isAtomicType() &&
+ th.isSubType((AtomicType)itemType, BuiltInAtomicType.ID);
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Determine whether an attribute is an IDREF/IDREFS attribute. (The represents the
+ * is-idref property in the data model)
+ * @param nr the node number of the attribute
+ * @return true if this is an IDREF/IDREFS attribute
+ */
+
+ public boolean isIdrefAttribute(int nr) {
+ if (attTypeCode == null) {
+ return false;
+ }
+ int tc = attTypeCode[nr];
+ if ((tc & TYPECODE_IDREF) != 0) {
+ return true;
+ }
+ if (tc == StandardNames.XS_UNTYPED_ATOMIC) {
+ return false;
+ } else if (tc == StandardNames.XS_IDREF) {
+ return true;
+ } else if (tc == StandardNames.XS_IDREFS) {
+ return true;
+ } else if (tc < 1024) {
+ return false;
+ } else {
+ SchemaType type = getConfiguration().getSchemaType(tc);
+ assert type != null;
+ return type.isIdRefType();
+ }
+ }
+
+ /**
+ * Ask whether an element is an ID element. (The represents the
+ * is-id property in the data model)
+ * @param nr the element node whose is-idref property is required
+ * @return true if the node has the is-idref property
+ */
+
+ public boolean isIdElement(int nr) {
+ if (typeCodeArray == null) {
+ return false;
+ }
+ int tc = typeCodeArray[nr];
+ return (tc & TYPECODE_IDREF) != 0 ||
+ getConfiguration().getTypeHierarchy().isIdCode(tc & NamePool.FP_MASK);
+ // TODO: there is an additional condition, which is that the typed value must be a singleton
+ }
+
+ /**
+ * Ask whether an element is an IDREF/IDREFS element. (The represents the
+ * is-idref property in the data model)
+ * @param nr the element node whose is-idref property is required
+ * @return true if the node has the is-idref property
+ */
+
+ public boolean isIdrefElement(int nr) {
+ if (typeCodeArray == null) {
+ return false;
+ }
+ int tc = typeCodeArray[nr];
+ return (tc & TYPECODE_IDREF) != 0 ||
+ getConfiguration().getTypeHierarchy().isIdrefsCode(tc & NamePool.FP_MASK);
+ }
+
+
+ /**
+ * Set the system id of an element in the document. This identifies the external entity containing
+ * the node - this is not necessarily the same as the base URI.
+ * @param seq the node number
+ * @param uri the system ID
+ */
+
+ void setSystemId(int seq, /*@Nullable*/ String uri) {
+ if (uri==null) {
+ uri = "";
+ }
+ if (systemIdMap==null) {
+ systemIdMap = new SystemIdMap();
+ }
+ systemIdMap.setSystemId(seq, uri);
+ }
+
+
+ /**
+ * Get the system id of an element in the document
+ * @param seq the node number of the element node
+ * @return the system id (base URI) of the element
+ */
+
+ /*@Nullable*/ public String getSystemId(int seq) {
+ if (systemIdMap==null) {
+ return null;
+ }
+ return systemIdMap.getSystemId(seq);
+ }
+
+ /**
+ * Get the root node for a given node
+ * @param nodeNr the node number of the given node
+ * @return the node number of the root of the tree containing the given node
+ */
+
+ int getRootNode(int nodeNr) {
+ for (int i=rootIndexUsed-1; i>=0; i--) {
+ if (rootIndex[i] <= nodeNr) {
+ return rootIndex[i];
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Set line numbering on
+ */
+
+ public void setLineNumbering() {
+ lineNumbers = new int[nodeKind.length];
+ Arrays.fill(lineNumbers, -1);
+ columnNumbers = new int[nodeKind.length];
+ Arrays.fill(columnNumbers, -1);
+ }
+
+ /**
+ * Set the line number for a node. Ignored if line numbering is off.
+ * @param sequence the node number
+ * @param line the line number to be set for the node
+ * @param column the column number for the node
+ */
+
+ void setLineNumber(int sequence, int line, int column) {
+ if (lineNumbers != null) {
+ assert columnNumbers != null;
+ lineNumbers[sequence] = line;
+ columnNumbers[sequence] = column;
+ }
+ }
+
+ /**
+ * Get the line number for a node.
+ * @param sequence the node number
+ * @return the line number of the node. Return -1 if line numbering is off.
+ */
+
+ public int getLineNumber(int sequence) {
+ if (lineNumbers != null) {
+ // find the nearest preceding node that has a known line number, and return it
+ for (int i=sequence; i>=0; i--) {
+ int c = lineNumbers[i];
+ if (c > 0) {
+ return c;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Get the column number for a node.
+ * @param sequence the node number
+ * @return the line number of the node. Return -1 if line numbering is off.
+ */
+
+ int getColumnNumber(int sequence) {
+ if (columnNumbers != null) {
+ // find the nearest preceding node that has a known column number, and return it
+ for (int i=sequence; i>=0; i--) {
+ int c = columnNumbers[sequence];
+ if (c > 0) {
+ return c;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Get the document number (actually, the tree number)
+ * @return the unique number of this TinyTree structure
+ */
+
+ public long getDocumentNumber() {
+ return documentNumber;
+ }
+
+ /**
+ * Ask whether a given node is nilled
+ * @param nodeNr the node in question (which must be an element node)
+ * @return true if the node has the nilled property
+ */
+
+ public boolean isNilled(int nodeNr) {
+ return (typeCodeArray != null && (typeCodeArray[nodeNr] & NodeInfo.IS_NILLED) != 0);
+ }
+
+ /**
+ * Produce diagnostic print of main tree arrays
+ */
+
+ public void diagnosticDump() {
+ NamePool pool = config.getNamePool();
+ System.err.println(" node kind depth next alpha beta name type");
+ for (int i=0; i<numberOfNodes; i++) {
+ System.err.println(n8(i) + n8(nodeKind[i]) + n8(depth[i]) + n8(next[i]) +
+ n8(alpha[i]) + n8(beta[i]) + n8(nameCode[i]) +
+ n8(getTypeAnnotation(i)) +
+ (nameCode[i]== -1 ? "" : " " + pool.getDisplayName(nameCode[i])));
+ }
+ System.err.println(" attr parent name value");
+ for (int i=0; i<numberOfAttributes; i++) {
+ System.err.println(n8(i) + n8(attParent[i]) + n8(attCode[i]) + " " + attValue[i]);
+ }
+ System.err.println(" ns parent prefix uri");
+ for (int i=0; i<numberOfNamespaces; i++) {
+ System.err.println(n8(i) + n8(namespaceParent[i]) + namespaceBinding[i].getPrefix() + "=" + namespaceBinding[i].getURI());
+ }
+ }
+
+ /**
+ * Create diagnostic dump of the tree containing a particular node.
+ * Designed to be called as an extension function for diagnostics.
+ * @param node the node in question
+ */
+
+ public static synchronized void diagnosticDump(/*@NotNull*/ NodeInfo node) {
+ if (node instanceof TinyNodeImpl) {
+ TinyTree tree = ((TinyNodeImpl)node).tree;
+ System.err.println("Tree containing node " + ((TinyNodeImpl)node).nodeNr);
+ tree.diagnosticDump();
+ } else {
+ System.err.println("Node is not in a TinyTree");
+ }
+ }
+
+ /**
+ * Output a number as a string of 8 characters
+ * @param val the number
+ * @return the string representation of the number, aligned in a fixed width field
+ */
+
+ private static String n8(int val) {
+ String s = " " + val;
+ return s.substring(s.length()-8);
+ }
+
+ /**
+ * Output a statistical summary to System.err
+ */
+
+ public void showSize() {
+ System.err.println("Tree size: " + numberOfNodes + " nodes, " + charBuffer.length() + " characters, " +
+ numberOfAttributes + " attributes");
+ }
+
+ /**
+ * Update the statistics held in static data. We don't bother to sychronize, on the basis that it doesn't
+ * matter if the stats are wrong.
+ */
+
+ private void updateStatistics() {
+ int n0 = treesCreated;
+ if (n0 < 1000000) { // it should have stabilized by then, and we don't want to overflow
+ int n1 = treesCreated + 1;
+ treesCreated = n1;
+ averageNodes = ((averageNodes * n0) + numberOfNodes) / n1;
+ if (averageNodes < 10.0) {
+ averageNodes = 10.0;
+ }
+ averageAttributes = ((averageAttributes * n0) + numberOfAttributes) / n1;
+ if (averageAttributes < 10.0) {
+ averageAttributes = 10.0;
+ }
+ averageNamespaces = ((averageNamespaces * n0) + numberOfNamespaces) / n1;
+ if (averageNamespaces < 5.0) {
+ averageNamespaces = 5.0;
+ }
+ averageCharacters = ((averageCharacters * n0) + charBuffer.length()) / n1;
+ if (averageCharacters < 100.0) {
+ averageCharacters = 100.0;
+ }
+ }
+
+ }
+
+ /**
+ * Get the number of nodes in the tree, excluding attributes and namespace nodes
+ * @return the number of nodes.
+ */
+
+ public int getNumberOfNodes() {
+ return numberOfNodes;
+ }
+
+ /**
+ * Get the number of attributes in the tree
+ * @return the number of attributes
+ */
+
+ public int getNumberOfAttributes() {
+ return numberOfAttributes;
+ }
+
+ /**
+ * Get the number of namespace declarations in the tree
+ * @return the number of namespace declarations
+ */
+
+ public int getNumberOfNamespaces() {
+ return numberOfNamespaces;
+ }
+
+ /**
+ * Get the array holding node kind information
+ * @return an array of bytes, byte N is the node kind of node number N
+ */
+
+ public byte[] getNodeKindArray() {
+ return nodeKind;
+ }
+
+ /**
+ * Get the array holding node depth information
+ * @return an array of shorts, byte N is the node depth of node number N
+ */
+
+ public short[] getNodeDepthArray() {
+ return depth;
+ }
+
+ /**
+ * Get the array holding node name information
+ * @return an array of integers, integer N is the name code of node number N
+ */
+
+ public int[] getNameCodeArray() {
+ return nameCode;
+ }
+
+ /**
+ * Get the array holding node type information
+ * @return an array of integers, integer N is the type code of node number N
+ */
+
+ /*@Nullable*/ public int[] getTypeCodeArray() {
+ return typeCodeArray;
+ }
+
+ /**
+ * Get the array holding next-sibling pointers
+ * @return an array of integers, integer N is the next-sibling pointer for node number N
+ */
+
+ public int[] getNextPointerArray() {
+ return next;
+ }
+
+ /**
+ * Get the array holding alpha information
+ * @return an array of integers, whose meaning depends on the node kind. For elements it is a pointer
+ * to the first attribute, for text, comment, and processing instruction nodes it is a pointer to the content
+ */
+
+ public int[] getAlphaArray() {
+ return alpha;
+ }
+
+ /**
+ * Get the array holding beta information
+ * @return an array of integers, whose meaning depends on the node kind. For elements it is a pointer
+ * to the first namespace declaration
+ */
+
+ public int[] getBetaArray() {
+ return beta;
+ }
+
+ /**
+ * Get the character buffer used to hold all the text data of the document
+ * @return the character buffer
+ */
+
+ public AppendableCharSequence getCharacterBuffer() {
+ //return new CharSlice(charBuffer, 0, charBufferLength);
+ return charBuffer;
+ }
+
+ /**
+ * Get the character buffer used to hold all the comment data of the document
+ * @return the character buffer used for comments
+ */
+
+ /*@Nullable*/ public CharSequence getCommentBuffer() {
+ return commentBuffer;
+ }
+
+ /**
+ * Get the array used to hold the name codes of all attributes
+ * @return an integer array; the Nth integer holds the attribute name code of attribute N
+ */
+
+ public int[] getAttributeNameCodeArray() {
+ return attCode;
+ }
+
+ /**
+ * Get the array used to hold the type codes of all attributes
+ * @return an integer array; the Nth integer holds the attribute type code of attribute N
+ */
+
+ /*@Nullable*/ public int[] getAttributeTypeCodeArray() {
+ return attTypeCode;
+ }
+
+ /**
+ * Get the array used to hold the parent pointers of all attributes
+ * @return an integer array; the Nth integer holds the pointer to the parent element of attribute N
+ */
+
+ public int[] getAttributeParentArray() {
+ return attParent;
+ }
+
+ /**
+ * Get the array used to hold the name codes of all attributes
+ * @return an array of strings; the Nth string holds the string value of attribute N
+ */
+
+ public CharSequence[] getAttributeValueArray() {
+ return attValue;
+ }
+
+ /**
+ * Get the array used to hold the namespace codes of namespace declarations
+ * @return an array of integer namespace codes
+ */
+
+ public NamespaceBinding[] getNamespaceCodeArray() {
+ return namespaceBinding;
+ }
+
+ /**
+ * Get the array used to hold the parent pointers of all namespace declarations
+ * @return an integer array; the Nth integer holds the pointer to the parent element of namespace N
+ */
+
+ public int[] getNamespaceParentArray() {
+ return namespaceParent;
+ }
+
+
+}
+
diff --git a/sf/saxon/tree/tiny/TinyTreeEventIterator.java b/sf/saxon/tree/tiny/TinyTreeEventIterator.java
new file mode 100644
index 0000000..41d4caa
--- /dev/null
+++ b/sf/saxon/tree/tiny/TinyTreeEventIterator.java
@@ -0,0 +1,191 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.evpull.*;
+import net.sf.saxon.om.CodedName;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.NamespaceIterator;
+import net.sf.saxon.type.Type;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * This implementation of the Saxon event-pull interface starts from a document, element,
+ * text, comment, or processing-instruction node in a TinyTree,
+ * and returns the events corresponding to that node and its descendants (including
+ * their attributes and namespaces). The class performs the same function as
+ * the general-purpose {@link net.sf.saxon.evpull.Decomposer} class, but is
+ * specialized to exploit the TinyTree data structure: in particular, it never
+ * materializes any Node objects.
+ */
+
+public class TinyTreeEventIterator implements EventIterator, LocationProvider {
+
+ private int startNodeNr;
+ private int currentNodeNr;
+ private int pendingEndEvents = 0;
+ private boolean startAtDocument = false;
+ private TinyTree tree;
+ private PipelineConfiguration pipe;
+ /*@NotNull*/ private NamespaceBinding[] nsBuffer = new NamespaceBinding[10];
+
+ /**
+ * Create a TinyTreeEventIterator to return events associated with a tree or subtree
+ * @param startNode the root of the tree or subtree. Must be a document or element node.
+ * @param pipe the Saxon pipeline configuration
+ * @throws IllegalArgumentException if the start node is an attribute or namespace node.
+ */
+
+ public TinyTreeEventIterator(/*@NotNull*/ TinyNodeImpl startNode, PipelineConfiguration pipe) {
+ this.pipe = pipe;
+ this.pipe.setLocationProvider(this);
+ int kind = startNode.getNodeKind();
+ if (kind != Type.DOCUMENT && kind != Type.ELEMENT) {
+ throw new IllegalArgumentException("TinyTreeEventIterator must start at a document or element node");
+ }
+ startNodeNr = startNode.nodeNr;
+ currentNodeNr = startNodeNr;
+ tree = startNode.tree;
+ pendingEndEvents = 0;
+ startAtDocument = (kind == Type.DOCUMENT);
+ }
+
+ /**
+ * Get the next event
+ * @return a PullEvent object representing the next event, or null when the sequence is exhausted
+ */
+
+ /*@Nullable*/ public PullEvent next() throws XPathException {
+
+ if (startNodeNr < 0) {
+ // this is a signal that we've finished
+ return null;
+ }
+
+ int thisDepth = tree.depth[currentNodeNr];
+ boolean lastNode = currentNodeNr + 1 >= tree.numberOfNodes;
+ int nextDepth = (lastNode ? 0 : tree.depth[currentNodeNr+1]);
+ if (nextDepth < tree.depth[startNodeNr]) {
+ nextDepth = tree.depth[startNodeNr];
+ }
+
+ boolean atEnd = (thisDepth <= tree.depth[startNodeNr] && currentNodeNr != startNodeNr);
+
+ if (atEnd && pendingEndEvents == 1) {
+ pendingEndEvents--;
+ startNodeNr = -1;
+ if (startAtDocument) {
+ return EndDocumentEvent.getInstance();
+ } else {
+ //return null;
+ return EndElementEvent.getInstance();
+ }
+ }
+
+ if (pendingEndEvents > 0) {
+ pendingEndEvents--;
+ return EndElementEvent.getInstance();
+ }
+
+ byte kind = tree.nodeKind[currentNodeNr];
+ switch (kind) {
+ case Type.DOCUMENT:
+ pendingEndEvents = thisDepth - nextDepth + 1;
+ currentNodeNr++;
+ return StartDocumentEvent.getInstance();
+ case Type.ELEMENT:
+ pendingEndEvents = thisDepth - nextDepth + 1;
+ StartElementEvent see = new StartElementEvent(pipe);
+ see.setElementName(new CodedName(tree.nameCode[currentNodeNr], tree.getNamePool()));
+ see.setTypeCode(tree.getConfiguration().getSchemaType(tree.getTypeAnnotation(currentNodeNr)));
+ see.setLocationId(currentNodeNr);
+ // add the attributes
+ int index = tree.alpha[currentNodeNr];
+ if (index >= 0) {
+ while (index < tree.numberOfAttributes && tree.attParent[index] == currentNodeNr) {
+ see.addAttribute(tree.getAttributeNode(index++));
+ }
+ }
+ if (currentNodeNr == startNodeNr) {
+ // get all inscope namespaces for a top-level element in the sequence.
+ List<NamespaceBinding> list = new ArrayList<NamespaceBinding>();
+ Iterator<NamespaceBinding> iter = NamespaceIterator.iterateNamespaces(tree.getNode(currentNodeNr));
+ while (iter.hasNext()) {
+ list.add(iter.next());
+ }
+ see.setLocalNamespaces(list.toArray(new NamespaceBinding[list.size()]));
+ } else {
+ // only namespace declarations (and undeclarations) on this element are required
+ see.setLocalNamespaces(TinyElementImpl.getDeclaredNamespaces(tree, currentNodeNr, nsBuffer));
+ }
+ currentNodeNr++;
+ return see;
+
+ case Type.TEXT:
+ case Type.WHITESPACE_TEXT:
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ pendingEndEvents = thisDepth - nextDepth;
+ return tree.getNode(currentNodeNr++);
+ case Type.PARENT_POINTER:
+ currentNodeNr++;
+ return next();
+ default:
+ throw new IllegalStateException("Unknown node kind " + tree.nodeKind[currentNodeNr]);
+ }
+
+ }
+
+ /**
+ * Determine whether the EventIterator returns a flat sequence of events, or whether it can return
+ * nested event iterators
+ *
+ * @return true if the next() method is guaranteed never to return an EventIterator
+ */
+
+ public boolean isFlatSequence() {
+ return true;
+ }
+
+ /**
+ * Get location information: the system Id of the current start element event
+ * @param locationId in this case, the node number in the tiny tree
+ * @return the system Id of the node: that is its base URI, before taking xml:base into account
+ */
+
+ /*@Nullable*/ public String getSystemId(long locationId) {
+ return tree.getSystemId((int)locationId);
+ }
+
+ /**
+ * Get location information: the line number of the current start element event
+ * @param locationId in this case, the node number in the tiny tree
+ * @return the line number of the node if known, or -1 otherwise
+ */
+
+ public int getLineNumber(long locationId) {
+ return tree.getLineNumber((int)locationId);
+ }
+
+ /**
+ * Get location information: the column number of the current start element event
+ * @param locationId in this case, the node number in the tiny tree
+ * @return the column number of the node if known, or -1 otherwise
+ */
+
+ public int getColumnNumber(long locationId) {
+ return tree.getColumnNumber((int)locationId);
+ }
+}
+
diff --git a/sf/saxon/tree/tiny/WhitespaceTextImpl.java b/sf/saxon/tree/tiny/WhitespaceTextImpl.java
new file mode 100644
index 0000000..7601824
--- /dev/null
+++ b/sf/saxon/tree/tiny/WhitespaceTextImpl.java
@@ -0,0 +1,124 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.tiny;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+/**
+ * A node in the XML parse tree representing a text node with compressed whitespace content
+ * @author Michael H. Kay
+ */
+
+public final class WhitespaceTextImpl extends TinyNodeImpl {
+
+ /**
+ * Create a compressed whitespace text node
+ * @param tree the tree to contain the node
+ * @param nodeNr the internal node number
+ */
+
+ public WhitespaceTextImpl(TinyTree tree, int nodeNr) {
+ this.tree = tree;
+ this.nodeNr = nodeNr;
+ }
+
+ /**
+ * Return the character value of the node.
+ * @return the string value of the node
+ */
+
+ public String getStringValue() {
+ return getStringValueCS().toString();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String. For a WhitespaceTextImpl node, it avoids the
+ * cost of decompressing the whitespace
+ */
+
+ public CharSequence getStringValueCS() {
+ long value = ((long)tree.alpha[nodeNr]<<32) | ((long)tree.beta[nodeNr] & 0xffffffffL);
+ return new CompressedWhitespace(value);
+ }
+
+ /**
+ * Static method to get the string value of a text node without first constructing the node object
+ * @param tree the tree
+ * @param nodeNr the node number of the text node
+ * @return the string value of the text node
+ */
+
+ public static CharSequence getStringValueCS(TinyTree tree, int nodeNr) {
+ long value = ((long)tree.alpha[nodeNr]<<32) | ((long)tree.beta[nodeNr] & 0xffffffffL);
+ return new CompressedWhitespace(value);
+ }
+
+ /**
+ * Static method to get the string value of a text node and append it to a supplied buffer
+ * without first constructing the node object
+ * @param tree the tree
+ * @param nodeNr the node number of the text node
+ * @param buffer a buffer to which the string value will be appended
+ */
+
+ public static void appendStringValue(TinyTree tree, int nodeNr, FastStringBuffer buffer) {
+ long value = ((long)tree.alpha[nodeNr]<<32) | ((long)tree.beta[nodeNr] & 0xffffffffL);
+ CompressedWhitespace.uncompress(value, buffer);
+ }
+
+ /**
+ * Get the typed value. The result of this method will always be consistent with the method
+ * {@link net.sf.saxon.om.Item#getTypedValue()}. However, this method is often more convenient and may be
+ * more efficient, especially in the common case where the value is expected to be a singleton.
+ * @return the typed value. This will either be a single AtomicValue or a Value whose items are
+ * atomic values.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize() throws XPathException {
+ return new UntypedAtomicValue(getStringValueCS());
+ }
+
+ /**
+ * Static method to get the "long" value representing the content of a whitespace text node
+ * @param tree the TinyTree
+ * @param nodeNr the internal node number
+ * @return a value representing the compressed whitespace content
+ * @see CompressedWhitespace
+ */
+
+ public static long getLongValue(TinyTree tree, int nodeNr) {
+ return ((long)tree.alpha[nodeNr]<<32) | ((long)tree.beta[nodeNr] & 0xffffffffL);
+ }
+
+ /**
+ * Return the type of node.
+ * @return Type.TEXT
+ */
+
+ public final int getNodeKind() {
+ return Type.TEXT;
+ }
+
+ /**
+ * Copy this node to a given outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ out.characters(getStringValueCS(), 0, 0);
+ }
+
+
+}
+
diff --git a/sf/saxon/tree/tiny/package.html b/sf/saxon/tree/tiny/package.html
new file mode 100644
index 0000000..0790a89
--- /dev/null
+++ b/sf/saxon/tree/tiny/package.html
@@ -0,0 +1,83 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.tinytree</title>
+</head>
+
+<body>
+
+<p>This package is an implementation of the Saxon internal tree structure,
+designed to minimize memory usage, and the costs of allocating and garbage-collecting
+Java objects.</p>
+
+<p>The data structure consists of a set of arrays, held in the <code>TinyTree</code> object.
+A <code>TinyTree</code> represents one or more root document or element nodes, together with their
+subtrees. If there is more than one root node, these will often be members of a sequence, but
+this is not essential and is never assumed.
+The arrays are in three groups. </p>
+
+<p>The principal group of arrays contain one entry for each node other
+than namespace and attribute nodes. These arrays are in document order. The following
+information is maintained for each node: the
+depth in the tree, the name code, the index of the next sibling, and two fields labelled "alpha" and "beta".
+ The meaning of "alpha" and
+"beat" depends on the node type. For text nodes, comment nodes, and processing instructions
+these fields index into a StringBuffer holding the text. For element nodes, "alpha" is
+an index into the attributes table, and "beta" is an offset into the namespaces table.
+Either of these may be set to -1 if there are no attributes/namespaces.</p>
+
+<p>A name code is an integer value that indexes into the NamePool object: it can be
+used to determine the prefix, local name, or namespace URI of an element or attribute name.</p>
+
+<p>The attribute group holds the following information for each attribute node: parent
+element, prefix, name code, attribute type, and attribute value. Attributes
+for the same element are adjacent.</p>
+
+<p>The namespace group holds one entry per namespace declaration (not one per namespace
+node). The following information is held: a pointer to the element on which the namespace
+was declared, and a namespace code. A namespace code is an integer, which the NamePool can
+resolve into a prefix and a namespace URI: the top 16 bits identify the prefix, the bottom
+16 bits the URI.</p>
+
+<p>The data structure contains no Java object references: the links between elements and
+attributes/namespaces are all held as integer offsets. This reduces size, and also makes
+the whole structure relocatable (though this capability is not currently exploited).
+ All navigation is done by serial traversal of the arrays, using
+the node depth as a guide. An array of pointers to the preceding sibling is created on
+demand, the first time that backwards navigation is attempted. There are no parent pointers;
+Saxon attempts to remember the parent while navigating down the tree, and where this is not
+possible it locates the parent by searching through the following siblings; the last sibling
+points back to the parent. The absence of the other pointers is a trade-off between tree-building time and
+transformation time: I found that in most cases, more time was spent creating these pointers
+than actually using them. Occasionally, however, in trees with a very large fan-out, locating
+ancestors can be slow.</p>
+
+<p>When the tree is navigated, transient ("flyweight") nodes are created as Java objects.
+These disappear as soon as they are no longer needed. Note that to compare two nodes for
+identity, you can use either the isSameNode() method, or compare the results of
+generateId(). Comparing the Java objects using "==" is incorrect.</p>
+
+<p>The tree structure implements the DOM interface as well as the Saxon NodeInfo interface.
+There are limitations in the DOM support, however: especially (a) the tree is immutable, so
+all updating methods throw an exception; (b) namespace declarations are not exposed as
+attributes, and (c) only the core DOM classes are provided.</p>
+
+<p>The primary way of navigating the tree is through the XPath axes, accessible through the
+iterateAxis() method. The familiar DOM methods such as getNextSibling() and getFirstChild()
+are not provided as an intrinsic part of the <code>NodeInfo</code> interface: all navigation
+is done by iterating the axes, and each tree model provides its own implementations of the axes.
+However, there are helper methods in the shared <code>Navigator</code> class which many
+of these implementations choose to use.</p>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/tree/util/AttributeCollectionImpl.java b/sf/saxon/tree/util/AttributeCollectionImpl.java
new file mode 100644
index 0000000..f4fdf61
--- /dev/null
+++ b/sf/saxon/tree/util/AttributeCollectionImpl.java
@@ -0,0 +1,807 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.LocationProvider;
+import net.sf.saxon.event.ReceiverOptions;
+import net.sf.saxon.om.*;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SimpleType;
+import org.xml.sax.Attributes;
+
+import java.util.Arrays;
+
+
+/**
+ * AttributeCollectionImpl is an implementation of both the SAX2 interface Attributes
+ * and the Saxon equivalent AttributeCollection.
+ * <p/>
+ * <p>As well as providing the information required by the SAX2 interface, an
+ * AttributeCollection can hold type information (as needed to support the JAXP 1.3
+ * {@link javax.xml.validation.ValidatorHandler} interface), and location information
+ * for debugging. The location information is used in the case of attributes on a result
+ * tree to identify the location in the query or stylesheet from which they were
+ * generated.
+ */
+
+public class AttributeCollectionImpl implements Attributes, AttributeCollection {
+
+ private Configuration config;
+ private LocationProvider locationProvider;
+ // Following fields can be null ONLY if used==0. We avoid allocating the arrays for the common
+ // case of an empty attribute collection.
+ /*@Nullable*/ private NodeName[] names = null;
+ /*@Nullable*/ private String[] values = null;
+ /*@Nullable*/ private long[] codes = null;
+ // locationId in top half, properties in bottom half
+ private int used = 0;
+ // the types array can be null even if used>0; this indicates that all attributes are untyped
+ /*@Nullable*/ private SimpleType[] types = null;
+
+ // Empty attribute collection. The caller is trusted not to try and modify it.
+
+ /*@NotNull*/ public static AttributeCollectionImpl EMPTY_ATTRIBUTE_COLLECTION =
+ new AttributeCollectionImpl((Configuration) null);
+
+ /**
+ * Create an empty attribute list.
+ *
+ * @param config the Saxon Configuration
+ */
+
+ public AttributeCollectionImpl(Configuration config) {
+ this.config = config;
+ used = 0;
+ }
+
+ /**
+ * Create an attribute list as a copy of an existing attribute list
+ *
+ * @param atts the existing attribute list to be copied
+ * @return the copied attribute list. Note that if the original attribute list
+ * is empty, the method returns the singleton object {@link #EMPTY_ATTRIBUTE_COLLECTION};
+ * this case must therefore be handled specially if the returned attribute list is to
+ * be modified.
+ */
+
+ /*@Nullable*/ public static AttributeCollectionImpl copy(/*@NotNull*/ AttributeCollectionImpl atts) {
+ if (atts.getLength() == 0) {
+ return EMPTY_ATTRIBUTE_COLLECTION;
+ }
+ AttributeCollectionImpl t = new AttributeCollectionImpl(atts.config);
+ t.used = atts.used;
+ t.locationProvider = atts.locationProvider;
+ t.names = new NodeName[atts.used];
+ t.values = new String[atts.used];
+ t.codes = new long[atts.used];
+ System.arraycopy(atts.names, 0, t.names, 0, atts.used);
+ System.arraycopy(atts.values, 0, t.values, 0, atts.used);
+ System.arraycopy(atts.codes, 0, t.codes, 0, atts.used);
+ if (atts.types != null) {
+ t.types = new SimpleType[atts.used];
+ System.arraycopy(atts.types, 0, t.types, 0, atts.used);
+ }
+ return t;
+ }
+
+ /**
+ * Set the location provider. This must be set if the methods getSystemId() and getLineNumber()
+ * are to be used to get location information for an attribute.
+ *
+ * @param provider the location provider
+ */
+
+ public void setLocationProvider(LocationProvider provider) {
+ locationProvider = provider;
+ }
+
+ /**
+ * Add an attribute to an attribute list. The parameters correspond
+ * to the parameters of the {@link net.sf.saxon.event.Receiver#attribute(net.sf.saxon.om.NodeName, net.sf.saxon.type.SimpleType, CharSequence, int, int)}
+ * method. There is no check that the name of the attribute is distinct from other attributes
+ * already in the collection: this check must be made by the caller.
+ *
+ * @param nodeName Object representing the attribute name.
+ * @param type The attribute type
+ * @param value The attribute value (must not be null)
+ * @param locationId Identifies the attribute location.
+ * @param properties Attribute properties
+ */
+
+ public void addAttribute(NodeName nodeName, SimpleType type, String value, int locationId, int properties) {
+ if (values == null) {
+ names = new NodeName[5];
+ values = new String[5];
+ codes = new long[5];
+ if (!type.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ types = new SimpleType[5];
+ }
+ used = 0;
+ }
+ if (values.length == used) {
+ int newsize = (used == 0 ? 5 : used * 2);
+ NodeName[] n2 = new NodeName[newsize];
+ String[] v2 = new String[newsize];
+ long[] c2 = new long[newsize];
+ System.arraycopy(names, 0, n2, 0, used);
+ System.arraycopy(values, 0, v2, 0, used);
+ System.arraycopy(codes, 0, c2, 0, used);
+ names = n2;
+ values = v2;
+ codes = c2;
+ if (types != null) {
+ SimpleType[] t2 = new SimpleType[newsize];
+ System.arraycopy(types, 0, t2, 0, used);
+ types = t2;
+ }
+ }
+ int n = used;
+ names[n] = nodeName;
+ codes[n] = ((long)locationId << 32) | (long)properties;
+ setTypeAnnotation(n, type);
+ values[used++] = value;
+ }
+
+ /**
+ * Set (overwrite) an attribute in the attribute list. The parameters correspond
+ * to the parameters of the {@link net.sf.saxon.event.Receiver#attribute(net.sf.saxon.om.NodeName, net.sf.saxon.type.SimpleType, CharSequence, int, int)}
+ * method.
+ *
+ * @param index Identifies the entry to be replaced. Must be in range (nasty things happen if not)
+ * @param nodeName representing the attribute name.
+ * @param type The attribute type code
+ * @param value The attribute value (must not be null)
+ * @param locationId Identifies the attribtue location.
+ * @param properties Attribute properties
+ */
+
+ public void setAttribute(int index, NodeName nodeName, SimpleType type, String value, int locationId, int properties) {
+ names[index] = nodeName;
+ codes[index] = ((long)locationId << 32) | (long)properties;
+ setTypeAnnotation(index, type);
+ values[used++] = value;
+ values[index] = value;
+ }
+
+
+ /**
+ * Clear the attribute list. This removes the values but doesn't free the memory used.
+ * free the memory, use clear() then compact().
+ */
+
+ public void clear() {
+ used = 0;
+ }
+
+ /**
+ * Compact the attribute list to avoid wasting memory
+ */
+
+ public void compact() {
+ if (used == 0) {
+ codes = null;
+ values = null;
+ } else if (values != null && values.length > used) {
+ NodeName[] n2 = new NodeName[used];
+ String[] v2 = new String[used];
+ long[] c2 = new long[used];
+ System.arraycopy(names, 0, n2, 0, used);
+ System.arraycopy(values, 0, v2, 0, used);
+ System.arraycopy(codes, 0, c2, 0, used);
+ values = v2;
+ codes = c2;
+ names = n2;
+ if (types != null) {
+ SimpleType[] t2 = new SimpleType[used];
+ System.arraycopy(types, 0, t2, 0, used);
+ types = t2;
+ }
+ }
+ }
+
+ /**
+ * Return the number of attributes in the list.
+ *
+ * @return The number of attributes that have been created in this attribute collection. This is the number
+ * of slots used in the list, including any slots allocated to attributes that have since been deleted.
+ * Such slots are not reused, to preserve attribute identity.
+ */
+
+ public int getLength() {
+ return (values == null ? 0 : used);
+ }
+
+ /**
+ * Get the namecode of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The display name of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ public int getNameCode(int index) {
+ if (names == null) {
+ return -1;
+ }
+ if (index < 0 || index >= used) {
+ return -1;
+ }
+
+ return names[index].allocateNameCode(config.getNamePool());
+ }
+
+ /**
+ * Get the node name of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The node name, as a NodeName object, or null if there is no name at this index
+ */
+
+ /*@Nullable*/ public NodeName getNodeName(int index) {
+ if (names == null) {
+ return null;
+ }
+ if (index < 0 || index >= used) {
+ return null;
+ }
+
+ return names[index];
+ }
+
+
+ /**
+ * Get the type of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The type annotation
+ */
+
+ public SimpleType getTypeAnnotation(int index) {
+ if (types == null) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ if (index < 0 || index >= used) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+
+ return types[index];
+ }
+
+ /**
+ * Get the locationID of an attribute (by position)
+ *
+ * @param index The position of the attribute in the list.
+ * @return The location identifier of the attribute. This can be supplied
+ * to a {@link net.sf.saxon.event.LocationProvider} in order to obtain the
+ * actual system identifier and line number of the relevant location
+ */
+
+ public int getLocationId(int index) {
+ if (codes == null) {
+ return -1;
+ }
+ if (index < 0 || index >= used) {
+ return -1;
+ }
+
+ return (int)(codes[index]>>32);
+ }
+
+ /**
+ * Get the systemId part of the location of an attribute, at a given index.
+ * <p/>
+ * <p>Attribute location information is not available from a SAX parser, so this method
+ * is not useful for getting the location of an attribute in a source document. However,
+ * in a Saxon result document, the location information represents the location in the
+ * stylesheet of the instruction used to generate this attribute, which is useful for
+ * debugging.</p>
+ *
+ * @param index the required attribute
+ * @return the systemId of the location of the attribute
+ */
+
+ public String getSystemId(int index) {
+ return locationProvider.getSystemId(getLocationId(index));
+ }
+
+ /**
+ * Get the line number part of the location of an attribute, at a given index.
+ * <p/>
+ * <p>Attribute location information is not available from a SAX parser, so this method
+ * is not useful for getting the location of an attribute in a source document. However,
+ * in a Saxon result document, the location information represents the location in the
+ * stylesheet of the instruction used to generate this attribute, which is useful for
+ * debugging.</p>
+ *
+ * @param index the required attribute
+ * @return the line number of the location of the attribute
+ */
+
+ public int getLineNumber(int index) {
+ return locationProvider.getLineNumber(getLocationId(index));
+ }
+
+ /**
+ * Get the properties of an attribute (by position)
+ *
+ * @param index The position of the attribute in the list.
+ * @return The properties of the attribute. This is a set
+ * of bit-settings defined in class {@link net.sf.saxon.event.ReceiverOptions}. The
+ * most interesting of these is {{@link net.sf.saxon.event.ReceiverOptions#DEFAULTED_ATTRIBUTE},
+ * which indicates an attribute that was added to an element as a result of schema validation.
+ */
+
+ public int getProperties(int index) {
+ if (codes == null) {
+ return -1;
+ }
+ if (index < 0 || index >= used) {
+ return -1;
+ }
+
+ return (int)codes[index];
+ }
+
+ /**
+ * Get the prefix of the name of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The prefix of the attribute name as a string, or null if there
+ * is no attribute at that position. Returns "" for an attribute that
+ * has no prefix.
+ */
+
+ /*@Nullable*/ public String getPrefix(int index) {
+ if (names == null) {
+ return null;
+ }
+ if (index < 0 || index >= used) {
+ return null;
+ }
+ return names[index].getPrefix();
+ }
+
+ /**
+ * Get the lexical QName of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The lexical QName of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ /*@Nullable*/ public String getQName(int index) {
+ if (names == null) {
+ return null;
+ }
+ if (index < 0 || index >= used) {
+ return null;
+ }
+ return names[index].getDisplayName();
+ }
+
+ /**
+ * Get the local name of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The local name of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ /*@Nullable*/ public String getLocalName(int index) {
+ if (names == null) {
+ return null;
+ }
+ if (index < 0 || index >= used) {
+ return null;
+ }
+ return names[index].getLocalPart();
+ }
+
+ /**
+ * Get the namespace URI of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The local name of the attribute as a string, or null if there
+ * is no attribute at that position.
+ */
+
+ /*@Nullable*/ public String getURI(int index) {
+ if (names == null) {
+ return null;
+ }
+ if (index < 0 || index >= used) {
+ return null;
+ }
+ return names[index].getURI();
+ }
+
+
+ /**
+ * Get the type of an attribute (by position). This is a SAX2 method,
+ * so it gets the type name as a DTD attribute type, mapped from the
+ * schema type code.
+ *
+ * @param index The position of the attribute in the list.
+ * @return The attribute type as a string ("NMTOKEN" for an
+ * enumeration, and "CDATA" if no declaration was
+ * read), or null if there is no attribute at
+ * that position.
+ */
+
+ /*@NotNull*/ public String getType(int index) {
+ int typeCode = getTypeAnnotation(index).getFingerprint();
+ switch (typeCode) {
+ case StandardNames.XS_ID:
+ return "ID";
+ case StandardNames.XS_IDREF:
+ return "IDREF";
+ case StandardNames.XS_NMTOKEN:
+ return "NMTOKEN";
+ case StandardNames.XS_ENTITY:
+ return "ENTITY";
+ case StandardNames.XS_IDREFS:
+ return "IDREFS";
+ case StandardNames.XS_NMTOKENS:
+ return "NMTOKENS";
+ case StandardNames.XS_ENTITIES:
+ return "ENTITIES";
+ default:
+ return "CDATA";
+ }
+ }
+
+ /**
+ * Get the type of an attribute (by name).
+ *
+ * @param uri The namespace uri of the attribute.
+ * @param localname The local name of the attribute.
+ * @return The index position of the attribute
+ */
+
+ /*@Nullable*/ public String getType(String uri, String localname) {
+ int index = findByName(uri, localname);
+ return (index < 0 ? null : getType(index));
+ }
+
+ /**
+ * Get the value of an attribute (by position).
+ *
+ * @param index The position of the attribute in the list.
+ * @return The attribute value as a string, or null if
+ * there is no attribute at that position.
+ */
+
+ /*@Nullable*/ public String getValue(int index) {
+ if (values == null) {
+ return null;
+ }
+ if (index < 0 || index >= used) {
+ return null;
+ }
+ return values[index];
+ }
+
+ /**
+ * Get the value of an attribute (by name).
+ *
+ * @param uri The namespace uri of the attribute.
+ * @param localname The local name of the attribute.
+ * @return The index position of the attribute
+ */
+
+ /*@Nullable*/ public String getValue(String uri, String localname) {
+ int index = findByName(uri, localname);
+ return (index < 0 ? null : getValue(index));
+ }
+
+ /**
+ * Get the attribute value using its fingerprint
+ */
+
+ /*@Nullable*/ public String getValueByFingerprint(int fingerprint) {
+ int index = findByFingerprint(fingerprint);
+ return (index < 0 ? null : getValue(index));
+ }
+
+ /**
+ * Get the index of an attribute, from its lexical QName
+ *
+ * @param qname The lexical QName of the attribute. The prefix must match.
+ * @return The index position of the attribute
+ */
+
+ public int getIndex(/*@NotNull*/ String qname) {
+ if (names == null) {
+ return -1;
+ }
+ if (qname.indexOf(':') < 0) {
+ return findByName("", qname);
+ }
+ // Searching using prefix+localname is not recommended, but SAX allows it...
+ String[] parts;
+ try {
+ parts = Name11Checker.getInstance().getQNameParts(qname);
+ } catch (QNameException err) {
+ return -1;
+ }
+ String prefix = parts[0];
+ if (prefix.length() == 0) {
+ return findByName("", qname);
+ } else {
+ String localName = parts[1];
+ for (int i = 0; i < used; i++) {
+ if (names[i] != null) {
+ String lname = names[i].getLocalPart();
+ String ppref = names[i].getPrefix();
+ if (localName.equals(lname) && prefix.equals(ppref)) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+ }
+
+ /**
+ * Get the index of an attribute (by name).
+ *
+ * @param uri The namespace uri of the attribute.
+ * @param localname The local name of the attribute.
+ * @return The index position of the attribute
+ */
+
+ public int getIndex(String uri, String localname) {
+ return findByName(uri, localname);
+ }
+
+ /**
+ * Get the index, given the fingerprint.
+ * Return -1 if not found.
+ */
+
+ public int getIndexByFingerprint(int fingerprint) {
+ return findByFingerprint(fingerprint);
+ }
+
+ /**
+ * Get the type of an attribute (by lexical QName).
+ *
+ * @param name The lexical QName of the attribute.
+ * @return The attribute type as a string (e.g. "NMTOKEN", or
+ * "CDATA" if no declaration was read).
+ */
+
+ /*@NotNull*/ public String getType(/*@NotNull*/ String name) {
+ int index = getIndex(name);
+ return getType(index);
+ }
+
+
+ /**
+ * Get the value of an attribute (by lexnical QName).
+ *
+ * @param name The attribute name (a lexical QName).
+ * The prefix must match the prefix originally used. This method is defined in SAX, but is
+ * not recommended except where the prefix is null.
+ */
+
+ /*@Nullable*/ public String getValue(/*@NotNull*/ String name) {
+ int index = getIndex(name);
+ return getValue(index);
+ }
+
+ /**
+ * Ask whether the attribute collection contains any attributes
+ * in a specified namespace
+ * @param uri the specified namespace
+ * @return true if there are one or more attributes in this namespace
+ */
+
+ public boolean hasAttributeInNamespace(String uri) {
+ if (names == null || config == null) {
+ return false; // indicates an empty attribute set
+ }
+ for (int i = 0; i < used; i++) {
+ if (names[i] != null && names[i].isInNamespace(uri)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Find an attribute by expanded name
+ *
+ * @param uri the namespace uri
+ * @param localName the local name
+ * @return the index of the attribute, or -1 if absent
+ */
+
+ private int findByName(String uri, String localName) {
+ if (names == null || config == null) {
+ return -1; // indicates an empty attribute set
+ }
+ for (int i = 0; i < used; i++) {
+ if (names[i] != null && names[i].isInNamespace(uri) && localName.equals(names[i].getLocalPart())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Find an attribute by fingerprint
+ *
+ * @param fingerprint the fingerprint representing the name of the required attribute
+ * @return the index of the attribute, or -1 if absent
+ */
+
+ private int findByFingerprint(int fingerprint) {
+ if (names == null || config == null) {
+ return -1;
+ }
+ NamePool pool = config.getNamePool();
+ for (int i = 0; i < used; i++) {
+ NodeName nn = names[i];
+ if (nn != null) {
+ if (nn.hasFingerprint()) {
+ if (nn.getFingerprint() == fingerprint) {
+ return i;
+ }
+ } else {
+ if (nn.isInNamespace(pool.getURI(fingerprint)) && nn.getLocalPart().equals(pool.getLocalName(fingerprint))) {
+ return i;
+ }
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Find an attribute by node name
+ *
+ * @param nodeName the name of the required attribute
+ * @return the index of the attribute, or -1 if absent
+ */
+
+ public int findByNodeName(/*@NotNull*/ NodeName nodeName) {
+ if (codes == null || config == null) {
+ return -1;
+ }
+ if (nodeName.hasFingerprint()) {
+ return findByFingerprint(nodeName.getFingerprint());
+ } else {
+ return findByName(nodeName.getURI(), nodeName.getLocalPart());
+ }
+ }
+
+
+ /**
+ * Determine whether a given attribute has the is-ID property set
+ */
+
+ public boolean isId(int index) {
+ return
+ (!isDeleted(index)) &&
+ (StandardNames.XML_ID_NAME.equals(names[index]) ||
+ (getProperties(index)& ReceiverOptions.IS_ID) != 0 ||
+ config.getTypeHierarchy().isIdCode(getTypeAnnotation(index).getFingerprint()));
+ }
+
+ /**
+ * Determine whether a given attribute has the is-idref property set
+ */
+
+ public boolean isIdref(int index) {
+ return config.getTypeHierarchy().isIdrefsCode(getTypeAnnotation(index).getFingerprint());
+ }
+
+ /**
+ * Delete the attribute at a given index position. Note that the index position will not be reused, to ensure
+ * that any new attributes added to the element have a distinct identity. Instead, the slot occupied
+ * by the attribute is nilled out.
+ *
+ * @param index The index position of the attribute to be removed
+ */
+
+ public void removeAttribute(int index) {
+ names[index] = null;
+ codes[index] = -1L;
+ values[index] = null;
+ if (types != null) {
+ types[index] = null;
+ }
+ }
+
+ /**
+ * Test whether the attribute at a given index has been deleted
+ *
+ * @param index the index position of the (ex-) attribute
+ * @return true if the attribute has been deleted
+ */
+
+ public boolean isDeleted(int index) {
+ return names[index] == null;
+ }
+
+ /**
+ * Rename an attribute
+ *
+ * @param index the index position of the attribute
+ * @param newName the namecode of the new name
+ */
+
+ public void renameAttribute(int index, NodeName newName) {
+ names[index] = newName;
+ if (types != null) {
+ types[index] = BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ }
+
+ /**
+ * Replace the value of an attribute
+ *
+ * @param index position of the attribute
+ * @param newValue the new string value of the attribute
+ */
+
+ public void replaceAttribute(int index, /*@NotNull*/ CharSequence newValue) {
+ values[index] = newValue.toString();
+ }
+
+ /**
+ * Set the type annotation of an attribute
+ *
+ * @param index the index position of the attribute node
+ * @param type the new type for the attribute
+ */
+
+ public void setTypeAnnotation(int index, SimpleType type) {
+ if (type.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ if (types != null) {
+ types[index] = type;
+ }
+ } else {
+ if (types == null) {
+ types = new SimpleType[names.length];
+ Arrays.fill(types, BuiltInAtomicType.UNTYPED_ATOMIC);
+ types[index] = type;
+ } else {
+ types[index] = type;
+ }
+ }
+ }
+
+ /**
+ * Swap two attributes (used for sorting)
+ * @param i the position of one value
+ * @param j the position of the other value
+ */
+
+ public void swap(int i, int j) {
+ NodeName n = names[i];
+ names[i] = names[j];
+ names[j] = n;
+ if (types != null) {
+ SimpleType s = types[i];
+ types[i] = types[j];
+ types[j] = s;
+ }
+ String c = values[i];
+ values[i] = values[j];
+ values[j] = c;
+ long p = codes[i];
+ codes[i] = codes[j];
+ codes[j] = p;
+ }
+
+
+}
+
diff --git a/sf/saxon/tree/util/DiagnosticNamePool.java b/sf/saxon/tree/util/DiagnosticNamePool.java
new file mode 100644
index 0000000..10308b5
--- /dev/null
+++ b/sf/saxon/tree/util/DiagnosticNamePool.java
@@ -0,0 +1,34 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+import net.sf.saxon.om.NamePool;
+
+import java.io.PrintStream;
+
+/**
+ * This class provides a diagnostic wrapper for the real NamePool. There are no formal interfaces
+ * to exploit it, but it can be patched into a system by use of setNamePool() on the Configuration,
+ * and its effect is to trace entry to selected methods, notably those that are synchronized, for
+ * diagnostic analysis.
+ */
+public class DiagnosticNamePool extends NamePool {
+
+ public PrintStream printStream = System.err;
+
+ public synchronized short allocateCodeForURI(String uri) {
+ printStream.println("allocateCodeForURI");
+ return super.allocateCodeForURI(uri);
+ }
+
+ public synchronized int allocate(String prefix, String uri, String localName) {
+ printStream.println("allocate " + prefix + " : " + uri + " : " + localName);
+ return super.allocate(prefix, uri, localName);
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/tree/util/DocumentNumberAllocator.java b/sf/saxon/tree/util/DocumentNumberAllocator.java
new file mode 100644
index 0000000..a1cb871
--- /dev/null
+++ b/sf/saxon/tree/util/DocumentNumberAllocator.java
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+import java.io.Serializable;
+
+/**
+ * This class (which has one instance per Configuration) is used to allocate unique document
+ * numbers. It's a separate class so that it can act as a monitor for synchronization
+ */
+public class DocumentNumberAllocator implements Serializable {
+
+ // Changed to a log in Saxon 9.4, because a user reported an int overflowing
+ // on a system that had been in live operation for several months. The effect wasn't fatal,
+ // but could cause incorrect node identity tests.
+
+ private long nextDocumentNumber = 0;
+
+ /**
+ * Allocate a unique document number
+ * @return a unique document number
+ */
+
+ public synchronized long allocateDocumentNumber() {
+ return nextDocumentNumber++;
+ }
+}
+
diff --git a/sf/saxon/tree/util/FastStringBuffer.java b/sf/saxon/tree/util/FastStringBuffer.java
new file mode 100644
index 0000000..30928ca
--- /dev/null
+++ b/sf/saxon/tree/util/FastStringBuffer.java
@@ -0,0 +1,559 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+import net.sf.saxon.regex.BMPString;
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.serialize.charcode.UTF16CharacterSet;
+import net.sf.saxon.tree.tiny.AppendableCharSequence;
+import net.sf.saxon.tree.tiny.CharSlice;
+import net.sf.saxon.tree.tiny.CompressedWhitespace;
+
+import java.io.Serializable;
+import java.io.Writer;
+import java.util.Arrays;
+
+/**
+ * A simple implementation of a class similar to StringBuffer. Unlike
+ * StringBuffer it is not synchronized. It also offers the capability
+ * to remove unused space. (This class could possibly be replaced by
+ * StringBuilder in JDK 1.5, but using our own class gives more control.)
+ */
+
+public final class FastStringBuffer implements AppendableCharSequence, Serializable {
+
+ public static final int TINY = 16;
+ public static final int SMALL = 64;
+ public static final int MEDIUM = 256;
+ public static final int LARGE = 1024;
+
+ private char[] array;
+ private int used = 0;
+
+ /**
+ * Create a FastStringBuffer with a given initial capacity
+ * @param initialSize the initial capacity
+ */
+
+ public FastStringBuffer(int initialSize) {
+ //System.err.println("FSB " + initialSize);
+ array = new char[initialSize];
+ }
+
+ /**
+ * Create a FastStringBuffer with initial content
+ * @param cs the initial content. The buffer is created with just enough capacity for
+ * this content (it will be expanded if more content is added later).
+ */
+
+ public FastStringBuffer(CharSequence cs) {
+ array = new char[cs.length()];
+ append(cs);
+ }
+
+ /**
+ * Append the contents of a String to the buffer
+ * @param s the String to be appended
+ */
+
+ public void append(String s) {
+ int len = s.length();
+ ensureCapacity(len);
+ s.getChars(0, len, array, used);
+ used += len;
+ }
+
+ /**
+ * Append the contents of a CharSlice to the buffer
+ * @param s the String to be appended
+ */
+
+ public void append(CharSlice s) {
+ int len = s.length();
+ ensureCapacity(len);
+ s.copyTo(array, used);
+ used += len;
+ }
+
+ /**
+ * Append the contents of a FastStringBuffer to the buffer
+ * @param s the FastStringBuffer to be appended
+ */
+
+ public void append(FastStringBuffer s) {
+ int len = s.length();
+ ensureCapacity(len);
+ s.getChars(0, len, array, used);
+ used += len;
+ }
+
+ /**
+ * Append the contents of a StringBuffer to the buffer
+ * @param s the StringBuffer to be appended
+ */
+
+ public void append(StringBuffer s) {
+ int len = s.length();
+ ensureCapacity(len);
+ s.getChars(0, len, array, used);
+ used += len;
+ }
+
+ /**
+ * Append the contents of a general CharSequence to the buffer
+ * @param s the CharSequence to be appended
+ */
+
+ public void append(CharSequence s) {
+ // Although we provide variants of this method for different subtypes, Java decides which to use based
+ // on the static type of the operand. We want to use the right method based on the dynamic type, to avoid
+ // creating objects and copying strings unnecessarily. So we do a dynamic dispatch.
+ final int len = s.length();
+ ensureCapacity(len);
+ if (s instanceof CharSlice) {
+ ((CharSlice)s).copyTo(array, used);
+ } else if (s instanceof String) {
+ ((String)s).getChars(0, len, array, used);
+ } else if (s instanceof FastStringBuffer) {
+ ((FastStringBuffer)s).getChars(0, len, array, used);
+ } else if (s instanceof CompressedWhitespace) {
+ ((CompressedWhitespace)s).uncompress(this);
+ return;
+ } else {
+ s.toString().getChars(0, len, array, used);
+ }
+ used += len;
+ }
+
+ /**
+ * Append the contents of a character array to the buffer
+ * @param srcArray the array whose contents are to be added
+ * @param start the offset of the first character in the array to be copied
+ * @param length the number of characters to be copied
+ */
+
+ public void append(char[] srcArray, int start, int length) {
+ ensureCapacity(length);
+ System.arraycopy(srcArray, start, array, used, length);
+ used += length;
+ }
+
+ /**
+ * Append the entire contents of a character array to the buffer
+ * @param srcArray the array whose contents are to be added
+ */
+
+ public void append(char[] srcArray) {
+ final int length = srcArray.length;
+ ensureCapacity(length);
+ System.arraycopy(srcArray, 0, array, used, length);
+ used += length;
+ }
+
+ /**
+ * Append a character to the buffer
+ * @param ch the character to be added
+ */
+
+ public void append(char ch) {
+ ensureCapacity(1);
+ array[used++] = ch;
+ }
+
+ /**
+ * Append a wide character to the buffer (as a surrogate pair if necessary)
+ * @param ch the character, as a 32-bit Unicode codepoint
+ */
+
+ public void appendWideChar(int ch) {
+ if (ch > 0xffff) {
+ append(UTF16CharacterSet.highSurrogate(ch));
+ append(UTF16CharacterSet.lowSurrogate(ch));
+ } else {
+ append((char)ch);
+ }
+ }
+
+ /**
+ * Append a UnicodeString to the buffer (using surrogate pairs if necessary)
+ * @param str the UnicodeString
+ */
+
+ public void append(UnicodeString str) {
+ if (str instanceof BMPString) {
+ append(((BMPString)str).getCharSequence());
+ } else {
+ for (int i=0; i<str.length(); i++) {
+ appendWideChar(str.charAt(i));
+ }
+ }
+ }
+
+ /**
+ * Prepend a wide character to the buffer (as a surrogate pair if necessary)
+ * @param ch the character, as a 32-bit Unicode codepoint
+ */
+
+ public void prependWideChar(int ch) {
+ if (ch > 0xffff) {
+ prepend(UTF16CharacterSet.lowSurrogate(ch));
+ prepend(UTF16CharacterSet.highSurrogate(ch));
+ } else {
+ prepend((char)ch);
+ }
+ }
+
+ /**
+ * Returns the length of this character sequence. The length is the number
+ * of 16-bit <code>char</code>s in the sequence.</p>
+ *
+ * @return the number of <code>char</code>s in this sequence
+ */
+ public int length() {
+ return used;
+ }
+
+ /**
+ * Returns the <code>char</code> value at the specified index. An index ranges from zero
+ * to <tt>length() - 1</tt>. The first <code>char</code> value of the sequence is at
+ * index zero, the next at index one, and so on, as for array
+ * indexing. </p>
+ * <p/>
+ * <p>If the <code>char</code> value specified by the index is a
+ * <a href="Character.html#unicode">surrogate</a>, the surrogate
+ * value is returned.
+ *
+ * @param index the index of the <code>char</code> value to be returned
+ * @return the specified <code>char</code> value
+ * @throws IndexOutOfBoundsException if the <tt>index</tt> argument is negative or not less than
+ * <tt>length()</tt>
+ */
+ public char charAt(int index) {
+ if (index >= used) {
+ throw new IndexOutOfBoundsException("" + index);
+ }
+ return array[index];
+ }
+
+ /**
+ * Returns a new <code>CharSequence</code> that is a subsequence of this sequence.
+ * The subsequence starts with the <code>char</code> value at the specified index and
+ * ends with the <code>char</code> value at index <tt>end - 1</tt>. The length
+ * (in <code>char</code>s) of the
+ * returned sequence is <tt>end - start</tt>, so if <tt>start == end</tt>
+ * then an empty sequence is returned. </p>
+ *
+ * @param start the start index, inclusive
+ * @param end the end index, exclusive
+ * @return the specified subsequence
+ * @throws IndexOutOfBoundsException if <tt>start</tt> or <tt>end</tt> are negative,
+ * if <tt>end</tt> is greater than <tt>length()</tt>,
+ * or if <tt>start</tt> is greater than <tt>end</tt>
+ */
+ public CharSequence subSequence(int start, int end) {
+ return new CharSlice(array, start, end - start);
+ }
+
+ /**
+ * Copies characters from this FastStringBuffer into the destination character
+ * array.
+ * <p>
+ * The first character to be copied is at index <code>srcBegin</code>;
+ * the last character to be copied is at index <code>srcEnd-1</code>
+ * (thus the total number of characters to be copied is
+ * <code>srcEnd-srcBegin</code>). The characters are copied into the
+ * subarray of <code>dst</code> starting at index <code>dstBegin</code>
+ * and ending at index:
+ * <p><blockquote><pre>
+ * dstbegin + (srcEnd-srcBegin) - 1
+ * </pre></blockquote>
+ *
+ * @param srcBegin index of the first character in the string
+ * to copy.
+ * @param srcEnd index after the last character in the string
+ * to copy.
+ * @param dst the destination array.
+ * @param dstBegin the start offset in the destination array.
+ * @exception IndexOutOfBoundsException If any of the following
+ * is true:
+ * <ul><li><code>srcBegin</code> is negative.
+ * <li><code>srcBegin</code> is greater than <code>srcEnd</code>
+ * <li><code>srcEnd</code> is greater than the length of this
+ * string
+ * <li><code>dstBegin</code> is negative
+ * <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
+ * <code>dst.length</code></ul>
+ */
+ public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
+ if (srcBegin < 0) {
+ throw new StringIndexOutOfBoundsException(srcBegin);
+ }
+ if (srcEnd > used) {
+ throw new StringIndexOutOfBoundsException(srcEnd);
+ }
+ if (srcBegin > srcEnd) {
+ throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
+ }
+ System.arraycopy(array, srcBegin, dst, dstBegin, srcEnd - srcBegin);
+ }
+
+ /**
+ * Get the index of the first character equal to a given value
+ * @param ch the character to search for
+ * @return the position of the first occurrence, or -1 if not found
+ */
+
+ public int indexOf(char ch) {
+ for (int i=0; i<used; i++) {
+ if (array[i] == ch) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Convert contents of the FastStringBuffer to a string
+ */
+
+ public String toString() {
+ condense(); // has side-effects which is nasty on the debugger!
+ return new String(array, 0, used);
+ }
+
+ /**
+ * Compare equality
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof CharSequence && toString().equals(other.toString());
+ }
+
+ /**
+ * Generate a hash code
+ */
+
+ public int hashCode() {
+ // Same algorithm as String#hashCode(), but not cached
+ int h = 0;
+ for (int i = 0; i < used; i++) {
+ h = 31 * h + array[i];
+ }
+ return h;
+ }
+
+
+ /**
+ * Get a char[] array containing the characters. The caller should not modify the
+ * array.
+ * @return a char[] array containing the characters
+ */
+
+ public char[] getCharArray() {
+ return array;
+ }
+
+ /**
+ * Set the character at a particular offset
+ * @param index the index of the character to be set
+ * @param ch the new character to overwrite the existing character at that location
+ * @throws IndexOutOfBoundsException if int<0 or int>=length()
+ */
+
+ public void setCharAt(int index, char ch) {
+ if (index<0 || index>used) {
+ throw new IndexOutOfBoundsException(""+index);
+ }
+ array[index] = ch;
+ }
+
+ /**
+ * Insert a character at a particular offset
+ * @param index the index of the character to be set
+ * @param ch the new character to insert at that location
+ * @throws IndexOutOfBoundsException if int<0 or int>=length()
+ */
+
+ public void insert(int index, char ch) {
+ if (index<0 || index>used) {
+ throw new IndexOutOfBoundsException(""+index);
+ }
+ ensureCapacity(1);
+ System.arraycopy(array, index, array, index + 1, used - index);
+ used++;
+ array[index] = ch;
+ }
+
+ /**
+ * Insert wide character at a particular offset
+ * @param index the index of the character to be set
+ * @param ch the character, as a 32-bit Unicode codepoint
+ * @throws IndexOutOfBoundsException if int<0 or int>=length()
+ */
+
+ public void insertWideChar(int index, int ch) {
+ if (index<0 || index>used) {
+ throw new IndexOutOfBoundsException(""+index);
+ }
+
+ if (ch > 0xffff) {
+ ensureCapacity(2);
+ used+=2;
+ System.arraycopy(array, index, array, index + 1 + 1, used - index);
+ array[index] = UTF16CharacterSet.highSurrogate(ch);
+ array[index+1] = UTF16CharacterSet.lowSurrogate(ch);
+ } else {
+ ensureCapacity(1);
+ used+=1;
+ System.arraycopy(array, index, array, index + 1, used - index);
+ array[index] = (char)ch;
+ }
+ }
+
+ /**
+ * Remove a character at a particular offset
+ * @param index the index of the character to be set
+ * @throws IndexOutOfBoundsException if int<0 or int>=length()
+ */
+
+ public void removeCharAt(int index) {
+ if (index<0 || index>used) {
+ throw new IndexOutOfBoundsException(""+index);
+ }
+ used--;
+ System.arraycopy(array, index + 1, array, index, used - index);
+ }
+
+ /**
+ * Insert a given character at the start of the buffer
+ * @param ch the character to insert
+ */
+
+ public void prepend(char ch) {
+ char[] a2 = new char[array.length + 1];
+ System.arraycopy(array, 0, a2, 1, used);
+ a2[0] = ch;
+ used += 1;
+ array = a2;
+ }
+
+ /**
+ * Insert a given string at the start of the buffer
+ * @param str the string to insert
+ */
+
+ public void prepend(CharSequence str) {
+ int len = str.length();
+ char[] a2 = new char[array.length + len];
+ System.arraycopy(array, 0, a2, len, used);
+ for (int i=0; i<len; i++) {
+ a2[i] = str.charAt(i);
+ }
+ used += len;
+ array = a2;
+ }
+
+ /**
+ * Insert a given character N times at the start of the buffer
+ * @param ch the character to insert
+ * @param repeat the number of occurrences required. Supplying 0 or a negative number is OK,
+ * and is treated as a no-op.
+ */
+
+ public void prependRepeated(char ch, int repeat) {
+ if (repeat > 0) {
+ char[] a2 = new char[array.length + repeat];
+ System.arraycopy(array, 0, a2, repeat, used);
+ Arrays.fill(a2, 0, repeat, ch);
+ used += repeat;
+ array = a2;
+ }
+ }
+
+ /**
+ * Set the length. If this exceeds the current length, this method is a no-op.
+ * If this is less than the current length, characters beyond the specified point
+ * are deleted.
+ * @param length the new length
+ */
+
+ public void setLength(int length) {
+ if (length < 0 || length > used) {
+ return;
+ }
+ used = length;
+ }
+
+ /**
+ * Expand the character array if necessary to ensure capacity for appended data
+ * @param extra the amount of additional capacity needed, in characters
+ */
+
+ public void ensureCapacity(int extra) {
+ if (used + extra > array.length) {
+ int newlen = array.length * 2;
+ if (newlen < used + extra) {
+ newlen = used + extra*2;
+ }
+ char[] array2 = new char[newlen];
+ System.arraycopy(array, 0, array2, 0, used);
+ array = array2;
+ }
+ }
+
+ /**
+ * Remove surplus space from the array. This doesn't reduce the array to the minimum
+ * possible size; it only reclaims space if it seems worth doing. Specifically, it
+ * contracts the array if the amount of wasted space is more than 256 characters, or
+ * more than half the allocated size and more than 20 chars.
+ * @return the buffer after removing unused space
+ */
+
+ public CharSequence condense() {
+ if (array.length - used > 256 || (array.length > used * 2 && array.length - used > 20)) {
+ char[] array2 = new char[used];
+ System.arraycopy(array, 0, array2, 0, used);
+ array = array2;
+ }
+ return this;
+ }
+
+ /**
+ * Write the value to a writer
+ * @param writer the writer to which the content is to be written
+ */
+
+ public void write(Writer writer) throws java.io.IOException {
+ writer.write(array, 0, used);
+ }
+
+ /**
+ * Diagnostic print of the contents of a CharSequence. Ordinary printable ASCII characters
+ * are displayed as themselves; anything else is displayed as a \\uNNNN escape sequence
+ * @param in the CharSequence whose contents are to be displayed.
+ * @return the diagnostic output
+ */
+
+ public static String diagnosticPrint(/*@NotNull*/ CharSequence in) {
+ FastStringBuffer buff = new FastStringBuffer(in.length()*2);
+ for (int i=0; i<in.length(); i++) {
+ char c = in.charAt(i);
+ if (c > 32 && c < 127) {
+ buff.append(c);
+ } else {
+ buff.append("\\u");
+ for (int d=12; d>=0; d-=4) {
+ buff.append("0123456789ABCDEF".charAt((c>>d)&0xf));
+ }
+ }
+ }
+ return buff.toString();
+ }
+
+}
+
diff --git a/sf/saxon/tree/util/NamespaceIterator.java b/sf/saxon/tree/util/NamespaceIterator.java
new file mode 100644
index 0000000..4411dac
--- /dev/null
+++ b/sf/saxon/tree/util/NamespaceIterator.java
@@ -0,0 +1,134 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.NamespaceBinding;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.Type;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * This class provides an iterator over the namespace codes representing the in-scope namespaces
+ * of any node. It relies on nodes to implement the method
+ * {@link net.sf.saxon.om.NodeInfo#getDeclaredNamespaces(net.sf.saxon.om.NamespaceBinding[])}.
+ *
+ * <p>The result does not include the XML namespace.</p>
+ */
+public class NamespaceIterator implements Iterator<NamespaceBinding> {
+
+ /*@Nullable*/ private NodeInfo element;
+ private int index;
+ /*@Nullable*/ private NamespaceBinding next;
+ private NamespaceBinding[] localDeclarations;
+ HashSet<String> undeclaredPrefixes;
+
+ /**
+ * Factory method: create an iterator over the in-scope namespace codes for an element
+ * @param element the element (or other node) whose in-scope namespaces are required. If this
+ * is not an element, the result will be an empty iterator
+ * @return an iterator over the namespace codes. A namespace code is an integer that represents
+ * a prefix-uri binding; the prefix and URI can be obtained by reference to the name pool. This
+ * iterator will represent all the in-scope namespaces, without duplicates, and respecting namespace
+ * undeclarations. It does not include the XML namespace.
+ */
+
+ public static Iterator<NamespaceBinding> iterateNamespaces(/*@NotNull*/ NodeInfo element) {
+ if (element.getNodeKind() == Type.ELEMENT) {
+ return new NamespaceIterator(element);
+ } else {
+ return Collections.EMPTY_LIST.iterator();
+ }
+ }
+
+ /**
+ * Send all the in-scope namespaces for a node (except the XML namespace) to a specified receiver
+ * @param element the element in question (the method does nothing if this is not an element)
+ * @param receiver the receiver to which the namespaces are notified
+ */
+
+ public static void sendNamespaces(/*@NotNull*/ NodeInfo element, /*@NotNull*/ Receiver receiver) throws XPathException {
+ if (element.getNodeKind() == Type.ELEMENT) {
+ boolean foundDefault = false;
+ for (Iterator<NamespaceBinding> iter = iterateNamespaces(element); iter.hasNext();) {
+ NamespaceBinding nb = iter.next();
+ if (nb.getPrefix().length()==0) {
+ foundDefault = true;
+ }
+ receiver.namespace(nb, 0);
+ }
+// if (!foundDefault) {
+// // see bug 5857
+// receiver.namespace(NamespaceBinding.DEFAULT_UNDECLARATION, 0);
+// }
+ }
+ }
+
+ private NamespaceIterator(/*@NotNull*/ NodeInfo element) {
+ this.element = element;
+ undeclaredPrefixes = new HashSet(8);
+ index = 0;
+ localDeclarations = element.getDeclaredNamespaces(null);
+ }
+
+ public boolean hasNext() {
+ if (element == null || (next == null && index != 0)) {
+ return false;
+ }
+ advance();
+ return next != null;
+ }
+
+ /*@Nullable*/ public NamespaceBinding next() {
+ return next;
+ }
+
+ private void advance() {
+ while (true) {
+ boolean ascend = index >= localDeclarations.length;
+ NamespaceBinding nsCode = null;
+ if (!ascend) {
+ nsCode = localDeclarations[index++];
+ ascend = nsCode == null;
+ }
+ if (ascend) {
+ element = element.getParent();
+ if (element != null && element.getNodeKind() == Type.ELEMENT) {
+ localDeclarations = element.getDeclaredNamespaces(localDeclarations);
+ index = 0;
+ continue;
+ } else {
+ next = null;
+ return;
+ }
+ }
+ String uri = nsCode.getURI();
+ String prefix = nsCode.getPrefix();
+ if (uri.length()==0) {
+ // this is an undeclaration
+ undeclaredPrefixes.add(prefix);
+ } else {
+ if (undeclaredPrefixes.add(prefix)) {
+ // it was added, so it's new, so return it
+ next = nsCode;
+ return;
+ }
+ // else it wasn't added, so we've already seen it
+ }
+ }
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
diff --git a/sf/saxon/tree/util/NamespaceResolverWithDefault.java b/sf/saxon/tree/util/NamespaceResolverWithDefault.java
new file mode 100644
index 0000000..cc842b8
--- /dev/null
+++ b/sf/saxon/tree/util/NamespaceResolverWithDefault.java
@@ -0,0 +1,67 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+import net.sf.saxon.om.NamespaceResolver;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * This class is a NamespaceResolver that modifies an underyling NamespaceResolver
+ * by changing the mapping of the null prefix to be a specified namespace, rather than
+ * the one used by the underlying namespace resolver.
+ */
+public class NamespaceResolverWithDefault implements NamespaceResolver {
+
+ private NamespaceResolver baseResolver;
+ private String defaultNamespace;
+
+ public NamespaceResolverWithDefault(NamespaceResolver base, String defaultNamespace) {
+ this.baseResolver = base;
+ this.defaultNamespace = defaultNamespace;
+ }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ * @param prefix the namespace prefix. May be the zero-length string, indicating
+ * that there is no prefix. This indicates either the default namespace or the
+ * null namespace, depending on the value of useDefault.
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is "". If false, the method returns "" when the prefix is "".
+ * @return the uri for the namespace, or null if the prefix is not in scope.
+ * The "null namespace" is represented by the pseudo-URI "".
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(/*@NotNull*/ String prefix, boolean useDefault) {
+ if (useDefault && prefix.length()==0) {
+ return defaultNamespace;
+ } else {
+ return baseResolver.getURIForPrefix(prefix, useDefault);
+ }
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ ArrayList<String> list = new ArrayList<String>(10);
+ for(Iterator<String> it = baseResolver.iteratePrefixes(); it.hasNext(); ) {
+ String p = it.next();
+ if (p.length() != 0) {
+ list.add(p);
+ }
+ }
+ list.add("");
+ return list.iterator();
+ }
+}
+
diff --git a/sf/saxon/tree/util/Navigator.java b/sf/saxon/tree/util/Navigator.java
new file mode 100644
index 0000000..5b22921
--- /dev/null
+++ b/sf/saxon/tree/util/Navigator.java
@@ -0,0 +1,1462 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.*;
+import net.sf.saxon.tree.tiny.TinyNodeImpl;
+import net.sf.saxon.tree.wrapper.SiblingCountingNode;
+import net.sf.saxon.type.*;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+
+/**
+ * The Navigator class provides helper classes for navigating a tree, irrespective
+ * of its implementation
+ *
+ * @author Michael H. Kay
+ */
+
+
+public final class Navigator {
+
+ // Class is never instantiated
+ private Navigator() {
+ }
+
+ /**
+ * Get the string value of an attribute of a given element, given the URI and
+ * local part of the attribute name.
+ * @param element the element on which the required attribute appears
+ * @param uri The namespace URI of the attribute name.
+ * The "no namespace" case is represented as an empty string.
+ * @param localName The local part of the attribute name.
+ * @return the attribute value, or null if the attribute is not present
+ * @since 9.0
+ */
+
+ public static String getAttributeValue(/*@NotNull*/ NodeInfo element, /*@NotNull*/ String uri, /*@NotNull*/ String localName) {
+ return element.getAttributeValue(uri, localName);
+ }
+
+ /**
+ * Helper method to get the outermost element of a document, given the document node
+ * @param doc the document node at the root of the document
+ * @return the first element child of the document node, if there is one, else null. This
+ * is often referred to as the "root element" or "document element". No error is reported
+ * if the document node has multiple element children, which can happen in a document
+ * constructed using XSLT or XQuery.
+ * @since 9.3
+ */
+
+ /*@Nullable*/ public static NodeInfo getOutermostElement(/*@NotNull*/ DocumentInfo doc) {
+ return doc.iterateAxis(AxisInfo.CHILD, NodeKindTest.ELEMENT).next();
+ }
+
+ /**
+ * Helper method to construct a NodeTest for use with the {@link NodeInfo#iterateAxis} method
+ * @param pool the NamePool. The relevant NamePool can be obtained by calling the method
+ * {@link NodeInfo#getNamePool}.
+ * @param nodeKind The kind of node required, for example {@link Type#ELEMENT} or {@link Type#ATTRIBUTE}.
+ * To select nodes of any kind, use {@link Type#NODE}.
+ * @param uri The namespace URI of the nodes to be selected. Supply null to selects nodes from any
+ * namespace or none. Supply "" to select nodes that are not in a namespace.
+ * @param localName The local name of the nodes to be selected. Supply null to select nodes irrespective
+ * of their local name.
+ * @return a NodeTest that matches the requested nodes
+ * @since 9.0
+ */
+
+ /*@Nullable*/ public static NodeTest makeNodeTest(/*@NotNull*/ NamePool pool, int nodeKind, /*@Nullable*/ String uri, /*@Nullable*/ String localName) {
+ if (uri == null && localName == null) {
+ return NodeKindTest.makeNodeKindTest(nodeKind);
+ } else if (uri == null) {
+ return new LocalNameTest(pool, nodeKind, localName);
+ } else if (localName == null) {
+ return new NamespaceTest(pool, nodeKind, uri);
+ } else {
+ int fp = pool.allocate("", uri, localName);
+ return new NameTest(nodeKind, fp, pool);
+ }
+ }
+
+ /**
+ * Helper method to get the base URI of an element or processing instruction node
+ * @param node the node whose base URI is required
+ * @return the base URI of the node
+ * @since 8.7
+ */
+
+ /*@Nullable*/ public static String getBaseURI(/*@NotNull*/ NodeInfo node) {
+ String xmlBase = node.getAttributeValue(NamespaceConstant.XML, "base");
+ if (xmlBase != null) {
+ URI baseURI;
+ try {
+ baseURI = new URI(xmlBase);
+ if (!baseURI.isAbsolute()) {
+ NodeInfo parent = node.getParent();
+ if (parent == null) {
+ // We have a parentless element with a relative xml:base attribute.
+ // See for example test XQTS fn-base-uri-10 and base-uri-27
+ URI base = new URI(node.getSystemId());
+ URI resolved = (xmlBase.length()==0 ? base : base.resolve(baseURI));
+ return resolved.toString();
+ }
+ String startSystemId = node.getSystemId();
+ if (startSystemId == null) {
+ return null;
+ }
+ String parentSystemId = parent.getSystemId();
+ URI base = new URI(startSystemId.equals(parentSystemId) ? parent.getBaseURI() : startSystemId);
+ baseURI = (xmlBase.length()==0 ? base : base.resolve(baseURI));
+ }
+ } catch (URISyntaxException e) {
+ // xml:base is an invalid URI. Just return it as is: the operation that needs the base URI
+ // will probably fail as a result. \
+ return xmlBase;
+ }
+ return baseURI.toString();
+ }
+ String startSystemId = node.getSystemId();
+ if (startSystemId == null) {
+ return null;
+ }
+ NodeInfo parent = node.getParent();
+ if (parent == null) {
+ return startSystemId;
+ }
+ String parentSystemId = parent.getSystemId();
+ if (startSystemId.equals(parentSystemId)) {
+ return parent.getBaseURI();
+ } else {
+ return startSystemId;
+ }
+ }
+
+ /**
+ * Get an absolute XPath expression that identifies a given node within its document
+ *
+ * @param node the node whose path is required. If null is supplied,
+ * an empty string is returned - this fact is used in making a recursive call
+ * for a parentless node.
+ * @return a path expression that can be used to retrieve the node
+ */
+
+ public static String getPath(NodeInfo node) {
+ return getPath(node, null);
+ }
+
+ /**
+ * Get an absolute XPath expression that identifies a given node within its document
+ *
+ * @param node the node whose path is required. If null is supplied,
+ * an empty string is returned - this fact is used in making a recursive call
+ * for a parentless node.
+ * @param context the XPath dynamic evaluation context. May be null if no context is known
+ * @return a path expression that can be used to retrieve the node
+ */
+
+ public static String getPath(/*@Nullable*/ NodeInfo node, /*@Nullable*/ XPathContext context) {
+ if (node == null) {
+ return "";
+ }
+ String pre;
+ NodeInfo parent = node.getParent();
+ // System.err.println("node = " + node + " parent = " + parent);
+
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT:
+ return "/";
+ case Type.ELEMENT:
+ if (parent == null) {
+ return node.getDisplayName();
+ } else {
+ pre = getPath(parent, context);
+ if (pre.equals("/")) {
+ return '/' + node.getDisplayName();
+ } else {
+ try {
+ return pre + '/' + node.getDisplayName() + '[' + getNumberSimple(node, context) + ']';
+ } catch (UnsupportedOperationException e) {
+ // Happens when streaming
+ return pre + '/' + node.getDisplayName();
+ }
+ }
+ }
+ case Type.ATTRIBUTE:
+ return getPath(parent, context) + "/@" + node.getDisplayName();
+ case Type.TEXT:
+ pre = getPath(parent, context);
+ try {
+ return (pre.equals("/") ? "" : pre) +
+ "/text()[" + getNumberSimple(node, context) + ']';
+ } catch (UnsupportedOperationException e) {
+ // Happens when streaming
+ return pre + "/text()";
+ }
+ case Type.COMMENT:
+ pre = getPath(parent, context);
+ try {
+ return (pre.equals("/") ? "" : pre) +
+ "/comment()[" + getNumberSimple(node, context) + ']';
+ } catch (UnsupportedOperationException e) {
+ // Happens when streaming
+ return pre + "/comment()";
+ }
+ case Type.PROCESSING_INSTRUCTION:
+ pre = getPath(parent, context);
+ try {
+ return (pre.equals("/") ? "" : pre) +
+ "/processing-instruction()[" + getNumberSimple(node, context) + ']';
+ } catch (UnsupportedOperationException e) {
+ // Happens when streaming
+ return pre + "/processing-instruction()";
+ }
+ case Type.NAMESPACE:
+ String test = node.getLocalPart();
+ if (test.length() == 0) {
+ // default namespace: need a node-test that selects unnamed nodes only
+ test = "*[not(local-name()]";
+ }
+ return getPath(parent, context) + "/namespace::" + test;
+ default:
+ return "";
+ }
+ }
+
+ /**
+ * Get the absolute path to a node
+ * @param node the node in question
+ * @return an object representing the path to the node
+ */
+
+ public static AbsolutePath getAbsolutePath(NodeInfo node) {
+ List<AbsolutePath.PathElement> path = new LinkedList<AbsolutePath.PathElement>();
+ String sysId = node.getSystemId();
+ while (node != null && node.getNodeKind() != Type.DOCUMENT) {
+ path.add(0, new AbsolutePath.PathElement(node.getNodeKind(), new NameOfNode(node), getNumberSimple(node, null)));
+ node = node.getParent();
+ }
+ AbsolutePath a = new AbsolutePath(path);
+ a.setSystemId(sysId);
+ return a;
+ }
+
+ /**
+ * Get simple node number. This is defined as one plus the number of previous siblings of the
+ * same node type and name. It is not accessible directly in XSL.
+ *
+ * @param node The node whose number is required
+ * @param context Used for remembering previous result, for
+ * performance. May be null.
+ * @return the node number, as defined above
+ */
+
+ public static int getNumberSimple(/*@NotNull*/ NodeInfo node, /*@Nullable*/ XPathContext context) {
+
+ //checkNumberable(node);
+
+ int fingerprint = node.getFingerprint();
+ NodeTest same;
+
+ if (fingerprint == -1) {
+ same = NodeKindTest.makeNodeKindTest(node.getNodeKind());
+ } else {
+ same = new NameTest(node);
+ }
+
+ Controller controller = (context == null ? null : context.getController());
+ AxisIterator preceding = node.iterateAxis(AxisInfo.PRECEDING_SIBLING, same);
+
+ int i = 1;
+ while (true) {
+ NodeInfo prev = preceding.next();
+ if (prev == null) {
+ break;
+ }
+
+ if (controller != null) {
+ int memo = controller.getRememberedNumber(prev);
+ if (memo > 0) {
+ memo += i;
+ controller.setRememberedNumber(node, memo);
+ return memo;
+ }
+ }
+
+ i++;
+ }
+
+ if (controller != null) {
+ controller.setRememberedNumber(node, i);
+ }
+ return i;
+ }
+
+ /**
+ * Get node number (level="single"). If the current node matches the supplied pattern, the returned
+ * number is one plus the number of previous siblings that match the pattern. Otherwise,
+ * return the element number of the nearest ancestor that matches the supplied pattern.
+ *
+ * @param node the current node, the one whose node number is required
+ * @param count Pattern that identifies which nodes should be
+ * counted. Default (null) is the element name if the current node is
+ * an element, or "node()" otherwise.
+ * @param from Pattern that specifies where counting starts from.
+ * Default (null) is the root node. (This parameter does not seem
+ * useful but is included for the sake of XSLT conformance.)
+ * @param context the dynamic context of the transformation, used if
+ * the patterns reference context values (e.g. variables)
+ * @return the node number established as follows: go to the nearest
+ * ancestor-or-self that matches the 'count' pattern and that is a
+ * descendant of the nearest ancestor that matches the 'from' pattern.
+ * Return one plus the nunber of preceding siblings of that ancestor
+ * that match the 'count' pattern. If there is no such ancestor,
+ * return 0.
+ * @throws XPathException when any error occurs in processing
+ */
+
+ public static int getNumberSingle(/*@NotNull*/ NodeInfo node, /*@Nullable*/ Pattern count,
+ /*@Nullable*/ Pattern from, XPathContext context) throws XPathException {
+
+ if (count == null && from == null) {
+ return getNumberSimple(node, context);
+ }
+
+ boolean knownToMatch = false;
+ if (count == null) {
+ if (node.getFingerprint() == -1) { // unnamed node
+ count = new ItemTypePattern(NodeKindTest.makeNodeKindTest(node.getNodeKind()));
+ } else {
+ count = new ItemTypePattern(new NameTest(node));
+ }
+ knownToMatch = true;
+ }
+
+ NodeInfo target = node;
+ // code changed in 9.5 to fix issue described in spec bug 9840
+ if (!knownToMatch) {
+ while (true) {
+ if (count.matches(target, context)) {
+ if (from == null) {
+ break;
+ } else {
+ // see whether there is an ancestor node that matches the from pattern
+ NodeInfo anc = target;
+ while (!from.matches(anc, context)) {
+ anc = anc.getParent();
+ if (anc == null) {
+ // there's no ancestor that matches the "from" pattern
+ return 0;
+ }
+ }
+ // we've found the node to be counted
+ break;
+ }
+ } else if (from != null && from.matches(target, context)) {
+ // if we find something that matches "from" before we find something that matches "count", exit
+ return 0;
+ } else {
+ target = target.getParent();
+ if (target == null) {
+ // found the root before finding a match on either "count" or "from"
+ return 0;
+ }
+ }
+ }
+ }
+
+ // we've found the ancestor to count from
+
+ SequenceIterator preceding =
+ target.iterateAxis(AxisInfo.PRECEDING_SIBLING, (NodeTest)count.getItemType());
+ // pass the filter condition down to the axis enumeration where possible
+ boolean alreadyChecked = (count instanceof ItemTypePattern);
+ int i = 1;
+ while (true) {
+ NodeInfo p = (NodeInfo)preceding.next();
+ if (p == null) {
+ return i;
+ }
+ if (alreadyChecked || count.matches(p, context)) {
+ i++;
+ }
+ }
+ }
+
+ /**
+ * Get node number (level="any").
+ * Return one plus the number of previous nodes in the
+ * document that match the supplied pattern
+ *
+ * @param inst Identifies the xsl:number expression; this is relevant
+ * when the function is memoised to support repeated use of the same
+ * instruction to number multiple nodes
+ * @param node The node being numbered
+ * @param count Pattern that identifies which nodes should be
+ * counted. Default (null) is the element name if the current node is
+ * an element, or "node()" otherwise.
+ * @param from Pattern that specifies where counting starts from.
+ * Default (null) is the root node. Only nodes at or after the first (most
+ * recent) node that matches the 'from' pattern are counted.
+ * @param context The dynamic context for the transformation
+ * @param hasVariablesInPatterns if the count or from patterns
+ * contain variables, then it's not safe to get the answer by adding
+ * one to the number of the most recent node that matches
+ * @return one plus the number of nodes that precede the current node,
+ * that match the count pattern, and that follow the first node that
+ * matches the from pattern if specified.
+ * @throws net.sf.saxon.trans.XPathException if any dynamic error occurs
+ *
+ */
+
+ public static int getNumberAny(/*@NotNull*/ Expression inst, /*@NotNull*/ NodeInfo node, /*@Nullable*/ Pattern count,
+ /*@Nullable*/ Pattern from, /*@NotNull*/ XPathContext context, boolean hasVariablesInPatterns) throws XPathException {
+
+ NodeInfo memoNode = null;
+ int memoNumber = 0;
+ Controller controller = context.getController();
+ assert controller != null;
+ boolean memoise = (!hasVariablesInPatterns) && from==null;
+ if (memoise) {
+ Object[] memo = (Object[])controller.getUserData(inst, "xsl:number");
+ if (memo != null) {
+ memoNode = (NodeInfo)memo[0];
+ memoNumber = (Integer) memo[1];
+ }
+ }
+
+ int num = 0;
+ if (count == null) {
+ if (node.getFingerprint() == -1) { // unnamed node
+ count = new ItemTypePattern(NodeKindTest.makeNodeKindTest(node.getNodeKind()));
+ } else {
+ count = new ItemTypePattern(new NameTest(node));
+ }
+ num = 1;
+ } else if (count.matches(node, context)) {
+ num = 1;
+ }
+
+ // We use a special axis invented for the purpose: the union of the preceding and
+ // ancestor axes, but in reverse document order
+
+ // Pass part of the filtering down to the axis iterator if possible
+ NodeTest filter;
+ if (from == null) {
+ filter = (NodeTest)count.getItemType();
+ } else if (from.getNodeKind() == Type.ELEMENT && count.getNodeKind() == Type.ELEMENT) {
+ filter = NodeKindTest.ELEMENT;
+ } else {
+ filter = AnyNodeTest.getInstance();
+ }
+
+ if (from != null && from.matches(node, context)) {
+ return num;
+ }
+
+ SequenceIterator preceding =
+ node.iterateAxis(AxisInfo.PRECEDING_OR_ANCESTOR, filter);
+
+ while (true) {
+ NodeInfo prev = (NodeInfo)preceding.next();
+ if (prev == null) {
+ break;
+ }
+
+ if (count.matches(prev, context)) {
+ if (num == 1 && memoNode != null && prev.isSameNodeInfo(memoNode)) {
+ num = memoNumber + 1;
+ break;
+ }
+ num++;
+ }
+
+ if (from != null && from.matches(prev, context)) {
+ break;
+ }
+ }
+ // The next four lines are commented out to implement the spec as suggested in spec bug 10299.
+// if (!found && from != null) {
+// // we've got to the end without matching the from pattern - result is ()
+// return 0;
+// }
+ if (memoise) {
+ Object[] memo = new Object[2];
+ memo[0] = node;
+ memo[1] = num;
+ controller.setUserData(inst, "xsl:number", memo);
+ }
+ return num;
+ }
+
+ /**
+ * Get node number (level="multiple").
+ * Return a vector giving the hierarchic position of this node. See the XSLT spec for details.
+ *
+ * @param node The node to be numbered
+ * @param count Pattern that identifies which nodes (ancestors and
+ * their previous siblings) should be counted. Default (null) is the
+ * element name if the current node is an element, or "node()"
+ * otherwise.
+ * @param from Pattern that specifies where counting starts from.
+ * Default (null) is the root node. Only nodes below the first (most
+ * recent) node that matches the 'from' pattern are counted.
+ * @param context The dynamic context for the transformation
+ * @return a vector containing for each ancestor-or-self that matches the
+ * count pattern and that is below the nearest node that matches the
+ * from pattern, an Integer which is one greater than the number of
+ * previous siblings that match the count pattern.
+ * @throws XPathException if an error occurs
+ */
+
+ /*@NotNull*/ public static List<Long> getNumberMulti(/*@NotNull*/ NodeInfo node, /*@Nullable*/ Pattern count,
+ /*@Nullable*/ Pattern from, XPathContext context) throws XPathException {
+
+ //checkNumberable(node);
+
+ ArrayList<Long> v = new ArrayList<Long>(5);
+
+ if (count == null) {
+ if (node.getFingerprint() == -1) { // unnamed node
+ count = new ItemTypePattern(NodeKindTest.makeNodeKindTest(node.getNodeKind()));
+ } else {
+ count = new ItemTypePattern(new NameTest(node));
+ }
+ }
+
+ NodeInfo curr = node;
+
+ while (true) {
+ if (count.matches(curr, context)) {
+ int num = getNumberSingle(curr, count, null, context);
+ v.add(0, (long) num);
+ }
+ curr = curr.getParent();
+ if (curr == null) {
+ break;
+ }
+ if (from != null && from.matches(curr, context)) {
+ break;
+ }
+ }
+
+ return v;
+ }
+
+ /**
+ * Generic (model-independent) implementation of deep copy algorithm for nodes.
+ * This is available for use by any node implementations that choose to use it.
+ *
+ *
+ * @param node The node to be copied
+ * @param out The receiver to which events will be sent
+ * @param copyOptions Options for copying namespaces, type annotations, etc,
+ * as defined in {@link net.sf.saxon.om.CopyOptions}
+ * @param locationId The location of the instruction invoking the copy
+ * @throws XPathException on any failure reported by the Receiver
+ */
+
+ public static void copy(/*@NotNull*/ NodeInfo node,
+ /*@NotNull*/ Receiver out,
+ int copyOptions,
+ int locationId) throws XPathException {
+
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT:
+ {
+ out.startDocument(CopyOptions.getStartDocumentProperties(copyOptions));
+ AxisIterator children0 = node.iterateAxis(AxisInfo.CHILD, AnyNodeTest.getInstance());
+ while (true) {
+ NodeInfo child = children0.next();
+ if (child == null) {
+ break;
+ }
+ child.copy(out, copyOptions, locationId);
+ }
+ out.endDocument();
+ break;
+ }
+ case Type.ELEMENT:
+ {
+ SchemaType annotation = ((copyOptions & CopyOptions.TYPE_ANNOTATIONS) != 0 ?
+ node.getSchemaType() :
+ Untyped.getInstance());
+ out.startElement(new NameOfNode(node), annotation, locationId, 0);
+
+ // output the namespaces
+
+ if ((copyOptions & CopyOptions.LOCAL_NAMESPACES) != 0) {
+ NamespaceBinding[] localNamespaces = node.getDeclaredNamespaces(null);
+ for (NamespaceBinding ns : localNamespaces) {
+ if (ns == null) {
+ break;
+ }
+ out.namespace(ns, 0);
+ }
+ } else if ((copyOptions & CopyOptions.ALL_NAMESPACES) != 0) {
+ NamespaceIterator.sendNamespaces(node, out);
+ }
+
+ // output the attributes
+
+ AxisIterator attributes = node.iterateAxis(AxisInfo.ATTRIBUTE, AnyNodeTest.getInstance());
+ while (true) {
+ NodeInfo att = attributes.next();
+ if (att == null) {
+ break;
+ }
+ att.copy(out, copyOptions, locationId);
+ }
+
+ // notify the start of content
+
+ out.startContent();
+
+ // output the children
+
+ AxisIterator children = node.iterateAxis(AxisInfo.CHILD, AnyNodeTest.getInstance());
+ while (true) {
+ NodeInfo child = children.next();
+ if (child == null) {
+ break;
+ }
+ child.copy(out, copyOptions, locationId);
+ }
+
+ // finally the end tag
+
+ out.endElement();
+ return;
+ }
+ case Type.ATTRIBUTE:
+ {
+ SimpleType annotation = ((copyOptions & CopyOptions.TYPE_ANNOTATIONS) != 0 ?
+ (SimpleType)node.getSchemaType() :
+ BuiltInAtomicType.UNTYPED_ATOMIC);
+ out.attribute(new NameOfNode(node), annotation, node.getStringValueCS(), locationId, 0);
+ return;
+ }
+ case Type.TEXT:
+ {
+ CharSequence value = node.getStringValueCS();
+ if (value.length() != 0) {
+ // zero-length text nodes can arise from external model wrappers
+ out.characters(value, locationId, 0);
+ }
+ return;
+ }
+ case Type.COMMENT:
+ {
+ out.comment(node.getStringValueCS(), locationId, 0);
+ return;
+ }
+ case Type.PROCESSING_INSTRUCTION:
+ {
+ out.processingInstruction(node.getLocalPart(), node.getStringValueCS(), locationId, 0);
+ return;
+ }
+ case Type.NAMESPACE:
+ {
+ out.namespace(new NamespaceBinding(node.getLocalPart(), node.getStringValue()), 0);
+ return;
+ }
+ default:
+
+ }
+ }
+
+ /**
+ * Generic (model-independent) method to determine the relative position of two
+ * node in document order. The nodes must be in the same tree.
+ *
+ * @param first The first node
+ * @param second The second node, whose position is to be compared with the first node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public static int compareOrder(/*@NotNull*/ SiblingCountingNode first, /*@NotNull*/ SiblingCountingNode second) {
+
+ // are they the same node?
+ if (first.isSameNodeInfo(second)) {
+ return 0;
+ }
+
+ NodeInfo firstParent = first.getParent();
+ if (firstParent == null) {
+ // first node is the root
+ return -1;
+ }
+
+ NodeInfo secondParent = second.getParent();
+ if (secondParent == null) {
+ // second node is the root
+ return +1;
+ }
+
+ // do they have the same parent (common case)?
+ if (firstParent.isSameNodeInfo(secondParent)) {
+ int cat1 = nodeCategories[first.getNodeKind()];
+ int cat2 = nodeCategories[second.getNodeKind()];
+ if (cat1 == cat2) {
+ return first.getSiblingPosition() - second.getSiblingPosition();
+ } else {
+ return cat1 - cat2;
+ }
+ }
+
+ // find the depths of both nodes in the tree
+ int depth1 = 0;
+ int depth2 = 0;
+ NodeInfo p1 = first;
+ NodeInfo p2 = second;
+ while (p1 != null) {
+ depth1++;
+ p1 = p1.getParent();
+ }
+ while (p2 != null) {
+ depth2++;
+ p2 = p2.getParent();
+ }
+ // move up one branch of the tree so we have two nodes on the same level
+
+ p1 = first;
+ while (depth1 > depth2) {
+ p1 = p1.getParent();
+ assert p1 != null;
+ if (p1.isSameNodeInfo(second)) {
+ return +1;
+ }
+ depth1--;
+ }
+
+ p2 = second;
+ while (depth2 > depth1) {
+ p2 = p2.getParent();
+ assert p2 != null;
+ if (p2.isSameNodeInfo(first)) {
+ return -1;
+ }
+ depth2--;
+ }
+
+ // now move up both branches in sync until we find a common parent
+ while (true) {
+ NodeInfo par1 = p1.getParent();
+ NodeInfo par2 = p2.getParent();
+ if (par1 == null || par2 == null) {
+ throw new NullPointerException("Node order comparison - internal error");
+ }
+ if (par1.isSameNodeInfo(par2)) {
+ if (p1.getNodeKind() == Type.ATTRIBUTE && p2.getNodeKind() != Type.ATTRIBUTE) {
+ return -1; // attributes first
+ }
+ if (p1.getNodeKind() != Type.ATTRIBUTE && p2.getNodeKind() == Type.ATTRIBUTE) {
+ return +1; // attributes first
+ }
+ return ((SiblingCountingNode)p1).getSiblingPosition() -
+ ((SiblingCountingNode)p2).getSiblingPosition();
+ }
+ p1 = par1;
+ p2 = par2;
+ }
+ }
+
+ /**
+ * Generic (model-independent) method to determine the relative position of two
+ * node in document order. The nodes must be in the same tree.
+ *
+ * @param first The first node
+ * @param second The second node, whose position is to be compared with the first node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+
+ public static int comparePosition(/*@NotNull*/ NodeInfo first, /*@NotNull*/ NodeInfo second) {
+
+ if (first.getNodeKind() == Type.ATTRIBUTE || first.getNodeKind() == Type.NAMESPACE ||
+ second.getNodeKind() == Type.ATTRIBUTE || second.getNodeKind() == Type.NAMESPACE) {
+ throw new UnsupportedOperationException();
+ }
+
+ // are they the same node?
+ if (first.isSameNodeInfo(second)) {
+ return AxisInfo.SELF;
+ }
+
+ NodeInfo firstParent = first.getParent();
+ if (firstParent == null) {
+ // first node is the root
+ return AxisInfo.ANCESTOR;
+ }
+
+ NodeInfo secondParent = second.getParent();
+ if (secondParent == null) {
+ // second node is the root
+ return AxisInfo.DESCENDANT;
+ }
+
+ // do they have the same parent (common case)?
+ if (firstParent.isSameNodeInfo(secondParent)) {
+ if (first.compareOrder(second) < 0) {
+ return AxisInfo.PRECEDING;
+ } else {
+ return AxisInfo.FOLLOWING;
+ }
+ }
+
+ // find the depths of both nodes in the tree
+ int depth1 = 0;
+ int depth2 = 0;
+ NodeInfo p1 = first;
+ NodeInfo p2 = second;
+ while (p1 != null) {
+ depth1++;
+ p1 = p1.getParent();
+ }
+ while (p2 != null) {
+ depth2++;
+ p2 = p2.getParent();
+ }
+ // Test if either node is an ancestor of the other
+
+ p1 = first;
+ while (depth1 > depth2) {
+ p1 = p1.getParent();
+ assert p1 != null;
+ if (p1.isSameNodeInfo(second)) {
+ return AxisInfo.DESCENDANT;
+ }
+ depth1--;
+ }
+
+ p2 = second;
+ while (depth2 > depth1) {
+ p2 = p2.getParent();
+ assert p2 != null;
+ if (p2.isSameNodeInfo(first)) {
+ return AxisInfo.ANCESTOR;
+ }
+ depth2--;
+ }
+
+ // now delegate to compareOrder()
+ if (first.compareOrder(second) < 0) {
+ return AxisInfo.PRECEDING;
+ } else {
+ return AxisInfo.FOLLOWING;
+ }
+ }
+
+
+ /**
+ * Classify node kinds into categories for sorting into document order:
+ * 0 = document, 1 = namespace, 2 = attribute, 3 = (element, text, comment, pi)
+ */
+
+ /*@NotNull*/ private static int[] nodeCategories = {
+ -1, //0 = not used
+ 3, //1 = element
+ 2, //2 = attribute
+ 3, //3 = text
+ -1, -1, -1, //4,5,6 = not used
+ 3, //7 = processing-instruction
+ 3, //8 = comment
+ 0, //9 = document
+ -1, -1, -1, //10,11,12 = not used
+ 1 //13 = namespace
+ };
+
+ /**
+ * Get a character string that uniquely identifies this node and that collates nodes
+ * into document order
+ * @param node the node whose unique identifier is reuqired
+ * @param sb a buffer to which the unique identifier will be appended
+ * @param addDocNr true if a unique document number is to be included in the information
+ */
+
+ public static void appendSequentialKey(/*@NotNull*/ SiblingCountingNode node, /*@NotNull*/ FastStringBuffer sb, boolean addDocNr) {
+ if (addDocNr) {
+ sb.append('w');
+ sb.append(Long.toString(node.getDocumentNumber()));
+ }
+ if (node.getNodeKind() != Type.DOCUMENT) {
+ NodeInfo parent = node.getParent();
+ if (parent != null) {
+ appendSequentialKey(((SiblingCountingNode)parent), sb, false);
+ }
+ if (node.getNodeKind() == Type.ATTRIBUTE) {
+ sb.append('A');
+ }
+ }
+ sb.append(alphaKey(node.getSiblingPosition()));
+ }
+
+
+ /**
+ * Construct an alphabetic key from an positive integer; the key collates in the same sequence
+ * as the integer
+ *
+ * @param value The positive integer key value (negative values are treated as zero).
+ * @return the alphabetic key value
+ */
+
+ /*@NotNull*/ public static String alphaKey(int value) {
+ if (value < 1) {
+ return "a";
+ }
+ if (value < 10) {
+ return "b" + value;
+ }
+ if (value < 100) {
+ return "c" + value;
+ }
+ if (value < 1000) {
+ return "d" + value;
+ }
+ if (value < 10000) {
+ return "e" + value;
+ }
+ if (value < 100000) {
+ return "f" + value;
+ }
+ if (value < 1000000) {
+ return "g" + value;
+ }
+ if (value < 10000000) {
+ return "h" + value;
+ }
+ if (value < 100000000) {
+ return "i" + value;
+ }
+ if (value < 1000000000) {
+ return "j" + value;
+ }
+ return "k" + value;
+ }
+
+ /**
+ * Test if one node is an ancestor-or-self of another
+ *
+ * @param a the putative ancestor-or-self node
+ * @param d the putative descendant node
+ * @return true if a is an ancestor-or-self of d
+ */
+
+ public static boolean isAncestorOrSelf(/*@NotNull*/ NodeInfo a, /*@NotNull*/ NodeInfo d) {
+ // Fast path for the TinyTree implementation
+ if (a instanceof TinyNodeImpl) {
+ if (d instanceof TinyNodeImpl) {
+ return ((TinyNodeImpl)a).isAncestorOrSelf((TinyNodeImpl)d);
+ } else if (d.getNodeKind() == Type.NAMESPACE) {
+ // fall through
+ } else {
+ return false;
+ }
+ }
+ // Generic implementation
+ NodeInfo p = d;
+ while (p != null) {
+ if (a.isSameNodeInfo(p)) {
+ return true;
+ }
+ p = p.getParent();
+ }
+ return false;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Helper classes to support axis iteration
+ ///////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Create an iterator over a singleton node, if it exists and matches a nodetest;
+ * otherwise return an empty iterator
+ * @param node the singleton node, or null if the node does not exist
+ * @param nodeTest the test to be applied
+ * @return an iterator over the node if it exists and matches the test.
+ */
+
+ /*@NotNull*/ public static AxisIterator filteredSingleton(/*@Nullable*/ NodeInfo node, /*@NotNull*/ NodeTest nodeTest) {
+ if (node != null && nodeTest.matches(node)) {
+ return SingleNodeIterator.makeIterator(node);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ }
+
+ /**
+ * AxisFilter is an iterator that applies a NodeTest filter to
+ * the nodes returned by an underlying AxisIterator.
+ */
+
+ public static class AxisFilter extends AxisIteratorImpl {
+ private AxisIterator base;
+ private NodeTest nodeTest;
+
+ /**
+ * Construct a AxisFilter
+ *
+ * @param base the underlying iterator that returns all the nodes on
+ * a required axis. This must not be an atomizing iterator!
+ * @param test a NodeTest that is applied to each node returned by the
+ * underlying AxisIterator; only those nodes that pass the NodeTest are
+ * returned by the AxisFilter
+ */
+
+ public AxisFilter(AxisIterator base, NodeTest test) {
+ this.base = base;
+ nodeTest = test;
+ position = 0;
+ }
+
+ /*@Nullable*/ public NodeInfo next() {
+ while (true) {
+ current = base.next();
+ if (current == null) {
+ position = -1;
+ return null;
+ }
+ if (nodeTest.matches(current)) {
+ position++;
+ return current;
+ }
+ }
+ }
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new AxisFilter(base.getAnother(), nodeTest);
+ }
+ }
+
+ /**
+ * EmptyTextFilter is an iterator that applies removes any zero-length text
+ * nodes returned by an underlying AxisIterator.
+ */
+
+ public static class EmptyTextFilter extends AxisIteratorImpl {
+ private AxisIterator base;
+
+ /**
+ * Construct an EmptyTextFilter
+ *
+ * @param base the underlying iterator that returns all the nodes on
+ * a required axis. This must not be an atomizing iterator
+ */
+
+ public EmptyTextFilter(AxisIterator base) {
+ this.base = base;
+ position = 0;
+ }
+
+ /*@Nullable*/ public NodeInfo next() {
+ while (true) {
+ NodeInfo curr = current = base.next();
+ if (curr == null) {
+ position = -1;
+ return null;
+ }
+ if (!(curr.getNodeKind() == Type.TEXT && curr.getStringValueCS().length() == 0)) {
+ position++;
+ return curr;
+ }
+ }
+ }
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new EmptyTextFilter(base.getAnother());
+ }
+ }
+
+
+ /**
+ * BaseEnumeration is an abstract implementation of an AxisIterator, it
+ * simplifies the implementation of the underlying AxisIterator by requiring
+ * it to provide only two methods: advance(), and getAnother().
+ *
+ * <p>BaseEnumeration takes responsibility for incrementing position
+ * when next() is called. The advance() method in a subclass should therefore
+ * not modify position.</p>
+ */
+
+ public static abstract class BaseEnumeration extends AxisIteratorImpl {
+
+ /*@Nullable*/ public final NodeInfo next() {
+ advance();
+ if (current == null) {
+ position = -1;
+ } else {
+ position++;
+ }
+ return current;
+ }
+
+ /**
+ * The advance() method must be provided in each concrete implementation.
+ * It must leave the variable current set to the next node to be returned in the
+ * iteration, or to null if there are no more nodes to be returned.
+ */
+
+ public abstract void advance();
+
+ /*@NotNull*/
+ public abstract AxisIterator getAnother();
+
+ }
+
+ /**
+ * General-purpose implementation of the ancestor and ancestor-or-self axes
+ */
+
+ public static final class AncestorEnumeration extends BaseEnumeration {
+
+ private boolean includeSelf;
+ private boolean atStart;
+ private NodeInfo start;
+
+ /**
+ * Create an iterator over the ancestor or ancestor-or-self axis
+ * @param start the initial context node
+ * @param includeSelf true if the "self" node is to be included
+ */
+
+ public AncestorEnumeration(NodeInfo start, boolean includeSelf) {
+ this.start = start;
+ this.includeSelf = includeSelf;
+ current = start;
+ atStart = true;
+ }
+
+ public void advance() {
+ if (atStart) {
+ atStart = false;
+ if (includeSelf) {
+ return;
+ }
+ }
+ current = (current == null ? null : current.getParent());
+ }
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new AncestorEnumeration(start, includeSelf);
+ }
+
+ } // end of class AncestorEnumeration
+
+ /**
+ * General-purpose implementation of the descendant and descendant-or-self axes,
+ * in terms of the child axis.
+ * But it also has the option to return the descendants in reverse document order;
+ * this is used when evaluating the preceding axis. Note that the includeSelf option
+ * should not be used when scanning in reverse order, as the self node will always be
+ * returned first.
+ */
+
+ public static final class DescendantEnumeration extends BaseEnumeration {
+
+ /*@Nullable*/ private AxisIterator children = null;
+ /*@Nullable*/ private AxisIterator descendants = null;
+ private NodeInfo start;
+ private boolean includeSelf;
+ private boolean forwards;
+ private boolean atEnd = false;
+
+ /**
+ * Create an iterator over the descendant or descendant-or-self axis
+ * @param start the initial context node
+ * @param includeSelf true if the "self" node is to be included
+ * @param forwards true for a forwards iteration, false for reverse order
+ */
+
+ public DescendantEnumeration(NodeInfo start,
+ boolean includeSelf, boolean forwards) {
+ this.start = start;
+ this.includeSelf = includeSelf;
+ this.forwards = forwards;
+ }
+
+ public void advance() {
+ if (descendants != null) {
+ NodeInfo nextd = descendants.next();
+ if (nextd != null) {
+ current = nextd;
+ return;
+ } else {
+ descendants = null;
+ }
+ }
+ if (children != null) {
+ NodeInfo n = children.next();
+ if (n != null) {
+ if (n.hasChildNodes()) {
+ if (forwards) {
+ descendants = new DescendantEnumeration(n, false, forwards);
+ current = n;
+ } else {
+ descendants = new DescendantEnumeration(n, true, forwards);
+ advance();
+ }
+ } else {
+ current = n;
+ }
+ } else {
+ if (forwards || !includeSelf) {
+ current = null;
+ } else {
+ atEnd = true;
+ children = null;
+ current = start;
+ }
+ }
+ } else if (atEnd) {
+ // we're just finishing a backwards scan
+ current = null;
+ } else {
+ // we're just starting...
+ if (start.hasChildNodes()) {
+ //children = new NodeWrapper.ChildEnumeration(start, true, forwards);
+ children = start.iterateAxis(AxisInfo.CHILD);
+ if (!forwards) {
+ if (children instanceof ReversibleIterator) {
+ children = (AxisIterator)((ReversibleIterator)children).getReverseIterator();
+ } else {
+ List<NodeInfo> list = new ArrayList<NodeInfo>(20);
+ AxisIterator forwards = start.iterateAxis(AxisInfo.CHILD);
+ while (true) {
+ NodeInfo n = forwards.next();
+ if (n == null) {
+ break;
+ }
+ list.add(n);
+ }
+ NodeInfo[] nodes = new NodeInfo[list.size()];
+ nodes = list.toArray(nodes);
+ children = new AxisIteratorOverSequence<NodeInfo>(
+ new ReverseArrayIterator<NodeInfo>(nodes, 0, nodes.length));
+ }
+ }
+ } else {
+ children = EmptyAxisIterator.emptyAxisIterator();
+ }
+ if (forwards && includeSelf) {
+ current = start;
+ } else {
+ advance();
+ }
+ }
+ }
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new DescendantEnumeration(start, includeSelf, forwards);
+ }
+
+ } // end of class DescendantEnumeration
+
+ /**
+ * General purpose implementation of the following axis, in terms of the
+ * ancestor, child, and following-sibling axes
+ */
+
+ public static final class FollowingEnumeration extends BaseEnumeration {
+ private NodeInfo start;
+ /*@NotNull*/ private AxisIterator ancestorEnum;
+ /*@Nullable*/ private AxisIterator siblingEnum = null;
+ /*@Nullable*/ private AxisIterator descendEnum = null;
+
+ /**
+ * Create an iterator over the "following" axis
+ * @param start the initial context node
+ */
+
+ public FollowingEnumeration(/*@NotNull*/ NodeInfo start) {
+ this.start = start;
+ ancestorEnum = new AncestorEnumeration(start, false);
+ switch (start.getNodeKind()) {
+ case Type.ELEMENT:
+ case Type.TEXT:
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ //siblingEnum = new NodeWrapper.ChildEnumeration(start, false, true);
+ // gets following siblings
+ siblingEnum = start.iterateAxis(AxisInfo.FOLLOWING_SIBLING);
+ break;
+ case Type.ATTRIBUTE:
+ case Type.NAMESPACE:
+ //siblingEnum = new NodeWrapper.ChildEnumeration((NodeWrapper)start.getParent(), true, true);
+ // gets children of the attribute's parent node
+ NodeInfo parent = start.getParent();
+ if (parent == null) {
+ siblingEnum = EmptyAxisIterator.emptyAxisIterator();
+ } else {
+ siblingEnum = parent.iterateAxis(AxisInfo.CHILD);
+ }
+ break;
+ default:
+ siblingEnum = EmptyAxisIterator.emptyAxisIterator();
+ }
+ //advance();
+ }
+
+ public void advance() {
+ if (descendEnum != null) {
+ NodeInfo nextd = descendEnum.next();
+ if (nextd != null) {
+ current = nextd;
+ return;
+ } else {
+ descendEnum = null;
+ }
+ }
+ if (siblingEnum != null) {
+ NodeInfo nexts = siblingEnum.next();
+ if (nexts != null) {
+ current = nexts;
+ NodeInfo n = current;
+ if (n.hasChildNodes()) {
+ descendEnum = new DescendantEnumeration(n, false, true);
+ } else {
+ descendEnum = null;
+ }
+ return;
+ } else {
+ descendEnum = null;
+ siblingEnum = null;
+ }
+ }
+ NodeInfo nexta = ancestorEnum.next();
+ if (nexta != null) {
+ current = nexta;
+ NodeInfo n = current;
+ if (n.getNodeKind() == Type.DOCUMENT) {
+ siblingEnum = EmptyAxisIterator.emptyAxisIterator();
+ } else {
+ //siblingEnum = new NodeWrapper.ChildEnumeration(next, false, true);
+ siblingEnum = n.iterateAxis(AxisInfo.FOLLOWING_SIBLING);
+ }
+ advance();
+ } else {
+ current = null;
+ }
+ }
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new FollowingEnumeration(start);
+ }
+
+ } // end of class FollowingEnumeration
+
+ /**
+ * Helper method to iterate over the preceding axis, or Saxon's internal
+ * preceding-or-ancestor axis, by making use of the ancestor, descendant, and
+ * preceding-sibling axes.
+ */
+
+ public static final class PrecedingEnumeration extends BaseEnumeration {
+
+ private NodeInfo start;
+ /*@NotNull*/ private AxisIterator ancestorEnum;
+ /*@Nullable*/ private AxisIterator siblingEnum = null;
+ /*@Nullable*/ private AxisIterator descendEnum = null;
+ private boolean includeAncestors;
+
+ /**
+ * Create an iterator for the preceding or "preceding-or-ancestor" axis (the latter being
+ * used internall to support xsl:number)
+ * @param start the initial context node
+ * @param includeAncestors true if ancestors of the initial context node are to be included
+ * in the result
+ */
+
+ public PrecedingEnumeration(/*@NotNull*/ NodeInfo start, boolean includeAncestors) {
+ this.start = start;
+ this.includeAncestors = includeAncestors;
+ ancestorEnum = new AncestorEnumeration(start, false);
+ switch (start.getNodeKind()) {
+ case Type.ELEMENT:
+ case Type.TEXT:
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ // get preceding-sibling enumeration
+ siblingEnum = start.iterateAxis(AxisInfo.PRECEDING_SIBLING);
+ break;
+ default:
+ siblingEnum = EmptyAxisIterator.emptyAxisIterator();
+ }
+ }
+
+ public void advance() {
+ if (descendEnum != null) {
+ NodeInfo nextd = descendEnum.next();
+ if (nextd != null) {
+ current = nextd;
+ return;
+ } else {
+ descendEnum = null;
+ }
+ }
+ if (siblingEnum != null) {
+ NodeInfo nexts = siblingEnum.next();
+ if (nexts != null) {
+ if (nexts.hasChildNodes()) {
+ descendEnum = new DescendantEnumeration(nexts, true, false);
+ advance();
+ } else {
+ descendEnum = null;
+ current = nexts;
+ }
+ return;
+ } else {
+ descendEnum = null;
+ siblingEnum = null;
+ }
+ }
+ NodeInfo nexta = ancestorEnum.next();
+ if (nexta != null) {
+ current = nexta;
+ NodeInfo n = current;
+ if (n.getNodeKind() == Type.DOCUMENT) {
+ siblingEnum = EmptyAxisIterator.emptyAxisIterator();
+ } else {
+ siblingEnum = n.iterateAxis(AxisInfo.PRECEDING_SIBLING);
+ }
+ if (!includeAncestors) {
+ advance();
+ }
+ } else {
+ current = null;
+ }
+ }
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new PrecedingEnumeration(start, includeAncestors);
+ }
+
+ } // end of class PrecedingEnumeration
+
+
+}
+
diff --git a/sf/saxon/tree/util/Orphan.java b/sf/saxon/tree/util/Orphan.java
new file mode 100644
index 0000000..7bcb35b
--- /dev/null
+++ b/sf/saxon/tree/util/Orphan.java
@@ -0,0 +1,844 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.EmptyAxisIterator;
+import net.sf.saxon.tree.iter.SingleNodeIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+/**
+ * A node (implementing the NodeInfo interface) representing an attribute, text node,
+ * comment, processing instruction, or namespace that has no parent (and of course no children).
+ * Exceptionally it is also used (during whitespace stripping) to represent a standalone element.
+ *
+ * <p>In general this class does not impose constraints defined in the data model: that is the responsibility
+ * of the client. For example, the class does not prevent you from creating a comment or text node that has
+ * a name or a non-trivial type annotation.</p>
+ *
+ * @author Michael H. Kay
+ */
+
+public final class Orphan implements MutableNodeInfo {
+
+ private short kind;
+ /*@Nullable*/ private NodeName nodeName = null;
+ private CharSequence stringValue;
+ private SchemaType typeAnnotation = null;
+ private Configuration config;
+ private String systemId;
+ private boolean isId;
+ private boolean isIdref;
+
+ /**
+ * Create an Orphan node
+ * @param config the Saxon configuration
+ */
+
+ public Orphan(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns the item itself
+ * @return this item
+ */
+
+ public Item head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ * @return a singleton iterator that returns this item
+ */
+
+ public SequenceIterator iterate() {
+ return SingletonIterator.makeIterator(this);
+ }
+
+
+ /**
+ * Set the node kind
+ * @param kind the kind of node, for example {@link Type#ELEMENT} or {@link Type#ATTRIBUTE}
+ */
+
+ public void setNodeKind(short kind) {
+ this.kind = kind;
+ }
+
+ /**
+ * Set the name of the node
+ * @param nodeName the name of the node. May be null for unnamed nodes such as text and comment nodes
+ */
+
+ public void setNodeName(/*@Nullable*/ NodeName nodeName) {
+ this.nodeName = nodeName;
+ }
+
+ /**
+ * Set the string value of the node
+ * @param stringValue the string value of the node
+ */
+
+ public void setStringValue(CharSequence stringValue) {
+ this.stringValue = stringValue;
+ }
+
+ /**
+ * Set the type annotation of the node
+ * @param typeAnnotation the type annotation
+ */
+
+ public void setTypeAnnotation(SchemaType typeAnnotation) {
+ this.typeAnnotation = typeAnnotation;
+ }
+
+ /**
+ * Set the type annotation of the node
+ * @param typeAnnotation the type annotation
+ */
+
+ public void setTypeAnnotation(int typeAnnotation) {
+ this.typeAnnotation = getConfiguration().getSchemaType(typeAnnotation);
+ }
+
+ /**
+ * Set the base URI of the node
+ * @param systemId the base URI of the node
+ */
+
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Set the isId property
+ * @param id the isId property
+ */
+
+ public void setIsId(boolean id) {
+ this.isId = id;
+ }
+
+ /**
+ * Set the isIdref property
+ * @param idref the isIdref property
+ */
+
+ public void setIsIdref(boolean idref) {
+ this.isIdref = idref;
+ }
+
+ /**
+ * Return the kind of node.
+ * @return one of the values Type.ELEMENT, Type.TEXT, Type.ATTRIBUTE, etc.
+ */
+
+ public int getNodeKind() {
+ return kind;
+ }
+
+ /**
+ * Get the typed value. The result of this method will always be consistent with the method
+ * {@link net.sf.saxon.om.Item#getTypedValue()}. However, this method is often more convenient and may be
+ * more efficient, especially in the common case where the value is expected to be a singleton.
+ *
+ * @return the typed value. If requireSingleton is set to true, the result will always be an
+ * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize() throws XPathException {
+ switch (getNodeKind()) {
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ return new StringValue(stringValue);
+ case Type.TEXT:
+ case Type.DOCUMENT:
+ case Type.NAMESPACE:
+ return new UntypedAtomicValue(stringValue);
+ default:
+ if (typeAnnotation == null || typeAnnotation == Untyped.getInstance() ||
+ typeAnnotation == BuiltInAtomicType.UNTYPED_ATOMIC) {
+ return new UntypedAtomicValue(stringValue);
+ } else {
+ return typeAnnotation.atomize(this);
+ }
+ }
+ }
+
+ /**
+ * Get the configuration
+ * @return the Saxon configuration object
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the name pool
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ /**
+ * Get the type annotation
+ */
+
+ public int getTypeAnnotation() {
+ SchemaType st = getSchemaType();
+ return (st == null ? -1 : st.getFingerprint());
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ public SchemaType getSchemaType() {
+ if (typeAnnotation == null) {
+ if (kind == Type.ELEMENT) {
+ return Untyped.getInstance();
+ } else if (kind == Type.ATTRIBUTE) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ }
+ return typeAnnotation;
+ }
+
+ /**
+ * Determine whether this is the same node as another node. <br />
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(NodeInfo other) {
+ return this==other;
+ }
+
+ /**
+ * The equals() method compares nodes for identity. It is defined to give the same result
+ * as isSameNodeInfo().
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ * @since 8.7 Previously, the effect of the equals() method was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics. It is safer to use isSameNodeInfo() for this reason.
+ * The equals() method has been defined because it is useful in contexts such as a Java Set or HashMap.
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof NodeInfo && isSameNodeInfo((NodeInfo)other);
+ }
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+
+ /**
+ * Get the System ID for the node.
+ * @return the System Identifier of the entity in the source document containing the node,
+ * or null if not known. Note this is not the same as the base URI: the base URI can be
+ * modified by xml:base, but the system ID cannot.
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
+ * in the node. This will be the same as the System ID unless xml:base has been used.
+ */
+
+ /*@Nullable*/ public String getBaseURI() {
+ if (kind == Type.PROCESSING_INSTRUCTION) {
+ return systemId;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get line number
+ * @return the line number of the node in its original source document; or -1 if not available
+ */
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+ /**
+ * Get column number
+ * @return the column number of the node in its original source document; or -1 if not available
+ */
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ * @param other The other node, whose position is to be compared with this node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public int compareOrder(/*@NotNull*/ NodeInfo other) {
+
+ // are they the same node?
+ if (this.isSameNodeInfo(other)) {
+ return 0;
+ }
+ return (this.hashCode() < other.hashCode() ? -1 : +1);
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+ if (kind == Type.ATTRIBUTE || kind == Type.NAMESPACE) {
+ throw new UnsupportedOperationException();
+ }
+ if (this.isSameNodeInfo(other)) {
+ return AxisInfo.SELF;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Return the string value of the node.
+ * @return the string value of the node
+ */
+
+ public String getStringValue() {
+ return stringValue.toString();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ return stringValue;
+ }
+
+ /**
+ * Get name code. The name code is a coded form of the node name: two nodes
+ * with the same name code have the same namespace URI, the same local name,
+ * and the same prefix. By masking the name code with &0xfffff, you get a
+ * fingerprint: two nodes with the same fingerprint have the same local name
+ * and namespace URI.
+ * @see net.sf.saxon.om.NamePool#allocate allocate
+ */
+
+ public int getNameCode() {
+ NodeName nn = nodeName;
+ if (nn == null) {
+ return -1;
+ } else {
+ return nn.allocateNameCode(getNamePool());
+ }
+ }
+
+ /**
+ * Get fingerprint. The fingerprint is a coded form of the expanded name
+ * of the node: two nodes
+ * with the same name code have the same namespace URI and the same local name.
+ * A fingerprint of -1 should be returned for a node with no name.
+ */
+
+ public int getFingerprint() {
+ if (nodeName == null) {
+ return -1;
+ } else {
+ return getNameCode() & NamePool.FP_MASK;
+ }
+ }
+
+ /**
+ * Get the local part of the name of this node. This is the name after the ":" if any.
+ * @return the local part of the name. For an unnamed node, returns "".
+ */
+
+ public String getLocalPart() {
+ if (nodeName == null) {
+ return "";
+ } else {
+ return nodeName.getLocalPart();
+ }
+ }
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ * @return The URI of the namespace of this node. For an unnamed node, return null.
+ * For a node with an empty prefix, return an empty string.
+ */
+
+ public String getURI() {
+ if (nodeName == null) {
+ return "";
+ } else {
+ return nodeName.getURI();
+ }
+ }
+
+ /**
+ * Get the prefix of the name of the node. This is defined only for elements and attributes.
+ * If the node has no prefix, or for other kinds of node, return a zero-length string.
+ *
+ * @return The prefix of the name of the node.
+ */
+
+ public String getPrefix() {
+ if (nodeName == null) {
+ return "";
+ } else {
+ return nodeName.getPrefix();
+ }
+ }
+
+ /**
+ * Get the display name of this node. For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ * @return The display name of this node.
+ * For a node with no name, return an empty string.
+ */
+
+ public String getDisplayName() {
+ if (nodeName == null) {
+ return "";
+ } else {
+ return nodeName.getDisplayName();
+ }
+ }
+
+ /**
+ * Get the NodeInfo object representing the parent of this node
+ * @return null - an Orphan has no parent.
+ */
+
+ /*@Nullable*/ public NodeInfo getParent() {
+ return null;
+ }
+
+ /**
+ * Return an iteration over the nodes reached by the given axis from this node
+ * @param axisNumber the axis to be searched, e.g. Axis.CHILD or Axis.ANCESTOR
+ * @return a SequenceIterator that scans the nodes reached by the axis in turn.
+ */
+
+ /*@NotNull*/ public AxisIterator iterateAxis(byte axisNumber) {
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR_OR_SELF:
+ case AxisInfo.DESCENDANT_OR_SELF:
+ case AxisInfo.SELF:
+ return SingleNodeIterator.makeIterator(this);
+ case AxisInfo.ANCESTOR:
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.CHILD:
+ case AxisInfo.DESCENDANT:
+ case AxisInfo.FOLLOWING:
+ case AxisInfo.FOLLOWING_SIBLING:
+ case AxisInfo.NAMESPACE:
+ case AxisInfo.PARENT:
+ case AxisInfo.PRECEDING:
+ case AxisInfo.PRECEDING_SIBLING:
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ return EmptyAxisIterator.emptyAxisIterator();
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+
+ /**
+ * Return an iteration over the nodes reached by the given axis from this node
+ * @param axisNumber the axis to be searched, e.g. Axis.CHILD or Axis.ANCESTOR
+ * @param nodeTest A pattern to be matched by the returned nodes
+ * @return a SequenceIterator that scans the nodes reached by the axis in turn.
+ */
+
+ /*@NotNull*/ public AxisIterator iterateAxis(byte axisNumber, /*@NotNull*/ NodeTest nodeTest) {
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR_OR_SELF:
+ case AxisInfo.DESCENDANT_OR_SELF:
+ case AxisInfo.SELF:
+ return Navigator.filteredSingleton(this, nodeTest);
+ case AxisInfo.ANCESTOR:
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.CHILD:
+ case AxisInfo.DESCENDANT:
+ case AxisInfo.FOLLOWING:
+ case AxisInfo.FOLLOWING_SIBLING:
+ case AxisInfo.NAMESPACE:
+ case AxisInfo.PARENT:
+ case AxisInfo.PRECEDING:
+ case AxisInfo.PRECEDING_SIBLING:
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ return EmptyAxisIterator.emptyAxisIterator();
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) {
+ return null;
+ }
+
+ /**
+ * Get the root node of this tree (not necessarily a document node).
+ * Always returns this node in the case of an Orphan node.
+ */
+
+ /*@NotNull*/ public NodeInfo getRoot() {
+ return this;
+ }
+
+ /**
+ * Get the root (document) node
+ * @return the DocumentInfo representing the containing document, or null if the
+ * node is not part of a document. Always null for an Orphan node.
+ */
+
+ /*@Nullable*/ public DocumentInfo getDocumentRoot() {
+ return null;
+ }
+
+ /**
+ * Determine whether the node has any children.
+ * @return false - an orphan node never has any children
+ */
+
+ public boolean hasChildNodes() {
+ return false;
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node.
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ * @param buffer a buffer, into which will be placed
+ * a string that uniquely identifies this node, within this
+ * document. The calling code prepends information to make the result
+ * unique across all documents.
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ buffer.append('Q');
+ buffer.append(Integer.toString(hashCode()));
+ }
+
+ /**
+ * Get the document number of the document containing this node. For a free-standing
+ * orphan node, just return the hashcode.
+ */
+
+ public long getDocumentNumber() {
+ return hashCode() & 0xffffff;
+ // lose the top bits because we need to subtract these values for comparison
+ }
+
+ /**
+ * Copy this node to a given outputter (deep copy)
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId) throws XPathException {
+ Navigator.copy(this, out, copyOptions, locationId);
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ /*@Nullable*/ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return null;
+ }
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ return isId || (kind == Type.ATTRIBUTE && nodeName.equals(StandardNames.XML_ID_NAME));
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return isIdref;
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return false;
+ }
+
+ /**
+ * Insert copies of a sequence of nodes as children of this node.
+ * <p/>
+ * <p>This method takes no action unless the target node is a document node or element node. It also
+ * takes no action in respect of any supplied nodes that are not elements, text nodes, comments, or
+ * processing instructions.</p>
+ * <p/>
+ * <p>The supplied nodes will be copied to form the new children. Adjacent text nodes will be merged, and
+ * zero-length text nodes removed.</p>
+ * @param source the nodes to be inserted
+ * @param atStart true if the new nodes are to be inserted before existing children; false if they are
+ * @param inherit true if the insert nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ */
+
+ public void insertChildren(NodeInfo[] source, boolean atStart, boolean inherit) {
+ // no action: node is not a document or element node
+ }
+
+ /**
+ * Insert copies of a sequence of nodes as siblings of this node.
+ * <p/>
+ * <p>This method takes no action unless the target node is an element, text node, comment, or
+ * processing instruction, and one that has a parent node. It also
+ * takes no action in respect of any supplied nodes that are not elements, text nodes, comments, or
+ * processing instructions.</p>
+ * <p/>
+ * <p>The supplied nodes must use the same data model implementation as the tree into which they
+ * will be inserted.</p>
+ * @param source the nodes to be inserted
+ * @param before true if the new nodes are to be inserted before the target node; false if they are
+ * @param inherit true if the insert nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ */
+
+ public void insertSiblings(NodeInfo[] source, boolean before, boolean inherit) {
+ // no action: node has no parent
+ }
+
+ /**
+ * Remove an attribute from this element node
+ * <p/>
+ * <p>If this node is not an element, or if it has no attribute with the specified name,
+ * this method takes no action.</p>
+ * <p/>
+ * <p>The attribute node itself is not modified in any way.</p>
+ * @param attribute the attribute node to be removed
+ */
+
+ public void removeAttribute(NodeInfo attribute) {
+ // no action: node is not an element
+ }
+
+ /**
+ * Add an attribute to this element node.
+ * <p/>
+ * <p>If this node is not an element, or if the supplied node is not an attribute, the method
+ * takes no action. If the element already has an attribute with this name, the existing attribute
+ * is replaced.</p>
+ * @param nameCode the name of the new attribute
+ * @param attType the type annotation of the new attribute
+ * @param value the string value of the new attribute
+ * @param properties properties including IS_ID and IS_IDREF properties
+ */
+
+ public void addAttribute(NodeName nameCode, SimpleType attType, CharSequence value, int properties) {
+ // no action: node is not an element
+ }
+
+ /**
+ * Delete this node (that is, detach it from its parent).
+ * <p>If this node has preceding and following siblings that are both text nodes,
+ * the two text nodes will be joined into a single text node (the identity of this node
+ * with respect to its predecessors is undefined).</p>
+ */
+
+ public void delete() {
+ // no action other than to mark it deleted: node has no parent from which it can be detached
+ kind = -1;
+ }
+
+ /**
+ * Test whether this MutableNodeInfo object represents a node that has been deleted.
+ * Generally, such a node is unusable, and any attempt to use it will result in an exception
+ * being thrown
+ * @return true if this node has been deleted
+ */
+
+ public boolean isDeleted() {
+ return kind == -1;
+ }
+
+ /**
+ * Replace this node with a given sequence of nodes
+ * @param replacement the replacement nodes
+ * @param inherit true if the replacement nodes are to inherit the namespaces of their new parent; false
+ * if such namespaces are to be undeclared
+ * @throws IllegalArgumentException if any of the replacement nodes is of the wrong kind. When replacing
+ * a child node, the replacement nodes must all be elements, text, comment, or PI nodes; when replacing
+ * an attribute, the replacement nodes must all be attributes.
+ * @throws IllegalStateException if this node is deleted or if it has no parent node.
+ */
+
+ public void replace(NodeInfo[] replacement, boolean inherit) {
+ throw new IllegalStateException("Cannot replace a parentless node");
+ }
+
+ /**
+ * Replace the string-value of this node. If applied to an element or document node, this
+ * causes all existing children to be deleted, and replaced with a new text node
+ * whose string value is the value supplied. The caller is responsible for checking
+ * that the value is valid, for example that comments do not contain a double hyphen; the
+ * implementation is not required to check for such conditions.
+ * @param stringValue the new string value
+ */
+
+ public void replaceStringValue(CharSequence stringValue) {
+ this.stringValue = stringValue;
+ }
+
+ /**
+ * Rename this node.
+ * <p>This call has no effect if applied to a nameless node, such as a text node or comment.</p>
+ * <p>If necessary, a new namespace binding will be added to the target element, or to the element
+ * parent of the target attribute</p>
+ *
+ * @param newNameCode the namecode of the new name in the name pool
+ * @throws IllegalArgumentException if the new name code is not present in the name pool, or if
+ * it has a (prefix, uri) pair in which the
+ * prefix is the same as that of an existing in-scope namespace binding and the uri is different from that
+ * namespace binding.
+ */
+
+ public void rename(NodeName newNameCode) {
+ if (kind==Type.ATTRIBUTE || kind==Type.PROCESSING_INSTRUCTION) {
+ nodeName = newNameCode;
+ }
+ }
+
+ /**
+ * Add a namespace binding (that is, a namespace node) to this element. This call has no effect if applied
+ * to a node other than an element.
+ * @param nscode The namespace code representing the (prefix, uri) pair of the namespace binding to be
+ * added. If the target element already has a namespace binding with this (prefix, uri) pair, the call has
+ * no effect. If the target element currently has a namespace binding with this prefix and a different URI, an
+ * exception is raised.
+ * @param inherit If true, the new namespace binding will be inherited by any children of the target element
+ * that do not already have a namespace binding for the specified prefix, recursively.
+ * If false, the new namespace binding will not be inherited.
+ * @throws IllegalArgumentException if the namespace code is not present in the namepool, or if the target
+ * element already has a namespace binding for this prefix
+ */
+
+ public void addNamespace(NamespaceBinding nscode, boolean inherit) {
+ // no action: node is not an element
+ }
+
+ /**
+ * Remove type information from this node (and its ancestors, recursively).
+ * This method implements the upd:removeType() primitive defined in the XQuery Update specification.
+ * (Note: the caller is responsible for updating the set of nodes marked for revalidation)
+ */
+
+ public void removeTypeAnnotation() {
+ typeAnnotation = BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+
+ /**
+ * Get a Builder suitable for building nodes that can be attached to this document.
+ * This implementation always throws an exception: the method should only be called on a document or element
+ * node when creating new children.
+ */
+
+ /*@NotNull*/ public Builder newBuilder() {
+ throw new UnsupportedOperationException("Cannot create children for an Orphan node");
+ }
+}
+
diff --git a/sf/saxon/tree/util/ProcInstParser.java b/sf/saxon/tree/util/ProcInstParser.java
new file mode 100644
index 0000000..c88bd8d
--- /dev/null
+++ b/sf/saxon/tree/util/ProcInstParser.java
@@ -0,0 +1,147 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+/**
+ * ProcInstParser is used to parse pseudo-attributes within Processing Instructions
+ * @author Michael H. Kay
+ * @version 10 July 2000
+ */
+
+
+public class ProcInstParser {
+
+ /**
+ * Class is never instantiated
+ */
+
+ private ProcInstParser() {
+ }
+
+ /**
+ * Get a pseudo-attribute. This is useful only if the processing instruction data part
+ * uses pseudo-attribute syntax, which it does not have to. This syntax is as described
+ * in the W3C Recommendation "Associating Style Sheets with XML Documents".
+ * @return the value of the pseudo-attribute if present, or null if not
+ */
+
+ /*@Nullable*/ public static String getPseudoAttribute(/*@NotNull*/ String content, /*@NotNull*/ String name) {
+
+ int pos = 0;
+ while (pos <= content.length()-4) { // minimum length of x=""
+ int nextQuote = -1;
+ for (int q=pos; q<content.length(); q++) {
+ if (content.charAt(q)=='"' || content.charAt(q)=='\'') {
+ nextQuote = q;
+ break;
+ }
+ }
+ if (nextQuote < 0) return null;
+
+ int closingQuote = content.indexOf(content.charAt(nextQuote), nextQuote+1);
+ if (closingQuote<0) return null;
+ int nextName = content.indexOf(name, pos);
+ if (nextName < 0) return null;
+ if (nextName < nextQuote) {
+ // check only spaces and equal signs between the name and the quote
+ boolean found = true;
+ for (int s = nextName + name.length(); s < nextQuote; s++) {
+ char c = content.charAt(s);
+ if (!Character.isWhitespace(c) && c!='=') {
+ found=false;
+ break;
+ }
+ }
+ if (found) {
+ String val = content.substring(nextQuote+1, closingQuote);
+ String u = unescape(val);
+ if (u == null) {
+ // unescaping failed
+ return val;
+ } else {
+ return u;
+ }
+ }
+ }
+ pos = closingQuote + 1;
+ }
+ return null;
+ }
+
+ /**
+ * Interpret character references and built-in entity references.
+ * @return value with character references and built-in entity references expanded.
+ * If badly-formed or unrecognized references are found, return null
+ */
+
+ /*@Nullable*/ private static String unescape(/*@NotNull*/ String value) {
+ if (value.indexOf('&')<0) return value;
+ FastStringBuffer sb = new FastStringBuffer(value.length());
+ for (int i=0; i<value.length(); i++) {
+ char c = value.charAt(i);
+ if (c=='&') {
+ if (i+2 < value.length() && value.charAt(i+1)=='#') {
+ if (value.charAt(i+2)=='x') {
+ int x = i+3;
+ int charval = 0;
+ while (x<value.length() && value.charAt(x)!=';') {
+ int digit = "0123456789abcdef".indexOf(value.charAt(x));
+ if (digit<0) {
+ digit = "0123456789ABCDEF".indexOf(value.charAt(x));
+ }
+ if (digit<0) {
+ return null;
+ }
+ charval = charval * 16 + digit;
+ x++;
+ }
+ char hexchar = (char)charval;
+ sb.append(hexchar);
+ i=x;
+ } else {
+ int x = i+2;
+ int charval = 0;
+ while (x<value.length() && value.charAt(x)!=';') {
+ int digit = "0123456789".indexOf(value.charAt(x));
+ if (digit<0) {
+ return null;
+ }
+ charval = charval * 10 + digit;
+ x++;
+ }
+ char decchar = (char)charval;
+ sb.append(decchar);
+ i=x;
+ }
+ } else if (value.substring(i+1).startsWith("lt;")) {
+ sb.append('<');
+ i+=3;
+ } else if (value.substring(i+1).startsWith("gt;")) {
+ sb.append('>');
+ i+=3;
+ } else if (value.substring(i+1).startsWith("amp;")) {
+ sb.append('&');
+ i+=4;
+ } else if (value.substring(i+1).startsWith("quot;")) {
+ sb.append('"');
+ i+=5;
+ } else if (value.substring(i+1).startsWith("apos;")) {
+ sb.append('\'');
+ i+=5;
+ } else {
+ return null;
+ }
+
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/sf/saxon/tree/util/SteppingNavigator.java b/sf/saxon/tree/util/SteppingNavigator.java
new file mode 100644
index 0000000..0805625
--- /dev/null
+++ b/sf/saxon/tree/util/SteppingNavigator.java
@@ -0,0 +1,344 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+import net.sf.saxon.om.FingerprintedNode;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.Type;
+
+
+/**
+ * The SteppingNavigator is a utility class containing methods to assist with navigating a tree whose nodes
+ * implement the {@link SteppingNode} interface
+ */
+
+public class SteppingNavigator {
+
+ /**
+ * Get the next following node after a given node
+ * @param start the starting node
+ * @param anchor the node whose descendants are being scanned; the scan terminates when
+ * the anchor node is reached
+ * @return the next node in document order after the starting node, excluding attributes and namespaces;
+ * or null if no such node is found
+ */
+
+ public static SteppingNode getFollowingNode(SteppingNode start, SteppingNode anchor) {
+ SteppingNode nodei = start.getFirstChild();
+ if (nodei != null) {
+ return nodei;
+ }
+ if (start.isSameNodeInfo(anchor)) {
+ return null;
+ }
+ nodei = start;
+ SteppingNode parenti = start.getParent();
+ do {
+ nodei = nodei.getNextSibling();
+ if (nodei != null) {
+ return nodei;
+ } else if (parenti.isSameNodeInfo(anchor)) {
+ return null;
+ }
+ nodei = parenti;
+ parenti = parenti.getParent();
+ } while (parenti != null);
+
+ return null;
+ }
+
+ /**
+ * Interface representing a function to step from one node to another within a tree
+ */
+
+ private interface Stepper {
+ /**
+ * Step from one node to another
+ * @param node the start node
+ * @return the end node
+ */
+ SteppingNode step(SteppingNode node);
+ }
+
+ /**
+ * Stepper that steps from one node in a document to the next node in document order,
+ * excluding attribute and namespace nodes, returning null when the root of the subtree
+ * is reached.
+ */
+
+ private static class FollowingNodeStepper implements Stepper {
+
+ SteppingNode anchor;
+
+ /**
+ * Create a stepper to step successively through all nodes in a subtree
+ * @param anchor the root of the subtree, marking the end point of the iteration
+ */
+
+ public FollowingNodeStepper(SteppingNode anchor) {
+ this.anchor = anchor;
+ }
+
+ public SteppingNode step(SteppingNode node) {
+ return getFollowingNode(node, anchor);
+ }
+ }
+
+ /**
+ * Stepper that steps from one node in a document to the next node in document order,
+ * excluding attribute and namespace nodes, returning null when the root of the subtree
+ * is reached, and including only nodes that match a specified node test
+ */
+
+ private static class FollowingFilteredNodeStepper implements Stepper {
+
+ SteppingNode anchor;
+ NodeTest test;
+
+ /**
+ * Create a stepper to step successively through selected nodes in a subtree
+ * @param anchor the root of the subtree, marking the end point of the iteration
+ * @param test the test that returned nodes must satisfy
+ */
+
+ public FollowingFilteredNodeStepper(SteppingNode anchor, NodeTest test) {
+ this.anchor = anchor;
+ this.test = test;
+ }
+
+ public SteppingNode step(SteppingNode node) {
+ do {
+ node = getFollowingNode(node, anchor);
+ } while (node != null && !test.matches(node));
+ return node;
+ }
+ }
+
+ /**
+ * Stepper that steps from one element in a document to the next element in document order,
+ * excluding attribute and namespace nodes, returning null when the root of the subtree
+ * is reached, and including only elements, with optional constraints on the namespace URI
+ * and/or local name.
+ */
+
+ private static class FollowingElementStepper implements Stepper {
+
+ SteppingNode anchor;
+ String uri;
+ String local;
+
+ /**
+ * Create a stepper to step successively through selected elements in a subtree
+ * @param anchor the root of the subtree, marking the end point of the iteration
+ * @param uri either null, or a namespace URI which the selected elements must match
+ * @param local either null, or a local name which the selected elements must match
+ */
+
+ public FollowingElementStepper(SteppingNode anchor, String uri, String local) {
+ this.anchor = anchor;
+ this.uri = uri;
+ this.local = local;
+ }
+
+ public SteppingNode step(SteppingNode node) {
+ return node.getSuccessorElement(anchor, uri, local);
+ }
+ }
+
+ /**
+ * Stepper that steps from one element in a document to the next element in document order,
+ * excluding attribute and namespace nodes, returning null when the root of the subtree
+ * is reached, and including only elements, with a constraint on the fingerprint of the element
+ */
+
+ private static class FollowingFingerprintedElementStepper implements Stepper {
+
+ SteppingNode anchor;
+ int fingerprint;
+
+ /**
+ * Create a stepper to step successively through selected elements in a subtree
+ * @param anchor the root of the subtree, marking the end point of the iteration
+ * @param fingerprint a fingerprint which selected elements must match
+ */
+
+ public FollowingFingerprintedElementStepper(SteppingNode anchor, int fingerprint) {
+ this.anchor = anchor;
+ this.fingerprint = fingerprint;
+ }
+
+ public SteppingNode step(SteppingNode node) {
+ do {
+ node = getFollowingNode(node, anchor);
+ } while (node != null && node.getFingerprint() != fingerprint);
+ return node;
+ }
+ }
+
+
+ /**
+ * An iterator over the descendant or descendant-or-self axis
+ */
+
+ public static class DescendantAxisIterator implements AxisIterator<NodeInfo> {
+
+ private SteppingNode start;
+ private boolean includeSelf;
+ private NodeTest test;
+
+ private SteppingNode current;
+ private int position;
+
+ private Stepper stepper;
+
+ /**
+ * Create an iterator over the descendant or descendant-or-self axis
+ * @param start the root of the subtree whose descendants are required
+ * @param includeSelf true if this is the descendant-or-self axis
+ * @param test the node-test that selected nodes must satisfy
+ */
+
+ public DescendantAxisIterator(SteppingNode start, boolean includeSelf, NodeTest test) {
+ this.start = start;
+ this.includeSelf = includeSelf;
+ this.test = test;
+
+ if (!(includeSelf && test.matches(start))) {
+ // initialize currNode to the start node if and only if this is NOT a descendant-or-self scan
+ current = start;
+ }
+
+ if (test == null || test == AnyNodeTest.getInstance()) {
+ stepper = new FollowingNodeStepper(start);
+ } else if (test instanceof NameTest) {
+ if (test.getPrimitiveType() == Type.ELEMENT) {
+ NameTest nt = (NameTest)test;
+ if (start instanceof FingerprintedNode) {
+ stepper = new FollowingFingerprintedElementStepper(start, nt.getFingerprint());
+ } else {
+ stepper = new FollowingElementStepper(start, nt.getNamespaceURI(), nt.getLocalPart());
+ }
+ } else {
+ stepper = new FollowingFilteredNodeStepper(start, test);
+ }
+ } else if (test instanceof NodeKindTest) {
+ if (test.getPrimitiveType() == Type.ELEMENT) {
+ stepper = new FollowingElementStepper(start, null, null);
+ } else {
+ stepper = new FollowingFilteredNodeStepper(start, test);
+ }
+ } else if (test instanceof LocalNameTest) {
+ if (test.getPrimitiveType() == Type.ELEMENT) {
+ LocalNameTest nt = (LocalNameTest)test;
+ stepper = new FollowingElementStepper(start, null, nt.getLocalName());
+ } else {
+ stepper = new FollowingFilteredNodeStepper(start, test);
+ }
+ } else if (test instanceof NamespaceTest) {
+ if (test.getPrimitiveType() == Type.ELEMENT) {
+ NamespaceTest nt = (NamespaceTest)test;
+ stepper = new FollowingElementStepper(start, nt.getNamespaceURI(), null);
+ } else {
+ stepper = new FollowingFilteredNodeStepper(start, test);
+ }
+ } else {
+ stepper = new FollowingFilteredNodeStepper(start, test);
+ }
+ position = 0;
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return (next() != null);
+ }
+
+
+ public SteppingNode next() {
+ if (current == null) {
+ // implies includeSelf: first time round, return the start node
+ current = start;
+ position = 1;
+ return start;
+ }
+ SteppingNode curr = stepper.step(current);
+
+ if (curr != null) {
+ position++;
+ } else {
+ position = -1;
+ }
+ return (current = curr);
+ }
+
+ public SteppingNode current() {
+ return (position()==0 ? null : current);
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ return current.iterateAxis(axis, test);
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ return current.atomize();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ return current.getStringValue();
+ }
+
+ /*@NotNull*/
+ public AxisIterator<NodeInfo> getAnother() {
+ return new DescendantAxisIterator(start, includeSelf, test);
+ }
+
+ public int getProperties() {
+ return 0;
+ }
+ }
+}
+
diff --git a/sf/saxon/tree/util/SteppingNode.java b/sf/saxon/tree/util/SteppingNode.java
new file mode 100644
index 0000000..100dcc1
--- /dev/null
+++ b/sf/saxon/tree/util/SteppingNode.java
@@ -0,0 +1,61 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.util;
+
+
+import net.sf.saxon.om.NodeInfo;
+
+/**
+ * This interface can be implemented by an implementation of NodeInfo to take advantage of a generic implementation
+ * of the descendant axis found in class {@link net.sf.saxon.tree.util.SteppingNavigator}
+ */
+public interface SteppingNode<N extends SteppingNode> extends NodeInfo {
+
+ /**
+ * Get the parent of this node
+ * @return the parent of this node; or null if it is the root of the tree
+ */
+
+ public N getParent();
+
+ /**
+ * Get the next sibling of this node
+ * @return the next sibling if there is one, or null otherwise
+ */
+ public N getNextSibling();
+
+ /**
+ * Get the previous sibling of this node
+ * @return the previous sibling if there is one, or null otherwise
+ */
+
+ public N getPreviousSibling();
+
+ /**
+ * Get the first child of this node
+ * @return the first child if there is one, or null otherwise
+ */
+
+ public N getFirstChild();
+
+ /**
+ * Find the next matching element in document order; that is, the first child element
+ * with the required name if there is one; otherwise the next sibling element
+ * if there is one; otherwise the next sibling element of the parent, grandparent, etc, up to the anchor element.
+ * @param anchor the root of the tree within which navigation is confined
+ * @param uri the required namespace URI, or null if any namespace is acceptable
+ * @param local the required local name, or null if any local name is acceptable
+ * @return the next element after this one in document order, with the given URI and local name
+ * if specified, or null if this is the last node in the document, or the last node
+ * within the subtree being navigated
+ */
+
+ public N getSuccessorElement(N anchor, String uri, String local);
+
+}
+
diff --git a/sf/saxon/tree/util/package.html b/sf/saxon/tree/util/package.html
new file mode 100644
index 0000000..9ee7ae6
--- /dev/null
+++ b/sf/saxon/tree/util/package.html
@@ -0,0 +1,31 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.tree.util</title>
+
+</head>
+ <body>
+ <p>This package defines a number of utility and helper classes for implementing tree models.</p>
+
+ <p>The {@link net.sf.saxon.tree.util.Navigator} class provides many utility methods, for example to implement various axes in terms
+ of other axes, and to support numbering.</p>
+
+ <p>The {@link net.sf.saxon.tree.util.Orphan} class is a general-purpose implementation of a node with no parent, no attributes, and no children.</p>
+
+
+
+
+ <p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+28 November 2011</i></p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/tree/wrapper/AbstractNodeWrapper.java b/sf/saxon/tree/wrapper/AbstractNodeWrapper.java
new file mode 100644
index 0000000..b4365b1
--- /dev/null
+++ b/sf/saxon/tree/wrapper/AbstractNodeWrapper.java
@@ -0,0 +1,644 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.NamespaceNode;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.EmptyAxisIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+/**
+ * A node in the XML parse tree representing an XML element, character content, or attribute.<P>
+ * This is the implementation of the NodeInfo interface used as a wrapper for JDOM nodes.
+ *
+ * @author Michael H. Kay
+ */
+
+public abstract class AbstractNodeWrapper implements NodeInfo, VirtualNode {
+
+ /**
+ * To implement {@link net.sf.saxon.om.Sequence}, this method returns the item itself
+ *
+ * @return this item
+ */
+
+ public final NodeInfo head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link net.sf.saxon.om.Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ *
+ * @return a singleton iterator that returns this item
+ */
+
+ public SequenceIterator<NodeInfo> iterate() {
+ return SingletonIterator.makeIterator((NodeInfo)this);
+ }
+
+ /**
+ * Get the node underlying this virtual node. If this is a VirtualNode the method
+ * will automatically drill down through several layers of wrapping.
+ *
+ * @return The underlying node.
+ */
+
+ public final Object getRealNode() {
+ return getUnderlyingNode();
+ }
+
+ /**
+ * Get the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return getDocumentRoot().getConfiguration();
+ }
+
+ /**
+ * Get the name pool for this node
+ *
+ * @return the NamePool
+ */
+
+ public NamePool getNamePool() {
+ return getConfiguration().getNamePool();
+ }
+
+ /**
+ * Get the typed value.
+ *
+ * @return the typed value. This will either be a single AtomicValue or a value whose items are
+ * atomic values.
+ * @since 8.5 - signature changed in 9.5
+ */
+
+ public AtomicSequence atomize() {
+ switch (getNodeKind()) {
+ case Type.COMMENT:
+ case Type.PROCESSING_INSTRUCTION:
+ return new StringValue(getStringValueCS());
+ default:
+ return new UntypedAtomicValue(getStringValueCS());
+ }
+ }
+
+ /**
+ * Get the type annotation
+ */
+
+ public int getTypeAnnotation() {
+ SchemaType st = getSchemaType();
+ return (st == null ? -1 : st.getFingerprint());
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+
+ public SchemaType getSchemaType() {
+ if (getNodeKind() == Type.ATTRIBUTE) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ } else {
+ return Untyped.getInstance();
+ }
+ }
+
+ /**
+ * Determine whether this is the same node as another node. <br />
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ *
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(NodeInfo other) {
+ if (!(other instanceof AbstractNodeWrapper)) {
+ return false;
+ }
+ AbstractNodeWrapper ow = (AbstractNodeWrapper) other;
+ return getUnderlyingNode().equals(ow.getUnderlyingNode());
+ }
+
+ /**
+ * The equals() method compares nodes for identity. It is defined to give the same result
+ * as isSameNodeInfo().
+ *
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ * @since 8.7 Previously, the effect of the equals() method was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics. It is safer to use isSameNodeInfo() for this reason.
+ * The equals() method has been defined because it is useful in contexts such as a Java Set or HashMap.
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof NodeInfo && isSameNodeInfo((NodeInfo) other);
+ }
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ *
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode() {
+ return getUnderlyingNode().hashCode();
+ }
+
+
+ /**
+ * Get the System ID for the node.
+ *
+ * @return the System Identifier of the entity in the source document containing the node,
+ * or null if not known. Note this is not the same as the base URI: the base URI can be
+ * modified by xml:base, but the system ID cannot.
+ */
+
+ public String getSystemId() {
+ return getDocumentRoot().getSystemId();
+ }
+
+ /**
+ * Set the system ID. Required because NodeInfo implements the JAXP Source interface
+ * @param uri the system ID.
+ */
+
+ public void setSystemId(String uri) {
+ getDocumentRoot().setSystemId(uri);
+ }
+
+ /**
+ * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
+ * in the node.
+ * @return the base URI of the node, taking into account xml:base attributes if present
+ */
+
+ public String getBaseURI() {
+ if (getNodeKind() == Type.NAMESPACE) {
+ return null;
+ }
+ NodeInfo n = this;
+ if (getNodeKind() != Type.ELEMENT) {
+ n = getParent();
+ }
+ // Look for an xml:base attribute
+ while (n != null) {
+ String xmlbase = n.getAttributeValue(NamespaceConstant.XML, "base");
+ if (xmlbase != null) {
+ return xmlbase;
+ }
+ n = n.getParent();
+ }
+ // if not found, return the base URI of the document node
+ return getDocumentRoot().getBaseURI();
+ }
+
+ /**
+ * Get line number
+ *
+ * @return the line number of the node in its original source document; or -1 if not available.
+ * Always returns -1 in this implementation.
+ */
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+ /**
+ * Get column number
+ *
+ * @return the column number of the node in its original source document; or -1 if not available
+ */
+
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+ return Navigator.comparePosition(this, other);
+ }
+
+ /**
+ * Return the string value of the node. The interpretation of this depends on the type
+ * of node. For an element it is the accumulated character content of the element,
+ * including descendant elements.
+ *
+ * @return the string value of the node
+ */
+
+ public String getStringValue() {
+ return getStringValueCS().toString();
+ }
+
+
+ /**
+ * Get name code. The name code is a coded form of the node name: two nodes
+ * with the same name code have the same namespace URI, the same local name,
+ * and the same prefix. By masking the name code with &0xfffff, you get a
+ * fingerprint: two nodes with the same fingerprint have the same local name
+ * and namespace URI.
+ *
+ * @see net.sf.saxon.om.NamePool#allocate allocate
+ */
+
+ public int getNameCode() {
+ switch (getNodeKind()) {
+ case Type.ELEMENT:
+ case Type.ATTRIBUTE:
+ case Type.PROCESSING_INSTRUCTION:
+ case Type.NAMESPACE:
+ return getNamePool().allocate(getPrefix(),
+ getURI(),
+ getLocalPart());
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Get fingerprint. The fingerprint is a coded form of the expanded name
+ * of the node: two nodes
+ * with the same name code have the same namespace URI and the same local name.
+ * A fingerprint of -1 should be returned for a node with no name.
+ */
+
+ public int getFingerprint() {
+ int nc = getNameCode();
+ if (nc == -1) {
+ return -1;
+ } else {
+ return nc & 0xfffff;
+ }
+ }
+
+
+ /**
+ * Get the display name of this node. For elements and attributes this is
+ * [prefix:]localname. For unnamed nodes, it is an empty string.
+ *
+ * @return The display name of this node. For a node with no name, return an
+ * empty string.
+ */
+
+ public String getDisplayName() {
+ String prefix = getPrefix();
+ String local = getLocalPart();
+ if (prefix.length() == 0) {
+ return local;
+ } else {
+ return prefix + ":" + local;
+ }
+ }
+
+ /**
+ * Get the string value of a given attribute of this node.
+ *
+ * <p>The default implementation is suitable for nodes other than elements.</p>
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+ public String getAttributeValue(String uri, String local) {
+ return null;
+ }
+
+
+
+ /**
+ * Return an iteration over the nodes reached by the given axis from this node
+ *
+ * @param axisNumber the axis to be used
+ * @return a SequenceIterator that scans the nodes reached by the axis in turn.
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber) {
+ return iterateAxis(axisNumber, AnyNodeTest.getInstance());
+ }
+
+ /**
+ * Return an iteration over the nodes reached by the given axis from this node.
+ *
+ * <p>This superclass provides implementations of the ancestor, ancestor-or-self,
+ * following, namespace, parent, preceding, self, and preceding-or-ancestor axes.
+ * The other axes are implemented by calling methods iterateAttributes(),
+ * iterateChildren(), iterateDescendants(), and iterateSiblings(), which must
+ * be provided in a subclass.</p>
+ *
+ * @param axisNumber the axis to be used
+ * @param nodeTest A pattern to be matched by the returned nodes
+ * @return a SequenceIterator that scans the nodes reached by the axis in turn.
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
+ int nodeKind = getNodeKind();
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR:
+ if (nodeKind == Type.DOCUMENT) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ return new Navigator.AxisFilter(
+ new Navigator.AncestorEnumeration(this, false),
+ nodeTest);
+
+ case AxisInfo.ANCESTOR_OR_SELF:
+ if (nodeKind == Type.DOCUMENT) {
+ return Navigator.filteredSingleton(this, nodeTest);
+ }
+ return new Navigator.AxisFilter(
+ new Navigator.AncestorEnumeration(this, true),
+ nodeTest);
+
+ case AxisInfo.ATTRIBUTE:
+ if (nodeKind != Type.ELEMENT) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ return iterateAttributes(nodeTest);
+
+ case AxisInfo.CHILD:
+ if (nodeKind == Type.ELEMENT || nodeKind == Type.DOCUMENT) {
+ return iterateChildren(nodeTest);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+
+ case AxisInfo.DESCENDANT:
+ if (nodeKind == Type.ELEMENT || nodeKind == Type.DOCUMENT) {
+ return iterateDescendants(nodeTest, false);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+
+ case AxisInfo.DESCENDANT_OR_SELF:
+ if (nodeKind == Type.ELEMENT || nodeKind == Type.DOCUMENT) {
+ return iterateDescendants(nodeTest, true);
+ } else {
+ return Navigator.filteredSingleton(this, nodeTest);
+ }
+
+ case AxisInfo.FOLLOWING:
+ return new Navigator.AxisFilter(
+ new Navigator.FollowingEnumeration(this),
+ nodeTest);
+
+ case AxisInfo.FOLLOWING_SIBLING:
+ switch (nodeKind) {
+ case Type.DOCUMENT:
+ case Type.ATTRIBUTE:
+ case Type.NAMESPACE:
+ return EmptyAxisIterator.emptyAxisIterator();
+ default:
+ return iterateSiblings(nodeTest, true);
+ }
+
+ case AxisInfo.NAMESPACE:
+ if (nodeKind != Type.ELEMENT) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ return NamespaceNode.makeIterator(this, nodeTest);
+
+ case AxisInfo.PARENT:
+ return Navigator.filteredSingleton(getParent(), nodeTest);
+
+ case AxisInfo.PRECEDING:
+ return new Navigator.AxisFilter(
+ new Navigator.PrecedingEnumeration(this, false),
+ nodeTest);
+
+ case AxisInfo.PRECEDING_SIBLING:
+ switch (nodeKind) {
+ case Type.DOCUMENT:
+ case Type.ATTRIBUTE:
+ case Type.NAMESPACE:
+ return EmptyAxisIterator.emptyAxisIterator();
+ default:
+ return iterateSiblings(nodeTest, false);
+ }
+
+ case AxisInfo.SELF:
+ return Navigator.filteredSingleton(this, nodeTest);
+
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ return new Navigator.AxisFilter(
+ new Navigator.PrecedingEnumeration(this, true),
+ nodeTest);
+
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+ /**
+ * Return an iterator over the attributes of this element node.
+ * This method is only called after checking that the node is an element.
+ *
+ * @param nodeTest a test that the returned attributes must satisfy
+ * @return an iterator over the attribute nodes. The order of the result,
+ * although arbitrary, must be consistent with document order.
+ */
+
+ protected abstract AxisIterator<NodeInfo> iterateAttributes(NodeTest nodeTest);
+
+ /**
+ * Return an iterator over the children of this node.
+ * This method is only called after checking that the node is an element or document.
+ *
+ * @param nodeTest a test that the returned attributes must satisfy
+ * @return an iterator over the child nodes, in document order.
+ */
+
+ protected abstract AxisIterator<NodeInfo> iterateChildren(NodeTest nodeTest);
+
+ /**
+ * Return an iterator over the siblings of this node.
+ * This method is only called after checking that the node is an element, text, comment, or PI node.
+ *
+ * @param nodeTest a test that the returned siblings must satisfy
+ * @param forwards true for following siblings, false for preceding siblings
+ * @return an iterator over the sibling nodes, in axis order.
+ */
+
+ protected abstract AxisIterator<NodeInfo> iterateSiblings(NodeTest nodeTest, boolean forwards);
+
+ /**
+ * Return an iterator over the descendants of this node.
+ * This method is only called after checking that the node is an element or document node.
+ *
+ * @param nodeTest a test that the returned descendants must satisfy
+ * @param includeSelf true if this node is to be included in the result
+ * @return an iterator over the sibling nodes, in axis order.
+ */
+
+ protected abstract AxisIterator<NodeInfo> iterateDescendants(NodeTest nodeTest, boolean includeSelf);
+// {
+// if (nodeTest == AnyNodeTest.getInstance()) {
+// return new Navigator.DescendantEnumeration(this, includeSelf, true);
+// } else {
+// return new Navigator.AxisFilter(
+// new Navigator.DescendantEnumeration(this, includeSelf, true),
+// nodeTest);
+// }
+// }
+
+
+ /**
+ * Get all namespace declarations and undeclarations defined on this element.
+ *
+ * <p>This method is intended primarily for internal use. User applications needing
+ * information about the namespace context of a node should use <code>iterateAxis(Axis.NAMESPACE)</code>.
+ * (However, not all implementations support the namespace axis, whereas all implementations are
+ * required to support this method.)</p>
+ *
+ * <p>This implementation of the method is suitable for all nodes other than elements; it returns
+ * an empty array.</p>
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p>
+ * For a node other than an element, the method returns null.</p>
+ */
+ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return new NamespaceBinding[0];
+ }
+
+ /**
+ * Get the root node - always a document node with this tree implementation
+ *
+ * @return the NodeInfo representing the containing document
+ */
+
+ public NodeInfo getRoot() {
+ return getDocumentRoot();
+ }
+
+
+ /**
+ * Determine whether the node has any children.
+ * This implementation calls iterateAxis, so the subclass implementation of iterateAxis
+ * must avoid calling this method.
+ */
+
+ public boolean hasChildNodes() {
+ switch (getNodeKind()) {
+ case Type.DOCUMENT:
+ case Type.ELEMENT:
+ return iterateAxis(AxisInfo.CHILD).moveNext();
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Get the document number of the document containing this node. For a free-standing
+ * orphan node, just return the hashcode.
+ */
+
+ public long getDocumentNumber() {
+ return getDocumentRoot().getDocumentNumber();
+ }
+
+ /**
+ * Copy this node to a given outputter (deep copy)
+ */
+
+ public void copy(Receiver out, int copyOptions, int locationId) throws XPathException {
+ Navigator.copy(this, out, copyOptions, locationId);
+ }
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID. This implementation always returns false
+ */
+
+ public boolean isId() {
+ return false;
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute.
+ * This implementation always returns false
+ */
+
+ public boolean isIdref() {
+ return false;
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property.
+ * This implementation always returns false
+ */
+
+ public boolean isNilled() {
+ return false;
+ }
+
+}
+
diff --git a/sf/saxon/tree/wrapper/AbstractVirtualNode.java b/sf/saxon/tree/wrapper/AbstractVirtualNode.java
new file mode 100644
index 0000000..7d4f65e
--- /dev/null
+++ b/sf/saxon/tree/wrapper/AbstractVirtualNode.java
@@ -0,0 +1,477 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.SchemaType;
+
+
+/**
+ * AbstractVirtualNode is an abstract superclass for VirtualNode implementations in which
+ * the underlying node is itself a Saxon NodeInfo.
+*/
+
+public abstract class AbstractVirtualNode implements VirtualNode {
+
+ protected NodeInfo node;
+ /*@Nullable*/ protected AbstractVirtualNode parent; // null means unknown
+ protected AbstractVirtualNode docWrapper;
+
+ /**
+ * To implement {@link Sequence}, this method returns the item itself
+ * @return this item
+ */
+
+ public Item head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ * @return a singleton iterator that returns this item
+ */
+
+ public SequenceIterator<NodeInfo> iterate() {
+ return SingletonIterator.makeIterator((NodeInfo)this);
+ }
+
+ /**
+ * Get the underlying node, to implement the VirtualNode interface
+ */
+
+ public NodeInfo getUnderlyingNode() {
+ return node;
+ }
+
+ /**
+ * Get the node underlying this virtual node. If this is a VirtualNode the method
+ * will automatically drill down through several layers of wrapping.
+ * @return The underlying node.
+ */
+
+ public Object getRealNode() {
+ Object u = this;
+ do {
+ u = ((VirtualNode)u).getUnderlyingNode();
+ } while (u instanceof VirtualNode);
+ return u;
+ }
+
+
+ /**
+ * Get the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return node.getConfiguration();
+ }
+
+ /**
+ * Get the name pool for this node
+ * @return the NamePool
+ */
+
+ public NamePool getNamePool() {
+ return node.getNamePool();
+ }
+
+ /**
+ * Return the type of node.
+ * @return one of the values Node.ELEMENT, Node.TEXT, Node.ATTRIBUTE, etc.
+ */
+
+ public int getNodeKind() {
+ return node.getNodeKind();
+ }
+
+ /**
+ * Get the typed value.
+ *
+ * @return the typed value. If requireSingleton is set to true, the result will always be an
+ * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize() throws XPathException {
+ // The default implementation atomizes the base node
+ return node.atomize();
+ }
+
+ /**
+ * Get the type annotation
+ * @return the type annotation of the base node
+ */
+
+ public int getTypeAnnotation() {
+ return node.getTypeAnnotation();
+ }
+
+ /**
+ * Get the type annotation
+ * @return the type annotation of the base node
+ */
+
+ public SchemaType getSchemaType() {
+ return node.getSchemaType();
+ }
+
+ /**
+ * Determine whether this is the same node as another node. <br />
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(/*@NotNull*/ NodeInfo other) {
+ if (other instanceof AbstractVirtualNode) {
+ return node.isSameNodeInfo(((AbstractVirtualNode)other).node);
+ } else {
+ return node.isSameNodeInfo(other);
+ }
+ }
+
+ /**
+ * The equals() method compares nodes for identity. It is defined to give the same result
+ * as isSameNodeInfo().
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ * @since 8.7 Previously, the effect of the equals() method was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics. It is safer to use isSameNodeInfo() for this reason.
+ * The equals() method has been defined because it is useful in contexts such as a Java Set or HashMap.
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof NodeInfo && isSameNodeInfo((NodeInfo)other);
+ }
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode() {
+ return node.hashCode() ^ 0x3c3c3c3c;
+ }
+
+ /**
+ * Get the System ID for the node.
+ * @return the System Identifier of the entity in the source document containing the node,
+ * or null if not known. Note this is not the same as the base URI: the base URI can be
+ * modified by xml:base, but the system ID cannot.
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return node.getSystemId();
+ }
+
+ public void setSystemId(String uri) {
+ node.setSystemId(uri);
+ }
+
+ /**
+ * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
+ * in the node. In the JDOM model, base URIs are held only an the document level. We don't
+ * currently take any account of xml:base attributes.
+ */
+
+ public String getBaseURI() {
+ return node.getBaseURI();
+ }
+
+ /**
+ * Get line number
+ * @return the line number of the node in its original source document; or -1 if not available
+ */
+
+ public int getLineNumber() {
+ return node.getLineNumber();
+ }
+
+ /**
+ * Get column number
+ * @return the column number of the node in its original source document; or -1 if not available
+ */
+
+ public int getColumnNumber() {
+ return node.getColumnNumber();
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ * @param other The other node, whose position is to be compared with this node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public int compareOrder(/*@NotNull*/ NodeInfo other) {
+ if (other instanceof AbstractVirtualNode) {
+ return node.compareOrder(((AbstractVirtualNode)other).node);
+ } else {
+ return node.compareOrder(other);
+ }
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+ if (other instanceof AbstractVirtualNode) {
+ return node.comparePosition(((AbstractVirtualNode)other).node);
+ } else {
+ return node.comparePosition(other);
+ }
+ }
+
+ /**
+ * Return the string value of the node. The interpretation of this depends on the type
+ * of node. For an element it is the accumulated character content of the element,
+ * including descendant elements.
+ * @return the string value of the node
+ */
+
+ public final String getStringValue() {
+ return getStringValueCS().toString();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ // default implementation returns the string value of the base node
+ return node.getStringValueCS();
+ }
+
+ /**
+ * Get name code. The name code is a coded form of the node name: two nodes
+ * with the same name code have the same namespace URI, the same local name,
+ * and the same prefix. By masking the name code with &0xfffff, you get a
+ * fingerprint: two nodes with the same fingerprint have the same local name
+ * and namespace URI.
+ * @see net.sf.saxon.om.NamePool#allocate allocate
+ */
+
+ public int getNameCode() {
+ return node.getNameCode();
+ }
+
+ /**
+ * Get fingerprint. The fingerprint is a coded form of the expanded name
+ * of the node: two nodes
+ * with the same name code have the same namespace URI and the same local name.
+ * A fingerprint of -1 should be returned for a node with no name.
+ */
+
+ public int getFingerprint() {
+ return node.getFingerprint();
+ }
+
+ /**
+ * Get the local part of the name of this node. This is the name after the ":" if any.
+ * @return the local part of the name. For an unnamed node, returns null, except for
+ * un unnamed namespace node, which returns "".
+ */
+
+ public String getLocalPart() {
+ return node.getLocalPart();
+ }
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ * @return The URI of the namespace of this node. For an unnamed node, return null.
+ * For a node with an empty prefix, return an empty string.
+ */
+
+ public String getURI() {
+ return node.getURI();
+ }
+
+ /**
+ * Get the prefix of the name of the node. This is defined only for elements and attributes.
+ * If the node has no prefix, or for other kinds of node, return a zero-length string.
+ *
+ * @return The prefix of the name of the node.
+ */
+
+ public String getPrefix() {
+ return node.getPrefix();
+ }
+
+ /**
+ * Get the display name of this node. For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ * @return The display name of this node.
+ * For a node with no name, return an empty string.
+ */
+
+ public String getDisplayName() {
+ return node.getDisplayName();
+ }
+
+ /**
+ * Return an iteration over the nodes reached by the given axis from this node
+ * @param axisNumber the axis to be used
+ * @param nodeTest A pattern to be matched by the returned nodes
+ * @return a SequenceIterator that scans the nodes reached by the axis in turn.
+ */
+
+ /*@NotNull*/ public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
+ return new Navigator.AxisFilter(iterateAxis(axisNumber), nodeTest);
+ }
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) {
+ return node.getAttributeValue(uri, local);
+ }
+
+ /**
+ * Get the root node - always a document node with this tree implementation
+ * @return the NodeInfo representing the containing document
+ */
+
+ public NodeInfo getRoot() {
+ return docWrapper;
+ }
+
+ /**
+ * Get the root (document) node
+ * @return the DocumentInfo representing the containing document
+ */
+
+ public DocumentInfo getDocumentRoot() {
+ return (DocumentInfo)docWrapper;
+ }
+
+ /**
+ * Determine whether the node has any children. <br />
+ * Note: the result is equivalent to <br />
+ * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
+ */
+
+ public boolean hasChildNodes() {
+ return node.hasChildNodes();
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node.
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ * @param buffer a buffer, into which will be placed
+ * a string that uniquely identifies this node, within this
+ * document. The calling code prepends information to make the result
+ * unique across all documents.
+ */
+
+ public void generateId(FastStringBuffer buffer) {
+ // Note: giving the node the same ID as its underlying node is slightly questionable; depends on usage
+ node.generateId(buffer);
+ }
+
+ /**
+ * Get the document number of the document containing this node. For a free-standing
+ * orphan node, just return the hashcode.
+ */
+
+ public long getDocumentNumber() {
+ return docWrapper.getDocumentNumber();
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return node.getDeclaredNamespaces(buffer);
+ }
+
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ return node.isId();
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return node.isIdref();
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return node.isNilled();
+ }
+
+}
+
diff --git a/sf/saxon/tree/wrapper/SiblingCountingNode.java b/sf/saxon/tree/wrapper/SiblingCountingNode.java
new file mode 100644
index 0000000..a3e0a88
--- /dev/null
+++ b/sf/saxon/tree/wrapper/SiblingCountingNode.java
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.om.NodeInfo;
+
+/**
+ * Interface that extends NodeInfo by providing a method to get the position
+ * of a node relative to its siblings.
+ */
+
+public interface SiblingCountingNode extends NodeInfo {
+
+ /**
+ * Get the index position of this node among its siblings (starting from 0)
+ * @return 0 for the first child, 1 for the second child, etc.
+ */
+ public int getSiblingPosition();
+}
+
diff --git a/sf/saxon/tree/wrapper/SpaceStrippedDocument.java b/sf/saxon/tree/wrapper/SpaceStrippedDocument.java
new file mode 100644
index 0000000..1d427d2
--- /dev/null
+++ b/sf/saxon/tree/wrapper/SpaceStrippedDocument.java
@@ -0,0 +1,240 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.ComplexType;
+import net.sf.saxon.type.SchemaType;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A SpaceStrippedDocument represents a view of a real Document in which selected
+ * whitespace text nodes are treated as having been stripped.
+ */
+
+public class SpaceStrippedDocument extends SpaceStrippedNode implements DocumentInfo {
+
+ private SpaceStrippingRule strippingRule;
+ private boolean preservesSpace;
+ private boolean containsAssertions;
+ private HashMap<String, Object> userData;
+
+ /**
+ * Create a space-stripped view of a document
+ * @param doc the underlying document
+ * @param strippingRule an object that contains the rules defining which whitespace
+ * text nodes are to be absent from the view
+ */
+
+ public SpaceStrippedDocument(/*@NotNull*/ DocumentInfo doc, SpaceStrippingRule strippingRule) {
+ node = doc;
+ parent = null;
+ docWrapper = this;
+ this.strippingRule = strippingRule;
+ preservesSpace = findPreserveSpace(doc);
+ containsAssertions = findAssertions(doc);
+ }
+
+ /**
+ * Create a wrapped node within this document
+ */
+
+ public SpaceStrippedNode wrap(NodeInfo node) {
+ return makeWrapper(node, this, null);
+ }
+
+ /**
+ * Ask whether the document contains any nodes whose type annotation is anything other than
+ * UNTYPED
+ *
+ * @return true if the document contains elements whose type is other than UNTYPED
+ */
+ public boolean isTyped() {
+ return ((DocumentInfo)node).isTyped();
+ }
+
+ /**
+ * Get the document's strippingRule
+ */
+
+ public SpaceStrippingRule getStrippingRule() {
+ return strippingRule;
+ }
+
+ /**
+ * Get the configuration previously set using setConfiguration
+ */
+
+ public Configuration getConfiguration() {
+ return node.getConfiguration();
+ }
+
+ /**
+ * Get the name pool used for the names in this document
+ */
+
+ public NamePool getNamePool() {
+ return node.getNamePool();
+ }
+
+ /**
+ * Get the unique document number
+ */
+
+ public long getDocumentNumber() {
+ return node.getDocumentNumber();
+ }
+
+ /**
+ * Get the element with a given ID, if any
+ * @param id the required ID value
+ * @param getParent
+ * @return the element with the given ID value, or null if there is none.
+ */
+
+ /*@Nullable*/ public NodeInfo selectID(String id, boolean getParent) {
+ NodeInfo n = ((DocumentInfo)node).selectID(id, false);
+ if (n==null) {
+ return null;
+ } else {
+ return makeWrapper(n, this, null);
+ }
+ }
+
+ /**
+ * Get the list of unparsed entities defined in this document
+ * @return an Iterator, whose items are of type String, containing the names of all
+ * unparsed entities defined in this document. If there are no unparsed entities or if the
+ * information is not available then an empty iterator is returned
+ */
+
+ public Iterator<String> getUnparsedEntityNames() {
+ return ((DocumentInfo)node).getUnparsedEntityNames();
+ }
+
+ /**
+ * Get the unparsed entity with a given name
+ * @param name the name of the entity
+ */
+
+ public String[] getUnparsedEntity(String name) {
+ return ((DocumentInfo)node).getUnparsedEntity(name);
+ }
+
+ /**
+ * Determine whether the wrapped document contains any xml:space="preserve" attributes. If it
+ * does, we will look for them when stripping individual nodes. It's more efficient to scan
+ * the document in advance checking for xml:space attributes than to look for them every time
+ * we hit a whitespace text node.
+ *
+ * @param doc the wrapper of the document node
+ * @return true if any element in the document has an xml:space attribute with the value "preserve"
+ */
+
+ private static boolean findPreserveSpace(/*@NotNull*/ DocumentInfo doc) {
+ AxisIterator iter = doc.iterateAxis(AxisInfo.DESCENDANT, NodeKindTest.ELEMENT);
+ while (true) {
+ NodeInfo node = iter.next();
+ if (node == null) {
+ return false;
+ }
+ String val = node.getAttributeValue(NamespaceConstant.XML, "space");
+ if ("preserve".equals(val)) {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Determine whether the wrapped document contains any nodes annotated with complex types that
+ * define assertions.
+ *
+ * @param doc the wrapper of the document node
+ * @return true if any element in the document has an xml:space attribute with the value "preserve"
+ */
+
+ private static boolean findAssertions(/*@NotNull*/ DocumentInfo doc) {
+ if (doc.isTyped()) {
+ AxisIterator iter = doc.iterateAxis(AxisInfo.DESCENDANT, NodeKindTest.ELEMENT);
+ while (true) {
+ NodeInfo node = iter.next();
+ if (node == null) {
+ return false;
+ }
+ SchemaType type = node.getSchemaType();
+ if (type.isComplexType() && ((ComplexType)type).hasAssertions()) {
+ return true;
+ }
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Ask whether the stripped document contains any xml:space="preserve" attributes.
+ * @return true if any element in the document has an xml:space attribute with the value "preserve"
+ */
+
+ public boolean containsPreserveSpace() {
+ return preservesSpace;
+ }
+
+ /**
+ * Ask whether the stripped document contain any nodes annotated with types that carry assertions
+ * @return true if any element in the document has a type that has an assertion
+ */
+
+ public boolean containsAssertions() {
+ return containsAssertions;
+ }
+
+
+ /**
+ * Set user data on the document node. The user data can be retrieved subsequently
+ * using {@link #getUserData}
+ * @param key A string giving the name of the property to be set. Clients are responsible
+ * for choosing a key that is likely to be unique. Must not be null. Keys used internally
+ * by Saxon are prefixed "saxon:".
+ * @param value The value to be set for the property. May be null, which effectively
+ * removes the existing value for the property.
+ */
+
+ public void setUserData(String key, /*@Nullable*/ Object value) {
+ if (userData == null) {
+ userData = new HashMap(4);
+ }
+ if (value == null) {
+ userData.remove(key);
+ } else {
+ userData.put(key, value);
+ }
+ }
+
+ /**
+ * Get user data held in the document node. This retrieves properties previously set using
+ * {@link #setUserData}
+ * @param key A string giving the name of the property to be retrieved.
+ * @return the value of the property, or null if the property has not been defined.
+ */
+
+ /*@Nullable*/ public Object getUserData(String key) {
+ if (userData == null) {
+ return null;
+ } else {
+ return userData.get(key);
+ }
+ }
+}
+
diff --git a/sf/saxon/tree/wrapper/SpaceStrippedNode.java b/sf/saxon/tree/wrapper/SpaceStrippedNode.java
new file mode 100644
index 0000000..f28fb4e
--- /dev/null
+++ b/sf/saxon/tree/wrapper/SpaceStrippedNode.java
@@ -0,0 +1,422 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.Stripper;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.EmptyAxisIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.ComplexType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.Whitespace;
+
+
+/**
+ * A StrippedNode is a view of a node, in a virtual tree that has whitespace
+ * text nodes stripped from it. All operations on the node produce the same result
+ * as operations on the real underlying node, except that iterations over the axes
+ * take care to skip whitespace-only text nodes that are supposed to be stripped.
+ * Note that this class is only used in cases where a pre-built tree is supplied as
+ * the input to a transformation, and where the stylesheet does whitespace stripping;
+ * if a SAXSource or StreamSource is supplied, whitespace is stripped as the tree
+ * is built.
+*/
+
+public class SpaceStrippedNode extends AbstractVirtualNode implements WrappingFunction {
+
+ protected SpaceStrippedNode() {}
+
+ /**
+ * This constructor is protected: nodes should be created using the makeWrapper
+ * factory method
+ * @param node The node to be wrapped
+ * @param parent The StrippedNode that wraps the parent of this node
+ */
+
+ protected SpaceStrippedNode(NodeInfo node, SpaceStrippedNode parent) {
+ this.node = node;
+ this.parent = parent;
+ }
+
+ /**
+ * Factory method to wrap a node with a wrapper that implements the Saxon
+ * NodeInfo interface.
+ * @param node The underlying node
+ * @param docWrapper The wrapper for the document node (must be supplied)
+ * @param parent The wrapper for the parent of the node (null if unknown)
+ * @return The new wrapper for the supplied node
+ */
+
+ /*@NotNull*/ protected static SpaceStrippedNode makeWrapper(NodeInfo node,
+ SpaceStrippedDocument docWrapper,
+ SpaceStrippedNode parent) {
+ SpaceStrippedNode wrapper = new SpaceStrippedNode(node, parent);
+ wrapper.docWrapper = docWrapper;
+ return wrapper;
+ }
+
+ /**
+ * Factory method to wrap a node within the same document as this node with a VirtualNode
+ * @param node The underlying node
+ * @param parent The wrapper for the parent of the node (null if unknown)
+ * @return The new wrapper for the supplied node
+ */
+
+ /*@NotNull*/ public VirtualNode makeWrapper(NodeInfo node, VirtualNode parent) {
+ SpaceStrippedNode wrapper = new SpaceStrippedNode(node, (SpaceStrippedNode)parent);
+ wrapper.docWrapper = this.docWrapper;
+ return wrapper;
+ }
+
+ /**
+ * Get the typed value.
+ *
+ * @return the typed value.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize() throws XPathException {
+ if (getNodeKind() == Type.ELEMENT) {
+ return getSchemaType().atomize(this);
+ } else {
+ return node.atomize();
+ }
+ }
+
+ /**
+ * Determine whether this is the same node as another node. <br />
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(/*@NotNull*/ NodeInfo other) {
+ if (other instanceof SpaceStrippedNode) {
+ return node.isSameNodeInfo(((SpaceStrippedNode)other).node);
+ } else {
+ return node.isSameNodeInfo(other);
+ }
+ }
+
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ * @param other The other node, whose position is to be compared with this node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public int compareOrder(/*@NotNull*/ NodeInfo other) {
+ if (other instanceof SpaceStrippedNode) {
+ return node.compareOrder(((SpaceStrippedNode)other).node);
+ } else {
+ return node.compareOrder(other);
+ }
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ @Override
+ public int comparePosition(NodeInfo other) {
+ if (other instanceof SpaceStrippedNode) {
+ return node.comparePosition(((SpaceStrippedNode)other).node);
+ } else {
+ return node.comparePosition(other);
+ }
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ // Might not be the same as the string value of the underlying node because of space stripping
+ switch (getNodeKind()) {
+ case Type.DOCUMENT:
+ case Type.ELEMENT:
+ AxisIterator iter = iterateAxis(AxisInfo.DESCENDANT, NodeKindTest.makeNodeKindTest(Type.TEXT));
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.SMALL);
+ while(true) {
+ NodeInfo it = iter.next();
+ if (it == null) {
+ break;
+ }
+ sb.append(it.getStringValueCS());
+ }
+ return sb.condense();
+ default:
+ return node.getStringValueCS();
+ }
+ }
+
+ /**
+ * Get the NodeInfo object representing the parent of this node
+ */
+
+ /*@Nullable*/ public NodeInfo getParent() {
+ if (parent==null) {
+ NodeInfo realParent = node.getParent();
+ if (realParent != null) {
+ parent = makeWrapper(realParent, (SpaceStrippedDocument)docWrapper, null);
+ }
+ }
+ return parent;
+ }
+
+ /**
+ * Return an iteration over the nodes reached by the given axis from this node
+ * @param axisNumber the axis to be used
+ * @return a SequenceIterator that scans the nodes reached by the axis in turn.
+ */
+
+ /*@Nullable*/ public AxisIterator iterateAxis(byte axisNumber) {
+ switch (axisNumber) {
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.NAMESPACE:
+ return new WrappingIterator(node.iterateAxis(axisNumber), this, this);
+ case AxisInfo.CHILD:
+ return new StrippingIterator(node.iterateAxis(axisNumber), this);
+ case AxisInfo.FOLLOWING_SIBLING:
+ case AxisInfo.PRECEDING_SIBLING:
+ SpaceStrippedNode parent = (SpaceStrippedNode)getParent();
+ if (parent == null) {
+ return EmptyAxisIterator.emptyAxisIterator();
+ } else {
+ return new StrippingIterator(node.iterateAxis(axisNumber), parent);
+ }
+ default:
+ return new StrippingIterator(node.iterateAxis(axisNumber), null);
+ }
+ }
+
+ /**
+ * Copy this node to a given outputter (deep copy)
+ */
+
+ public void copy(Receiver out, int copyOptions, int locationId) throws XPathException {
+ // The underlying code does not do whitespace stripping. So we need to interpose
+ // a stripper.
+ Stripper stripper = new Stripper(((SpaceStrippedDocument)docWrapper).getStrippingRule(), out);
+ node.copy(stripper, copyOptions, locationId);
+ }
+
+
+ /**
+ * A StrippingIterator delivers wrappers for the nodes delivered
+ * by its underlying iterator. It is used when whitespace stripping
+ * may be needed, e.g. for the child axis. It examines all text nodes
+ * encountered to see if they need to be stripped, and if so, it
+ * skips them.
+ */
+
+ private final class StrippingIterator implements AxisIterator {
+
+ AxisIterator base;
+ SpaceStrippedNode parent;
+ NodeInfo currentVirtualNode;
+ int position;
+
+ /**
+ * Create a StrippingIterator
+ * @param base The underlying iterator
+ * @param parent If all the nodes to be wrapped have the same parent,
+ * it can be specified here. Otherwise specify null.
+ */
+
+ public StrippingIterator(AxisIterator base, SpaceStrippedNode parent) {
+ this.base = base;
+ this.parent = parent;
+ position = 0;
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return (next() != null);
+ }
+
+
+ /*@Nullable*/ public NodeInfo next() {
+ NodeInfo nextRealNode;
+ while (true) {
+ nextRealNode = (NodeInfo)base.next();
+ if (nextRealNode==null) {
+ return null;
+ }
+ if (isPreserved(nextRealNode)) {
+ break;
+ }
+ // otherwise skip this whitespace text node
+ }
+
+ currentVirtualNode = makeWrapper(nextRealNode,(SpaceStrippedDocument)docWrapper, parent);
+ position++;
+ return currentVirtualNode;
+ }
+
+ private boolean isPreserved(/*@NotNull*/ NodeInfo nextRealNode) {
+ if (nextRealNode.getNodeKind() != Type.TEXT) {
+ return true;
+ }
+ if (!Whitespace.isWhite(nextRealNode.getStringValueCS())) {
+ return true;
+ }
+ NodeInfo actualParent =
+ (parent==null ? nextRealNode.getParent() : parent.node);
+
+ // if the node has a simple type annotation, it is preserved
+ SchemaType type = actualParent.getSchemaType();
+ if (type.isSimpleType() || ((ComplexType)type).isSimpleContent()) {
+ return true;
+ }
+
+ // if there is an ancestor with xml:space="preserve", it is preserved
+ if (((SpaceStrippedDocument)docWrapper).containsPreserveSpace()) {
+ NodeInfo p = actualParent;
+ // the document contains one or more xml:space="preserve" attributes, so we need to see
+ // if one of them is on an ancestor of this node
+ while (p.getNodeKind() == Type.ELEMENT) {
+ String val = p.getAttributeValue(NamespaceConstant.XML, "space");
+ if (val != null) {
+ if ("preserve".equals(val)) {
+ return true;
+ } else if ("default".equals(val)) {
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+ }
+
+ // if there is an ancestor whose type has an assertion, it is preserved
+ if (((SpaceStrippedDocument)docWrapper).containsAssertions()) {
+ NodeInfo p = actualParent;
+ // the document contains one or more xml:space="preserve" attributes, so we need to see
+ // if one of them is on an ancestor of this node
+ while (p.getNodeKind() == Type.ELEMENT) {
+ SchemaType t = p.getSchemaType();
+ if (t instanceof ComplexType && ((ComplexType)t).hasAssertions()) {
+ return true;
+ }
+ p = p.getParent();
+ }
+ }
+
+ // otherwise it depends on xsl:strip-space
+ try {
+ byte preserve = ((SpaceStrippedDocument)docWrapper).getStrippingRule().isSpacePreserving(new NameOfNode(actualParent));
+ return preserve == Stripper.ALWAYS_PRESERVE;
+ } catch (XPathException e) {
+ // Ambiguity between strip-space and preserve-space. Because we're in an axis iterator,
+ // we don't get an opportunity to fail, so take the recovery action.
+ return true;
+ }
+ }
+
+ public NodeInfo current() {
+ return currentVirtualNode;
+ }
+
+ public int position() {
+ return position;
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ return currentVirtualNode.iterateAxis(axis, test);
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ return currentVirtualNode.atomize();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ return currentVirtualNode.getStringValue();
+ }
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new StrippingIterator(base.getAnother(), parent);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+ } // end of class StrippingIterator
+
+
+
+}
+
diff --git a/sf/saxon/tree/wrapper/TypeStrippedDocument.java b/sf/saxon/tree/wrapper/TypeStrippedDocument.java
new file mode 100644
index 0000000..40b58ba
--- /dev/null
+++ b/sf/saxon/tree/wrapper/TypeStrippedDocument.java
@@ -0,0 +1,174 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Untyped;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+
+/**
+ * A TypeStrippedDocument represents a view of a real Document in which all nodes are
+ * untyped
+ */
+
+public class TypeStrippedDocument extends TypeStrippedNode implements DocumentInfo {
+
+ private HashMap<String, Object> userData;
+
+ /**
+ * Create a type-stripped view of a document
+ * @param doc the underlying document
+ */
+
+ public TypeStrippedDocument(DocumentInfo doc) {
+ node = doc;
+ parent = null;
+ docWrapper = this;
+ }
+
+ /**
+ * Create a wrapped node within this document
+ */
+
+ public TypeStrippedNode wrap(NodeInfo node) {
+ return makeWrapper(node, this, null);
+ }
+
+ /**
+ * Get the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return node.getConfiguration();
+ }
+
+ /**
+ * Get the name pool used for the names in this document
+ */
+
+ public NamePool getNamePool() {
+ return node.getNamePool();
+ }
+
+ /**
+ * Get the unique document number
+ */
+
+ public long getDocumentNumber() {
+ return node.getDocumentNumber();
+ }
+
+ /**
+ * Ask whether the document contains any nodes whose type annotation is anything other than
+ * UNTYPED
+ *
+ * @return true if the document contains elements whose type is other than UNTYPED
+ */
+ public boolean isTyped() {
+ return false;
+ }
+
+ /**
+ * Get the element with a given ID, if any
+ * @param id the required ID value
+ * @param getParent
+ * @return the element with the given ID value, or null if there is none.
+ */
+
+ /*@Nullable*/ public NodeInfo selectID(String id, boolean getParent) {
+ NodeInfo n = ((DocumentInfo)node).selectID(id, false);
+ if (n==null) {
+ return null;
+ } else {
+ return makeWrapper(n, this, null);
+ }
+ }
+
+ /**
+ * Get the list of unparsed entities defined in this document
+ * @return an Iterator, whose items are of type String, containing the names of all
+ * unparsed entities defined in this document. If there are no unparsed entities or if the
+ * information is not available then an empty iterator is returned
+ */
+
+ public Iterator<String> getUnparsedEntityNames() {
+ return ((DocumentInfo)node).getUnparsedEntityNames();
+ }
+
+ /**
+ * Get the unparsed entity with a given name
+ * @param name the name of the entity
+ */
+
+ public String[] getUnparsedEntity(String name) {
+ return ((DocumentInfo)node).getUnparsedEntity(name);
+ }
+
+ /**
+ * Get the type annotation of this node. This implementation always returns XS_UNTYPED.
+ * @return XS_UNTYPED
+ */
+
+ public int getTypeAnnotation() {
+ return StandardNames.XS_UNTYPED;
+ }
+
+ /**
+ * Get the type annotation. This implementation always returns XS_UNTYPED.
+ * @return XS_UNTYPED
+ */
+ @Override
+ public SchemaType getSchemaType() {
+ return Untyped.getInstance();
+ }
+
+ /**
+ * Set user data on the document node. The user data can be retrieved subsequently
+ * using {@link #getUserData}
+ * @param key A string giving the name of the property to be set. Clients are responsible
+ * for choosing a key that is likely to be unique. Must not be null. Keys used internally
+ * by Saxon are prefixed "saxon:".
+ * @param value The value to be set for the property. May be null, which effectively
+ * removes the existing value for the property.
+ */
+
+ public void setUserData(String key, /*@Nullable*/ Object value) {
+ if (userData == null) {
+ userData = new HashMap(4);
+ }
+ if (value == null) {
+ userData.remove(key);
+ } else {
+ userData.put(key, value);
+ }
+ }
+
+ /**
+ * Get user data held in the document node. This retrieves properties previously set using
+ * {@link #setUserData}
+ * @param key A string giving the name of the property to be retrieved.
+ * @return the value of the property, or null if the property has not been defined.
+ */
+
+ /*@Nullable*/ public Object getUserData(String key) {
+ if (userData == null) {
+ return null;
+ } else {
+ return userData.get(key);
+ }
+ }
+}
+
diff --git a/sf/saxon/tree/wrapper/TypeStrippedNode.java b/sf/saxon/tree/wrapper/TypeStrippedNode.java
new file mode 100644
index 0000000..5ba838c
--- /dev/null
+++ b/sf/saxon/tree/wrapper/TypeStrippedNode.java
@@ -0,0 +1,195 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+
+/**
+ * A TypeStrippedNode is a view of a node, in a virtual tree that has type
+ * annotations stripped from it.
+*/
+
+public class TypeStrippedNode extends AbstractVirtualNode implements WrappingFunction {
+
+ protected TypeStrippedNode() {}
+
+ /**
+ * This constructor is protected: nodes should be created using the makeWrapper
+ * factory method
+ * @param node The node to be wrapped
+ * @param parent The StrippedNode that wraps the parent of this node
+ */
+
+ protected TypeStrippedNode(NodeInfo node, TypeStrippedNode parent) {
+ this.node = node;
+ this.parent = parent;
+ }
+
+ /**
+ * Factory method to wrap a node with a wrapper that implements the Saxon
+ * NodeInfo interface.
+ * @param node The underlying node
+ * @param docWrapper The wrapper for the document node (must be supplied)
+ * @param parent The wrapper for the parent of the node (null if unknown)
+ * @return The new wrapper for the supplied node
+ */
+
+ /*@NotNull*/ public static TypeStrippedNode makeWrapper(NodeInfo node,
+ TypeStrippedDocument docWrapper,
+ TypeStrippedNode parent) {
+ TypeStrippedNode wrapper = new TypeStrippedNode(node, parent);
+ wrapper.docWrapper = docWrapper;
+ return wrapper;
+ }
+
+ /**
+ * Factory method to wrap a node with a VirtualNode
+ * @param node The underlying node
+ * @param parent The wrapper for the parent of the node (null if unknown)
+ * @return The new wrapper for the supplied node
+ */
+
+ /*@NotNull*/ public VirtualNode makeWrapper(NodeInfo node, VirtualNode parent) {
+ TypeStrippedNode wrapper = new TypeStrippedNode(node, (TypeStrippedNode)parent);
+ wrapper.docWrapper = this.docWrapper;
+ return wrapper;
+ }
+
+ /**
+ * Get the typed value.
+ *
+ * @return the typed value. If requireSingleton is set to true, the result will always be an
+ * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize() throws XPathException {
+ return new UntypedAtomicValue(getStringValueCS());
+ }
+
+ /**
+ * Get the type annotation
+ * @return untyped or untypedAtomic (the purpose of this class is to strip the type annotation)
+ */
+
+ public int getTypeAnnotation() {
+ if (getNodeKind() == Type.ELEMENT) {
+ return StandardNames.XS_UNTYPED;
+ } else {
+ return StandardNames.XS_UNTYPED_ATOMIC;
+ }
+ }
+
+ /**
+ * Get the type annotation
+ *
+ * @return the type annotation of the base node
+ */
+ @Override
+ public SchemaType getSchemaType() {
+ if (getNodeKind() == Type.ELEMENT) {
+ return Untyped.getInstance();
+ } else {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+ }
+
+ /**
+ * Determine whether this is the same node as another node. <br />
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(/*@NotNull*/ NodeInfo other) {
+ if (other instanceof TypeStrippedNode) {
+ return node.isSameNodeInfo(((TypeStrippedNode)other).node);
+ } else {
+ return node.isSameNodeInfo(other);
+ }
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ * @param other The other node, whose position is to be compared with this node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public int compareOrder(/*@NotNull*/ NodeInfo other) {
+ if (other instanceof TypeStrippedNode) {
+ return node.compareOrder(((TypeStrippedNode)other).node);
+ } else {
+ return node.compareOrder(other);
+ }
+ }
+
+ /**
+ * Get the NodeInfo object representing the parent of this node
+ */
+
+ /*@Nullable*/ public NodeInfo getParent() {
+ if (parent==null) {
+ NodeInfo realParent = node.getParent();
+ if (realParent != null) {
+ parent = makeWrapper(realParent, ((TypeStrippedDocument)docWrapper), null);
+ }
+ }
+ return parent;
+ }
+
+ /**
+ * Return an iteration over the nodes reached by the given axis from this node
+ * @param axisNumber the axis to be used
+ * @return a SequenceIterator that scans the nodes reached by the axis in turn.
+ */
+
+ /*@Nullable*/ public AxisIterator iterateAxis(byte axisNumber) {
+ return new WrappingIterator(node.iterateAxis(axisNumber), this, null);
+ }
+
+ /**
+ * Get the root (document) node
+ * @return the DocumentInfo representing the containing document
+ */
+
+ /*@NotNull*/ public DocumentInfo getDocumentRoot() {
+ return (DocumentInfo)docWrapper;
+ }
+
+ /**
+ * Copy this node to a given outputter (deep copy)
+ */
+
+ public void copy(Receiver out, int copyOptions, int locationId) throws XPathException {
+ node.copy(out, copyOptions & ~CopyOptions.TYPE_ANNOTATIONS, locationId);
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property
+ */
+ @Override
+ public boolean isNilled() {
+ return false;
+ }
+}
+
diff --git a/sf/saxon/tree/wrapper/VirtualCopy.java b/sf/saxon/tree/wrapper/VirtualCopy.java
new file mode 100644
index 0000000..5e5f6b4
--- /dev/null
+++ b/sf/saxon/tree/wrapper/VirtualCopy.java
@@ -0,0 +1,874 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * This class represents a node that is a virtual copy of another node: that is, it behaves as a node that's the
+ * same as another node, but has different identity. Moreover, this class can create a virtual copy of a subtree,
+ * so that the parent of the virtual copy is null rather than being a virtual copy of the parent of the original.
+ * This means that none of the axes when applied to the virtual copy is allowed to stray outside the subtree.
+ * The virtual copy is implemented by means of a reference to the node of which
+ * it is a copy, but methods that are sensitive to node identity return a different result.
+ */
+
+public class VirtualCopy implements NodeInfo, SourceLocator {
+
+ protected String systemId;
+ protected long documentNumber;
+ protected NodeInfo original;
+ protected VirtualCopy parent;
+ protected NodeInfo root; // the node forming the root of the subtree that was copied
+
+ /**
+ * Protected constructor: create a virtual copy of a node
+ *
+ * @param base the node to be copied
+ */
+
+ protected VirtualCopy(/*@NotNull*/ NodeInfo base) {
+ original = base;
+ systemId = base.getBaseURI();
+ }
+
+ /**
+ * Public factory method: create a virtual copy of a node
+ *
+ * @param original the node to be copied
+ * @param root the root of the tree containing the node to be copied
+ * @return the virtual copy. If the original was already a virtual copy, this will be a virtual copy
+ * of the real underlying node.
+ */
+
+ public static VirtualCopy makeVirtualCopy(NodeInfo original, NodeInfo root) {
+
+ VirtualCopy vc;
+ // Don't allow copies of copies of copies: define the new copy in terms of the original
+ while (original instanceof VirtualCopy) {
+ original = ((VirtualCopy) original).original;
+ }
+ while (root instanceof VirtualCopy) {
+ root = ((VirtualCopy) root).original;
+ }
+ if (original.getNodeKind() == Type.DOCUMENT) {
+ vc = new VirtualDocumentCopy((DocumentInfo) original);
+ } else {
+ vc = new VirtualCopy(original);
+ }
+ vc.root = root;
+ return vc;
+ }
+
+ /**
+ * Wrap a node in a VirtualCopy.
+ *
+ * @param node the node to be wrapped
+ * @return a virtual copy of the node
+ */
+
+ /*@NotNull*/
+ protected VirtualCopy wrap(/*@NotNull*/ NodeInfo node) {
+ return new VirtualCopy(node);
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns the item itself
+ * @return this item
+ */
+
+ public Item head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ * @return a singleton iterator that returns this item
+ */
+
+ public SequenceIterator iterate() {
+ return SingletonIterator.makeIterator(this);
+ }
+
+ /**
+ * Set the unique document number of the virtual document. This method must be called to ensure
+ * that nodes in the virtual copy have unique node identifiers
+ *
+ * @param documentNumber the document number to be allocated. This can be obtained from the
+ * {@link net.sf.saxon.tree.util.DocumentNumberAllocator} which is accessible from the Configuration using
+ * {@link net.sf.saxon.Configuration#getDocumentNumberAllocator()}
+ */
+
+ public void setDocumentNumber(long documentNumber) {
+ this.documentNumber = documentNumber;
+ }
+
+ /**
+ * Get the kind of node. This will be a value such as Type.ELEMENT or Type.ATTRIBUTE
+ *
+ * @return an integer identifying the kind of node. These integer values are the
+ * same as those used in the DOM
+ * @see net.sf.saxon.type.Type
+ */
+
+ public int getNodeKind() {
+ return original.getNodeKind();
+ }
+
+ /**
+ * Determine whether this is the same node as another node.
+ * Note: a.isSameNodeInfo(b) if and only if generateId(a)==generateId(b).
+ * This method has the same semantics as isSameNode() in DOM Level 3, but
+ * works on Saxon NodeInfo objects rather than DOM Node objects.
+ *
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(/*@NotNull*/ NodeInfo other) {
+ return other instanceof VirtualCopy &&
+ documentNumber == other.getDocumentNumber() &&
+ original.isSameNodeInfo(((VirtualCopy) other).original);
+
+
+ }
+
+ /**
+ * The equals() method compares nodes for identity. It is defined to give the same result
+ * as isSameNodeInfo().
+ *
+ * @param other the node to be compared with this node
+ * @return true if this NodeInfo object and the supplied NodeInfo object represent
+ * the same node in the tree.
+ * @since 8.7 Previously, the effect of the equals() method was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics. It is safer to use isSameNodeInfo() for this reason.
+ * The equals() method has been defined because it is useful in contexts such as a Java Set or HashMap.
+ */
+
+ public boolean equals(Object other) {
+ return other instanceof NodeInfo && isSameNodeInfo((NodeInfo) other);
+ }
+
+ /**
+ * The hashCode() method obeys the contract for hashCode(): that is, if two objects are equal
+ * (represent the same node) then they must have the same hashCode()
+ *
+ * @since 8.7 Previously, the effect of the equals() and hashCode() methods was not defined. Callers
+ * should therefore be aware that third party implementations of the NodeInfo interface may
+ * not implement the correct semantics.
+ */
+
+ public int hashCode() {
+ return original.hashCode() ^ ((int) (documentNumber & 0x7fffffff) << 19);
+ }
+
+ /**
+ * Get the System ID for the node.
+ *
+ * @return the System Identifier of the entity in the source document
+ * containing the node, or null if not known. Note this is not the
+ * same as the base URI: the base URI can be modified by xml:base, but
+ * the system ID cannot.
+ */
+
+ public String getSystemId() {
+ return systemId;
+ }
+
+ /**
+ * Get the Base URI for the node, that is, the URI used for resolving a relative URI contained
+ * in the node. This will be the same as the System ID unless xml:base has been used.
+ *
+ * @return the base URI of the node
+ */
+
+ /*@Nullable*/
+ public String getBaseURI() {
+ return Navigator.getBaseURI(this);
+ }
+
+ /**
+ * Get line number
+ *
+ * @return the line number of the node in its original source document; or
+ * -1 if not available
+ */
+
+ public int getLineNumber() {
+ return original.getLineNumber();
+ }
+
+ /**
+ * Get column number
+ *
+ * @return the column number of the node in its original source document; or -1 if not available
+ */
+
+ public int getColumnNumber() {
+ return original.getColumnNumber();
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return -1 if this node precedes the other node, +1 if it follows the
+ * other node, or 0 if they are the same node. (In this case,
+ * isSameNode() will always return true, and the two nodes will
+ * produce the same result for generateId())
+ */
+
+ public int compareOrder(/*@NotNull*/ NodeInfo other) {
+ return original.compareOrder(((VirtualCopy) other).original);
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+ return original.comparePosition(((VirtualCopy) other).original);
+ }
+
+ /**
+ * Return the string value of the node. The interpretation of this depends on the type
+ * of node. For an element it is the accumulated character content of the element,
+ * including descendant elements.
+ *
+ * @return the string value of the node
+ */
+
+ public String getStringValue() {
+ return original.getStringValue();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ return original.getStringValueCS();
+ }
+
+ /**
+ * Get name code. The name code is a coded form of the node name: two nodes
+ * with the same name code have the same namespace URI, the same local name,
+ * and the same prefix. By masking the name code with &0xfffff, you get a
+ * fingerprint: two nodes with the same fingerprint have the same local name
+ * and namespace URI.
+ *
+ * @return an integer name code, which may be used to obtain the actual node
+ * name from the name pool
+ * @see net.sf.saxon.om.NamePool#allocate allocate
+ * @see net.sf.saxon.om.NamePool#getFingerprint getFingerprint
+ */
+
+ public int getNameCode() {
+ return original.getNameCode();
+ }
+
+ /**
+ * Get fingerprint. The fingerprint is a coded form of the expanded name
+ * of the node: two nodes
+ * with the same name code have the same namespace URI and the same local name.
+ * A fingerprint of -1 should be returned for a node with no name.
+ *
+ * @return an integer fingerprint; two nodes with the same fingerprint have
+ * the same expanded QName
+ */
+
+ public int getFingerprint() {
+ return original.getFingerprint();
+ }
+
+ /**
+ * Get the local part of the name of this node. This is the name after the ":" if any.
+ *
+ * @return the local part of the name. For an unnamed node, returns "". Unlike the DOM
+ * interface, this returns the full name in the case of a non-namespaced name.
+ */
+
+ public String getLocalPart() {
+ return original.getLocalPart();
+ }
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ *
+ * @return The URI of the namespace of this node. For an unnamed node,
+ * or for a node with an empty prefix, return an empty
+ * string.
+ */
+
+ public String getURI() {
+ return original.getURI();
+ }
+
+ /**
+ * Get the prefix of the name of the node. This is defined only for elements and attributes.
+ * If the node has no prefix, or for other kinds of node, return a zero-length string.
+ *
+ * @return The prefix of the name of the node.
+ */
+
+ public String getPrefix() {
+ return original.getPrefix();
+ }
+
+ /**
+ * Get the display name of this node. For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ *
+ * @return The display name of this node. For a node with no name, return
+ * an empty string.
+ */
+
+ public String getDisplayName() {
+ return original.getDisplayName();
+ }
+
+ /**
+ * Get the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return original.getConfiguration();
+ }
+
+ /**
+ * Get the NamePool that holds the namecode for this node
+ *
+ * @return the namepool
+ */
+
+ public NamePool getNamePool() {
+ return original.getNamePool();
+ }
+
+ /**
+ * Get the type annotation of this node, if any.
+ *
+ * @return the type annotation of the node.
+ * @see net.sf.saxon.type.Type
+ */
+
+ public int getTypeAnnotation() {
+ return original.getTypeAnnotation();
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ public SchemaType getSchemaType() {
+ return original.getSchemaType();
+ }
+
+ /**
+ * Get the NodeInfo object representing the parent of this node
+ *
+ * @return the parent of this node; null if this node has no parent
+ */
+
+ /*@Nullable*/
+ public NodeInfo getParent() {
+ if (original.isSameNodeInfo(root)) {
+ return null;
+ }
+ if (parent == null) {
+ NodeInfo basep = original.getParent();
+ if (basep == null) {
+ return null;
+ }
+ parent = wrap(basep);
+ parent.setDocumentNumber(documentNumber);
+ }
+ return parent;
+ }
+
+ /**
+ * Return an iteration over all the nodes reached by the given axis from this node
+ *
+ * @param axisNumber an integer identifying the axis; one of the constants
+ * defined in class net.sf.saxon.om.Axis
+ * @return an AxisIterator that scans the nodes reached by the axis in
+ * turn.
+ * @throws UnsupportedOperationException if the namespace axis is
+ * requested and this axis is not supported for this implementation.
+ * @see net.sf.saxon.om.AxisInfo
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber) {
+ return iterateAxis(axisNumber, AnyNodeTest.getInstance());
+ }
+
+ /**
+ * Return an iteration over all the nodes reached by the given axis from this node
+ * that match a given NodeTest
+ *
+ * @param axisNumber an integer identifying the axis; one of the constants
+ * defined in class net.sf.saxon.om.Axis
+ * @param nodeTest A pattern to be matched by the returned nodes; nodes
+ * that do not match this pattern are not included in the result
+ * @return an AxisIterator that scans the nodes reached by the axis in
+ * turn.
+ * @throws UnsupportedOperationException if the namespace axis is
+ * requested and this axis is not supported for this implementation.
+ * @see net.sf.saxon.om.AxisInfo
+ */
+
+ public AxisIterator iterateAxis(byte axisNumber, NodeTest nodeTest) {
+ VirtualCopy newParent = null;
+ if (axisNumber == AxisInfo.CHILD || axisNumber == AxisInfo.ATTRIBUTE || axisNumber == AxisInfo.NAMESPACE) {
+ newParent = this;
+ } else if (axisNumber == AxisInfo.SELF || axisNumber == AxisInfo.PRECEDING_SIBLING || axisNumber == AxisInfo.FOLLOWING_SIBLING) {
+ newParent = parent;
+ }
+ NodeInfo root;
+ if (AxisInfo.isSubtreeAxis[axisNumber]) {
+ root = null;
+ } else {
+ root = this.root;
+ }
+ return makeCopier(original.iterateAxis(axisNumber, nodeTest), newParent, root);
+ }
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ */
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) {
+ return original.getAttributeValue(uri, local);
+ }
+
+ /**
+ * Get the root node of the tree containing this node
+ *
+ * @return the NodeInfo representing the top-level ancestor of this node.
+ * This will not necessarily be a document node
+ */
+
+ /*@Nullable*/
+ public NodeInfo getRoot() {
+ NodeInfo n = this;
+ while (true) {
+ NodeInfo p = n.getParent();
+ if (p == null) {
+ return n;
+ }
+ n = p;
+ }
+ }
+
+ /**
+ * Get the root node, if it is a document node.
+ *
+ * @return the DocumentInfo representing the containing document. If this
+ * node is part of a tree that does not have a document node as its
+ * root, return null.
+ */
+
+ /*@Nullable*/
+ public DocumentInfo getDocumentRoot() {
+ NodeInfo root = getRoot();
+ if (root.getNodeKind() == Type.DOCUMENT) {
+ return (DocumentInfo) root;
+ }
+ return null;
+ }
+
+ /**
+ * Determine whether the node has any children. <br />
+ * Note: the result is equivalent to <br />
+ * getEnumeration(Axis.CHILD, AnyNodeTest.getInstance()).hasNext()
+ *
+ * @return True if the node has one or more children
+ */
+
+ public boolean hasChildNodes() {
+ return original.hasChildNodes();
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node.
+ * Note: a.isSameNode(b) if and only if generateId(a)==generateId(b)
+ *
+ * @param buffer a buffer, to which will be appended
+ * a string that uniquely identifies this node, across all
+ * documents.
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ buffer.append("d");
+ buffer.append(Long.toString(documentNumber));
+ original.generateId(buffer);
+ }
+
+ /**
+ * Get the document number of the document containing this node. For a free-standing
+ * orphan node, just return the hashcode.
+ */
+
+ public long getDocumentNumber() {
+ return documentNumber;
+ }
+
+ /**
+ * Copy this node to a given outputter
+ *
+ * @param out the Receiver to which the node should be copied
+ * @param copyOptions a selection of the options defined in {@link net.sf.saxon.om.CopyOptions}
+ * @param locationId Identifies the location of the instruction
+ */
+
+ public void copy(Receiver out, int copyOptions, int locationId) throws XPathException {
+ original.copy(out, copyOptions, locationId);
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return original.getDeclaredNamespaces(buffer);
+ }
+
+ /**
+ * Set the system identifier for this Source.
+ * <p/>
+ * <p>The system identifier is optional if the source does not
+ * get its data from a URL, but it may still be useful to provide one.
+ * The application can use a system identifier, for example, to resolve
+ * relative URIs and to include in error messages and warnings.</p>
+ *
+ * @param systemId The system identifier as a URL string.
+ */
+ public void setSystemId(String systemId) {
+ this.systemId = systemId;
+ }
+
+ /**
+ * Get the typed value.
+ *
+ * @return the typed value. If requireSingleton is set to true, the result will always be an
+ * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize() throws XPathException {
+ return original.atomize();
+ }
+
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ return original.isId();
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return original.isIdref();
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return original.isNilled();
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ * <p/>
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup that
+ * triggered the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ /*@Nullable*/
+ public String getPublicId() {
+ return (original instanceof SourceLocator ? ((SourceLocator) original).getPublicId() : null);
+ }
+
+ /**
+ * Create an iterator that makes and returns virtual copies of nodes on the original tree
+ *
+ * @param axis the axis to be navigated
+ * @param newParent the parent of the nodes in the new virtual tree (may be null)
+ * @param root the root of the virtual tree
+ * @return the iterator that does the copying
+ */
+
+ protected VirtualCopier makeCopier(AxisIterator axis, VirtualCopy newParent, NodeInfo root) {
+ return new VirtualCopier(axis, newParent, root);
+ }
+
+ /**
+ * VirtualCopier implements the XPath axes as applied to a VirtualCopy node. It works by
+ * applying the requested axis to the node of which this is a copy. There are two
+ * complications: firstly, all nodes encountered must themselves be (virtually) copied
+ * to give them a new identity. Secondly, axes that stray outside the subtree rooted at
+ * the original copied node must be truncated.
+ */
+
+ protected class VirtualCopier implements AxisIterator {
+
+ protected AxisIterator base;
+ private VirtualCopy parent;
+ protected NodeInfo subtreeRoot;
+ /*@Nullable*/
+ private NodeInfo current;
+
+ public VirtualCopier(AxisIterator base, VirtualCopy parent, NodeInfo subtreeRoot) {
+ this.base = base;
+ this.parent = parent;
+ this.subtreeRoot = subtreeRoot;
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return (next() != null);
+ }
+
+
+ /**
+ * Get the next item in the sequence. <BR>
+ *
+ * @return the next Item. If there are no more nodes, return null.
+ */
+
+ /*@Nullable*/
+ public NodeInfo next() {
+ NodeInfo next = base.next();
+ if (next != null) {
+ if (subtreeRoot != null) {
+ // we're only interested in nodes within the subtree that was copied.
+ // Assert: once we find a node outside this subtree, all further nodes will also be outside
+ // the subtree.
+ if (!isAncestorOrSelf(subtreeRoot, next)) {
+ return null;
+ }
+ }
+ VirtualCopy vc = createCopy(next, root);
+ vc.parent = parent;
+ vc.systemId = systemId;
+ vc.documentNumber = documentNumber;
+ next = vc;
+ }
+ current = next;
+ return next;
+ }
+
+ /**
+ * Get the current item in the sequence.
+ *
+ * @return the current item, that is, the item most recently returned by
+ * next()
+ */
+
+ /*@Nullable*/
+ public NodeInfo current() {
+ return current;
+ }
+
+ /**
+ * Get the current position
+ *
+ * @return the position of the current item (the item most recently
+ * returned by next()), starting at 1 for the first node
+ */
+
+ public int position() {
+ return base.position();
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ return current.iterateAxis(axis, test);
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ return current.atomize();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ return current.getStringValueCS();
+ }
+
+ /**
+ * Get another iterator over the same sequence of items, positioned at the
+ * start of the sequence
+ *
+ * @return a new iterator over the same sequence
+ */
+
+ /*@NotNull*/
+ public AxisIterator getAnother() {
+ return new VirtualCopier(base.getAnother(), parent, subtreeRoot);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+ /**
+ * Test whether a node is an ancestor-or-self of another
+ *
+ * @param a the putative ancestor
+ * @param d the putative descendant
+ * @return true if a is an ancestor of d
+ */
+
+ private boolean isAncestorOrSelf(/*@NotNull*/ NodeInfo a, NodeInfo d) {
+ while (true) {
+ if (a.isSameNodeInfo(d)) {
+ return true;
+ }
+ d = d.getParent();
+ if (d == null) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Method to create the virtual copy of a node encountered when navigating. This method
+ * is separated out so that it can be overridden in a subclass.
+ *
+ * @param node the node to be copied
+ * @param root the root of the tree
+ * @return the virtual copy
+ */
+
+ protected VirtualCopy createCopy(NodeInfo node, NodeInfo root) {
+ return VirtualCopy.makeVirtualCopy(node, root);
+ }
+
+ }
+
+}
+
diff --git a/sf/saxon/tree/wrapper/VirtualDocumentCopy.java b/sf/saxon/tree/wrapper/VirtualDocumentCopy.java
new file mode 100644
index 0000000..57a15d4
--- /dev/null
+++ b/sf/saxon/tree/wrapper/VirtualDocumentCopy.java
@@ -0,0 +1,120 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NodeInfo;
+
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * A virtual copy of a document node
+ *
+ */
+
+public class VirtualDocumentCopy extends VirtualCopy implements DocumentInfo {
+
+ private HashMap<String, Object> userData;
+
+ public VirtualDocumentCopy(/*@NotNull*/ DocumentInfo base) {
+ super(base);
+ }
+
+ /**
+ * Ask whether the document contains any nodes whose type annotation is anything other than
+ * UNTYPED
+ *
+ * @return true if the document contains elements whose type is other than UNTYPED
+ */
+ public boolean isTyped() {
+ return ((DocumentInfo)original).isTyped();
+ }
+
+ /**
+ * Get the element with a given ID, if any
+ *
+ * @param id the required ID value
+ * @param getParent
+ * @return the element with the given ID, or null if there is no such ID
+ * present (or if the parser has not notified attributes as being of
+ * type ID)
+ */
+
+ /*@Nullable*/ public NodeInfo selectID(String id, boolean getParent) {
+ NodeInfo n = ((DocumentInfo)original).selectID(id, false);
+ if (n == null) {
+ return null;
+ }
+ VirtualCopy vc = makeVirtualCopy(n, original);
+ vc.documentNumber = documentNumber;
+ return vc;
+ }
+
+ /**
+ * Get the list of unparsed entities defined in this document
+ * @return an Iterator, whose items are of type String, containing the names of all
+ * unparsed entities defined in this document. If there are no unparsed entities or if the
+ * information is not available then an empty iterator is returned
+ */
+
+ public Iterator<String> getUnparsedEntityNames() {
+ return ((DocumentInfo)original).getUnparsedEntityNames();
+ }
+
+ /**
+ * Get the unparsed entity with a given name
+ *
+ * @param name the name of the entity
+ * @return if the entity exists, return an array of two Strings, the first
+ * holding the system ID of the entity, the second holding the public
+ * ID if there is one, or null if not. If the entity does not exist,
+ * return null.
+ */
+
+ public String[] getUnparsedEntity(String name) {
+ return ((DocumentInfo)original).getUnparsedEntity(name);
+ }
+
+ /**
+ * Set user data on the document node. The user data can be retrieved subsequently
+ * using {@link #getUserData}
+ * @param key A string giving the name of the property to be set. Clients are responsible
+ * for choosing a key that is likely to be unique. Must not be null. Keys used internally
+ * by Saxon are prefixed "saxon:".
+ * @param value The value to be set for the property. May be null, which effectively
+ * removes the existing value for the property.
+ */
+
+ public void setUserData(String key, /*@Nullable*/ Object value) {
+ if (userData == null) {
+ userData = new HashMap(4);
+ }
+ if (value == null) {
+ userData.remove(key);
+ } else {
+ userData.put(key, value);
+ }
+ }
+
+ /**
+ * Get user data held in the document node. This retrieves properties previously set using
+ * {@link #setUserData}
+ * @param key A string giving the name of the property to be retrieved.
+ * @return the value of the property, or null if the property has not been defined.
+ */
+
+ /*@Nullable*/ public Object getUserData(String key) {
+ if (userData == null) {
+ return null;
+ } else {
+ return userData.get(key);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/tree/wrapper/VirtualNode.java b/sf/saxon/tree/wrapper/VirtualNode.java
new file mode 100644
index 0000000..f046f88
--- /dev/null
+++ b/sf/saxon/tree/wrapper/VirtualNode.java
@@ -0,0 +1,47 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.om.NodeInfo;
+
+/**
+ * This interface is implemented by NodeInfo implementations that act as wrappers
+ * on some underlying tree. It provides a method to access the real node underlying
+ * the virtual node, for use by applications that need to drill down to the
+ * underlying data.
+ */
+
+public interface VirtualNode extends NodeInfo {
+
+ /**
+ * Get the node underlying this virtual node. Note that this may itself be
+ * a VirtualNode; you may have to drill down through several layers of
+ * wrapping.
+ * <p>
+ * In some cases a single VirtualNode may represent an XPath text node that maps to a sequence
+ * of adjacent nodes (for example text nodes and CDATA nodes) in the underlying tree. In this case
+ * the first node in this sequence is returned.
+ * @return The underlying node.
+ */
+
+ public Object getUnderlyingNode();
+
+ /**
+ * Get the node underlying this virtual node. If this is a VirtualNode the method
+ * will automatically drill down through several layers of wrapping.
+ * <p>
+ * In some cases a single VirtualNode may represent an XPath text node that maps to a sequence
+ * of adjacent nodes (for example text nodes and CDATA nodes) in the underlying tree. In this case
+ * the first node in this sequence is returned.
+ * @return The underlying node.
+ */
+
+ public Object getRealNode();
+
+}
+
diff --git a/sf/saxon/tree/wrapper/VirtualUntypedCopy.java b/sf/saxon/tree/wrapper/VirtualUntypedCopy.java
new file mode 100644
index 0000000..11def7e
--- /dev/null
+++ b/sf/saxon/tree/wrapper/VirtualUntypedCopy.java
@@ -0,0 +1,174 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+/**
+ * This class represents a virtual copy of a node with type annotations stripped
+ */
+public class VirtualUntypedCopy extends VirtualCopy {
+
+ // TODO: need a VirtualUntypedDocumentCopy for the document node??
+
+ /**
+ * Public factory method: create an untyped virtual copy of a node
+ * @param original the node to be copied
+ * @param root the root of the tree
+ * @return the virtual copy.
+ */
+
+ public static VirtualCopy makeVirtualUntypedCopy(NodeInfo original, NodeInfo root) {
+ VirtualCopy vc;
+ // Don't allow copies of copies of copies: define the new copy in terms of the original
+ while (original instanceof VirtualUntypedCopy) {
+ original = ((VirtualUntypedCopy)original).original;
+ }
+ while (root instanceof VirtualUntypedCopy) {
+ root = ((VirtualUntypedCopy)root).original;
+ }
+ if (original.getNodeKind() == Type.DOCUMENT) {
+ vc = new VirtualDocumentCopy((DocumentInfo)original);
+ } else {
+ vc = new VirtualUntypedCopy(original);
+ }
+ vc.root = root;
+ return vc;
+ }
+
+ /**
+ * Protected constructor: create a virtual copy of a node
+ *
+ * @param base the node to be copied
+ */
+
+ protected VirtualUntypedCopy(NodeInfo base) {
+ super(base);
+ }
+
+ /**
+ * Get the type annotation of this node, if any.
+ *
+ * @return the type annotation of the node.
+ * @see net.sf.saxon.type.Type
+ */
+
+ public int getTypeAnnotation() {
+ switch (getNodeKind()) {
+ case Type.ELEMENT:
+ return StandardNames.XS_UNTYPED;
+ case Type.ATTRIBUTE:
+ return StandardNames.XS_UNTYPED_ATOMIC;
+ default:
+ return super.getTypeAnnotation();
+ }
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ @Override
+ public SchemaType getSchemaType() {
+ switch (getNodeKind()) {
+ case Type.ELEMENT:
+ return Untyped.getInstance();
+ case Type.ATTRIBUTE:
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ default:
+ return super.getSchemaType();
+ }
+ }
+
+ /**
+ * Get the typed value. The result of this method will always be consistent with the method
+ * {@link net.sf.saxon.om.Item#getTypedValue()}. However, this method is often more convenient and may be
+ * more efficient, especially in the common case where the value is expected to be a singleton.
+ *
+ * @return the typed value. If requireSingleton is set to true, the result will always be an
+ * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize() throws XPathException {
+ switch (getNodeKind()) {
+ case Type.ELEMENT:
+ case Type.ATTRIBUTE:
+ return new UntypedAtomicValue(getStringValueCS());
+ default:
+ return super.atomize();
+ }
+ }
+
+
+ public void copy(Receiver out, int copyOptions, int locationId) throws XPathException {
+ super.copy(out, copyOptions & ~CopyOptions.TYPE_ANNOTATIONS, locationId);
+ }
+
+
+ /**
+ * Create an iterator that makes and returns virtual copies of nodes on the original tree
+ *
+ * @param axis the axis to be navigated
+ * @param newParent the parent of the nodes in the new virtual tree (may be null)
+ * @param root the root of the virtual tree
+ */
+
+ protected VirtualCopy.VirtualCopier makeCopier(AxisIterator axis, VirtualCopy newParent, NodeInfo root) {
+ return new VirtualUntypedCopier(axis, newParent, root);
+ }
+
+ protected class VirtualUntypedCopier extends VirtualCopy.VirtualCopier {
+
+
+ public VirtualUntypedCopier(AxisIterator base, VirtualCopy parent, NodeInfo subtreeRoot) {
+ super(base, parent, subtreeRoot);
+ }
+
+ /**
+ * Method to create the virtual copy of a node encountered when navigating. This method
+ * is separated out so that it can be overridden in a subclass.
+ */
+
+ protected VirtualCopy createCopy(NodeInfo node, NodeInfo root) {
+ return VirtualUntypedCopy.makeVirtualUntypedCopy(node, root);
+ }
+
+ /**
+ * Get another iterator over the same sequence of items, positioned at the
+ * start of the sequence
+ *
+ * @return a new iterator over the same sequence
+ */
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new VirtualUntypedCopier(base.getAnother(), parent, subtreeRoot);
+ }
+
+
+ }
+}
+
diff --git a/sf/saxon/tree/wrapper/WrappingFunction.java b/sf/saxon/tree/wrapper/WrappingFunction.java
new file mode 100644
index 0000000..0cae34e
--- /dev/null
+++ b/sf/saxon/tree/wrapper/WrappingFunction.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.om.NodeInfo;
+
+/**
+ * Callback to create a VirtualNode that wraps a given NodeInfo
+ */
+public interface WrappingFunction {
+
+ /**
+ * Factory method to wrap a node with a wrapper that implements the Saxon
+ * NodeInfo interface.
+ * @param node The underlying node
+ * @param parent The wrapper for the parent of the node (null if unknown)
+ * @return The new wrapper for the supplied node
+ */
+
+ /*@NotNull*/ public VirtualNode makeWrapper(NodeInfo node, VirtualNode parent);
+
+
+}
+
diff --git a/sf/saxon/tree/wrapper/WrappingIterator.java b/sf/saxon/tree/wrapper/WrappingIterator.java
new file mode 100644
index 0000000..d08f2f6
--- /dev/null
+++ b/sf/saxon/tree/wrapper/WrappingIterator.java
@@ -0,0 +1,136 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.tree.wrapper;
+
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.AxisIterator;
+
+/**
+ * A WrappingIterator delivers wrappers for the nodes delivered
+ * by its underlying iterator. It is used when no whitespace stripping
+ * is actually needed, e.g. for the attribute axis. But we still need to
+ * create wrappers, so that further iteration remains in the virtual layer
+ * rather than switching to the real nodes.
+ */
+
+public class WrappingIterator implements AxisIterator {
+
+ AxisIterator base;
+ VirtualNode parent;
+ /*@Nullable*/ NodeInfo current;
+ boolean atomizing = false;
+ WrappingFunction wrappingFunction;
+
+ /**
+ * Create a WrappingIterator
+ *
+ * @param base The underlying iterator
+ * @param parent If all the nodes to be wrapped have the same parent,
+ * it can be specified here. Otherwise specify null.
+ */
+
+ public WrappingIterator(AxisIterator base, WrappingFunction function, VirtualNode parent) {
+ this.base = base;
+ this.wrappingFunction = function;
+ this.parent = parent;
+ }
+
+ /**
+ * Move to the next node, without returning it. Returns true if there is
+ * a next node, false if the end of the sequence has been reached. After
+ * calling this method, the current node may be retrieved using the
+ * current() function.
+ */
+
+ public boolean moveNext() {
+ return (next() != null);
+ }
+
+
+ /*@Nullable*/ public NodeInfo next() {
+ Item n = base.next();
+ if (n instanceof NodeInfo && !atomizing) {
+ current = wrappingFunction.makeWrapper((NodeInfo) n, parent);
+ } else {
+ current = (NodeInfo) n;
+ }
+ return current;
+ }
+
+ /*@Nullable*/ public NodeInfo current() {
+ return current;
+ }
+
+ public int position() {
+ return base.position();
+ }
+
+ public void close() {
+ base.close();
+ }
+
+ /**
+ * Return an iterator over an axis, starting at the current node.
+ *
+ * @param axis the axis to iterate over, using a constant such as
+ * {@link net.sf.saxon.om.AxisInfo#CHILD}
+ * @param test a predicate to apply to the nodes before returning them.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public AxisIterator iterateAxis(byte axis, NodeTest test) {
+ return current.iterateAxis(axis, test);
+ }
+
+ /**
+ * Return the atomized value of the current node.
+ *
+ * @return the atomized value.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public Sequence atomize() throws XPathException {
+ return current.atomize();
+ }
+
+ /**
+ * Return the string value of the current node.
+ *
+ * @return the string value, as an instance of CharSequence.
+ * @throws NullPointerException if there is no current node
+ */
+
+ public CharSequence getStringValue() {
+ return current.getStringValueCS();
+ }
+
+ /*@NotNull*/ public AxisIterator getAnother() {
+ return new WrappingIterator(base.getAnother(), wrappingFunction, parent);
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED}, {@link #LAST_POSITION_FINDER},
+ * and {@link #LOOKAHEAD}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ * It is acceptable for the properties of the iterator to change depending on its state.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+
+
+}
+
diff --git a/sf/saxon/tree/wrapper/package.html b/sf/saxon/tree/wrapper/package.html
new file mode 100644
index 0000000..2015eb6
--- /dev/null
+++ b/sf/saxon/tree/wrapper/package.html
@@ -0,0 +1,35 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.tree.wrapper</title>
+
+</head>
+ <body>
+ <p>This package defines a number of implementations of "virtual nodes" implemented as wrappers around other nodes.</p>
+
+ <p>The {@link net.sf.saxon.tree.wrapper.SpaceStrippedNode} class provides a virtual tree in which whitespace text nodes in the underlying real
+ tree are ignored.</p>
+
+ <p>The {@link net.sf.saxon.tree.wrapper.TypeStrippedNode} class provides a virtual tree in which type annotations in the underlying real tree
+ are ignored.</p>
+
+ <p>The {@link net.sf.saxon.tree.wrapper.VirtualCopy} class provides a tree that is the same as the underlying tree in everything except
+ node identity.</p>
+
+
+
+
+ <p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+28 November 2011</i></p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/type/AnyFunctionType.java b/sf/saxon/type/AnyFunctionType.java
new file mode 100644
index 0000000..8e2c44f
--- /dev/null
+++ b/sf/saxon/type/AnyFunctionType.java
@@ -0,0 +1,232 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.TypeCheckerEnvironment;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceType;
+
+/**
+ * An ItemType representing the type function(). Subtypes represent function items with more specific
+ * type signatures.
+ *
+ * <p>Note that although this class has a singleton instance representing the type <code>function(*)</code>,
+ * there are also likely to be instances of subclasses representing more specific function types.</p>
+ */
+public class AnyFunctionType implements FunctionItemType {
+
+ /*@NotNull*/ public final static AnyFunctionType ANY_FUNCTION = new AnyFunctionType();
+
+ public static SequenceType SINGLE_FUNCTION =
+ SequenceType.makeSequenceType(ANY_FUNCTION, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * Get the singular instance of this type (Note however that subtypes of this type
+ * may have any number of instances)
+ * @return the singular instance of this type
+ */
+
+ /*@NotNull*/ public static AnyFunctionType getInstance() {
+ return ANY_FUNCTION;
+ }
+
+ /**
+ * Determine whether this item type is an atomic type
+ *
+ * @return true if this is ANY_ATOMIC_TYPE or a subtype thereof
+ */
+ public boolean isAtomicType() {
+ return false;
+ }
+
+ /**
+ * Determine whether this item type is atomic (that is, whether it can ONLY match
+ * atomic values)
+ * @return true if this is ANY_ATOMIC_TYPE or a subtype thereof
+ */
+
+ public boolean isPlainType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this function item type is a map type. In this case function coercion (to the map type)
+ * will never succeed.
+ *
+ * @return true if this FunctionItemType is a map type
+ */
+ public boolean isMapType() {
+ return false;
+ }
+
+ /**
+ * Get the argument types of the function
+ * @return the argument types, as an array of SequenceTypes, or null if this is the generic function
+ * type function(*)
+ */
+ /*@Nullable*/ public SequenceType[] getArgumentTypes() {
+ return null;
+ }
+
+ /**
+ * Test whether a given item conforms to this type
+ *
+ * @param item The item to be tested
+ * @param context the XPath dynamic evaluation context
+ * @return true if the item is an instance of this type; false otherwise
+ */
+ public boolean matches(Item item, /*@NotNull*/ XPathContext context) {
+ return matchesItem(item, false, context.getConfiguration());
+ }
+
+ /**
+ * Test whether a given item conforms to this type
+ * @param item The item to be tested
+ * @param allowURIPromotion if a URI value is to be treated as a string
+ * @param config The Saxon configuration
+ * @return true if the item is an instance of this type; false otherwise
+ */
+
+ public boolean matchesItem(Item item, boolean allowURIPromotion, Configuration config) {
+ return (item instanceof FunctionItem);
+ }
+
+ /**
+ * Get the type from which this item type is derived by restriction. This
+ * is the supertype in the XPath type heirarchy, as distinct from the Schema
+ * base type: this means that the supertype of xs:boolean is xs:anyAtomicType,
+ * whose supertype is item() (rather than xs:anySimpleType).
+ * <p/>
+ * In fact the concept of "supertype" is not really well-defined, because the types
+ * form a lattice rather than a hierarchy. The only real requirement on this function
+ * is that it returns a type that strictly subsumes this type, ideally as narrowly
+ * as possible.
+ * @param th the type hierarchy cache
+ * @return the supertype, or null if this type is item()
+ */
+
+ public ItemType getSuperType(TypeHierarchy th) {
+ return AnyItemType.getInstance();
+ }
+
+ /**
+ * Get the primitive item type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue it is Type.ATOMIC_VALUE. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that integer, xs:dayTimeDuration, and xs:yearMonthDuration
+ * are considered to be primitive types. For function items it is the singular
+ * instance FunctionItemType.getInstance().
+ */
+
+ /*@NotNull*/ public final ItemType getPrimitiveItemType() {
+ return ANY_FUNCTION;
+ }
+
+ /**
+ * Get the primitive type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue it is Type.ATOMIC_VALUE. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that INTEGER is considered to be a primitive type.
+ */
+
+ public final int getPrimitiveType() {
+ return Type.FUNCTION;
+ }
+
+ /**
+ * Produce a representation of this type name for use in error messages.
+ * @return a string representation of the type, in notation resembling but not necessarily
+ * identical to XPath syntax
+ */
+
+ public String toString() {
+ return "function(*)";
+ }
+
+ /**
+ * Get the item type of the atomic values that will be produced when an item
+ * of this type is atomized
+ * @return the item type of the atomic values that will be produced when an item
+ * of this type is atomized
+ */
+
+ /*@NotNull*/
+ public AtomicType getAtomizedItemType() {
+ return null;
+ }
+
+ /**
+ * Ask whether values of this type are atomizable
+ * @return true unless it is known that these items will be elements with element-only
+ * content, in which case return false
+ */
+
+ public boolean isAtomizable() {
+ return false;
+ }
+
+ /**
+ * Determine the relationship of one function item type to another
+ * @return for example {@link TypeHierarchy#SUBSUMES}, {@link TypeHierarchy#SAME_TYPE}
+ */
+
+ public int relationship(FunctionItemType other, TypeHierarchy th) {
+ if (other == this) {
+ return TypeHierarchy.SAME_TYPE;
+ } else {
+ return TypeHierarchy.SUBSUMES;
+ }
+ }
+
+ /**
+ * Create an expression whose effect is to apply function coercion to coerce a function from this type to another type
+ * @param exp the expression that delivers the supplied sequence of function items (the ones in need of coercion)
+ * @param role information for use in diagnostics
+ * @param visitor the expression visitor, supplies context information
+ * @return the coerced function, a function that calls the original function after checking the parameters
+ */
+
+ public Expression makeFunctionSequenceCoercer(Expression exp, RoleLocator role, TypeCheckerEnvironment visitor)
+ throws XPathException {
+ return exp;
+ }
+
+ /**
+ * Visit all the schema components used in this ItemType definition
+ * @param visitor the visitor class to be called when each component is visited
+ */
+
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException {
+ // no action
+ }
+
+ /**
+ * Get the result type
+ * @return the result type
+ */
+
+ public SequenceType getResultType() {
+ return SequenceType.ANY_SEQUENCE;
+ }
+
+ public double getDefaultPriority() {
+ return 0.5;
+ }
+}
+
diff --git a/sf/saxon/type/AnyItemType.java b/sf/saxon/type/AnyItemType.java
new file mode 100644
index 0000000..eb5b8c8
--- /dev/null
+++ b/sf/saxon/type/AnyItemType.java
@@ -0,0 +1,141 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Serializable;
+
+
+/**
+ * An implementation of ItemType that matches any item (node or atomic value)
+*/
+
+public class AnyItemType implements ItemType, Serializable {
+
+ private AnyItemType(){}
+
+ /*@NotNull*/ private static AnyItemType theInstance = new AnyItemType();
+
+ /**
+ * Factory method to get the singleton instance
+ */
+
+ /*@NotNull*/ public static AnyItemType getInstance() {
+ return theInstance;
+ }
+
+ /**
+ * Determine whether this item type is an atomic type
+ *
+ * @return true if this is ANY_ATOMIC_TYPE or a subtype thereof
+ */
+ public boolean isAtomicType() {
+ return false;
+ }
+
+ /**
+ * Determine whether this item type is atomic (that is, whether it can ONLY match
+ * atomic values)
+ *
+ * @return false: this type can match nodes or atomic values
+ */
+
+ public boolean isPlainType() {
+ return false;
+ }
+
+ /**
+ * Test whether a given item conforms to this type
+ *
+ * @param item The item to be tested
+ * @param context the XPath dynamic evaluation context
+ * @return true if the item is an instance of this type; false otherwise
+ */
+ public boolean matches(Item item, XPathContext context) {
+ return true;
+ }
+
+ /**
+ * Test whether a given item conforms to this type
+ * @param item The item to be tested
+ * @param allowURIPromotion
+ * @param config
+ * @return true if the item is an instance of this type; false otherwise
+ */
+
+ public boolean matchesItem(Item item, boolean allowURIPromotion, Configuration config) {
+ return true;
+ }
+
+ /*@Nullable*/ public ItemType getSuperType(TypeHierarchy th) {
+ return null;
+ }
+
+ /**
+ * Get the primitive item type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue it is Type.ATOMIC_VALUE. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that INTEGER is considered to be a primitive type.
+ */
+
+ /*@NotNull*/ public ItemType getPrimitiveItemType() {
+ return this;
+ }
+
+ public int getPrimitiveType() {
+ return Type.ITEM;
+ }
+
+ /*@NotNull*/
+ public AtomicType getAtomizedItemType() {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+
+ /**
+ * Ask whether values of this type are atomizable
+ * @return true unless it is known that these items will be elements with element-only
+ * content, in which case return false
+ */
+
+ public boolean isAtomizable() {
+ return true;
+ }
+
+ /**
+ * Visit all the schema components used in this ItemType definition
+ * @param visitor the visitor class to be called when each component is visited
+ */
+
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException {
+ // no action
+ }
+
+ public double getDefaultPriority() {
+ return -2;
+ }
+
+ /*@NotNull*/ public String toString() {
+ return "item()";
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return "AnyItemType".hashCode();
+ }
+
+}
+
diff --git a/sf/saxon/type/AnySimpleType.java b/sf/saxon/type/AnySimpleType.java
new file mode 100644
index 0000000..69e8f70
--- /dev/null
+++ b/sf/saxon/type/AnySimpleType.java
@@ -0,0 +1,434 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import com.saxonica.schema.UserSimpleType;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.value.UntypedAtomicValue;
+import net.sf.saxon.value.Whitespace;
+
+/**
+ * This class has a singleton instance which represents the XML Schema built-in type xs:anySimpleType
+ */
+
+public final class AnySimpleType implements SimpleType {
+
+ /*@NotNull*/ private static AnySimpleType theInstance = new AnySimpleType();
+
+ /**
+ * Private constructor
+ */
+ private AnySimpleType() {
+ }
+
+ /**
+ * Get the local name of this type
+ *
+ * @return the local name of this type definition, if it has one. Return null in the case of an
+ * anonymous type.
+ */
+
+ /*@NotNull*/ public String getName() {
+ return "anySimpleType";
+ }
+
+ /**
+ * Get the target namespace of this type
+ *
+ * @return the target namespace of this type definition, if it has one. Return null in the case
+ * of an anonymous type, and in the case of a global type defined in a no-namespace schema.
+ */
+
+ public String getTargetNamespace() {
+ return NamespaceConstant.SCHEMA;
+ }
+
+ /**
+ * Get the name of this type as an EQName, that is, a string in the format Q{uri}local.
+ *
+ * @return an EQName identifying the type. In the case of an anonymous type, an internally-generated
+ * name is returned
+ */
+ public String getEQName() {
+ return "Q{" + NamespaceConstant.SCHEMA + "}anySimpleType";
+ }
+
+ /**
+ * Return true if this is an external object type, that is, a Saxon-defined type for external
+ * Java or .NET objects
+ */
+
+ public boolean isExternalType() {
+ return false;
+ }
+
+ /**
+ * Determine whether this is a built-in type or a user-defined type
+ */
+
+ public boolean isBuiltInType() {
+ return true;
+ }
+
+ /**
+ * Ask whether this type is an ID type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:ID: that is, it includes types derived
+ * from ID by restriction, list, or union. Note that for a node to be treated
+ * as an ID, its typed value must be a *single* atomic value of type ID; the type of the
+ * node, however, can still allow a list.
+ */
+
+ public boolean isIdType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this type is an IDREF or IDREFS type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:IDREF: that is, it includes types derived
+ * from IDREF or IDREFS by restriction, list, or union
+ */
+
+ public boolean isIdRefType() {
+ return false;
+ }
+
+ /**
+ * Get the redefinition level. This is zero for a component that has not been redefined;
+ * for a redefinition of a level-0 component, it is 1; for a redefinition of a level-N
+ * component, it is N+1. This concept is used to support the notion of "pervasive" redefinition:
+ * if a component is redefined at several levels, the top level wins, but it is an error to have
+ * two versions of the component at the same redefinition level.
+ * @return the redefinition level
+ */
+
+ public int getRedefinitionLevel() {
+ return 0;
+ }
+
+ /**
+ * Get the URI of the schema document containing the definition of this type
+ * @return null for a built-in type
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return null;
+ }
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular object representing xs:anyType
+ */
+
+ /*@NotNull*/ public static AnySimpleType getInstance() {
+ return theInstance;
+ }
+
+ /**
+ * Get the validation status - always valid
+ */
+ public int getValidationStatus() {
+ return VALIDATED;
+ }
+
+ /**
+ * Get the base type
+ * @return AnyType
+ */
+
+ public SchemaType getBaseType() {
+ return AnyType.getInstance();
+ }
+
+ /**
+ * Test whether this SchemaType is a complex type
+ *
+ * @return true if this SchemaType is a complex type
+ */
+
+ public boolean isComplexType() {
+ return false;
+ }
+
+ /**
+ * Test whether this SchemaType is a simple type
+ * @return true if this SchemaType is a simple type
+ */
+
+ public boolean isSimpleType() {
+ return true;
+ }
+
+ /**
+ * Get the fingerprint of the name of this type
+ * @return the fingerprint.
+ */
+
+ public int getFingerprint() {
+ return StandardNames.XS_ANY_SIMPLE_TYPE;
+ }
+
+ /**
+ * Get the namecode of the name of this type. This includes the prefix from the original
+ * type declaration: in the case of built-in types, there may be a conventional prefix
+ * or there may be no prefix.
+ */
+
+ public int getNameCode() {
+ return StandardNames.XS_ANY_SIMPLE_TYPE;
+ }
+
+ /**
+ * Get a description of this type for use in diagnostics
+ * @return the string "xs:anyType"
+ */
+
+ /*@NotNull*/ public String getDescription() {
+ return "xs:anySimpleType";
+ }
+
+ /**
+ * Get the display name of the type: that is, a lexical QName with an arbitrary prefix
+ *
+ * @return a lexical QName identifying the type
+ */
+
+ /*@NotNull*/ public String getDisplayName() {
+ return "xs:anySimpleType";
+ }
+
+ /**
+ * Test whether this is the same type as another type. They are considered to be the same type
+ * if they are derived from the same type definition in the original XML representation (which
+ * can happen when there are multiple includes of the same file)
+ */
+
+ public boolean isSameType(SchemaType other) {
+ return (other instanceof AnySimpleType);
+ }
+
+ /**
+ * Get the typed value of a node that is annotated with this schema type.
+ * @param node the node whose typed value is required
+ * @return the typed value.
+ * @since 8.5
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize(/*@NotNull*/ NodeInfo node) {
+ return new UntypedAtomicValue(node.getStringValueCS());
+ }
+
+ /**
+ * Check that this type is validly derived from a given type
+ *
+ * @param type the type from which this type is derived
+ * @param block the derivations that are blocked by the relevant element declaration
+ * @throws SchemaException
+ * if the derivation is not allowed
+ */
+
+ public void checkTypeDerivationIsOK(SchemaType type, int block) throws SchemaException {
+ if (type == this) {
+ return;
+ }
+ throw new SchemaException("Cannot derive xs:anySimpleType from another type");
+ }
+
+ /**
+ * Test whether this Simple Type is an atomic type
+ * @return false, this is not (necessarily) an atomic type
+ */
+
+ public boolean isAtomicType() {
+ return false;
+ }
+
+ public boolean isAnonymousType() {
+ return false;
+ }
+
+
+ /**
+ * Determine whether this is a list type
+ * @return false (it isn't a list type)
+ */
+ public boolean isListType() {
+ return false;
+ }
+
+ /**
+ * Determin whether this is a union type
+ * @return false (it isn't a union type)
+ */
+ public boolean isUnionType() {
+ return false;
+ }
+
+ /**
+ * Get the built-in ancestor of this type in the type hierarchy
+ * @return this type itself
+ */
+ /*@NotNull*/ public SchemaType getBuiltInBaseType() {
+ return this;
+ }
+
+ /**
+ * Get the typed value corresponding to a given string value, assuming it is
+ * valid against this type
+ *
+ *
+ * @param value the string value
+ * @param resolver a namespace resolver used to resolve any namespace prefixes appearing
+ * in the content of values. Can supply null, in which case any namespace-sensitive content
+ * will be rejected.
+ * @param rules
+ * @return an iterator over the atomic sequence comprising the typed value. The objects
+ * returned by this SequenceIterator will all be of type {@link net.sf.saxon.value.AtomicValue}
+ */
+
+ /*@NotNull*/ public AtomicSequence getTypedValue(CharSequence value, NamespaceResolver resolver, ConversionRules rules) {
+ return new UntypedAtomicValue(value);
+ }
+
+ /**
+ * Check whether a given input string is valid according to this SimpleType
+ * @param value the input string to be checked
+ * @param nsResolver a namespace resolver used to resolve namespace prefixes if the type
+ * is namespace sensitive. The value supplied may be null; in this case any namespace-sensitive
+ * content will throw an UnsupportedOperationException.
+ * @param rules
+ * @return null if validation succeeds (which it always does for this implementation)
+ * @throws UnsupportedOperationException if the type is namespace-sensitive and no namespace
+ * resolver is supplied
+ */
+ /*@Nullable*/ public ValidationFailure validateContent(/*@NotNull*/ CharSequence value, NamespaceResolver nsResolver, /*@NotNull*/ ConversionRules rules) {
+ return null;
+ }
+
+ /**
+ * Test whether this type represents namespace-sensitive content
+ * @return false
+ */
+ public boolean isNamespaceSensitive() {
+ return false;
+ }
+
+ /**
+ * Returns the value of the 'block' attribute for this type, as a bit-signnificant
+ * integer with fields such as {@link SchemaType#DERIVATION_LIST} and {@link SchemaType#DERIVATION_EXTENSION}
+ *
+ * @return the value of the 'block' attribute for this type
+ */
+
+ public int getBlock() {
+ return 0;
+ }
+
+ /**
+ * Gets the integer code of the derivation method used to derive this type from its
+ * parent. Returns zero for primitive types.
+ *
+ * @return a numeric code representing the derivation method, for example {@link SchemaType#DERIVATION_RESTRICTION}
+ */
+
+ public int getDerivationMethod() {
+ return SchemaType.DERIVATION_RESTRICTION;
+ }
+
+ /**
+ * Determines whether derivation (of a particular kind)
+ * from this type is allowed, based on the "final" property
+ *
+ * @param derivation the kind of derivation, for example {@link SchemaType#DERIVATION_LIST}
+ * @return true if this kind of derivation is allowed
+ */
+
+ public boolean allowsDerivation(int derivation) {
+ return true;
+ }
+
+ /**
+ * Get the types of derivation that are not permitted, by virtue of the "final" property.
+ *
+ * @return the types of derivation that are not permitted, as a bit-significant integer
+ * containing bits such as {@link net.sf.saxon.type.SchemaType#DERIVATION_EXTENSION}
+ */
+ public int getFinalProhibitions() {
+ return 0;
+ }
+
+ /**
+ * Determine how values of this simple type are whitespace-normalized.
+ *
+ * @return one of {@link net.sf.saxon.value.Whitespace#PRESERVE}, {@link net.sf.saxon.value.Whitespace#COLLAPSE},
+ * {@link net.sf.saxon.value.Whitespace#REPLACE}.
+ */
+
+ public int getWhitespaceAction() {
+ return Whitespace.PRESERVE;
+ }
+
+ /**
+ * Analyze an expression to see whether the expression is capable of delivering a value of this
+ * type.
+ *
+ * @param expression the expression that delivers the content
+ * @param kind the node kind whose content is being delivered: {@link net.sf.saxon.type.Type#ELEMENT},
+ * {@link net.sf.saxon.type.Type#ATTRIBUTE}, or {@link net.sf.saxon.type.Type#DOCUMENT}
+ * @param env
+ */
+
+ public void analyzeContentExpression(Expression expression, int kind, StaticContext env) {
+ //return;
+ }
+
+ /**
+ * Apply any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess
+ * @param input the value to be preprocessed
+ * @return the value after preprocessing
+ */
+
+ public CharSequence preprocess(CharSequence input) {
+ return input;
+ }
+
+ /**
+ * Reverse any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess. This is called when converting a value of this type to
+ * a string
+ * @param input the value to be postprocessed: this is the "ordinary" result of converting
+ * the value to a string
+ * @return the value after postprocessing
+ */
+
+ public CharSequence postprocess(CharSequence input) throws ValidationException {
+ return input;
+ }
+
+
+//#ifdefined SCHEMA
+ /**
+ * Get the schema component in the form of a function item. This allows schema information
+ * to be made visible to XSLT or XQuery code. The function makes available the contents of the
+ * schema component as defined in the XSD specification. The function takes a string as argument
+ * representing a property name, and returns the corresponding property of the schema component.
+ * There is also a property "class" which returns the kind of schema component, for example
+ * "Attribute Declaration".
+ *
+ * @return the schema component represented as a function from property names to property values.
+ */
+ public FunctionItem getComponentAsFunction() {
+ return UserSimpleType.getComponentAsFunction(this);
+ }
+//#endif
+}
+
diff --git a/sf/saxon/type/AnyType.java b/sf/saxon/type/AnyType.java
new file mode 100644
index 0000000..54a1e03
--- /dev/null
+++ b/sf/saxon/type/AnyType.java
@@ -0,0 +1,593 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import com.saxonica.schema.UserComplexType;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.z.IntHashSet;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+import java.io.Serializable;
+
+/**
+ * This class has a singleton instance which represents the XML Schema built-in type xs:anyType,
+ * also known as the urtype.
+ *
+ * See XML Schema 1.1 Part 1 section 3.4.7
+ */
+
+public final class AnyType implements ComplexType, Serializable {
+
+ /*@NotNull*/ private static AnyType theInstance = new AnyType();
+
+ /**
+ * Private constructor
+ */
+ private AnyType() {
+ super();
+ }
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular object representing xs:anyType
+ */
+
+ /*@NotNull*/ public static AnyType getInstance() {
+ return theInstance;
+ }
+
+ /**
+ * Get the local name of this type
+ *
+ * @return the local name of this type definition, if it has one. Return null in the case of an
+ * anonymous type.
+ */
+
+ /*@NotNull*/ public String getName() {
+ return "anyType";
+ }
+
+ /**
+ * Get the name of this type as an EQName, that is, a string in the format Q{uri}local.
+ *
+ * @return an EQName identifying the type, specifically "Q{http://www.w3.org/2001/XMLSchema}anyType"
+ */
+ public String getEQName() {
+ return "Q{" + NamespaceConstant.SCHEMA + "}anyType";
+ }
+
+ /**
+ * Get the target namespace of this type
+ *
+ * @return the target namespace of this type definition, if it has one. Return null in the case
+ * of an anonymous type, and in the case of a global type defined in a no-namespace schema.
+ */
+
+ public String getTargetNamespace() {
+ return NamespaceConstant.SCHEMA;
+ }
+
+ /**
+ * Get the variety of this complex type. This will be one of the values
+ * {@link #VARIETY_EMPTY}, {@link #VARIETY_MIXED}, {@link #VARIETY_SIMPLE}, or
+ * {@link #VARIETY_ELEMENT_ONLY}
+ */
+
+ public int getVariety() {
+ return VARIETY_MIXED;
+ }
+
+ /**
+ * Get the validation status - always valid
+ */
+ public int getValidationStatus() {
+ return VALIDATED;
+ }
+
+ /**
+ * Get the redefinition level. This is zero for a component that has not been redefined;
+ * for a redefinition of a level-0 component, it is 1; for a redefinition of a level-N
+ * component, it is N+1. This concept is used to support the notion of "pervasive" redefinition:
+ * if a component is redefined at several levels, the top level wins, but it is an error to have
+ * two versions of the component at the same redefinition level.
+ * @return the redefinition level
+ */
+
+ public int getRedefinitionLevel() {
+ return 0;
+ }
+
+ /**
+ * Get the base type
+ * @return null (this is the root of the type hierarchy)
+ */
+
+ /*@Nullable*/ public SchemaType getBaseType() {
+ return null;
+ }
+
+ /**
+ * Returns the base type that this type inherits from. This method can be used to get the
+ * base type of a type that is known to be valid.
+ * If this type is a Simpletype that is a built in primitive type then null is returned.
+ *
+ * @return the base type.
+ * @throws IllegalStateException if this type is not valid.
+ */
+
+ /*@Nullable*/ public SchemaType getKnownBaseType() throws IllegalStateException {
+ return null;
+ }
+
+ /**
+ * Gets the integer code of the derivation method used to derive this type from its
+ * parent. Returns zero for primitive types.
+ *
+ * @return a numeric code representing the derivation method, for example
+ * {@link SchemaType#DERIVATION_RESTRICTION}
+ */
+
+ public int getDerivationMethod() {
+ return 0;
+ }
+
+ /**
+ * Determines whether derivation (of a particular kind)
+ * from this type is allowed, based on the "final" property
+ *
+ * @param derivation the kind of derivation, for example {@link SchemaType#DERIVATION_LIST}
+ * @return true if this kind of derivation is allowed
+ */
+
+ public boolean allowsDerivation(int derivation) {
+ return true;
+ }
+
+ /**
+ * Get the types of derivation that are not permitted, by virtue of the "final" property.
+ *
+ * @return the types of derivation that are not permitted, as a bit-significant integer
+ * containing bits such as {@link net.sf.saxon.type.SchemaType#DERIVATION_EXTENSION}
+ */
+ public int getFinalProhibitions() {
+ return 0;
+ }
+
+ //#ifdefined SCHEMA
+ /**
+ * Get the schema component in the form of a function item. This allows schema information
+ * to be made visible to XSLT or XQuery code. The function makes available the contents of the
+ * schema component as defined in the XSD specification. The function takes a string as argument
+ * representing a property name, and returns the corresponding property of the schema component.
+ * There is also a property "class" which returns the kind of schema component, for example
+ * "Attribute Declaration".
+ *
+ * @return the schema component represented as a function from property names to property values.
+ */
+ public FunctionItem getComponentAsFunction() {
+ return UserComplexType.getComponentAsFunction(this);
+ }
+//#endif
+
+ /**
+ * Test whether this ComplexType has been marked as abstract.
+ * @return false: this class is not abstract.
+ */
+
+ public boolean isAbstract() {
+ return false;
+ }
+
+ /**
+ * Test whether this SchemaType is a complex type
+ *
+ * @return true if this SchemaType is a complex type
+ */
+
+ public boolean isComplexType() {
+ return true;
+ }
+
+ /**
+ * Test whether this is an anonymous type
+ * @return true if this SchemaType is an anonymous type
+ */
+
+ public boolean isAnonymousType() {
+ return false;
+ }
+
+ /**
+ * Test whether this SchemaType is a simple type
+ * @return true if this SchemaType is a simple type
+ */
+
+ public boolean isSimpleType() {
+ return false;
+ }
+
+ /**
+ * Test whether this SchemaType is an atomic type
+ * @return true if this SchemaType is an atomic type
+ */
+
+ public boolean isAtomicType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this type is an ID type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:ID: that is, it includes types derived
+ * from ID by restriction, list, or union. Note that for a node to be treated
+ * as an ID, its typed value must be a *single* atomic value of type ID; the type of the
+ * node, however, can still allow a list.
+ */
+
+ public boolean isIdType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this type is an IDREF or IDREFS type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:IDREF: that is, it includes types derived
+ * from IDREF or IDREFS by restriction, list, or union
+ */
+
+ public boolean isIdRefType() {
+ return false;
+ }
+
+ /**
+ * Returns the value of the 'block' attribute for this type, as a bit-signnificant
+ * integer with fields such as {@link SchemaType#DERIVATION_LIST} and {@link SchemaType#DERIVATION_EXTENSION}
+ *
+ * @return the value of the 'block' attribute for this type
+ */
+
+ public int getBlock() {
+ return 0;
+ }
+
+ /**
+ * Test whether this complex type has complex content
+ * @return true: this complex type has complex content
+ */
+ public boolean isComplexContent() {
+ return true;
+ }
+
+ /**
+ * Test whether this complex type has simple content
+ * @return false: this complex type has complex content
+ */
+
+ public boolean isSimpleContent() {
+ return false;
+ }
+
+ /**
+ * Test whether this complex type has "all" content, that is, a content model
+ * using an xs:all compositor
+ * @return false: this complex type does not use an "all" compositor
+ */
+
+ public boolean isAllContent() {
+ return false;
+ }
+
+ /**
+ * For a complex type with simple content, return the simple type of the content.
+ * Otherwise, return null.
+ * @return null: this complex type does not have simple content
+ */
+
+ /*@Nullable*/ public SimpleType getSimpleContentType() {
+ return null;
+ }
+
+ /**
+ * Test whether this complex type is derived by restriction
+ * @return false: this type is not a restriction
+ */
+ public boolean isRestricted() {
+ return false;
+ }
+
+ /**
+ * Test whether the content type of this complex type is empty
+ * @return false: the content model is not empty
+ */
+
+ public boolean isEmptyContent() {
+ return false;
+ }
+
+ /**
+ * Test whether the content model of this complexType allows empty content
+ * @return true: the content is allowed to be empty
+ */
+
+ public boolean isEmptiable() {
+ return true;
+ }
+
+ /**
+ * Test whether this complex type allows mixed content
+ * @return true: mixed content is allowed
+ */
+
+ public boolean isMixedContent() {
+ return true;
+ }
+
+ /**
+ * Get the fingerprint of the name of this type
+ * @return the fingerprint.
+ */
+
+ public int getFingerprint() {
+ return StandardNames.XS_ANY_TYPE;
+ }
+
+ /**
+ * Get the namecode of the name of this type. This includes the prefix from the original
+ * type declaration: in the case of built-in types, there may be a conventional prefix
+ * or there may be no prefix.
+ */
+
+ public int getNameCode() {
+ return StandardNames.XS_ANY_TYPE;
+ }
+
+ /**
+ * Get a description of this type for use in diagnostics
+ * @return the string "xs:anyType"
+ */
+
+ /*@NotNull*/ public String getDescription() {
+ return "xs:anyType";
+ }
+
+ /**
+ * Get the display name of the type: that is, a lexical QName with an arbitrary prefix
+ *
+ * @return a lexical QName identifying the type
+ */
+
+ /*@NotNull*/ public String getDisplayName() {
+ return "xs:anyType";
+ }
+
+
+ /**
+ * Get the URI of the schema document containing the definition of this type
+ * @return null for a built-in type
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return null;
+ }
+
+ /**
+ * Test whether this is the same type as another type. They are considered to be the same type
+ * if they are derived from the same type definition in the original XML representation (which
+ * can happen when there are multiple includes of the same file)
+ */
+
+ public boolean isSameType(SchemaType other) {
+ return (other instanceof AnyType);
+ }
+
+ /**
+ * Analyze an expression to see whether the expression is capable of delivering a value of this
+ * type.
+ *
+ @param expression the expression that delivers the content
+ * @param kind the node kind whose content is being delivered: {@link net.sf.saxon.type.Type#ELEMENT},
+ * {@link net.sf.saxon.type.Type#ATTRIBUTE}, or {@link net.sf.saxon.type.Type#DOCUMENT}
+ * @param env
+
+ */
+
+ public void analyzeContentExpression(Expression expression, int kind, StaticContext env) {
+ //return;
+ }
+
+ /**
+ * Get the typed value of a node that is annotated with this schema type.
+ * @param node the node whose typed value is required
+ * @return the typed value.
+ * @since 8.5
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize(/*@NotNull*/ NodeInfo node) {
+ return new UntypedAtomicValue(node.getStringValue());
+ }
+
+ /**
+ * Test whether this complex type subsumes another complex type. The algorithm
+ * used is as published by Thompson and Tobin, XML Europe 2003.
+ * @param sub the other type (the type that is derived by restriction, validly or otherwise)
+ * @param compiler
+ * @return null indicating that this type does indeed subsume the other; or a string indicating
+ * why it doesn't.
+ */
+
+// public String subsumes(ComplexType sub, ISchemaCompiler compiler) {
+// return null;
+// }
+
+ /**
+ * Check that this type is validly derived from a given type
+ *
+ * @param type the type from which this type is derived
+ * @param block the derivations that are blocked by the relevant element declaration
+ * @throws SchemaException
+ * if the derivation is not allowed
+ */
+
+ public void checkTypeDerivationIsOK(SchemaType type, int block) throws SchemaException {
+ if (!(type instanceof AnyType)) {
+ throw new SchemaException("Cannot derive xs:anyType from another type");
+ }
+ }
+
+ /**
+ * Find an element particle within this complex type definition having a given element name
+ * (identified by fingerprint), and return the schema type associated with that element particle.
+ * If there is no such particle, return null. If the fingerprint matches an element wildcard,
+ * return the type of the global element declaration with the given name if one exists, or AnyType
+ * if none exists and lax validation is permitted by the wildcard.
+ *
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @param considerExtensions
+ */
+
+ /*@NotNull*/ public SchemaType getElementParticleType(int fingerprint, boolean considerExtensions) {
+ return this;
+ }
+
+ /**
+ * Find an element particle within this complex type definition having a given element name
+ * (identified by fingerprint), and return the cardinality associated with that element particle,
+ * that is, the number of times the element can occur within this complex type. The value is one of
+ * {@link net.sf.saxon.expr.StaticProperty#EXACTLY_ONE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_ONE},
+ * {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_MORE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ONE_OR_MORE},
+ * If there is no such particle, return zero.
+ *
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @param considerExtensions
+ */
+
+ public int getElementParticleCardinality(int fingerprint, boolean considerExtensions) {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Find an attribute use within this complex type definition having a given attribute name
+ * (identified by fingerprint), and return the schema type associated with that attribute.
+ * If there is no such attribute use, return null. If the fingerprint matches an attribute wildcard,
+ * return the type of the global attribute declaration with the given name if one exists, or AnySimpleType
+ * if none exists and lax validation is permitted by the wildcard.
+ *
+ * @param fingerprint Identifies the name of the child element within this content model
+ */
+
+ /*@NotNull*/ public SimpleType getAttributeUseType(int fingerprint) {
+ return AnySimpleType.getInstance();
+ }
+
+ /**
+ * Find an attribute use within this complex type definition having a given attribute name
+ * (identified by fingerprint), and return the cardinality associated with that attribute,
+ * which will always be 0, 1, or 0-or-1.
+ * If there is no such attribute use, return null. If the fingerprint matches an attribute wildcard,
+ * return the type of the global attribute declaration with the given name if one exists, or AnySimpleType
+ * if none exists and lax validation is permitted by the wildcard.
+ * <p/>
+ * If there are types derived from this type by extension, search those too.
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @return the schema type associated with the attribute use identified by the fingerprint.
+ * If there is no such attribute use, return null.
+ */
+
+ public int getAttributeUseCardinality(int fingerprint) throws SchemaException {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+
+ /**
+ * Return true if this type (or any known type derived from it by extension) allows the element
+ * to have one or more attributes.
+ * @return true if attributes are allowed
+ */
+
+ public boolean allowsAttributes() {
+ return true;
+ }
+
+ /**
+ * Get a list of all the names of elements that can appear as children of an element having this
+ * complex type, as integer fingerprints. If the list is unbounded (because of wildcards or the use
+ * of xs:anyType), return null.
+ *
+ * @param children an integer set, initially empty, which on return will hold the fingerprints of all permitted
+ * child elements; if the result contains the value -1, this indicates that it is not possible to enumerate
+ * all the children, typically because of wildcards. In this case the other contents of the set should
+ * @param ignoreWildcards
+ */
+
+ public void gatherAllPermittedChildren(/*@NotNull*/ IntHashSet children, boolean ignoreWildcards) throws SchemaException {
+ children.add(-1);
+ }
+
+ /**
+ * Get a list of all the names of elements that can appear as descendants of an element having this
+ * complex type, as integer fingerprints. If the list is unbounded (because of wildcards or the use
+ * of xs:anyType), return null.
+ *
+ * @param descendants an integer set, initially empty, which on return will hold the fingerprints of all permitted
+ * descendant elements; if the result contains the value -1, this indicates that it is not possible to enumerate
+ * all the descendants, typically because of wildcards. In this case the other contents of the set should
+ * be ignored.
+ */
+
+ public void gatherAllPermittedDescendants(/*@NotNull*/ IntHashSet descendants) throws SchemaException {
+ descendants.add(-1);
+ }
+
+ /**
+ * Assuming an element is a permitted descendant in the content model of this type, determine
+ * the type of the element when it appears as a descendant. If it appears with more than one type,
+ * return xs:anyType.
+ * @param fingerprint the name of the required descendant element
+ * @return the type of the descendant element; null if the element cannot appear as a descendant;
+ * anyType if it can appear with several different types
+ */
+
+ /*@NotNull*/ public SchemaType getDescendantElementType(int fingerprint) throws SchemaException {
+ return this;
+ }
+
+ /**
+ * Assuming an element is a permitted descendant in the content model of this type, determine
+ * the cardinality of the element when it appears as a descendant.
+ * @param fingerprint the name of the required descendant element
+ * @return the cardinality of the descendant element within this complex type
+ */
+
+ public int getDescendantElementCardinality(int fingerprint) throws SchemaException {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Ask whether this type (or any known type derived from it by extension) allows the element
+ * to have children that match a wildcard
+ * @return true if the content model of this type, or its extensions, contains an element wildcard
+ */
+
+ public boolean containsElementWildcard() {
+ return true;
+ }
+
+ /**
+ * Ask whether there are any assertions defined on this complex type
+ *
+ * @return true if there are any assertions
+ */
+ public boolean hasAssertions() {
+ return false;
+ }
+}
+
diff --git a/sf/saxon/type/AtomicType.java b/sf/saxon/type/AtomicType.java
new file mode 100644
index 0000000..b7bd1dc
--- /dev/null
+++ b/sf/saxon/type/AtomicType.java
@@ -0,0 +1,79 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * Interface for atomic types (these are either built-in atomic types
+ * or user-defined atomic types). An AtomicType is both an ItemType (a possible type
+ * for items in a sequence) and a SchemaType (a possible type for validating and
+ * annotating nodes).
+ */
+public interface AtomicType extends SimpleType, PlainType {
+
+ /**
+ * Validate that a primitive atomic value is a valid instance of a type derived from the
+ * same primitive type.
+ * @param primValue the value in the value space of the primitive type.
+ * @param lexicalValue the value in the lexical space. If null, the string value of primValue
+ * is used. This value is checked against the pattern facet (if any)
+ * @param rules
+ * @return null if the value is valid; otherwise, a ValidationFailure object indicating
+ * the nature of the error.
+ * @throws UnsupportedOperationException in the case of an external object type
+ */
+
+ public ValidationFailure validate(AtomicValue primValue, CharSequence lexicalValue, ConversionRules rules);
+
+ /**
+ * Determine whether the atomic type is ordered, that is, whether less-than and greater-than comparisons
+ * are permitted
+ * @param optimistic if true, the function takes an optimistic view, returning true if ordering comparisons
+ * are available for some subtype. This mainly affects xs:duration, where the function returns true if
+ * optimistic is true, false if it is false.
+ * @return true if ordering operations are permitted
+ */
+
+ public boolean isOrdered(boolean optimistic);
+
+ /**
+ * Determine whether the type is abstract, that is, whether it cannot have instances that are not also
+ * instances of some concrete subtype
+ */
+
+ public boolean isAbstract();
+
+ /**
+ * Determine whether the atomic type is a primitive type. The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration;
+ * xs:untypedAtomic; and all supertypes of these (xs:anyAtomicType, xs:numeric, ...)
+ * @return true if the type is considered primitive under the above rules
+ */
+
+ public boolean isPrimitiveType();
+
+ /**
+ * Determine whether the atomic type is a built-in type. The built-in atomic types are the 41 atomic types
+ * defined in XML Schema, plus xs:dayTimeDuration and xs:yearMonthDuration,
+ * xs:untypedAtomic, and all supertypes of these (xs:anyAtomicType, xs:numeric, ...)
+ */
+
+ public boolean isBuiltInType();
+
+ /**
+ * Get the name of this type as a StructuredQName, unless the type is anonymous, in which case
+ * return null
+ * @return the name of the atomic type, or null if the type is anonymous.
+ */
+
+ public StructuredQName getTypeName();
+}
+
diff --git a/sf/saxon/type/BuiltInAtomicType.java b/sf/saxon/type/BuiltInAtomicType.java
new file mode 100644
index 0000000..710afc0
--- /dev/null
+++ b/sf/saxon/type/BuiltInAtomicType.java
@@ -0,0 +1,1102 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import com.saxonica.schema.UserSimpleType;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.Literal;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.instruct.ValueOf;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.io.Serializable;
+import java.util.Set;
+
+/**
+ * This class represents a built-in atomic type, which may be either a primitive type
+ * (such as xs:decimal or xs:anyURI) or a derived type (such as xs:ID or xs:dayTimeDuration).
+ */
+
+public class BuiltInAtomicType implements AtomicType, Serializable {
+
+ int fingerprint;
+ int baseFingerprint;
+ int primitiveFingerprint;
+ boolean ordered = false;
+
+ /*@NotNull*/ public static BuiltInAtomicType ANY_ATOMIC = makeAtomicType(StandardNames.XS_ANY_ATOMIC_TYPE, AnySimpleType.getInstance(), true);
+
+ /*@NotNull*/ public static BuiltInAtomicType NUMERIC = makeAtomicType(StandardNames.XS_NUMERIC, ANY_ATOMIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType STRING = makeAtomicType(StandardNames.XS_STRING, ANY_ATOMIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType BOOLEAN = makeAtomicType(StandardNames.XS_BOOLEAN, ANY_ATOMIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType DURATION = makeAtomicType(StandardNames.XS_DURATION, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType DATE_TIME = makeAtomicType(StandardNames.XS_DATE_TIME, ANY_ATOMIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType DATE = makeAtomicType(StandardNames.XS_DATE, ANY_ATOMIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType TIME = makeAtomicType(StandardNames.XS_TIME, ANY_ATOMIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType G_YEAR_MONTH = makeAtomicType(StandardNames.XS_G_YEAR_MONTH, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType G_MONTH = makeAtomicType(StandardNames.XS_G_MONTH, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType G_MONTH_DAY = makeAtomicType(StandardNames.XS_G_MONTH_DAY, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType G_YEAR = makeAtomicType(StandardNames.XS_G_YEAR, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType G_DAY = makeAtomicType(StandardNames.XS_G_DAY, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType HEX_BINARY = makeAtomicType(StandardNames.XS_HEX_BINARY, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType BASE64_BINARY = makeAtomicType(StandardNames.XS_BASE64_BINARY, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType ANY_URI = makeAtomicType(StandardNames.XS_ANY_URI, ANY_ATOMIC, true);;
+
+ /*@NotNull*/ public static BuiltInAtomicType QNAME = makeAtomicType(StandardNames.XS_QNAME, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType NOTATION = makeAtomicType(StandardNames.XS_NOTATION, ANY_ATOMIC, false);
+
+ /*@NotNull*/ public static BuiltInAtomicType UNTYPED_ATOMIC = makeAtomicType(StandardNames.XS_UNTYPED_ATOMIC, ANY_ATOMIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType DECIMAL = makeAtomicType(StandardNames.XS_DECIMAL, NUMERIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType FLOAT = makeAtomicType(StandardNames.XS_FLOAT, NUMERIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType DOUBLE = makeAtomicType(StandardNames.XS_DOUBLE, NUMERIC, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType INTEGER = makeAtomicType(StandardNames.XS_INTEGER, DECIMAL, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType NON_POSITIVE_INTEGER = makeAtomicType(StandardNames.XS_NON_POSITIVE_INTEGER, INTEGER, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType NEGATIVE_INTEGER = makeAtomicType(StandardNames.XS_NEGATIVE_INTEGER, NON_POSITIVE_INTEGER, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType LONG = makeAtomicType(StandardNames.XS_LONG, INTEGER, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType INT = makeAtomicType(StandardNames.XS_INT, LONG, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType SHORT = makeAtomicType(StandardNames.XS_SHORT, INT, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType BYTE = makeAtomicType(StandardNames.XS_BYTE, SHORT, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType NON_NEGATIVE_INTEGER = makeAtomicType(StandardNames.XS_NON_NEGATIVE_INTEGER, INTEGER, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType POSITIVE_INTEGER = makeAtomicType(StandardNames.XS_POSITIVE_INTEGER, NON_NEGATIVE_INTEGER, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType UNSIGNED_LONG = makeAtomicType(StandardNames.XS_UNSIGNED_LONG, NON_NEGATIVE_INTEGER, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType UNSIGNED_INT = makeAtomicType(StandardNames.XS_UNSIGNED_INT, UNSIGNED_LONG, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType UNSIGNED_SHORT = makeAtomicType(StandardNames.XS_UNSIGNED_SHORT, UNSIGNED_INT, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType UNSIGNED_BYTE = makeAtomicType(StandardNames.XS_UNSIGNED_BYTE, UNSIGNED_SHORT, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType YEAR_MONTH_DURATION = makeAtomicType(StandardNames.XS_YEAR_MONTH_DURATION, DURATION, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType DAY_TIME_DURATION = makeAtomicType(StandardNames.XS_DAY_TIME_DURATION, DURATION, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType NORMALIZED_STRING = makeAtomicType(StandardNames.XS_NORMALIZED_STRING, STRING, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType TOKEN = makeAtomicType(StandardNames.XS_TOKEN, NORMALIZED_STRING, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType LANGUAGE = makeAtomicType(StandardNames.XS_LANGUAGE, TOKEN, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType NAME = makeAtomicType(StandardNames.XS_NAME, TOKEN, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType NMTOKEN = makeAtomicType(StandardNames.XS_NMTOKEN, TOKEN, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType NCNAME = makeAtomicType(StandardNames.XS_NCNAME, NAME, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType ID = makeAtomicType(StandardNames.XS_ID, NCNAME, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType IDREF = makeAtomicType(StandardNames.XS_IDREF, NCNAME, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType ENTITY = makeAtomicType(StandardNames.XS_ENTITY, NCNAME, true);
+
+ /*@NotNull*/ public static BuiltInAtomicType DATE_TIME_STAMP = makeAtomicType(StandardNames.XS_DATE_TIME_STAMP, DATE_TIME, true);
+
+ static {
+
+ }
+
+ private BuiltInAtomicType(int fingerprint) {
+ this.fingerprint = fingerprint;
+ }
+
+ /**
+ * Get the local name of this type
+ *
+ * @return the local name of this type definition, if it has one. Return null in the case of an
+ * anonymous type.
+ */
+
+ public String getName() {
+ if (fingerprint == StandardNames.XS_NUMERIC) {
+ return "numeric";
+ } else {
+ return StandardNames.getLocalName(fingerprint);
+ }
+ }
+
+ /**
+ * Get the target namespace of this type
+ *
+ * @return the target namespace of this type definition, if it has one. Return null in the case
+ * of an anonymous type, and in the case of a global type defined in a no-namespace schema.
+ */
+
+ public String getTargetNamespace() {
+ return NamespaceConstant.SCHEMA;
+ }
+
+ /**
+ * Get the name of this type as an EQName, that is, a string in the format Q{uri}local.
+ *
+ * @return an EQName identifying the type.
+ */
+ public String getEQName() {
+ return "Q{" + NamespaceConstant.SCHEMA + "}" + getName();
+ }
+
+ /**
+ * Determine whether the type is abstract, that is, whether it cannot have instances that are not also
+ * instances of some concrete subtype
+ */
+
+ public boolean isAbstract() {
+ switch (fingerprint) {
+ case StandardNames.XS_NOTATION:
+ case StandardNames.XS_ANY_ATOMIC_TYPE:
+ case StandardNames.XS_NUMERIC:
+ case StandardNames.XS_ANY_SIMPLE_TYPE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Return true if this is an external object type, that is, a Saxon-defined type for external
+ * Java or .NET objects
+ */
+
+ public boolean isExternalType() {
+ return false;
+ }
+
+ /**
+ * Determine whether this is a built-in type or a user-defined type
+ */
+
+ public boolean isBuiltInType() {
+ return true;
+ }
+
+ /**
+ * Get the name of this type as a StructuredQName, unless the type is anonymous, in which case
+ * return null
+ * @return the name of the atomic type, or null if the type is anonymous.
+ */
+
+ /*@NotNull*/ public StructuredQName getTypeName() {
+ return new StructuredQName(
+ StandardNames.getPrefix(fingerprint),
+ StandardNames.getURI(fingerprint),
+ StandardNames.getLocalName(fingerprint)
+ );
+ }
+
+ /**
+ * Get the redefinition level. This is zero for a component that has not been redefined;
+ * for a redefinition of a level-0 component, it is 1; for a redefinition of a level-N
+ * component, it is N+1. This concept is used to support the notion of "pervasive" redefinition:
+ * if a component is redefined at several levels, the top level wins, but it is an error to have
+ * two versions of the component at the same redefinition level.
+ * @return the redefinition level
+ */
+
+ public int getRedefinitionLevel() {
+ return 0;
+ }
+
+ /**
+ * Determine whether the atomic type is ordered, that is, whether less-than and greater-than comparisons
+ * are permitted
+ * @param optimistic if true, the function takes an optimistic view, returning true if ordering comparisons
+ * are available for some subtype. This mainly affects xs:duration, where the function returns true if
+ * optimistic is true, false if it is false.
+ * @return true if ordering operations are permitted
+ */
+
+ public boolean isOrdered(boolean optimistic) {
+ return ordered || (optimistic && (this == BuiltInAtomicType.DURATION || this == BuiltInAtomicType.ANY_ATOMIC));
+ }
+
+
+ /**
+ * Get the URI of the schema document where the type was originally defined.
+ *
+ * @return the URI of the schema document. Returns null if the information is unknown or if this
+ * is a built-in type
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return null;
+ }
+
+ /**
+ * Determine whether the atomic type is numeric
+ *
+ * @return true if the type is a built-in numeric type
+ */
+
+ public boolean isPrimitiveNumeric() {
+ switch (fingerprint) {
+ case StandardNames.XS_INTEGER:
+ case StandardNames.XS_DECIMAL:
+ case StandardNames.XS_DOUBLE:
+ case StandardNames.XS_FLOAT:
+ case StandardNames.XS_NUMERIC:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Get the validation status - always valid
+ */
+ public final int getValidationStatus() {
+ return VALIDATED;
+ }
+
+ /**
+ * Returns the value of the 'block' attribute for this type, as a bit-significant
+ * integer with fields such as {@link SchemaType#DERIVATION_LIST} and {@link SchemaType#DERIVATION_EXTENSION}
+ *
+ * @return the value of the 'block' attribute for this type
+ */
+
+ public final int getBlock() {
+ return 0;
+ }
+
+ /**
+ * Gets the integer code of the derivation method used to derive this type from its
+ * parent. Returns zero for primitive types.
+ *
+ * @return a numeric code representing the derivation method, for example {@link SchemaType#DERIVATION_RESTRICTION}
+ */
+
+ public final int getDerivationMethod() {
+ return SchemaType.DERIVATION_RESTRICTION;
+ }
+
+ /**
+ * Determines whether derivation (of a particular kind)
+ * from this type is allowed, based on the "final" property
+ *
+ * @param derivation the kind of derivation, for example {@link SchemaType#DERIVATION_LIST}
+ * @return true if this kind of derivation is allowed
+ */
+
+ public final boolean allowsDerivation(int derivation) {
+ return true;
+ }
+
+ /**
+ * Get the types of derivation that are not permitted, by virtue of the "final" property.
+ *
+ * @return the types of derivation that are not permitted, as a bit-significant integer
+ * containing bits such as {@link net.sf.saxon.type.SchemaType#DERIVATION_EXTENSION}
+ */
+ public int getFinalProhibitions() {
+ return 0;
+ }
+
+ /**
+ * Set the base type of this type
+ *
+ * @param baseFingerprint the namepool fingerprint of the name of the base type
+ */
+
+ public final void setBaseTypeFingerprint(int baseFingerprint) {
+ this.baseFingerprint = baseFingerprint;
+ }
+
+ /**
+ * Get the fingerprint of the name of this type
+ *
+ * @return the fingerprint. Returns an invented fingerprint for an anonymous type.
+ */
+
+ public int getFingerprint() {
+ return fingerprint;
+ }
+
+ /**
+ * Get the namecode of the name of this type. This includes the prefix from the original
+ * type declaration: in the case of built-in types, there may be a conventional prefix
+ * or there may be no prefix.
+ */
+
+ public int getNameCode() {
+ return fingerprint;
+ }
+
+ /**
+ * Get the name of the type as a QName
+ *
+ * @return a StructuredQName containing the name of the type. The conventional prefix "xs" is used
+ * to represent the XML Schema namespace
+ */
+
+ /*@NotNull*/ public StructuredQName getQualifiedName() {
+ return new StructuredQName("xs", NamespaceConstant.SCHEMA, StandardNames.getLocalName(fingerprint));
+ }
+
+ /**
+ * Get the display name of the type: that is, a lexical QName with an arbitrary prefix
+ *
+ * @return a lexical QName identifying the type
+ */
+
+ public String getDisplayName() {
+ if (fingerprint == StandardNames.XS_NUMERIC) {
+ return "numeric";
+ } else {
+ return StandardNames.getDisplayName(fingerprint);
+ }
+ }
+
+
+ /**
+ * Ask whether the atomic type is a primitive type. The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration;
+ * xs:untypedAtomic; and all supertypes of these (xs:anyAtomicType, xs:numeric, ...)
+ *
+ * @return true if the type is considered primitive under the above rules
+ */
+
+ public boolean isPrimitiveType() {
+ return Type.isPrimitiveType(fingerprint);
+ }
+
+ /**
+ * Ask whether this SchemaType is a complex type
+ *
+ * @return true if this SchemaType is a complex type
+ */
+
+ public final boolean isComplexType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this is an anonymous type
+ *
+ * @return true if this SchemaType is an anonymous type
+ */
+
+ public boolean isAnonymousType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this is a plain type (a type whose instances are always atomic values)
+ * @return true
+ */
+
+ public boolean isPlainType() {
+ return true;
+ }
+
+ /**
+ * Returns the base type that this type inherits from. This method can be used to get the
+ * base type of a type that is known to be valid.
+ * If this type is a Simpletype that is a built in primitive type then null is returned.
+ *
+ * @return the base type.
+ * @throws IllegalStateException if this type is not valid.
+ */
+
+ /*@Nullable*/ public final SchemaType getBaseType() {
+ if (baseFingerprint == -1) {
+ return null;
+ } else {
+ return BuiltInType.getSchemaType(baseFingerprint);
+ }
+ }
+
+ /**
+ * Test whether a given item conforms to this type
+ *
+ * @param item The item to be tested
+ * @param context the XPath dynamic evaluation context
+ * @return true if the item is an instance of this type; false otherwise
+ */
+ public boolean matches(Item item, /*@NotNull*/ XPathContext context) {
+ return matchesItem(item, false, context.getConfiguration());
+ }
+
+ /**
+ * Test whether a given item conforms to this type
+ *
+ * @param item The item to be tested
+ * @param allowURIPromotion true if we regard a URI as effectively a subtype of String
+ * @param config the Saxon configuration (used to locate the type hierarchy cache)
+ * @return true if the item is an instance of this type; false otherwise
+ */
+
+ public boolean matchesItem(Item item, boolean allowURIPromotion, /*@NotNull*/ Configuration config) {
+ if (item instanceof AtomicValue) {
+ AtomicValue value = (AtomicValue)item;
+ // Try to match primitive types first
+ if (value.getPrimitiveType() == this) {
+ return true;
+ }
+ AtomicType type = value.getItemType();
+ if (type.getFingerprint() == getFingerprint()) {
+ // note, with compiled stylesheets one can have two objects representing
+ // the same type, so comparing identity is not safe
+ return true;
+ }
+ final TypeHierarchy th = config.getTypeHierarchy();
+ boolean ok = th.isSubType(type, this);
+ if (ok) {
+ return true;
+ }
+ if (allowURIPromotion && getFingerprint() == StandardNames.XS_STRING && th.isSubType(type, BuiltInAtomicType.ANY_URI)) {
+ // allow promotion from anyURI to string
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the type from which this item type is derived by restriction. This
+ * is the supertype in the XPath type heirarchy, as distinct from the Schema
+ * base type: this means that the supertype of xs:boolean is xs:anyAtomicType,
+ * whose supertype is item() (rather than xs:anySimpleType).
+ *
+ * @param th the type hierarchy cache, not used in this implementation
+ * @return the supertype, or null if this type is item()
+ */
+
+ /*@NotNull*/ public ItemType getSuperType(TypeHierarchy th) {
+ SchemaType base = getBaseType();
+ if (base instanceof AnySimpleType) {
+ return AnyItemType.getInstance();
+ } else {
+ return (ItemType)base;
+ }
+ }
+
+ /**
+ * Get the primitive item type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue it is Type.ATOMIC_VALUE. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that INTEGER is considered to be a primitive type.
+ */
+
+ /*@NotNull*/
+ public ItemType getPrimitiveItemType() {
+ if (isPrimitiveType()) {
+ return this;
+ } else {
+ ItemType s = (ItemType)getBaseType();
+ if (s.isPlainType()) {
+ return s.getPrimitiveItemType();
+ } else {
+ return this;
+ }
+ }
+ }
+
+ /**
+ * Get the primitive type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue it is Type.ATOMIC_VALUE. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that INTEGER is considered to be a primitive type.
+ */
+
+ public int getPrimitiveType() {
+ return primitiveFingerprint;
+ }
+
+ /**
+ * Determine whether this type is supported in a basic XSLT 2.0 processor (all types are
+ * allowed in a basic XSLT 3.0 processor)
+ *
+ * @return true if this type is permitted in a basic XSLT 2.0 processor
+ */
+
+ public boolean isAllowedInBasicXSLT() {
+ return (isPrimitiveType() && getFingerprint() != StandardNames.XS_NOTATION);
+ }
+
+ /**
+ * Determine whether this type is supported when using XSD 1.0
+ * @return true if this type is permitted in XSD 1.0
+ */
+
+ public boolean isAllowedInXSD10() {
+ return (getFingerprint() != StandardNames.XS_DATE_TIME_STAMP);
+ }
+
+ public String toString() {
+ return getDisplayName();
+ }
+
+ /**
+ * Get the item type of the atomic values that will be produced when an item
+ * of this type is atomized
+ */
+
+ /*@NotNull*/ public AtomicType getAtomizedItemType() {
+ return this;
+ }
+
+ /**
+ * Ask whether values of this type are atomizable
+ * @return true unless it is known that these items will be elements with element-only
+ * content, in which case return false
+ */
+
+ public boolean isAtomizable() {
+ return true;
+ }
+
+ /**
+ * Returns the base type that this type inherits from. This method can be used to get the
+ * base type of a type that is known to be valid.
+ * If this type is a Simpletype that is a built in primitive type then null is returned.
+ *
+ * @return the base type.
+ * @throws IllegalStateException if this type is not valid.
+ */
+
+ /*@Nullable*/ public SchemaType getKnownBaseType() {
+ return getBaseType();
+ }
+
+ /**
+ * Test whether this is the same type as another type. They are considered to be the same type
+ * if they are derived from the same type definition in the original XML representation (which
+ * can happen when there are multiple includes of the same file)
+ */
+
+ public boolean isSameType(/*@NotNull*/ SchemaType other) {
+ return other.getFingerprint() == getFingerprint();
+ }
+
+ public String getDescription() {
+ return getDisplayName();
+ }
+
+
+ /**
+ * Check that this type is validly derived from a given type
+ *
+ * @param type the type from which this type is derived
+ * @param block the derivations that are blocked by the relevant element declaration
+ * @throws SchemaException if the derivation is not allowed
+ */
+
+ public void checkTypeDerivationIsOK(/*@NotNull*/ SchemaType type, int block) throws SchemaException {
+ if (type == AnySimpleType.getInstance()) {
+ // OK
+ } else if (isSameType(type)) {
+ // OK
+ } else {
+ SchemaType base = getBaseType();
+ if (base == null) {
+ throw new SchemaException("Type " + getDescription() +
+ " is not validly derived from type " + type.getDescription());
+ }
+ try {
+ base.checkTypeDerivationIsOK(type, block);
+ } catch (SchemaException se) {
+ throw new SchemaException("Type " + getDescription() +
+ " is not validly derived from type " + type.getDescription());
+ }
+ }
+ }
+
+ /**
+ * Returns true if this SchemaType is a SimpleType
+ *
+ * @return true (always)
+ */
+
+ public final boolean isSimpleType() {
+ return true;
+ }
+
+ /**
+ * Test whether this Simple Type is an atomic type
+ *
+ * @return true, this is an atomic type
+ */
+
+ public boolean isAtomicType() {
+ return true;
+ }
+
+ /**
+ * Ask whether this type is an ID type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:ID: that is, it includes types derived
+ * from ID by restriction, list, or union. Note that for a node to be treated
+ * as an ID, its typed value must be a *single* atomic value of type ID; the type of the
+ * node, however, can still allow a list.
+ */
+
+ public boolean isIdType() {
+ return fingerprint == StandardNames.XS_ID;
+ }
+
+ /**
+ * Ask whether this type is an IDREF or IDREFS type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:IDREF: that is, it includes types derived
+ * from IDREF or IDREFS by restriction, list, or union
+ */
+
+ public boolean isIdRefType() {
+ return fingerprint == StandardNames.XS_IDREF;
+ }
+
+ /**
+ * Returns true if this type is derived by list, or if it is derived by restriction
+ * from a list type, or if it is a union that contains a list as one of its members
+ *
+ * @return true if this is a list type
+ */
+
+ public boolean isListType() {
+ return false;
+ }
+
+ /**
+ * Return true if this type is a union type (that is, if its variety is union)
+ *
+ * @return true for a union type
+ */
+
+ public boolean isUnionType() {
+ return false;
+ }
+
+ /**
+ * Determine the whitespace normalization required for values of this type
+ *
+ * @return one of PRESERVE, REPLACE, COLLAPSE
+ */
+
+ public int getWhitespaceAction() {
+ switch (getFingerprint()) {
+ case StandardNames.XS_STRING:
+ return Whitespace.PRESERVE;
+ case StandardNames.XS_NORMALIZED_STRING:
+ return Whitespace.REPLACE;
+ default:
+ return Whitespace.COLLAPSE;
+ }
+ }
+
+ /**
+ * Returns the built-in base type this type is derived from.
+ *
+ * @return the first built-in type found when searching up the type hierarchy
+ */
+ /*@Nullable*/ public SchemaType getBuiltInBaseType() {
+ BuiltInAtomicType base = this;
+ while ((base != null) && (base.getFingerprint() > 1023)) {
+ base = (BuiltInAtomicType)base.getBaseType();
+ }
+ return base;
+ }
+
+ /**
+ * Test whether this simple type is namespace-sensitive, that is, whether
+ * it is derived from xs:QName or xs:NOTATION
+ *
+ * @return true if this type is derived from xs:QName or xs:NOTATION
+ */
+
+ public boolean isNamespaceSensitive() {
+ BuiltInAtomicType base = this;
+ int fp = base.getFingerprint();
+ while (fp > 1023) {
+ base = (BuiltInAtomicType)base.getBaseType();
+ fp = base.getFingerprint();
+ }
+
+ return fp == StandardNames.XS_QNAME || fp == StandardNames.XS_NOTATION;
+ }
+
+ /**
+ * Check whether a given input string is valid according to this SimpleType
+ *
+ * @param value the input string to be checked
+ * @param nsResolver a namespace resolver used to resolve namespace prefixes if the type
+ * is namespace sensitive. The value supplied may be null; in this case any namespace-sensitive
+ * content will throw an UnsupportedOperationException.
+ * @param rules
+ * @return XPathException if the value is invalid. Note that the exception is returned rather than being thrown.
+ * Returns null if the value is valid.
+ * @throws UnsupportedOperationException if the type is namespace-sensitive and no namespace
+ * resolver is supplied
+ */
+
+ /*@Nullable*/ public ValidationFailure validateContent(/*@NotNull*/ CharSequence value, /*@Nullable*/ NamespaceResolver nsResolver,
+ /*@NotNull*/ ConversionRules rules) {
+ int f = getFingerprint();
+ if (f == StandardNames.XS_STRING ||
+ f == StandardNames.XS_ANY_SIMPLE_TYPE ||
+ f == StandardNames.XS_UNTYPED_ATOMIC ||
+ f == StandardNames.XS_ANY_ATOMIC_TYPE) {
+ return null;
+ }
+ StringConverter converter = rules.getStringConverter(this);
+ if (isNamespaceSensitive()) {
+ if (nsResolver == null) {
+ throw new UnsupportedOperationException("Cannot validate a QName without a namespace resolver");
+ }
+ converter.setNamespaceResolver(nsResolver);
+ ConversionResult result = converter.convertString(value);
+ if (result instanceof ValidationFailure) {
+ return (ValidationFailure)result;
+ }
+ if (fingerprint == StandardNames.XS_NOTATION) {
+ NotationValue nv = (NotationValue)result;
+ // This check added in 9.3. The XSLT spec says that this check should not be performed during
+ // validation. However, this appears to be based on an incorrect assumption: see spec bug 6952
+ if (!rules.isDeclaredNotation(nv.getNamespaceURI(), nv.getLocalName())) {
+ return new ValidationFailure("Notation {" + nv.getNamespaceURI() + "}" +
+ nv.getLocalName() + " is not declared in the schema");
+ }
+ }
+ return null;
+ } else {
+ return converter.validate(value);
+ }
+ }
+
+ /**
+ * Get the typed value of a node that is annotated with this schema type.
+ * @param node the node whose typed value is required
+ * @return the typed value.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize(/*@NotNull*/ NodeInfo node) throws XPathException {
+ // Fast path for common cases
+ CharSequence stringValue = node.getStringValueCS();
+ if (stringValue.length() == 0 && node.isNilled()) {
+ return new AtomicArray(EmptyIterator.emptyIterator());
+ }
+ if (fingerprint == StandardNames.XS_STRING) {
+ return StringValue.makeStringValue(stringValue);
+ } else if (fingerprint == StandardNames.XS_UNTYPED_ATOMIC) {
+ return new UntypedAtomicValue(stringValue);
+ }
+ final Configuration config = node.getConfiguration();
+ StringConverter converter = config.getConversionRules().getStringConverter(this);
+ if (isNamespaceSensitive()) {
+ converter.setNamespaceResolver(new InscopeNamespaceResolver(node));
+ }
+ return converter.convertString(stringValue).asAtomic();
+ }
+
+ /**
+ * Get the typed value corresponding to a given string value, assuming it is
+ * valid against this type (and that the containing node is not nilled)
+ *
+ *
+ * @param value the string value
+ * @param resolver a namespace resolver used to resolve any namespace prefixes appearing
+ * in the content of values. Can supply null, in which case any namespace-sensitive content
+ * will be rejected.
+ * @param rules
+ * @return an iterator over the atomic sequence comprising the typed value. The objects
+ * returned by this SequenceIterator will all be of type {@link AtomicValue}
+ * @throws ValidationException This method should be called only if it is known that the value is
+ * valid. If the value is not valid, there is no guarantee that this method will perform validation,
+ * but if it does detect a validity error, then it MAY throw a ValidationException.
+ */
+
+ /*@NotNull*/ public AtomicSequence getTypedValue(CharSequence value, NamespaceResolver resolver, /*@NotNull*/ ConversionRules rules)
+ throws ValidationException {
+ // Fast path for common cases
+ if (fingerprint == StandardNames.XS_STRING) {
+ return StringValue.makeStringValue(value);
+ } else if (fingerprint == StandardNames.XS_UNTYPED_ATOMIC) {
+ return new UntypedAtomicValue(value);
+ }
+ StringConverter converter = rules.getStringConverter(this);
+ if (isNamespaceSensitive()) {
+ converter.setNamespaceResolver(resolver);
+ }
+ return converter.convertString(value).asAtomic();
+ }
+
+ /**
+ * Two types are equal if they have the same fingerprint.
+ * Note: it is normally safe to use ==, because we always use the static constants, one instance
+ * for each built in atomic type. However, after serialization and deserialization a different instance
+ * can appear.
+ */
+
+ public boolean equals(/*@NotNull*/ Object obj) {
+ return obj instanceof BuiltInAtomicType &&
+ getFingerprint() == ((BuiltInAtomicType)obj).getFingerprint();
+ }
+
+ /**
+ * The fingerprint can be used as a hashcode
+ */
+
+ public int hashCode() {
+ return getFingerprint();
+ }
+
+
+ /**
+ * Validate that a primitive atomic value is a valid instance of a type derived from the
+ * same primitive type.
+ *
+ * @param primValue the value in the value space of the primitive type.
+ * @param lexicalValue the value in the lexical space. If null, the string value of primValue
+ * is used. This value is checked against the pattern facet (if any)
+ * @param rules
+ * @return null if the value is valid; otherwise, a ValidationFailure object indicating
+ * the nature of the error.
+ * @throws UnsupportedOperationException in the case of an external object type
+ */
+
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ AtomicValue primValue, CharSequence lexicalValue, ConversionRules rules) {
+ switch (fingerprint) {
+ case StandardNames.XS_NUMERIC:
+ case StandardNames.XS_STRING:
+ case StandardNames.XS_BOOLEAN:
+ case StandardNames.XS_DURATION:
+ case StandardNames.XS_DATE_TIME:
+ case StandardNames.XS_DATE:
+ case StandardNames.XS_TIME:
+ case StandardNames.XS_G_YEAR_MONTH:
+ case StandardNames.XS_G_MONTH:
+ case StandardNames.XS_G_MONTH_DAY:
+ case StandardNames.XS_G_YEAR:
+ case StandardNames.XS_G_DAY:
+ case StandardNames.XS_HEX_BINARY:
+ case StandardNames.XS_BASE64_BINARY:
+ case StandardNames.XS_ANY_URI:
+ case StandardNames.XS_QNAME:
+ case StandardNames.XS_NOTATION:
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ case StandardNames.XS_DECIMAL:
+ case StandardNames.XS_FLOAT:
+ case StandardNames.XS_DOUBLE:
+ case StandardNames.XS_INTEGER:
+ return null;
+ case StandardNames.XS_NON_POSITIVE_INTEGER:
+ case StandardNames.XS_NEGATIVE_INTEGER:
+ case StandardNames.XS_LONG:
+ case StandardNames.XS_INT:
+ case StandardNames.XS_SHORT:
+ case StandardNames.XS_BYTE:
+ case StandardNames.XS_NON_NEGATIVE_INTEGER:
+ case StandardNames.XS_POSITIVE_INTEGER:
+ case StandardNames.XS_UNSIGNED_LONG:
+ case StandardNames.XS_UNSIGNED_INT:
+ case StandardNames.XS_UNSIGNED_SHORT:
+ case StandardNames.XS_UNSIGNED_BYTE:
+ return ((IntegerValue)primValue).validateAgainstSubType(this);
+ case StandardNames.XS_YEAR_MONTH_DURATION:
+ case StandardNames.XS_DAY_TIME_DURATION:
+ return null; // treated as primitive
+ case StandardNames.XS_DATE_TIME_STAMP:
+ return (((CalendarValue)primValue).getTimezoneInMinutes() == CalendarValue.NO_TIMEZONE)
+ ? new ValidationFailure("xs:dateTimeStamp value must have a timezone") : null;
+ case StandardNames.XS_NORMALIZED_STRING:
+ case StandardNames.XS_TOKEN:
+ case StandardNames.XS_LANGUAGE:
+ case StandardNames.XS_NAME:
+ case StandardNames.XS_NMTOKEN:
+ case StandardNames.XS_NCNAME:
+ case StandardNames.XS_ID:
+ case StandardNames.XS_IDREF:
+ case StandardNames.XS_ENTITY:
+ StringConverter sc = rules.getStringConverter(this);
+ return sc.validate(primValue.getStringValueCS());
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Analyze an expression to see whether the expression is capable of delivering a value of this
+ * type.
+ *
+ * @param expression the expression that delivers the content
+ * @param kind the node kind whose content is being delivered: {@link Type#ELEMENT},
+ * {@link Type#ATTRIBUTE}, or {@link Type#DOCUMENT}
+ * @param env the static context
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression will never deliver a value of the correct type
+ */
+
+ public void analyzeContentExpression(/*@NotNull*/ Expression expression, int kind, StaticContext env) throws XPathException {
+ analyzeContentExpression(this, expression, env, kind);
+ }
+
+ /**
+ * Analyze an expression to see whether the expression is capable of delivering a value of this
+ * type.
+ *
+ * @param simpleType the simple type against which the expression is to be checked
+ * @param expression the expression that delivers the content
+ * @param env the static context of the expression
+ * @param kind the node kind whose content is being delivered: {@link Type#ELEMENT},
+ * {@link Type#ATTRIBUTE}, or {@link Type#DOCUMENT}
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression will never deliver a value of the correct type
+ */
+
+ public static void analyzeContentExpression(SimpleType simpleType, /*@NotNull*/ Expression expression, StaticContext env, int kind)
+ throws XPathException {
+ if (kind == Type.ELEMENT) {
+ expression.checkPermittedContents(simpleType, env, true);
+// // if we are building the content of an element or document, no atomization will take
+// // place, and therefore the presence of any element or attribute nodes in the content will
+// // cause a validity error, since only simple content is allowed
+// if (Type.isSubType(itemType, NodeKindTest.makeNodeKindTest(Type.ELEMENT))) {
+// throw new XPathException("The content of an element with a simple type must not include any element nodes");
+// }
+// if (Type.isSubType(itemType, NodeKindTest.makeNodeKindTest(Type.ATTRIBUTE))) {
+// throw new XPathException("The content of an element with a simple type must not include any attribute nodes");
+// }
+ } else if (kind == Type.ATTRIBUTE) {
+ // for attributes, do a check only for text nodes and atomic values: anything else gets atomized
+ if (expression instanceof ValueOf || expression instanceof Literal) {
+ expression.checkPermittedContents(simpleType, env, true);
+ }
+ }
+ }
+
+ /**
+ * Internal factory method to create a BuiltInAtomicType. There is one instance for each of the
+ * built-in atomic types
+ *
+ * @param fingerprint The name of the type
+ * @param baseType The base type from which this type is derived
+ * @return the newly constructed built in atomic type
+ */
+ /*@NotNull*/ private static BuiltInAtomicType makeAtomicType(int fingerprint, /*@NotNull*/ SimpleType baseType, boolean ordered) {
+ BuiltInAtomicType t = new BuiltInAtomicType(fingerprint);
+ t.setBaseTypeFingerprint(baseType.getFingerprint());
+ if (t.isPrimitiveType()) {
+ t.primitiveFingerprint = fingerprint;
+ } else {
+ t.primitiveFingerprint = ((AtomicType)baseType).getPrimitiveType();
+ }
+ t.ordered = ordered;
+ BuiltInType.register(fingerprint, t);
+ return t;
+ }
+
+ /**
+ * Apply any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess
+ * @param input the value to be preprocessed
+ * @return the value after preprocessing
+ */
+
+ public CharSequence preprocess(CharSequence input) {
+ return input;
+ }
+
+ /**
+ * Reverse any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess. This is called when converting a value of this type to
+ * a string
+ * @param input the value to be postprocessed: this is the "ordinary" result of converting
+ * the value to a string
+ * @return the value after postprocessing
+ */
+
+ public CharSequence postprocess(CharSequence input) throws ValidationException {
+ return input;
+ }
+
+ /**
+ * Visit all the schema components used in this ItemType definition
+ * @param visitor the visitor class to be called when each component is visited
+ */
+
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException {
+ // no action
+ }
+
+ /**
+ * Get the list of plain types that are subsumed by this type
+ * @return for an atomic type, the type itself; for a plain union type, the list of plain types
+ * in its transitive membership, in declaration order
+ */
+ /*@NotNull*/ public Set<PlainType> getPlainMemberTypes() {
+ return new SingletonSet<PlainType>(this);
+ }
+
+ public double getDefaultPriority() {
+ int steps = -1;
+ AtomicType base = this;
+ while (base.getFingerprint() != StandardNames.XS_ANY_ATOMIC_TYPE) {
+ if (base.getFingerprint() != StandardNames.XS_NUMERIC) {
+ // don't count "numeric" because it doesn't officially exist
+ steps++;
+ }
+ base = (AtomicType)base.getBaseType();
+ }
+ return steps;
+ }
+
+ /**
+ * Ask whether a built-in type is a numeric type (integer, float, double)
+ */
+
+ public boolean isNumericType() {
+ ItemType p = getPrimitiveItemType();
+ return (p == BuiltInAtomicType.NUMERIC || p == BuiltInAtomicType.DECIMAL ||
+ p == BuiltInAtomicType.DOUBLE || p == BuiltInAtomicType.FLOAT ||
+ p == BuiltInAtomicType.INTEGER);
+ }
+
+//#ifdefined SCHEMA
+ public FunctionItem getComponentAsFunction() {
+ return UserSimpleType.getComponentAsFunction(this);
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/type/BuiltInListType.java b/sf/saxon/type/BuiltInListType.java
new file mode 100644
index 0000000..0ba23aa
--- /dev/null
+++ b/sf/saxon/type/BuiltInListType.java
@@ -0,0 +1,571 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import com.saxonica.schema.UserSimpleType;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.value.Whitespace;
+
+import java.io.Serializable;
+
+/**
+ * <p>This class is used to implement the built-in
+ * list types NMTOKENS, ENTITIES, IDREFS. It is also used to represent the anonymous type of the
+ * xsi:schemaLocation attribute (a list of xs:anyURI values).</p>
+ */
+
+public class BuiltInListType implements ListType, Serializable {
+
+ private int fingerprint;
+
+ /*@NotNull*/ public static BuiltInListType ENTITIES = makeListType(NamespaceConstant.SCHEMA, "ENTITIES");
+ /*@NotNull*/ public static BuiltInListType IDREFS = makeListType(NamespaceConstant.SCHEMA, "IDREFS");
+ /*@NotNull*/ public static BuiltInListType NMTOKENS = makeListType(NamespaceConstant.SCHEMA, "NMTOKENS");
+ /*@NotNull*/ public static BuiltInListType ANY_URIS = makeListType(NamespaceConstant.SCHEMA_INSTANCE, "anonymous_schemaLocationType");
+
+ /**
+ * Return true if this is an external object type, that is, a Saxon-defined type for external
+ * Java or .NET objects
+ */
+
+ public boolean isExternalType() {
+ return false;
+ }
+
+ /**
+ * Determine whether this is a built-in type or a user-defined type
+ */
+
+ public boolean isBuiltInType() {
+ return true;
+ }
+
+
+ /**
+ * Get the URI of the schema document containing the definition of this type
+ *
+ * @return null for a built-in type
+ */
+
+ /*@Nullable*/
+ public String getSystemId() {
+ return null;
+ }
+
+ /**
+ * Get the redefinition level. This is zero for a component that has not been redefined;
+ * for a redefinition of a level-0 component, it is 1; for a redefinition of a level-N
+ * component, it is N+1. This concept is used to support the notion of "pervasive" redefinition:
+ * if a component is redefined at several levels, the top level wins, but it is an error to have
+ * two versions of the component at the same redefinition level.
+ *
+ * @return the redefinition level
+ */
+
+ public int getRedefinitionLevel() {
+ return 0;
+ }
+
+ /**
+ * Determine how values of this simple type are whitespace-normalized.
+ *
+ * @return one of {@link net.sf.saxon.value.Whitespace#PRESERVE}, {@link net.sf.saxon.value.Whitespace#COLLAPSE},
+ * {@link net.sf.saxon.value.Whitespace#REPLACE}.
+ */
+
+ public int getWhitespaceAction() {
+ return Whitespace.COLLAPSE;
+ }
+
+ /**
+ * The SimpleType of the items in the list.
+ */
+
+ /*@NotNull*/
+ private BuiltInAtomicType itemType;
+
+ /**
+ * Create a new ListType.
+ *
+ * @param fingerprint identifies the name of the type
+ */
+
+ public BuiltInListType(int fingerprint) {
+ this.fingerprint = fingerprint;
+ switch (fingerprint) {
+ case StandardNames.XS_ENTITIES:
+ itemType = BuiltInAtomicType.ENTITY;
+ break;
+ case StandardNames.XS_IDREFS:
+ itemType = BuiltInAtomicType.IDREF;
+ break;
+ case StandardNames.XS_NMTOKENS:
+ itemType = BuiltInAtomicType.NMTOKEN;
+ break;
+ case StandardNames.XSI_SCHEMA_LOCATION_TYPE:
+ itemType = BuiltInAtomicType.ANY_URI;
+ break;
+ }
+ }
+
+ /**
+ * Get the validation status - always valid
+ */
+ public int getValidationStatus() {
+ return VALIDATED;
+ }
+
+ /**
+ * Returns the base type that this type inherits from.
+ * If this type is a Simpletype that is a built in primitive type then null is returned.
+ *
+ * @return the base type.
+ */
+
+ /*@NotNull*/
+ public SchemaType getBaseType() {
+ return AnySimpleType.getInstance();
+ }
+
+ /**
+ * Test whether this Simple Type is an atomic type
+ *
+ * @return false, this is not an atomic type
+ */
+
+ public boolean isAtomicType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this type is an ID type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:ID: that is, it includes types derived
+ * from ID by restriction, list, or union. Note that for a node to be treated
+ * as an ID, its typed value must be a *single* atomic value of type ID; the type of the
+ * node, however, can still allow a list.
+ */
+
+ public boolean isIdType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this type is an IDREF or IDREFS type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:IDREF: that is, it includes types derived
+ * from IDREF or IDREFS by restriction, list, or union
+ */
+
+ public boolean isIdRefType() {
+ return fingerprint == StandardNames.XS_IDREFS;
+ }
+
+ /**
+ * Returns true if this type is derived by list, or if it is derived by restriction
+ * from a list type, or if it is a union that contains a list as one of its members
+ */
+
+ public boolean isListType() {
+ return true;
+ }
+
+ public boolean isUnionType() {
+ return false;
+ }
+
+ /**
+ * Test whether this is an anonymous type
+ *
+ * @return true if this SchemaType is an anonymous type
+ */
+
+ public boolean isAnonymousType() {
+ return false;
+ }
+
+ /*@NotNull*/
+ public SchemaType getBuiltInBaseType() {
+ return this;
+ }
+
+ public boolean isNamespaceSensitive() {
+ return false;
+ }
+
+ /**
+ * Get the local name of this type
+ *
+ * @return the local name of this type definition, if it has one. Return null in the case of an
+ * anonymous type.
+ */
+
+ public String getName() {
+ return StandardNames.getLocalName(fingerprint);
+ }
+
+ /**
+ * Get the target namespace of this type
+ *
+ * @return the target namespace of this type definition, if it has one. Return null in the case
+ * of an anonymous type, and in the case of a global type defined in a no-namespace schema.
+ */
+
+ public String getTargetNamespace() {
+ return NamespaceConstant.SCHEMA;
+ }
+
+ /**
+ * Get the name of this type as an EQName, that is, a string in the format Q{uri}local.
+ *
+ * @return an EQName identifying the type.
+ */
+ public String getEQName() {
+ return "Q{" + NamespaceConstant.SCHEMA + "}" + getName();
+ }
+
+ /**
+ * Get the fingerprint of the name of this type
+ *
+ * @return the fingerprint. Returns an invented fingerprint for an anonymous type.
+ */
+
+ public int getFingerprint() {
+ return fingerprint;
+ }
+
+ /**
+ * Get the namecode of the name of this type. Because built-in types don't depend on the namePool,
+ * this actually returns the fingerprint, which contains no information about the namespace prefix
+ */
+
+ public int getNameCode() {
+ return fingerprint;
+ }
+
+ /**
+ * Get the display name of the type: that is, a lexical QName with an arbitrary prefix
+ *
+ * @return a lexical QName identifying the type
+ */
+
+ public String getDisplayName() {
+ return StandardNames.getDisplayName(fingerprint);
+ }
+
+ /**
+ * Test whether this SchemaType is a complex type
+ *
+ * @return true if this SchemaType is a complex type
+ */
+
+ public boolean isComplexType() {
+ return false;
+ }
+
+ /**
+ * Test whether this SchemaType is a simple type
+ *
+ * @return true if this SchemaType is a simple type
+ */
+
+ public boolean isSimpleType() {
+ return true;
+ }
+
+ /**
+ * Returns the value of the 'block' attribute for this type, as a bit-signnificant
+ * integer with fields such as {@link SchemaType#DERIVATION_LIST} and {@link SchemaType#DERIVATION_EXTENSION}
+ *
+ * @return the value of the 'block' attribute for this type
+ */
+
+ public int getBlock() {
+ return 0;
+ }
+
+ /**
+ * Returns the base type that this type inherits from. This method can be used to get the
+ * base type of a type that is known to be valid.
+ * If this type is a Simpletype that is a built in primitive type then null is returned.
+ *
+ * @return the base type.
+ * @throws IllegalStateException if this type is not valid.
+ */
+
+ /*@NotNull*/
+ public SchemaType getKnownBaseType() throws IllegalStateException {
+ return AnySimpleType.getInstance();
+ }
+
+ /**
+ * Gets the integer code of the derivation method used to derive this type from its
+ * parent. Returns zero for primitive types.
+ *
+ * @return a numeric code representing the derivation method, for example {@link SchemaType#DERIVATION_RESTRICTION}
+ */
+
+ public int getDerivationMethod() {
+ return SchemaType.DERIVATION_LIST;
+ }
+
+ /**
+ * Determines whether derivation (of a particular kind)
+ * from this type is allowed, based on the "final" property
+ *
+ * @param derivation the kind of derivation, for example {@link SchemaType#DERIVATION_LIST}
+ * @return true if this kind of derivation is allowed
+ */
+
+ public boolean allowsDerivation(int derivation) {
+ return true;
+ }
+
+ /**
+ * Get the types of derivation that are not permitted, by virtue of the "final" property.
+ *
+ * @return the types of derivation that are not permitted, as a bit-significant integer
+ * containing bits such as {@link net.sf.saxon.type.SchemaType#DERIVATION_EXTENSION}
+ */
+ public int getFinalProhibitions() {
+ return 0;
+ }
+
+
+//#ifdefined SCHEMA
+
+ /**
+ * Get the schema component in the form of a function item. This allows schema information
+ * to be made visible to XSLT or XQuery code. The function makes available the contents of the
+ * schema component as defined in the XSD specification. The function takes a string as argument
+ * representing a property name, and returns the corresponding property of the schema component.
+ * There is also a property "class" which returns the kind of schema component, for example
+ * "Attribute Declaration".
+ *
+ * @return the schema component represented as a function from property names to property values.
+ */
+ public FunctionItem getComponentAsFunction() {
+ return UserSimpleType.getComponentAsFunction(this);
+ }
+//#endif
+
+ /**
+ * Get the typed value of a node that is annotated with this schema type. The result of this method will always be consistent with the method
+ *
+ * @param node the node whose typed value is required
+ * @return the typed value.
+ * @since 8.5
+ */
+
+ public AtomicSequence atomize(/*@NotNull*/ NodeInfo node) throws XPathException {
+ try {
+ return getTypedValue(node.getStringValue(),
+ new InscopeNamespaceResolver(node),
+ node.getConfiguration().getConversionRules());
+ } catch (ValidationException err) {
+ throw new XPathException("Internal error: value doesn't match its type annotation. " + err.getMessage());
+ }
+ }
+
+ /**
+ * Test whether this is the same type as another type. They are considered to be the same type
+ * if they are derived from the same type definition in the original XML representation (which
+ * can happen when there are multiple includes of the same file)
+ */
+
+ public boolean isSameType(/*@NotNull*/ SchemaType other) {
+ return other.getFingerprint() == getFingerprint();
+ }
+
+ public String getDescription() {
+ return getDisplayName();
+ }
+
+ /**
+ * Check that this type is validly derived from a given type
+ *
+ * @param type the type from which this type is derived
+ * @param block the derivations that are blocked by the relevant element declaration
+ * @throws SchemaException if the derivation is not allowed
+ */
+
+ public void checkTypeDerivationIsOK(SchemaType type, int block) throws SchemaException {
+ //
+ }
+
+ /**
+ * Get the local name of this type
+ *
+ * @return the local part of the name, or null if the type is anonymous
+ */
+
+ public String getLocalName() {
+ return getDisplayName().substring(3);
+ }
+
+ /**
+ * Returns the simpleType of the items in this ListType.
+ *
+ * @return the simpleType of the items in this ListType.
+ */
+
+ /*@NotNull*/
+ public SimpleType getItemType() {
+ return itemType;
+ }
+
+ /**
+ * Apply the whitespace normalization rules for this simple type
+ *
+ * @param value the string before whitespace normalization
+ * @return the string after whitespace normalization
+ */
+
+ public String applyWhitespaceNormalization(String value) {
+ return Whitespace.collapseWhitespace(value).toString();
+ }
+
+ /**
+ * Analyze an expression to see whether the expression is capable of delivering a value of this
+ * type.
+ *
+ * @param expression the expression that delivers the content
+ * @param kind the node kind whose content is being delivered: {@link Type#ELEMENT},
+ * {@link Type#ATTRIBUTE}, or {@link Type#DOCUMENT}
+ * @param env the XPath static context
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression will never deliver a value of the correct type
+ */
+
+ public void analyzeContentExpression(/*@NotNull*/ Expression expression, int kind, StaticContext env) throws XPathException {
+ BuiltInAtomicType.analyzeContentExpression(this, expression, env, kind);
+ }
+
+ /**
+ * Check whether a given input string is valid according to this SimpleType
+ *
+ * @param value the input string to be checked
+ * @param nsResolver a namespace resolver used to resolve namespace prefixes if the type
+ * is namespace sensitive. The value supplied may be null; in this case any namespace-sensitive
+ * content will throw an UnsupportedOperationException.
+ * @param rules the conversion rules for this configuration
+ * @return either null to indicate that validation succeeded, or a ValidationFailure object giving information
+ * about why it failed
+ * @throws UnsupportedOperationException if the type is namespace-sensitive and no namespace
+ * resolver is supplied
+ */
+
+ /*@Nullable*/
+ public ValidationFailure validateContent(
+ /*@NotNull*/ CharSequence value, /*@Nullable*/ NamespaceResolver nsResolver, /*@NotNull*/ ConversionRules rules) {
+ SimpleType base = getItemType();
+ StringTokenIterator iter = new StringTokenIterator(value.toString());
+ int count = 0;
+ while (true) {
+ StringValue val = iter.next();
+ if (val == null) {
+ break;
+ }
+ count++;
+ ValidationFailure v = base.validateContent(val.getStringValue(), nsResolver, rules);
+ if (v != null) {
+ return v;
+ }
+ }
+ if (count == 0) {
+ return new ValidationFailure("The built-in list type " +
+ StandardNames.getDisplayName(fingerprint) +
+ " does not allow a zero-length list");
+ }
+ return null;
+ }
+
+ /**
+ * Get the typed value of a given input string. This method assumes that the input value
+ * is valid according to this SimpleType
+ *
+ * @param value the string whose typed value is required
+ * @param resolver namespace resolver for namespace-sensitive content
+ * @param rules
+ */
+
+ /*@NotNull*/
+ public AtomicSequence getTypedValue(/*@NotNull*/ CharSequence value, NamespaceResolver resolver, ConversionRules rules) throws ValidationException {
+ UnfailingIterator<StringValue> iter = new StringTokenIterator(value.toString());
+ ListTypeMappingFunction map = new ListTypeMappingFunction();
+ map.resolver = resolver;
+ map.atomicType = (AtomicType) getItemType();
+ map.rules = rules;
+ try {
+ return new AtomicArray(new MappingIterator(iter, map));
+ } catch (XPathException err) {
+ throw new ValidationException(err); // should not happen
+ }
+ }
+
+ /*@NotNull*/
+ private static BuiltInListType makeListType(String namespace, String lname) {
+ BuiltInListType t = new BuiltInListType(StandardNames.getFingerprint(namespace, lname));
+ BuiltInType.register(t.getFingerprint(), t);
+ return t;
+ }
+
+ private static class ListTypeMappingFunction implements MappingFunction<StringValue, AtomicValue> {
+
+ public NamespaceResolver resolver;
+ /*@Nullable*/ public AtomicType atomicType;
+ public ConversionRules rules;
+
+ /**
+ * The typed value of a list-valued node is obtained by tokenizing the string value and
+ * applying a mapping function to the sequence of tokens.
+ * This method implements the mapping function. It is for internal use only.
+ * For details see {@link net.sf.saxon.expr.MappingFunction}
+ */
+
+ public SequenceIterator map(/*@NotNull*/ StringValue item) throws XPathException {
+ try {
+ return atomicType.getTypedValue(item.getStringValue(), resolver, rules).iterate();
+ } catch (ValidationException err) {
+ throw new XPathException(err);
+ }
+ }
+ }
+
+ /**
+ * Apply any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess
+ *
+ * @param input the value to be preprocessed
+ * @return the value after preprocessing
+ */
+
+ public CharSequence preprocess(CharSequence input) {
+ return input;
+ }
+
+ /**
+ * Reverse any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess. This is called when converting a value of this type to
+ * a string
+ *
+ * @param input the value to be postprocessed: this is the "ordinary" result of converting
+ * the value to a string
+ * @return the value after postprocessing
+ */
+
+ public CharSequence postprocess(CharSequence input) throws ValidationException {
+ return input;
+ }
+}
+
diff --git a/sf/saxon/type/BuiltInType.java b/sf/saxon/type/BuiltInType.java
new file mode 100644
index 0000000..c54233f
--- /dev/null
+++ b/sf/saxon/type/BuiltInType.java
@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.z.IntHashMap;
+import net.sf.saxon.om.StandardNames;
+
+import java.io.Serializable;
+
+/**
+ * This non-instantiable class acts as a register of Schema objects containing all the built-in types:
+ * that is, the types defined in the "xs" namespace.
+ *
+ * <p>Previously called BuiltInSchemaFactory; but its original function has largely been moved to the two
+ * classes {@link BuiltInAtomicType} and {@link BuiltInListType}
+ */
+
+public abstract class BuiltInType implements Serializable {
+
+ /**
+ * Table of all built in types
+ */
+
+ /*@NotNull*/ private static IntHashMap<SchemaType> lookup = new IntHashMap(100);
+
+ /**
+ * Class is never instantiated
+ */
+
+ private BuiltInType() {
+ }
+
+ static {
+ register(StandardNames.XS_ANY_SIMPLE_TYPE, AnySimpleType.getInstance());
+ register(StandardNames.XS_ANY_TYPE, AnyType.getInstance());
+ register(StandardNames.XS_UNTYPED, Untyped.getInstance());
+ register(StandardNames.XS_ERROR, ErrorType.getInstance());
+ }
+
+ /**
+ * Get the schema type with a given fingerprint
+ * @param fingerprint the fingerprint representing the name of the required type
+ * @return the SchemaType object representing the given type, if known, otherwise null
+ */
+
+ public static SchemaType getSchemaType(int fingerprint) {
+ SchemaType st = lookup.get(fingerprint);
+ if (st == null) {
+ // this means the method has been called before doing the static initialization of BuiltInAtomicType
+ // or BuiltInListType. So force it now
+ if (BuiltInAtomicType.DOUBLE == null || BuiltInListType.NMTOKENS == null) {
+ // no action, except to force the initialization to run
+ }
+ st = lookup.get(fingerprint);
+ }
+ return st;
+ }
+
+ /**
+ * Method for internal use to register a built in type with this class
+ * @param fingerprint the fingerprint of the type name
+ * @param type the SchemaType representing the built in type
+ */
+
+ static void register(int fingerprint, SchemaType type) {
+ lookup.put(fingerprint, type);
+ }
+
+
+}
+
diff --git a/sf/saxon/type/ComplexType.java b/sf/saxon/type/ComplexType.java
new file mode 100644
index 0000000..9a934c8
--- /dev/null
+++ b/sf/saxon/type/ComplexType.java
@@ -0,0 +1,247 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.z.IntHashSet;
+
+
+/**
+ * A complex type as defined in XML Schema: either a user-defined complex type, or xs:anyType, or xs:untyped.
+ * In the non-schema-aware version of the Saxon product, the only complex type encountered is xs:untyped.
+ */
+
+public interface ComplexType extends SchemaType {
+
+ public static final int VARIETY_EMPTY = 0;
+ public static final int VARIETY_SIMPLE = 1;
+ public static final int VARIETY_ELEMENT_ONLY = 2;
+ public static final int VARIETY_MIXED = 3;
+
+ public static final int OPEN_CONTENT_ABSENT = 0;
+ public static final int OPEN_CONTENT_NONE = 1;
+ public static final int OPEN_CONTENT_INTERLEAVE = 2;
+ public static final int OPEN_CONTENT_SUFFIX = 3;
+
+ /**
+ * Get the variety of this complex type. This will be one of the values
+ * {@link #VARIETY_EMPTY}, {@link #VARIETY_MIXED}, {@link #VARIETY_SIMPLE}, or
+ * {@link #VARIETY_ELEMENT_ONLY}
+ */
+
+ public int getVariety();
+
+ /**
+ * Test whether this complex type has been marked as abstract. This corresponds to
+ * the {abstract} property in the schema component model.
+ *
+ * @return true if this complex type is abstract.
+ */
+
+ public boolean isAbstract();
+
+ /**
+ * Test whether this complex type has complex content. This represents one aspect of the
+ * {content type} property in the schema component model.
+ *
+ * @return true if and only if this complex type has a complex content model, that is, if its variety is one
+ * of empty, mixed, or element-only.
+ */
+
+ public boolean isComplexContent();
+
+ /**
+ * Test whether this complexType has simple content. This represents one aspect of the
+ * {content type} property in the schema component model.
+ *
+ * @return true if and only if this complex type has a simple content model, that is, if its variety is simple.
+ */
+
+ public boolean isSimpleContent();
+
+ /**
+ * Test whether this complex type has "all" content, that is, a content model
+ * using an xs:all compositor
+ * @return true if the type has an "all" content model
+ */
+
+ public boolean isAllContent();
+
+ /**
+ * Get the simple content type. This represents one aspect of the
+ * {content type} property in the schema component model.
+ *
+ * @return For a complex type with simple content, returns the simple type of the content.
+ * Otherwise, returns null.
+ */
+
+ /*@Nullable*/ public SimpleType getSimpleContentType();
+
+ /**
+ * Test whether this complex type is derived by restriction. This corresponds to one
+ * aspect of the {derivation method} property in the schema component model.
+ *
+ * @return true if this complex type is derived by restriction
+ */
+
+ public boolean isRestricted();
+
+ /**
+ * Test whether the content model of this complex type is empty. This represents one aspect of the
+ * {content type} property in the schema component model.
+ *
+ * @return true if the content model is defined as empty
+ */
+
+ public boolean isEmptyContent();
+
+ /**
+ * Test whether the content model of this complex type allows empty content. This property applies only if
+ * this is a complex type with complex content.
+ *
+ * @return true if empty content is valid
+ */
+
+ public boolean isEmptiable() throws SchemaException;
+
+ /**
+ * Test whether this complex type allows mixed content. This represents one aspect of the
+ * {content type} property in the schema component model. This property applies only if
+ * this is a complex type with complex content.
+ *
+ * @return true if mixed content is allowed
+ */
+
+ public boolean isMixedContent();
+
+ /**
+ * Find an element particle within this complex type definition having a given element name
+ * (identified by fingerprint), and return the schema type associated with that element particle.
+ * If there is no such particle, return null. If the fingerprint matches an element wildcard,
+ * return the type of the global element declaration with the given name if one exists, or AnyType
+ * if none exists and lax validation is permitted by the wildcard.
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @param considerExtensions
+ * @return the schema type associated with the child element particle with the given name.
+ * If there is no such particle, return null.
+ */
+
+ /*@Nullable*/ public SchemaType getElementParticleType(int fingerprint, boolean considerExtensions) throws SchemaException, ValidationException;
+
+ /**
+ * Find an element particle within this complex type definition having a given element name
+ * (identified by fingerprint), and return the cardinality associated with that element particle,
+ * that is, the number of times the element can occur within this complex type. The value is one of
+ * {@link net.sf.saxon.expr.StaticProperty#EXACTLY_ONE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_ONE},
+ * {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_MORE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ONE_OR_MORE},
+ * If there is no such particle, return {@link net.sf.saxon.expr.StaticProperty#EMPTY}.
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @param considerExtensions
+ * @return the cardinality associated with the child element particle with the given name.
+ * If there is no such particle, return {@link net.sf.saxon.expr.StaticProperty#EMPTY}.
+ */
+
+ public int getElementParticleCardinality(int fingerprint, boolean considerExtensions) throws SchemaException, ValidationException;
+
+ /**
+ * Find an attribute use within this complex type definition having a given attribute name
+ * (identified by fingerprint), and return the schema type associated with that attribute.
+ * If there is no such attribute use, return null. If the fingerprint matches an attribute wildcard,
+ * return the type of the global attribute declaration with the given name if one exists, or AnySimpleType
+ * if none exists and lax validation is permitted by the wildcard.
+ * <p>
+ * If there are types derived from this type by extension, search those too.
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @return the schema type associated with the attribute use identified by the fingerprint.
+ * If there is no such attribute use, return null.
+ */
+
+ /*@Nullable*/ public SimpleType getAttributeUseType(int fingerprint) throws SchemaException;
+
+ /**
+ * Find an attribute use within this complex type definition having a given attribute name
+ * (identified by fingerprint), and return the cardinality associated with that attribute,
+ * which will always be 0, 1, or 0-or-1.
+ * If there is no such attribute use, return 0. If the fingerprint matches an attribute wildcard,
+ * return 0-or-1.
+ * <p>
+ * If there are types derived from this type by extension, search those too.
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @return the cardinality associated with the attribute use identified by the fingerprint.
+ */
+
+ public int getAttributeUseCardinality(int fingerprint) throws SchemaException;
+
+ /**
+ * Return true if this type (or any known type derived from it by extension) allows the element
+ * to have one or more attributes.
+ * @return true if attributes (other than the standard xsi: attributes) are allowed. The value
+ * false indicates that only the standard attributes in the xsi namespace are permitted.
+ */
+
+ public boolean allowsAttributes();
+
+ /**
+ * Get a list of all the names of elements that can appear as children of an element having this
+ * complex type, as integer fingerprints. If the list is unbounded (because of wildcards or the use
+ * of xs:anyType), return null.
+ * @param children an integer set, initially empty, which on return will hold the fingerprints of all permitted
+ * child elements; if the result contains the value -1, this indicates that it is not possible to enumerate
+ * all the children, typically because of wildcards. In this case the other contents of the set should
+ * @param ignoreWildcards
+ */
+
+ public void gatherAllPermittedChildren(IntHashSet children, boolean ignoreWildcards) throws SchemaException;
+
+ /**
+ * Get a list of all the names of elements that can appear as descendants of an element having this
+ * complex type, as integer fingerprints. If the list is unbounded (because of wildcards or the use
+ * of xs:anyType), include a -1 in the result.
+ * @param descendants an integer set, initially empty, which on return will hold the fingerprints of all permitted
+ * descendant elements; if the result contains the value -1, this indicates that it is not possible to enumerate
+ * all the descendants, typically because of wildcards. In this case the other contents of the set should
+ * be ignored.
+ */
+
+ public void gatherAllPermittedDescendants(IntHashSet descendants) throws SchemaException;
+
+ /**
+ * Assuming an element is a permitted descendant in the content model of this type, determine
+ * the type of the element when it appears as a descendant. If it appears with more than one type,
+ * return xs:anyType.
+ * @param fingerprint the name of the required descendant element
+ * @return the type of the descendant element; null if the element cannot appear as a descendant;
+ * anyType if it can appear with several different types
+ */
+
+ /*@Nullable*/ public SchemaType getDescendantElementType(int fingerprint) throws SchemaException;
+
+ /**
+ * Assuming an element is a permitted descendant in the content model of this type, determine
+ * the cardinality of the element when it appears as a descendant.
+ * @param fingerprint the name of the required descendant element
+ * @return the cardinality of the descendant element within this complex type
+ */
+
+ public int getDescendantElementCardinality(int fingerprint) throws SchemaException;
+
+ /**
+ * Ask whether this type (or any known type derived from it by extension) allows the element
+ * to have children that match a wildcard
+ * @return true if the content model of this type, or its extensions, contains an element wildcard
+ */
+
+ boolean containsElementWildcard();
+
+ /**
+ * Ask whether there are any assertions defined on this complex type
+ * @return true if there are any assertions
+ */
+
+ public boolean hasAssertions();
+}
+
diff --git a/sf/saxon/type/ConversionResult.java b/sf/saxon/type/ConversionResult.java
new file mode 100644
index 0000000..91f35ae
--- /dev/null
+++ b/sf/saxon/type/ConversionResult.java
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.value.AtomicValue;
+
+/**
+ * This is a marker interface used as the result methods that convert or cast values from one type
+ * to another. It is implemented by AtomicValue, which indicates a successful conversion, and by
+ * ValidationFailure, which indicates an unsuccessful conversion. An unsuccessful conversion does not
+ * throw an exception because exceptions are expensive and should not be used on success paths. For example
+ * when validating a union, conversion failures are to be expected.
+ */
+public interface ConversionResult {
+
+ /**
+ * Calling this method on a ConversionResult returns the AtomicValue that results
+ * from the conversion if the conversion was successful, and throws a ValidationException
+ * explaining the conversion error otherwise.
+ *
+ * <p>Use this method if you are calling a conversion method that returns a ConversionResult,
+ * and if you want to throw an exception if the conversion fails.</p>
+ *
+ * @return the atomic value that results from the conversion if the conversion was successful
+ * @throws ValidationException if the conversion was not successful
+ */
+
+ public AtomicValue asAtomic() throws ValidationException;
+
+}
+
diff --git a/sf/saxon/type/Converter.java b/sf/saxon/type/Converter.java
new file mode 100644
index 0000000..af268e3
--- /dev/null
+++ b/sf/saxon/type/Converter.java
@@ -0,0 +1,871 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.*;
+
+import java.math.BigDecimal;
+
+/**
+ * A converter implements conversions from one atomic type to another - that is, it implements the casting
+ * rules for a (source type, destination type) pair.
+ * <p/>
+ * <p>There is potentially one Converter implementation for each pair of (source, target) classes; though in many
+ * cases the same implementation handles a number of such pairs.</p>
+ * <p/>
+ * <p>In cases where the conversion rules are fixed (specifically, where they do not depend on differences between
+ * versions of the XSD or QT specifications), the appropriate Converter can be obtained as a static constant, for example
+ * {@link #BOOLEAN_TO_DOUBLE}. In other cases the converter is paramaterized by the {@link ConversionRules} object,
+ * and should be obtained by calling the appropriate factory method on the ConversionRules.</p>
+ * <p/>
+ * <p>Where the source type of the conversion is xs:string, the converter will always be a subclass of
+ * {@link StringConverter}</p>
+ */
+public abstract class Converter {
+ /*@NotNull*/ public final static StringConverter.IdentityConverter
+ IDENTITY_CONVERTER = new StringConverter.IdentityConverter();
+ /*@NotNull*/ public final static ToUntypedAtomicConverter
+ TO_UNTYPED_ATOMIC = new ToUntypedAtomicConverter();
+ /*@NotNull*/ public final static ToStringConverter
+ TO_STRING = new ToStringConverter();
+ /*@NotNull*/ public final static NumericToFloat
+ NUMERIC_TO_FLOAT = new NumericToFloat();
+ /*@NotNull*/ public final static BooleanToFloat
+ BOOLEAN_TO_FLOAT = new BooleanToFloat();
+ /*@NotNull*/ public final static NumericToDouble
+ NUMERIC_TO_DOUBLE = new NumericToDouble();
+ /*@NotNull*/ public final static BooleanToDouble
+ BOOLEAN_TO_DOUBLE = new BooleanToDouble();
+ /*@NotNull*/ public final static DoubleToDecimal
+ DOUBLE_TO_DECIMAL = new DoubleToDecimal();
+ /*@NotNull*/ public final static FloatToDecimal
+ FLOAT_TO_DECIMAL = new FloatToDecimal();
+ /*@NotNull*/ public final static IntegerToDecimal
+ INTEGER_TO_DECIMAL = new IntegerToDecimal();
+ /*@NotNull*/ public final static NumericToDecimal
+ NUMERIC_TO_DECIMAL = new NumericToDecimal();
+ /*@NotNull*/ public final static BooleanToDecimal
+ BOOLEAN_TO_DECIMAL = new BooleanToDecimal();
+ /*@NotNull*/ public final static DoubleToInteger
+ DOUBLE_TO_INTEGER = new DoubleToInteger();
+ /*@NotNull*/ public final static FloatToInteger
+ FLOAT_TO_INTEGER = new FloatToInteger();
+ /*@NotNull*/ public final static DecimalToInteger
+ DECIMAL_TO_INTEGER = new DecimalToInteger();
+ /*@NotNull*/ public final static NumericToInteger
+ NUMERIC_TO_INTEGER = new NumericToInteger();
+ /*@NotNull*/ public final static BooleanToInteger
+ BOOLEAN_TO_INTEGER = new BooleanToInteger();
+ /*@NotNull*/ public final static DurationToDayTimeDuration
+ DURATION_TO_DAY_TIME_DURATION = new DurationToDayTimeDuration();
+ /*@NotNull*/ public final static DurationToYearMonthDuration
+ DURATION_TO_YEAR_MONTH_DURATION = new DurationToYearMonthDuration();
+ /*@NotNull*/ public final static DateToDateTime
+ DATE_TO_DATE_TIME = new DateToDateTime();
+ /*@NotNull*/ public final static DateTimeToDate
+ DATE_TIME_TO_DATE = new DateTimeToDate();
+ /*@NotNull*/ public final static DateTimeToGMonth
+ DATE_TIME_TO_G_MONTH = new DateTimeToGMonth();
+ /*@NotNull*/ public final static DateTimeToGYearMonth
+ DATE_TIME_TO_G_YEAR_MONTH = new DateTimeToGYearMonth();
+ /*@NotNull*/ public final static DateTimeToGYear
+ DATE_TIME_TO_G_YEAR = new DateTimeToGYear();
+ /*@NotNull*/ public final static DateTimeToGMonthDay
+ DATE_TIME_TO_G_MONTH_DAY = new DateTimeToGMonthDay();
+ /*@NotNull*/ public final static DateTimeToGDay
+ DATE_TIME_TO_G_DAY = new DateTimeToGDay();
+ /*@NotNull*/ public final static DateTimeToTime
+ DATE_TIME_TO_TIME = new DateTimeToTime();
+ /*@NotNull*/ public final static NumericToBoolean
+ NUMERIC_TO_BOOLEAN = new NumericToBoolean();
+ /*@NotNull*/ public final static Base64BinaryToHexBinary
+ BASE64_BINARY_TO_HEX_BINARY = new Base64BinaryToHexBinary();
+ /*@NotNull*/ public final static HexBinaryToBase64Binary
+ HEX_BINARY_TO_BASE64_BINARY = new HexBinaryToBase64Binary();
+ /*@NotNull*/ public final static NotationToQName
+ NOTATION_TO_QNAME = new NotationToQName();
+ /*@NotNull*/ public final static QNameToNotation
+ QNAME_TO_NOTATION = new QNameToNotation();
+
+
+ /**
+ * Convenience method to convert a given value to a given type. Note: it is more efficient
+ * to obtain a converter in advance and to reuse it for multiple conversions
+ * @param value the value to be converted
+ * @param targetType the type to which the value is to be converted
+ * @param rules the conversion rules for the configuration
+ * @return the converted value
+ * @throws ValidationException if conversion fails
+ */
+
+ public static AtomicValue convert(/*@NotNull*/ AtomicValue value, /*@NotNull*/ AtomicType targetType, /*@NotNull*/ ConversionRules rules)
+ throws ValidationException {
+ Converter converter = rules.getConverter(value.getPrimitiveType(), targetType);
+ if (converter == null) {
+ ValidationException ve = new ValidationException("Cannot convert value from " + value.getPrimitiveType() + " to " + targetType);
+ ve.setErrorCode("FORG0001");
+ throw ve;
+ }
+ return converter.convert(value).asAtomic();
+ }
+
+
+ // All converters can hold a reference to the conversion rules, though many don't
+ private ConversionRules conversionRules;
+
+ // Protected constructor for a Converter
+
+ protected Converter() {
+ }
+
+ /**
+ * Construct a converter with a given set of conversion rules. For use in constructing subclasses
+ *
+ * @param rules the conversion rules for the configuration
+ */
+ protected Converter(ConversionRules rules) {
+ setConversionRules(rules);
+ }
+
+ /**
+ * Convert an atomic value from the source type to the target type
+ *
+ * @param input the atomic value to be converted, which the caller guarantees to be of the appropriate
+ * type for the converter. The results are undefined if the value is of the wrong type;
+ * possible outcomes are (apparent) success, or a ClassCastException.
+ * @return the result of the conversion, as an {@link AtomicValue}, if conversion succeeds, or a {@link ValidationFailure}
+ * object describing the reasons for failure if conversion is not possible. Note that the ValidationFailure
+ * object is not (and does not contain) an exception, because it does not necessarily result in an error being
+ * thrown, and creating exceptions on non-failure paths is expensive.
+ */
+
+ /*@NotNull*/
+ public abstract ConversionResult convert(/*@NotNull*/ AtomicValue input);
+
+ /**
+ * Set the conversion rules to be used by this Converter
+ *
+ * @param rules the conversion rules
+ */
+
+ public final void setConversionRules(ConversionRules rules) {
+ this.conversionRules = rules;
+ }
+
+ /**
+ * Get the conversion rules to be used by this Converter
+ *
+ * @return the conversion rules
+ */
+
+
+ public final ConversionRules getConversionRules() {
+ return conversionRules;
+ }
+
+ /**
+ * Ask if this converter implements a conversion that requires XPath 3.0 (or XQuery 3.0 etc)
+ * to be enabled
+ *
+ * @return true if XPath 3.0 support is required
+ */
+
+ public boolean isXPath30Conversion() {
+ return false;
+ }
+
+ /**
+ * Ask if this converter will always succeed
+ *
+ * @return true if this Converter will never return a ValidationFailure
+ */
+
+ public boolean isAlwaysSuccessful() {
+ return false;
+ }
+
+ /**
+ * Provide a namespace resolver, needed for conversion to namespace-sensitive types such as QName and NOTATION.
+ * The resolver is ignored if the target type is not namespace-sensitive
+ *
+ * @param resolver the namespace resolver to be used
+ */
+
+ public void setNamespaceResolver(NamespaceResolver resolver) {
+ // no action
+ }
+
+ /**
+ * Get the namespace resolver if one has been supplied
+ *
+ * @return the namespace resolver, or null if none has been supplied
+ */
+
+ /*@Nullable*/ public NamespaceResolver getNamespaceResolver() {
+ return null;
+ }
+
+ /**
+ * Converter that does nothing except change the type annotation of the value. The caller
+ * is responsible for ensuring that this type annotation is legimite, that is, that the value
+ * is in the value space of this type
+ */
+
+ public static class UpCastingConverter extends Converter {
+ private AtomicType newTypeAnnotation;
+
+ public UpCastingConverter(AtomicType annotation) {
+ this.newTypeAnnotation = annotation;
+ }
+
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return input.copyAsSubType(newTypeAnnotation);
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converter that does nothing except change the type annotation of the value. The caller
+ * is responsible for ensuring that this type annotation is legimite, that is, that the value
+ * is in the value space of this type
+ */
+
+ public static class DownCastingConverter extends Converter {
+ private AtomicType newType;
+
+ public DownCastingConverter(AtomicType annotation, ConversionRules rules) {
+ this.newType = annotation;
+ setConversionRules(rules);
+ }
+
+ public AtomicType getTargetType() {
+ return newType;
+ }
+
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return convert(input, input.getCanonicalLexicalRepresentation());
+ }
+
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input, CharSequence lexicalForm) {
+ ValidationFailure f = newType.validate(input, lexicalForm, getConversionRules());
+ if (f == null) {
+ // success
+ return input.copyAsSubType(newType);
+ } else {
+ // validation failed
+ return f;
+ }
+ }
+ }
+
+ /**
+ * Converter that operates in two phases, via an intermediate type
+ */
+
+ public static class TwoPhaseConverter extends StringConverter {
+ private Converter phaseOne;
+ private Converter phaseTwo;
+
+ public TwoPhaseConverter(Converter phaseOne, Converter phaseTwo) {
+ this.phaseOne = phaseOne;
+ this.phaseTwo = phaseTwo;
+ }
+
+ /*@Nullable*/ public static TwoPhaseConverter makeTwoPhaseConverter(/*@NotNull*/ AtomicType inputType, /*@NotNull*/ AtomicType viaType, /*@NotNull*/ AtomicType outputType, ConversionRules rules) {
+ return new TwoPhaseConverter(
+ rules.getConverter(inputType, viaType),
+ rules.getConverter(viaType, outputType));
+ }
+
+ @Override
+ public void setNamespaceResolver(NamespaceResolver resolver) {
+ phaseOne.setNamespaceResolver(resolver);
+ phaseTwo.setNamespaceResolver(resolver);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ ConversionResult temp = phaseOne.convert(input);
+ if (temp instanceof ValidationFailure) {
+ return temp;
+ }
+ return phaseTwo.convert((AtomicValue) temp);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ ConversionResult temp = ((StringConverter) phaseOne).convertString(input);
+ if (temp instanceof ValidationFailure) {
+ return temp;
+ }
+ return ((DownCastingConverter) phaseTwo).convert((AtomicValue) temp, input);
+ }
+ }
+
+ /**
+ * Converts any value to untyped atomic
+ */
+
+ public static class ToUntypedAtomicConverter extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new UntypedAtomicValue(input.getStringValueCS());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts any value to a string
+ */
+
+ public static class ToStringConverter extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new StringValue(input.getStringValueCS());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts any numeric value to xs:float
+ */
+
+ public static class NumericToFloat extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new FloatValue(((NumericValue) input).getFloatValue());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a boolean to an xs:float
+ */
+
+ public static class BooleanToFloat extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new FloatValue(((BooleanValue) input).getBooleanValue() ? 1.0f : 0.0f);
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts any numeric value to a double.
+ */
+
+ public static class NumericToDouble extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ if (input instanceof DoubleValue) {
+ return input;
+ } else {
+ return new DoubleValue(((NumericValue) input).getDoubleValue());
+ }
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a boolean to a double
+ */
+
+ public static class BooleanToDouble extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new DoubleValue(((BooleanValue) input).getBooleanValue() ? 1.0e0 : 0.0e0);
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Convers a double to a decimal
+ */
+
+ public static class DoubleToDecimal extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ try {
+ return new DecimalValue(((DoubleValue) input).getDoubleValue());
+ } catch (ValidationException e) {
+ return new ValidationFailure(e);
+ }
+ }
+ }
+
+ /**
+ * Converts a float to a decimal
+ */
+
+ public static class FloatToDecimal extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ try {
+ return new DecimalValue(((FloatValue) input).getFloatValue());
+ } catch (ValidationException e) {
+ return new ValidationFailure(e);
+ }
+ }
+ }
+
+ /**
+ * Converts an integer to a decimal
+ */
+
+ public static class IntegerToDecimal extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ if (input instanceof Int64Value) {
+ return new DecimalValue(((Int64Value) input).longValue());
+ } else {
+ return new DecimalValue(((BigIntegerValue) input).asDecimal());
+ }
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts any numeric value to a decimal
+ */
+
+ public static class NumericToDecimal extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ try {
+ BigDecimal decimal = ((NumericValue) input).getDecimalValue();
+ return new DecimalValue(decimal);
+ } catch (XPathException e) {
+ return new ValidationFailure(e);
+ }
+ }
+ }
+
+ /**
+ * Converts a boolean to a decimal
+ */
+
+ public static class BooleanToDecimal extends Converter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return ((BooleanValue) input).getBooleanValue() ? DecimalValue.ONE : DecimalValue.ZERO;
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+
+ /**
+ * Converts a double to an integer
+ */
+
+ public static class DoubleToInteger extends Converter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return IntegerValue.makeIntegerValue((DoubleValue) input);
+ }
+ }
+
+ /**
+ * Converts a float to an integer
+ */
+
+ public static class FloatToInteger extends Converter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return IntegerValue.makeIntegerValue(new DoubleValue(((FloatValue) input).getDoubleValue()));
+ }
+ }
+
+ /**
+ * Converts a decimal to an integer. Because an xs:integer is an xs:decimal,
+ * this must also be prepared to accept an xs:integer
+ */
+
+ public static class DecimalToInteger extends Converter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ if (input instanceof IntegerValue) {
+ return input;
+ }
+ return BigIntegerValue.makeIntegerValue(((DecimalValue) input).getDecimalValue().toBigInteger());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts any numeric value to an integer.
+ */
+
+ public static class NumericToInteger extends Converter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ try {
+ if (input instanceof IntegerValue) {
+ return input;
+ } else if (input instanceof DoubleValue) {
+ return IntegerValue.makeIntegerValue((DoubleValue) input);
+ } else if (input instanceof FloatValue) {
+ return IntegerValue.makeIntegerValue(new DoubleValue(((FloatValue) input).getDoubleValue()));
+ } else {
+ return BigIntegerValue.makeIntegerValue(((NumericValue) input).getDecimalValue().toBigInteger());
+ }
+ } catch (XPathException e) {
+ return new ValidationFailure(e);
+ }
+ }
+ }
+
+ /**
+ * Converts a boolean to an integer
+ */
+
+
+ public static class BooleanToInteger extends Converter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return ((BooleanValue) input).getBooleanValue() ? Int64Value.PLUS_ONE : Int64Value.ZERO;
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a duration to a dayTimeDuration
+ */
+
+ public static class DurationToDayTimeDuration extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ DurationValue d = (DurationValue) input;
+ return new DayTimeDurationValue(d.signum(), d.getDays(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMicroseconds());
+ }
+ }
+
+ /**
+ * Converts a duration to a yearMonthDuration
+ */
+
+ public static class DurationToYearMonthDuration extends Converter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ DurationValue d = (DurationValue) input;
+ return YearMonthDurationValue.fromMonths(d.getTotalMonths());
+ }
+ }
+
+ /**
+ * Converts a date to a dateTime
+ */
+
+ public static class DateToDateTime extends Converter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return ((DateValue) input).toDateTime();
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a dateTime to a date
+ */
+
+ public static class DateTimeToDate extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ DateTimeValue dt = (DateTimeValue) input;
+ return new DateValue(dt.getYear(), dt.getMonth(), dt.getDay(), dt.getTimezoneInMinutes(), dt.isXsd10Rules());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a dateTime to a gMonth
+ */
+
+ public static class DateTimeToGMonth extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ DateTimeValue dt = (DateTimeValue) input;
+ return new GMonthValue(dt.getMonth(), dt.getTimezoneInMinutes());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a dateTime to a gYearMonth
+ */
+
+ public static class DateTimeToGYearMonth extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ DateTimeValue dt = (DateTimeValue) input;
+ return new GYearMonthValue(dt.getYear(), dt.getMonth(), dt.getTimezoneInMinutes(), dt.isXsd10Rules());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a dateTime to a gYear
+ */
+
+ public static class DateTimeToGYear extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ DateTimeValue dt = (DateTimeValue) input;
+ return new GYearValue(dt.getYear(), dt.getTimezoneInMinutes(), dt.isXsd10Rules());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a dateTime to a gMonthDay
+ */
+
+ public static class DateTimeToGMonthDay extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ DateTimeValue dt = (DateTimeValue) input;
+ return new GMonthDayValue(dt.getMonth(), dt.getDay(), dt.getTimezoneInMinutes());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a dateTime to a gDay
+ */
+
+ public static class DateTimeToGDay extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ DateTimeValue dt = (DateTimeValue) input;
+ return new GDayValue(dt.getDay(), dt.getTimezoneInMinutes());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a dateTime to a time
+ */
+
+ public static class DateTimeToTime extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ DateTimeValue dt = (DateTimeValue) input;
+ return new TimeValue(dt.getHour(), dt.getMinute(), dt.getSecond(), dt.getMicrosecond(), dt.getTimezoneInMinutes());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts a numeric value to a boolean
+ */
+
+ public static class NumericToBoolean extends Converter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ try {
+ return BooleanValue.get(input.effectiveBooleanValue());
+ } catch (XPathException err) {
+ throw new AssertionError(err);
+ }
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts base64 to hexBinary
+ */
+
+ public static class Base64BinaryToHexBinary extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new HexBinaryValue(((Base64BinaryValue) input).getBinaryValue());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts string to base64
+ */
+
+ public static class StringToBase64BinaryConverter extends StringConverter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return convertString(input.getStringValueCS());
+ }
+
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ try {
+ return new Base64BinaryValue(input);
+ } catch (XPathException e) {
+ return new ValidationFailure(e);
+ }
+ }
+ }
+
+ /**
+ * Converts hexBinary to base64Binary
+ */
+
+ public static class HexBinaryToBase64Binary extends Converter {
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new Base64BinaryValue(((HexBinaryValue) input).getBinaryValue());
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts Notation to QName
+ */
+
+ public static class NotationToQName extends Converter {
+ @Override
+ public boolean isXPath30Conversion() {
+ return true;
+ }
+
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new QNameValue(((NotationValue) input).getStructuredQName(), BuiltInAtomicType.QNAME);
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts QName to Notation
+ */
+
+ public static class QNameToNotation extends Converter {
+
+ @Override
+ public boolean isXPath30Conversion() {
+ return true;
+ }
+
+ /*@NotNull*/ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new NotationValue(((QNameValue) input).getStructuredQName(), BuiltInAtomicType.NOTATION);
+ }
+ }
+
+ /**
+ * Converter that implements the promotion rules to a required type of xs:double
+ */
+
+ public static class PromoterToDouble extends Converter {
+
+ /*@Nullable*/ private StringConverter stringToDouble = null;
+
+ /*@NotNull*/
+ @Override
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ if (input instanceof DoubleValue) {
+ return input;
+ } else if (input instanceof NumericValue) {
+ return new DoubleValue(((NumericValue) input).getDoubleValue());
+ } else if (input instanceof UntypedAtomicValue) {
+ if (stringToDouble == null) {
+ stringToDouble = getConversionRules().getStringConverter(BuiltInAtomicType.DOUBLE);
+ }
+ return stringToDouble.convert(input);
+ } else {
+ ValidationFailure err = new ValidationFailure(
+ "Cannot promote non-numeric value to xs:double");
+ err.setErrorCode("XPTY0004");
+ return err;
+ }
+ }
+ }
+
+ /**
+ * Converter that implements the promotion rules to a required type of xs:float
+ */
+
+ public static class PromoterToFloat extends Converter {
+
+ /*@Nullable*/ private StringConverter stringToFloat = null;
+
+ /*@NotNull*/
+ @Override
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ if (input instanceof FloatValue) {
+ return input;
+ } else if (input instanceof DoubleValue) {
+ ValidationFailure err = new ValidationFailure(
+ "Cannot promote from xs:double to xs:float");
+ err.setErrorCode("XPTY0004");
+ return err;
+ } else if (input instanceof NumericValue) {
+ return new FloatValue((float) ((NumericValue) input).getDoubleValue());
+ } else if (input instanceof UntypedAtomicValue) {
+ if (stringToFloat == null) {
+ stringToFloat = getConversionRules().getStringConverter(BuiltInAtomicType.FLOAT);
+ }
+ return stringToFloat.convert(input);
+ } else {
+ ValidationFailure err = new ValidationFailure(
+ "Cannot promote non-numeric value to xs:double");
+ err.setErrorCode("XPTY0004");
+ return err;
+ }
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/type/ErrorType.java b/sf/saxon/type/ErrorType.java
new file mode 100644
index 0000000..bf766fd
--- /dev/null
+++ b/sf/saxon/type/ErrorType.java
@@ -0,0 +1,513 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import com.saxonica.schema.UserSimpleType;
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.value.UntypedAtomicValue;
+import net.sf.saxon.value.Whitespace;
+
+import java.util.Collections;
+import java.util.Set;
+
+
+/**
+ * This class has a singleton instance which represents the XML Schema 1.1 built-in type xs:error
+ */
+
+public final class ErrorType implements UnionType, PlainType {
+
+ /*@NotNull*/ private static ErrorType theInstance = new ErrorType();
+
+ /**
+ * Private constructor
+ */
+ private ErrorType() {
+ }
+
+ /**
+ * Get the local name of this type
+ *
+ * @return the local name of this type definition, if it has one. Return null in the case of an
+ * anonymous type.
+ */
+
+ /*@NotNull*/ public String getName() {
+ return "error";
+ }
+
+ /**
+ * Get the target namespace of this type
+ *
+ * @return the target namespace of this type definition, if it has one. Return null in the case
+ * of an anonymous type, and in the case of a global type defined in a no-namespace schema.
+ */
+
+ public String getTargetNamespace() {
+ return NamespaceConstant.SCHEMA;
+ }
+
+ /**
+ * Get the name of this type as an EQName, that is, a string in the format Q{uri}local.
+ *
+ * @return an EQName identifying the type. In the case of an anonymous type, an internally-generated
+ * name is returned
+ */
+ public String getEQName() {
+ return "Q{" + NamespaceConstant.SCHEMA + "}error";
+ }
+
+ public boolean containsListType() {
+ return false;
+ }
+
+ public Set<PlainType> getPlainMemberTypes() {
+ return Collections.emptySet();
+ }
+
+ /**
+ * Return true if this is an external object type, that is, a Saxon-defined type for external
+ * Java or .NET objects
+ */
+
+ public boolean isExternalType() {
+ return false;
+ }
+
+ /**
+ * Determine whether this is a built-in type or a user-defined type
+ */
+
+ public boolean isBuiltInType() {
+ return true;
+ }
+
+ /**
+ * Get the redefinition level. This is zero for a component that has not been redefined;
+ * for a redefinition of a level-0 component, it is 1; for a redefinition of a level-N
+ * component, it is N+1. This concept is used to support the notion of "pervasive" redefinition:
+ * if a component is redefined at several levels, the top level wins, but it is an error to have
+ * two versions of the component at the same redefinition level.
+ * @return the redefinition level
+ */
+
+ public int getRedefinitionLevel() {
+ return 0;
+ }
+
+ /**
+ * Get the URI of the schema document containing the definition of this type
+ * @return null for a built-in type
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return null;
+ }
+
+ /**
+ * Get the singular instance of this class
+ * @return the singular object representing xs:anyType
+ */
+
+ /*@NotNull*/ public static ErrorType getInstance() {
+ return theInstance;
+ }
+
+ /**
+ * Get the validation status - always valid
+ */
+ public int getValidationStatus() {
+ return VALIDATED;
+ }
+
+ /**
+ * Get the base type
+ * @return AnyType
+ */
+
+ /*@NotNull*/ public SchemaType getBaseType() {
+ return AnySimpleType.getInstance();
+ }
+
+ /**
+ * Returns the base type that this type inherits from. This method can be used to get the
+ * base type of a type that is known to be valid.
+ * @return the base type.
+ */
+
+ /*@NotNull*/ public SchemaType getKnownBaseType() throws IllegalStateException {
+ return getBaseType();
+ }
+
+ /**
+ * Test whether this SchemaType is a complex type
+ *
+ * @return true if this SchemaType is a complex type
+ */
+
+ public boolean isComplexType() {
+ return false;
+ }
+
+ /**
+ * Test whether this SchemaType is a simple type
+ * @return true if this SchemaType is a simple type
+ */
+
+ public boolean isSimpleType() {
+ return true;
+ }
+
+ /**
+ * Get the fingerprint of the name of this type
+ * @return the fingerprint.
+ */
+
+ public int getFingerprint() {
+ return StandardNames.XS_ERROR;
+ }
+
+
+
+ /**
+ * Get the namecode of the name of this type. This includes the prefix from the original
+ * type declaration: in the case of built-in types, there may be a conventional prefix
+ * or there may be no prefix.
+ */
+
+ public int getNameCode() {
+ return StandardNames.XS_ERROR;
+ }
+
+ /**
+ * Get a description of this type for use in diagnostics
+ * @return the string "xs:anyType"
+ */
+
+ /*@NotNull*/ public String getDescription() {
+ return "xs:error";
+ }
+
+ /**
+ * Get the display name of the type: that is, a lexical QName with an arbitrary prefix
+ *
+ * @return a lexical QName identifying the type
+ */
+
+ /*@NotNull*/ public String getDisplayName() {
+ return "xs:error";
+ }
+
+ /**
+ * Test whether this is the same type as another type. They are considered to be the same type
+ * if they are derived from the same type definition in the original XML representation (which
+ * can happen when there are multiple includes of the same file)
+ */
+
+ public boolean isSameType(SchemaType other) {
+ return (other instanceof ErrorType);
+ }
+
+ /**
+ * Get the typed value of a node that is annotated with this schema type.
+ * @param node the node whose typed value is required
+ * @return the typed value.
+ * @since 8.5
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize(/*@NotNull*/ NodeInfo node) {
+ return new UntypedAtomicValue(node.getStringValueCS());
+ }
+
+ /**
+ * Check that this type is validly derived from a given type
+ *
+ * @param type the type from which this type is derived
+ * @param block the derivations that are blocked by the relevant element declaration
+ * @throws net.sf.saxon.type.SchemaException
+ * if the derivation is not allowed
+ */
+
+ public void checkTypeDerivationIsOK(/*@NotNull*/ SchemaType type, int block) throws SchemaException {
+ if (type == this || type == AnySimpleType.getInstance()) {
+ return;
+ }
+ throw new SchemaException("Type xs:error is not validly derived from " + type.getDescription());
+ }
+
+ /**
+ * Test whether this Simple Type is an atomic type
+ * @return false, this is not (necessarily) an atomic type
+ */
+
+ public boolean isAtomicType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this type is an ID type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:ID: that is, it includes types derived
+ * from ID by restriction, list, or union. Note that for a node to be treated
+ * as an ID, its typed value must be a *single* atomic value of type ID; the type of the
+ * node, however, can still allow a list.
+ */
+
+ public boolean isIdType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this type is an IDREF or IDREFS type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:IDREF: that is, it includes types derived
+ * from IDREF or IDREFS by restriction, list, or union
+ */
+
+ public boolean isIdRefType() {
+ return false;
+ }
+
+ public boolean isAnonymousType() {
+ return false;
+ }
+
+
+ /**
+ * Determine whether this is a list type
+ * @return false (it isn't a list type)
+ */
+ public boolean isListType() {
+ return false;
+ }
+
+ /**
+ * Determin whether this is a union type
+ * @return true (this is a union type with no members)
+ */
+ public boolean isUnionType() {
+ return true;
+ }
+
+ /**
+ * Get the built-in ancestor of this type in the type hierarchy
+ * @return xs:anySimpleType
+ */
+ /*@NotNull*/ public SchemaType getBuiltInBaseType() {
+ return AnySimpleType.getInstance();
+ }
+
+ /**
+ * Get the typed value corresponding to a given string value, assuming it is
+ * valid against this type
+ *
+ *
+ * @param value the string value
+ * @param resolver a namespace resolver used to resolve any namespace prefixes appearing
+ * in the content of values. Can supply null, in which case any namespace-sensitive content
+ * will be rejected.
+ * @param rules
+ * @return an iterator over the atomic sequence comprising the typed value. The objects
+ * returned by this SequenceIterator will all be of type {@link net.sf.saxon.value.AtomicValue}
+ * @throws ValidationException if the supplied value is not in the lexical space of the data type (which is
+ * always true for this type)
+ */
+
+ /*@NotNull*/ public AtomicSequence getTypedValue(CharSequence value, NamespaceResolver resolver, ConversionRules rules) throws ValidationException {
+ throw new ValidationException("Cast to xs:error always fails");
+ }
+
+ /**
+ * Check whether a given input string is valid according to this SimpleType
+ * @param value the input string to be checked
+ * @param nsResolver a namespace resolver used to resolve namespace prefixes if the type
+ * is namespace sensitive. The value supplied may be null; in this case any namespace-sensitive
+ * content will throw an UnsupportedOperationException.
+ * @param rules
+ * @return null if validation succeeds (which it never does for this implementation)
+ * @throws UnsupportedOperationException if the type is namespace-sensitive and no namespace
+ * resolver is supplied
+ */
+ /*@NotNull*/ public ValidationFailure validateContent(/*@NotNull*/ CharSequence value, NamespaceResolver nsResolver, /*@NotNull*/ ConversionRules rules) {
+ return new ValidationFailure("No content is ever valid against the type xs:error");
+ }
+
+ /**
+ * Test whether this type represents namespace-sensitive content
+ * @return false
+ */
+ public boolean isNamespaceSensitive() {
+ return false;
+ }
+
+ /**
+ * Returns the value of the 'block' attribute for this type, as a bit-signnificant
+ * integer with fields such as {@link net.sf.saxon.type.SchemaType#DERIVATION_LIST} and {@link net.sf.saxon.type.SchemaType#DERIVATION_EXTENSION}
+ *
+ * @return the value of the 'block' attribute for this type
+ */
+
+ public int getBlock() {
+ return 0;
+ }
+
+ /**
+ * Gets the integer code of the derivation method used to derive this type from its
+ * parent. Returns zero for primitive types.
+ *
+ * @return a numeric code representing the derivation method, for example {@link net.sf.saxon.type.SchemaType#DERIVATION_RESTRICTION}
+ */
+
+ public int getDerivationMethod() {
+ return SchemaType.DERIVATION_RESTRICTION;
+ }
+
+ /**
+ * Determines whether derivation (of a particular kind)
+ * from this type is allowed, based on the "final" property
+ *
+ * @param derivation the kind of derivation, for example {@link net.sf.saxon.type.SchemaType#DERIVATION_LIST}
+ * @return true if this kind of derivation is allowed
+ */
+
+ public boolean allowsDerivation(int derivation) {
+ return false;
+ }
+
+ /**
+ * Get the types of derivation that are not permitted, by virtue of the "final" property.
+ *
+ * @return the types of derivation that are not permitted, as a bit-significant integer
+ * containing bits such as {@link net.sf.saxon.type.SchemaType#DERIVATION_EXTENSION}
+ */
+ public int getFinalProhibitions() {
+ return SchemaType.DERIVATION_EXTENSION | SchemaType.DERIVATION_RESTRICTION | SchemaType.DERIVATION_LIST |
+ SchemaType.DERIVATION_UNION;
+ }
+
+ /**
+ * Determine how values of this simple type are whitespace-normalized.
+ *
+ * @return one of {@link net.sf.saxon.value.Whitespace#PRESERVE}, {@link net.sf.saxon.value.Whitespace#COLLAPSE},
+ * {@link net.sf.saxon.value.Whitespace#REPLACE}.
+ */
+
+ public int getWhitespaceAction() {
+ return Whitespace.COLLAPSE;
+ }
+
+ /**
+ * Analyze an expression to see whether the expression is capable of delivering a value of this
+ * type.
+ *
+ * @param expression the expression that delivers the content
+ * @param kind the node kind whose content is being delivered: {@link Type#ELEMENT},
+ * {@link Type#ATTRIBUTE}, or {@link Type#DOCUMENT}
+ * @param env
+ */
+
+ public void analyzeContentExpression(Expression expression, int kind, StaticContext env) throws XPathException {
+ throw new XPathException("No expression can ever return a value of type xs:error");
+ }
+
+ /**
+ * Apply any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess
+ * @param input the value to be preprocessed
+ * @return the value after preprocessing
+ */
+
+ public CharSequence preprocess(CharSequence input) {
+ return input;
+ }
+
+ /**
+ * Reverse any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess. This is called when converting a value of this type to
+ * a string
+ * @param input the value to be postprocessed: this is the "ordinary" result of converting
+ * the value to a string
+ * @return the value after postprocessing
+ */
+
+ public CharSequence postprocess(CharSequence input) throws ValidationException {
+ return input;
+ }
+
+ public boolean isPlainType() {
+ return true;
+ }
+
+ public boolean matches(Item item, XPathContext context) {
+ return false;
+ }
+
+ public boolean matchesItem(Item item, boolean allowURIPromotion, Configuration config) {
+ return false;
+ }
+
+ public ItemType getSuperType(TypeHierarchy th) {
+ return AnyItemType.getInstance();
+ }
+
+ public ItemType getPrimitiveItemType() {
+ return this;
+ }
+
+ public int getPrimitiveType() {
+ return Type.ITEM;
+ }
+
+ public double getDefaultPriority() {
+ return -1000;
+ }
+
+ public PlainType getAtomizedItemType() {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+
+ public boolean isAtomizable() {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SequenceType getResultTypeOfCast() {
+ return SequenceType.EMPTY_SEQUENCE;
+ }
+
+ public String toString() {
+ return "xs:error";
+ }
+
+ //#ifdefined SCHEMA
+ /**
+ * Get the schema component in the form of a function item. This allows schema information
+ * to be made visible to XSLT or XQuery code. The function makes available the contents of the
+ * schema component as defined in the XSD specification. The function takes a string as argument
+ * representing a property name, and returns the corresponding property of the schema component.
+ * There is also a property "class" which returns the kind of schema component, for example
+ * "Attribute Declaration".
+ *
+ * @return the schema component represented as a function from property names to property values.
+ */
+ public FunctionItem getComponentAsFunction() {
+ return UserSimpleType.getComponentAsFunction(this);
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/type/ExternalObjectType.java b/sf/saxon/type/ExternalObjectType.java
new file mode 100644
index 0000000..0dc7210
--- /dev/null
+++ b/sf/saxon/type/ExternalObjectType.java
@@ -0,0 +1,273 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.ObjectValue;
+
+/**
+ * This class represents the type of an external Java object returned by
+ * an extension function, or supplied as an external variable/parameter.
+ */
+
+public class ExternalObjectType implements ItemType {
+
+ public static ExternalObjectType EXTERNAL_OBJECT_TYPE = new ExternalObjectType(Object.class);
+
+ private Class javaClass;
+
+ /**
+ * Create an external object type.
+ * @param javaClass the Java class to which this type corresponds
+ */
+
+ public ExternalObjectType(Class javaClass) {
+ this.javaClass = javaClass;
+ }
+
+ /**
+ * Create an external object type.
+ * @param javaClass the Java class to which this type corresponds
+ * @param config the Saxon configuration. This argument is no longer used (since 9.5).
+ */
+
+ public ExternalObjectType(/*@NotNull*/ Class javaClass, /*@NotNull*/ Configuration config) {
+ this(javaClass);
+ }
+
+ /**
+ * Get the local name of this type.
+ * @return the fully qualified name of the Java class.
+ */
+
+ /*@Nullable*/ public String getName() {
+ return javaClass.getName();
+ }
+
+ /**
+ * Get the target namespace of this type. The is always NamespaceConstant.JAVA_TYPE.
+ * @return the target namespace of this type definition.
+ */
+
+ /*@Nullable*/ public String getTargetNamespace() {
+ return NamespaceConstant.JAVA_TYPE;
+ }
+
+ /**
+ * Return true if this is an external object type, that is, a Saxon-defined type for external
+ * Java or .NET objects
+ * @return true (always)
+ */
+
+ public boolean isExternalType() {
+ return true;
+ }
+
+ /**
+ * Get the name of this type as a StructuredQName, unless the type is anonymous, in which case
+ * return null
+ * @return the name of the atomic type, or null if the type is anonymous.
+ */
+
+ /*@Nullable*/ public StructuredQName getTypeName() {
+ return new StructuredQName("", NamespaceConstant.JAVA_TYPE, javaClass.getName());
+ }
+
+ /**
+ * Ask whether this is a plain type (a type whose instances are always atomic values)
+ * @return true
+ */
+
+ public boolean isPlainType() {
+ return false;
+ }
+
+ /**
+ * Get the primitive item type corresponding to this item type.
+ * @return EXTERNAL_OBJECT_TYPE, the ExternalObjectType that encapsulates
+ * the Java type Object.class.
+ */
+
+ /*@NotNull*/ public ItemType getPrimitiveItemType() {
+ return EXTERNAL_OBJECT_TYPE;
+ }
+
+ /**
+ * Get the primitive type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue it is Type.ATOMIC. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that INTEGER is considered to be a primitive type.
+ */
+
+ public int getPrimitiveType() {
+ return StandardNames.XS_ANY_ATOMIC_TYPE;
+ }
+
+ /**
+ * Get the item type of the atomic values that will be produced when an item
+ * of this type is atomized
+ * @return BuiltInAtomicType.STRING, because atomization returns the result of toString()
+ */
+
+ public AtomicType getAtomizedItemType() {
+ return BuiltInAtomicType.STRING;
+ }
+
+ /**
+ * Ask whether values of this type are atomizable
+ * @return true, because (since Saxon 9.5) atomization will always return the result of toString()
+ */
+
+ public boolean isAtomizable() {
+ return true;
+ }
+
+ /**
+ * Get the relationship of this external object type to another external object type
+ * @param other the other external object type
+ * @return the relationship of this external object type to another external object type,
+ * as one of the constants in class {@link TypeHierarchy}, for example {@link TypeHierarchy#SUBSUMES}
+ */
+
+ public int getRelationship(/*@NotNull*/ ExternalObjectType other) {
+ Class j2 = other.javaClass;
+ if (javaClass.equals(j2)) {
+ return TypeHierarchy.SAME_TYPE;
+ } else if (javaClass.isAssignableFrom(j2)) {
+ return TypeHierarchy.SUBSUMES;
+ } else if (j2.isAssignableFrom(javaClass)) {
+ return TypeHierarchy.SUBSUMED_BY;
+ } else if (javaClass.isInterface() || j2.isInterface()) {
+ return TypeHierarchy.OVERLAPS; // there may be an overlap, we play safe
+ } else {
+ return TypeHierarchy.DISJOINT;
+ }
+ }
+
+ /*@NotNull*/ public String getDescription() {
+ return getDisplayName();
+ }
+
+ /**
+ * Test whether this item type is an atomic type
+ * @return false, this is not considered to be an atomic type
+ */
+
+ public boolean isAtomicType() {
+ return false;
+ }
+
+ /**
+ * Get the Java class to which this external object type corresponds
+ * @return the corresponding Java class
+ */
+
+ public Class getJavaClass() {
+ return javaClass;
+ }
+
+ /**
+ * Test whether a given item conforms to this type
+ *
+ * @param item The item to be tested
+ * @param context the XPath dynamic evaluation context
+ * @return true if the item is an instance of this type; false otherwise
+ */
+ public boolean matches(/*@NotNull*/ Item item, /*@NotNull*/ XPathContext context) {
+ if (item instanceof ObjectValue) {
+ Object obj = ((ObjectValue)item).getObject();
+ return javaClass.isAssignableFrom(obj.getClass());
+ }
+ return false;
+ }
+
+ /**
+ * Test whether a given item conforms to this type
+ * @param item The item to be tested
+ * @param allowURIPromotion ignored for this overloading of the method
+ * @param config ignored for this overloading of the method
+ * @return true if the item is an instance of this type; false otherwise
+ */
+
+ public boolean matchesItem(/*@NotNull*/ Item item, boolean allowURIPromotion, Configuration config) {
+ if (item instanceof ObjectValue) {
+ Object obj = ((ObjectValue)item).getObject();
+ return javaClass.isAssignableFrom(obj.getClass());
+ }
+ return false;
+ }
+
+
+
+ /*@NotNull*/ public ItemType getSuperType(TypeHierarchy th) {
+ if (javaClass == Object.class) {
+ return AnyItemType.getInstance();
+ }
+ Class javaSuper = javaClass.getSuperclass();
+ if (javaSuper == null) {
+ // this happens for an interface
+ return EXTERNAL_OBJECT_TYPE;
+ }
+ return new ExternalObjectType(javaSuper);
+ }
+
+ /*@NotNull*/ public String toString() {
+ return getDisplayName();
+ }
+
+ /*@NotNull*/ public String getDisplayName() {
+ return "java-type:" + javaClass.getName();
+ }
+
+ /**
+ * Determine the default priority of this item type when used on its own as a Pattern
+ *
+ * @return the default priority
+ */
+ public double getDefaultPriority() {
+ return 0;
+ }
+
+ /**
+ * Visit all the schema components used in this ItemType definition
+ *
+ * @param visitor the visitor class to be called when each component is visited
+ * @throws net.sf.saxon.trans.XPathException
+ * if an error occurs
+ */
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException {
+ // no action
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+
+ public int hashCode() {
+ return javaClass.hashCode();
+ }
+
+ /**
+ * Test whether two ExternalObjectType objects represent the same type
+ * @param obj the other ExternalObjectType
+ * @return true if the two objects represent the same type
+ */
+
+ public boolean equals(/*@NotNull*/ Object obj) {
+ return obj instanceof ExternalObjectType && javaClass == ((ExternalObjectType)obj).javaClass;
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/type/FunctionItemType.java b/sf/saxon/type/FunctionItemType.java
new file mode 100644
index 0000000..053aef1
--- /dev/null
+++ b/sf/saxon/type/FunctionItemType.java
@@ -0,0 +1,65 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.TypeCheckerEnvironment;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.SequenceType;
+
+/**
+ * Higher-order functions in XPath 3.0 introduce a third kind of Item, namely a Function Item.
+ * This type is represented here by a placeholder interfaces. The implementation of this type
+ * is found only in Saxon-EE
+ */
+
+public interface FunctionItemType extends ItemType {
+
+ /**
+ * Get the argument types of the function
+ * @return the argument types, as an array of SequenceTypes; or null if this is the generic function type
+ * function(*)
+ */
+
+ /*@Nullable*/ public SequenceType[] getArgumentTypes();
+
+ /**
+ * Get the result type of the function
+ * @return the result type, as a SequenceType
+ */
+
+ public SequenceType getResultType();
+
+ /**
+ * Determine the relationship of one function item type to another
+ * @return for example {@link TypeHierarchy#SUBSUMES}, {@link TypeHierarchy#SAME_TYPE}
+ */
+
+ public int relationship(FunctionItemType other, TypeHierarchy th);
+
+ /**
+ * Create an expression whose effect is to apply function coercion to coerce a function to this function type
+ * @param exp the expression that delivers the supplied sequence of function items (the ones in need of coercion)
+ * @param role information for use in diagnostics
+ * @param visitor the expression visitor, supplies context information
+ * @return the coerced function, a function that calls the original function after checking the parameters
+ */
+
+ public Expression makeFunctionSequenceCoercer(Expression exp, RoleLocator role, TypeCheckerEnvironment visitor)
+ throws XPathException;
+
+ /**
+ * Ask whether this function item type is a map type. In this case function coercion (to the map type)
+ * will never succeed.
+ * @return true if this FunctionItemType is a map type
+ */
+
+ public boolean isMapType();
+}
+
diff --git a/sf/saxon/type/ISchemaCompiler.java b/sf/saxon/type/ISchemaCompiler.java
new file mode 100644
index 0000000..d8abde6
--- /dev/null
+++ b/sf/saxon/type/ISchemaCompiler.java
@@ -0,0 +1,15 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+/**
+ * Marker interface: the only instance of this class is the SchemaCompiler object in Saxon-EE
+ */
+public interface ISchemaCompiler {
+}
+
diff --git a/sf/saxon/type/ItemType.java b/sf/saxon/type/ItemType.java
new file mode 100644
index 0000000..84fba87
--- /dev/null
+++ b/sf/saxon/type/ItemType.java
@@ -0,0 +1,149 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.trans.XPathException;
+
+import java.io.Serializable;
+
+
+/**
+ * ItemType is an interface that allows testing of whether an Item conforms to an
+ * expected type. ItemType represents the types in the type hierarchy in the XPath model,
+ * as distinct from the schema model: an item type is either item() (matches everything),
+ * a node type (matches nodes), an atomic type (matches atomic values), or empty()
+ * (matches nothing). Atomic types, represented by the class AtomicType, are also
+ * instances of SimpleType in the schema type hierarchy. Node Types, represented by
+ * the class NodeTest, are also Patterns as used in XSLT.
+ *
+ * <p>Saxon assumes that apart from {@link AnyItemType} (which corresponds to <code>item()</item>
+ * and matches anything), every ItemType will be either an {@link AtomicType}, a {@link net.sf.saxon.pattern.NodeTest},
+ * or a {@link FunctionItemType}. User-defined implementations of ItemType must therefore extend one of those
+ * three classes/interfaces.</p>
+ * @see AtomicType
+ * @see net.sf.saxon.pattern.NodeTest
+ * @see FunctionItemType
+*/
+
+public interface ItemType extends Serializable {
+
+ /**
+ * Determine whether this item type is an atomic type
+ * @return true if this is ANY_ATOMIC_TYPE or a subtype thereof
+ */
+
+ public boolean isAtomicType();
+
+ /**
+ * Determine whether this item type is a plain type (that is, whether it can ONLY match
+ * atomic values)
+ * @return true if this is ANY_ATOMIC_TYPE or a subtype thereof, or a
+ * "plain" union type (that is, unions of atomic types that impose no further restrictions)
+ */
+
+ public boolean isPlainType();
+
+ /**
+ * Test whether a given item conforms to this type
+ * @param item The item to be tested
+ * @param context the XPath dynamic evaluation context
+ * @return true if the item is an instance of this type; false otherwise
+ */
+
+ public boolean matches(Item item, XPathContext context);
+
+
+ /**
+ * Test whether a given item conforms to this type
+ * @param item The item to be tested
+ * @param allowURIPromotion if a URI value is to be treated as a string
+ * @param config the Saxon configuration
+ * @return true if the item is an instance of this type; false otherwise
+ */
+
+ public boolean matchesItem(Item item, boolean allowURIPromotion, Configuration config);
+
+ /**
+ * Get the type from which this item type is derived by restriction. This
+ * is the supertype in the XPath type heirarchy, as distinct from the Schema
+ * base type: this means that the supertype of xs:boolean is xs:anyAtomicType,
+ * whose supertype is item() (rather than xs:anySimpleType).
+ * <p>
+ * In fact the concept of "supertype" is not really well-defined, because the types
+ * form a lattice rather than a hierarchy. The only real requirement on this function
+ * is that it returns a type that strictly subsumes this type, ideally as narrowly
+ * as possible.
+ * @return the supertype, or null if this type is item()
+ * @param th the type hierarchy cache
+ */
+
+ /*@Nullable*/ public ItemType getSuperType(TypeHierarchy th);
+
+ /**
+ * Get the primitive item type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue and union types it is Type.ATOMIC_VALUE. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that integer, xs:dayTimeDuration, and xs:yearMonthDuration
+ * are considered to be primitive types.
+ * @return the corresponding primitive type
+ */
+
+ /*@NotNull*/
+ public ItemType getPrimitiveItemType();
+
+ /**
+ * Get the primitive type corresponding to this item type. For item(),
+ * this is Type.ITEM. For node(), it is Type.NODE. For specific node kinds,
+ * it is the value representing the node kind, for example Type.ELEMENT.
+ * For anyAtomicValue it is BuiltInAtomicType.ANY_ATOMIC. For numeric it is Type.NUMBER.
+ * For other atomic types it is the primitive type as defined in XML Schema,
+ * except that INTEGER is considered to be a primitive type.
+ * @return the integer fingerprint of the corresponding primitive type
+ */
+
+ public int getPrimitiveType();
+
+ /**
+ * Determine the default priority of this item type when used on its own as a Pattern
+ * @return the default priority
+ */
+
+ public abstract double getDefaultPriority();
+
+ /**
+ * Get the item type of the atomic values that will be produced when an item
+ * of this type is atomized
+ * @return the best available item type of the atomic values that will be produced when an item
+ * of this type is atomized, or null if it is known that atomization will throw an error.
+ */
+
+ public PlainType getAtomizedItemType();
+
+ /**
+ * Ask whether values of this type are atomizable
+ * @return true unless it is known that these items will be elements with element-only
+ * content, or function items, in which case return false
+ */
+
+ public boolean isAtomizable();
+
+ /**
+ * Visit all the schema components used in this ItemType definition
+ * @param visitor the visitor class to be called when each component is visited
+ * @throws net.sf.saxon.trans.XPathException if an error occurs
+ */
+
+ public void visitNamedSchemaComponents(SchemaComponentVisitor visitor) throws XPathException;
+
+}
+
diff --git a/sf/saxon/type/ListType.java b/sf/saxon/type/ListType.java
new file mode 100644
index 0000000..9feacf9
--- /dev/null
+++ b/sf/saxon/type/ListType.java
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+/**
+ * Interface representing a simple type of variety List
+ */
+
+public interface ListType extends SimpleType {
+
+ /**
+ * Returns the simpleType of the items in this ListType. This method assumes that the
+ * item type has been fully resolved
+ * @return the simpleType of the items in this ListType.
+ * @throws IllegalStateException if the item type has not been fully resolved
+ */
+
+ /*@NotNull*/
+ SimpleType getItemType();
+}
diff --git a/sf/saxon/type/PlainType.java b/sf/saxon/type/PlainType.java
new file mode 100644
index 0000000..4b8aa0f
--- /dev/null
+++ b/sf/saxon/type/PlainType.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import java.util.Set;
+
+/**
+ * A "plain type" is either an atomic type, or a union type that (a) imposes no restrictions other
+ * than those imposed by its member types, and (b) has exclusively plain types as its member types
+ */
+
+public interface PlainType extends ItemType {
+
+ public boolean isExternalType();
+
+ /**
+ * Get the list of plain types that are subsumed by this type
+ * @return for an atomic type, the type itself; for a plain union type, the list of plain types
+ * in its transitive membership
+ */
+
+ public Set<PlainType> getPlainMemberTypes();
+}
+
diff --git a/sf/saxon/type/SchemaComponent.java b/sf/saxon/type/SchemaComponent.java
new file mode 100644
index 0000000..996aba9
--- /dev/null
+++ b/sf/saxon/type/SchemaComponent.java
@@ -0,0 +1,97 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import com.saxonica.functions.hof.SpecificFunctionType;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.value.SequenceType;
+
+import java.io.Serializable;
+
+/**
+ * This is a marker interface that represents any "schema component" as defined in the XML Schema
+ * specification. This may be a user-defined schema component or a built-in schema component. Since
+ * all built-in schema components are types, every SchemaComponent in practice is either a
+ * {@link com.saxonica.schema.UserSchemaComponent} or a {@link SchemaType} or both.
+ */
+public interface SchemaComponent extends Serializable {
+
+ /**
+ * Get the validation status of this component.
+ * @return one of the values {@link #UNVALIDATED}, {@link #VALIDATING},
+ * {@link #VALIDATED}, {@link #INVALID}, {@link #INCOMPLETE}
+ */
+
+ public int getValidationStatus();
+
+ /**
+ * Validation status: not yet validated
+ */
+ public static final int UNVALIDATED = 0;
+
+ /**
+ * Validation status: fixed up (all references to other components have been resolved)
+ */
+ public static final int FIXED_UP = 1;
+
+ /**
+ * Validation status: currently being validated
+ */
+ public static final int VALIDATING = 2;
+
+ /**
+ * Validation status: successfully validated
+ */
+ public static final int VALIDATED = 3;
+
+ /**
+ * Validation status: validation attempted and failed with fatal errors
+ */
+ public static final int INVALID = 4;
+
+ /**
+ * Validation status: validation attempted, component contains references to
+ * other components that are not (yet) available
+ */
+ public static final int INCOMPLETE = 5;
+
+ /**
+ * Get the redefinition level. This is zero for a component that has not been redefined;
+ * for a redefinition of a level-0 component, it is 1; for a redefinition of a level-N
+ * component, it is N+1. This concept is used to support the notion of "pervasive" redefinition:
+ * if a component is redefined at several levels, the top level wins, but it is an error to have
+ * two versions of the component at the same redefinition level.
+ * @return the redefinition level
+ */
+
+ public int getRedefinitionLevel();
+
+//#ifdefined SCHEMA
+ /**
+ * Get the schema component in the form of a function item. This allows schema information
+ * to be made visible to XSLT or XQuery code. The function makes available the contents of the
+ * schema component as defined in the XSD specification. The function takes a string as argument
+ * representing a property name, and returns the corresponding property of the schema component.
+ * There is also a property "class" which returns the kind of schema component, for example
+ * "Attribute Declaration".
+ * @return the schema component represented as a function from property names to property values.
+ */
+
+ public FunctionItem getComponentAsFunction();
+
+
+ /**
+ * The function type of the function returned by getComponentAsFunction()
+ */
+
+ public final static FunctionItemType COMPONENT_FUNCTION_TYPE =
+ new SpecificFunctionType(new SequenceType[]{SequenceType.SINGLE_STRING}, SequenceType.ANY_SEQUENCE);
+//#endif
+
+}
+
diff --git a/sf/saxon/type/SchemaComponentVisitor.java b/sf/saxon/type/SchemaComponentVisitor.java
new file mode 100644
index 0000000..34f8b67
--- /dev/null
+++ b/sf/saxon/type/SchemaComponentVisitor.java
@@ -0,0 +1,20 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * Interface for a general purpose visitor object used to process schema components
+ */
+
+public interface SchemaComponentVisitor {
+
+ public void visitSchemaComponent(SchemaComponent component) throws XPathException;
+}
+
diff --git a/sf/saxon/type/SchemaDeclaration.java b/sf/saxon/type/SchemaDeclaration.java
new file mode 100644
index 0000000..f610852
--- /dev/null
+++ b/sf/saxon/type/SchemaDeclaration.java
@@ -0,0 +1,54 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.pattern.NodeTest;
+
+/**
+ * This is a marker interface that acts as a surrogate for an object representing
+ * a global element or attribute declaration.
+ * The real implementation of these declarations is available in the schema-aware
+ * version of the Saxon product.
+ */
+public interface SchemaDeclaration {
+
+ /**
+ * Get the name of the schema component
+ * @return the fingerprint of the component name
+ */
+
+ public int getFingerprint();
+
+ /**
+ * Get the simple or complex type associated with the element or attribute declaration
+ * @return the simple or complex type
+ */
+
+ public SchemaType getType();
+
+ /**
+ * Create a NodeTest that implements the semantics of schema-element(name) or
+ * schema-attribute(name) applied to this element or attribute declaration.
+ */
+
+ public NodeTest makeSchemaNodeTest();
+
+ /**
+ * Determine, in the case of an Element Declaration, whether it is nillable.
+ */
+
+ public boolean isNillable();
+
+ /**
+ * Determine, in the case of an Element Declaration, whether the declaration is abstract
+ */
+
+ public boolean isAbstract();
+
+}
+
diff --git a/sf/saxon/type/SchemaException.java b/sf/saxon/type/SchemaException.java
new file mode 100644
index 0000000..77d738c
--- /dev/null
+++ b/sf/saxon/type/SchemaException.java
@@ -0,0 +1,68 @@
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.TransformerConfigurationException;
+
+/**
+ * An exception that identifies an error in reading, parsing, or
+ * validating a schema.
+ */
+
+public class SchemaException extends TransformerConfigurationException {
+
+ /**
+ * Creates a new XMLException with no message
+ * or nested Exception.
+ */
+
+ public SchemaException() {
+ super();
+ }
+
+ public SchemaException(String message, SourceLocator locator) {
+ super(message, locator);
+ }
+
+ /**
+ * Creates a new XMLException with the given message.
+ *
+ * @param message the message for this Exception
+ */
+
+ public SchemaException(String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new XMLException with the given nested
+ * exception.
+ *
+ * @param exception the nested exception
+ */
+
+ public SchemaException(Throwable exception) {
+ super(exception);
+ }
+
+ /**
+ * Creates a new XMLException with the given message
+ * and nested exception.
+ *
+ * @param message the detail message for this exception
+ * @param exception the nested exception
+ */
+
+ public SchemaException(String message, Throwable exception) {
+ super(message, exception);
+ }
+
+}
+
diff --git a/sf/saxon/type/SchemaType.java b/sf/saxon/type/SchemaType.java
new file mode 100644
index 0000000..8396c4b
--- /dev/null
+++ b/sf/saxon/type/SchemaType.java
@@ -0,0 +1,296 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+
+/**
+ * SchemaType is an interface implemented by all schema types: simple and complex types, built-in and
+ * user-defined types.
+ *
+ * <p>There is a hierarchy of interfaces that extend SchemaType, representing the top levels of the schema
+ * type system: SimpleType and ComplexType, with SimpleType further subdivided into List, Union, and Atomic
+ * types.</p>
+ *
+ * <p>The implementations of these interfaces are organized into a different hierarchy: on the one side,
+ * built-in types such as AnyType, AnySimpleType, and the built-in atomic types and list types; on the other
+ * side, user-defined types defined in a schema.</p>
+ */
+
+public interface SchemaType extends SchemaComponent {
+
+ // DerivationMethods. These constants are copied from org.w3.dom.TypeInfo. They are redefined here to avoid
+ // creating a dependency on the TypeInfo class, which is only available when JAXP 1.3 is available.
+
+ /**
+ * If the document's schema is an XML Schema [<a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/'>XML Schema Part 1</a>]
+ * , this constant represents the derivation by <a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/#key-typeRestriction'>
+ * restriction</a> if complex types are involved, or a <a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/#element-restriction'>
+ * restriction</a> if simple types are involved.
+ * <br> The reference type definition is derived by restriction from the
+ * other type definition if the other type definition is the same as the
+ * reference type definition, or if the other type definition can be
+ * reached recursively following the {base type definition} property
+ * from the reference type definition, and all the <em>derivation methods</em> involved are restriction.
+ */
+ public static final int DERIVATION_RESTRICTION = 0x00000001;
+ /**
+ * If the document's schema is an XML Schema [<a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/'>XML Schema Part 1</a>]
+ * , this constant represents the derivation by <a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/#key-typeExtension'>
+ * extension</a>.
+ * <br> The reference type definition is derived by extension from the
+ * other type definition if the other type definition can be reached
+ * recursively following the {base type definition} property from the
+ * reference type definition, and at least one of the <em>derivation methods</em> involved is an extension.
+ */
+ public static final int DERIVATION_EXTENSION = 0x00000002;
+ /**
+ * If the document's schema is an XML Schema [<a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/'>XML Schema Part 1</a>]
+ * , this constant represents the <a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/#element-union'>
+ * union</a> if simple types are involved.
+ * <br> The reference type definition is derived by union from the other
+ * type definition if there exists two type definitions T1 and T2 such
+ * as the reference type definition is derived from T1 by
+ * <code>DERIVATION_RESTRICTION</code> or
+ * <code>DERIVATION_EXTENSION</code>, T2 is derived from the other type
+ * definition by <code>DERIVATION_RESTRICTION</code>, T1 has {variety} <em>union</em>, and one of the {member type definitions} is T2. Note that T1 could be
+ * the same as the reference type definition, and T2 could be the same
+ * as the other type definition.
+ */
+ public static final int DERIVATION_UNION = 0x00000004;
+ /**
+ * If the document's schema is an XML Schema [<a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/'>XML Schema Part 1</a>]
+ * , this constant represents the <a href='http://www.w3.org/TR/2001/REC-xmlschema-1-20010502/#element-list'>list</a>.
+ * <br> The reference type definition is derived by list from the other
+ * type definition if there exists two type definitions T1 and T2 such
+ * as the reference type definition is derived from T1 by
+ * <code>DERIVATION_RESTRICTION</code> or
+ * <code>DERIVATION_EXTENSION</code>, T2 is derived from the other type
+ * definition by <code>DERIVATION_RESTRICTION</code>, T1 has {variety} <em>list</em>, and T2 is the {item type definition}. Note that T1 could be the same as
+ * the reference type definition, and T2 could be the same as the other
+ * type definition.
+ */
+ public static final int DERIVATION_LIST = 0x00000008;
+
+ /**
+ * Derivation by substitution.
+ * This constant, unlike the others, is NOT defined in the DOM level 3 TypeInfo interface.
+ */
+
+ public static final int DERIVE_BY_SUBSTITUTION = 16;
+
+ /**
+ * Get the local name of this type
+ * @return the local name of this type definition, if it has one. Return null in the case of an
+ * anonymous type.
+ */
+
+ /*@Nullable*/ public String getName();
+
+ /**
+ * Get the target namespace of this type
+ * @return the target namespace of this type definition, if it has one. Return null in the case
+ * of an anonymous type, and in the case of a global type defined in a no-namespace schema.
+ */
+
+ /*@Nullable*/ public String getTargetNamespace();
+
+ /**
+ * Get the namecode of the name of this type. This includes the prefix from the original
+ * type declaration: in the case of built-in types, there may be a conventional prefix
+ * or there may be no prefix.
+ * @return the namecode. Returns an invented namecode for an anonymous type.
+ */
+
+ int getNameCode();
+
+ /**
+ * Get the fingerprint of the name of this type
+ * @return the fingerprint. Returns an invented fingerprint for an anonymous type.
+ */
+
+ int getFingerprint();
+
+ /**
+ * Get the display name of the type: that is, a lexical QName with an arbitrary prefix
+ * @return a lexical QName identifying the type. In the case of an anonymous type, an internally-generated
+ * name is returned
+ */
+
+ String getDisplayName();
+
+ /**
+ * Get the name of this type as an EQName, that is, a string in the format Q{uri}local.
+ * @return an EQName identifying the type. In the case of an anonymous type, an internally-generated
+ * name is returned
+ */
+
+ String getEQName();
+
+ /**
+ * Test whether this SchemaType is a complex type
+ * @return true if this SchemaType is a complex type
+ */
+
+ boolean isComplexType();
+
+ /**
+ * Test whether this SchemaType is a simple type
+ * @return true if this SchemaType is a simple type
+ */
+
+ boolean isSimpleType();
+
+ /**
+ * Test whether this SchemaType is an atomic type
+ * @return true if this SchemaType is an atomic type
+ */
+
+ boolean isAtomicType();
+
+ /**
+ * Test whether this is an anonymous type
+ * @return true if this SchemaType is an anonymous type
+ */
+
+ boolean isAnonymousType();
+
+ /**
+ * Returns the value of the 'block' attribute for this type, as a bit-significant
+ * integer with fields such as {@link SchemaType#DERIVATION_LIST} and {@link SchemaType#DERIVATION_EXTENSION}.
+ * This corresponds to the property "prohibited substitutions" in the schema component model.
+ * @return the value of the 'block' attribute for this type
+ */
+
+ int getBlock();
+
+ /**
+ * Returns the base type that this type inherits from. This method can be used to get the
+ * base type of a type that is known to be valid.
+ * If this type is a Simpletype that is a built in primitive type then null is returned.
+ * @return the base type, or null if this is xs:anyType (the root of the type hierarchy)
+ * @throws IllegalStateException if this type is not valid.
+ * @throws UnresolvedReferenceException if the reference from this type to its base
+ * type cannot be resolved; this will generally make the schema unusable, but only
+ * if the type is actually used.
+ */
+
+ /*@Nullable*/ SchemaType getBaseType() throws UnresolvedReferenceException;
+
+ /**
+ * Gets the integer code of the derivation method used to derive this type from its
+ * parent. Returns zero for primitive types.
+ * @return a numeric code representing the derivation method, for example {@link SchemaType#DERIVATION_RESTRICTION}
+ */
+
+ int getDerivationMethod();
+
+ /**
+ * Get the types of derivation that are not permitted, by virtue of the "final" property.
+ * @return the types of derivation that are not permitted, as a bit-significant integer
+ * containing bits such as {@link SchemaType#DERIVATION_EXTENSION}
+ */
+
+ int getFinalProhibitions();
+
+ /**
+ * Determines whether derivation (of a particular kind)
+ * from this type is allowed, based on the "final" property
+ * @param derivation the kind of derivation, for example {@link SchemaType#DERIVATION_LIST}
+ * @return true if this kind of derivation is allowed
+ */
+
+ boolean allowsDerivation(int derivation);
+
+ /**
+ * Analyze an XPath expression to see whether the expression is capable of delivering a value of this
+ * type. This method is called during static analysis of a query or stylesheet to give compile-time
+ * warnings when "impossible" paths are used.
+ * @param expression the expression that delivers the content
+ * @param kind the node kind whose content is being delivered: {@link Type#ELEMENT},
+ * {@link Type#ATTRIBUTE}, or {@link Type#DOCUMENT}
+ * @param env The static evaluation context for the query or stylesheet
+ * @throws XPathException if the expression will never deliver a value of the correct type
+ */
+
+ void analyzeContentExpression(Expression expression, int kind, StaticContext env) throws XPathException;
+
+ /**
+ * Get the typed value of a node that is annotated with this schema type.
+ * @param node the node whose typed value is required
+ * @return the typed value.
+ * @throws net.sf.saxon.trans.XPathException if the node cannot be atomized, for example if this is a complex type
+ * with element-only content
+ * @since 8.5. Changed in 9.5 to return the new type AtomicSequence
+ */
+
+ AtomicSequence atomize(NodeInfo node) throws XPathException;
+
+ /**
+ * Test whether this is the same type as another type. They are considered to be the same type
+ * if they are derived from the same type definition in the original XML representation (which
+ * can happen when there are multiple includes of the same file)
+ * @param other the other type
+ * @return true if this is the same type as other
+ */
+
+ boolean isSameType(SchemaType other);
+
+ /**
+ * Get a description of this type for use in error messages. This is the same as the display name
+ * in the case of named types; for anonymous types it identifies the type by its position in a source
+ * schema document.
+ * @return text identifing the type, for use in a phrase such as "the type XXXX".
+ */
+
+ String getDescription();
+
+ /**
+ * Check that this type is validly derived from a given type, following the rules for the Schema Component
+ * Constraint "Is Type Derivation OK (Simple)" (3.14.6) or "Is Type Derivation OK (Complex)" (3.4.6) as
+ * appropriate.
+ * @param base the base type; the algorithm tests whether derivation from this type is permitted
+ * @param block the derivations that are blocked by the relevant element declaration
+ * @throws SchemaException if the derivation is not allowed
+ */
+
+ void checkTypeDerivationIsOK(SchemaType base, int block) throws SchemaException;
+
+ /**
+ * Get the URI of the schema document where the type was originally defined.
+ * @return the URI of the schema document. Returns null if the information is unknown or if this
+ * is a built-in type
+ */
+
+ /*@Nullable*/ public String getSystemId();
+
+ /**
+ * Ask whether this type is an ID type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:ID: that is, it includes types derived
+ * from ID by restriction, list, or union. Note that for a node to be treated
+ * as an ID in XSD 1.0, its typed value must be a *single* atomic value of type ID; the type of the
+ * node, however, can still allow a list. But in XSD 1.1, a list of IDs is permitted
+ * @return true if this type is an ID type
+ */
+
+ public boolean isIdType();
+
+ /**
+ * Ask whether this type is an IDREF or IDREFS type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:IDREF: that is, it includes types derived
+ * from IDREF or IDREFS by restriction, list, or union
+ * @return true if this type is an IDREF type
+ */
+
+ public boolean isIdRefType();
+
+}
\ No newline at end of file
diff --git a/sf/saxon/type/SimpleType.java b/sf/saxon/type/SimpleType.java
new file mode 100644
index 0000000..8ca1ab0
--- /dev/null
+++ b/sf/saxon/type/SimpleType.java
@@ -0,0 +1,140 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.NamespaceResolver;
+
+/**
+ * This interface represents a simple type, which may be a built-in simple type, or
+ * a user-defined simple type.
+ */
+
+public interface SimpleType extends SchemaType {
+
+ /**
+ * Test whether this Simple Type is an atomic type
+ * @return true if this is an atomic type
+ */
+
+ boolean isAtomicType();
+
+ /**
+ * Test whether this Simple Type is a list type
+ * @return true if this is a list type
+ */
+ boolean isListType();
+
+ /**
+ * Test whether this Simple Type is a union type
+ * @return true if this is a union type
+ */
+
+ boolean isUnionType();
+
+ /**
+ * Return true if this is an external object type, that is, a Saxon-defined type for external
+ * Java or .NET objects
+ * @return true if this is an external type
+ */
+
+ boolean isExternalType();
+
+ /**
+ * Determine whether this is a built-in type or a user-defined type
+ * @return true if this is a built-in type
+ */
+
+ boolean isBuiltInType();
+
+ /**
+ * Get the built-in type from which this type is derived by restriction
+ * @return the built-in type from which this type is derived by restriction. This will not necessarily
+ * be a primitive type.
+ */
+
+ /*@Nullable*/ SchemaType getBuiltInBaseType();
+
+ /**
+ * Get the typed value corresponding to a given string value, assuming it is
+ * valid against this type
+ *
+ * @param value the string value
+ * @param resolver a namespace resolver used to resolve any namespace prefixes appearing
+ * in the content of values. Can supply null, in which case any namespace-sensitive content
+ * will be rejected.
+ * @param rules the conversion rules from the configuration
+ * @return an iterator over the atomic sequence comprising the typed value. The objects
+ * returned by this SequenceIterator will all be of type {@link net.sf.saxon.value.AtomicValue},
+ * The next() method on the iterator throws no checked exceptions, although it is not actually
+ * declared as an UnfailingIterator.
+ * @throws ValidationException if the supplied value is not in the lexical space of the data type
+ */
+
+ public AtomicSequence getTypedValue(CharSequence value, /*@Nullable*/ NamespaceResolver resolver, ConversionRules rules)
+ throws ValidationException;
+
+ /**
+ * Check whether a given input string is valid according to this SimpleType
+ * @param value the input string to be checked
+ * @param nsResolver a namespace resolver used to resolve namespace prefixes if the type
+ * is namespace sensitive. The value supplied may be null; in this case any namespace-sensitive
+ * content will throw an UnsupportedOperationException.
+ * @param rules the conversion rules from the configuration
+ * @return null if validation succeeds; or return a ValidationFailure describing the validation failure
+ * if validation fails. Note that the exception is returned rather than being thrown.
+ * @throws UnsupportedOperationException if the type is namespace-sensitive and no namespace
+ * resolver is supplied
+ */
+
+ /*@Nullable*/
+ ValidationFailure validateContent(
+ /*@NotNull*/ CharSequence value, /*@Nullable*/ NamespaceResolver nsResolver, /*@NotNull*/ ConversionRules rules);
+
+ /**
+ * Test whether this type is namespace sensitive, that is, if a namespace context is needed
+ * to translate between the lexical space and the value space. This is true for types derived
+ * from, or containing, QNames and NOTATIONs
+ * @return true if the type is namespace-sensitive
+ */
+
+ boolean isNamespaceSensitive();
+
+ /**
+ * Determine how values of this simple type are whitespace-normalized.
+ * @return one of {@link net.sf.saxon.value.Whitespace#PRESERVE}, {@link net.sf.saxon.value.Whitespace#COLLAPSE},
+ * {@link net.sf.saxon.value.Whitespace#REPLACE}.
+ */
+
+ public int getWhitespaceAction();
+
+ /**
+ * Apply any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess
+ * @param input the value to be preprocessed
+ * @return the value after preprocessing
+ * @throws ValidationException if preprocessing detects that the value is invalid
+ */
+
+ public CharSequence preprocess(CharSequence input) throws ValidationException;
+
+ /**
+ * Reverse any pre-lexical facets, other than whitespace. At the moment the only such
+ * facet is saxon:preprocess. This is called when converting a value of this type to
+ * a string
+ * @param input the value to be postprocessed: this is the "ordinary" result of converting
+ * the value to a string
+ * @return the value after postprocessing
+ * @throws ValidationException if postprocessing detects that the value is invalid
+ */
+
+ public CharSequence postprocess(CharSequence input) throws ValidationException;
+
+}
+
diff --git a/sf/saxon/type/StringConverter.java b/sf/saxon/type/StringConverter.java
new file mode 100644
index 0000000..b15e5c1
--- /dev/null
+++ b/sf/saxon/type/StringConverter.java
@@ -0,0 +1,926 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.QNameException;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+
+import java.util.regex.Pattern;
+
+/**
+ * A {@link Converter} that accepts a string as input. This subclass of Converter is provided
+ * to avoid having to wrap the string into a StringValue prior to conversion. Every Converter whose
+ * source type is xs:string must be an instance of this subclass.
+ *
+ * <p>The input to a StringConverter can also be an xs:untypedAtomic value, since the conversion semantics
+ * are always the same as from a string.</p>
+ *
+ * <p>A StringConverter also provides a method to validate that a string is valid against the target type,
+ * without actually performing the conversion.</p>
+ */
+public abstract class StringConverter extends Converter {
+
+ // Constants are defined only for converters that are independent of the conversion rules
+
+ /*@NotNull*/ public final static StringToString
+ STRING_TO_STRING = new StringToString();
+ /*@NotNull*/ public final static StringToLanguage
+ STRING_TO_LANGUAGE = new StringToLanguage();
+ /*@NotNull*/ public final static StringToNormalizedString
+ STRING_TO_NORMALIZED_STRING = new StringToNormalizedString();
+ /*@NotNull*/ public final static StringToToken
+ STRING_TO_TOKEN = new StringToToken();
+ /*@NotNull*/ public final static StringToDecimal
+ STRING_TO_DECIMAL = new StringToDecimal();
+ /*@NotNull*/ public final static StringToInteger
+ STRING_TO_INTEGER = new StringToInteger();
+ /*@NotNull*/ public final static StringToDuration
+ STRING_TO_DURATION = new StringToDuration();
+ /*@NotNull*/ public final static StringToDayTimeDuration
+ STRING_TO_DAY_TIME_DURATION = new StringToDayTimeDuration();
+ /*@NotNull*/ public final static StringToYearMonthDuration
+ STRING_TO_YEAR_MONTH_DURATION = new StringToYearMonthDuration();
+ /*@NotNull*/ public final static StringToTime
+ STRING_TO_TIME = new StringToTime();
+ /*@NotNull*/ public final static StringToBoolean
+ STRING_TO_BOOLEAN = new StringToBoolean();
+ /*@NotNull*/ public final static StringToHexBinary
+ STRING_TO_HEX_BINARY = new StringToHexBinary();
+ /*@NotNull*/ public final static StringToBase64BinaryConverter
+ STRING_TO_BASE64_BINARY = new StringToBase64BinaryConverter();
+ /*@NotNull*/ public final static StringToUntypedAtomic
+ STRING_TO_UNTYPED_ATOMIC = new StringToUntypedAtomic();
+
+ /**
+ * Create a StringConverter
+ */
+
+ protected StringConverter(){}
+
+ /**
+ * Create a StringConverter
+ * @param rules the conversion rules to be applied
+ */
+
+ protected StringConverter(ConversionRules rules) {
+ super(rules);
+ }
+
+ /**
+ * Convert a string to the target type of this converter.
+ * @param input the string to be converted
+ * @return either an {@link net.sf.saxon.value.AtomicValue} of the appropriate type for this converter (if conversion
+ * succeeded), or a {@link ValidationFailure} if conversion failed.
+ */
+
+ /*@NotNull*/
+ public abstract ConversionResult convertString(/*@NotNull*/ CharSequence input);
+
+ /**
+ * Validate a string for conformance to the target type, without actually performing
+ * the conversion
+ * @param input the string to be validated
+ * @return null if validation is successful, or a ValidationFailure indicating the reasons for failure
+ * if unsuccessful
+ */
+
+ /*@Nullable*/
+ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ ConversionResult result = convertString(input);
+ return (result instanceof ValidationFailure ? (ValidationFailure)result : null);
+ }
+
+ /*@NotNull*/
+ @Override
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return convertString(input.getStringValueCS());
+ }
+
+ /**
+ * Converter from string to a derived type (derived from a type other than xs:string),
+ * where the derived type needs to retain the original
+ * string for validating against lexical facets such as pattern.
+ */
+
+ public static class StringToNonStringDerivedType extends StringConverter {
+ private StringConverter phaseOne;
+ private DownCastingConverter phaseTwo;
+
+ public StringToNonStringDerivedType(StringConverter phaseOne, DownCastingConverter phaseTwo) {
+ this.phaseOne = phaseOne;
+ this.phaseTwo = phaseTwo;
+ }
+
+ @Override
+ public void setNamespaceResolver(NamespaceResolver resolver) {
+ phaseOne.setNamespaceResolver(resolver);
+ phaseTwo.setNamespaceResolver(resolver);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ CharSequence in = input.getStringValueCS();
+ try {
+ in = phaseTwo.getTargetType().preprocess(in);
+ } catch (ValidationException err) {
+ return new ValidationFailure(err);
+ }
+ ConversionResult temp = phaseOne.convertString(in);
+ if (temp instanceof ValidationFailure) {
+ return temp;
+ }
+ return phaseTwo.convert((AtomicValue) temp, in);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ try {
+ input = phaseTwo.getTargetType().preprocess(input);
+ } catch (ValidationException err) {
+ return new ValidationFailure(err);
+ }
+ ConversionResult temp = phaseOne.convertString(input);
+ if (temp instanceof ValidationFailure) {
+ return temp;
+ }
+ return phaseTwo.convert((AtomicValue) temp, input);
+ }
+ }
+
+ /**
+ * Converts from xs:string or xs:untypedAtomic to xs:String
+ */
+
+ public static class StringToString extends StringConverter {
+ /*@NotNull*/@Override
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new StringValue(input.getStringValueCS());
+ }
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return new StringValue(input);
+ }
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ return null;
+ }
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+/**
+ * Converts from xs:string or xs:untypedAtomic to xs:untypedAtomic
+ */
+
+ public static class StringToUntypedAtomic extends StringConverter {
+ /*@NotNull*/@Override
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return new UntypedAtomicValue(input.getStringValueCS());
+ }
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return new UntypedAtomicValue(input);
+ }
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ return null;
+ }
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+
+ /**
+ * Converts from xs:string to xs:normalizedString
+ */
+
+ public static class StringToNormalizedString extends StringConverter {
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return new StringValue(Whitespace.normalizeWhitespace(input), BuiltInAtomicType.NORMALIZED_STRING);
+ }
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ return null;
+ }
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts from xs:string to xs:token
+ */
+
+ public static class StringToToken extends StringConverter {
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return new StringValue(Whitespace.collapseWhitespace(input), BuiltInAtomicType.TOKEN);
+ }
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ return null;
+ }
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+ }
+
+ /**
+ * Converts from xs:string to xs:language
+ */
+
+ public static class StringToLanguage extends StringConverter {
+ private final static Pattern regex = Pattern.compile("[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*");
+ // See erratum E2-25 to XML Schema Part 2.
+
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ CharSequence trimmed = Whitespace.trimWhitespace(input);
+ if (!regex.matcher(trimmed).matches()) {
+ return new ValidationFailure("The value '" + input + "' is not a valid xs:language");
+ }
+ return new StringValue(trimmed, BuiltInAtomicType.LANGUAGE);
+ }
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ if (regex.matcher(Whitespace.trimWhitespace(input)).matches()) {
+ return null;
+ } else {
+ return new ValidationFailure("The value '" + input + "' is not a valid xs:language");
+ }
+ }
+ }
+
+ /**
+ * Converts from xs:string to xs:NCName, xs:ID, xs:IDREF, or xs:ENTITY
+ */
+
+ public static class StringToNCName extends StringConverter {
+ NameChecker checker;
+ AtomicType targetType;
+
+ public StringToNCName(/*@NotNull*/ ConversionRules rules, AtomicType targetType) {
+ super(rules);
+ checker = rules.getNameChecker();
+ this.targetType = targetType;
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ CharSequence trimmed = Whitespace.trimWhitespace(input);
+ if (checker.isValidNCName(trimmed)) {
+ return new StringValue(trimmed, targetType);
+ } else {
+ return new ValidationFailure("The value '" + input + "' is not a valid " + targetType.getDisplayName());
+ }
+ }
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ if (checker.isValidNCName(Whitespace.trimWhitespace(input))) {
+ return null;
+ } else {
+ return new ValidationFailure("The value '" + input + "' is not a valid "+ targetType.getDisplayName());
+ }
+ }
+ }
+
+ /**
+ * Converts from xs:string to xs:NMTOKEN
+ */
+
+ public static class StringToNMTOKEN extends StringConverter {
+ NameChecker checker;
+
+ public StringToNMTOKEN(/*@NotNull*/ ConversionRules rules) {
+ super(rules);
+ checker = rules.getNameChecker();
+ }
+
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ CharSequence trimmed = Whitespace.trimWhitespace(input);
+ if (checker.isValidNmtoken(trimmed)) {
+ return new StringValue(trimmed, BuiltInAtomicType.NMTOKEN);
+ } else {
+ return new ValidationFailure("The value '" + input + "' is not a valid xs:NMTOKEN");
+ }
+ }
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ if (checker.isValidNmtoken(Whitespace.trimWhitespace(input))) {
+ return null;
+ } else {
+ return new ValidationFailure("The value '" + input + "' is not a valid xs:NMTOKEN");
+ }
+ }
+ }
+
+
+ /**
+ * Converts from xs:string to xs:Name
+ */
+
+ public static class StringToName extends StringToNCName {
+
+ private final static Pattern colon = Pattern.compile(":");
+
+ public StringToName(/*@NotNull*/ ConversionRules rules) {
+ super(rules, BuiltInAtomicType.NAME);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ ValidationFailure vf = validate(input);
+ if (vf == null) {
+ return new StringValue(Whitespace.trimWhitespace(input), BuiltInAtomicType.NAME);
+ } else {
+ return vf;
+ }
+ }
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ // if it's valid as an NCName then it's OK
+ CharSequence trimmed = Whitespace.trimWhitespace(input);
+ if (checker.isValidNCName(trimmed)) {
+ return null;
+ }
+
+ // if not, replace any colons by underscores and then test if it's a valid NCName
+ FastStringBuffer buff = new FastStringBuffer(trimmed.length());
+ buff.append(trimmed);
+ for (int i = 0; i < buff.length(); i++) {
+ if (buff.charAt(i) == ':') {
+ buff.setCharAt(i, '_');
+ }
+ }
+ if (checker.isValidNCName(buff)) {
+ return null;
+ } else {
+ return new ValidationFailure("The value '" + trimmed + "' is not a valid xs:Name");
+ }
+ }
+ }
+
+ /**
+ * Converts from xs:string to a user-defined type derived directly from xs:string
+ */
+
+ public static class StringToStringSubtype extends StringConverter {
+ AtomicType targetType;
+ int whitespaceAction;
+
+ public StringToStringSubtype(ConversionRules rules, /*@NotNull*/ AtomicType targetType) {
+ super(rules);
+ this.targetType = targetType;
+ this.whitespaceAction = targetType.getWhitespaceAction();
+ }
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ CharSequence cs = Whitespace.applyWhitespaceNormalization(whitespaceAction, input);
+ try {
+ cs = targetType.preprocess(cs);
+ } catch (ValidationException err) {
+ return new ValidationFailure(err);
+ }
+ StringValue sv = new StringValue(cs);
+ ValidationFailure f = targetType.validate(sv, cs, getConversionRules());
+ if (f == null) {
+ sv.setTypeLabel(targetType);
+ return sv;
+ } else {
+ return f;
+ }
+ }
+
+ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ CharSequence cs = Whitespace.applyWhitespaceNormalization(whitespaceAction, input);
+ return targetType.validate(new StringValue(cs), cs, getConversionRules());
+ }
+ }
+
+ /**
+ * Converts from xs;string to a user-defined type derived from a built-in subtype of xs:string
+ */
+
+ public static class StringToDerivedStringSubtype extends StringConverter {
+ AtomicType targetType;
+ StringConverter builtInValidator;
+ int whitespaceAction;
+
+ public StringToDerivedStringSubtype(/*@NotNull*/ ConversionRules rules, /*@NotNull*/ AtomicType targetType) {
+ super(rules);
+ this.targetType = targetType;
+ this.whitespaceAction = targetType.getWhitespaceAction();
+ builtInValidator = rules.getStringConverter((AtomicType) targetType.getBuiltInBaseType());
+ }
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ CharSequence cs = Whitespace.applyWhitespaceNormalization(whitespaceAction, input);
+ ValidationFailure f = builtInValidator.validate(cs);
+ if (f != null) {
+ return f;
+ }
+ try {
+ cs = targetType.preprocess(cs);
+ } catch (ValidationException err) {
+ return new ValidationFailure(err);
+ }
+ StringValue sv = new StringValue(cs);
+ f = targetType.validate(sv, cs, getConversionRules());
+ if (f == null) {
+ sv.setTypeLabel(targetType);
+ return sv;
+ } else {
+ return f;
+ }
+ }
+ }
+
+
+ /**
+ * Converts a string to xs:float
+ */
+
+ public static class StringToFloat extends StringConverter {
+ public StringToFloat(ConversionRules rules) {
+ super(rules);
+ }
+
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ try {
+ float flt = (float) getConversionRules().getStringToDoubleConverter().stringToNumber(input);
+ return new FloatValue(flt);
+ } catch (NumberFormatException err) {
+ ValidationFailure ve = new ValidationFailure("Cannot convert string to float: " + input.toString());
+ ve.setErrorCode("FORG0001");
+ return ve;
+ }
+ }
+ }
+
+ /**
+ * Converts a string to a double. The rules change in XSD 1.1 to permit "+INF"
+ */
+
+ public static class StringToDouble extends StringConverter {
+ net.sf.saxon.type.StringToDouble worker;
+ public StringToDouble(/*@NotNull*/ ConversionRules rules) {
+ super(rules);
+ worker = rules.getStringToDoubleConverter();
+ }
+
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ try {
+ double dble = worker.stringToNumber(input);
+ return new DoubleValue(dble);
+ } catch (NumberFormatException err) {
+ return new ValidationFailure("Cannot convert string to double: " + Err.wrap(input.toString(), Err.VALUE));
+ }
+ }
+ }
+
+ /**
+ * Converts a string to an xs:decimal
+ */
+
+ public static class StringToDecimal extends StringConverter {
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return DecimalValue.makeDecimalValue(input, true);
+ }
+
+ /*@Nullable*/ @Override
+ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ if (DecimalValue.castableAsDecimal(input)) {
+ return null;
+ } else {
+ return new ValidationFailure("Cannot convert string to decimal: " + input.toString());
+ }
+ }
+ }
+
+ /**
+ * Converts a string to an integer
+ */
+
+ public static class StringToInteger extends StringConverter {
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return IntegerValue.stringToInteger(input.getStringValueCS());
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return IntegerValue.stringToInteger(input);
+ }
+
+ @Override
+ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ return IntegerValue.castableAsInteger(input);
+ }
+ }
+
+ /**
+ * Converts a string to a built-in subtype of integer
+ */
+
+ public static class StringToIntegerSubtype extends StringConverter {
+
+ BuiltInAtomicType targetType;
+
+ public StringToIntegerSubtype(BuiltInAtomicType targetType) {
+ this.targetType = targetType;
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ ConversionResult iv = IntegerValue.stringToInteger(input);
+ if (iv instanceof Int64Value) {
+ boolean ok = IntegerValue.checkRange(((Int64Value)iv).longValue(), targetType);
+ if (ok) {
+ return ((Int64Value)iv).copyAsSubType(targetType);
+ } else {
+ return new ValidationFailure("Integer value is out of range for type " + targetType.toString());
+ }
+ } else if (iv instanceof BigIntegerValue) {
+ boolean ok = IntegerValue.checkBigRange(((BigIntegerValue)iv).asBigInteger(), targetType);
+ if (ok) {
+ ((BigIntegerValue)iv).setTypeLabel(targetType);
+ return iv;
+ } else {
+ return new ValidationFailure("Integer value is out of range for type " + targetType.toString());
+ }
+ } else {
+ assert (iv instanceof ValidationFailure);
+ return iv;
+ }
+ }
+ }
+
+ /**
+ * Converts a string to a duration
+ */
+
+ public static class StringToDuration extends StringConverter {
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return DurationValue.makeDuration(input);
+ }
+ }
+
+ /**
+ * Converts a string to a dayTimeDuration
+ */
+
+
+ public static class StringToDayTimeDuration extends StringConverter {
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return DayTimeDurationValue.makeDayTimeDurationValue(input);
+ }
+ }
+
+ /**
+ * Converts a string to a yearMonthDuration
+ */
+
+ public static class StringToYearMonthDuration extends StringConverter {
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return YearMonthDurationValue.makeYearMonthDurationValue(input);
+ }
+ }
+
+ /**
+ * Converts a string to a dateTime
+ */
+
+ public static class StringToDateTime extends StringConverter {
+ public StringToDateTime(ConversionRules rules) {
+ super(rules);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return DateTimeValue.makeDateTimeValue(input, getConversionRules());
+ }
+ }
+
+ /**
+ * Converts a string to a date
+ */
+
+ public static class StringToDate extends StringConverter{
+ public StringToDate(ConversionRules rules) {
+ super(rules);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return DateValue.makeDateValue(input, getConversionRules());
+ }
+ }
+
+ /**
+ * Converts a string to a gMonth
+ */
+
+ public static class StringToGMonth extends StringConverter {
+ public StringToGMonth(ConversionRules rules) {
+ super(rules);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return GMonthValue.makeGMonthValue(input, getConversionRules());
+ }
+ }
+
+ /**
+ * Converts a string to a gYearMonth
+ */
+
+ public static class StringToGYearMonth extends StringConverter {
+ public StringToGYearMonth(ConversionRules rules) {
+ super(rules);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return GYearMonthValue.makeGYearMonthValue(input, getConversionRules());
+ }
+ }
+
+ /**
+ * Converts a string to a gYear
+ */
+
+ public static class StringToGYear extends StringConverter {
+ public StringToGYear(ConversionRules rules) {
+ super(rules);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return GYearValue.makeGYearValue(input, getConversionRules());
+ }
+ }
+
+ /**
+ * Converts a string to a gMonthDay
+ */
+
+ public static class StringToGMonthDay extends StringConverter {
+ public StringToGMonthDay(ConversionRules rules) {
+ super(rules);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return GMonthDayValue.makeGMonthDayValue(input, getConversionRules());
+ }
+ }
+
+ /**
+ * Converts a string to a gDay
+ */
+
+ public static class StringToGDayConverter extends StringConverter {
+ public StringToGDayConverter(ConversionRules rules) {
+ super(rules);
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return GDayValue.makeGDayValue(input, getConversionRules());
+ }
+ }
+
+ /**
+ * Converts a string to a time
+ */
+
+ public static class StringToTime extends StringConverter {
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return TimeValue.makeTimeValue(input);
+ }
+ }
+
+ /**
+ * Converts a string to a boolean
+ */
+
+ public static class StringToBoolean extends StringConverter {
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return BooleanValue.fromString(input);
+ }
+ }
+
+ /**
+ * Converts a string to hexBinary
+ */
+
+ public static class StringToHexBinary extends StringConverter {
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ try {
+ return new HexBinaryValue(input);
+ } catch (XPathException e) {
+ return new ValidationFailure(e);
+ }
+ }
+ }
+
+ /**
+ * Converts String to QName
+ */
+
+ public static class StringToQName extends StringConverter {
+
+ NamespaceResolver nsResolver;
+
+ public StringToQName(ConversionRules rules) {
+ super(rules);
+ }
+
+ @Override
+ public boolean isXPath30Conversion() {
+ return true;
+ }
+
+ public void setNamespaceResolver(NamespaceResolver resolver) {
+ this.nsResolver = resolver;
+ }
+
+ @Override
+ public NamespaceResolver getNamespaceResolver() {
+ return nsResolver;
+ }
+
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ if (nsResolver == null) {
+ throw new UnsupportedOperationException("Cannot validate a QName without a namespace resolver");
+ }
+ try {
+ NameChecker nameChecker = getConversionRules().getNameChecker();
+ String[] parts = nameChecker.getQNameParts(Whitespace.trimWhitespace(input));
+ String uri = nsResolver.getURIForPrefix(parts[0], true);
+ if (uri == null) {
+ return new ValidationFailure("Namespace prefix " + Err.wrap(parts[0]) + " has not been declared");
+ }
+// if (fingerprint == StandardNames.XS_NOTATION) {
+// // This check added in 9.3. The XSLT spec says that this check should not be performed during
+// // validation. However, this appears to be based on an incorrect assumption: see spec bug 6952
+// if (!rules.isDeclaredNotation(uri, parts[1])) {
+// return new ValidationFailure("Notation {" + uri + "}" + parts[1] + " is not declared in the schema");
+// }
+// }
+ return new QNameValue(parts[0], uri, parts[1], BuiltInAtomicType.QNAME, nameChecker);
+ } catch (QNameException err) {
+ return new ValidationFailure("Invalid lexical QName " + Err.wrap(input));
+ } catch (XPathException err) {
+ return new ValidationFailure(err.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Converts String to NOTATION
+ */
+
+ public static class StringToNotation extends StringConverter {
+
+ NamespaceResolver nsResolver;
+
+ public StringToNotation(ConversionRules rules) {
+ super(rules);
+ }
+
+ @Override
+ public void setNamespaceResolver(NamespaceResolver resolver) {
+ nsResolver = resolver;
+ }
+
+ @Override
+ public NamespaceResolver getNamespaceResolver() {
+ return nsResolver;
+ }
+
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ if (getNamespaceResolver() == null) {
+ throw new UnsupportedOperationException("Cannot validate a NOTATION without a namespace resolver");
+ }
+ try {
+ NameChecker nameChecker = getConversionRules().getNameChecker();
+ String[] parts = nameChecker.getQNameParts(Whitespace.trimWhitespace(input));
+ String uri = getNamespaceResolver().getURIForPrefix(parts[0], true);
+ if (uri == null) {
+ return new ValidationFailure("Namespace prefix " + Err.wrap(parts[0]) + " has not been declared");
+ }
+ // This check added in 9.3. The XSLT spec says that this check should not be performed during
+ // validation. However, this appears to be based on an incorrect assumption: see spec bug 6952
+ if (!getConversionRules().isDeclaredNotation(uri, parts[1])) {
+ return new ValidationFailure("Notation {" + uri + "}" + parts[1] + " is not declared in the schema");
+ }
+ return new NotationValue(parts[0], uri, parts[1], nameChecker);
+ } catch (QNameException err) {
+ return new ValidationFailure("Invalid lexical QName " + Err.wrap(input));
+ } catch (XPathException err) {
+ return new ValidationFailure(err.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Converts string to anyURI
+ */
+
+ public static class StringToAnyURI extends StringConverter {
+ public StringToAnyURI(ConversionRules rules) {
+ super(rules);
+ }
+
+ /*@NotNull*/ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ if (getConversionRules().isValidURI(input)) {
+ return new AnyURIValue(input);
+ } else {
+ return new ValidationFailure("Invalid URI: " + input.toString());
+ }
+ }
+
+ /*@Nullable*/@Override
+ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ if (getConversionRules().isValidURI(input)) {
+ return null;
+ } else {
+ return new ValidationFailure("Invalid URI: " + input.toString());
+ }
+ }
+ }
+
+ /**
+ * Converter that does nothing - it returns the input unchanged
+ */
+
+ public static class IdentityConverter extends StringConverter {
+ /*@NotNull*/ public static IdentityConverter THE_INSTANCE = new IdentityConverter();
+
+ /*@NotNull*/
+ public ConversionResult convert(/*@NotNull*/ AtomicValue input) {
+ return input;
+ }
+
+ /*@NotNull*/
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ return StringValue.makeStringValue(input);
+ }
+
+ public boolean isAlwaysSuccessful() {
+ return true;
+ }
+
+ /*@Nullable*/ public ValidationFailure validate(/*@NotNull*/ CharSequence input) {
+ return null;
+ }
+ }
+
+ /**
+ * Converter from string to plain union types
+ */
+
+ public static class StringToUnionConverter extends StringConverter {
+
+ SimpleType targetType;
+ ConversionRules rules;
+
+ public StringToUnionConverter(PlainType targetType, ConversionRules rules) {
+ if (!targetType.isPlainType()) {
+ throw new IllegalArgumentException();
+ }
+ if (((SimpleType)targetType).isNamespaceSensitive()) {
+ throw new IllegalArgumentException();
+ }
+ this.targetType = (SimpleType)targetType;
+ this.rules = rules;
+ }
+ /**
+ * Convert a string to the target type of this converter.
+ *
+ * @param input the string to be converted
+ * @return either an {@link net.sf.saxon.value.AtomicValue} of the appropriate type for this converter (if conversion
+ * succeeded), or a {@link net.sf.saxon.type.ValidationFailure} if conversion failed.
+ */
+ /*@NotNull*/
+ @Override
+ public ConversionResult convertString(/*@NotNull*/ CharSequence input) {
+ try {
+ return targetType.getTypedValue(input, null, rules).head();
+ } catch (XPathException err) {
+ return new ValidationFailure(ValidationException.makeXPathException(err));
+ }
+ }
+ }
+}
+
diff --git a/sf/saxon/type/StringToDouble.java b/sf/saxon/type/StringToDouble.java
new file mode 100644
index 0000000..172341c
--- /dev/null
+++ b/sf/saxon/type/StringToDouble.java
@@ -0,0 +1,158 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.value.Whitespace;
+
+import java.io.Serializable;
+
+/**
+ * This class converts a string to an xs:double according to the rules in XML Schema 1.0
+ */
+public class StringToDouble implements Serializable {
+
+ private static StringToDouble THE_INSTANCE = new StringToDouble();
+
+ /**
+ * Get the singleton instance
+ * @return the singleton instance of this class
+ */
+
+ public static StringToDouble getInstance() {
+ return THE_INSTANCE;
+ }
+
+ protected StringToDouble() {}
+
+ /**
+ * Convert a string to a double.
+ * @param s the String to be converted
+ * @return a double representing the value of the String
+ * @throws NumberFormatException if the value cannot be converted
+ */
+
+ public double stringToNumber(CharSequence s) throws NumberFormatException {
+ // first try to parse simple numbers by hand (it's cheaper)
+ int len = s.length();
+ boolean containsDisallowedChars = false;
+ boolean containsWhitespace = false;
+ if (len < 9) {
+ boolean useJava = false;
+ long num = 0;
+ int dot = -1;
+ int lastDigit = -1;
+ boolean onlySpaceAllowed = false;
+ for (int i=0; i<len; i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\r':
+ containsWhitespace = true;
+ if (lastDigit != -1) {
+ onlySpaceAllowed = true;
+ }
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (onlySpaceAllowed) {
+ throw new NumberFormatException("Numeric value contains embedded whitespace");
+ }
+ lastDigit = i;
+ num = num*10 + (c - '0');
+ break;
+ case '.':
+ if (onlySpaceAllowed) {
+ throw new NumberFormatException("Numeric value contains embedded whitespace");
+ }
+ if (dot != -1) {
+ throw new NumberFormatException("Only one decimal point allowed");
+ }
+ dot = i;
+ break;
+ case 'x':
+ case 'X':
+ case 'f':
+ case 'F':
+ case 'd':
+ case 'D':
+ case 'n':
+ case 'N':
+ containsDisallowedChars = true;
+ useJava = true;
+ break;
+ default:
+ // there's something like a sign or an exponent: take the slow train instead
+ useJava = true;
+ break;
+ }
+ }
+ if (!useJava) {
+ if (lastDigit == -1) {
+ throw new NumberFormatException("No digits found");
+ } else if (dot == -1 || dot > lastDigit) {
+ return (double)num;
+ } else {
+ int afterPoint = lastDigit - dot;
+ return ((double)num)/powers[afterPoint];
+ }
+ }
+ } else {
+ loop2: for (int i=0; i<len; i++) {
+ char c = s.charAt(i);
+ switch (c) {
+ case ' ':
+ case '\n':
+ case '\t':
+ case '\r':
+ containsWhitespace = true;
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '.':
+ case 'e':
+ case 'E':
+ case '+':
+ case '-':
+ break;
+ default:
+ containsDisallowedChars = true;
+ break loop2;
+ }
+ }
+ }
+ String n = (containsWhitespace ? Whitespace.trimWhitespace(s).toString() : s.toString());
+ if ("INF".equals(n)) {
+ return Double.POSITIVE_INFINITY;
+ } else if ("+INF".equals(n)) {
+ // Allowed in XSD 1.1 but not in XSD 1.0
+ return signedPositiveInfinity();
+ } else if ("-INF".equals(n)) {
+ return Double.NEGATIVE_INFINITY;
+ } else if ("NaN".equals(n)) {
+ return Double.NaN;
+ } else {
+ // reject strings containing characters such as (x, f, d) allowed in Java but not in XPath,
+ // and other representations of NaN and Infinity such as 'Infinity'
+ if (containsDisallowedChars) {
+ throw new NumberFormatException("invalid floating point value: " + s);
+ }
+ return Double.parseDouble(n);
+ }
+ }
+
+ protected double signedPositiveInfinity() {
+ throw new NumberFormatException("the float/double value '+INF' is not allowed under XSD 1.0");
+ }
+
+ /*@NotNull*/ private static double[] powers = new double[]{1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
+
+
+}
+
diff --git a/sf/saxon/type/Type.java b/sf/saxon/type/Type.java
new file mode 100644
index 0000000..4d3dcc7
--- /dev/null
+++ b/sf/saxon/type/Type.java
@@ -0,0 +1,590 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.tree.iter.AxisIterator;
+import net.sf.saxon.value.AtomicValue;
+import net.sf.saxon.value.ObjectValue;
+
+import java.io.Serializable;
+
+
+/**
+ * This class contains static information about types and methods for constructing type codes.
+ * The class is never instantiated.
+ * <p/>
+ * <p><i>The constant integers used for type names in earlier versions of this class have been replaced
+ * by constants in {@link StandardNames}. The constants representing {@link AtomicType} objects are now
+ * available through the {@link BuiltInAtomicType} class.</i></p>
+ */
+
+public abstract class Type implements Serializable {
+
+ // Note that the integer codes representing node kinds are the same as
+ // the codes allocated in the DOM interface, while the codes for built-in
+ // atomic types are fingerprints allocated in StandardNames. These two sets of
+ // codes must not overlap!
+
+ /**
+ * Type representing an element node - element()
+ */
+
+ public static final short ELEMENT = 1;
+ /**
+ * Item type representing an attribute node - attribute()
+ */
+ public static final short ATTRIBUTE = 2;
+ /**
+ * Item type representing a text node - text()
+ */
+ public static final short TEXT = 3;
+ /**
+ * Item type representing a text node stored in the tiny tree as compressed whitespace
+ */
+ public static final short WHITESPACE_TEXT = 4;
+ /**
+ * Item type representing a processing-instruction node
+ */
+ public static final short PROCESSING_INSTRUCTION = 7;
+ /**
+ * Item type representing a comment node
+ */
+ public static final short COMMENT = 8;
+ /**
+ * Item type representing a document node
+ */
+ public static final short DOCUMENT = 9;
+ /**
+ * Item type representing a namespace node
+ */
+ public static final short NAMESPACE = 13;
+ /**
+ * Dummy node kind used in the tiny tree to mark the end of the tree
+ */
+ public static final short STOPPER = 11;
+ /**
+ * Dummy node kind used in the tiny tree to contain a parent pointer
+ */
+ public static final short PARENT_POINTER = 12;
+
+ /**
+ * An item type that matches any node
+ */
+
+ public static final short NODE = 0;
+
+ public static final ItemType NODE_TYPE = AnyNodeTest.getInstance();
+
+ /**
+ * An item type that matches any item
+ */
+
+ public static final short ITEM = 88;
+
+ /*@NotNull*/ public static final ItemType ITEM_TYPE = AnyItemType.getInstance();
+
+ /**
+ * A type number for function()
+ */
+
+ public static final short FUNCTION = 99;
+
+ public static final short MAX_NODE_TYPE = 13;
+ /**
+ * Item type that matches no items (corresponds to SequenceType empty())
+ */
+ public static final short EMPTY = 15; // a test for this type will never be satisfied
+
+ private Type() {
+ }
+
+ /**
+ * Test whether a given type is (some subtype of) node()
+ *
+ * @param type The type to be tested
+ * @return true if the item type is node() or a subtype of node()
+ */
+
+ public static boolean isNodeType(ItemType type) {
+ return type instanceof NodeTest;
+ }
+
+ /**
+ * Get the ItemType of an Item
+ *
+ * @param item the item whose type is required
+ * @param th the type hierarchy cache. If null, the returned type may be less precise
+ * @return the item type of the item
+ */
+
+ /*@NotNull*/
+ public static ItemType getItemType(/*@NotNull*/ Item item, /*@Nullable*/ TypeHierarchy th) {
+ if (item == null) {
+ return AnyItemType.getInstance();
+ } else if (item instanceof AtomicValue) {
+ return ((AtomicValue) item).getItemType();
+ } else if (item instanceof NodeInfo) {
+ NodeInfo node = ((NodeInfo)item);
+ if (th == null) {
+ th = node.getConfiguration().getTypeHierarchy();
+ }
+ switch (node.getNodeKind()) {
+ case Type.DOCUMENT:
+ // Need to know whether the document is well-formed and if so what the element type is
+ AxisIterator iter = node.iterateAxis(AxisInfo.CHILD);
+ ItemType elementType = null;
+ while (true) {
+ NodeInfo n = iter.next();
+ if (n==null) {
+ break;
+ }
+ int kind = n.getNodeKind();
+ if (kind==Type.TEXT) {
+ elementType = null;
+ break;
+ } else if (kind==Type.ELEMENT) {
+ if (elementType != null) {
+ elementType = null;
+ break;
+ }
+ elementType = Type.getItemType(n, th);
+ }
+ }
+ if (elementType == null) {
+ return NodeKindTest.DOCUMENT;
+ } else {
+ return new DocumentNodeTest((NodeTest)elementType);
+ }
+
+ case Type.ELEMENT:
+ SchemaType eltype = node.getSchemaType();
+ if (eltype.equals(Untyped.getInstance()) || eltype.equals(AnyType.getInstance())) {
+ return new NameTest(Type.ELEMENT, node.getFingerprint(), node.getNamePool());
+ } else {
+ return new CombinedNodeTest(
+ new NameTest(Type.ELEMENT, node.getFingerprint(), node.getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ELEMENT, eltype, node.getConfiguration(), false));
+ }
+
+ case Type.ATTRIBUTE:
+ SchemaType attype = node.getSchemaType();
+ if (attype.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ return new NameTest(Type.ATTRIBUTE, node.getFingerprint(), node.getNamePool());
+ } else {
+ return new CombinedNodeTest(
+ new NameTest(Type.ATTRIBUTE, node.getFingerprint(), node.getNamePool()),
+ Token.INTERSECT,
+ new ContentTypeTest(Type.ATTRIBUTE, attype, node.getConfiguration(), false));
+ }
+
+ case Type.TEXT:
+ return NodeKindTest.TEXT;
+
+ case Type.COMMENT:
+ return NodeKindTest.COMMENT;
+
+ case Type.PROCESSING_INSTRUCTION:
+ return NodeKindTest.PROCESSING_INSTRUCTION;
+
+ case Type.NAMESPACE:
+ return NodeKindTest.NAMESPACE;
+
+ default:
+ throw new IllegalArgumentException("Unknown node kind " + node.getNodeKind());
+ }
+ } else if (item instanceof ObjectValue) {
+ return ((ObjectValue) item).getItemType(th);
+ } else { //if (item instanceof FunctionItem) {
+ return ((FunctionItem) item).getFunctionItemType(th);
+ }
+ }
+
+
+ /**
+ * Output (for diagnostics) a representation of the type of an item. This
+ * does not have to be the most specific type
+ *
+ * @param item the item whose type is to be displayed
+ * @return a string representation of the type of the item
+ */
+
+ public static String displayTypeName(/*@NotNull*/ Item item) {
+ if (item instanceof NodeInfo) {
+ NodeInfo node = (NodeInfo) item;
+ switch (node.getNodeKind()) {
+ case DOCUMENT:
+ return "document-node()";
+ case ELEMENT:
+ SchemaType annotation = node.getSchemaType();
+ return "element(" +
+ ((NodeInfo) item).getDisplayName() + ", " +
+ (annotation.getDisplayName() + ')');
+ case ATTRIBUTE:
+ SchemaType annotation2 = node.getSchemaType();
+ return "attribute(" +
+ ((NodeInfo) item).getDisplayName() + ", " +
+ annotation2.getDisplayName() + ')';
+ case TEXT:
+ return "text()";
+ case COMMENT:
+ return "comment()";
+ case PROCESSING_INSTRUCTION:
+ return "processing-instruction()";
+ case NAMESPACE:
+ return "namespace()";
+ default:
+ return "";
+ }
+ } else if (item instanceof ObjectValue) {
+ return ((ObjectValue) item).displayTypeName();
+ } else if (item instanceof AtomicValue) {
+ return ((AtomicValue) item).getItemType().toString();
+ } else if (item instanceof FunctionItem) {
+ return "function(*)";
+ } else {
+ return item.getClass().toString();
+ }
+ }
+
+ /**
+ * Get the ItemType object for a built-in atomic type
+ *
+ * @param namespace the namespace URI of the type
+ * @param localName the local name of the type
+ * @return the ItemType, or null if not found
+ */
+
+ /*@Nullable*/
+ public static ItemType getBuiltInItemType(String namespace, String localName) {
+ SchemaType t = BuiltInType.getSchemaType(
+ StandardNames.getFingerprint(namespace, localName));
+ if (t instanceof ItemType) {
+ return (ItemType) t;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the SimpleType object for a built-in simple type (atomic type or list type)
+ *
+ * @param namespace the namespace URI of the type
+ * @param localName the local name of the type
+ * @return the SimpleType, or null if not found
+ */
+
+ /*@Nullable*/
+ public static SimpleType getBuiltInSimpleType(String namespace, String localName) {
+ SchemaType t = BuiltInType.getSchemaType(
+ StandardNames.getFingerprint(namespace, localName));
+ if (t instanceof SimpleType && ((SimpleType) t).isBuiltInType()) {
+ return (SimpleType) t;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get a type that is a common supertype of two given item types
+ *
+ * @param t1 the first item type
+ * @param t2 the second item type
+ * @param th the type hierarchy cache
+ * @return the item type that is a supertype of both
+ * the supplied item types
+ */
+
+ /*@NotNull*/
+ public static ItemType getCommonSuperType(/*@NotNull*/ ItemType t1, /*@NotNull*/ ItemType t2, /*@NotNull*/ TypeHierarchy th) {
+ if (t1 == t2) {
+ return t1;
+ }
+ if (t1 instanceof ErrorType) {
+ return t2;
+ }
+ if (t2 instanceof ErrorType) {
+ return t1;
+ }
+ if (t1 instanceof ExternalObjectType && t2 instanceof ExternalObjectType) {
+ Class c1 = ((ExternalObjectType) t1).getJavaClass();
+ Class c2 = ((ExternalObjectType) t2).getJavaClass();
+ return new ExternalObjectType(leastCommonSuperClass(c1, c2));
+ }
+ int r = th.relationship(t1, t2);
+ if (r == TypeHierarchy.SAME_TYPE) {
+ return t1;
+ } else if (r == TypeHierarchy.SUBSUMED_BY) {
+ return t2;
+ } else if (r == TypeHierarchy.SUBSUMES) {
+ return t1;
+ } else {
+ ItemType st = t2.getSuperType(th);
+ if (st == null) {
+ return AnyItemType.getInstance();
+ } else {
+ return getCommonSuperType(st, t1, th);
+ }
+ // eventually we will hit a type that is a supertype of t2. We reverse
+ // the arguments so we go up each branch of the tree alternately.
+ // If we hit the root of the tree, one of the earlier conditions will be satisfied,
+ // so the recursion will stop.
+ }
+ }
+
+ /**
+ * Get a type that is a common supertype of two given item types, without the benefit of a TypeHierarchy cache.
+ * This will generally give a less precise answer than the method {@link #getCommonSuperType(ItemType, ItemType, TypeHierarchy)}
+ *
+ * @param t1 the first item type
+ * @param t2 the second item type
+ * @return an item type that is a supertype of both the supplied item types
+ */
+
+ /*@NotNull*/
+ public static ItemType getCommonSuperType(/*@NotNull*/ ItemType t1, /*@NotNull*/ ItemType t2) {
+ if (t1 == t2) {
+ return t1;
+ }
+ if (t1 instanceof ErrorType) {
+ return t2;
+ }
+ if (t2 instanceof ErrorType) {
+ return t1;
+ }
+ if (t1 == AnyItemType.getInstance() || t2 == AnyItemType.getInstance()) {
+ return AnyItemType.getInstance();
+ }
+ ItemType p1 = t1.getPrimitiveItemType();
+ ItemType p2 = t2.getPrimitiveItemType();
+ if (p1 == p2) {
+ return p1;
+ }
+ if ((p1 == BuiltInAtomicType.DECIMAL && p2 == BuiltInAtomicType.INTEGER) ||
+ (p2 == BuiltInAtomicType.DECIMAL && p1 == BuiltInAtomicType.INTEGER)) {
+ return BuiltInAtomicType.DECIMAL;
+ }
+ if (p1 instanceof BuiltInAtomicType && ((BuiltInAtomicType)p1).isNumericType() &&
+ p2 instanceof BuiltInAtomicType && ((BuiltInAtomicType)p2).isNumericType()) {
+ return BuiltInAtomicType.NUMERIC;
+ }
+ if (t1.isAtomicType() && t2.isAtomicType()) {
+ return BuiltInAtomicType.ANY_ATOMIC;
+ }
+ if (t1 instanceof NodeTest && t2 instanceof NodeTest) {
+ return AnyNodeTest.getInstance();
+ }
+ if (t1 instanceof ExternalObjectType && t2 instanceof ExternalObjectType) {
+ Class c1 = ((ExternalObjectType) t1).getJavaClass();
+ Class c2 = ((ExternalObjectType) t2).getJavaClass();
+ return new ExternalObjectType(leastCommonSuperClass(c1, c2));
+ }
+ return AnyItemType.getInstance();
+
+ // Note: for function items, the result is always AnyFunctionType, since all functions have the same primitive type
+
+ }
+
+ private static Class leastCommonSuperClass(Class class1, Class class2) {
+ if (class1 == class2) {
+ return class1;
+ }
+ if (class1 == null || class2 == null) {
+ return null;
+ }
+ if (!class1.isArray() && class1.isAssignableFrom(class2)) {
+ return class1;
+ }
+ if (!class2.isArray() && class2.isAssignableFrom(class1)) {
+ return class2;
+ }
+ if (class1.isInterface() || class2.isInterface()) {
+ return Object.class;
+ }
+ return leastCommonSuperClass(class1.getSuperclass(), class2.getSuperclass());
+ }
+
+
+ /**
+ * Determine whether this type is a primitive type. The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration;
+ * xs:untypedAtomic; the 7 node kinds; and all supertypes of these (item(), node(), xs:anyAtomicType,
+ * xs:numeric, ...)
+ *
+ * @param code the item type code to be tested
+ * @return true if the type is considered primitive under the above rules
+ */
+ public static boolean isPrimitiveType(int code) {
+ return code >= 0 && (code <= StandardNames.XS_INTEGER ||
+ code == StandardNames.XS_NUMERIC ||
+ code == StandardNames.XS_UNTYPED_ATOMIC ||
+ code == StandardNames.XS_ANY_ATOMIC_TYPE ||
+ code == StandardNames.XS_DAY_TIME_DURATION ||
+ code == StandardNames.XS_YEAR_MONTH_DURATION ||
+ code == StandardNames.XS_ANY_SIMPLE_TYPE);
+ }
+
+ /**
+ * Determine whether two primitive atomic types are comparable under the rules for ValueComparisons
+ * (that is, untyped atomic values treated as strings)
+ *
+ * @param t1 the first type to compared.
+ * This must be a primitive atomic type as defined by {@link ItemType#getPrimitiveType}
+ * @param t2 the second type to compared.
+ * This must be a primitive atomic type as defined by {@link ItemType#getPrimitiveType}
+ * @param ordered true if testing for an ordering comparison (lt, gt, le, ge). False
+ * if testing for an equality comparison (eq, ne)
+ * @return true if the types are comparable, as defined by the rules of the "eq" operator; false if they
+ * are not comparable, or if we don't yet know (because some subtypes of the static type are comparable
+ * and others are not)
+ */
+
+ public static boolean isGuaranteedComparable(/*@NotNull*/ BuiltInAtomicType t1, /*@NotNull*/ BuiltInAtomicType t2, boolean ordered) {
+ if (t1 == t2) {
+ return true; // short cut
+ }
+// if (t1.equals(BuiltInAtomicType.ANY_ATOMIC) || t2.equals(BuiltInAtomicType.ANY_ATOMIC)) {
+// return true; // meaning we don't actually know at this stage
+// }
+ if (t1.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ t1 = BuiltInAtomicType.STRING;
+ }
+ if (t2.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ t2 = BuiltInAtomicType.STRING;
+ }
+ if (t1.equals(BuiltInAtomicType.ANY_URI)) {
+ t1 = BuiltInAtomicType.STRING;
+ }
+ if (t2.equals(BuiltInAtomicType.ANY_URI)) {
+ t2 = BuiltInAtomicType.STRING;
+ }
+ if (t1.isPrimitiveNumeric()) {
+ t1 = BuiltInAtomicType.NUMERIC;
+ }
+ if (t2.isPrimitiveNumeric()) {
+ t2 = BuiltInAtomicType.NUMERIC;
+ }
+ if (!ordered) {
+ if (t1.equals(BuiltInAtomicType.DAY_TIME_DURATION)) {
+ t1 = BuiltInAtomicType.DURATION;
+ }
+ if (t2.equals(BuiltInAtomicType.DAY_TIME_DURATION)) {
+ t2 = BuiltInAtomicType.DURATION;
+ }
+ if (t1.equals(BuiltInAtomicType.YEAR_MONTH_DURATION)) {
+ t1 = BuiltInAtomicType.DURATION;
+ }
+ if (t2.equals(BuiltInAtomicType.YEAR_MONTH_DURATION)) {
+ t2 = BuiltInAtomicType.DURATION;
+ }
+ }
+ return t1 == t2;
+ }
+
+ /**
+ * Determine whether two primitive atomic types are comparable under the rules for ValueComparisons
+ * (that is, untyped atomic values treated as strings)
+ *
+ * @param t1 the first type to compared.
+ * This must be a primitive atomic type as defined by {@link ItemType#getPrimitiveType}
+ * @param t2 the second type to compared.
+ * This must be a primitive atomic type as defined by {@link ItemType#getPrimitiveType}
+ * @param ordered true if testing for an ordering comparison (lt, gt, le, ge). False
+ * if testing for an equality comparison (eq, ne)
+ * @return true if the types are guaranteed comparable, as defined by the rules of the "eq" operator,
+ * or if we don't yet know (because some subtypes of the static type are comparable
+ * and others are not). False if they are definitely not comparable.
+ */
+
+ public static boolean isPossiblyComparable(/*@NotNull*/ BuiltInAtomicType t1, /*@NotNull*/ BuiltInAtomicType t2, boolean ordered) {
+ if (t1 == t2) {
+ return true; // short cut
+ }
+ if (t1.equals(BuiltInAtomicType.ANY_ATOMIC) || t2.equals(BuiltInAtomicType.ANY_ATOMIC)) {
+ return true; // meaning we don't actually know at this stage
+ }
+ if (t1.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ t1 = BuiltInAtomicType.STRING;
+ }
+ if (t2.equals(BuiltInAtomicType.UNTYPED_ATOMIC)) {
+ t2 = BuiltInAtomicType.STRING;
+ }
+ if (t1.equals(BuiltInAtomicType.ANY_URI)) {
+ t1 = BuiltInAtomicType.STRING;
+ }
+ if (t2.equals(BuiltInAtomicType.ANY_URI)) {
+ t2 = BuiltInAtomicType.STRING;
+ }
+ if (t1.isPrimitiveNumeric()) {
+ t1 = BuiltInAtomicType.NUMERIC;
+ }
+ if (t2.isPrimitiveNumeric()) {
+ t2 = BuiltInAtomicType.NUMERIC;
+ }
+ if (t1.equals(BuiltInAtomicType.DAY_TIME_DURATION)) {
+ t1 = BuiltInAtomicType.DURATION;
+ }
+ if (t2.equals(BuiltInAtomicType.DAY_TIME_DURATION)) {
+ t2 = BuiltInAtomicType.DURATION;
+ }
+ if (t1.equals(BuiltInAtomicType.YEAR_MONTH_DURATION)) {
+ t1 = BuiltInAtomicType.DURATION;
+ }
+ if (t2.equals(BuiltInAtomicType.YEAR_MONTH_DURATION)) {
+ t2 = BuiltInAtomicType.DURATION;
+ }
+ return t1 == t2;
+ }
+
+
+ /**
+ * Determine whether two primitive atomic types are comparable under the rules for GeneralComparisons
+ * (that is, untyped atomic values treated as comparable to anything)
+ *
+ * @param t1 the first type to compared.
+ * This must be a primitive atomic type as defined by {@link ItemType#getPrimitiveType}
+ * @param t2 the second type to compared.
+ * This must be a primitive atomic type as defined by {@link ItemType#getPrimitiveType}
+ * @param ordered true if testing for an ordering comparison (lt, gt, le, ge). False
+ * if testing for an equality comparison (eq, ne)
+ * @return true if the types are comparable, as defined by the rules of the "=" operator
+ */
+
+ public static boolean isGenerallyComparable(/*@NotNull*/ BuiltInAtomicType t1, /*@NotNull*/ BuiltInAtomicType t2, boolean ordered) {
+ return t1.equals(BuiltInAtomicType.ANY_ATOMIC)
+ || t2.equals(BuiltInAtomicType.ANY_ATOMIC)
+ || t1.equals(BuiltInAtomicType.UNTYPED_ATOMIC)
+ || t2.equals(BuiltInAtomicType.UNTYPED_ATOMIC)
+ || isGuaranteedComparable(t1, t2, ordered);
+ }
+
+ /**
+ * Determine whether two primitive atomic types are guaranteed comparable under the rules for GeneralComparisons
+ * (that is, untyped atomic values treated as comparable to anything). This method returns false if a run-time
+ * check is necessary.
+ *
+ * @param t1 the first type to compared.
+ * This must be a primitive atomic type as defined by {@link ItemType#getPrimitiveType}
+ * @param t2 the second type to compared.
+ * This must be a primitive atomic type as defined by {@link ItemType#getPrimitiveType}
+ * @param ordered true if testing for an ordering comparison (lt, gt, le, ge). False
+ * if testing for an equality comparison (eq, ne)
+ * @return true if the types are comparable, as defined by the rules of the "=" operator
+ */
+
+ public static boolean isGuaranteedGenerallyComparable(/*@NotNull*/ BuiltInAtomicType t1, /*@NotNull*/ BuiltInAtomicType t2, boolean ordered) {
+ return !(t1.equals(BuiltInAtomicType.ANY_ATOMIC) || t2.equals(BuiltInAtomicType.ANY_ATOMIC))
+ && isGenerallyComparable(t1, t2, ordered);
+ }
+
+
+}
+
diff --git a/sf/saxon/type/TypeHierarchy.java b/sf/saxon/type/TypeHierarchy.java
new file mode 100644
index 0000000..2a90832
--- /dev/null
+++ b/sf/saxon/type/TypeHierarchy.java
@@ -0,0 +1,651 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.RoleLocator;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.z.IntHashSet;
+import net.sf.saxon.z.IntSet;
+
+import javax.xml.transform.SourceLocator;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class exists to provide answers to questions about the type hierarchy. Because
+ * such questions are potentially expensive, it caches the answers. There is one instance of
+ * this class for a Configuration.
+ */
+
+public class TypeHierarchy implements Serializable {
+
+ private Map<ItemTypePair, Integer> map;
+ protected Configuration config;
+
+ /**
+ * Constant denoting relationship between two types: A is the same type as B
+ */
+ public static final int SAME_TYPE = 0;
+ /**
+ * Constant denoting relationship between two types: A subsumes B
+ */
+ public static final int SUBSUMES = 1;
+ /**
+ * Constant denoting relationship between two types: A is subsumed by B
+ */
+ public static final int SUBSUMED_BY = 2;
+ /**
+ * Constant denoting relationship between two types: A overlaps B
+ */
+ public static final int OVERLAPS = 3;
+ /**
+ * Constant denoting relationship between two types: A is disjoint from B
+ */
+ public static final int DISJOINT = 4;
+
+ //private String[] relnames = {"SAME", "SUBSUMES", "SUBSUMED_BY", "OVERLAPS", "DISJOINT"};
+
+ /**
+ * Create the type hierarchy cache for a configuration
+ * @param config the configuration
+ */
+
+ public TypeHierarchy(Configuration config){
+ this.config = config;
+ map = new ConcurrentHashMap<ItemTypePair, Integer>();
+ }
+
+ /**
+ * Apply the function conversion rules to a value, given a required type.
+ * The parameter type S represents the supplied type, R the required type
+ *
+ *
+ * @param value a value to be converted
+ * @param requiredType the required type
+ * @return the converted value
+ * @throws net.sf.saxon.trans.XPathException
+ * if the value cannot be converted to the required type
+ */
+
+ public Sequence applyFunctionConversionRules(
+ Sequence value, SequenceType requiredType,
+ RoleLocator role, SourceLocator locator)
+ throws XPathException {
+
+ ItemType suppliedItemType = SequenceTool.getItemType(value, this);
+
+ SequenceIterator iterator = value.iterate();
+ final ItemType requiredItemType = requiredType.getPrimaryType();
+
+ if (iterator instanceof EmptyIterator) {
+ return EmptySequence.getInstance();
+ }
+
+ if (requiredItemType.isPlainType()) {
+
+ // step 1: apply atomization if necessary
+
+ if (!suppliedItemType.isPlainType()) {
+ iterator = Atomizer.getAtomizingIterator(iterator, false);
+ suppliedItemType = suppliedItemType.getAtomizedItemType();
+ }
+
+ // step 2: convert untyped atomic values to target item type
+
+ if (relationship(suppliedItemType, BuiltInAtomicType.UNTYPED_ATOMIC) != DISJOINT) {
+ final boolean nsSensitive = ((SimpleType) requiredItemType).isNamespaceSensitive();
+ ItemMappingFunction converter;
+ if (nsSensitive) {
+ converter = new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ if (item instanceof UntypedAtomicValue) {
+ ValidationFailure vf = new ValidationFailure(
+ "Implicit conversion of untypedAtomic value to " + requiredItemType.toString() +
+ " is not allowed");
+ vf.setErrorCode("XPTY0117");
+ throw vf.makeException();
+ } else {
+ return item;
+ }
+ }
+ };
+ } else if (((SimpleType) requiredItemType).isUnionType()) {
+ final ConversionRules rules = config.getConversionRules();
+ converter = new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ if (item instanceof UntypedAtomicValue) {
+ try {
+ return ((SimpleType) requiredItemType).getTypedValue(
+ item.getStringValueCS(), null, rules).head();
+ } catch (ValidationException ve) {
+ ve.setErrorCode("XPTY0004");
+ throw (XPathException)ve;
+ }
+ } else {
+ return item;
+ }
+ }
+ };
+ } else {
+ converter = new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ if (item instanceof UntypedAtomicValue) {
+ ConversionResult val = Converter.convert(
+ ((UntypedAtomicValue) item), (AtomicType) requiredItemType, config.getConversionRules());
+ if (val instanceof ValidationFailure) {
+ ValidationFailure vex = (ValidationFailure) val;
+ throw vex.makeException();
+ }
+ return (Item) val;
+ } else {
+ //noinspection unchecked
+ return item;
+ }
+ }
+ };
+ }
+ iterator = new ItemMappingIterator(iterator, converter, true);
+ }
+
+ // step 3: apply numeric promotion
+
+ if (requiredItemType.equals(BuiltInAtomicType.DOUBLE)) {
+ ItemMappingFunction<AtomicValue, DoubleValue> promoter = new ItemMappingFunction<AtomicValue, DoubleValue>() {
+ public DoubleValue mapItem(AtomicValue item) throws XPathException {
+ if (item instanceof NumericValue) {
+ return (DoubleValue) Converter.convert(item, BuiltInAtomicType.DOUBLE, config.getConversionRules()).asAtomic();
+ } else {
+ throw new XPathException(
+ "Cannot promote non-numeric value to xs:double", "XPTY0004");
+ }
+ }
+ };
+ iterator = new ItemMappingIterator(iterator, promoter, true);
+ } else if (requiredItemType.equals(BuiltInAtomicType.FLOAT)) {
+ ItemMappingFunction<AtomicValue, FloatValue> promoter = new ItemMappingFunction<AtomicValue, FloatValue>() {
+ public FloatValue mapItem(AtomicValue item) throws XPathException {
+ if (item instanceof DoubleValue) {
+ throw new XPathException(
+ "Cannot promote xs:double value to xs:float", "XPTY0004");
+ } else if (item instanceof NumericValue) {
+ return (FloatValue) Converter.convert(item, BuiltInAtomicType.FLOAT, config.getConversionRules()).asAtomic();
+ } else {
+ throw new XPathException(
+ "Cannot promote non-numeric value to xs:float", "XPTY0004");
+ }
+ }
+ };
+ iterator = new ItemMappingIterator(iterator, promoter, true);
+ }
+
+ // step 4: apply URI-to-string promotion
+
+ if (requiredItemType.equals(BuiltInAtomicType.STRING) &&
+ relationship(suppliedItemType, BuiltInAtomicType.ANY_URI) != DISJOINT) {
+ ItemMappingFunction promoter = new ItemMappingFunction() {
+ public Item mapItem(Item item) throws XPathException {
+ if (item instanceof AnyURIValue) {
+ return new StringValue(item.getStringValueCS());
+ } else {
+ return item;
+ }
+ }
+ };
+ iterator = new ItemMappingIterator(iterator, promoter, true);
+ }
+ }
+
+ // step 5: apply function coercion
+
+ iterator = applyFunctionCoercion(iterator, suppliedItemType, requiredItemType, role, locator);
+
+ // Add a check that the values conform to the required type
+
+ int relation = relationship(suppliedItemType, requiredItemType);
+
+ if (!(relation == SAME_TYPE || relation == SUBSUMED_BY)) {
+ ItemTypeCheckingFunction itemChecker =
+ new ItemTypeCheckingFunction<Item>(requiredItemType, role, locator, config);
+ iterator = new ItemMappingIterator(iterator, itemChecker, true);
+ }
+
+ if (requiredType.getCardinality() != StaticProperty.ALLOWS_ZERO_OR_MORE) {
+ iterator = new CardinalityCheckingIterator(iterator, requiredType.getCardinality(), role, locator);
+ }
+
+ return SequenceTool.toLazySequence(iterator);
+
+ }
+
+ /**
+ * Apply function coercion when function items are supplied as arguments to a function call.
+ * This does not happen in Saxon-HE, so the implementation is subclassed in Saxon-PE/EE
+ * @param iterator An iterator over the supplied value of the parameter
+ * @param suppliedItemType the item type of the supplied value
+ * @param requiredItemType the required item type (typically a function item type)
+ * @param role information for diagnostics
+ * @param locator information for diagnostics
+ * @return an iterator over the converted value
+ */
+
+ protected SequenceIterator applyFunctionCoercion(SequenceIterator iterator,
+ ItemType suppliedItemType, ItemType requiredItemType,
+ RoleLocator role, SourceLocator locator) {
+ return iterator;
+ }
+
+ /**
+ * Get the Saxon configuration to which this type hierarchy belongs
+ * @return the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Determine whether type A is type B or one of its subtypes, recursively.
+ * "Subtype" here means a type that is subsumed, that is, a type whose instances
+ * are a subset of the instances of the other type.
+ *
+ * @param subtype identifies the first type
+ * @param supertype identifies the second type
+ * @return true if the first type is the second type or is subsumed by the second type
+ */
+
+ public boolean isSubType(ItemType subtype, /*@NotNull*/ ItemType supertype) {
+ int relation = relationship(subtype, supertype);
+ return (relation==SAME_TYPE || relation==SUBSUMED_BY);
+ }
+
+ /**
+ * Determine the relationship of one item type to another.
+ * @param t1 the first item type
+ * @param t2 the second item type
+ * @return {@link #SAME_TYPE} if the types are the same; {@link #SUBSUMES} if the first
+ * type subsumes the second (that is, all instances of the second type are also instances
+ * of the first); {@link #SUBSUMED_BY} if the second type subsumes the first;
+ * {@link #OVERLAPS} if the two types overlap (have a non-empty intersection, but neither
+ * subsumes the other); {@link #DISJOINT} if the two types are disjoint (have an empty intersection)
+ */
+
+ public int relationship(/*@Nullable*/ ItemType t1, /*@NotNull*/ ItemType t2) {
+ if (t1 == null) {
+ throw new NullPointerException();
+ }
+ if (t1.equals(t2)) {
+ return SAME_TYPE;
+ }
+ ItemTypePair pair = new ItemTypePair(t1, t2);
+ Integer result = map.get(pair);
+ if (result == null) {
+ result = computeRelationship(t1, t2);
+ map.put(pair, result);
+ }
+ return result;
+ }
+
+ /**
+ * Determine the relationship of one item type to another.
+ * @param t1 the first item type
+ * @param t2 the second item type
+ * @return {@link #SAME_TYPE} if the types are the same; {@link #SUBSUMES} if the first
+ * type subsumes the second (that is, all instances of the second type are also instances
+ * of the first); {@link #SUBSUMED_BY} if the second type subsumes the first;
+ * {@link #OVERLAPS} if the two types overlap (have a non-empty intersection, but neither
+ * subsumes the other); {@link #DISJOINT} if the two types are disjoint (have an empty intersection)
+ */
+
+ private int computeRelationship(/*@NotNull*/ ItemType t1, /*@NotNull*/ ItemType t2) {
+ //System.err.println("computeRelationship " + t1 + ", " + t2);
+ if (t1 == t2) {
+ return SAME_TYPE;
+ }
+ if (t1 instanceof AnyItemType) {
+ if (t2 instanceof AnyItemType) {
+ return SAME_TYPE;
+ } else {
+ return SUBSUMES;
+ }
+ } else if (t2 instanceof AnyItemType) {
+ return SUBSUMED_BY;
+ } else if (t1 == ErrorType.getInstance()) {
+ return SUBSUMED_BY;
+ } else if (t2 == ErrorType.getInstance()) {
+ return SUBSUMES;
+ } else if (t1.isPlainType()) {
+ if (t2 instanceof NodeTest || t2 instanceof FunctionItemType || t2 instanceof ExternalObjectType) {
+ return DISJOINT;
+ } else if (t2 instanceof ExternalObjectType) {
+ if (((AtomicType)t1).getFingerprint() == StandardNames.XS_ANY_ATOMIC_TYPE) {
+ return SUBSUMES;
+ } else {
+ return DISJOINT;
+ }
+ } else if (t1 == BuiltInAtomicType.ANY_ATOMIC && t2.isPlainType()) {
+ return SUBSUMES;
+ } else if (t2 == BuiltInAtomicType.ANY_ATOMIC) {
+ return SUBSUMED_BY;
+ } else if (t1 instanceof AtomicType && t2 instanceof AtomicType) {
+ if (((AtomicType)t1).getFingerprint() == ((AtomicType)t2).getFingerprint()) {
+ return SAME_TYPE;
+ }
+ ItemType t = t2;
+ while (t.isPlainType()) {
+ if (((AtomicType)t1).getFingerprint() == ((AtomicType)t).getFingerprint()) {
+ return SUBSUMES;
+ }
+ t = t.getSuperType(this);
+ }
+ t = t1;
+ while (t.isPlainType()) {
+ if (((AtomicType)t).getFingerprint() == ((AtomicType)t2).getFingerprint()) {
+ return SUBSUMED_BY;
+ }
+ t = t.getSuperType(this);
+ }
+ return DISJOINT;
+
+ } else if (!(t1.isAtomicType()) && t2 instanceof PlainType) {
+ // relationship(union, atomic) or relationship(union, union)
+ Set<PlainType> s1 = ((PlainType)t1).getPlainMemberTypes();
+ Set<PlainType> s2 = ((PlainType)t2).getPlainMemberTypes();
+
+ boolean gt = s1.containsAll(s2);
+ boolean lt = s2.containsAll(s1);
+ if (gt && lt) {
+ return SAME_TYPE;
+ } else if (gt) {
+ return SUBSUMES;
+ } else if (lt) {
+ return SUBSUMED_BY;
+ } else {
+ // TODO: we could return a more precise result, e.g. union(string, decimal) subsumes union(string, int)
+ boolean allSubsumed = true;
+ boolean foundOverlap = false;
+ for (PlainType a1 : s1) {
+ boolean foundSupertype = false;
+ for (PlainType a2 : s2) {
+ int rel = relationship(a1, a2);
+ if (rel == SUBSUMED_BY || rel == SAME_TYPE) {
+ foundSupertype = true;
+ break;
+ }
+ if (rel == SUBSUMES || rel == OVERLAPS) {
+ foundOverlap = true;
+ }
+ }
+ if (!foundSupertype) {
+ allSubsumed = false;
+ break;
+ }
+ }
+ if (allSubsumed) {
+ return SUBSUMED_BY;
+ }
+ allSubsumed = true;
+ for (PlainType a2 : s2) {
+ boolean foundSupertype = false;
+ for (PlainType a1 : s1) {
+ int rel = relationship(a1, a2);
+ if (rel == SUBSUMES || rel == SAME_TYPE) {
+ foundSupertype = true;
+ break;
+ }
+ if (rel == SUBSUMED_BY || rel == OVERLAPS) {
+ foundOverlap = true;
+ }
+ }
+ if (!foundSupertype) {
+ allSubsumed = false;
+ break;
+ }
+ }
+ if (allSubsumed) {
+ return SUBSUMES;
+ }
+ if (foundOverlap) {
+ return OVERLAPS;
+ }
+ return DISJOINT;
+ }
+ } else if (t1 instanceof AtomicType) {
+ // relationship (atomic, union)
+ int r = relationship(t2, t1);
+ return inverseRelationship(r);
+
+ } else {
+ // all options exhausted
+ throw new IllegalStateException();
+ }
+ } else if (t1 instanceof NodeTest) {
+ if (t2.isPlainType() || (t2 instanceof FunctionItemType) || (t2 instanceof ExternalObjectType)) {
+ return DISJOINT;
+ } else {
+ // both types are NodeTests
+ if (t1 instanceof AnyNodeTest) {
+ if (t2 instanceof AnyNodeTest) {
+ return SAME_TYPE;
+ } else {
+ return SUBSUMES;
+ }
+ } else if (t2 instanceof AnyNodeTest) {
+ return SUBSUMED_BY;
+// } else if (t1 instanceof ErrorType) {
+// return DISJOINT;
+// } else if (t2 instanceof ErrorType) {
+// return DISJOINT;
+ } else {
+ // first find the relationship between the node kinds allowed
+ int nodeKindRelationship;
+ int m1 = ((NodeTest)t1).getNodeKindMask();
+ int m2 = ((NodeTest)t2).getNodeKindMask();
+ if ((m1 & m2) == 0) {
+ return DISJOINT;
+ } else if (m1 == m2) {
+ nodeKindRelationship = SAME_TYPE;
+ } else if ((m1 & m2) == m1) {
+ nodeKindRelationship = SUBSUMED_BY;
+ } else if ((m1 & m2) == m2) {
+ nodeKindRelationship = SUBSUMES;
+ } else {
+ nodeKindRelationship = OVERLAPS;
+ }
+
+ // now find the relationship between the node names allowed. Note that although
+ // NamespaceTest and LocalNameTest are NodeTests, they do not occur in SequenceTypes,
+ // so we don't need to consider them.
+ int nodeNameRelationship;
+ IntSet n1 = ((NodeTest)t1).getRequiredNodeNames(); // null means all names allowed
+ IntSet n2 = ((NodeTest)t2).getRequiredNodeNames(); // null means all names allowed
+ if (n1 == null) {
+ if (n2 == null) {
+ nodeNameRelationship = SAME_TYPE;
+ } else {
+ nodeNameRelationship = SUBSUMES;
+ }
+ } else if (n2 == null) {
+ nodeNameRelationship = SUBSUMED_BY;
+ } else if (n1.containsAll(n2)) {
+ if (n1.size() == n2.size()) {
+ nodeNameRelationship = SAME_TYPE;
+ } else {
+ nodeNameRelationship = SUBSUMES;
+ }
+ } else if (n2.containsAll(n1)) {
+ nodeNameRelationship = SUBSUMED_BY;
+ } else if (IntHashSet.containsSome(n1, n2)) {
+ nodeNameRelationship = OVERLAPS;
+ } else {
+ nodeNameRelationship = DISJOINT;
+ }
+
+ // now find the relationship between the content types allowed
+
+ int contentRelationship = computeContentRelationship(t1, t2, n1, n2);
+
+ // now analyse the three different relationsships
+
+ if (nodeKindRelationship == SAME_TYPE &&
+ nodeNameRelationship == SAME_TYPE &&
+ contentRelationship == SAME_TYPE) {
+ return SAME_TYPE;
+ } else if ((nodeKindRelationship == SAME_TYPE || nodeKindRelationship == SUBSUMES) &&
+ (nodeNameRelationship == SAME_TYPE || nodeNameRelationship == SUBSUMES) &&
+ (contentRelationship == SAME_TYPE || contentRelationship == SUBSUMES)) {
+ return SUBSUMES;
+ } else if ((nodeKindRelationship == SAME_TYPE || nodeKindRelationship == SUBSUMED_BY) &&
+ (nodeNameRelationship == SAME_TYPE || nodeNameRelationship == SUBSUMED_BY) &&
+ (contentRelationship == SAME_TYPE || contentRelationship == SUBSUMED_BY)) {
+ return SUBSUMED_BY;
+ } else if (nodeKindRelationship == DISJOINT ||
+ nodeNameRelationship == DISJOINT ||
+ contentRelationship == DISJOINT) {
+ return DISJOINT;
+ } else {
+ return OVERLAPS;
+ }
+ }
+ }
+ } else if (t1 instanceof ExternalObjectType) {
+ if (t2 instanceof ExternalObjectType) {
+ return ((ExternalObjectType)t1).getRelationship((ExternalObjectType)t2);
+ } else {
+ return DISJOINT;
+ }
+ } else {
+ // t1 is a FunctionItemType
+ if (t2 instanceof FunctionItemType) {
+ return ((FunctionItemType)t1).relationship((FunctionItemType)t2, this);
+ } else {
+ return DISJOINT;
+ }
+ }
+
+ }
+
+ /**
+ * Compute the relationship between the allowed content-types of two types
+ * @param t1 the first type
+ * @param t2 the second types
+ * @param n1 the set of element names allowed by the first type
+ * @param n2 the set of element names allowed by the second type
+ * @return the relationship (same type, subsumes, overlaps, subsumed-by)
+ */
+ protected int computeContentRelationship(ItemType t1, ItemType t2, IntSet n1, IntSet n2) {
+ return SAME_TYPE;
+ }
+
+ private static int inverseRelationship(int relation) {
+ switch (relation) {
+ case SAME_TYPE: return SAME_TYPE;
+ case SUBSUMES: return SUBSUMED_BY;
+ case SUBSUMED_BY: return SUBSUMES;
+ case OVERLAPS: return OVERLAPS;
+ case DISJOINT: return DISJOINT;
+ default: throw new IllegalArgumentException();
+ }
+ }
+
+ public ItemType getGenericFunctionItemType() {
+ return AnyItemType.getInstance();
+ }
+
+
+ /**
+ * Test whether a type annotation code represents the type xs:ID or one of its subtypes
+ * @param typeCode the type annotation to be tested
+ * @return true if the type annotation represents an xs:ID
+ */
+
+ public boolean isIdCode(int typeCode) {
+ typeCode &= NamePool.FP_MASK;
+ if (typeCode == StandardNames.XS_ID) {
+ return true;
+ } else if (typeCode < 1024) {
+ // No other built-in type is an ID
+ return false;
+ } else {
+ SchemaType type = config.getSchemaType(typeCode);
+ if (type == null) {
+ return false; // this shouldn't happen, but there's no need to crash right here
+ }
+ return type.isIdType();
+ }
+ }
+
+ /**
+ * Test whether a type annotation code represents the type xs:IDREF, xs:IDREFS or one of their subtypes
+ * @param typeCode the type annotation to be tested
+ * @return true if the type annotation represents an xs:IDREF or xs:IDREFS or a subtype thereof
+ */
+
+ public boolean isIdrefsCode(int typeCode) {
+ typeCode &= NamePool.FP_MASK;
+ if (typeCode == StandardNames.XS_IDREF || typeCode == StandardNames.XS_IDREFS) {
+ return true;
+ } else if (typeCode < 1024) {
+ // No other built-in type is an IDREF or IDREFS
+ return false;
+ } else {
+ SchemaType type = config.getSchemaType(typeCode);
+ if (type == null) {
+ // shouldn't happen, but we don't need to crash right now
+ return false;
+ }
+ return type.isIdRefType();
+ }
+ }
+
+ private static class ItemTypePair implements Serializable {
+ ItemType s;
+ ItemType t;
+
+ public ItemTypePair(ItemType s, ItemType t) {
+ this.s = s;
+ this.t = t;
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ * @return a hash code value for this object.
+ * @see Object#equals(Object)
+ * @see java.util.Hashtable
+ */
+ public int hashCode() {
+ return s.hashCode() ^ t.hashCode();
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+
+ public boolean equals(Object obj) {
+ if (obj instanceof ItemTypePair) {
+ final ItemTypePair pair = (ItemTypePair)obj;
+ return s.equals(pair.s) && t.equals(pair.t);
+ } else {
+ return false;
+ }
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/type/UnionType.java b/sf/saxon/type/UnionType.java
new file mode 100644
index 0000000..b6906e5
--- /dev/null
+++ b/sf/saxon/type/UnionType.java
@@ -0,0 +1,24 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+
+import net.sf.saxon.value.SequenceType;
+
+import java.util.Set;
+
+public interface UnionType extends SimpleType, ItemType {
+
+ public boolean containsListType();
+
+ public Set<PlainType> getPlainMemberTypes();
+
+ public SequenceType getResultTypeOfCast();
+
+}
+
diff --git a/sf/saxon/type/UnresolvedReferenceException.java b/sf/saxon/type/UnresolvedReferenceException.java
new file mode 100644
index 0000000..137ad9b
--- /dev/null
+++ b/sf/saxon/type/UnresolvedReferenceException.java
@@ -0,0 +1,23 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+/**
+ * This exception occurs when an attempt is made to dereference a reference from one
+ * schema component to another, if the target of the reference cannot be found. Note that
+ * an unresolved reference is not necessarily an error: a schema containing unresolved
+ * references may be used for validation, provided the components containing the
+ * unresolved references are not actually used.
+ */
+
+public abstract class UnresolvedReferenceException extends RuntimeException {
+
+ public UnresolvedReferenceException(String ref) {
+ super(ref);
+ }
+}
diff --git a/sf/saxon/type/Untyped.java b/sf/saxon/type/Untyped.java
new file mode 100644
index 0000000..6f29695
--- /dev/null
+++ b/sf/saxon/type/Untyped.java
@@ -0,0 +1,604 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import com.saxonica.schema.UserComplexType;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.om.AtomicSequence;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.z.IntHashSet;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.value.UntypedAtomicValue;
+
+import java.io.Serializable;
+
+/**
+ * This class has a singleton instance which represents the complex type xdt:untyped,
+ * used for elements that have not been validated.
+ */
+
+public final class Untyped implements ComplexType, Serializable {
+
+ /*@NotNull*/ private static Untyped theInstance = new Untyped();
+
+ /**
+ * Private constructor
+ */
+ private Untyped() {
+ }
+
+ /**
+ * Get the validation status - always valid
+ */
+ public int getValidationStatus() {
+ return VALIDATED;
+ }
+
+ /**
+ * Get the local name of this type
+ *
+ * @return the local name of this type definition, if it has one. Return null in the case of an
+ * anonymous type.
+ */
+
+ /*@NotNull*/ public String getName() {
+ return "untyped";
+ }
+
+ /**
+ * Get the name of this type as an EQName, that is, a string in the format Q{uri}local.
+ *
+ * @return an EQName identifying the type, specifically "Q{http://www.w3.org/2001/XMLSchema}untyped"
+ */
+ public String getEQName() {
+ return "Q{" + NamespaceConstant.SCHEMA + "}untyped";
+ }
+
+ /**
+ * Get the redefinition level. This is zero for a component that has not been redefined;
+ * for a redefinition of a level-0 component, it is 1; for a redefinition of a level-N
+ * component, it is N+1. This concept is used to support the notion of "pervasive" redefinition:
+ * if a component is redefined at several levels, the top level wins, but it is an error to have
+ * two versions of the component at the same redefinition level.
+ * @return the redefinition level
+ */
+
+ public int getRedefinitionLevel() {
+ return 0;
+ }
+
+ /**
+ * Get the target namespace of this type
+ *
+ * @return the target namespace of this type definition, if it has one. Return null in the case
+ * of an anonymous type, and in the case of a global type defined in a no-namespace schema.
+ */
+
+ public String getTargetNamespace() {
+ return NamespaceConstant.SCHEMA;
+ }
+
+ /**
+ * Get the variety of this complex type. This will be one of the values
+ * {@link #VARIETY_EMPTY}, {@link #VARIETY_MIXED}, {@link #VARIETY_SIMPLE}, or
+ * {@link #VARIETY_ELEMENT_ONLY}
+ */
+
+ public int getVariety() {
+ return VARIETY_MIXED;
+ }
+
+ /**
+ * Get the URI of the schema document containing the definition of this type
+ * @return null for a built-in type
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return null;
+ }
+
+ /**
+ * Returns the value of the 'block' attribute for this type, as a bit-signnificant
+ * integer with fields such as {@link SchemaType#DERIVATION_LIST} and {@link SchemaType#DERIVATION_EXTENSION}
+ *
+ * @return the value of the 'block' attribute for this type
+ */
+
+ public int getBlock() {
+ return 0;
+ }
+
+ /**
+ * Gets the integer code of the derivation method used to derive this type from its
+ * parent. Returns zero for primitive types.
+ *
+ * @return a numeric code representing the derivation method, for example {@link SchemaType#DERIVATION_RESTRICTION}
+ */
+
+ public int getDerivationMethod() {
+ return 0;
+ }
+
+ /**
+ * Determines whether derivation (of a particular kind)
+ * from this type is allowed, based on the "final" property
+ *
+ * @param derivation the kind of derivation, for example {@link SchemaType#DERIVATION_LIST}
+ * @return true if this kind of derivation is allowed
+ */
+
+ public boolean allowsDerivation(int derivation) {
+ return false;
+ }
+
+ /**
+ * Get the types of derivation that are not permitted, by virtue of the "final" property.
+ *
+ * @return the types of derivation that are not permitted, as a bit-significant integer
+ * containing bits such as {@link net.sf.saxon.type.SchemaType#DERIVATION_EXTENSION}
+ */
+ public int getFinalProhibitions() {
+ return 0;
+ }
+
+ /**
+ * Check that this type is validly derived from a given type
+ *
+ * @param type the type from which this type is derived
+ * @param block the derivations that are blocked by the relevant element declaration
+ */
+
+ public void checkTypeDerivationIsOK(SchemaType type, int block) {
+
+ }
+
+ /**
+ * Get the fingerprint of the name of this type
+ *
+ * @return the fingerprint. Returns an invented fingerprint for an anonymous type.
+ */
+
+ public int getFingerprint() {
+ return StandardNames.XS_UNTYPED;
+ }
+
+ /**
+ * Get the namecode of the name of this type. This includes the prefix from the original
+ * type declaration: in the case of built-in types, there may be a conventional prefix
+ * or there may be no prefix.
+ */
+
+ public int getNameCode() {
+ return StandardNames.XS_UNTYPED;
+ }
+
+ /**
+ * Get the display name of the type: that is, a lexical QName with an arbitrary prefix
+ *
+ * @return a lexical QName identifying the type
+ */
+
+ /*@NotNull*/ public String getDisplayName() {
+ return "xs:untyped";
+ }
+
+ /**
+ * Test whether this SchemaType is a complex type
+ *
+ * @return true if this SchemaType is a complex type
+ */
+
+ public boolean isComplexType() {
+ return true;
+ }
+
+ /**
+ * Test whether this is an anonymous type
+ *
+ * @return true if this SchemaType is an anonymous type
+ */
+
+ public boolean isAnonymousType() {
+ return false;
+ }
+
+ /**
+ * Returns the base type that this type inherits from. This method can be used to get the
+ * base type of a type that is known to be valid.
+ * If this type is a Simpletype that is a built in primitive type then null is returned.
+ *
+ * @return the base type.
+ * @throws IllegalStateException if this type is not valid.
+ */
+
+ /*@NotNull*/ public SchemaType getKnownBaseType() throws IllegalStateException {
+ return AnyType.getInstance();
+ }
+
+ /**
+ * Test whether this is the same type as another type. They are considered to be the same type
+ * if they are derived from the same type definition in the original XML representation (which
+ * can happen when there are multiple includes of the same file)
+ */
+
+ public boolean isSameType(SchemaType other) {
+ return (other instanceof Untyped);
+ }
+
+ /**
+ * Returns the base type that this type inherits from.
+ * If this type is a Simpletype that is a built in primitive type then null is returned.
+ *
+ * @return the base type.
+ */
+
+ /*@NotNull*/ public SchemaType getBaseType() {
+ return AnyType.getInstance();
+ }
+
+
+ /**
+ * Get the singular instance of this class
+ *
+ * @return the singular object representing xs:anyType
+ */
+
+ /*@NotNull*/ public static Untyped getInstance() {
+ return theInstance;
+ }
+
+ /**
+ * Test whether this ComplexType has been marked as abstract.
+ *
+ * @return false: this class is not abstract.
+ */
+
+ public boolean isAbstract() {
+ return false;
+ }
+
+ /**
+ * Test whether this SchemaType is a simple type
+ *
+ * @return true if this SchemaType is a simple type
+ */
+
+ public boolean isSimpleType() {
+ return false;
+ }
+
+ /**
+ * Test whether this SchemaType is an atomic type
+ *
+ * @return true if this SchemaType is an atomic type
+ */
+
+ public boolean isAtomicType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this type is an ID type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:ID: that is, it includes types derived
+ * from ID by restriction, list, or union. Note that for a node to be treated
+ * as an ID, its typed value must be a *single* atomic value of type ID; the type of the
+ * node, however, can still allow a list.
+ */
+
+ public boolean isIdType() {
+ return false;
+ }
+
+ /**
+ * Ask whether this type is an IDREF or IDREFS type. This is defined to be any simple type
+ * who typed value may contain atomic values of type xs:IDREF: that is, it includes types derived
+ * from IDREF or IDREFS by restriction, list, or union
+ */
+
+ public boolean isIdRefType() {
+ return false;
+ }
+
+ /**
+ * Test whether this complex type has complex content
+ *
+ * @return true: this complex type has complex content
+ */
+ public boolean isComplexContent() {
+ return true;
+ }
+
+ /**
+ * Test whether this complex type has simple content
+ *
+ * @return false: this complex type has complex content
+ */
+
+ public boolean isSimpleContent() {
+ return false;
+ }
+
+ /**
+ * Test whether this complex type has "all" content, that is, a content model
+ * using an xs:all compositor
+ *
+ * @return false: this complex type does not use an "all" compositor
+ */
+
+ public boolean isAllContent() {
+ return false;
+ }
+
+ /**
+ * For a complex type with simple content, return the simple type of the content.
+ * Otherwise, return null.
+ *
+ * @return null: this complex type does not have simple content
+ */
+
+ /*@Nullable*/ public SimpleType getSimpleContentType() {
+ return null;
+ }
+
+ /**
+ * Test whether this complex type is derived by restriction
+ *
+ * @return true: this type is treated as a restriction of xs:anyType
+ */
+ public boolean isRestricted() {
+ return true;
+ }
+
+ /**
+ * Test whether the content type of this complex type is empty
+ *
+ * @return false: the content model is not empty
+ */
+
+ public boolean isEmptyContent() {
+ return false;
+ }
+
+ /**
+ * Test whether the content model of this complexType allows empty content
+ *
+ * @return true: the content is allowed to be empty
+ */
+
+ public boolean isEmptiable() {
+ return true;
+ }
+
+ /**
+ * Test whether this complex type allows mixed content
+ *
+ * @return true: mixed content is allowed
+ */
+
+ public boolean isMixedContent() {
+ return true;
+ }
+
+ /**
+ * Get a description of this type for use in diagnostics
+ *
+ * @return the string "xs:anyType"
+ */
+
+ /*@NotNull*/ public String getDescription() {
+ return "xs:untyped";
+ }
+
+ /**
+ * Analyze an expression to see whether the expression is capable of delivering a value of this
+ * type.
+ *
+ * @param expression the expression that delivers the content
+ * @param kind the node kind whose content is being delivered: {@link Type#ELEMENT},
+ * {@link Type#ATTRIBUTE}, or {@link Type#DOCUMENT}
+ * @param env the static context
+ */
+
+ public void analyzeContentExpression(Expression expression, int kind, StaticContext env) {
+ //return;
+ }
+
+ /**
+ * Get the typed value of a node that is annotated with this schema type.
+ * @param node the node whose typed value is required
+ * @return the typed value.
+ * @since 8.5
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize(/*@NotNull*/ NodeInfo node) {
+ return new UntypedAtomicValue(node.getStringValueCS());
+ }
+
+ /**
+ * Test whether this complex type subsumes another complex type. The algorithm
+ * used is as published by Thompson and Tobin, XML Europe 2003.
+ *
+ * @param sub the other type (the type that is derived by restriction, validly or otherwise)
+ * @param compiler used for error reporting
+ * @return null indicating that this type does indeed subsume the other; or a string indicating
+ * why it doesn't.
+ */
+
+// public String subsumes(ComplexType sub, ISchemaCompiler compiler) {
+// return null;
+// }
+
+ /**
+ * Find an element particle within this complex type definition having a given element name
+ * (identified by fingerprint), and return the schema type associated with that element particle.
+ * If there is no such particle, return null. If the fingerprint matches an element wildcard,
+ * return the type of the global element declaration with the given name if one exists, or AnyType
+ * if none exists and lax validation is permitted by the wildcard.
+ *
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @param considerExtensions
+ */
+
+ /*@NotNull*/ public SchemaType getElementParticleType(int fingerprint, boolean considerExtensions) {
+ return this;
+ }
+
+ /**
+ * Find an element particle within this complex type definition having a given element name
+ * (identified by fingerprint), and return the cardinality associated with that element particle,
+ * that is, the number of times the element can occur within this complex type. The value is one of
+ * {@link net.sf.saxon.expr.StaticProperty#EXACTLY_ONE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_ONE},
+ * {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ZERO_OR_MORE}, {@link net.sf.saxon.expr.StaticProperty#ALLOWS_ONE_OR_MORE},
+ * If there is no such particle, return zero.
+ *
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @param considerExtensions
+ */
+
+ public int getElementParticleCardinality(int fingerprint, boolean considerExtensions) {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Find an attribute use within this complex type definition having a given attribute name
+ * (identified by fingerprint), and return the schema type associated with that attribute.
+ * If there is no such attribute use, return null. If the fingerprint matches an attribute wildcard,
+ * return the type of the global attribute declaration with the given name if one exists, or AnySimpleType
+ * if none exists and lax validation is permitted by the wildcard.
+ *
+ * @param fingerprint Identifies the name of the child element within this content model
+ */
+
+ /*@NotNull*/ public SimpleType getAttributeUseType(int fingerprint) {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+
+ /**
+ * Find an attribute use within this complex type definition having a given attribute name
+ * (identified by fingerprint), and return the cardinality associated with that attribute,
+ * which will always be 0, 1, or 0-or-1.
+ * If there is no such attribute use, return null. If the fingerprint matches an attribute wildcard,
+ * return the type of the global attribute declaration with the given name if one exists, or AnySimpleType
+ * if none exists and lax validation is permitted by the wildcard.
+ * <p/>
+ * If there are types derived from this type by extension, search those too.
+ * @param fingerprint Identifies the name of the child element within this content model
+ * @return the schema type associated with the attribute use identified by the fingerprint.
+ * If there is no such attribute use, return null.
+ */
+
+ public int getAttributeUseCardinality(int fingerprint) throws SchemaException {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+
+ /**
+ * Return true if this type (or any known type derived from it by extension) allows the element
+ * to have one or more attributes.
+ * @return true if attributes are allowed
+ */
+
+ public boolean allowsAttributes() {
+ return true;
+ }
+
+
+ /**
+ * Get a list of all the names of elements that can appear as children of an element having this
+ * complex type, as integer fingerprints. If the list is unbounded (because of wildcards or the use
+ * of xs:anyType), return null.
+ *
+ * @param children an integer set, initially empty, which on return will hold the fingerprints of all permitted
+ * child elements; if the result contains the value -1, this indicates that it is not possible to enumerate
+ * all the children, typically because of wildcards. In this case the other contents of the set should
+ * @param ignoreWildcards
+ */
+
+ public void gatherAllPermittedChildren(/*@NotNull*/ IntHashSet children, boolean ignoreWildcards) throws SchemaException {
+ children.add(-1);
+ }
+
+ /**
+ * Get a list of all the names of elements that can appear as descendants of an element having this
+ * complex type, as integer fingerprints. If the list is unbounded (because of wildcards or the use
+ * of xs:anyType), return null.
+ *
+ * @param descendants an integer set, initially empty, which on return will hold the fingerprints of all permitted
+ * descendant elements; if the result contains the value -1, this indicates that it is not possible to enumerate
+ * all the descendants, typically because of wildcards. In this case the other contents of the set should
+ * be ignored.
+ */
+
+ public void gatherAllPermittedDescendants(/*@NotNull*/ IntHashSet descendants) throws SchemaException {
+ descendants.add(-1);
+ }
+
+ /**
+ * Assuming an element is a permitted descendant in the content model of this type, determine
+ * the type of the element when it appears as a descendant. If it appears with more than one type,
+ * return xs:anyType.
+ * @param fingerprint the name of the required descendant element
+ * @return the type of the descendant element; null if the element cannot appear as a descendant;
+ * anyType if it can appear with several different types
+ */
+
+ /*@NotNull*/ public SchemaType getDescendantElementType(int fingerprint) throws SchemaException {
+ return this;
+ }
+
+ /**
+ * Assuming an element is a permitted descendant in the content model of this type, determine
+ * the cardinality of the element when it appears as a descendant.
+ * @param fingerprint the name of the required descendant element
+ * @return the cardinality of the descendant element within this complex type
+ */
+
+ public int getDescendantElementCardinality(int fingerprint) throws SchemaException {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Ask whether this type (or any known type derived from it by extension) allows the element
+ * to have children that match a wildcard
+ * @return true if the content model of this type, or its extensions, contains an element wildcard
+ */
+
+ public boolean containsElementWildcard() {
+ return true;
+ }
+
+ /**
+ * Ask whether there are any assertions defined on this complex type
+ *
+ * @return true if there are any assertions
+ */
+ public boolean hasAssertions() {
+ return false;
+ }
+
+//#ifdefined SCHEMA
+ /**
+ * Get the schema component in the form of a function item. This allows schema information
+ * to be made visible to XSLT or XQuery code. The function makes available the contents of the
+ * schema component as defined in the XSD specification. The function takes a string as argument
+ * representing a property name, and returns the corresponding property of the schema component.
+ * There is also a property "class" which returns the kind of schema component, for example
+ * "Attribute Declaration".
+ *
+ * @return the schema component represented as a function from property names to property values.
+ */
+ public FunctionItem getComponentAsFunction() {
+ return UserComplexType.getComponentAsFunction(this);
+ }
+//#endif
+
+}
+
diff --git a/sf/saxon/type/ValidationException.java b/sf/saxon/type/ValidationException.java
new file mode 100644
index 0000000..07a4856
--- /dev/null
+++ b/sf/saxon/type/ValidationException.java
@@ -0,0 +1,397 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.om.AbsolutePath;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import org.xml.sax.Locator;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * This exception indicates a failure when validating an instance against a type
+ * defined in a schema.
+ */
+
+public class ValidationException extends XPathException
+ implements SourceLocator, Locator {
+
+ private String systemId;
+ private String publicId;
+ private int lineNumber = -1;
+ private int columnNumber = -1;
+ /*@Nullable*/ private NodeInfo node;
+ private int schemaPart = -1;
+ private String constraintName;
+ private String constraintClauseNumber;
+ private AbsolutePath path;
+ private AbsolutePath contextPath;
+ private SchemaType type;
+
+ // TODO: during output validation, it would sometimes be useful to know what the position in the input file was.
+
+ /**
+ * Creates a new ValidationException with the given message.
+ * @param message the message for this Exception
+ */
+
+ public ValidationException(String message) {
+ super(message);
+ setIsTypeError(true);
+ }
+
+ /**
+ * Creates a new ValidationException with the given nested
+ * exception.
+ * @param exception the nested exception
+ */
+ public ValidationException(Exception exception) {
+ super(exception);
+ setIsTypeError(true);
+ }
+
+ /**
+ * Creates a new ValidationException with the given message
+ * and nested exception.
+ * @param message the detail message for this exception
+ * @param exception the nested exception
+ */
+ public ValidationException(String message, Exception exception) {
+ super(message, exception);
+ setIsTypeError(true);
+ }
+
+ /**
+ * Create a new ValidationException from a message and a Locator.
+ * @param message The error or warning message.
+ * @param locator The locator object for the error or warning.
+ */
+ public ValidationException(String message, SourceLocator locator) {
+ super(message, locator);
+ setIsTypeError(true);
+ // With Xerces, it's enough to store the locator as part of the exception. But with Crimson,
+ // the locator is destroyed when the parse terminates, which means the location information
+ // will not be available in the final error message. So we copy the location information now,
+ // as part of the exception object itself.
+ setSourceLocator(locator);
+ }
+
+ /**
+ * Set a reference to the constraint in XML Schema that is not satisfied
+ * @param schemaPart - 1 or 2, depending whether the constraint is in XMLSchema part 1 or part 2
+ * @param constraintName - the short name of the constraint in XMLSchema, as a fragment identifier in the
+ * HTML of the XML Schema Part 1 specification
+ * @param clause - the clause number within the description of that constraint
+ */
+
+ public void setConstraintReference(int schemaPart, String constraintName, String clause) {
+ this.schemaPart = schemaPart;
+ this.constraintName = constraintName;
+ this.constraintClauseNumber = clause;
+ }
+
+ /**
+ * Copy the constraint reference from another exception object
+ * @param e the other exception object from which to copy the information
+ */
+
+ public void setConstraintReference(/*@NotNull*/ ValidationException e) {
+ schemaPart = e.schemaPart;
+ constraintName = e.constraintName;
+ constraintClauseNumber = e.constraintClauseNumber;
+ }
+
+ /**
+ * Get the constraint reference as a string for inserting into an error message.
+ * @return the reference as a message, or null if no information is available
+ */
+
+ /*@Nullable*/ public String getConstraintReferenceMessage() {
+ if (schemaPart == -1) {
+ return null;
+ }
+ return "See http://www.w3.org/TR/xmlschema11-" + schemaPart + "/#" + constraintName
+ + " clause " + constraintClauseNumber;
+ }
+
+ /**
+ * Get the "schema part" component of the constraint reference
+ * @return 1 or 2 depending on whether the violated constraint is in XML Schema Part 1 or Part 2;
+ * or -1 if there is no constraint reference
+ */
+
+ public int getConstraintSchemaPart() {
+ return schemaPart;
+ }
+
+ /**
+ * Get the constraint name
+ * @return the name of the violated constraint, in the form of a fragment identifier within
+ * the published XML Schema specification; or null if the information is not available.
+ */
+
+ /*@Nullable*/
+ public String getConstraintName() {
+ return constraintName;
+ }
+
+ /**
+ * Get the constraint clause number
+ * @return the section number of the clause containing the constraint that has been violated.
+ * Generally a decimal number in the form n.n.n; possibly a sequence of such numbers separated
+ * by semicolons. Or null if the information is not available.
+ */
+
+ /*@Nullable*/
+ public String getConstraintClauseNumber() {
+ return constraintClauseNumber;
+ }
+
+ /**
+ * Get the constraint name and clause in the format defined in XML Schema Part C (Outcome Tabulations).
+ * This mandates the format validation-rule-name.clause-number
+ * @return the constraint reference, for example "cos-ct-extends.1.2"; or null if the reference
+ * is not known.
+ */
+
+ /*@NotNull*/ public String getConstraintReference() {
+ return (constraintName == null ? "" : constraintName) + '.' +
+ (constraintClauseNumber == null ? "" : constraintClauseNumber);
+ }
+
+ /**
+ * Set the path in the source document
+ * @param path the path to the invalid element in the source document
+ */
+
+ public void setPath(AbsolutePath path) {
+ this.path = path;
+ }
+
+ /**
+ * Returns the String representation of this Exception
+ * @return the String representation of this Exception
+ **/
+ public String toString() {
+ StringBuilder sb = new StringBuilder("ValidationException: ");
+ String message = getMessage();
+ if (message != null) {
+ sb.append(message);
+ }
+ return sb.toString();
+ }
+
+ public String getPublicId() {
+ SourceLocator loc = getLocator();
+ if (publicId == null && loc != null && loc != this) {
+ return loc.getPublicId();
+ } else{
+ return publicId;
+ }
+ }
+
+ public String getSystemId() {
+ SourceLocator loc = getLocator();
+ if (systemId == null && loc != null && loc != this) {
+ return loc.getSystemId();
+ } else{
+ return systemId;
+ }
+ }
+
+ public int getLineNumber() {
+ SourceLocator loc = getLocator();
+ if (lineNumber == -1 && loc != null && loc != this) {
+ return loc.getLineNumber();
+ } else{
+ return lineNumber;
+ }
+ }
+
+ public int getColumnNumber() {
+ SourceLocator loc = getLocator();
+ if (columnNumber == -1 && loc != null && loc != this) {
+ return loc.getColumnNumber();
+ } else{
+ return columnNumber;
+ }
+ }
+
+ /*@Nullable*/ public NodeInfo getNode() {
+ return node;
+ }
+
+ /**
+ * Get the location of the error in terms of a path expressed as a string
+ * @return the location, as a path. The result format is similar to that of the fn:path() function
+ */
+
+ /*@Nullable*/ public String getPath() {
+ if (path != null) {
+ return path.getPathUsingAbbreviatedUris();
+ } else if (node != null) {
+ return Navigator.getPath(node);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the location of the error as a structured path object
+ * @return the location, as a structured path object indicating the position of the error within the containing document
+ */
+
+ public AbsolutePath getAbsolutePath() {
+ if (path != null) {
+ return path;
+ } else if (node != null) {
+ return Navigator.getAbsolutePath(node);
+ } else {
+ return null;
+ }
+ }
+
+ public void setPublicId(String id) {
+ publicId = id;
+ }
+
+ public void setSystemId(String id) {
+ systemId = id;
+ }
+
+ public void setLineNumber(int line) {
+ lineNumber = line;
+ }
+
+ public void setColumnNumber(int column) {
+ columnNumber = column;
+ }
+
+ public void setLocator(/*@Nullable*/ Locator locator) {
+ if (locator != null) {
+ setPublicId(locator.getPublicId());
+ setSystemId(locator.getSystemId());
+ setLineNumber(locator.getLineNumber());
+ setColumnNumber(locator.getColumnNumber());
+ if (locator instanceof NodeInfo) {
+ node = ((NodeInfo)locator);
+ }
+ }
+ super.setLocator(null);
+ }
+
+ public void setSourceLocator(/*@Nullable*/ SourceLocator locator) {
+ if (locator != null) {
+ setPublicId(locator.getPublicId());
+ setSystemId(locator.getSystemId());
+ setLineNumber(locator.getLineNumber());
+ setColumnNumber(locator.getColumnNumber());
+ if (locator instanceof NodeInfo) {
+ node = ((NodeInfo)locator);
+ }
+ }
+ super.setLocator(null);
+ }
+
+ public void setNode(NodeInfo node) {
+ this.node = node;
+ }
+
+ public SourceLocator getLocator() {
+ SourceLocator loc = super.getLocator();
+ if (loc != null) {
+ return loc;
+ } else {
+ return this;
+ }
+ }
+
+ public void setContextPath(AbsolutePath path) {
+ this.contextPath = path;
+ }
+
+ public AbsolutePath getContextPath() {
+ return contextPath;
+ }
+
+ /**
+ * Get additional location text, if any. This gives extra information about the position of the error
+ * in textual form. Where XPath is embedded within a host language such as XSLT, the
+ * formal location information identifies the location of the error in the XSLT module,
+ * while this string locates the error within a specific XPath expression. The information
+ * is typically used only for static errors.
+ *
+ * @return additional information about the location of the error, designed to be output
+ * as a prefix to the error message if desired. (It is not concatenated with the message, because
+ * it may be superfluous in an IDE environment.)
+ */
+
+ public String getValidationLocationText() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ AbsolutePath valPath = getAbsolutePath();
+ if (valPath != null) {
+ fsb.append("Validating ");
+ fsb.append(valPath.getPathUsingPrefixes());
+ if (valPath.getSystemId() != null) {
+ fsb.append(" in ");
+ fsb.append(valPath.getSystemId());
+ }
+ }
+ return fsb.toString();
+ }
+
+
+ /**
+ * Get additional location text, if any. This gives extra information about the position of the error
+ * in textual form. Where XPath is embedded within a host language such as XSLT, the
+ * formal location information identifies the location of the error in the XSLT module,
+ * while this string locates the error within a specific XPath expression. The information
+ * is typically used only for static errors.
+ *
+ * @return additional information about the location of the error, designed to be output
+ * as a prefix to the error message if desired. (It is not concatenated with the message, because
+ * it may be superfluous in an IDE environment.)
+ */
+
+ public String getContextLocationText() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.MEDIUM);
+ AbsolutePath contextPath = getContextPath();
+ if (contextPath != null) {
+ fsb.append("Currently processing ");
+ fsb.append(contextPath.getPathUsingPrefixes());
+ if (contextPath.getSystemId() != null) {
+ fsb.append(" in ");
+ fsb.append(contextPath.getSystemId());
+ }
+ }
+ return fsb.toString();
+ }
+
+ /**
+ * Get the schema type against which validation was attempted and failed
+ * @return the relevant schema type if available, or null otherwise
+ */
+
+ public SchemaType getSchemaType() {
+ return type;
+ }
+
+ /**
+ * Set the schema type against which validation was attempted and failed
+ * @param type the relevant schema type if available, or null otherwise
+ */
+
+ public void setSchemaType(SchemaType type) {
+ this.type = type;
+ }
+
+}
+
diff --git a/sf/saxon/type/ValidationFailure.java b/sf/saxon/type/ValidationFailure.java
new file mode 100644
index 0000000..5e614dd
--- /dev/null
+++ b/sf/saxon/type/ValidationFailure.java
@@ -0,0 +1,293 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.value.AtomicValue;
+import org.xml.sax.Locator;
+
+import javax.xml.transform.SourceLocator;
+
+/**
+ * This exception indicates a failure when validating an instance against a type
+ * defined in a schema.
+ *
+ * <p>This class holds the same information as a ValidationException, except that it is not an exception,
+ * and does not carry system overheads such as a stack trace. It is used because operations such as "castable",
+ * and validation of values in a union, cause validation failures on a success path and it is costly to throw,
+ * or even to create, exception objects on a success path.</p>
+ */
+
+public class ValidationFailure
+ implements SourceLocator, Locator, ConversionResult {
+
+ private String message;
+ private String systemId;
+ private String publicId;
+ private int lineNumber = -1;
+ private int columnNumber = -1;
+ private int schemaPart = -1;
+ private String constraintName;
+ private String clause;
+ /*@Nullable*/ private StructuredQName errorCode;
+
+ /**
+ * Creates a new ValidationException with the given message.
+ * @param message the message for this Exception
+ **/
+ public ValidationFailure(String message) {
+ this.message = message;
+ setErrorCode("FORG0001");
+ }
+
+ /**
+ * Creates a new ValidationFailure with the given nested
+ * exception.
+ * @param exception the nested exception
+ **/
+ public ValidationFailure(/*@NotNull*/ Exception exception) {
+ message = exception.getMessage();
+ if (exception instanceof XPathException) {
+ errorCode = ((XPathException)exception).getErrorCodeQName();
+ } else {
+ setErrorCode("FORG0001");
+ }
+ }
+
+ /**
+ * Set a reference to the constraint in XML Schema that is not satisfied
+ * @param schemaPart - 1 or 2, depending whether the constraint is in XMLSchema part 1 or part 2
+ * @param constraintName - the short name of the constraint in XMLSchema, as a fragment identifier in the
+ * HTML of the XML Schema Part 1 specification
+ * @param clause - the clause number within the description of that constraint
+ */
+
+ public void setConstraintReference(int schemaPart, String constraintName, String clause) {
+ this.schemaPart = schemaPart;
+ this.constraintName = constraintName;
+ this.clause = clause;
+ }
+
+ /**
+ * Copy the constraint reference from another exception object
+ * @param e the other exception object from which to copy the information
+ */
+
+ public void setConstraintReference(/*@NotNull*/ ValidationFailure e) {
+ schemaPart = e.schemaPart;
+ constraintName = e.constraintName;
+ clause = e.clause;
+ }
+
+ /**
+ * Get the constraint reference as a string for inserting into an error message.
+ * @return the reference as a message, or null if no information is available
+ */
+
+ /*@Nullable*/ public String getConstraintReferenceMessage() {
+ if (schemaPart == -1) {
+ return null;
+ }
+ return "See http://www.w3.org/TR/xmlschema-" + schemaPart + "/#" + constraintName
+ + " clause " + clause;
+ }
+
+ /**
+ * Get the "schema part" component of the constraint reference
+ * @return 1 or 2 depending on whether the violated constraint is in XML Schema Part 1 or Part 2;
+ * or -1 if there is no constraint reference
+ */
+
+ public int getConstraintSchemaPart() {
+ return schemaPart;
+ }
+
+ /**
+ * Get the constraint name
+ * @return the name of the violated constraint, in the form of a fragment identifier within
+ * the published XML Schema specification; or null if the information is not available.
+ */
+
+ public String getConstraintName() {
+ return constraintName;
+ }
+
+ /**
+ * Get the constraint clause number
+ * @return the section number of the clause containing the constraint that has been violated.
+ * Generally a decimal number in the form n.n.n; possibly a sequence of such numbers separated
+ * by semicolons. Or null if the information is not available.
+ */
+
+ public String getConstraintClauseNumber() {
+ return clause;
+ }
+
+ /**
+ * Get the constraint name and clause in the format defined in XML Schema Part C (Outcome Tabulations).
+ * This mandates the format validation-rule-name.clause-number
+ * @return the constraint reference, for example "cos-ct-extends.1.2"; or null if the reference
+ * is not known.
+ */
+
+ /*@NotNull*/ public String getConstraintReference() {
+ return constraintName + '.' + clause;
+ }
+
+
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Returns the String representation of this Exception
+ * @return the String representation of this Exception
+ **/
+ public String toString() {
+ FastStringBuffer sb = new FastStringBuffer("ValidationException: ");
+ String message = getMessage();
+ if (message != null) {
+ sb.append(message);
+ }
+ return sb.toString();
+ }
+
+ public String getPublicId() {
+ SourceLocator loc = getLocator();
+ if (publicId == null && loc != null && loc != this) {
+ return loc.getPublicId();
+ } else{
+ return publicId;
+ }
+ }
+
+ public String getSystemId() {
+ SourceLocator loc = getLocator();
+ if (systemId == null && loc != null && loc != this) {
+ return loc.getSystemId();
+ } else{
+ return systemId;
+ }
+ }
+
+ public int getLineNumber() {
+ SourceLocator loc = getLocator();
+ if (lineNumber == -1 && loc != null && loc != this) {
+ return loc.getLineNumber();
+ } else{
+ return lineNumber;
+ }
+ }
+
+ public int getColumnNumber() {
+ SourceLocator loc = getLocator();
+ if (columnNumber == -1 && loc != null && loc != this) {
+ return loc.getColumnNumber();
+ } else{
+ return columnNumber;
+ }
+ }
+
+ public void setPublicId(String id) {
+ publicId = id;
+ }
+
+ public void setSystemId(String id) {
+ systemId = id;
+ }
+
+ public void setLineNumber(int line) {
+ lineNumber = line;
+ }
+
+ public void setColumnNumber(int column) {
+ columnNumber = column;
+ }
+
+ public void setLocator(/*@Nullable*/ SourceLocator locator) {
+ if (locator != null) {
+ setPublicId(locator.getPublicId());
+ setSystemId(locator.getSystemId());
+ setLineNumber(locator.getLineNumber());
+ setColumnNumber(locator.getColumnNumber());
+ }
+ }
+
+ public void setSourceLocator(/*@Nullable*/ SourceLocator locator) {
+ if (locator != null) {
+ setPublicId(locator.getPublicId());
+ setSystemId(locator.getSystemId());
+ setLineNumber(locator.getLineNumber());
+ setColumnNumber(locator.getColumnNumber());
+ }
+ }
+
+ /*@NotNull*/ public SourceLocator getLocator() {
+ return this;
+ }
+
+ public void setErrorCode(String errorCode) {
+ this.errorCode = new StructuredQName("err", NamespaceConstant.ERR, errorCode);
+ }
+
+ public void setErrorCodeQName(StructuredQName errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ /*@Nullable*/ public String getErrorCode() {
+ return errorCode == null ? null : errorCode.getLocalPart();
+ }
+
+ /*@Nullable*/ public StructuredQName getErrorCodeQName() {
+ return errorCode;
+ }
+
+ /*@NotNull*/ public ValidationException makeException() {
+ ValidationException ve = new ValidationException(message, getLocator());
+ ve.setConstraintReference(schemaPart, constraintName, clause);
+ if (errorCode == null) {
+ ve.setErrorCode("FORG0001");
+ } else {
+ ve.setErrorCodeQName(errorCode);
+ }
+ return ve;
+ }
+
+ /*@Nullable*/ public ValidationException makeException(/*@Nullable*/ String contextMessage) {
+ ValidationException ve = new ValidationException((contextMessage == null ? message : contextMessage + message));
+ ve.setConstraintReference(schemaPart, constraintName, clause);
+ if (errorCode == null) {
+ ve.setErrorCode("FORG0001");
+ } else {
+ ve.setErrorCodeQName(errorCode);
+ }
+ return ve;
+ }
+
+
+ /**
+ * Calling this method on a ConversionResult returns the AtomicValue that results
+ * from the conversion if the conversion was successful, and throws a ValidationException
+ * explaining the conversion error otherwise.
+ * <p/>
+ * <p>Use this method if you are calling a conversion method that returns a ConversionResult,
+ * and if you want to throw an exception if the conversion fails.</p>
+ *
+ * @return the atomic value that results from the conversion if the conversion was successful
+ * @throws net.sf.saxon.type.ValidationException
+ * if the conversion was not successful
+ */
+
+ /*@NotNull*/ public AtomicValue asAtomic() throws ValidationException {
+ throw makeException();
+ }
+}
+
diff --git a/sf/saxon/type/ValidationParams.java b/sf/saxon/type/ValidationParams.java
new file mode 100644
index 0000000..3e1d84b
--- /dev/null
+++ b/sf/saxon/type/ValidationParams.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.type;
+
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+
+import java.util.HashMap;
+
+/**
+ * This class represents a collection of parameter values for use in schema validation;
+ * it defines values for the parameters declared using the saxon:param XSD extension.
+ *
+ * The implementation is just a HashMap; giving the class a name helps type safety.
+ */
+
+public class ValidationParams extends HashMap<StructuredQName, Sequence> {
+
+ public ValidationParams() {
+ super(20);
+ }
+
+}
+
diff --git a/sf/saxon/type/package.html b/sf/saxon/type/package.html
new file mode 100644
index 0000000..99b0511
--- /dev/null
+++ b/sf/saxon/type/package.html
@@ -0,0 +1,44 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.type</title>
+</head>
+
+<body>
+
+<p>This package contains classes that implement the XPath 2.0 type system.
+It contains that part of the functionality relevant to a non-schema-aware
+implementation: that is, the overall structure of the type system, together
+with representations of the built-in types.</p>
+
+<p>The hierarchy of schema types is represented by the interfaces
+<code>SchemaType</code>, <code>ComplexType</code>, <code>SimpleType</code>,
+<code>ListType</code>, and <code>AtomicType</code>. (Union types never arise
+in non-schema-aware processing). There are concrete classes representing
+built-in types such as <code>AnyType</code>, <code>BuiltInAtomicType</code>,
+and <code>BuiltInListType</code>: the corresponding classes for user-defined
+types are in the <code>com.saxonica.schema</code> package.</p>
+
+<p>The class <code>SequenceType</code> ought logically to be in this package
+but is actually in <code>net.sf.saxon.value</code>. A sequence type contains
+an <code>ItemType</code> which may be an <code>AtomicType</code> or a
+<code>NodeTest</code>: NodeTests are found in the package <code>net.sf.saxon.pattern</code>.</p>
+
+<p>The logic for performing type checking is partly in the singleton class
+<code>Type</code> (which also contains many useful constants), and partly in
+the class <code>TypeChecker</code> found in package <code>net.sf.saxon.expr</code>.</p>
+
+
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/value/AnyURIValue.java b/sf/saxon/value/AnyURIValue.java
new file mode 100644
index 0000000..a6e7683
--- /dev/null
+++ b/sf/saxon/value/AnyURIValue.java
@@ -0,0 +1,159 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+
+/**
+ * An XPath value of type xs:anyURI.
+ * <p/>
+ * <p>This is implemented as a subtype of StringValue even though xs:anyURI is not a subtype of
+ * xs:string in the XPath type hierarchy. This enables type promotion from URI to String to happen
+ * automatically in most cases where it is appropriate.</p>
+ * <p/>
+ * <p>This implementation of xs:anyURI allows any string to be contained in the value space. To check that
+ * the URI is valid according to some set of syntax rules, the caller should invoke a {@link net.sf.saxon.lib.StandardURIChecker}
+ * before constructing the AnyURIValue.</p>
+ */
+
+public final class AnyURIValue extends StringValue {
+
+ /*@NotNull*/ public static final AnyURIValue EMPTY_URI = new AnyURIValue("");
+
+
+ /**
+ * Constructor
+ * @param value the String value. Null is taken as equivalent to "". This constructor
+ * does not check that the value is a valid anyURI instance. It does however
+ * perform whitespace normalization.
+ */
+
+ public AnyURIValue(/*@Nullable*/ CharSequence value) {
+ this.value = (value == null ? "" : Whitespace.collapseWhitespace(value).toString());
+ typeLabel = BuiltInAtomicType.ANY_URI;
+ }
+
+ /**
+ * Constructor for a user-defined subtype of anyURI
+ * @param value the String value. Null is taken as equivalent to "".
+ * @param type a user-defined subtype of anyURI. It is the caller's responsibility
+ * to ensure that this is actually a subtype of anyURI, and that the value conforms
+ * to the definition of this type.
+ */
+
+ public AnyURIValue(/*@Nullable*/ CharSequence value, AtomicType type) {
+ this.value = (value == null ? "" : Whitespace.collapseWhitespace(value).toString());
+ typeLabel = type;
+ }
+
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ AnyURIValue v = new AnyURIValue(value);
+ v.unicodeString = unicodeString;
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /*@NotNull*/ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.ANY_URI;
+ }
+
+
+ /*@Nullable*/ public static String decode(/*@Nullable*/ String s) {
+ // Evaluates all escapes in s, applying UTF-8 decoding if needed. Assumes
+ // that escapes are well-formed syntactically, i.e., of the form %XX. If a
+ // sequence of escaped octets is not valid UTF-8 then the erroneous octets
+ // are replaced with '\uFFFD'.
+ // Exception: any "%" found between "[]" is left alone. It is an IPv6 literal
+ // with a scope_id
+ //
+
+ if (s == null) {
+ return s;
+ }
+ int n = s.length();
+ if (n == 0) {
+ return s;
+ }
+ if (s.indexOf('%') < 0) {
+ return s;
+ }
+
+ FastStringBuffer sb = new FastStringBuffer(n);
+ ByteBuffer bb = ByteBuffer.allocate(n);
+ Charset utf8 = Charset.forName("UTF-8");
+
+ // This is not horribly efficient, but it will do for now
+ char c = s.charAt(0);
+ boolean betweenBrackets = false;
+
+ for (int i = 0; i < n;) {
+ assert c == s.charAt(i); // Loop invariant
+ if (c == '[') {
+ betweenBrackets = true;
+ } else if (betweenBrackets && c == ']') {
+ betweenBrackets = false;
+ }
+ if (c != '%' || betweenBrackets) {
+ sb.append(c);
+ if (++i >= n) {
+ break;
+ }
+ c = s.charAt(i);
+ continue;
+ }
+ bb.clear();
+ for (; ;) {
+ assert (n - i >= 2);
+ bb.put(hex(s.charAt(++i), s.charAt(++i)));
+ if (++i >= n) {
+ break;
+ }
+ c = s.charAt(i);
+ if (c != '%') {
+ break;
+ }
+ }
+ bb.flip();
+ sb.append(utf8.decode(bb));
+ }
+
+ return sb.toString();
+ }
+
+ private static byte hex(char high, char low) {
+ return (byte)((hexToDec(high)<<4) | hexToDec(low));
+ }
+
+ private static int hexToDec(char c) {
+ if (c >= '0' && c <= '9') {
+ return c - '0';
+ } else if (c >= 'a' && c <= 'f') {
+ return c - 'a' + 10;
+ } else if (c >= 'A' && c <= 'F') {
+ return c - 'A' + 10;
+ } else {
+ return 0;
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/value/AtomicValue.java b/sf/saxon/value/AtomicValue.java
new file mode 100644
index 0000000..71cdb35
--- /dev/null
+++ b/sf/saxon/value/AtomicValue.java
@@ -0,0 +1,386 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.type.*;
+
+
+/**
+ * The AtomicValue class corresponds to the concept of an atomic value in the
+ * XPath 2.0 data model. Atomic values belong to one of the 19 primitive types
+ * defined in XML Schema; or they are of type xs:untypedAtomic; or they are
+ * "external objects", representing a Saxon extension to the XPath 2.0 type system.
+ * <p/>
+ * The AtomicValue class contains some methods that are suitable for applications
+ * to use, and many others that are designed for internal use by Saxon itself.
+ * These have not been fully classified. At present, therefore, none of the methods on this
+ * class should be considered to be part of the public Saxon API.
+ * <p/>
+ *
+ * @author Michael H. Kay
+ */
+
+public abstract class AtomicValue extends AbstractItem
+ implements AtomicSequence, ConversionResult, IdentityComparable {
+
+ protected AtomicType typeLabel;
+
+ /**
+ * To implement {@link net.sf.saxon.om.Sequence}, this method returns the item itself
+ * @return this item
+ */
+
+ public final AtomicValue head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link net.sf.saxon.om.Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ * @return a singleton iterator that returns this item
+ */
+
+ public final SequenceIterator<AtomicValue> iterate() {
+ return SingletonIterator.makeIterator(this);
+ }
+
+ /**
+ * Set the type label on this atomic value. Note that this modifies the value, so it must only called
+ * if the caller is confident that the value is not shared. In other cases,
+ * use {@link #copyAsSubType(net.sf.saxon.type.AtomicType)}
+ *
+ * @param type the type label to be set
+ */
+
+ public void setTypeLabel(AtomicType type) {
+ typeLabel = type;
+ }
+
+
+ /**
+ * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
+ * An implementation must be provided for all atomic types.
+ * <p/>
+ * <p>In the case of data types that are partially ordered, the returned Comparable extends the standard
+ * semantics of the compareTo() method by returning the value {@link SequenceTool#INDETERMINATE_ORDERING} when there
+ * is no defined order relationship between two given values. This value is also returned when two values
+ * of different types are compared.</p>
+ *
+ * @return a Comparable that follows XML Schema comparison rules
+ */
+
+ public abstract Comparable getSchemaComparable();
+
+ /**
+ * Get an object value that implements the XPath equality and ordering comparison semantics for this value.
+ * If the ordered parameter is set to true, the result will be a Comparable and will support a compareTo()
+ * method with the semantics of the XPath lt/gt operator, provided that the other operand is also obtained
+ * using the getXPathComparable() method. In all cases the result will support equals() and hashCode() methods
+ * that support the semantics of the XPath eq operator, again provided that the other operand is also obtained
+ * using the getXPathComparable() method. A context argument is supplied for use in cases where the comparison
+ * semantics are context-sensitive, for example where they depend on the implicit timezone or the default
+ * collation.
+ * @param ordered true if an ordered comparison is required. In this case the result is null if the
+ * type is unordered; in other cases the returned value will be a Comparable.
+ * @param collator the collation to be used when comparing strings
+ * @param context the XPath dynamic evaluation context, used in cases where the comparison is context
+ * sensitive
+ * @return an Object whose equals() and hashCode() methods implement the XPath comparison semantics
+ * with respect to this atomic value. If ordered is specified, the result will either be null if
+ * no ordering is defined, or will be a Comparable
+ * @throws NoDynamicContextException if the comparison depends on dynamic context information that
+ * is not available, for example implicit timezone
+ */
+
+ public abstract Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context)
+ throws NoDynamicContextException;
+
+ /**
+ * The equals() methods on atomic values is defined to follow the semantics of eq when applied
+ * to two atomic values. When the other operand is not an atomic value, the result is undefined
+ * (may be false, may be an exception). When the other operand is an atomic value that cannot be
+ * compared with this one, the method must throw a ClassCastException.
+ *
+ * <p>The hashCode() method is consistent with equals().</p>
+ * @param o the other value
+ * @return true if the other operand is an atomic value and the two values are equal as defined
+ * by the XPath eq operator
+ */
+
+ public abstract boolean equals(Object o);
+
+ /**
+ * Determine whether two atomic values are identical, as determined by XML Schema rules. This is a stronger
+ * test than equality (even schema-equality); for example two dateTime values are not identical unless
+ * they are in the same timezone.
+ * <p>Note that even this check ignores the type annotation of the value. The integer 3 and the short 3
+ * are considered identical, even though they are not fully interchangeable. "Identical" means the
+ * same point in the value space, regardless of type annotation.</p>
+ * <p>NaN is identical to itself.</p>
+ *
+ *
+ * @param v the other value to be compared with this one
+ * @return true if the two values are identical, false otherwise.
+ */
+
+ public boolean isIdentical(/*@NotNull*/ AtomicValue v) {
+ // default implementation
+ return getSchemaComparable().equals(v.getSchemaComparable());
+ }
+
+ /**
+ * Determine whether two IdentityComparable values are identical. This is a stronger
+ * test than equality (even schema-equality); for example two dateTime values are not identical unless
+ * they are in the same timezone.
+ * @param other
+ * @return true if the two values are identical, false otherwise
+ */
+
+ public boolean isIdentical(IdentityComparable other) {
+ if(other instanceof AtomicValue) {
+ return isIdentical((AtomicValue) other);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public final CharSequence getStringValueCS() {
+ CharSequence cs = getPrimitiveStringValue();
+ try {
+ return typeLabel.postprocess(cs);
+ } catch (XPathException err) {
+ // Ignore any XPath errors that occur during postprocessing
+ return cs;
+ }
+ }
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules.
+ *
+ * @return the canonical lexical representation if defined in XML Schema; otherwise, the result
+ * of casting to string according to the XPath 2.0 rules
+ */
+ public CharSequence getCanonicalLexicalRepresentation() {
+ return getStringValueCS();
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ * @throws XPathException if the current receiver fails for any reason, for example
+ * with a serialization error due to invalid characters in the content
+ */
+
+ public void process(/*@NotNull*/ XPathContext context) throws XPathException {
+ context.getReceiver().append(this, 0, NodeInfo.ALL_NAMESPACES);
+ }
+
+ /**
+ * Get the n'th item in the sequence (starting from 0). This is defined for all
+ * Values, but its real benefits come for a sequence Value stored extensionally
+ * (or for a MemoClosure, once all the values have been read)
+ *
+ * @param n position of the required item, counting from zero.
+ * @return the n'th item in the sequence, where the first item in the sequence is
+ * numbered zero. If n is negative or >= the length of the sequence, returns null.
+ */
+
+ /*@Nullable*/ public final AtomicValue itemAt(int n) {
+ return (n == 0 ? this : null);
+ }
+
+
+ /**
+ * Determine the data type of the value
+ * @return the type annotation of the atomic value
+ */
+
+ /*@NotNull*/
+ public final AtomicType getItemType() {
+ return typeLabel;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is xs:anyAtomicType.
+ *
+ * @return the primitive type
+ */
+
+ public abstract BuiltInAtomicType getPrimitiveType();
+
+ /**
+ * Determine the static cardinality
+ *
+ * @return code identifying the cardinality
+ * @see net.sf.saxon.value.Cardinality
+ */
+
+ public final int getCardinality() {
+ return StaticProperty.EXACTLY_ONE;
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ * @return the copied value
+ */
+
+ public abstract AtomicValue copyAsSubType(AtomicType typeLabel);
+
+ /**
+ * Test whether the value is the special value NaN
+ * @return true if the value is float NaN or double NaN or precisionDecimal NaN; otherwise false
+ */
+
+ public boolean isNaN() {
+ return false;
+ }
+
+ /**
+ * Convert the value to a string, using the serialization rules.
+ * For atomic values this is the same as a cast; for sequence values
+ * it gives a space-separated list. This method is refined for AtomicValues
+ * so that it never throws an Exception.
+ */
+
+ public final String getStringValue() {
+ return getStringValueCS().toString();
+ }
+
+ /**
+ * Convert the value to a string, using the serialization rules for the primitive type.
+ * This is the result of conversion to a string except that postprocessing defined by the
+ * saxon:preprocess facet is not (yet) applied.
+ * @return the value converted to a string according to the rules for the primitive type
+ */
+
+ protected abstract CharSequence getPrimitiveStringValue();
+
+
+ /**
+ * Get the effective boolean value of the value
+ *
+ * @return true, unless the value is boolean false, numeric zero, or
+ * zero-length string
+ * @throws XPathException if effective boolean value is not defined for this type (the default behaviour)
+ */
+ public boolean effectiveBooleanValue() throws XPathException {
+ XPathException err = new XPathException("Effective boolean value is not defined for an atomic value of type " +
+ Type.displayTypeName(this));
+ err.setIsTypeError(true);
+ err.setErrorCode("FORG0006");
+ throw err;
+ // unless otherwise specified in a subclass
+ }
+
+ /**
+ * Method to extract components of a value. Implemented by some subclasses,
+ * but defined at this level for convenience
+ *
+ * @param component identifies the required component, as a constant defined in class
+ * {@link net.sf.saxon.functions.Component}, for example {@link net.sf.saxon.functions.Component#HOURS}
+ * @return the value of the requested component of this value
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ * @throws UnsupportedOperationException if applied to a value of a type that has no components
+ */
+
+ public AtomicValue getComponent(int component) throws XPathException {
+ throw new UnsupportedOperationException("Data type does not support component extraction");
+ }
+
+ /**
+ * Check statically that the results of the expression are capable of constructing the content
+ * of a given schema type.
+ *
+ * @param parentType The schema type
+ * @param env the static context
+ * @param whole true if this atomic value accounts for the entire content of the containing node
+ * @throws net.sf.saxon.trans.XPathException
+ * if the expression doesn't match the required content type
+ */
+
+ public void checkPermittedContents(/*@NotNull*/ SchemaType parentType, /*@NotNull*/ StaticContext env, boolean whole) throws XPathException {
+ if (whole) {
+ SimpleType stype = null;
+ if (parentType instanceof SimpleType) {
+ stype = (SimpleType)parentType;
+ } else if (parentType instanceof ComplexType && ((ComplexType)parentType).isSimpleContent()) {
+ stype = ((ComplexType)parentType).getSimpleContentType();
+ }
+ if (stype != null && !stype.isNamespaceSensitive()) {
+ // Can't validate namespace-sensitive content statically
+ ValidationFailure err = stype.validateContent(
+ getStringValueCS(), null, env.getConfiguration().getConversionRules());
+ if (err != null) {
+ throw err.makeException();
+ }
+ return;
+ }
+ }
+ if (parentType instanceof ComplexType &&
+ !((ComplexType)parentType).isSimpleContent() &&
+ !((ComplexType)parentType).isMixedContent() &&
+ !Whitespace.isWhite(getStringValueCS())) {
+ XPathException err = new XPathException("Complex type " + parentType.getDescription() +
+ " does not allow text content " +
+ Err.wrap(getStringValueCS()));
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+
+
+ /**
+ * Calling this method on a ConversionResult returns the AtomicValue that results
+ * from the conversion if the conversion was successful, and throws a ValidationException
+ * explaining the conversion error otherwise.
+ * <p/>
+ * <p>Use this method if you are calling a conversion method that returns a ConversionResult,
+ * and if you want to throw an exception if the conversion fails.</p>
+ *
+ * @return the atomic value that results from the conversion if the conversion was successful
+ */
+
+ /*@NotNull*/ public AtomicValue asAtomic() {
+ return this;
+ }
+
+ /**
+ * Get string value. In general toString() for an atomic value displays the value as it would be
+ * written in XPath: that is, as a literal if available, or as a call on a constructor function
+ * otherwise.
+ */
+
+ public String toString() {
+ return typeLabel.toString() + " (\"" + getStringValueCS() + "\")";
+ }
+
+}
+
diff --git a/sf/saxon/value/Base64BinaryValue.java b/sf/saxon/value/Base64BinaryValue.java
new file mode 100644
index 0000000..8e02f14
--- /dev/null
+++ b/sf/saxon/value/Base64BinaryValue.java
@@ -0,0 +1,339 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+
+import java.util.Arrays;
+
+/**
+ * A value of type xs:base64Binary
+ *
+ * <p><i>Rewritten for Saxon 9.5 to avoid dependency on the open-source Netscape code, whose
+ * license many users were unhappy with.</i></p>
+ */
+
+public class Base64BinaryValue extends AtomicValue {
+
+ private byte[] binaryValue;
+
+
+ /**
+ * Constructor: create a base64Binary value from a supplied string in base64 encoding
+ * @param s the lexical representation of the base64 binary value. There is no requirement
+ * that whitespace should already be collapsed.
+ * @throws net.sf.saxon.trans.XPathException if the supplied value is not in the lexical
+ * space of the xs:base64Binary data type
+ */
+
+ public Base64BinaryValue(/*@NotNull*/ CharSequence s) throws XPathException {
+ binaryValue = decode(s);
+ typeLabel = BuiltInAtomicType.BASE64_BINARY;
+ }
+
+ /**
+ * Constructor: create a base64Binary value from a given array of bytes
+ * @param value array of bytes holding the octet sequence
+ */
+
+ public Base64BinaryValue(byte[] value) {
+ binaryValue = value;
+ typeLabel = BuiltInAtomicType.BASE64_BINARY;
+ }
+
+ /**
+ * Create a copy of this atomic value (usually so that the type label can be changed).
+ * The type label of the copy will be reset to the primitive type.
+ * @param typeLabel the type label to be attached to the value, a subtype of xs:base64Binary
+ * @return the copied value
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ Base64BinaryValue v = new Base64BinaryValue(binaryValue);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Get the binary value
+ * @return the octet sequence that is the typed value
+ */
+
+ public byte[] getBinaryValue() {
+ return binaryValue;
+ }
+
+ /*@NotNull*/ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.BASE64_BINARY;
+ }
+
+ /**
+ * Convert to string
+ * @return the canonical representation.
+ */
+
+ /*@NotNull*/ public String getPrimitiveStringValue() {
+ return encode(binaryValue).toString();
+ }
+
+ /**
+ * Get the number of octets in the value
+ * @return the number of octets
+ */
+
+ public int getLengthInOctets() {
+ return binaryValue.length;
+ }
+
+ /**
+ * Support XML Schema comparison semantics
+ */
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return new Base64BinaryComparable();
+ }
+
+ /**
+ * Private inner class to support XML Schema comparison semantics
+ */
+
+ private class Base64BinaryComparable implements Comparable {
+
+ /*@NotNull*/ public Base64BinaryValue getBase64BinaryValue() {
+ return Base64BinaryValue.this;
+ }
+
+ public int compareTo(/*@NotNull*/ Object o) {
+ if (o instanceof Base64BinaryComparable &&
+ Arrays.equals(getBase64BinaryValue().binaryValue,
+ ((Base64BinaryComparable)o).getBase64BinaryValue().binaryValue)) {
+ return 0;
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ }
+
+ @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
+ public boolean equals(/*@NotNull*/ Object o) {
+ return compareTo(o) == 0;
+ }
+
+ public int hashCode() {
+ return Base64BinaryValue.this.hashCode();
+ }
+ }
+
+
+ /**
+ * Get an object value that implements the XPath equality and ordering comparison semantics for this value.
+ * If the ordered parameter is set to true, the result will be a Comparable and will support a compareTo()
+ * method with the semantics of the XPath lt/gt operator, provided that the other operand is also obtained
+ * using the getXPathComparable() method. In all cases the result will support equals() and hashCode() methods
+ * that support the semantics of the XPath eq operator, again provided that the other operand is also obtained
+ * using the getXPathComparable() method. A context argument is supplied for use in cases where the comparison
+ * semantics are context-sensitive, for example where they depend on the implicit timezone or the default
+ * collation.
+ *
+ * @param ordered true if an ordered comparison is required. In this case the result is null if the
+ * type is unordered; in other cases the returned value will be a Comparable.
+ * @param collator the collation (not used in this version of the method)
+ * @param context the XPath dynamic evaluation context, used in cases where the comparison is context
+* sensitive @return an Object whose equals() and hashCode() methods implement the XPath comparison semantics
+ */
+
+ /*@Nullable*/ public Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context) {
+ return (ordered ? null : this);
+ }
+
+ /**
+ * Test if the two base64Binary values are equal.
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ return other instanceof Base64BinaryValue
+ && Arrays.equals(binaryValue, ((Base64BinaryValue) other).binaryValue);
+ }
+
+ public int hashCode() {
+ return byteArrayHashCode(binaryValue);
+ }
+
+ protected static int byteArrayHashCode(/*@NotNull*/ byte[] value) {
+ long h = 0;
+ for (int i=0; i<Math.min(value.length, 64); i++) {
+ h = (h<<1) ^ value[i];
+ }
+ return (int)((h >> 32) ^ h);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // Code for converting to/from base64 representation
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ private final static String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ private static int[] encoding = new int[64];
+ private static int[] decoding = new int[128];
+
+ static {
+ Arrays.fill(decoding, -1);
+ for (int i=0; i<alphabet.length(); i++) {
+ char c = alphabet.charAt(i);
+ encoding[i] = c;
+ decoding[c] = i;
+ }
+ }
+
+ /**
+ * Encode a byte sequence into base64 representation
+ * @param value the byte sequence
+ * @return the base64 representation
+ */
+
+ public static CharSequence encode(byte[] value) {
+ FastStringBuffer buff = new FastStringBuffer(value.length);
+ int whole = value.length - value.length % 3;
+ // process bytes 3 at a time: 3 bytes => 4 characters
+ for (int i=0; i<whole; i+=3) {
+ // 3 bytes = 24 bits = 4 characters
+ int val = ((((int)value[i])&0xff)<<16) + ((((int)value[i+1])&0xff)<<8) + ((((int)value[i+2])&0xff));
+ buff.append((char)encoding[(val>>18)&0x3f]);
+ buff.append((char)encoding[(val>>12)&0x3f]);
+ buff.append((char)encoding[(val>>6)&0x3f]);
+ buff.append((char)encoding[val&0x3f]);
+ }
+ int remainder = (value.length%3);
+ switch (remainder) {
+ case 0:
+ default:
+ // no action
+ break;
+ case 1: {
+ // pad the final 8 bits to 12 (2 groups of 6)
+ int val = ((((int)value[whole])&0xff)<<4);
+ buff.append((char)encoding[(val>>6)&0x3f]);
+ buff.append((char)encoding[val&0x3f]);
+ buff.append("==");
+ break;
+ }
+ case 2: {
+ // pad the final 16 bits to 18 (3 groups of 6)
+ int val = ((((int)value[whole])&0xff)<<10) + ((((int)value[whole+1])&0xff)<<2);
+ buff.append((char)encoding[(val>>12)&0x3f]);
+ buff.append((char)encoding[(val>>6)&0x3f]);
+ buff.append((char)encoding[val&0x3f]);
+ buff.append("=");
+ break;
+ }
+ }
+ return buff.condense();
+ }
+
+ /**
+ * Decode a character string in base64 notation to yield the encoded octets
+ * @param in the lexical representation
+ * @return the array of octets represented
+ * @throws XPathException if the format is invalid (as required by XSD, this method
+ * does draconian error handling, unlike many other base64 decoders which are liberal
+ * in what they accept)
+ */
+
+ public static byte[] decode(CharSequence in) throws XPathException {
+ char[] unit = new char[4];
+ byte[] result = new byte[in.length()];
+ int bytesUsed = 0;
+ int i = 0;
+ int u = 0;
+ int pad = 0;
+ int chars = 0;
+ char last = 0;
+
+ // process characters 4 at a time: 4 characters => 3 bytes
+ while (i < in.length()) {
+ char c = in.charAt(i++);
+ if (!Whitespace.isWhite(c)) {
+ chars++;
+ if (c == '=') {
+ // all following chars must be '=' or whitespace
+ pad = 1;
+ for (int k=i; k<in.length(); k++) {
+ char ch = in.charAt(k);
+ if (ch == '=') {
+ pad++;
+ chars++;
+ } else if (Whitespace.isWhite(ch)) {
+ // no action
+ } else {
+ throw new XPathException("Base64 padding character '=' is followed by non-padding characters", "FORG0001");
+ }
+ }
+ if (pad == 1 && "AEIMQUYcgkosw048".indexOf(last) < 0) {
+ throw new XPathException("In base64, if the value ends with a single '=' character, then the preceding character must be" +
+ " one of [AEIMQUYcgkosw048]", "FORG0001");
+ } else if (pad == 2 && "AQgw".indexOf(last) < 0) {
+ throw new XPathException("In base64, if the value ends with '==', then the preceding character must be" +
+ " one of [AQgw]", "FORG0001");
+ }
+ // number of padding characters must be the number required
+ if (pad != ((4-u)%4) || pad > 2) {
+ throw new XPathException("Required " + ((4-u)%4) + " padding characters at end of base64 value; found " + pad, "FORG0001");
+ }
+ // append 0 sextets corresponding to number of padding characters
+ for (int p=0; p<pad; p++) {
+ unit[u++] = 'A';
+ }
+ i = in.length();
+ } else {
+ last = c;
+ unit[u++] = c;
+ }
+ if (u == 4) {
+ int t = (decodeChar(unit[0])<<18) +
+ (decodeChar(unit[1])<<12) +
+ (decodeChar(unit[2])<<6) +
+ (decodeChar(unit[3]));
+ if (bytesUsed + 3 > result.length) {
+ byte[] r2 = new byte[bytesUsed*2];
+ System.arraycopy(result, 0, r2, 0, bytesUsed);
+ result = r2;
+ }
+ result[bytesUsed++] = (byte)((t>>16) & 0xff);
+ result[bytesUsed++] = (byte)((t>>8) & 0xff);
+ result[bytesUsed++] = (byte)(t & 0xff);
+ u = 0;
+ }
+ }
+ if (i >= in.length()) {
+ bytesUsed -= pad;
+ break;
+ }
+ }
+ if (chars % 4 != 0) {
+ throw new XPathException("Length of base64 value must be a multiple of four", "FORG0001");
+ }
+ byte[] r3 = new byte[bytesUsed];
+ System.arraycopy(result, 0, r3, 0, bytesUsed);
+ return r3;
+
+ }
+
+ private static int decodeChar(char c) throws XPathException {
+ int d = decoding[c];
+ if (d == -1) {
+ throw new XPathException("Invalid character '" + c + "' in base64 value", "FORG0001");
+ }
+ return d;
+ }
+}
+
diff --git a/sf/saxon/value/BigIntegerValue.java b/sf/saxon/value/BigIntegerValue.java
new file mode 100644
index 0000000..d81c73f
--- /dev/null
+++ b/sf/saxon/value/BigIntegerValue.java
@@ -0,0 +1,575 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.Calculator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * An integer value: note this is a subtype of decimal in XML Schema, not a primitive type.
+ * The abstract class IntegerValue is used to represent any xs:integer value; this implementation
+ * is used for values that do not fit comfortably in a Java long; including the built-in subtype xs:unsignedLong
+ */
+
+public final class BigIntegerValue extends IntegerValue {
+
+ private BigInteger value;
+
+ private static final BigInteger MAX_INT = BigInteger.valueOf(Integer.MAX_VALUE);
+ private static final BigInteger MIN_INT = BigInteger.valueOf(Integer.MIN_VALUE);
+ public static final BigInteger MAX_LONG = BigInteger.valueOf(Long.MAX_VALUE);
+ public static final BigInteger MIN_LONG = BigInteger.valueOf(Long.MIN_VALUE);
+ /*@NotNull*/ public static final BigInteger MAX_UNSIGNED_LONG = new BigInteger("18446744073709551615");
+ /*@NotNull*/ public static final BigIntegerValue ZERO = new BigIntegerValue(BigInteger.ZERO);
+
+ /**
+ * Construct an xs:integer value from a Java BigInteger
+ * @param value the supplied BigInteger
+ */
+
+ public BigIntegerValue(BigInteger value) {
+ this.value = value;
+ typeLabel = BuiltInAtomicType.INTEGER;
+ }
+
+ /**
+ * Construct an xs:integer value from a Java BigInteger, supplying a type label.
+ * It is the caller's responsibility to ensure that the supplied value conforms
+ * with the rules for the specified type.
+ * @param value the value of the integer
+ * @param typeLabel the type, which must represent a type derived from xs:integer
+ */
+
+ public BigIntegerValue(BigInteger value, AtomicType typeLabel) {
+ this.value = value;
+ this.typeLabel = typeLabel;
+ }
+
+ /**
+ * Construct an xs:integer value from a Java long. Note: normally, if the value fits in a long,
+ * then an Int64Value should be used. This constructor is largely for internal use, when operations
+ * are required that require two integers to use the same implementation class to be used.
+ * @param value the supplied Java long
+ */
+
+ public BigIntegerValue(long value) {
+ this.value = BigInteger.valueOf(value);
+ typeLabel = BuiltInAtomicType.INTEGER;
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(/*@NotNull*/ AtomicType typeLabel) {
+ if (typeLabel.getPrimitiveType() == StandardNames.XS_INTEGER) {
+ BigIntegerValue v = new BigIntegerValue(value);
+ v.typeLabel = typeLabel;
+ return v;
+ } else {
+ return new DecimalValue(new BigDecimal(value));
+ }
+
+ }
+
+ /**
+ * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
+ * This method sets the required type label. Note that this method modifies the value in situ.
+ * @param type the subtype of integer required
+ * @return null if the operation succeeds, or a ValidationException if the value is out of range
+ */
+
+ /*@Nullable*/ public ValidationFailure convertToSubType(/*@NotNull*/ BuiltInAtomicType type, boolean validate) {
+ if (!validate) {
+ typeLabel = type;
+ return null;
+ }
+ if (IntegerValue.checkBigRange(value, type)) {
+ typeLabel = type;
+ return null;
+ } else {
+ ValidationFailure err = new ValidationFailure(
+ "Integer value is out of range for subtype " + type.getDisplayName());
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+ }
+
+
+ /**
+ * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
+ * This method checks that the value is valid against the rules for a given subtype.
+ *
+ * @param type the subtype of integer required
+ * @return null if the operation succeeds, or a ValidationException if the value is out of range
+ */
+
+ /*@Nullable*/ public ValidationFailure validateAgainstSubType(/*@NotNull*/ BuiltInAtomicType type) {
+ if (IntegerValue.checkBigRange(value, type)) {
+ typeLabel = type;
+ return null;
+ } else {
+ ValidationFailure err = new ValidationFailure(
+ "Integer value is out of range for subtype " + type.getDisplayName());
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+ }
+
+ /**
+ * Get the hashCode. This must conform to the rules for other NumericValue hashcodes
+ * @see NumericValue#hashCode
+ */
+
+ public int hashCode() {
+ if (value.compareTo(MIN_INT) >= 0 && value.compareTo(MAX_INT) <= 0) {
+ return value.intValue();
+ } else {
+ return new Double(getDoubleValue()).hashCode();
+ }
+ }
+
+ /**
+ * Get the value as a long
+ *
+ * @return the value of the xs:integer, as a Java long
+ */
+
+ public long longValue() {
+ return value.longValue();
+ }
+
+ /**
+ * Get the value as a BigInteger
+ * @return the value of the xs:integer as a Java BigInteger
+ */
+
+ public BigInteger asBigInteger() {
+ return value;
+ }
+
+ /**
+ * Test whether the value is within the range that can be held in a 64-bit signed integer
+ * @return true if the value is within range for a long
+ */
+
+ public boolean isWithinLongRange() {
+ return value.compareTo(MIN_LONG) >= 0 && value.compareTo(MAX_LONG) <= 0;
+ }
+
+ /**
+ * Convert the value to a BigDecimal
+ * @return the resulting BigDecimal
+ */
+
+ /*@NotNull*/ public BigDecimal asDecimal() {
+ return new BigDecimal(value);
+ }
+
+ /**
+ * Return the effective boolean value of this integer
+ *
+ * @return false if the integer is zero, otherwise true
+ */
+ public boolean effectiveBooleanValue() {
+ return value.compareTo(BigInteger.ZERO) != 0;
+ }
+
+ /**
+ * Compare the value to another numeric value
+ *
+ * @param other the numeric value to be compared to this value
+ * @return -1 if this value is less than the other, 0 if they are equal,
+ * +1 if this value is greater
+ */
+
+ public int compareTo(/*@NotNull*/ Object other) {
+ if (other instanceof BigIntegerValue) {
+ return value.compareTo(((BigIntegerValue)other).value);
+ } else if (other instanceof Int64Value) {
+ return value.compareTo(BigInteger.valueOf(((Int64Value)other).longValue()));
+ } else if (other instanceof DecimalValue) {
+ return asDecimal().compareTo(((DecimalValue)other).getDecimalValue());
+ } else {
+ return super.compareTo(other);
+ }
+ }
+
+ /**
+ * Compare the value to a long
+ * @param other the value to be compared with
+ * @return -1 if this is less, 0 if this is equal, +1 if this is greater or if this is NaN
+ */
+
+ public int compareTo(long other) {
+ if (other == 0) {
+ return value.signum();
+ }
+ return value.compareTo((BigInteger.valueOf(other)));
+ }
+
+
+ /**
+ * Get the value as a String
+ * @return a String representation of the value
+ */
+
+ public String getPrimitiveStringValue() {
+ return value.toString();
+ }
+
+ /**
+ * Get the numeric value as a double
+ *
+ * @return A double representing this numeric value; NaN if it cannot be
+ * converted
+ */
+ public double getDoubleValue() {
+ return value.doubleValue();
+ }
+
+ /**
+ * Get the numeric value converted to a decimal
+ *
+ * @return a decimal representing this numeric value;
+ */
+
+ /*@NotNull*/ public BigDecimal getDecimalValue() {
+ return new BigDecimal(value);
+ }
+
+ /**
+ * Get the numeric value converted to a float
+ *
+ * @return a float representing this numeric value; NaN if it cannot be converted
+ */
+ @Override
+ public float getFloatValue() {
+ return (float)getDoubleValue();
+ }
+
+ /**
+ * Negate the value
+ * @return the result of inverting the sign of the value
+ */
+
+ /*@NotNull*/ public NumericValue negate() {
+ return new BigIntegerValue(value.negate());
+ }
+
+ /**
+ * Implement the XPath floor() function
+ * @return the integer value, unchanged
+ */
+
+ /*@NotNull*/ public NumericValue floor() {
+ return this;
+ }
+
+ /**
+ * Implement the XPath ceiling() function
+ * @return the integer value, unchanged
+ */
+
+ /*@NotNull*/ public NumericValue ceiling() {
+ return this;
+ }
+
+ /**
+ * Implement the XPath round() function
+ * @return the integer value, unchanged
+ */
+
+ public NumericValue round(int scale) {
+ if (scale >= 0) {
+ return this;
+ } else {
+ BigInteger factor = BigInteger.valueOf(10).pow(-scale);
+ BigInteger[] pair = value.divideAndRemainder(factor);
+ int up = pair[1].compareTo(factor.divide(BigInteger.valueOf(2)));
+ if (up >= 0) {
+ // remainder is > .5
+ pair[0] = pair[0].add(BigInteger.valueOf(1));
+ }
+ return makeIntegerValue(pair[0].multiply(factor));
+ }
+ }
+
+ /**
+ * Implement the XPath round-to-half-even() function
+ *
+ * @param scale number of digits required after the decimal point; the
+ * value -2 (for example) means round to a multiple of 100
+ * @return if the scale is >=0, return this value unchanged. Otherwise
+ * round it to a multiple of 10**-scale
+ */
+
+ public NumericValue roundHalfToEven(int scale) {
+ if (scale >= 0) {
+ return this;
+ } else {
+ BigInteger factor = BigInteger.valueOf(10).pow(-scale);
+ BigInteger[] pair = value.divideAndRemainder(factor);
+ int up = pair[1].compareTo(factor.divide(BigInteger.valueOf(2)));
+ if (up > 0) {
+ // remainder is > .5
+ pair[0] = pair[0].add(BigInteger.valueOf(1));
+ } else if (up == 0) {
+ // remainder == .5
+ if (pair[0].mod(BigInteger.valueOf(2)).signum() != 0) {
+ // last digit of quotient is odd: make it even
+ pair[0] = pair[0].add(BigInteger.valueOf(1));
+ }
+ }
+ return makeIntegerValue(pair[0].multiply(factor));
+ }
+ }
+
+ /**
+ * Determine whether the value is negative, zero, or positive
+ * @return -1 if negative, 0 if zero, +1 if positive, NaN if NaN
+ */
+
+ public int signum() {
+ return value.signum();
+ }
+
+ /**
+ * Get the absolute value as defined by the XPath abs() function
+ * @return the absolute value
+ */
+
+ /*@NotNull*/ public NumericValue abs() {
+ if (value.signum() >= 0) {
+ return this;
+ } else {
+ return new BigIntegerValue(value.abs());
+ }
+ }
+
+ /**
+ * Determine whether the value is a whole number, that is, whether it compares
+ * equal to some integer
+ *
+ * @return always true for this implementation
+ */
+
+ public boolean isWholeNumber() {
+ return true;
+ }
+
+ /**
+ * Add another integer
+ */
+
+ public IntegerValue plus(/*@NotNull*/ IntegerValue other) {
+ if (other instanceof BigIntegerValue) {
+ return makeIntegerValue(value.add(((BigIntegerValue)other).value));
+ } else {
+ //noinspection RedundantCast
+ return makeIntegerValue(value.add(BigInteger.valueOf(((Int64Value)other).longValue())));
+ }
+ }
+
+ /**
+ * Subtract another integer
+ */
+
+ public IntegerValue minus(/*@NotNull*/ IntegerValue other) {
+ if (other instanceof BigIntegerValue) {
+ return makeIntegerValue(value.subtract(((BigIntegerValue)other).value));
+ } else {
+ //noinspection RedundantCast
+ return makeIntegerValue(value.subtract(BigInteger.valueOf(((Int64Value)other).longValue())));
+ }
+ }
+
+ /**
+ * Multiply by another integer
+ */
+
+ public IntegerValue times(/*@NotNull*/ IntegerValue other) {
+ if (other instanceof BigIntegerValue) {
+ return makeIntegerValue(value.multiply(((BigIntegerValue)other).value));
+ } else {
+ //noinspection RedundantCast
+ return makeIntegerValue(value.multiply(BigInteger.valueOf(((Int64Value)other).longValue())));
+ }
+ }
+
+ /**
+ * Divide by another integer
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if the other integer is zero
+ */
+
+ public NumericValue div(/*@NotNull*/ IntegerValue other) throws XPathException {
+ BigInteger oi;
+ if (other instanceof BigIntegerValue) {
+ oi = ((BigIntegerValue)other).value;
+ } else {
+ oi = BigInteger.valueOf(other.longValue());
+ }
+ DecimalValue a = new DecimalValue(new BigDecimal(value));
+ DecimalValue b = new DecimalValue(new BigDecimal(oi));
+ return Calculator.decimalDivide(a, b);
+ }
+
+ /**
+ * Take modulo another integer
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if the other integer is zero
+ */
+
+ public IntegerValue mod(/*@NotNull*/ IntegerValue other) throws XPathException {
+ try {
+ if (other instanceof BigIntegerValue) {
+ return makeIntegerValue(value.remainder(((BigIntegerValue)other).value));
+ } else {
+ return makeIntegerValue(value.remainder(BigInteger.valueOf(other.longValue())));
+ }
+ } catch (ArithmeticException err) {
+ XPathException e;
+ if (BigInteger.valueOf(other.longValue()).signum() == 0) {
+ e = new XPathException("Integer modulo zero", "FOAR0001");
+ } else {
+ e = new XPathException("Integer mod operation failure", err);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Integer divide by another integer
+ *
+ * @throws net.sf.saxon.trans.XPathException
+ * if the other integer is zero
+ */
+
+ public IntegerValue idiv(/*@NotNull*/ IntegerValue other) throws XPathException {
+ BigInteger oi;
+ if (other instanceof BigIntegerValue) {
+ oi = ((BigIntegerValue)other).value;
+ } else {
+ oi = BigInteger.valueOf(other.longValue());
+ }
+ try {
+ return makeIntegerValue(value.divide(oi));
+ } catch (ArithmeticException err) {
+ XPathException e;
+ if ("/ by zero".equals(err.getMessage())) {
+ e = new XPathException("Integer division by zero", "FOAR0001");
+ } else {
+ e = new XPathException("Integer division failure", err);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Get an object that implements XML Schema comparison semantics
+ */
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return new BigIntegerComparable(this);
+ }
+
+ /**
+ * A Comparable that performs comparison of BigInteger values either with other
+ * BigInteger values or with other representations of XPath numeric values
+ */
+
+ protected static class BigIntegerComparable implements Comparable {
+
+ protected BigIntegerValue value;
+
+ public BigIntegerComparable(BigIntegerValue value) {
+ this.value = value;
+ }
+
+ public BigInteger asBigInteger() {
+ return value.asBigInteger();
+ }
+
+ public int compareTo(/*@NotNull*/ Object o) {
+ if (o instanceof Int64Value.Int64Comparable) {
+ return asBigInteger().compareTo(BigInteger.valueOf(((Int64Value.Int64Comparable)o).asLong()));
+ } else if (o instanceof BigIntegerComparable) {
+ return asBigInteger().compareTo(((BigIntegerComparable)o).asBigInteger());
+ } else if (o instanceof DecimalValue.DecimalComparable) {
+ return value.getDecimalValue().compareTo(((DecimalValue.DecimalComparable)o).asBigDecimal());
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ }
+
+ public boolean equals(/*@NotNull*/ Object o) {
+ return compareTo(o) == 0;
+ }
+ public int hashCode() {
+ // Must align with hashCodes for other subtypes of xs:decimal
+ BigInteger big = value.asBigInteger();
+ if (big.compareTo(MAX_LONG) < 0 && big.compareTo(MIN_LONG) > 0) {
+ Int64Value iv = new Int64Value(big.longValue());
+ return iv.hashCode();
+ }
+ return big.hashCode();
+ }
+ }
+
+ /**
+ * Reduce a value to its simplest form.
+ */
+
+ /*@NotNull*/ public IntegerValue reduce() {
+ if (compareTo(Long.MAX_VALUE) < 0 && compareTo(Long.MIN_VALUE) > 0) {
+ Int64Value iv = new Int64Value(longValue());
+ iv.setTypeLabel(typeLabel);
+ return iv;
+ }
+ return this;
+ }
+
+ /**
+ * Convert to Java object (for passing to external functions)
+ *
+ * @param target The Java class to which conversion is required
+ * @exception XPathException if conversion is not possible, or fails
+ * @return the Java object that results from the conversion; always an
+ * instance of the target class
+ */
+
+// public Object convertAtomicToJava(Class target, XPathContext context) throws XPathException {
+// if (isWithinLongRange()) {
+// Int64Value val = Int64Value.makeIntegerValue(longValue());
+// return val.convertToJava(target, context);
+// } else if (target.isAssignableFrom(Int64Value.class)) {
+// return this;
+// } else if (target == BigInteger.class) {
+// return value;
+// } else {
+// return convertPrimitive(BuiltInAtomicType.DECIMAL, true, context).asAtomic().convertToJava(target, context);
+// }
+// }
+
+
+}
+
diff --git a/sf/saxon/value/BooleanValue.java b/sf/saxon/value/BooleanValue.java
new file mode 100644
index 0000000..5625605
--- /dev/null
+++ b/sf/saxon/value/BooleanValue.java
@@ -0,0 +1,287 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+/**
+ * A boolean XPath value
+ */
+
+public final class BooleanValue extends AtomicValue implements Comparable {
+ private boolean value;
+
+ /**
+ * The boolean value TRUE
+ */
+ public static final BooleanValue TRUE = new BooleanValue(true);
+ /**
+ * The boolean value FALSE
+ */
+ public static final BooleanValue FALSE = new BooleanValue(false);
+
+ /**
+ * Private Constructor: create a boolean value. Only two instances of this class are
+ * ever created, one to represent true and one to represent false.
+ * @param value the initial value, true or false
+ */
+
+ private BooleanValue(boolean value) {
+ this.value = value;
+ typeLabel = BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Factory method: get a BooleanValue
+ *
+ * @param value true or false, to determine which boolean value is
+ * required
+ * @return the BooleanValue requested
+ */
+
+ public static BooleanValue get(boolean value) {
+ return (value ? TRUE : FALSE);
+ }
+
+ /**
+ * Create a new Boolean value with a user-supplied type label.
+ * It is the caller's responsibility to ensure that the value is valid for the subtype
+ * @param value the boolean value
+ * @param typeLabel the type label, xs:boolean or a subtype
+ */
+
+ public BooleanValue(boolean value, AtomicType typeLabel) {
+ this.value = value;
+ this.typeLabel = typeLabel;
+ }
+
+ /**
+ * Create a copy of this atomic value (usually so that the type label can be changed).
+ * The type label of the copy will be reset to the primitive type.
+ * @param typeLabel the atomic type label to be added to the copied value
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ BooleanValue v = new BooleanValue(value);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Convert a string to a boolean value, using the XML Schema rules (including
+ * whitespace trimming)
+ * @param s the input string
+ * @return the relevant BooleanValue if validation succeeds; or a ValidationFailure if not.
+ */
+
+ public static ConversionResult fromString(CharSequence s) {
+ // implementation designed to avoid creating new objects
+ s = Whitespace.trimWhitespace(s);
+ int len = s.length();
+ if (len == 1) {
+ char c = s.charAt(0);
+ if (c == '1') {
+ return TRUE;
+ } else if (c == '0') {
+ return FALSE;
+ }
+ } else if (len == 4) {
+ if (s.charAt(0) == 't' && s.charAt(1) == 'r' && s.charAt(2) == 'u' && s.charAt(3) == 'e') {
+ return TRUE;
+ }
+ } else if (len == 5) {
+ if (s.charAt(0) == 'f' && s.charAt(1) == 'a' && s.charAt(2) == 'l' && s.charAt(3) == 's' && s.charAt(4) == 'e') {
+ return FALSE;
+ }
+ }
+ ValidationFailure err = new ValidationFailure(
+ "The string " + Err.wrap(s, Err.VALUE) + " cannot be cast to a boolean");
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+
+ /**
+ * Get the value
+ * @return true or false, the actual boolean value of this BooleanValue
+ */
+
+ public boolean getBooleanValue() {
+ return value;
+ }
+
+ /**
+ * Get the effective boolean value of this expression
+ *
+ * @return the boolean value
+ */
+ public boolean effectiveBooleanValue() {
+ return value;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.BOOLEAN;
+ }
+
+ /**
+ * Convert to string
+ * @return "true" or "false"
+ */
+
+ public String getPrimitiveStringValue() {
+ return (value ? "true" : "false");
+ }
+
+ /**
+ * Convert to Java object (for passing to external functions)
+ *
+ * @param target the Java class to which conversion is required
+ * @exception XPathException if conversion is not possible or fails
+ * @return An object of the specified Java class
+ */
+
+// public Object convertAtomicToJava(Class target, XPathContext context) throws XPathException {
+// if (target==Object.class) {
+// return Boolean.valueOf(value);
+// } else if (target.isAssignableFrom(BooleanValue.class)) {
+// return this;
+// } else if (target==boolean.class) {
+// return Boolean.valueOf(value);
+// } else if (target==Boolean.class) {
+// return Boolean.valueOf(value);
+// } else {
+// Object o = super.convertSequenceToJava(target, context);
+// if (o == null) {
+// XPathException err = new XPathException("Conversion of xs:boolean to " + target.getName() +
+// " is not supported");
+// err.setXPathContext(context);
+// err.setErrorCode(SaxonErrorCode.SXJE0001);
+// throw err;
+// }
+// return o;
+// }
+// }
+
+
+ /**
+ * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
+ * The default implementation returns "this". This is overridden for particular atomic types.
+ * <p/>
+ * <p>In the case of data types that are partially ordered, the returned Comparable extends the standard
+ * semantics of the compareTo() method by returning the value {@link net.sf.saxon.om.SequenceTool#INDETERMINATE_ORDERING} when there
+ * is no defined order relationship between two given values.</p>
+ *
+ * @return a Comparable that follows XML Schema comparison rules
+ */
+
+ public Comparable getSchemaComparable() {
+ return new BooleanComparable();
+ }
+
+ private class BooleanComparable implements Comparable {
+
+ public boolean asBoolean() {
+ return BooleanValue.this.getBooleanValue();
+ }
+
+ public int compareTo(/*@NotNull*/ Object o) {
+ return equals(o) ? 0 : SequenceTool.INDETERMINATE_ORDERING;
+ }
+
+ public boolean equals(Object o) {
+ if (o instanceof BooleanComparable) {
+ return o instanceof BooleanComparable && asBoolean() == ((BooleanComparable)o).asBoolean();
+ } else {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+ return asBoolean() ? 9999999 : 8888888;
+ }
+
+ }
+
+ /**
+ * Get a Comparable value that implements the XPath ordering comparison semantics for this value.
+ * Returns null if the value is not comparable according to XPath rules. The default implementation
+ * returns null. This is overridden for types that allow ordered comparisons in XPath: numeric, boolean,
+ * string, date, time, dateTime, yearMonthDuration, dayTimeDuration, and anyURI.
+ * @param ordered
+ * @param collator
+ * @param context
+ */
+
+ public Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context) {
+ return this;
+ }
+
+ /**
+ * Compare the value to another boolean value
+ *
+ * @throws ClassCastException if the other value is not a BooleanValue
+ * (the parameter is declared as Object to satisfy the Comparable
+ * interface)
+ * @param other The other boolean value
+ * @return -1 if this one is the lower, 0 if they are equal, +1 if this
+ * one is the higher. False is considered to be less than true.
+ */
+
+ public int compareTo(Object other) {
+ if (!(other instanceof BooleanValue)) {
+ throw new ClassCastException("Boolean values are not comparable to " + other.getClass());
+ }
+ if (value == ((BooleanValue)other).value) return 0;
+ if (value) return +1;
+ return -1;
+ }
+
+ /**
+ * Determine whether two boolean values are equal
+ *
+ * @param other the value to be compared to this value
+ * @return true if the other value is a boolean value and is equal to this
+ * value
+ * @throws ClassCastException if other value is not xs:boolean or derived therefrom
+ */
+ public boolean equals(Object other) {
+ return (other instanceof BooleanValue && value == ((BooleanValue)other).value);
+ }
+
+ /**
+ * Get a hash code for comparing two BooleanValues
+ *
+ * @return the hash code
+ */
+ public int hashCode() {
+ return (value ? 0 : 1);
+ }
+
+ /**
+ * Diagnostic display of this value as a string
+ * @return a string representation of this value: "true()" or "false()"
+ */
+ public String toString() {
+ return getStringValue() + "()";
+ }
+}
+
diff --git a/sf/saxon/value/CalendarValue.java b/sf/saxon/value/CalendarValue.java
new file mode 100644
index 0000000..2c8f0b1
--- /dev/null
+++ b/sf/saxon/value/CalendarValue.java
@@ -0,0 +1,312 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.ComparisonKey;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.math.BigDecimal;
+import java.util.GregorianCalendar;
+
+
+/**
+* Abstract superclass for Date, Time, and DateTime.
+*/
+
+public abstract class CalendarValue extends AtomicValue {
+
+ // This is a reimplementation that makes no use of the Java Calendar/Date types except for computations.
+
+ private int tzMinutes = NO_TIMEZONE; // timezone offset in minutes: or the special value NO_TIMEZONE
+ public static final int NO_TIMEZONE = Integer.MIN_VALUE;
+
+ /**
+ * Parse a string to create a CalendarValue whose actual type will depend on the format of the string
+ * @param s a string in the lexical space of one of the date/time types (date, time, dateTime,
+ * gYearMonth, gYear, gMonth, gMonthDay, or gDay
+ * @param rules
+ * @return either a value of the appropriate type, or a ValidationFailure if the format is invalid
+ */
+
+ public static ConversionResult makeCalendarValue(CharSequence s, ConversionRules rules) {
+ ConversionResult cr = DateTimeValue.makeDateTimeValue(s, rules);
+ ConversionResult firstError = cr;
+ if (cr instanceof ValidationFailure) {
+ cr = DateValue.makeDateValue(s, rules);
+ }
+ if (cr instanceof ValidationFailure) {
+ cr = TimeValue.makeTimeValue(s);
+ }
+ if (cr instanceof ValidationFailure) {
+ cr = GYearValue.makeGYearValue(s, rules);
+ }
+ if (cr instanceof ValidationFailure) {
+ cr = GYearMonthValue.makeGYearMonthValue(s, rules);
+ }
+ if (cr instanceof ValidationFailure) {
+ cr = GMonthValue.makeGMonthValue(s, rules);
+ }
+ if (cr instanceof ValidationFailure) {
+ cr = GMonthDayValue.makeGMonthDayValue(s, rules);
+ }
+ if (cr instanceof ValidationFailure) {
+ cr = GDayValue.makeGDayValue(s, rules);
+ }
+ if (cr instanceof ValidationFailure) {
+ return firstError;
+ }
+ return cr;
+ }
+
+ /**
+ * Determine whether this value includes a timezone
+ * @return true if there is a timezone in the value, false if not
+ */
+
+ public final boolean hasTimezone() {
+ return tzMinutes != NO_TIMEZONE;
+ }
+
+ /**
+ * Modify the timezone value held in this object. This must be done only while the value is being
+ * constructed.
+ * @param minutes The timezone offset from GMT in minutes, positive or negative; or the special
+ * value NO_TIMEZONE indicating that the value is not in a timezone (this is the default if this
+ * method is not called)
+ */
+
+ public final void setTimezoneInMinutes(int minutes) {
+ tzMinutes = minutes;
+ }
+
+ /**
+ * Convert the value to a DateTime, retaining all the components that are actually present, and
+ * substituting conventional values for components that are missing
+ * @return the equivalent DateTimeValue
+ */
+
+ public abstract DateTimeValue toDateTime();
+
+ /**
+ * Get the timezone value held in this object.
+ * @return The timezone offset from GMT in minutes, positive or negative; or the special
+ * value NO_TIMEZONE indicating that the value is not in a timezone
+ */
+
+ public final int getTimezoneInMinutes() {
+ return tzMinutes;
+ }
+
+ /**
+ * Convert the value to a string
+ */
+
+// public final String getStringValue() {
+// return getStringValueCS().toString();
+// }
+
+ /**
+ * Get a Java Calendar object that represents this date/time value. The Calendar
+ * object will be newly created for the purpose
+ * @return A Calendar object representing the date and time. Note that Java can only
+ * represent the time to millisecond precision, and that it does not support the full
+ * range of timezones required by XPath (-14:00 to +14:00)
+ */
+
+ public abstract GregorianCalendar getCalendar();
+
+ /**
+ * Add a duration to this date/time value
+ * @param duration the duration to be added (which might be negative)
+ * @return a new date/time value representing the result of adding the duration. The original
+ * object is not modified.
+ * @throws XPathException
+ */
+
+ public abstract CalendarValue add(DurationValue duration) throws XPathException;
+
+ /**
+ * Determine the difference between two points in time, as a duration
+ * @param other the other point in time
+ * @param context the dynamic context, used to obtain timezone information. May be set to null
+ * only if both values contain an explicit timezone, or if neither does so.
+ * @return the duration as an xs:dayTimeDuration
+ * @throws net.sf.saxon.trans.XPathException for example if one value is a date and the other is a time
+ */
+
+ public DayTimeDurationValue subtract(/*@NotNull*/ CalendarValue other, /*@Nullable*/ XPathContext context) throws XPathException {
+ DateTimeValue dt1 = toDateTime();
+ DateTimeValue dt2 = other.toDateTime();
+ if (dt1.getTimezoneInMinutes() != dt2.getTimezoneInMinutes()) {
+ dt1 = dt1.normalize(context);
+ dt2 = dt2.normalize(context);
+ }
+ BigDecimal d1 = dt1.toJulianInstant();
+ BigDecimal d2 = dt2.toJulianInstant();
+ BigDecimal difference = d1.subtract(d2);
+ return DayTimeDurationValue.fromSeconds(difference);
+ }
+
+ /**
+ * Return a date, time, or dateTime with the same localized value, but
+ * without the timezone component
+ * @return the result of removing the timezone
+ */
+
+ /*@NotNull*/ public final CalendarValue removeTimezone() {
+ CalendarValue c = (CalendarValue)copyAsSubType(typeLabel);
+ c.tzMinutes = NO_TIMEZONE;
+ return c;
+ }
+
+ /**
+ * Return a new date, time, or dateTime with the same normalized value, but
+ * in a different timezone
+ * @param tz the new timezone offset from UTC, in minutes
+ * @return the date/time in the new timezone
+ */
+
+ public abstract CalendarValue adjustTimezone(int tz);
+
+ /**
+ * Return a new date, time, or dateTime with the same normalized value, but
+ * in a different timezone, specified as a dayTimeDuration
+ * @param tz the new timezone, in minutes
+ * @return the date/time in the new timezone
+ */
+
+ public final CalendarValue adjustTimezone(/*@NotNull*/ DayTimeDurationValue tz) throws XPathException {
+ long microseconds = tz.getLengthInMicroseconds();
+ if (microseconds%60000000 != 0) {
+ XPathException err = new XPathException("Timezone is not an integral number of minutes");
+ err.setErrorCode("FODT0003");
+ throw err;
+ }
+ int tzminutes = (int)(microseconds / 60000000);
+ if (Math.abs(tzminutes) > 14*60) {
+ XPathException err = new XPathException("Timezone out of range (-14:00 to +14:00)");
+ err.setErrorCode("FODT0003");
+ throw err;
+ }
+ return adjustTimezone(tzminutes);
+ }
+
+
+ /**
+ * Get an object value that implements the XPath equality and ordering comparison semantics for this value.
+ * If the ordered parameter is set to true, the result will be a Comparable and will support a compareTo()
+ * method with the semantics of the XPath lt/gt operator, provided that the other operand is also obtained
+ * using the getXPathComparable() method. In all cases the result will support equals() and hashCode() methods
+ * that support the semantics of the XPath eq operator, again provided that the other operand is also obtained
+ * using the getXPathComparable() method. A context argument is supplied for use in cases where the comparison
+ * semantics are context-sensitive, for example where they depend on the implicit timezone or the default
+ * collation.
+ *
+ * @param ordered true if an ordered comparison is required. In this case the result is null if the
+ * type is unordered; in other cases the returned value will be a Comparable.
+ * @param collator collation used for strings
+ * @param context the XPath dynamic evaluation context, used in cases where the comparison is context
+* sensitive @return an Object whose equals() and hashCode() methods implement the XPath comparison semantics
+ */
+
+ /*@Nullable*/ public Object getXPathComparable(boolean ordered, StringCollator collator, /*@NotNull*/ XPathContext context) throws NoDynamicContextException {
+ if (ordered && !(this instanceof Comparable)) {
+ return null;
+ }
+ return (hasTimezone() ? this : adjustTimezone(context.getImplicitTimezone()));
+ }
+
+ /**
+ * Compare this value to another value of the same type, using the supplied Configuration
+ * to get the implicit timezone if required.
+ * @param other the other value to be compared
+ * @param context the XPath dynamic evaluation context. May be null if no context is required
+ * @return the comparison result
+ * @throws NoDynamicContextException if the supplied context is an early evaluation context and the
+ * result depends on the implicit timezone, which is not available at compile time
+ */
+
+ public abstract int compareTo(CalendarValue other, /*@Nullable*/ XPathContext context) throws NoDynamicContextException;
+
+ /**
+ * Get a comparison key for this value. Two values are equal if and only if they their comparison
+ * keys are equal
+ * @param context XPath dynamic evaluation context, used to obtain implicit timezone
+ * @return a comparison key
+ * @throws NoDynamicContextException if the implicit timezone is needed and is not available
+ */
+
+ public abstract ComparisonKey getComparisonKey(XPathContext context) throws NoDynamicContextException;
+
+ public boolean isIdentical(/*@NotNull*/ AtomicValue v) {
+ return super.isIdentical(v) && tzMinutes == ((CalendarValue)v).tzMinutes;
+ }
+
+ /**
+ * Add a string representation of the timezone, typically
+ * formatted as "Z" or "+03:00" or "-10:00", to a supplied
+ * string buffer
+ * @param sb The StringBuffer that will be updated with the resulting string
+ * representation
+ */
+
+ public final void appendTimezone(/*@NotNull*/ FastStringBuffer sb) {
+ if (hasTimezone()) {
+ appendTimezone(getTimezoneInMinutes(), sb);
+ }
+ }
+
+ /**
+ * Format a timezone and append it to a buffer
+ * @param tz the timezone
+ * @param sb the buffer
+ */
+
+ public static void appendTimezone(int tz, /*@NotNull*/ FastStringBuffer sb) {
+ if (tz == 0) {
+ sb.append("Z");
+ } else {
+ sb.append(tz > 0 ? "+" : "-");
+ tz = Math.abs(tz);
+ appendTwoDigits(sb, tz/60);
+ sb.append(':');
+ appendTwoDigits(sb, tz%60);
+ }
+ }
+
+ /**
+ * Append an integer, formatted with leading zeros to a fixed size, to a string buffer
+ * @param sb the string buffer
+ * @param value the integer to be formatted
+ * @param size the number of digits required (max 9)
+ */
+
+ static void appendString(/*@NotNull*/ FastStringBuffer sb, int value, int size) {
+ String s = "000000000"+value;
+ sb.append( s.substring(s.length()-size) );
+ }
+
+/**
+ * Append an integer, formatted as two digits, to a string buffer
+ * @param sb the string buffer
+ * @param value the integer to be formatted (must be in the range 0..99
+ */
+
+ static void appendTwoDigits(/*@NotNull*/ FastStringBuffer sb, int value) {
+ sb.append((char)(value/10 + '0'));
+ sb.append((char)(value%10 + '0'));
+ }
+}
+
diff --git a/sf/saxon/value/Cardinality.java b/sf/saxon/value/Cardinality.java
new file mode 100644
index 0000000..c2ed6ef
--- /dev/null
+++ b/sf/saxon/value/Cardinality.java
@@ -0,0 +1,203 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.*;
+
+/**
+* This class contains static methods to manipulate the cardinality
+* property of a type.
+* Cardinality of expressions is denoted by one of the values ONE_OR_MORE, ZERO_OR_MORE,
+* ZERO_OR_ONE, EXACTLY_ONE, or EMPTY. These are combinations of the three bit-significant
+* values ALLOWS_ZERO, ALLOWS_ONE, and ALLOWS_MANY.
+*/
+
+public final class Cardinality {
+
+ /**
+ * Private constructor: no instances allowed
+ */
+
+ private Cardinality() {}
+
+ /**
+ * Determine whether multiple occurrences are allowed
+ * @param cardinality the cardinality of a sequence
+ * @return true if the cardinality allows the sequence to contain more than one item
+ */
+
+ public static boolean allowsMany(int cardinality) {
+ return (cardinality & StaticProperty.ALLOWS_MANY) != 0;
+ }
+
+ /**
+ * Determine whether multiple occurrences are not only allowed, but likely.
+ * This returns false for an expression that is the atomization of a singleton
+ * node, since in that case it's quite unusual for the typed value to be a
+ * non-singleton sequence.
+ * @param expression an expression
+ * @return true if multiple occurrences are not only allowed, but likely. Return
+ * false if multiple occurrences are unlikely, even though they might be allowed.
+ * This is typically the case for the atomized sequence that is obtained by atomizing
+ * a singleton node.
+ */
+
+ public static boolean expectsMany(Expression expression) {
+ if (expression instanceof VariableReference) {
+ Binding b = ((VariableReference)expression).getBinding();
+ if (b instanceof LetExpression) {
+ return expectsMany(((LetExpression)b).getSequence());
+ }
+ }
+ if (expression instanceof Atomizer) {
+ return expectsMany(((Atomizer)expression).getBaseExpression());
+ }
+ if (expression instanceof FilterExpression) {
+ return expectsMany(((FilterExpression)expression).getControllingExpression());
+ }
+ return allowsMany(expression.getCardinality());
+ }
+
+ /**
+ * Determine whether empty sequence is allowed
+ * @param cardinality the cardinality of a sequence
+ * @return true if the cardinality allows the sequence to be empty
+ */
+
+ public static boolean allowsZero(int cardinality) {
+ return (cardinality & StaticProperty.ALLOWS_ZERO) != 0;
+ }
+
+ /**
+ * Form the union of two cardinalities. The cardinality of the expression "if (c) then e1 else e2"
+ * is the union of the cardinalities of e1 and e2.
+ * @param c1 a cardinality
+ * @param c2 another cardinality
+ * @return the cardinality that allows both c1 and c2
+ */
+
+ public static int union(int c1, int c2) {
+ int r = c1 | c2;
+ // eliminate disallowed options
+ if (r == (StaticProperty.ALLOWS_MANY |
+ StaticProperty.ALLOWS_ZERO ))
+ r = StaticProperty.ALLOWS_ZERO_OR_MORE;
+ return r;
+ }
+
+
+ /**
+ * Add two cardinalities
+ * @param c1 the first cardinality
+ * @param c2 the second cardinality
+ * @return the cardinality of a sequence formed by concatenating the sequences whose cardinalities
+ * are c1 and c2
+ */
+
+ public static int sum(int c1, int c2) {
+ if (c1==StaticProperty.EMPTY) {
+ return c2;
+ }
+ if (c2==StaticProperty.EMPTY) {
+ return c1;
+ }
+ boolean allowsZero = Cardinality.allowsZero(c1) && Cardinality.allowsZero(c2);
+ return StaticProperty.ALLOWS_ONE_OR_MORE | (allowsZero ? StaticProperty.ALLOWS_ZERO : 0);
+ }
+
+ /**
+ * Test if one cardinality subsumes another. Cardinality c1 subsumes c2 if every option permitted
+ * by c2 is also permitted by c1.
+ * @param c1 a cardinality
+ * @param c2 another cardinality
+ * @return true if if every option permitted
+ * by c2 is also permitted by c1.
+ */
+
+ public static boolean subsumes(int c1, int c2) {
+ return (c1|c2)==c1;
+ }
+
+
+
+ /**
+ * Multiply two cardinalities
+ * @param c1 the first cardinality
+ * @param c2 the second cardinality
+ * @return the product of the cardinalities, that is, the cardinality of the sequence
+ * "for $x in S1 return S2", where c1 is the cardinality of S1 and c2 is the cardinality of S2
+ */
+
+ public static int multiply(int c1, int c2) {
+ if (c1==StaticProperty.EMPTY || c2==StaticProperty.EMPTY) {
+ return StaticProperty.EMPTY;
+ }
+ if (c2==StaticProperty.EXACTLY_ONE) {
+ return c1;
+ }
+ if (c1==StaticProperty.EXACTLY_ONE) {
+ return c2;
+ }
+ if (c1==StaticProperty.ALLOWS_ZERO_OR_ONE && c2==StaticProperty.ALLOWS_ZERO_OR_ONE) {
+ return StaticProperty.ALLOWS_ZERO_OR_ONE;
+ }
+ if (c1==StaticProperty.ALLOWS_ONE_OR_MORE && c2==StaticProperty.ALLOWS_ONE_OR_MORE) {
+ return StaticProperty.ALLOWS_ONE_OR_MORE;
+ }
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+ /**
+ * Display the cardinality as a string
+ * @param cardinality the cardinality value to be displayed
+ * @return the representation as a string, for example "zero or one", "zero or more"
+ *
+ */
+
+ public static String toString(int cardinality) {
+ switch (cardinality) {
+ case StaticProperty.ALLOWS_ZERO_OR_ONE:
+ return "zero or one";
+ case StaticProperty.EXACTLY_ONE:
+ return "exactly one";
+ case StaticProperty.ALLOWS_ZERO_OR_MORE:
+ return "zero or more";
+ case StaticProperty.ALLOWS_ONE_OR_MORE:
+ return "one or more";
+ case StaticProperty.EMPTY:
+ return "exactly zero";
+ case StaticProperty.ALLOWS_MANY:
+ return "zero or more";
+ default:
+ return "code " + cardinality;
+ }
+ }
+
+ /**
+ * Get the occurence indicator representing the cardinality
+ * @param cardinality the cardinality value
+ * @return the occurrence indicator, for example "*", "+", "?", "".
+ */
+
+ /*@NotNull*/ public static String getOccurrenceIndicator(int cardinality) {
+ switch (cardinality) {
+ case StaticProperty.ALLOWS_ZERO_OR_ONE:
+ return "?";
+ case StaticProperty.EXACTLY_ONE:
+ return "";
+ case StaticProperty.ALLOWS_ZERO_OR_MORE:
+ return "*";
+ case StaticProperty.ALLOWS_ONE_OR_MORE:
+ return "+";
+ case StaticProperty.EMPTY:
+ return "\u00B0";
+ default: throw new AssertionError("unknown cardinality value");
+ }
+ }
+}
+
diff --git a/sf/saxon/value/Closure.java b/sf/saxon/value/Closure.java
new file mode 100644
index 0000000..820a4b1
--- /dev/null
+++ b/sf/saxon/value/Closure.java
@@ -0,0 +1,264 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trace.Location;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+/**
+ * A Closure represents a value that has not yet been evaluated: the value is represented
+ * by an expression, together with saved values of all the context variables that the
+ * expression depends on.
+ *
+ * <p>This Closure is designed for use when the value is only read once. If the value
+ * is read more than once, a new iterator over the underlying expression is obtained
+ * each time: this may (for example in the case of a filter expression) involve
+ * significant re-calculation.</p>
+ *
+ * <p>The expression may depend on local variables and on the context item; these values
+ * are held in the saved XPathContext object that is kept as part of the Closure, and they
+ * will always be read from that object. The expression may also depend on global variables;
+ * these are unchanging, so they can be read from the Bindery in the normal way. Expressions
+ * that depend on other contextual information, for example the values of position(), last(),
+ * current(), current-group(), should not be evaluated using this mechanism: they should
+ * always be evaluated eagerly. This means that the Closure does not need to keep a copy
+ * of these context variables.</p>
+ *
+*/
+
+public class Closure<T extends Item> implements Sequence {
+
+ protected Expression expression;
+ /*@Nullable*/ protected XPathContextMajor savedXPathContext;
+ protected int depth = 0;
+
+ // The base iterator is used to copy items on demand from the underlying value
+ // to the reservoir. It only ever has one instance (for each Closure) and each
+ // item is read only once.
+
+ /*@Nullable*/ protected SequenceIterator<T> inputIterator;
+
+// private static int countClosures = 0;
+// private static int countMemoClosures = 0;
+
+ /**
+ * Constructor should not be called directly, instances should be made using the make() method.
+ */
+ //private static int closureCount = 0;
+ public Closure() {
+// this.expression = exp;
+// this.savedXPathContext = context.newContext();
+// this.savedXPathContext.setOriginatingConstructType(Location.LAZY_EVALUATION);
+// saveContext(expression, context);
+ }
+
+ /**
+ * Construct a Closure by supplying the expression and the set of context variables.
+ * @param expression the expression to be lazily evaluated
+ * @param context the dynamic context of the expression including for example the variables
+ * on which it depends
+ * @param ref the number of references to the value being lazily evaluated; this affects
+ * the kind of Closure that is created
+ * @return the Closure, a virtual value that can later be materialized when its content is required
+ * @throws net.sf.saxon.trans.XPathException if a dynamic error occurs
+ */
+
+ /*@NotNull*/ public static Sequence make(/*@NotNull*/ Expression expression, /*@NotNull*/ XPathContext context, int ref) throws XPathException {
+
+ // special cases such as TailExpressions and shared append expressions are now picked up before
+ // this method is called (where possible, at compile time)
+// SequenceIterator iter = expression.iterate(context);
+// return Value.asValue(SequenceExtent.makeSequenceExtent(iter));
+ Sequence v = context.getConfiguration().makeClosure(expression, ref, context);
+ if (v instanceof Closure) {
+ Closure c = (Closure)v;
+ c.expression = expression;
+ c.savedXPathContext = context.newContext();
+ c.savedXPathContext.setOriginatingConstructType(Location.LAZY_EVALUATION);
+ c.saveContext(expression, context);
+ return c;
+ } else {
+ return v;
+ }
+ }
+
+ public void saveContext(/*@NotNull*/ Expression expression, /*@NotNull*/ XPathContext context) throws XPathException {
+ // Make a copy of all local variables. If the value of any local variable is a closure
+ // whose depth exceeds a certain threshold, we evaluate the closure eagerly to avoid
+ // creating deeply nested lists of Closures, which consume memory unnecessarily
+
+ // We only copy the local variables if the expression has dependencies on local variables.
+ // What's more, we only copy those variables that the expression actually depends on.
+
+ if ((expression.getDependencies() & StaticProperty.DEPENDS_ON_LOCAL_VARIABLES) != 0) {
+ StackFrame localStackFrame = context.getStackFrame();
+ Sequence[] local = localStackFrame.getStackFrameValues();
+ int[] slotsUsed = expression.getSlotsUsed(); // computed on first call
+ if (local != null) {
+ final SlotManager stackFrameMap = localStackFrame.getStackFrameMap();
+ final Sequence[] savedStackFrame =
+ new Sequence[stackFrameMap.getNumberOfVariables()];
+ for (int i : slotsUsed) {
+ if (local[i] instanceof Closure) {
+ int cdepth = ((Closure) local[i]).depth;
+ if (cdepth >= 10) {
+ local[i] = SequenceExtent.makeSequenceExtent(((Closure) local[i]).iterate());
+ } else if (cdepth + 1 > depth) {
+ depth = cdepth + 1;
+ }
+ }
+ savedStackFrame[i] = local[i];
+ }
+
+ savedXPathContext.setStackFrame(stackFrameMap, savedStackFrame);
+ }
+ }
+
+ // Make a copy of the context item
+ SequenceIterator currentIterator = context.getCurrentIterator();
+ if (currentIterator != null) {
+ Item contextItem = currentIterator.current();
+ UnfailingIterator single = SingletonIterator.makeIterator(contextItem);
+ single.next();
+ savedXPathContext.setCurrentIterator(single);
+ // we don't save position() and last() because we have no way
+ // of restoring them. So the caller must ensure that a Closure is not
+ // created if the expression depends on position() or last()
+ }
+
+ savedXPathContext.setReceiver(null);
+ }
+
+ /**
+ * Get the first item in the sequence.
+ *
+ * @return the first item in the sequence if there is one, or null if the sequence
+ * is empty
+ * @throws net.sf.saxon.trans.XPathException
+ * in the situation where the sequence is evaluated lazily, and
+ * evaluation of the first item causes a dynamic error.
+ */
+ public Item head() throws XPathException {
+ return iterate().next();
+ }
+
+ /**
+ * Get the static item type
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(/*@Nullable*/ TypeHierarchy th) {
+ if (expression == null || th == null) {
+ return AnyItemType.getInstance();
+ } else {
+ return expression.getItemType(th);
+ }
+ // This is probably rarely used, because a Closure has no static existence, and
+ // run-time polymorphism applies mainly to singletons, which are rarely evaluated as Closures.
+ }
+
+ /**
+ * Get the cardinality
+ */
+
+ public int getCardinality() {
+ if (expression == null) {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ } else {
+ return expression.getCardinality();
+ }
+ }
+
+ public Expression getExpression() {
+ return expression;
+ }
+
+ /*@Nullable*/ public XPathContextMajor getSavedXPathContext() {
+ return savedXPathContext;
+ }
+
+ public void setExpression(Expression expression) {
+ this.expression = expression;
+ }
+
+ public void setSavedXPathContext(XPathContextMajor savedXPathContext) {
+ this.savedXPathContext = savedXPathContext;
+ }
+
+ /**
+ * Evaluate the expression in a given context to return an iterator over a sequence
+ */
+
+ /*@NotNull*/
+ public SequenceIterator<T> iterate() throws XPathException {
+
+ if (inputIterator == null) {
+ return (inputIterator = (SequenceIterator<T>)expression.iterate(savedXPathContext));
+ } else {
+ // In an ideal world this shouldn't happen: if the value is needed more than once, we should
+ // have chosen a MemoClosure. In fact, this path is never taken when executing the standard
+ // test suite (April 2005). However, it provides robustness in case the compile-time analysis
+ // is flawed. I believe it's also possible that this path can be taken if a Closure needs to be
+ // evaluated when the chain of dependencies gets too long: this was happening routinely when
+ // all local variables were saved, rather than only those that the expression depends on.
+ return inputIterator.getAnother();
+ }
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(/*@NotNull*/ XPathContext context) throws XPathException {
+ if (expression == null) {
+ // This is a Closure that simply wraps a SequenceIterator supplied from the Java level
+ SequenceReceiver out = context.getReceiver();
+ while (true) {
+ T item = inputIterator.next();
+ if (item == null) {
+ break;
+ }
+ out.append(item, 0, NodeInfo.ALL_NAMESPACES);
+ }
+ inputIterator = inputIterator.getAnother();
+ } else {
+ // To evaluate the closure in push mode, we need to use the original context of the
+ // expression for everything except the current output destination, which is newly created
+ XPathContextMajor c2 = savedXPathContext.newContext();
+ SequenceReceiver out = context.getReceiver();
+ c2.setReceiver(out);
+ c2.setTemporaryOutputState(true);
+ expression.process(c2);
+ }
+ }
+
+ /**
+ * Reduce a value to its simplest form. If the value is a closure or some other form of deferred value
+ * such as a FunctionCallPackage, then it is reduced to a SequenceExtent. If it is a SequenceExtent containing
+ * a single item, then it is reduced to that item. One consequence that is exploited by class FilterExpression
+ * is that if the value is a singleton numeric value, then the result will be an instance of NumericValue
+ */
+
+ public GroundedValue reduce() throws XPathException {
+ return SequenceExtent.makeSequenceExtent(iterate());
+ }
+
+}
+
diff --git a/sf/saxon/value/DateTimeValue.java b/sf/saxon/value/DateTimeValue.java
new file mode 100644
index 0000000..a469188
--- /dev/null
+++ b/sf/saxon/value/DateTimeValue.java
@@ -0,0 +1,1183 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.ComparisonKey;
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+/**
+ * A value of type DateTime
+ */
+
+public final class DateTimeValue extends CalendarValue implements Comparable {
+
+ private int year; // the year as written, +1 for BC years
+ private byte month; // the month as written, range 1-12
+ private byte day; // the day as written, range 1-31
+ private byte hour; // the hour as written (except for midnight), range 0-23
+ private byte minute; // the minutes as written, range 0-59
+ private byte second; // the seconds as written, range 0-59 (no leap seconds)
+ private int microsecond;
+ private boolean xsd10rules; // true if XSD 1.0 rules apply for negative years
+
+ /**
+ * Private default constructor
+ */
+
+ private DateTimeValue() {
+ }
+
+ /**
+ * Get the dateTime value representing the nominal
+ * date/time of this transformation run. Two calls within the same
+ * query or transformation will always return the same answer.
+ *
+ * @param context the XPath dynamic context. May be null, in which case
+ * the current date and time are taken directly from the system clock
+ * @return the current xs:dateTime
+ */
+
+ /*@Nullable*/ public static DateTimeValue getCurrentDateTime(/*@Nullable*/ XPathContext context) {
+ Controller c;
+ if (context == null || (c = context.getController()) == null) {
+ // non-XSLT/XQuery environment
+ // We also take this path when evaluating compile-time expressions that require an implicit timezone.
+ return new DateTimeValue(new GregorianCalendar(), true);
+ } else {
+ return c.getCurrentDateTime();
+ }
+ }
+
+ /**
+ * Constructor: create a dateTime value given a Java calendar object
+ *
+ * @param calendar holds the date and time
+ * @param tzSpecified indicates whether the timezone is specified
+ */
+
+ public DateTimeValue(/*@NotNull*/ Calendar calendar, boolean tzSpecified) {
+ int era = calendar.get(GregorianCalendar.ERA);
+ year = calendar.get(Calendar.YEAR);
+ if (era == GregorianCalendar.BC) {
+ year = 1 - year;
+ }
+ month = (byte)(calendar.get(Calendar.MONTH) + 1);
+ day = (byte)(calendar.get(Calendar.DATE));
+ hour = (byte)(calendar.get(Calendar.HOUR_OF_DAY));
+ minute = (byte)(calendar.get(Calendar.MINUTE));
+ second = (byte)(calendar.get(Calendar.SECOND));
+ microsecond = calendar.get(Calendar.MILLISECOND) * 1000;
+ if (tzSpecified) {
+ int tz = (calendar.get(Calendar.ZONE_OFFSET) +
+ calendar.get(Calendar.DST_OFFSET)) / 60000;
+ setTimezoneInMinutes(tz);
+ }
+ typeLabel = BuiltInAtomicType.DATE_TIME;
+ xsd10rules = true;
+ }
+
+ /**
+ * Factory method: create a dateTime value given a Java Date object. The returned dateTime
+ * value will always have a timezone, which will always be UTC.
+ *
+ * @param suppliedDate holds the date and time
+ * @return the corresponding xs:dateTime value
+ */
+
+ /*@NotNull*/ public static DateTimeValue fromJavaDate(/*@NotNull*/ Date suppliedDate) throws XPathException {
+ long millis = suppliedDate.getTime();
+ return (DateTimeValue)EPOCH.add(DayTimeDurationValue.fromMilliseconds(millis));
+ }
+
+ /**
+ * Fixed date/time used by Java (and Unix) as the origin of the universe: 1970-01-01
+ */
+
+ /*@NotNull*/ public static final DateTimeValue EPOCH =
+ new DateTimeValue(1970, (byte)1, (byte)1, (byte)0, (byte)0, (byte)0, 0, 0, true);
+
+ /**
+ * Factory method: create a dateTime value given a date and a time.
+ *
+ * @param date the date
+ * @param time the time
+ * @return the dateTime with the given components. If either component is null, returns null
+ * @throws XPathException if the timezones are both present and inconsistent
+ */
+
+ /*@Nullable*/ public static DateTimeValue makeDateTimeValue(/*@Nullable*/ DateValue date, /*@Nullable*/ TimeValue time) throws XPathException {
+ if (date == null || time == null) {
+ return null;
+ }
+ int tz1 = date.getTimezoneInMinutes();
+ int tz2 = time.getTimezoneInMinutes();
+ if (tz1 != NO_TIMEZONE && tz2 != NO_TIMEZONE && tz1 != tz2) {
+ XPathException err = new XPathException("Supplied date and time are in different timezones");
+ err.setErrorCode("FORG0008");
+ throw err;
+ }
+
+ DateTimeValue v = date.toDateTime();
+ v.hour = time.getHour();
+ v.minute = time.getMinute();
+ v.second = time.getSecond();
+ v.microsecond = time.getMicrosecond();
+ v.setTimezoneInMinutes(Math.max(tz1, tz2));
+ v.typeLabel = BuiltInAtomicType.DATE_TIME;
+ v.xsd10rules = date.xsd10rules;
+ return v;
+ }
+
+ /**
+ * Factory method: create a dateTime value from a supplied string, in
+ * ISO 8601 format
+ *
+ * @param s a string in the lexical space of xs:dateTime
+ * @param rules the conversion rules to be used (determining whether year zero is allowed)
+ * @return either a DateTimeValue representing the xs:dateTime supplied, or a ValidationFailure if
+ * the lexical value was invalid
+ */
+
+ /*@NotNull*/ public static ConversionResult makeDateTimeValue(CharSequence s, /*@NotNull*/ ConversionRules rules) {
+ // input must have format [-]yyyy-mm-ddThh:mm:ss[.fff*][([+|-]hh:mm | Z)]
+ DateTimeValue dt = new DateTimeValue();
+ dt.xsd10rules = !rules.isAllowYearZero();
+ StringTokenizer tok = new StringTokenizer(Whitespace.trimWhitespace(s).toString(), "-:.+TZ", true);
+
+ if (!tok.hasMoreElements()) {
+ return badDate("too short", s);
+ }
+ String part = (String)tok.nextElement();
+ int era = +1;
+ if ("+".equals(part)) {
+ return badDate("Date must not start with '+' sign", s);
+ } else if ("-".equals(part)) {
+ era = -1;
+ if (!tok.hasMoreElements()) {
+ return badDate("No year after '-'", s);
+ }
+ part = (String)tok.nextElement();
+ }
+ int value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ if (value == -1) {
+ return badDate("Non-numeric year component", s);
+ } else {
+ return badDate("Year is outside the range that Saxon can handle", s, "FODT0001");
+ }
+ }
+ dt.year = value * era;
+ if (part.length() < 4) {
+ return badDate("Year is less than four digits", s);
+ }
+ if (part.length() > 4 && part.charAt(0) == '0') {
+ return badDate("When year exceeds 4 digits, leading zeroes are not allowed", s);
+ }
+ if (dt.year == 0 && !rules.isAllowYearZero()) {
+ return badDate("Year zero is not allowed", s);
+ }
+ if (era < 0 && !rules.isAllowYearZero()) {
+ dt.year++; // if year zero not allowed, -0001 is the year before +0001, represented as 0 internally.
+ }
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ if (!"-".equals(tok.nextElement())) {
+ return badDate("Wrong delimiter after year", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badDate("Month must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric month component", s);
+ }
+ dt.month = (byte)value;
+ if (dt.month < 1 || dt.month > 12) {
+ return badDate("Month is out of range", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ if (!"-".equals(tok.nextElement())) {
+ return badDate("Wrong delimiter after month", s);
+ }
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badDate("Day must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric day component", s);
+ }
+ dt.day = (byte)value;
+ if (dt.day < 1 || dt.day > 31) {
+ return badDate("Day is out of range", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ if (!"T".equals(tok.nextElement())) {
+ return badDate("Wrong delimiter after day", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badDate("Hour must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric hour component", s);
+ }
+ dt.hour = (byte)value;
+ if (dt.hour > 24) {
+ return badDate("Hour is out of range", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ if (!":".equals(tok.nextElement())) {
+ return badDate("Wrong delimiter after hour", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badDate("Minute must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric minute component", s);
+ }
+ dt.minute = (byte)value;
+ if (dt.minute > 59) {
+ return badDate("Minute is out of range", s);
+ }
+ if (dt.hour == 24 && dt.minute != 0) {
+ return badDate("If hour is 24, minute must be 00", s);
+ }
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ if (!":".equals(tok.nextElement())) {
+ return badDate("Wrong delimiter after minute", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badDate("Second must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric second component", s);
+ }
+ dt.second = (byte)value;
+
+ if (dt.second > 59) {
+ return badDate("Second is out of range", s);
+ }
+ if (dt.hour == 24 && dt.second != 0) {
+ return badDate("If hour is 24, second must be 00", s);
+ }
+
+ int tz = 0;
+
+ int state = 0;
+ while (tok.hasMoreElements()) {
+ if (state == 9) {
+ return badDate("Characters after the end", s);
+ }
+ String delim = (String)tok.nextElement();
+ if (".".equals(delim)) {
+ if (state != 0) {
+ return badDate("Decimal separator occurs twice", s);
+ }
+ if (!tok.hasMoreElements()) {
+ return badDate("Decimal point must be followed by digits", s);
+ }
+ part = (String)tok.nextElement();
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric fractional seconds component", s);
+ }
+ double fractionalSeconds = Double.parseDouble('.' + part);
+ dt.microsecond = (int)(Math.round(fractionalSeconds * 1000000));
+ if (dt.hour == 24 && dt.microsecond != 0) {
+ return badDate("If hour is 24, fractional seconds must be 0", s);
+ }
+ state = 1;
+ } else if ("Z".equals(delim)) {
+ if (state > 1) {
+ return badDate("Z cannot occur here", s);
+ }
+ tz = 0;
+ state = 9; // we've finished
+ dt.setTimezoneInMinutes(0);
+ } else if ("+".equals(delim) || "-".equals(delim)) {
+ if (state > 1) {
+ return badDate(delim + " cannot occur here", s);
+ }
+ state = 2;
+ if (!tok.hasMoreElements()) {
+ return badDate("Missing timezone", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badDate("Timezone hour must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric timezone hour component", s);
+ }
+ tz = value;
+ if (tz > 14) {
+ return badDate("Timezone is out of range (-14:00 to +14:00)", s);
+ }
+ tz *= 60;
+
+ if ("-".equals(delim)) {
+ tz = -tz;
+ }
+
+ } else if (":".equals(delim)) {
+ if (state != 2) {
+ return badDate("Misplaced ':'", s);
+ }
+ state = 9;
+ part = (String)tok.nextElement();
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric timezone minute component", s);
+ }
+ int tzminute = value;
+ if (part.length() != 2) {
+ return badDate("Timezone minute must be two digits", s);
+ }
+ if (tzminute > 59) {
+ return badDate("Timezone minute is out of range", s);
+ }
+ if (tz < 0) {
+ tzminute = -tzminute;
+ }
+ if (Math.abs(tz) == 14 * 60 && tzminute != 0) {
+ return badDate("Timezone is out of range (-14:00 to +14:00)", s);
+ }
+ tz += tzminute;
+ dt.setTimezoneInMinutes(tz);
+ } else {
+ return badDate("Timezone format is incorrect", s);
+ }
+ }
+
+ if (state == 2 || state == 3) {
+ return badDate("Timezone incomplete", s);
+ }
+
+ boolean midnight = false;
+ if (dt.hour == 24) {
+ dt.hour = 0;
+ midnight = true;
+ }
+
+ // Check that this is a valid calendar date
+ if (!DateValue.isValidDate(dt.year, dt.month, dt.day)) {
+ return badDate("Non-existent date", s);
+ }
+
+ // Adjust midnight to 00:00:00 on the next day
+ if (midnight) {
+ DateValue t = DateValue.tomorrow(dt.year, dt.month, dt.day);
+ dt.year = t.getYear();
+ dt.month = t.getMonth();
+ dt.day = t.getDay();
+ }
+
+
+ dt.typeLabel = BuiltInAtomicType.DATE_TIME;
+ return dt;
+ }
+
+ private static ValidationFailure badDate(String msg, CharSequence value) {
+ ValidationFailure err = new ValidationFailure(
+ "Invalid dateTime value " + Err.wrap(value, Err.VALUE) + " (" + msg + ")");
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+
+ private static ValidationFailure badDate(String msg, CharSequence value, String errorCode) {
+ ValidationFailure err = new ValidationFailure(
+ "Invalid dateTime value " + Err.wrap(value, Err.VALUE) + " (" + msg + ")");
+ err.setErrorCode(errorCode);
+ return err;
+ }
+
+ /**
+ * Constructor: construct a DateTimeValue from its components.
+ * This constructor performs no validation.
+ *
+ * @param year The year as held internally (note that the year before 1AD is 0)
+ * @param month The month, 1-12
+ * @param day The day 1-31
+ * @param hour the hour value, 0-23
+ * @param minute the minutes value, 0-59
+ * @param second the seconds value, 0-59
+ * @param microsecond the number of microseconds, 0-999999
+ * @param tz the timezone displacement in minutes from UTC. Supply the value
+ * {@link CalendarValue#NO_TIMEZONE} if there is no timezone component.
+ * @param xsd10Check true if the dateTime value should behave under XSD 1.0 rules, that is,
+ * negative dates assume there is no year zero. (Not that regardless of this
+ * setting, the year argument is set on the basis that the year before +1 is
+ * supplied as zero; but if the xsd10Check flag is set, this value will be displayed
+ * with a year of -1.)
+ */
+
+ public DateTimeValue(int year, byte month, byte day,
+ byte hour, byte minute, byte second, int microsecond, int tz, boolean xsd10Check) {
+
+ this.xsd10rules = xsd10Check;
+ this.year = year;
+ this.month = month;
+ this.day = day;
+ this.hour = hour;
+ this.minute = minute;
+ this.second = second;
+ this.microsecond = microsecond;
+ setTimezoneInMinutes(tz);
+ typeLabel = BuiltInAtomicType.DATE_TIME;
+ }
+
+ /**
+ * Convert the value to a built-in subtype of xs:dateTime
+ * @param subtype the target subtype
+ * @return null if the conversion succeeds; a ValidationFailure describing the failure if it fails.
+ */
+
+ /*@Nullable*/ public ValidationFailure convertToSubType(/*@NotNull*/ BuiltInAtomicType subtype) {
+ if (subtype.getFingerprint() == StandardNames.XS_DATE_TIME_STAMP) {
+ if (hasTimezone()) {
+ setTypeLabel(subtype);
+ return null;
+ } else {
+ ValidationFailure err = new ValidationFailure(
+ "The value " + Err.depict(this) +
+ " is not a valid xs:dateTimeStamp: it has no timezone");
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown subtype of xs:dateTime");
+ }
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ /*@NotNull*/ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.DATE_TIME;
+ }
+
+ /**
+ * Get the year component, in its internal form (which allows a year zero)
+ *
+ * @return the year component
+ */
+
+ public int getYear() {
+ return year;
+ }
+
+ /**
+ * Get the month component, 1-12
+ *
+ * @return the month component
+ */
+
+ public byte getMonth() {
+ return month;
+ }
+
+ /**
+ * Get the day component, 1-31
+ *
+ * @return the day component
+ */
+
+ public byte getDay() {
+ return day;
+ }
+
+ /**
+ * Get the hour component, 0-23
+ *
+ * @return the hour component (never 24, even if the input was specified as 24:00:00)
+ */
+
+ public byte getHour() {
+ return hour;
+ }
+
+ /**
+ * Get the minute component, 0-59
+ *
+ * @return the minute component
+ */
+
+ public byte getMinute() {
+ return minute;
+ }
+
+ /**
+ * Get the second component, 0-59
+ *
+ * @return the second component
+ */
+
+ public byte getSecond() {
+ return second;
+ }
+
+ /**
+ * Get the microsecond component, 0-999999
+ *
+ * @return the microsecond component
+ */
+
+ public int getMicrosecond() {
+ return microsecond;
+ }
+
+ /**
+ * Convert the value to a DateTime, retaining all the components that are actually present, and
+ * substituting conventional values for components that are missing. (This method does nothing in
+ * the case of xs:dateTime, but is there to implement a method in the {@link CalendarValue} interface).
+ *
+ * @return the value as an xs:dateTime
+ */
+
+ /*@NotNull*/ public DateTimeValue toDateTime() {
+ return this;
+ }
+
+ /**
+ * Ask whether this value uses the XSD 1.0 rules (which don't allow year zero) or the XSD 1.1 rules (which do).
+ * @return true if the value uses the XSD 1.0 rules
+ */
+
+ public boolean isXsd10Rules() {
+ return xsd10rules;
+ }
+
+ /**
+ * Normalize the date and time to be in timezone Z.
+ *
+ * @param cc used to supply the implicit timezone, used when the value has
+ * no explicit timezone
+ * @return in general, a new DateTimeValue in timezone Z, representing the same instant in time.
+ * Returns the original DateTimeValue if this is already in timezone Z.
+ * @throws NoDynamicContextException if the implicit timezone is needed and is not available
+ */
+
+ /*@NotNull*/ public DateTimeValue normalize(/*@Nullable*/ XPathContext cc) throws NoDynamicContextException {
+ if (hasTimezone()) {
+ return (DateTimeValue)adjustTimezone(0);
+ } else {
+ if (cc == null){
+ throw new NoDynamicContextException("DateTime operation needs access to implicit timezone");
+ }
+ DateTimeValue dt = (DateTimeValue)copyAsSubType(null);
+ dt.setTimezoneInMinutes(cc.getImplicitTimezone());
+ return (DateTimeValue)dt.adjustTimezone(0);
+ }
+ }
+
+ /**
+ * Get a comparison key for this value. Two values are equal if and only if they their comparison
+ * keys are equal
+ * @param context XPath dynamic context
+ * @throws NoDynamicContextException if the implicit timezone is needed and is not available
+ */
+
+ /*@NotNull*/ public ComparisonKey getComparisonKey(/*@NotNull*/ XPathContext context) throws NoDynamicContextException {
+ return new ComparisonKey(StandardNames.XS_DATE_TIME, normalize(context));
+ }
+
+ /**
+ * Get the Julian instant: a decimal value whose integer part is the Julian day number
+ * multiplied by the number of seconds per day,
+ * and whose fractional part is the fraction of the second.
+ * This method operates on the local time, ignoring the timezone. The caller should call normalize()
+ * before calling this method to get a normalized time.
+ *
+ * @return the Julian instant corresponding to this xs:dateTime value
+ */
+
+ public BigDecimal toJulianInstant() {
+ int julianDay = DateValue.getJulianDayNumber(year, month, day);
+ long julianSecond = julianDay * (24L * 60L * 60L);
+ julianSecond += (((hour * 60L + minute) * 60L) + second);
+ BigDecimal j = BigDecimal.valueOf(julianSecond);
+ if (microsecond == 0) {
+ return j;
+ } else {
+ return j.add(BigDecimal.valueOf(microsecond).divide(DecimalValue.BIG_DECIMAL_ONE_MILLION, 6, BigDecimal.ROUND_HALF_EVEN));
+ }
+ }
+
+ /**
+ * Get the DateTimeValue corresponding to a given Julian instant
+ *
+ * @param instant the Julian instant: a decimal value whose integer part is the Julian day number
+ * multiplied by the number of seconds per day, and whose fractional part is the fraction of the second.
+ * @return the xs:dateTime value corresponding to the Julian instant. This will always be in timezone Z.
+ */
+
+ /*@NotNull*/ public static DateTimeValue fromJulianInstant(/*@NotNull*/ BigDecimal instant) {
+ BigInteger julianSecond = instant.toBigInteger();
+ BigDecimal microseconds = instant.subtract(new BigDecimal(julianSecond)).multiply(DecimalValue.BIG_DECIMAL_ONE_MILLION);
+ long js = julianSecond.longValue();
+ long jd = js / (24L * 60L * 60L);
+ DateValue date = DateValue.dateFromJulianDayNumber((int)jd);
+ js = js % (24L * 60L * 60L);
+ byte hour = (byte)(js / (60L * 60L));
+ js = js % (60L * 60L);
+ byte minute = (byte)(js / (60L));
+ js = js % (60L);
+ return new DateTimeValue(date.getYear(), date.getMonth(), date.getDay(),
+ hour, minute, (byte)js, microseconds.intValue(),0 , true);
+ }
+
+ /**
+ * Get a Java Calendar object representing the value of this DateTime. This will respect the timezone
+ * if there is one, or be in GMT otherwise.
+ *
+ * @return a Java GregorianCalendar object representing the value of this xs:dateTime value.
+ */
+
+ /*@NotNull*/ public GregorianCalendar getCalendar() {
+ int tz = (hasTimezone() ? getTimezoneInMinutes() * 60000 : 0);
+ TimeZone zone = new SimpleTimeZone(tz, "LLL");
+ GregorianCalendar calendar = new GregorianCalendar(zone);
+ calendar.setGregorianChange(new Date(Long.MIN_VALUE));
+ calendar.setLenient(false);
+ int yr = year;
+ if (year <= 0) {
+ yr = (xsd10rules ? 1 - year : 0 - year);
+ calendar.set(Calendar.ERA, GregorianCalendar.BC);
+ }
+ calendar.set(yr, month - 1, day, hour, minute, second);
+ calendar.set(Calendar.MILLISECOND, microsecond / 1000); // loses precision unavoidably
+ calendar.set(Calendar.ZONE_OFFSET, tz);
+// Note JDK 1.5 dependency. JDK 1.4 limited the range to +|- 12 hours, so the code was:
+// if (tz >= -12*60*60*1000 && tz <= +12*60*60*1000) {
+// calendar.set(Calendar.ZONE_OFFSET, tz);
+// }
+ calendar.set(Calendar.DST_OFFSET, 0);
+ return calendar;
+ }
+
+ /**
+ * Convert to string
+ *
+ * @return ISO 8601 representation. The value returned is the localized representation,
+ * that is it uses the timezone contained within the value itself.
+ */
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+
+ FastStringBuffer sb = new FastStringBuffer(30);
+ int yr = year;
+ if (year <= 0) {
+ yr = -yr + (xsd10rules ? 1 : 0); // no year zero in lexical space for XSD 1.0
+ if(yr!=0){
+ sb.append('-');
+ }
+ }
+ appendString(sb, yr, (yr > 9999 ? (yr + "").length() : 4));
+ sb.append('-');
+ appendTwoDigits(sb, month);
+ sb.append('-');
+ appendTwoDigits(sb, day);
+ sb.append('T');
+ appendTwoDigits(sb, hour);
+ sb.append(':');
+ appendTwoDigits(sb, minute);
+ sb.append(':');
+ appendTwoDigits(sb, second);
+ if (microsecond != 0) {
+ sb.append('.');
+ int ms = microsecond;
+ int div = 100000;
+ while (ms > 0) {
+ int d = ms / div;
+ sb.append((char)(d + '0'));
+ ms = ms % div;
+ div /= 10;
+ }
+ }
+
+ if (hasTimezone()) {
+ appendTimezone(sb);
+ }
+
+ return sb;
+
+ }
+
+ /**
+ * Extract the Date part
+ * @return a DateValue representing the date part of the dateTime, retaining the timezone or its absence
+ */
+
+ /*@NotNull*/ public DateValue toDateValue() {
+ DateValue dv = new DateValue(year, month, day, getTimezoneInMinutes(),xsd10rules);
+ return dv;
+ }
+
+ /**
+ * Extract the Time part
+ * @return a TimeValue representing the date part of the dateTime, retaining the timezone or its absence
+ */
+
+ /*@NotNull*/ public TimeValue toTimeValue() {
+ return new TimeValue(hour, minute, second, microsecond, getTimezoneInMinutes());
+ }
+
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules. For an xs:dateTime it is the
+ * date/time adjusted to UTC.
+ *
+ * @return the canonical lexical representation as defined in XML Schema
+ */
+
+ public CharSequence getCanonicalLexicalRepresentation() {
+ if (hasTimezone() && getTimezoneInMinutes() != 0) {
+ return adjustTimezone(0).getStringValueCS();
+ } else {
+ return getStringValueCS();
+ }
+ }
+
+ /**
+ * Make a copy of this date, time, or dateTime value, but with a new type label
+ *
+ * @param typeLabel the type label to be attached to the new copy. It is the caller's responsibility
+ * to ensure that the value actually conforms to the rules for this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ DateTimeValue v = new DateTimeValue(year, month, day,
+ hour, minute, second, microsecond, getTimezoneInMinutes(),xsd10rules);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Return a new dateTime with the same normalized value, but
+ * in a different timezone.
+ *
+ * @param timezone the new timezone offset, in minutes
+ * @return the date/time in the new timezone. This will be a new DateTimeValue unless no change
+ * was required to the original value
+ */
+
+ /*@NotNull*/ public CalendarValue adjustTimezone(int timezone) {
+ if (!hasTimezone()) {
+ CalendarValue in = (CalendarValue)copyAsSubType(typeLabel);
+ in.setTimezoneInMinutes(timezone);
+ return in;
+ }
+ int oldtz = getTimezoneInMinutes();
+ if (oldtz == timezone) {
+ return this;
+ }
+ int tz = timezone - oldtz;
+ int h = hour;
+ int mi = minute;
+ mi += tz;
+ if (mi < 0 || mi > 59) {
+ h += Math.floor(mi / 60.0);
+ mi = (mi + 60 * 24) % 60;
+ }
+
+ if (h >= 0 && h < 24) {
+ return new DateTimeValue(year, month, day, (byte)h, (byte)mi, second, microsecond, timezone,xsd10rules);
+ }
+
+ // Following code is designed to handle the corner case of adjusting from -14:00 to +14:00 or
+ // vice versa, which can cause a change of two days in the date
+ DateTimeValue dt = this;
+ while (h < 0) {
+ h += 24;
+ DateValue t = DateValue.yesterday(dt.getYear(), dt.getMonth(), dt.getDay());
+ dt = new DateTimeValue(t.getYear(), t.getMonth(), t.getDay(),
+ (byte)h, (byte)mi, second, microsecond, timezone,xsd10rules);
+ }
+ if (h > 23) {
+ h -= 24;
+ DateValue t = DateValue.tomorrow(year, month, day);
+ return new DateTimeValue(t.getYear(), t.getMonth(), t.getDay(),
+ (byte)h, (byte)mi, second, microsecond, timezone,xsd10rules);
+ }
+ return dt;
+ }
+
+ /**
+ * Add a duration to a dateTime
+ *
+ * @param duration the duration to be added (may be negative)
+ * @return the new date
+ * @throws net.sf.saxon.trans.XPathException
+ * if the duration is an xs:duration, as distinct from
+ * a subclass thereof
+ */
+
+ /*@NotNull*/ public CalendarValue add(/*@NotNull*/ DurationValue duration) throws XPathException {
+ if (duration instanceof DayTimeDurationValue) {
+ long microseconds = ((DayTimeDurationValue)duration).getLengthInMicroseconds();
+ BigDecimal seconds = BigDecimal.valueOf(microseconds).divide(
+ DecimalValue.BIG_DECIMAL_ONE_MILLION, 6, BigDecimal.ROUND_HALF_EVEN);
+ BigDecimal julian = toJulianInstant();
+ julian = julian.add(seconds);
+ DateTimeValue dt = fromJulianInstant(julian);
+ dt.setTimezoneInMinutes(getTimezoneInMinutes());
+ dt.xsd10rules = this.xsd10rules;
+ return dt;
+ } else if (duration instanceof YearMonthDurationValue) {
+ int months = ((YearMonthDurationValue)duration).getLengthInMonths();
+ int m = (month - 1) + months;
+ int y = year + m / 12;
+ m = m % 12;
+ if (m < 0) {
+ m += 12;
+ y -= 1;
+ }
+ m++;
+ int d = day;
+ while (!DateValue.isValidDate(y, m, d)) {
+ d -= 1;
+ }
+ return new DateTimeValue(y, (byte)m, (byte)d,
+ hour, minute, second, microsecond, getTimezoneInMinutes(),xsd10rules);
+ } else {
+ XPathException err = new XPathException("DateTime arithmetic is not supported on xs:duration, only on its subtypes");
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+
+ /**
+ * Determine the difference between two points in time, as a duration
+ *
+ * @param other the other point in time
+ * @param context the XPath dynamic context
+ * @return the duration as an xs:dayTimeDuration
+ * @throws net.sf.saxon.trans.XPathException
+ * for example if one value is a date and the other is a time
+ */
+
+ public DayTimeDurationValue subtract(/*@NotNull*/ CalendarValue other, XPathContext context) throws XPathException {
+ if (!(other instanceof DateTimeValue)) {
+ XPathException err = new XPathException("First operand of '-' is a dateTime, but the second is not");
+ err.setIsTypeError(true);
+ throw err;
+ }
+ return super.subtract(other, context);
+ }
+
+ /**
+ * Convert to Java object (for passing to external functions)
+ */
+
+// public Object convertAtomicToJava(Class target, XPathContext context) throws XPathException {
+// if (target.isAssignableFrom(Date.class)) {
+// return getCalendar().getTime();
+// } else if (target.isAssignableFrom(GregorianCalendar.class)) {
+// return getCalendar();
+// } else if (target.isAssignableFrom(DateTimeValue.class)) {
+// return this;
+// } else if (target == Object.class) {
+// return getStringValue();
+// } else {
+// Object o = super.convertSequenceToJava(target, context);
+// if (o == null) {
+// throw new XPathException("Conversion of dateTime to " + target.getName() +
+// " is not supported");
+// }
+// return o;
+// }
+// }
+//
+ /**
+ * Get a component of the value. Returns null if the timezone component is
+ * requested and is not present.
+ */
+
+ /*@Nullable*/ public AtomicValue getComponent(int component) throws XPathException {
+ switch (component) {
+ case Component.YEAR_ALLOWING_ZERO:
+ return Int64Value.makeIntegerValue(year);
+ case Component.YEAR:
+ return Int64Value.makeIntegerValue(year > 0 ? year : year - 1);
+ case Component.MONTH:
+ return Int64Value.makeIntegerValue(month);
+ case Component.DAY:
+ return Int64Value.makeIntegerValue(day);
+ case Component.HOURS:
+ return Int64Value.makeIntegerValue(hour);
+ case Component.MINUTES:
+ return Int64Value.makeIntegerValue(minute);
+ case Component.SECONDS:
+ BigDecimal d = BigDecimal.valueOf(microsecond);
+ d = d.divide(DecimalValue.BIG_DECIMAL_ONE_MILLION, 6, BigDecimal.ROUND_HALF_UP);
+ d = d.add(BigDecimal.valueOf(second));
+ return new DecimalValue(d);
+ case Component.WHOLE_SECONDS: //(internal use only)
+ return Int64Value.makeIntegerValue(second);
+ case Component.MICROSECONDS:
+ // internal use only
+ return new Int64Value(microsecond);
+ case Component.TIMEZONE:
+ if (hasTimezone()) {
+ return DayTimeDurationValue.fromMilliseconds(60000L * getTimezoneInMinutes());
+ } else {
+ return null;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown component for dateTime: " + component);
+ }
+ }
+
+ /**
+ * Compare the value to another dateTime value, following the XPath comparison semantics
+ *
+ * @param other The other dateTime value
+ * @param context XPath dynamic evaluation context
+ * @return negative value if this one is the earler, 0 if they are chronologically equal,
+ * positive value if this one is the later. For this purpose, dateTime values with an unknown
+ * timezone are considered to be values in the implicit timezone (the Comparable interface requires
+ * a total ordering).
+ * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
+ * is declared as CalendarValue to satisfy the interface)
+ * @throws NoDynamicContextException if the implicit timezone is needed and is not available
+ */
+
+ public int compareTo(/*@NotNull*/ CalendarValue other, /*@Nullable*/ XPathContext context) throws NoDynamicContextException {
+ if (!(other instanceof DateTimeValue)) {
+ throw new ClassCastException("DateTime values are not comparable to " + other.getClass());
+ }
+ DateTimeValue v2 = (DateTimeValue)other;
+ if (getTimezoneInMinutes() == v2.getTimezoneInMinutes()) {
+ // both values are in the same timezone (explicitly or implicitly)
+ if (year != v2.year) {
+ return IntegerValue.signum(year - v2.year);
+ }
+ if (month != v2.month) {
+ return IntegerValue.signum(month - v2.month);
+ }
+ if (day != v2.day) {
+ return IntegerValue.signum(day - v2.day);
+ }
+ if (hour != v2.hour) {
+ return IntegerValue.signum(hour - v2.hour);
+ }
+ if (minute != v2.minute) {
+ return IntegerValue.signum(minute - v2.minute);
+ }
+ if (second != v2.second) {
+ return IntegerValue.signum(second - v2.second);
+ }
+ if (microsecond != v2.microsecond) {
+ return IntegerValue.signum(microsecond - v2.microsecond);
+ }
+ return 0;
+ }
+ return normalize(context).compareTo(v2.normalize(context), context);
+ }
+
+ /**
+ * Context-free comparison of two DateTimeValue values. For this to work,
+ * the two values must either both have a timezone or both have none.
+ * @param v2 the other value
+ * @return the result of the comparison: -1 if the first is earlier, 0 if they
+ * are equal, +1 if the first is later
+ * @throws ClassCastException if the values are not comparable (which might be because
+ * no timezone is available)
+ */
+
+ public int compareTo(Object v2) {
+ try {
+ return compareTo((DateTimeValue)v2, null);
+ } catch (Exception err) {
+ throw new ClassCastException("DateTime comparison requires access to implicit timezone");
+ }
+ }
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return new DateTimeComparable();
+ }
+
+ /**
+ * DateTimeComparable is an object that implements the XML Schema rules for comparing date/time values
+ */
+
+ private class DateTimeComparable implements Comparable {
+
+ /*@NotNull*/ private DateTimeValue asDateTimeValue() {
+ return DateTimeValue.this;
+ }
+
+ // Rules from XML Schema Part 2
+ public int compareTo(/*@NotNull*/ Object o) {
+ if (o instanceof DateTimeComparable) {
+ DateTimeValue dt0 = DateTimeValue.this;
+ DateTimeValue dt1 = ((DateTimeComparable)o).asDateTimeValue();
+ if (dt0.hasTimezone()) {
+ if (dt1.hasTimezone()) {
+ dt0 = (DateTimeValue)dt0.adjustTimezone(0);
+ dt1 = (DateTimeValue)dt1.adjustTimezone(0);
+ return dt0.compareTo(dt1);
+ } else {
+ DateTimeValue dt1max = (DateTimeValue)dt1.adjustTimezone(14*60);
+ if (dt0.compareTo(dt1max) < 0) {
+ return -1;
+ }
+ DateTimeValue dt1min = (DateTimeValue)dt1.adjustTimezone(-14*60);
+ if (dt0.compareTo(dt1min) > 0) {
+ return +1;
+ }
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ } else {
+ if (dt1.hasTimezone()) {
+ DateTimeValue dt0min = (DateTimeValue)dt0.adjustTimezone(-14*60);
+ if (dt0min.compareTo(dt1) < 0) {
+ return -1;
+ }
+ DateTimeValue dt0max = (DateTimeValue)dt0.adjustTimezone(14*60);
+ if (dt0max.compareTo(dt1) > 0) {
+ return +1;
+ }
+ return SequenceTool.INDETERMINATE_ORDERING;
+ } else {
+ dt0 = (DateTimeValue)dt0.adjustTimezone(0);
+ dt1 = (DateTimeValue)dt1.adjustTimezone(0);
+ return dt0.compareTo(dt1);
+ }
+ }
+
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ }
+
+ public boolean equals(/*@NotNull*/ Object o) {
+ return o instanceof DateTimeComparable &&
+ DateTimeValue.this.hasTimezone() == ((DateTimeComparable)o).asDateTimeValue().hasTimezone() &&
+ compareTo(o) == 0;
+ }
+
+ public int hashCode() {
+ DateTimeValue dt0 = (DateTimeValue)adjustTimezone(0);
+ return (dt0.year<<20) ^ (dt0.month<<16) ^ (dt0.day<<11) ^
+ (dt0.hour<<7) ^ (dt0.minute<<2) ^ (dt0.second*1000000 + dt0.microsecond);
+ }
+ }
+
+ /**
+ * Context-free comparison of two dateTime values
+ * @param o the other date time value
+ * @return true if the two values represent the same instant in time
+ * @throws ClassCastException if one of the values has a timezone and the other does not
+ */
+
+ public boolean equals(Object o) {
+ //noinspection RedundantCast
+ return compareTo((DateTimeValue)o) == 0;
+ }
+
+ /**
+ * Hash code for context-free comparison of date time values. Note that equality testing
+ * and therefore hashCode() works only for values with a timezone
+ * @return a hash code
+ */
+
+ public int hashCode() {
+ return hashCode(year, month, day, hour, minute, second, microsecond, getTimezoneInMinutes());
+ }
+
+ static int hashCode(int year, byte month, byte day, byte hour, byte minute, byte second, int microsecond, int tzMinutes) {
+ int tz = -tzMinutes;
+ int h = hour;
+ int mi = minute;
+ mi += tz;
+ if (mi < 0 || mi > 59) {
+ h += Math.floor(mi / 60.0);
+ mi = (mi + 60 * 24) % 60;
+ }
+ while (h < 0) {
+ h += 24;
+ DateValue t = DateValue.yesterday(year, month, day);
+ year = t.getYear();
+ month = t.getMonth();
+ day = t.getDay();
+ }
+ while (h > 23) {
+ h -= 24;
+ DateValue t = DateValue.tomorrow(year, month, day);
+ year = t.getYear();
+ month = t.getMonth();
+ day = t.getDay();
+ }
+ return (year<<4) ^ (month<<28) ^ (day<<23) ^ (h<<18) ^ (mi<<13) ^ second ^ microsecond;
+
+ }
+
+}
+
diff --git a/sf/saxon/value/DateValue.java b/sf/saxon/value/DateValue.java
new file mode 100644
index 0000000..f13e6b2
--- /dev/null
+++ b/sf/saxon/value/DateValue.java
@@ -0,0 +1,544 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationException;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+/**
+ * A value of type Date. Note that a Date may include a TimeZone.
+ */
+
+public class DateValue extends GDateValue implements Comparable {
+
+ /**
+ * Private constructor of a skeletal DateValue
+ */
+
+ private DateValue() {
+ }
+
+ /**
+ * Constructor given a year, month, and day. Performs no validation.
+ *
+ * @param year The year as held internally (note that the year before 1AD is supplied as 0,
+ * but will be displayed on output as -0001)
+ * @param month The month, 1-12
+ * @param day The day, 1-31
+ */
+
+ public DateValue(int year, byte month, byte day) {
+ this.xsd10rules = true;
+ this.year = year;
+ this.month = month;
+ this.day = day;
+ typeLabel = BuiltInAtomicType.DATE;
+ }
+
+ /**
+ * Constructor given a year, month, day and xsd10. Performs no validation.
+ *
+ * @param year The year as held internally (note that the year before 1AD is supplied as 0)
+ * @param month The month, 1-12
+ * @param day The day, 1-31
+ * @param xsd10 Schema version flag. If set to true, the year before 1AD is displayed as -0001;
+ * if false, it is displayed as 0000; but in both cases, it is held internally as zero. The flag
+ * makes no difference to dates later than 1AD.
+ */
+
+ public DateValue(int year, byte month, byte day, boolean xsd10) {
+ this.xsd10rules = xsd10;
+ this.year = year;
+ this.month = month;
+ this.day = day;
+ typeLabel = BuiltInAtomicType.DATE;
+ }
+
+ /**
+ * Constructor given a year, month, and day, and timezone. Performs no validation.
+ *
+ * @param year The year as held internally (note that the year before 1AD is 0)
+ * @param month The month, 1-12
+ * @param day The day, 1-31
+ * @param tz the timezone displacement in minutes from UTC. Supply the value
+ * {@link CalendarValue#NO_TIMEZONE} if there is no timezone component.
+ * @param xsd10 Schema version flag. If set to true, the year before 1AD is displayed as -0001;
+ * if false, it is displayed as 0000; but in both cases, it is held internally as zero. The flag
+ * makes no difference to dates later than 1AD.
+ */
+
+ public DateValue(int year, byte month, byte day, int tz, boolean xsd10) {
+ // Method is called by generated Java code.
+ this.xsd10rules = xsd10;
+ this.year = year;
+ this.month = month;
+ this.day = day;
+ setTimezoneInMinutes(tz);
+ typeLabel = BuiltInAtomicType.DATE;
+ }
+
+ /**
+ * Constructor given a year, month, and day, and timezone, and an AtomicType representing
+ * a subtype of xs:date. Performs no validation.
+ *
+ * @param year The year as held internally (note that the year before 1AD is 0)
+ * @param month The month, 1-12
+ * @param day The day 1-31
+ * @param tz the timezone displacement in minutes from UTC. Supply the value
+ * {@link CalendarValue#NO_TIMEZONE} if there is no timezone component.
+ * @param type the type. This must be a type derived from xs:date, and the value
+ * must conform to this type. The method does not check these conditions.
+ */
+
+ public DateValue(int year, byte month, byte day, int tz, AtomicType type) {
+ this.year = year;
+ this.month = month;
+ this.day = day;
+ setTimezoneInMinutes(tz);
+ typeLabel = type;
+ }
+
+ /**
+ * Constructor: create a date value from a supplied string, in
+ * ISO 8601 format
+ *
+ * @param s the lexical form of the date value. Currently this assumes XSD 1.0
+ * conventions for BC years (that is, no year zero), but this may change at some time.
+ * @throws ValidationException if the supplied string is not a valid date
+ */
+ public DateValue(CharSequence s) throws ValidationException {
+ this(s, new ConversionRules());
+ }
+
+ /**
+ * Constructor: create a date value from a supplied string, in
+ * ISO 8601 format
+ *
+ * @param s the lexical form of the date value
+ * @param rules the conversion rules (determining whether year zero is allowed)
+ * @throws ValidationException if the supplied string is not a valid date
+ */
+ public DateValue(CharSequence s, ConversionRules rules) throws ValidationException {
+ setLexicalValue(this, s, rules).asAtomic();
+ typeLabel = BuiltInAtomicType.DATE;
+ }
+
+ /**
+ * Create a DateValue
+ *
+ * @param calendar the absolute date/time value
+ * @param tz The timezone offset from GMT in minutes, positive or negative; or the special
+ * value NO_TIMEZONE indicating that the value is not in a timezone
+ */
+ public DateValue(GregorianCalendar calendar, int tz) {
+ // Note: this constructor is not used by Saxon itself, but might be used by applications
+ int era = calendar.get(GregorianCalendar.ERA);
+ year = calendar.get(Calendar.YEAR);
+ if (era == GregorianCalendar.BC) {
+ year = 1 - year;
+ }
+ month = (byte) (calendar.get(Calendar.MONTH) + 1);
+ day = (byte) (calendar.get(Calendar.DATE));
+ setTimezoneInMinutes(tz);
+ typeLabel = BuiltInAtomicType.DATE;
+ }
+
+ /**
+ * Static factory method: construct a DateValue from a string in the lexical form
+ * of a date, returning a ValidationFailure if the supplied string is invalid
+ *
+ * @param in the lexical form of the date
+ * @param rules
+ * @return either a DateValue or a ValidationFailure
+ */
+
+ public static ConversionResult makeDateValue(CharSequence in, ConversionRules rules) {
+ DateValue d = new DateValue();
+ d.typeLabel = BuiltInAtomicType.DATE;
+ return setLexicalValue(d, in, rules);
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.DATE;
+ }
+
+ /**
+ * Get the date that immediately follows a given date
+ *
+ * @param year the year
+ * @param month the month (1-12)
+ * @param day the day (1-31)
+ * @return a new DateValue with no timezone information
+ */
+
+ public static DateValue tomorrow(int year, byte month, byte day) {
+ if (DateValue.isValidDate(year, month, day + 1)) {
+ return new DateValue(year, month, (byte) (day + 1), true);
+ } else if (month < 12) {
+ return new DateValue(year, (byte) (month + 1), (byte) 1, true);
+ } else {
+ return new DateValue(year + 1, (byte) 1, (byte) 1, true);
+ }
+ }
+
+ /**
+ * Get the date that immediately precedes a given date
+ *
+ * @param year the year
+ * @param month the month (1-12)
+ * @param day the day (1-31)
+ * @return a new DateValue with no timezone information
+ */
+
+ public static DateValue yesterday(int year, byte month, byte day) {
+ if (day > 1) {
+ return new DateValue(year, month, (byte) (day - 1), true);
+ } else if (month > 1) {
+ if (month == 3 && isLeapYear(year)) {
+ return new DateValue(year, (byte) 2, (byte) 29, true);
+ } else {
+ return new DateValue(year, (byte) (month - 1), daysPerMonth[month - 2], true);
+ }
+ } else {
+ return new DateValue(year - 1, (byte) 12, (byte) 31, true);
+ }
+ }
+
+ /**
+ * Convert to string
+ *
+ * @return ISO 8601 representation.
+ */
+
+ public CharSequence getPrimitiveStringValue() {
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+ int yr = year;
+ if (year <= 0) {
+ yr = -yr + (xsd10rules ? 1 : 0); // no year zero in lexical space for XSD 1.0
+ if (yr != 0) {
+ sb.append('-');
+ }
+ }
+ appendString(sb, yr, (yr > 9999 ? (yr + "").length() : 4));
+ sb.append('-');
+ appendTwoDigits(sb, month);
+ sb.append('-');
+ appendTwoDigits(sb, day);
+
+ if (hasTimezone()) {
+ appendTimezone(sb);
+ }
+
+ return sb;
+
+ }
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules. For xs:date, the timezone is
+ * adjusted to be in the range +12:00 to -11:59
+ *
+ * @return the canonical lexical representation if defined in XML Schema; otherwise, the result
+ * of casting to string according to the XPath 2.0 rules
+ */
+
+ public CharSequence getCanonicalLexicalRepresentation() {
+ DateValue target = this;
+ if (hasTimezone()) {
+ if (getTimezoneInMinutes() > 12 * 60) {
+ target = (DateValue) adjustTimezone(getTimezoneInMinutes() - 24 * 60);
+ } else if (getTimezoneInMinutes() <= -12 * 60) {
+ target = (DateValue) adjustTimezone(getTimezoneInMinutes() + 24 * 60);
+ }
+ }
+ return target.getStringValueCS();
+ }
+
+ /**
+ * Make a copy of this date value, but with a new type label
+ *
+ * @param typeLabel the new type label: must be a subtype of xs:date
+ * @return the new xs:date value
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ DateValue v = new DateValue(year, month, day, getTimezoneInMinutes(), xsd10rules);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Return a new date with the same normalized value, but
+ * in a different timezone. This is called only for a DateValue that has an explicit timezone
+ *
+ * @param timezone the new timezone offset, in minutes
+ * @return the time in the new timezone. This will be a new TimeValue unless no change
+ * was required to the original value
+ */
+
+ public CalendarValue adjustTimezone(int timezone) {
+ DateTimeValue dt = (DateTimeValue) toDateTime().adjustTimezone(timezone);
+ return new DateValue(dt.getYear(), dt.getMonth(), dt.getDay(), dt.getTimezoneInMinutes(), xsd10rules);
+ }
+
+ /**
+ * Add a duration to a date
+ *
+ * @param duration the duration to be added (may be negative)
+ * @return the new date
+ * @throws net.sf.saxon.trans.XPathException
+ * if the duration is an xs:duration, as distinct from
+ * a subclass thereof
+ */
+
+ public CalendarValue add(DurationValue duration) throws XPathException {
+ if (duration instanceof DayTimeDurationValue) {
+ long microseconds = ((DayTimeDurationValue) duration).getLengthInMicroseconds();
+ boolean negative = (microseconds < 0);
+ microseconds = Math.abs(microseconds);
+ int days = (int) Math.floor((double) microseconds / (1000000L * 60L * 60L * 24L));
+ boolean partDay = (microseconds % (1000000L * 60L * 60L * 24L)) > 0;
+ int julian = getJulianDayNumber(year, month, day);
+ DateValue d = dateFromJulianDayNumber(julian + (negative ? -days : days));
+ if (partDay) {
+ if (negative) {
+ d = yesterday(d.year, d.month, d.day);
+ }
+ }
+ d.setTimezoneInMinutes(getTimezoneInMinutes());
+ d.xsd10rules = this.xsd10rules;
+ return d;
+ } else if (duration instanceof YearMonthDurationValue) {
+ int months = ((YearMonthDurationValue) duration).getLengthInMonths();
+ int m = (month - 1) + months;
+ int y = year + m / 12;
+ m = m % 12;
+ if (m < 0) {
+ m += 12;
+ y -= 1;
+ }
+ m++;
+ int d = day;
+ while (!isValidDate(y, m, d)) {
+ d -= 1;
+ }
+ return new DateValue(y, (byte) m, (byte) d, getTimezoneInMinutes(), xsd10rules);
+ } else {
+ XPathException err = new XPathException("Date arithmetic is not supported on xs:duration, only on its subtypes");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+ }
+
+ /**
+ * Determine the difference between two points in time, as a duration
+ *
+ * @param other the other point in time
+ * @param context the XPath dynamic context. May be set to null
+ * only if both values contain an explicit timezone, or if neither does so.
+ * @return the duration as an xs:dayTimeDuration
+ * @throws XPathException for example if one value is a date and the other is a time
+ */
+
+ public DayTimeDurationValue subtract(/*@NotNull*/ CalendarValue other, /*@Nullable*/ XPathContext context) throws XPathException {
+ if (!(other instanceof DateValue)) {
+ XPathException err = new XPathException("First operand of '-' is a date, but the second is not");
+ err.setIsTypeError(true);
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+ return super.subtract(other, context);
+ }
+
+
+ /**
+ * Context-free comparison of two DateValue values. For this to work,
+ * the two values must either both have a timezone or both have none.
+ *
+ * @param v2 the other value
+ * @return the result of the comparison: -1 if the first is earlier, 0 if they
+ * are equal, +1 if the first is later
+ * @throws ClassCastException if the values are not comparable (which might be because
+ * no timezone is available)
+ */
+
+ public int compareTo(Object v2) {
+ try {
+ return compareTo((DateValue) v2, null);
+ } catch (Exception err) {
+ throw new ClassCastException("DateTime comparison requires access to implicit timezone");
+ }
+ }
+
+ /**
+ * Calculate the Julian day number at 00:00 on a given date. This algorithm is taken from
+ * http://vsg.cape.com/~pbaum/date/jdalg.htm and
+ * http://vsg.cape.com/~pbaum/date/jdalg2.htm
+ * (adjusted to handle BC dates correctly)
+ * <p/>
+ * <p>Note that this assumes dates in the proleptic Gregorian calendar</p>
+ *
+ * @param year the year
+ * @param month the month (1-12)
+ * @param day the day (1-31)
+ * @return the Julian day number
+ */
+
+ public static int getJulianDayNumber(int year, int month, int day) {
+ int z = year - (month < 3 ? 1 : 0);
+ short f = monthData[month - 1];
+ if (z >= 0) {
+ return day + f + 365 * z + z / 4 - z / 100 + z / 400 + 1721118;
+ } else {
+ // for negative years, add 12000 years and then subtract the days!
+ z += 12000;
+ int j = day + f + 365 * z + z / 4 - z / 100 + z / 400 + 1721118;
+ return j - (365 * 12000 + 12000 / 4 - 12000 / 100 + 12000 / 400); // number of leap years in 12000 years
+ }
+ }
+
+ /**
+ * Calculate the Julian day number at 00:00 on this date.
+ * <p>Note that this assumes dates in the proleptic Gregorian calendar</p>
+ *
+ * @return the Julian day number
+ */
+
+ public int getJulianDayNumber() {
+ return getJulianDayNumber(year, month, day);
+ }
+
+ /**
+ * Get the Gregorian date corresponding to a particular Julian day number. The algorithm
+ * is taken from http://www.hermetic.ch/cal_stud/jdn.htm#comp
+ *
+ * @param julianDayNumber the Julian day number
+ * @return a DateValue with no timezone information set
+ */
+
+ public static DateValue dateFromJulianDayNumber(int julianDayNumber) {
+ if (julianDayNumber >= 0) {
+ int L = julianDayNumber + 68569 + 1; // +1 adjustment for days starting at noon
+ int n = (4 * L) / 146097;
+ L = L - (146097 * n + 3) / 4;
+ int i = (4000 * (L + 1)) / 1461001;
+ L = L - (1461 * i) / 4 + 31;
+ int j = (80 * L) / 2447;
+ int d = L - (2447 * j) / 80;
+ L = j / 11;
+ int m = j + 2 - (12 * L);
+ int y = 100 * (n - 49) + i + L;
+ return new DateValue(y, (byte) m, (byte) d, true);
+ } else {
+ // add 12000 years and subtract them again...
+ DateValue dt = dateFromJulianDayNumber(julianDayNumber +
+ (365 * 12000 + 12000 / 4 - 12000 / 100 + 12000 / 400));
+ dt.year -= 12000;
+ return dt;
+ }
+ }
+
+ /**
+ * Get the ordinal day number within the year (1 Jan = 1, 1 Feb = 32, etc)
+ *
+ * @param year the year
+ * @param month the month (1-12)
+ * @param day the day (1-31)
+ * @return the ordinal day number within the year
+ */
+
+ public static int getDayWithinYear(int year, int month, int day) {
+ int j = getJulianDayNumber(year, month, day);
+ int k = getJulianDayNumber(year, 1, 1);
+ return j - k + 1;
+ }
+
+ /**
+ * Get the day of the week. The days of the week are numbered from
+ * 1 (Monday) to 7 (Sunday)
+ *
+ * @param year the year
+ * @param month the month (1-12)
+ * @param day the day (1-31)
+ * @return the day of the week, 1=Monday .... 7=Sunday
+ */
+
+ public static int getDayOfWeek(int year, int month, int day) {
+ int d = getJulianDayNumber(year, month, day);
+ d -= 2378500; // 1800-01-05 - any Monday would do
+ while (d <= 0) {
+ d += 70000000; // any sufficiently high multiple of 7 would do
+ }
+ return (d - 1) % 7 + 1;
+ }
+
+ /**
+ * Get the ISO week number for a given date. The days of the week are numbered from
+ * 1 (Monday) to 7 (Sunday), and week 1 in any calendar year is the week (from Monday to Sunday)
+ * that includes the first Thursday of that year
+ *
+ * @param year the year
+ * @param month the month (1-12)
+ * @param day the day (1-31)
+ * @return the ISO week number
+ */
+
+ public static int getWeekNumber(int year, int month, int day) {
+ int d = getDayWithinYear(year, month, day);
+ int firstDay = getDayOfWeek(year, 1, 1);
+ if (firstDay > 4 && (firstDay + d) <= 8) {
+ // days before week one are part of the last week of the previous year (52 or 53)
+ return getWeekNumber(year - 1, 12, 31);
+ }
+ int inc = (firstDay < 5 ? 1 : 0); // implements the First Thursday rule
+ return ((d + firstDay - 2) / 7) + inc;
+
+ }
+
+ /**
+ * Get the week number within a month. This is required for the XSLT format-date() function.
+ * The days of the week are numbered from 1 (Monday) to 7 (Sunday), and week 1
+ * in any calendar month is the week (from Monday to Sunday) that includes the first Thursday
+ * of that month.
+ *
+ * <p>See bug 21370 which clarified the specification. This caused a change to the Saxon
+ * implementation such that the days before the start of week 1 go in week 5, not week zero.</p>
+ *
+ * @param year the year
+ * @param month the month (1-12)
+ * @param day the day (1-31)
+ * @return the week number within a month
+ */
+
+ public static int getWeekNumberWithinMonth(int year, int month, int day) {
+ int firstDay = getDayOfWeek(year, month, 1);
+ int inc = (firstDay < 5 ? 1 : 5); // implements the First Thursday rule
+ int wk = ((day + firstDay - 2) / 7) + inc;
+ return (wk > 5 ? wk-5 : wk);
+ }
+
+}
+
diff --git a/sf/saxon/value/DayTimeDurationValue.java b/sf/saxon/value/DayTimeDurationValue.java
new file mode 100644
index 0000000..90fc198
--- /dev/null
+++ b/sf/saxon/value/DayTimeDurationValue.java
@@ -0,0 +1,447 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.*;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A value of type xs:dayTimeDuration
+ */
+
+public final class DayTimeDurationValue extends DurationValue implements Comparable {
+
+ /**
+ * Private constructor for internal use
+ */
+
+ private DayTimeDurationValue() {
+ typeLabel = BuiltInAtomicType.DAY_TIME_DURATION;
+ }
+
+ /**
+ * Factory method: create a duration value from a supplied string, in
+ * ISO 8601 format [-]PnDTnHnMnS
+ *
+ * @param s the lexical representation of the xs:dayTimeDuration value
+ * @return a DayTimeDurationValue if the format is correct, or a ValidationErrorValue if not
+ */
+
+ public static ConversionResult makeDayTimeDurationValue(CharSequence s) {
+ ConversionResult d = DurationValue.makeDuration(s, false, true);
+ if (d instanceof ValidationFailure) {
+ return d;
+ }
+ DurationValue dv = (DurationValue)d;
+ return Converter.DURATION_TO_DAY_TIME_DURATION.convert(dv);
+ //return dv.convertPrimitive(BuiltInAtomicType.DAY_TIME_DURATION, false, null);
+ }
+
+ /**
+ * Create a dayTimeDuration given the number of days, hours, minutes, and seconds. This
+ * constructor performs no validation. The components (apart from sign) must all be non-negative
+ * integers; they need not be normalized (for example, 36 hours is acceptable)
+ *
+ * @param sign positive number for positive durations, negative for negative duratoins
+ * @param days number of days
+ * @param hours number of hours
+ * @param minutes number of minutes
+ * @param seconds number of seconds
+ * @param microseconds number of microseconds
+ * @throws IllegalArgumentException if the value is out of range; specifically, if the total
+ * number of seconds exceeds 2^63; or if any of the values is negative
+ */
+
+ public DayTimeDurationValue(int sign, int days, int hours, int minutes, long seconds, int microseconds)
+ throws IllegalArgumentException {
+ if (days < 0 || hours < 0 || minutes < 0 || seconds < 0 || microseconds < 0) {
+ throw new IllegalArgumentException("Negative component value");
+ }
+ if (((double)days)*(24*60*60) + ((double)hours)*(60*60) +
+ ((double)minutes)*60 + (double)seconds > Long.MAX_VALUE) {
+ throw new IllegalArgumentException("Duration seconds limit exceeded");
+ }
+ negative = (sign < 0);
+ months = 0;
+ long h = (long)days * 24L + (long)hours;
+ long m = h * 60L + (long)minutes;
+ long s = m * 60L + seconds;
+ if (microseconds > 1000000) {
+ s += microseconds / 1000000;
+ microseconds %= 1000000;
+ }
+ this.seconds = s;
+ this.microseconds = microseconds;
+ if (s == 0 && microseconds == 0) {
+ negative = false;
+ }
+ typeLabel = BuiltInAtomicType.DAY_TIME_DURATION;
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ DayTimeDurationValue v = DayTimeDurationValue.fromMicroseconds(getLengthInMicroseconds());
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.DAY_TIME_DURATION;
+ }
+
+ /**
+ * Convert to string
+ *
+ * @return ISO 8601 representation.
+ */
+
+ public CharSequence getPrimitiveStringValue() {
+
+ FastStringBuffer sb = new FastStringBuffer(32);
+ if (negative) {
+ sb.append('-');
+ }
+
+ int days = getDays();
+ int hours = getHours();
+ int minutes = getMinutes();
+ int seconds = getSeconds();
+
+ sb.append('P');
+ if (days != 0) {
+ sb.append(days + "D");
+ }
+ if (days == 0 || hours != 0 || minutes != 0 || seconds != 0 || microseconds != 0) {
+ sb.append('T');
+ }
+ if (hours != 0) {
+ sb.append(hours + "H");
+ }
+ if (minutes != 0) {
+ sb.append(minutes + "M");
+ }
+ if (seconds != 0 || microseconds != 0 || (days == 0 && minutes == 0 && hours == 0)) {
+ if (microseconds == 0) {
+ sb.append(seconds + "S");
+ } else {
+ long ms = (seconds * 1000000) + microseconds;
+ String mss = ms + "";
+ if (seconds == 0) {
+ mss = "0000000" + mss;
+ mss = mss.substring(mss.length() - 7);
+ }
+ sb.append(mss.substring(0, mss.length() - 6));
+ sb.append('.');
+ int lastSigDigit = mss.length() - 1;
+ while (mss.charAt(lastSigDigit) == '0') {
+ lastSigDigit--;
+ }
+ sb.append(mss.substring(mss.length() - 6, lastSigDigit + 1));
+ sb.append('S');
+ }
+ }
+ return sb;
+ }
+
+// /**
+// * Normalize the value, for example 90M becomes 1H30M
+// */
+//
+// public void normalize() throws ValidationException {
+// long seconds2 = seconds;
+// long minutes2 = minutes;
+// long hours2 = hours;
+// long days2 = days;
+// if (microseconds >= 1000000) {
+// seconds2 += (microseconds / 1000000);
+// microseconds = microseconds % 1000000;
+// }
+// if (seconds >= 60) {
+// minutes2 += (seconds2 / 60);
+// seconds2 = (int)(seconds2 % 60);
+// }
+// if (minutes2 >= 60) {
+// hours2 += (minutes2 / 60);
+// minutes2 = (int)(minutes2 % 60);
+// }
+// if (hours2 >= 24) {
+// days2 += (hours2 / 24);
+// if (days2 > Integer.MAX_VALUE || days2 < Integer.MIN_VALUE) {
+// throw new ValidationException("Duration exceeds implementation-defined limits");
+// }
+// hours2 = (int)(hours2 % 24);
+// }
+// days = (int)days2;
+// hours = (int)hours2;
+// minutes = (int)minutes2;
+// seconds = (int)seconds2;
+// normalizeZeroDuration();
+// normalized = true;
+// }
+
+ /**
+ * Get length of duration in seconds
+ */
+
+ public double getLengthInSeconds() {
+ double a = seconds + ((double)microseconds / 1000000);
+ // System.err.println("Duration length " + days + "/" + hours + "/" + minutes + "/" + seconds + " is " + a);
+ return (negative ? -a : a);
+ }
+
+ /**
+ * Get length of duration in microseconds, as a long
+ *
+ * @return the length in microseconds
+ */
+
+ public long getLengthInMicroseconds() {
+ long a = seconds * 1000000 + microseconds;
+ return (negative ? -a : a);
+ }
+
+
+ /**
+ * Construct a duration value as a number of seconds.
+ *
+ * @param seconds the number of seconds in the duration. May be negative
+ * @return the xs:dayTimeDuration value with the specified length
+ */
+
+ public static DayTimeDurationValue fromSeconds(BigDecimal seconds) {
+ DayTimeDurationValue sdv = new DayTimeDurationValue();
+ sdv.negative = (seconds.signum() < 0);
+ if (sdv.negative) {
+ seconds = seconds.negate();
+ }
+ BigDecimal microseconds = seconds.multiply(DecimalValue.BIG_DECIMAL_ONE_MILLION);
+ BigInteger intMicros = microseconds.toBigInteger();
+ BigInteger[] parts = intMicros.divideAndRemainder(BigInteger.valueOf(1000000));
+ sdv.seconds = parts[0].longValue();
+ sdv.microseconds = parts[1].intValue();
+ return sdv;
+ }
+
+ /**
+ * Construct a duration value as a number of milliseconds.
+ *
+ * @param milliseconds the number of milliseconds in the duration (may be negative)
+ * @return the corresponding xs:dayTimeDuration value
+ * @throws ValidationException if implementation-defined limits are exceeded, specifically
+ * if the total number of seconds exceeds 2^63.
+ */
+
+ public static DayTimeDurationValue fromMilliseconds(long milliseconds) throws ValidationException {
+ int sign = Long.signum(milliseconds);
+ // Note JDK 1.5 dependency on Long.signum
+ if (sign < 0) {
+ milliseconds = -milliseconds;
+ }
+ try {
+ return new DayTimeDurationValue(
+ sign, 0, 0, 0, milliseconds / 1000, (int)(milliseconds % 1000) * 1000);
+ } catch (IllegalArgumentException err) {
+ // limits exceeded
+ throw new ValidationException("Duration exceeds limits");
+ }
+ }
+
+ /**
+ * Construct a duration value as a number of microseconds.
+ *
+ * @param microseconds the number of microseconds in the duration. The maximum and minimum
+ * limits are such that the number of days in the duration must fit in a 32-bit signed integer.
+ * @return the xs:dayTimeDuration represented by the given number of microseconds
+ * @throws IllegalArgumentException if the value is out of range.
+ */
+
+ public static DayTimeDurationValue fromMicroseconds(long microseconds) throws IllegalArgumentException {
+ int sign = Long.signum(microseconds);
+ // Note JDK 1.5 dependency on Long.signum()
+ if (sign < 0) {
+ microseconds = -microseconds;
+ }
+ return new DayTimeDurationValue(
+ sign, 0, 0, 0, microseconds / 1000000, (int)(microseconds % 1000000));
+
+ }
+
+
+ /**
+ * Multiply duration by a number. This is also used when dividing a duration by a number.
+ */
+
+ public DurationValue multiply(double n) throws XPathException {
+ if (Double.isNaN(n)) {
+ XPathException err = new XPathException("Cannot multiply/divide a duration by NaN");
+ err.setErrorCode("FOCA0005");
+ throw err;
+ }
+ double m = (double)getLengthInMicroseconds();
+ double product = n * m;
+ if (Double.isInfinite(product) || Double.isNaN(product) ||
+ product > Long.MAX_VALUE || product < Long.MIN_VALUE) {
+ XPathException err = new XPathException("Overflow when multiplying/dividing a duration by a number");
+ err.setErrorCode("FODT0002");
+ throw err;
+ }
+ try {
+ return fromMicroseconds((long)product);
+ } catch (IllegalArgumentException err) {
+ if (err.getCause() instanceof XPathException) {
+ throw (XPathException)err.getCause();
+ } else {
+ XPathException err2 = new XPathException("Overflow when multiplying/dividing a duration by a number", err);
+ err2.setErrorCode("FODT0002");
+ throw err2;
+ }
+ }
+ }
+
+ /**
+ * Find the ratio between two durations
+ *
+ * @param other the dividend
+ * @return the ratio, as a decimal
+ * @throws XPathException
+ */
+ public DecimalValue divide(DurationValue other) throws XPathException {
+ if (other instanceof DayTimeDurationValue) {
+ BigDecimal v1 = BigDecimal.valueOf(getLengthInMicroseconds());
+ BigDecimal v2 = BigDecimal.valueOf(((DayTimeDurationValue)other).getLengthInMicroseconds());
+ if (v2.signum() == 0) {
+ XPathException err = new XPathException("Divide by zero (durations)");
+ err.setErrorCode("FOAR0001");
+ throw err;
+ }
+ return new DecimalValue(v1.divide(v2, 20, BigDecimal.ROUND_HALF_EVEN));
+ } else {
+ XPathException err = new XPathException("Cannot divide two durations of different type");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+ }
+
+ /**
+ * Add two dayTimeDurations
+ */
+
+ public DurationValue add(DurationValue other) throws XPathException {
+ if (other instanceof DayTimeDurationValue) {
+ try {
+ return fromMicroseconds(getLengthInMicroseconds() +
+ ((DayTimeDurationValue)other).getLengthInMicroseconds());
+ } catch (IllegalArgumentException e) {
+ XPathException err = new XPathException("Overflow when adding two durations");
+ err.setErrorCode("FODT0002");
+ throw err;
+ }
+ } else {
+ XPathException err = new XPathException("Cannot add two durations of different type");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+ }
+
+ /**
+ * Subtract two dayTime-durations
+ */
+
+ public DurationValue subtract(DurationValue other) throws XPathException {
+ if (other instanceof DayTimeDurationValue) {
+ try {
+ return fromMicroseconds(getLengthInMicroseconds() -
+ ((DayTimeDurationValue)other).getLengthInMicroseconds());
+ } catch (IllegalArgumentException e) {
+ XPathException err = new XPathException("Overflow when subtracting two durations");
+ err.setErrorCode("FODT0002");
+ throw err;
+ }
+ } else {
+ XPathException err = new XPathException("Cannot subtract two durations of different type");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+ }
+
+ /**
+ * Negate a duration (same as subtracting from zero, but it preserves the type of the original duration)
+ *
+ * @throws IllegalArgumentException in the extremely unlikely event that the duration is one that cannot
+ * be negated (because the limit for positive durations is one second
+ * off from the limit for negative durations)
+ */
+
+ public DurationValue negate() throws IllegalArgumentException {
+ return fromMicroseconds(-getLengthInMicroseconds());
+ }
+
+ /**
+ * Compare the value to another duration value
+ *
+ * @param other The other dateTime value
+ * @return negative value if this one is the earler, 0 if they are chronologically equal,
+ * positive value if this one is the later. For this purpose, dateTime values with an unknown
+ * timezone are considered to be UTC values (the Comparable interface requires
+ * a total ordering).
+ * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
+ * is declared as Object to satisfy the Comparable interface)
+ */
+
+ public int compareTo(Object other) {
+ if (other instanceof DayTimeDurationValue) {
+ long diff = getLengthInMicroseconds() - ((DayTimeDurationValue)other).getLengthInMicroseconds();
+ if (diff < 0) {
+ return -1;
+ } else if (diff > 0) {
+ return +1;
+ } else {
+ return 0;
+ }
+ } else {
+ throw new ClassCastException("Cannot compare a dayTimeDuration to an object of class "
+ + other.getClass());
+ }
+ }
+
+ /**
+ * Get a Comparable value that implements the XPath ordering comparison semantics for this value.
+ * Returns null if the value is not comparable according to XPath rules. The default implementation
+ * returns the value itself. This is modified for types such as
+ * xs:duration which allow ordering comparisons in XML Schema, but not in XPath.
+ * @param ordered true if an ordered comparable is needed
+ * @param collator Collation used for string comparison
+ * @param context XPath dynamic context
+ */
+
+ public Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context) {
+ return this;
+ }
+
+
+}
+
diff --git a/sf/saxon/value/DecimalValue.java b/sf/saxon/value/DecimalValue.java
new file mode 100644
index 0000000..c3e52b9
--- /dev/null
+++ b/sf/saxon/value/DecimalValue.java
@@ -0,0 +1,579 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.*;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.regex.Pattern;
+
+/**
+* A decimal value
+*/
+
+public final class DecimalValue extends NumericValue {
+
+ public static final int DIVIDE_PRECISION = 18;
+
+ private BigDecimal value;
+
+ public static final BigDecimal BIG_DECIMAL_ONE_MILLION = BigDecimal.valueOf(1000000);
+
+ public static final DecimalValue ZERO = new DecimalValue(BigDecimal.valueOf(0));
+ public static final DecimalValue ONE = new DecimalValue(BigDecimal.valueOf(1));
+ public static final DecimalValue ONE_POINT_ONE = new DecimalValue(BigDecimal.valueOf(11, 1));
+ public static final DecimalValue TWO = new DecimalValue(BigDecimal.valueOf(2));
+ public static final DecimalValue TWO_POINT_ONE = new DecimalValue(BigDecimal.valueOf(21, 1));
+ public static final DecimalValue THREE = new DecimalValue(BigDecimal.valueOf(3));
+
+ /**
+ * Constructor supplying a BigDecimal
+ * @param value the value of the DecimalValue
+ */
+
+ public DecimalValue(BigDecimal value) {
+ this.value = value.stripTrailingZeros();
+ typeLabel = BuiltInAtomicType.DECIMAL;
+ }
+
+ private static final Pattern decimalPattern = Pattern.compile("(\\-|\\+)?((\\.[0-9]+)|([0-9]+(\\.[0-9]*)?))");
+
+ /**
+ * Factory method to construct a DecimalValue from a string
+ * @param in the value of the DecimalValue
+ * @param validate true if validation is required; false if the caller knows that the value is valid
+ * @return the required DecimalValue if the input is valid, or a ValidationFailure encapsulating the error
+ * message if not.
+ */
+
+ public static ConversionResult makeDecimalValue(CharSequence in, boolean validate) {
+
+ try {
+ FastStringBuffer digits = new FastStringBuffer(in.length());
+ int scale = 0;
+ int state = 0;
+ // 0 - in initial whitespace; 1 - after sign
+ // 3 - after decimal point; 5 - in final whitespace
+ boolean foundDigit = false;
+ int len = in.length();
+ for (int i=0; i<len; i++) {
+ char c = in.charAt(i);
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ if (state != 0) {
+ state = 5;
+ }
+ break;
+ case '+':
+ if (state != 0) {
+ throw new NumberFormatException("unexpected sign");
+ }
+ state = 1;
+ break;
+ case '-':
+ if (state != 0) {
+ throw new NumberFormatException("unexpected sign");
+ }
+ state = 1;
+ digits.append(c);
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (state == 0) {
+ state = 1;
+ } else if (state >= 3) {
+ scale++;
+ }
+ if (state == 5) {
+ throw new NumberFormatException("contains embedded whitespace");
+ }
+ digits.append(c);
+ foundDigit = true;
+ break;
+ case '.':
+ if (state == 5) {
+ throw new NumberFormatException("contains embedded whitespace");
+ }
+ if (state >= 3) {
+ throw new NumberFormatException("more than one decimal point");
+ }
+ state = 3;
+ break;
+ default:
+ throw new NumberFormatException("invalid character '" + c + "'");
+ }
+
+ }
+
+ if (!foundDigit) {
+ throw new NumberFormatException("no digits in value");
+ }
+
+ // remove insignificant trailing zeroes
+ while (scale > 0) {
+ if (digits.charAt(digits.length()-1) == '0') {
+ digits.setLength(digits.length() - 1);
+ scale--;
+ } else {
+ break;
+ }
+ }
+ if (digits.length() == 0 || (digits.length() == 1 && digits.charAt(0) == '-')) {
+ return DecimalValue.ZERO;
+ }
+ BigInteger bigInt = new BigInteger(digits.toString());
+ BigDecimal bigDec = new BigDecimal(bigInt, scale);
+ return new DecimalValue(bigDec);
+ } catch (NumberFormatException err) {
+ ValidationFailure e = new ValidationFailure(
+ "Cannot convert string " + Err.wrap(Whitespace.trim(in), Err.VALUE) +
+ " to xs:decimal: " + err.getMessage());
+ e.setErrorCode("FORG0001");
+ return e;
+ }
+ }
+
+ /**
+ * Test whether a string is castable to a decimal value
+ * @param in the string to be tested
+ * @return true if the string has the correct format for a decimal
+ */
+
+ public static boolean castableAsDecimal(CharSequence in) {
+ CharSequence trimmed = Whitespace.trimWhitespace(in);
+ return decimalPattern.matcher(trimmed).matches();
+ }
+
+ /**
+ * Constructor supplying a double
+ * @param in the value of the DecimalValue
+ */
+
+ public DecimalValue(double in) throws ValidationException {
+ try {
+ BigDecimal d = new BigDecimal(in);
+ value = d.stripTrailingZeros();
+ } catch (NumberFormatException err) {
+ // Must be a special value such as NaN or infinity
+ ValidationException e = new ValidationException(
+ "Cannot convert double " + Err.wrap(in+"", Err.VALUE) + " to decimal");
+ e.setErrorCode("FOCA0002");
+ throw e;
+ }
+ typeLabel = BuiltInAtomicType.DECIMAL;
+ }
+
+ /**
+ * Constructor supplying a long integer
+ * @param in the value of the DecimalValue
+ */
+
+ public DecimalValue(long in) {
+ value = BigDecimal.valueOf(in);
+ typeLabel = BuiltInAtomicType.DECIMAL;
+ }
+
+ /**
+ * Create a copy of this atomic value, with a difNferent type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ DecimalValue v = new DecimalValue(value);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.DECIMAL;
+ }
+
+ /**
+ * Get the numeric value as a double
+ *
+ * @return A double representing this numeric value; NaN if it cannot be
+ * converted
+ */
+ @Override
+ public double getDoubleValue() {
+ return value.doubleValue();
+ }
+
+ /**
+ * Get the numeric value converted to a float
+ *
+ * @return a float representing this numeric value; NaN if it cannot be converted
+ */
+ @Override
+ public float getFloatValue() {
+ return (float)value.doubleValue();
+ }
+
+ /**
+ * Return the numeric value as a Java long.
+ *
+ * @return the numeric value as a Java long. This performs truncation
+ * towards zero.
+ * @throws net.sf.saxon.trans.XPathException
+ * if the value cannot be converted
+ */
+ @Override
+ public long longValue() throws XPathException {
+ return (long)value.doubleValue();
+ }
+
+ /**
+ * Get the value
+ */
+
+ public BigDecimal getDecimalValue() {
+ return value;
+ }
+
+ /**
+ * Get the hashCode. This must conform to the rules for other NumericValue hashcodes
+ * @see NumericValue#hashCode
+ */
+
+ public int hashCode() {
+ BigDecimal round = value.setScale(0, BigDecimal.ROUND_DOWN);
+ long value = round.longValue();
+ if (value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) {
+ return (int)value;
+ } else {
+ return new Double(getDoubleValue()).hashCode();
+ }
+ }
+
+ public boolean effectiveBooleanValue() {
+ return value.signum() != 0;
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+// public CharSequence getStringValueCS() {
+// return decimalToString(value, new FastStringBuffer(20));
+// }
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules. For xs:decimal, the canonical
+ * representation always contains a decimal point.
+ */
+
+ public CharSequence getCanonicalLexicalRepresentation() {
+ String s = getStringValue();
+ if (s.indexOf('.') < 0) {
+ s += ".0";
+ }
+ return s;
+ }
+
+ /**
+ * Get the value as a String
+ * @return a String representation of the value
+ */
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+ return decimalToString(value, new FastStringBuffer(FastStringBuffer.TINY));
+ }
+
+ /**
+ * Convert a decimal value to a string, using the XPath rules for formatting
+ * @param value the decimal value to be converted
+ * @param fsb the FastStringBuffer to which the value is to be appended
+ * @return the supplied FastStringBuffer, suitably populated
+ */
+
+ public static FastStringBuffer decimalToString(BigDecimal value, FastStringBuffer fsb) {
+ // Can't use the plain BigDecimal#toString() under JDK 1.5 because this produces values like "1E-5".
+ // JDK 1.5 offers BigDecimal#toPlainString() which might do the job directly
+ int scale = value.scale();
+ if (scale == 0) {
+ fsb.append(value.toString());
+ return fsb;
+ } else if (scale < 0) {
+ String s = value.abs().unscaledValue().toString();
+ if (s.equals("0")) {
+ fsb.append('0');
+ return fsb;
+ }
+ //FastStringBuffer sb = new FastStringBuffer(s.length() + (-scale) + 2);
+ if (value.signum() < 0) {
+ fsb.append('-');
+ }
+ fsb.append(s);
+ for (int i=0; i<(-scale); i++) {
+ fsb.append('0');
+ }
+ return fsb;
+ } else {
+ String s = value.abs().unscaledValue().toString();
+ if (s.equals("0")) {
+ fsb.append('0');
+ return fsb;
+ }
+ int len = s.length();
+ //FastStringBuffer sb = new FastStringBuffer(len+1);
+ if (value.signum() < 0) {
+ fsb.append('-');
+ }
+ if (scale >= len) {
+ fsb.append("0.");
+ for (int i=len; i<scale; i++) {
+ fsb.append('0');
+ }
+ fsb.append(s);
+ } else {
+ fsb.append(s.substring(0, len-scale));
+ fsb.append('.');
+ fsb.append(s.substring(len-scale));
+ }
+ return fsb;
+ }
+ }
+
+ /**
+ * Negate the value
+ */
+
+ public NumericValue negate() {
+ return new DecimalValue(value.negate());
+ }
+
+ /**
+ * Implement the XPath floor() function
+ */
+
+ public NumericValue floor() {
+ return new DecimalValue(value.setScale(0, BigDecimal.ROUND_FLOOR));
+ }
+
+ /**
+ * Implement the XPath ceiling() function
+ */
+
+ public NumericValue ceiling() {
+ return new DecimalValue(value.setScale(0, BigDecimal.ROUND_CEILING));
+ }
+
+ /**
+ * Implement the XPath round() function
+ */
+
+ public NumericValue round(int scale) {
+ // The XPath rules say that we should round to the nearest integer, with .5 rounding towards
+ // positive infinity. Unfortunately this is not one of the rounding modes that the Java BigDecimal
+ // class supports, so we need different rules depending on the value.
+
+ // If the value is positive, we use ROUND_HALF_UP; if it is negative, we use ROUND_HALF_DOWN (here "UP"
+ // means "away from zero")
+
+ switch (value.signum()) {
+ case -1:
+ return new DecimalValue(value.setScale(scale, BigDecimal.ROUND_HALF_DOWN));
+ case 0:
+ return this;
+ case +1:
+ return new DecimalValue(value.setScale(scale, BigDecimal.ROUND_HALF_UP));
+ default:
+ // can't happen
+ return this;
+ }
+
+ }
+
+ /**
+ * Implement the XPath round-half-to-even() function
+ */
+
+ public NumericValue roundHalfToEven(int scale) {
+ BigDecimal scaledValue = value.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
+ return new DecimalValue(scaledValue.stripTrailingZeros());
+ }
+
+ /**
+ * Determine whether the value is negative, zero, or positive
+ * @return -1 if negative, 0 if zero, +1 if positive, NaN if NaN
+ */
+
+ public int signum() {
+ return value.signum();
+ }
+
+ /**
+ * Determine whether the value is a whole number, that is, whether it compares
+ * equal to some integer
+ */
+
+ public boolean isWholeNumber() {
+ return value.scale()==0 ||
+ value.compareTo(value.setScale(0, BigDecimal.ROUND_DOWN)) == 0;
+ }
+
+ /**
+ * Get the absolute value as defined by the XPath abs() function
+ * @return the absolute value
+ * @since 9.2
+ */
+
+ public NumericValue abs() {
+ if (value.signum() > 0) {
+ return this;
+ } else {
+ return new DecimalValue(value.negate());
+ }
+ }
+
+ /**
+ * Compare the value to another numeric value
+ */
+
+ public int compareTo(Object other) {
+ if ((NumericValue.isInteger((NumericValue)other))) {
+ // deliberately triggers a ClassCastException if other value is the wrong type
+ try {
+ return value.compareTo(((NumericValue)other).getDecimalValue());
+ } catch (XPathException err) {
+ throw new AssertionError("Conversion of integer to decimal should never fail");
+ }
+ } else if (other instanceof DecimalValue) {
+ return value.compareTo(((DecimalValue)other).value);
+ } else if (other instanceof FloatValue) {
+ return 0 - ((FloatValue)other).compareTo(this);
+ } else {
+ return super.compareTo(other);
+ }
+ }
+
+ /**
+ * Compare the value to a long
+ * @param other the value to be compared with
+ * @return -1 if this is less, 0 if this is equal, +1 if this is greater or if this is NaN
+ */
+
+ public int compareTo(long other) {
+ if (other == 0) {
+ return value.signum();
+ }
+ return value.compareTo(BigDecimal.valueOf(other));
+ }
+
+ /**
+ * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
+ * Returns null if the value is not comparable according to XML Schema rules. The default implementation
+ * returns the value itself if it is comparable, or null otherwise. This is modified for types such as
+ * xs:duration which allow ordering comparisons in XML Schema, but not in XPath.
+ * <p/>
+ * <p>In the case of data types that are partially ordered, the returned Comparable extends the standard
+ * semantics of the compareTo() method by returning the value {@link net.sf.saxon.om.SequenceTool#INDETERMINATE_ORDERING} when there
+ * is no defined order relationship between two given values.</p>
+ */
+
+ /**
+ * Get an object that implements XML Schema comparison semantics
+ */
+
+ public Comparable getSchemaComparable() {
+ return new DecimalComparable(this);
+ }
+
+ /**
+ * A Comparable that performs comparison of a DecimalValue either with another
+ * DecimalValue or with some other representation of an XPath numeric value
+ */
+
+ protected static class DecimalComparable implements Comparable {
+
+ protected DecimalValue value;
+
+ public DecimalComparable(DecimalValue value) {
+ this.value = value;
+ }
+
+ public BigDecimal asBigDecimal() {
+ return value.getDecimalValue();
+ }
+
+ public int compareTo(Object o) {
+ if (o instanceof DecimalComparable) {
+ return asBigDecimal().compareTo(((DecimalComparable)o).asBigDecimal());
+ } else if (o instanceof Int64Value.Int64Comparable) {
+ return asBigDecimal().compareTo(BigDecimal.valueOf(((Int64Value.Int64Comparable)o).asLong()));
+ } else if (o instanceof BigIntegerValue.BigIntegerComparable) {
+ return value.compareTo(new BigDecimal(((BigIntegerValue.BigIntegerComparable)o).asBigInteger()));
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ }
+
+ public boolean equals(Object o) {
+ return compareTo(o) == 0;
+ }
+
+ public int hashCode() {
+ // Must align with hashCodes for other subtypes of xs:decimal
+ if (value.isWholeNumber()) {
+ try {
+ IntegerValue iv = ((IntegerValue) Converter.DECIMAL_TO_INTEGER.convert(value).asAtomic());
+ return iv.getSchemaComparable().hashCode();
+ } catch (ValidationException e) {
+ return 12345678; // can't happen
+ }
+ }
+ return value.hashCode();
+ }
+ }
+
+ /**
+ * Determine whether two atomic values are identical, as determined by XML Schema rules. This is a stronger
+ * test than equality (even schema-equality); for example two dateTime values are not identical unless
+ * they are in the same timezone.
+ * <p>Note that even this check ignores the type annotation of the value. The integer 3 and the short 3
+ * are considered identical, even though they are not fully interchangeable. "Identical" means the
+ * same point in the value space, regardless of type annotation.</p>
+ * <p>NaN is identical to itself.</p>
+ *
+ * @param v the other value to be compared with this one
+ * @return true if the two values are identical, false otherwise.
+ */
+
+ public boolean isIdentical(/*@NotNull*/ AtomicValue v) {
+ return (v instanceof DecimalValue) && equals(v);
+ }
+
+}
+
diff --git a/sf/saxon/value/DoubleValue.java b/sf/saxon/value/DoubleValue.java
new file mode 100644
index 0000000..357826a
--- /dev/null
+++ b/sf/saxon/value/DoubleValue.java
@@ -0,0 +1,493 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.sort.DoubleSortComparer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+* A numeric (double precision floating point) value
+*/
+
+public final class DoubleValue extends NumericValue {
+
+ public static final DoubleValue ZERO = new DoubleValue(0.0);
+ public static final DoubleValue NEGATIVE_ZERO = new DoubleValue(-0.0);
+ public static final DoubleValue ONE = new DoubleValue(1.0);
+ public static final DoubleValue NaN = new DoubleValue(Double.NaN);
+
+ private double value;
+
+ /**
+ * Constructor supplying a double
+ * @param value the value of the NumericValue
+ */
+
+ public DoubleValue(double value) {
+ this.value = value;
+ typeLabel = BuiltInAtomicType.DOUBLE;
+ }
+
+ /**
+ * Constructor supplying a double and an AtomicType, for creating
+ * a value that belongs to a user-defined subtype of xs:double. It is
+ * the caller's responsibility to ensure that the supplied value conforms
+ * to the supplied type.
+ * @param value the value of the NumericValue
+ * @param type the type of the value. This must be a subtype of xs:double, and the
+ * value must conform to this type. The methosd does not check these conditions.
+ */
+
+ public DoubleValue(double value, AtomicType type) {
+ this.value = value;
+ typeLabel = type;
+ }
+
+ /**
+ * Static factory method (for convenience in compiled bytecode)
+ * @param value the value of the double
+ * @return a new DoubleValue
+ */
+
+ public static DoubleValue makeDoubleValue(double value) {
+ return new DoubleValue(value);
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ DoubleValue v = new DoubleValue(value);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.DOUBLE;
+ }
+
+ /**
+ * Return this numeric value as a double
+ * @return the value as a double
+ */
+
+ public double getDoubleValue() {
+ return value;
+ }
+
+ /**
+ * Get the numeric value converted to a float
+ *
+ * @return a float representing this numeric value; NaN if it cannot be converted
+ */
+ @Override
+ public float getFloatValue() {
+ return (float)value;
+ }
+
+ /**
+ * Get the numeric value converted to a decimal
+ *
+ * @return a decimal representing this numeric value;
+ * @throws net.sf.saxon.trans.XPathException
+ * if the value cannot be converted, for example if it is NaN or infinite
+ */
+ @Override
+ public BigDecimal getDecimalValue() throws XPathException {
+ return new BigDecimal(value);
+ }
+
+ /**
+ * Return the numeric value as a Java long.
+ *
+ * @return the numeric value as a Java long. This performs truncation
+ * towards zero.
+ * @throws net.sf.saxon.trans.XPathException
+ * if the value cannot be converted
+ */
+ @Override
+ public long longValue() throws XPathException {
+ return (long)value;
+ }
+
+ /**
+ * Get the hashCode. This must conform to the rules for other NumericValue hashcodes
+ * @see NumericValue#hashCode
+ */
+
+ public int hashCode() {
+ if (value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) {
+ return (int)value;
+ } else {
+ return new Double(value).hashCode();
+ }
+ }
+
+ /**
+ * Test whether the value is the double/float value NaN
+ */
+
+ public boolean isNaN() {
+ return Double.isNaN(value);
+ }
+
+ /**
+ * Get the effective boolean value
+ * @return the effective boolean value (true unless the value is zero or NaN)
+ */
+ public boolean effectiveBooleanValue() {
+ return (value!=0.0 && !Double.isNaN(value));
+ }
+
+ /**
+ * Convert the double to a string according to the XPath 2.0 rules
+ * @return the string value
+ */
+// public String getStringValue() {
+// return doubleToString(value).toString(); //, Double.toString(value)).toString();
+// }
+
+ /**
+ * Convert the double to a string according to the XPath 2.0 rules
+ * @return the string value
+ */
+ public CharSequence getPrimitiveStringValue() {
+ return doubleToString(value);
+ }
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules. For xs:double, the canonical
+ * representation always uses exponential notation.
+ */
+
+ public CharSequence getCanonicalLexicalRepresentation() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.TINY);
+ return FloatingPointConverter.appendDouble(fsb, value, true);
+ }
+
+ /**
+ * Internal method used for conversion of a double to a string
+ * @param value the actual value
+ * @return the value converted to a string, according to the XPath casting rules.
+ */
+
+ public static CharSequence doubleToString(double value) {
+ return FloatingPointConverter.appendDouble(new FastStringBuffer(FastStringBuffer.TINY), value, false);
+ }
+
+
+ /**
+ * Negate the value
+ */
+
+ public NumericValue negate() {
+ return new DoubleValue(-value);
+ }
+
+ /**
+ * Implement the XPath floor() function
+ */
+
+ public NumericValue floor() {
+ return new DoubleValue(Math.floor(value));
+ }
+
+ /**
+ * Implement the XPath ceiling() function
+ */
+
+ public NumericValue ceiling() {
+ return new DoubleValue(Math.ceil(value));
+ }
+
+ /**
+ * Implement the XPath round() function
+ */
+
+ public NumericValue round(int scale) {
+ if (Double.isNaN(value)) {
+ return this;
+ }
+ if (Double.isInfinite(value)) {
+ return this;
+ }
+ if (value == 0.0) {
+ return this; // handles the negative zero case
+ }
+
+ if (value == -0.0) {
+ return this; // handles the negative zero case
+ }
+
+ if (value >= -0.5 && value < 0.0) {
+ return new DoubleValue(-0.0);
+ }
+
+ if (scale==0 && value > Long.MIN_VALUE && value < Long.MAX_VALUE) {
+ return new DoubleValue(Math.round(value));
+ }
+
+ // Convert to a scaled integer, by multiplying by 10^scale
+
+ double factor = Math.pow(10, scale+1);
+ double d = Math.abs(value * factor);
+
+ if (Double.isInfinite(d)) {
+ // double arithmetic has overflowed - do it in decimal
+ BigDecimal dec = new BigDecimal(value);
+ dec = dec.setScale(scale, BigDecimal.ROUND_HALF_UP);
+ return new DoubleValue(dec.doubleValue());
+ }
+
+ // Now apply any rounding needed, using the "round half to even" rule***CHANGE
+
+ double rem = d % 10;
+ if (rem >= 5) {
+ d += (10-rem);
+ } else if (rem < 5){
+ d -= rem;
+ }
+
+ // Now convert back to the original magnitude
+
+ d /= factor;
+ if (value < 0) {
+ d = -d;
+ }
+ return new DoubleValue(d);
+ }
+
+ /**
+ * Implement the XPath round-to-half-even() function
+ */
+
+ public NumericValue roundHalfToEven(int scale) {
+ if (Double.isNaN(value)) return this;
+ if (Double.isInfinite(value)) return this;
+ if (value==0.0) return this; // handles the negative zero case
+
+ // Convert to a scaled integer, by multiplying by 10^scale
+
+ double factor = Math.pow(10, scale+1);
+ double d = Math.abs(value * factor);
+
+ if (Double.isInfinite(d)) {
+ // double arithmetic has overflowed - do it in decimal
+ BigDecimal dec = new BigDecimal(value);
+ dec = dec.setScale(scale, BigDecimal.ROUND_HALF_EVEN);
+ return new DoubleValue(dec.doubleValue());
+ }
+
+ // Now apply any rounding needed, using the "round half to even" rule
+
+ double rem = d % 10;
+ if (rem > 5) {
+ d += (10-rem);
+ } else if (rem < 5){
+ d -= rem;
+ } else {
+ // round half to even - check the last bit
+ if ((d % 20) == 15) {
+ d +=5 ;
+ } else {
+ d -=5;
+ }
+ }
+
+ // Now convert back to the original magnitude
+
+ d /= factor;
+ if (value < 0) {
+ d = -d;
+ }
+ return new DoubleValue(d);
+
+ }
+
+ /**
+ * Determine whether the value is negative, zero, or positive
+ * @return -1 if negative, 0 if zero (including negative zero) or NaN, +1 if positive
+ */
+
+ public int signum() {
+ if (Double.isNaN(value)) {
+ return 0;
+ }
+ if (value > 0) return 1;
+ if (value == 0) return 0;
+ return -1;
+ }
+
+ /**
+ * Determine whether the value is a whole number, that is, whether it compares
+ * equal to some integer
+ */
+
+ public boolean isWholeNumber() {
+ return value == Math.floor(value) && !Double.isInfinite(value);
+ }
+
+ /**
+ * Get the absolute value as defined by the XPath abs() function
+ * @return the absolute value
+ * @since 9.2
+ */
+
+ public NumericValue abs() {
+ if (value > 0.0) {
+ return this;
+ } else {
+ return new DoubleValue(Math.abs(value));
+ }
+ }
+
+ /**
+ * Compare the value to a long.
+ * @param other the value to be compared with
+ * @return -1 if this is less, 0 if this is equal, +1 if this is greater or if this is NaN
+ */
+
+ public int compareTo(long other) {
+ double otherDouble = (double)other;
+ if (value == otherDouble) return 0;
+ if (value < otherDouble) return -1;
+ return +1;
+ }
+
+ /**
+ * Get an object that implements XML Schema comparison semantics
+ */
+
+ public Comparable getSchemaComparable() {
+ // convert negative to positive zero because Double.compareTo() does the wrong thing
+ // TODO: what shall we do with NaN?
+ return (value == 0.0 ? 0.0 : value);
+ }
+
+ /**
+ * Determine whether two atomic values are identical, as determined by XML Schema rules. This is a stronger
+ * test than equality (even schema-equality); for example two dateTime values are not identical unless
+ * they are in the same timezone.
+ * <p>Note that even this check ignores the type annotation of the value. The integer 3 and the short 3
+ * are considered identical, even though they are not fully interchangeable. "Identical" means the
+ * same point in the value space, regardless of type annotation.</p>
+ * <p>NaN is identical to itself.</p>
+ *
+ * @param v the other value to be compared with this one
+ * @return true if the two values are identical, false otherwise.
+ */
+
+ public boolean isIdentical(/*@NotNull*/ AtomicValue v) {
+ return v instanceof DoubleValue && DoubleSortComparer.getInstance().comparesEqual(this, (DoubleValue)v);
+ }
+
+ /**
+ * Diagnostic method: print the sign, exponent, and significand
+ * @param d the double to be diagnosed
+ */
+
+ public static void printInternalForm(double d) {
+ System.err.println("==== Double " + d + " ====");
+ long bits = Double.doubleToLongBits(d);
+ System.err.println("Internal form: " + Long.toHexString(bits));
+ if (bits == 0x7ff0000000000000L) {
+ System.err.println("+Infinity");
+ } else if (bits == 0xfff0000000000000L) {
+ System.err.println("-Infinity");
+ } else if (bits == 0x7ff8000000000000L) {
+ System.err.println("NaN");
+ } else {
+ int s = ((bits >> 63) == 0) ? 1 : -1;
+ int e = (int)((bits >> 52) & 0x7ffL);
+ long m = (e == 0) ?
+ (bits & 0xfffffffffffffL) << 1 :
+ (bits & 0xfffffffffffffL) | 0x10000000000000L;
+ int exponent = e-1075;
+ System.err.println("Sign: " + s);
+ System.err.println("Raw Exponent: " + e);
+ System.err.println("Exponent: " + exponent);
+ System.err.println("Significand: " + m);
+ BigDecimal dec = BigDecimal.valueOf(m);
+ if (exponent > 0) {
+ dec = dec.multiply(new BigDecimal(BigInteger.valueOf(2).pow(exponent)));
+ } else {
+ // Next line is sometimes failing, e.g. on -3.62e-5. Not investigated.
+ dec = dec.divide(new BigDecimal(BigInteger.valueOf(2).pow(-exponent)), BigDecimal.ROUND_HALF_EVEN);
+ }
+ System.err.println("Exact value: " + (s>0?"":"-") + dec);
+ }
+ }
+
+ public static DoubleValue fromInternalForm(String hex) {
+ return new DoubleValue(Double.longBitsToDouble(Long.parseLong(hex, 16)));
+
+ }
+
+ public static void main(String[] args) {
+ System.err.println(fromInternalForm("3fdeaee8744b05f1"));
+ //printInternalForm(0.4794255386042030002732879352155713880818033679406000675);
+// System.err.println("3e0 : " + new DoubleValue(3e0).isWholeNumber());
+// System.err.println("3e1 : " + new DoubleValue(3e1).isWholeNumber());
+// System.err.println("3e-1 : " + new DoubleValue(3e-1).isWholeNumber());
+// System.err.println("1e0 : " + new DoubleValue(1e0).isWholeNumber());
+// System.err.println("1 - 20");
+// printInternalForm(1e0);
+// printInternalForm(2e0);
+// printInternalForm(3e0);
+// printInternalForm(4e0);
+// printInternalForm(5e0);
+// printInternalForm(6e0);
+// printInternalForm(7e0);
+// printInternalForm(8e0);
+// printInternalForm(9e0);
+// printInternalForm(10e0);
+// printInternalForm(11e0);
+// printInternalForm(12e0);
+// printInternalForm(13e0);
+// printInternalForm(14e0);
+// printInternalForm(15e0);
+// printInternalForm(16e0);
+// printInternalForm(17e0);
+// printInternalForm(18e0);
+// printInternalForm(19e0);
+// printInternalForm(20e0);
+// System.err.println("3.0000001");
+// printInternalForm(3.0000001e0);
+// System.err.println("1");
+// printInternalForm(1e0);
+// System.err.println("1.00000001");
+// printInternalForm(1.00000001e0);
+// System.err.println("0.9999999e0");
+// printInternalForm(0.9999999e0);
+// System.err.println("30");
+// printInternalForm(3e1);
+// System.err.println("== 0.3 ==");
+// printInternalForm(3e-1);
+
+ }
+
+ }
+
diff --git a/sf/saxon/value/DurationValue.java b/sf/saxon/value/DurationValue.java
new file mode 100644
index 0000000..72fcb98
--- /dev/null
+++ b/sf/saxon/value/DurationValue.java
@@ -0,0 +1,854 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.StringTokenizer;
+
+/**
+ * A value of type xs:duration
+ */
+
+public class DurationValue extends AtomicValue {
+
+ protected boolean negative = false;
+ protected int months = 0;
+ protected long seconds = 0;
+ protected int microseconds = 0;
+
+ /**
+ * Private constructor for internal use
+ */
+
+ protected DurationValue() {
+ }
+
+ /**
+ * Constructor for xs:duration taking the components of the duration. There is no requirement
+ * that the values are normalized, for example it is acceptable to specify months=18. The values of
+ * the individual components must all be non-negative.
+ *
+ * @param positive true if the duration is positive, false if negative. For a negative duration
+ * the components are all supplied as positive integers (or zero).
+ * @param years the number of years
+ * @param months the number of months
+ * @param days the number of days
+ * @param hours the number of hours
+ * @param minutes the number of minutes
+ * @param seconds the number of seconds
+ * @param microseconds the number of microseconds
+ * @throws IllegalArgumentException if the size of the duration exceeds implementation-defined
+ * limits: specifically, if the total number of months exceeds 2^31, or if the total number
+ * of seconds exceeds 2^63.
+ */
+
+ public DurationValue(boolean positive, int years, int months, int days,
+ int hours, int minutes, long seconds, int microseconds)
+ throws IllegalArgumentException {
+ this(positive, years, months, days, hours, minutes, seconds, microseconds, BuiltInAtomicType.DURATION);
+ }
+
+ /**
+ * Constructor for xs:duration taking the components of the duration, plus a user-specified
+ * type which must be a subtype of xs:duration. There is no requirement
+ * that the values are normalized, for example it is acceptable to specify months=18. The values of
+ * the individual components must all be non-negative.
+ *
+ * @param positive true if the duration is positive, false if negative. For a negative duration
+ * the components are all supplied as positive integers (or zero).
+ * @param years the number of years
+ * @param months the number of months
+ * @param days the number of days
+ * @param hours the number of hours
+ * @param minutes the number of minutes
+ * @param seconds the number of seconds (long to allow copying)
+ * @param microseconds the number of microseconds
+ * @param type the user-defined subtype of xs:duration. Note that this constructor cannot
+ * be used to create an instance of xs:dayTimeDuration or xs:yearMonthDuration.
+ * @throws IllegalArgumentException if the size of the duration exceeds implementation-defined
+ * limits: specifically, if the total number of months exceeds 2^31, or if the total number
+ * of seconds exceeds 2^63.
+ */
+
+ public DurationValue(boolean positive, int years, int months, int days,
+ int hours, int minutes, long seconds, int microseconds, AtomicType type) {
+ negative = !positive;
+ if (years < 0 || months < 0 || days < 0 || hours < 0 || minutes < 0 || seconds < 0 || microseconds < 0) {
+ throw new IllegalArgumentException("Negative component value");
+ }
+ if (((double)years)*12 + (double)months > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("Duration months limit exceeded");
+ }
+ if (((double)days)*(24*60*60) + ((double)hours)*(60*60) +
+ ((double)minutes)*60 + (double)seconds > Long.MAX_VALUE) {
+ throw new IllegalArgumentException("Duration seconds limit exceeded");
+ }
+ this.months = years*12 + months;
+ long h = days * 24L + hours ;
+ long m = h * 60L + minutes;
+ this.seconds = m * 60L + seconds;
+ this.microseconds = microseconds;
+ normalizeZeroDuration();
+ typeLabel = type;
+ }
+
+ /**
+ * Ensure that a zero duration is considered positive
+ */
+
+ protected void normalizeZeroDuration() {
+ if (months == 0 && seconds == 0L && microseconds == 0) {
+ negative = false;
+ }
+ }
+
+ /**
+ * Static factory method: create a duration value from a supplied string, in
+ * ISO 8601 format [-]PnYnMnDTnHnMnS
+ *
+ * @param s a string in the lexical space of xs:duration
+ * @return the constructed xs:duration value, or a {@link ValidationFailure} if the
+ * supplied string is lexically invalid.
+ */
+
+ /*@NotNull*/ public static ConversionResult makeDuration(CharSequence s) {
+ return makeDuration(s, true, true);
+ }
+
+ /*@NotNull*/ protected static ConversionResult makeDuration(CharSequence s, boolean allowYM, boolean allowDT) {
+ int years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0, microseconds = 0;
+ boolean negative = false;
+ StringTokenizer tok = new StringTokenizer(Whitespace.trimWhitespace(s).toString(), "-+.PYMDTHS", true);
+ int components = 0;
+ if (!tok.hasMoreElements()) {
+ return badDuration("empty string", s);
+ }
+ String part = (String)tok.nextElement();
+ if ("+".equals(part)) {
+ return badDuration("+ sign not allowed in a duration", s);
+ } else if ("-".equals(part)) {
+ negative = true;
+ part = (String)tok.nextElement();
+ }
+ if (!"P".equals(part)) {
+ return badDuration("missing 'P'", s);
+ }
+ int state = 0;
+ while (tok.hasMoreElements()) {
+ part = (String)tok.nextElement();
+ if ("T".equals(part)) {
+ state = 4;
+ if (!tok.hasMoreElements()) {
+ return badDuration("T must be followed by time components", s);
+ }
+ part = (String)tok.nextElement();
+ }
+ int value = simpleInteger(part);
+ if (value < 0) {
+ if (value == -2) {
+ return badDuration("component of duration exceeds Saxon limits", s, "FODT0002");
+ } else {
+ return badDuration("invalid or non-numeric component", s);
+ }
+ }
+ if (!tok.hasMoreElements()) {
+ return badDuration("missing unit letter at end", s);
+ }
+ char delim = ((String)tok.nextElement()).charAt(0);
+ switch (delim) {
+ case'Y':
+ if (state > 0) {
+ return badDuration("Y is out of sequence", s);
+ }
+ if (!allowYM) {
+ return badDuration("Year component is not allowed in dayTimeDuration", s);
+ }
+ years = value;
+ state = 1;
+ components++;
+ break;
+ case'M':
+ if (state == 4 || state == 5) {
+ if (!allowDT) {
+ return badDuration("Minute component is not allowed in yearMonthDuration", s);
+ }
+ minutes = value;
+ state = 6;
+ components++;
+ break;
+ } else if (state == 0 || state == 1) {
+ if (!allowYM) {
+ return badDuration("Month component is not allowed in dayTimeDuration", s);
+ }
+ months = value;
+ state = 2;
+ components++;
+ break;
+ } else {
+ return badDuration("M is out of sequence", s);
+ }
+ case'D':
+ if (state > 2) {
+ return badDuration("D is out of sequence", s);
+ }
+ if (!allowDT) {
+ return badDuration("Day component is not allowed in yearMonthDuration", s);
+ }
+ days = value;
+ state = 3;
+ components++;
+ break;
+ case'H':
+ if (state != 4) {
+ return badDuration("H is out of sequence", s);
+ }
+ if (!allowDT) {
+ return badDuration("Hour component is not allowed in yearMonthDuration", s);
+ }
+ hours = value;
+ state = 5;
+ components++;
+ break;
+ case'.':
+ if (state < 4 || state > 6) {
+ return badDuration("misplaced decimal point", s);
+ }
+ seconds = value;
+ state = 7;
+ break;
+ case'S':
+ if (state < 4 || state > 7) {
+ return badDuration("S is out of sequence", s);
+ }
+ if (!allowDT) {
+ return badDuration("Seconds component is not allowed in yearMonthDuration", s);
+ }
+ if (state == 7) {
+ while (part.length() < 6) {
+ part += "0";
+ }
+ if (part.length() > 6) {
+ part = part.substring(0, 6);
+ }
+ value = simpleInteger(part);
+ if (value < 0) {
+ return badDuration("non-numeric fractional seconds", s);
+ }
+ microseconds = value;
+ } else {
+ seconds = value;
+ }
+ state = 8;
+ components++;
+ break;
+ default:
+ return badDuration("misplaced " + delim, s);
+ }
+ }
+
+ if (components == 0) {
+ return badDuration("Duration specifies no components", s);
+ }
+
+ try {
+ return new DurationValue(
+ !negative, years, months, days, hours, minutes, seconds, microseconds, BuiltInAtomicType.DURATION);
+ } catch (IllegalArgumentException err) {
+ // catch values that exceed limits
+ return new ValidationFailure(err.getMessage());
+ }
+ }
+
+ protected static ValidationFailure badDuration(String msg, CharSequence s) {
+ ValidationFailure err = new ValidationFailure("Invalid duration value '" + s + "' (" + msg + ')');
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+
+ protected static ValidationFailure badDuration(String msg, CharSequence s, String errorCode) {
+ ValidationFailure err = new ValidationFailure("Invalid duration value '" + s + "' (" + msg + ')');
+ err.setErrorCode(errorCode);
+ return err;
+ }
+
+ /**
+ * Parse a simple unsigned integer
+ *
+ * @param s the string containing the sequence of digits. No sign or whitespace is allowed.
+ * @return the integer. Return -1 if the string is not a sequence of digits, or -2 if it exceeds 2^31
+ */
+
+ protected static int simpleInteger(/*@NotNull*/ String s) {
+ long result = 0;
+ int len = s.length();
+ if (len == 0) {
+ return -1;
+ }
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt(i);
+ if (c >= '0' && c <= '9') {
+ result = result * 10 + (c - '0');
+ if (result > Integer.MAX_VALUE) {
+ return -2;
+ }
+ } else {
+ return -1;
+ }
+ }
+ return (int)result;
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ return new DurationValue(!negative, 0, months, 0, 0, 0, seconds, microseconds, typeLabel);
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.DURATION;
+ }
+
+ /**
+ * Normalize the duration, so that months<12, hours<24, minutes<60, seconds<60.
+ * Since durations are now always normalized, this method has become a no-op, but is retained
+ * for backwards compatibility
+ * @deprecated since 9.0 - the method does nothing
+ *
+ * @return the duration unchanged
+ */
+
+ /*@NotNull*/ public DurationValue normalizeDuration() {
+ return this;
+ }
+
+ /**
+ * Return the signum of the value
+ *
+ * @return -1 if the duration is negative, zero if it is zero-length, +1 if it is positive
+ */
+
+ public int signum() {
+ if (negative) {
+ return -1;
+ }
+ if (months == 0 && seconds == 0L && microseconds == 0) {
+ return 0;
+ }
+ return +1;
+ }
+
+ /**
+ * Get the year component
+ *
+ * @return the number of years in the normalized duration; always positive
+ */
+
+ public int getYears() {
+ return months / 12;
+ }
+
+ /**
+ * Get the months component
+ *
+ * @return the number of months in the normalized duration; always positive, in the range 0-11
+ */
+
+ public int getMonths() {
+ return months % 12;
+ }
+
+ /**
+ * Get the days component
+ *
+ * @return the number of days in the normalized duration; always positive
+ */
+
+ public int getDays() {
+// System.err.println("seconds = " + seconds);
+// System.err.println("minutes = " + seconds / 60L);
+// System.err.println("hours = " + seconds / (60L*60L));
+// System.err.println("days = " + seconds / (24L*60L*60L));
+// System.err.println("days (int) = " + (int)(seconds / (24L*60L*60L)));
+ return (int)(seconds / (24L*60L*60L));
+ }
+
+ /**
+ * Get the hours component
+ *
+ * @return the number of hours in the normalized duration; always positive, in the range 0-23
+ */
+
+ public int getHours() {
+ return (int)(seconds % (24L*60L*60L) / (60L*60L));
+ }
+
+ /**
+ * Get the minutes component
+ *
+ * @return the number of minutes in the normalized duration; always positive, in the range 0-59
+ */
+
+ public int getMinutes() {
+ return (int)(seconds % (60L*60L) / 60L);
+ }
+
+ /**
+ * Get the seconds component
+ *
+ * @return the number of whole seconds in the normalized duration; always positive, in the range 0-59
+ */
+
+ public int getSeconds() {
+ return (int)(seconds % 60L);
+ }
+
+ /**
+ * Get the microseconds component
+ *
+ * @return the number of microseconds in the normalized duration; always positive, in the range 0-999999
+ */
+
+ public int getMicroseconds() {
+ return microseconds;
+ }
+
+ /**
+ * Get the total number of months (ignoring the days/hours/minutes/seconds)
+ * @return the total number of months, that is (getYears()*12) + getMonths(), as a positive
+ * or negative number according as the duration is positive or negative
+ */
+
+ public int getTotalMonths() {
+ return (negative ? -months : months);
+ }
+
+ /**
+ * Get the total number of seconds (ignoring the years/months)
+ * @return the total number of seconds, as a positive
+ * or negative number according as the duration is positive or negative
+ */
+
+ public BigDecimal getTotalSeconds() {
+ BigDecimal dec = new BigDecimal(negative ? -seconds : seconds);
+ if (microseconds != 0) {
+ dec = dec.add(new BigDecimal(BigInteger.valueOf(negative ? -microseconds : microseconds), -6));
+ }
+ return dec;
+ }
+
+
+ /**
+ * Convert the value to a string, using the serialization rules.
+ * For atomic values this is the same as a cast; for sequence values
+ * it gives a space-separated list. This method is refined for AtomicValues
+ * so that it never throws an Exception.
+ */
+
+// public String getStringValue() {
+// return getStringValueCS().toString();
+// }
+
+ /**
+ * Convert to string
+ *
+ * @return ISO 8601 representation.
+ */
+
+ public CharSequence getPrimitiveStringValue() {
+
+ // Note, Schema does not define a canonical representation. We omit all zero components, unless
+ // the duration is zero-length, in which case we output PT0S.
+
+ if (months == 0 && seconds == 0L && microseconds == 0) {
+ return "PT0S";
+ }
+
+ FastStringBuffer sb = new FastStringBuffer(32);
+ if (negative) {
+ sb.append('-');
+ }
+ int years = getYears();
+ int months = getMonths();
+ int days = getDays();
+ int hours = getHours();
+ int minutes = getMinutes();
+ int seconds = getSeconds();
+
+ sb.append("P");
+ if (years != 0) {
+ sb.append(years + "Y");
+ }
+ if (months != 0) {
+ sb.append(months + "M");
+ }
+ if (days != 0) {
+ sb.append(days + "D");
+ }
+ if (hours != 0 || minutes != 0 || seconds != 0 || microseconds != 0) {
+ sb.append("T");
+ }
+ if (hours != 0) {
+ sb.append(hours + "H");
+ }
+ if (minutes != 0) {
+ sb.append(minutes + "M");
+ }
+ if (seconds != 0 || microseconds != 0) {
+ if (seconds != 0 && microseconds == 0) {
+ sb.append(seconds + "S");
+ } else {
+ long ms = (seconds * 1000000) + microseconds;
+ String mss = ms + "";
+ if (seconds == 0) {
+ mss = "0000000" + mss;
+ mss = mss.substring(mss.length() - 7);
+ }
+ sb.append(mss.substring(0, mss.length() - 6));
+ sb.append('.');
+ int lastSigDigit = mss.length() - 1;
+ while (mss.charAt(lastSigDigit) == '0') {
+ lastSigDigit--;
+ }
+ sb.append(mss.substring(mss.length() - 6, lastSigDigit + 1));
+ sb.append('S');
+ }
+ }
+
+ return sb;
+
+ }
+
+ /**
+ * Get length of duration in seconds, assuming an average length of month. (Note, this defines a total
+ * ordering on durations which is different from the partial order defined in XML Schema; XPath 2.0
+ * currently avoids defining an ordering at all. But the ordering here is consistent with the ordering
+ * of the two duration subtypes in XPath 2.0.)
+ *
+ * @return the duration in seconds, as a double
+ */
+
+ public double getLengthInSeconds() {
+ double a = months * (365.242199 / 12.0) * 24 * 60 * 60 + seconds + ((double)microseconds / 1000000);
+ return (negative ? -a : a);
+ }
+
+ /**
+ * Convert to Java object (for passing to external functions)
+ */
+
+// public Object convertAtomicToJava(Class target, XPathContext context) throws XPathException {
+// if (target.isAssignableFrom(DurationValue.class)) {
+// return this;
+// } else if (target == Object.class) {
+// return getStringValue();
+// } else {
+// Object o = super.convertSequenceToJava(target, context);
+// if (o == null) {
+// XPathException err = new XPathException("Conversion of xs:duration to " + target.getName() +
+// " is not supported");
+// err.setXPathContext(context);
+// err.setErrorCode(SaxonErrorCode.SXJE0003);
+// }
+// return o;
+// }
+// }
+
+ /**
+ * Get a component of the normalized value
+ */
+
+ public AtomicValue getComponent(int component) throws XPathException {
+ switch (component) {
+ case Component.YEAR:
+ return Int64Value.makeIntegerValue((negative ? -getYears() : getYears()));
+ case Component.MONTH:
+ return Int64Value.makeIntegerValue((negative ? -getMonths() : getMonths()));
+ case Component.DAY:
+ return Int64Value.makeIntegerValue((negative ? -getDays() : getDays()));
+ case Component.HOURS:
+ return Int64Value.makeIntegerValue((negative ? -getHours() : getHours()));
+ case Component.MINUTES:
+ return Int64Value.makeIntegerValue((negative ? -getMinutes() : getMinutes()));
+ case Component.SECONDS:
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+ String ms = ("000000" + microseconds);
+ ms = ms.substring(ms.length() - 6);
+ sb.append((negative ? "-" : "") + getSeconds() + '.' + ms);
+ return (AtomicValue)DecimalValue.makeDecimalValue(sb, false);
+ case Component.WHOLE_SECONDS:
+ return Int64Value.makeIntegerValue((negative ? -seconds : seconds));
+ case Component.MICROSECONDS:
+ return new Int64Value((negative ? -microseconds : microseconds));
+ default:
+ throw new IllegalArgumentException("Unknown component for duration: " + component);
+ }
+ }
+
+
+ /**
+ * Get an object value that implements the XPath equality and ordering comparison semantics for this value.
+ * If the ordered parameter is set to true, the result will be a Comparable and will support a compareTo()
+ * method with the semantics of the XPath lt/gt operator, provided that the other operand is also obtained
+ * using the getXPathComparable() method. In all cases the result will support equals() and hashCode() methods
+ * that support the semantics of the XPath eq operator, again provided that the other operand is also obtained
+ * using the getXPathComparable() method. A context argument is supplied for use in cases where the comparison
+ * semantics are context-sensitive, for example where they depend on the implicit timezone or the default
+ * collation.
+ *
+ * @param ordered true if an ordered comparison is required. In this case the result is null if the
+ * type is unordered; in other cases the returned value will be a Comparable.
+ * @param collator collation used for comparing string values
+ * @param context the XPath dynamic evaluation context, used in cases where the comparison is context
+* sensitive @return an Object whose equals() and hashCode() methods implement the XPath comparison semantics
+ */
+
+ /*@Nullable*/ public Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context) {
+ return (ordered ? null : this);
+ }
+
+ /**
+ * Test if the two durations are of equal length.
+ *
+ * @throws ClassCastException if the other value is not an xs:duration or subtype thereof
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof DurationValue) {
+ DurationValue d1 = this;
+ DurationValue d2 = (DurationValue)other;
+
+ return d1.negative == d2.negative &&
+ d1.months == d2.months &&
+ d1.seconds == d2.seconds &&
+ d1.microseconds == d2.microseconds;
+ } else {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+ return new Double(getLengthInSeconds()).hashCode();
+ }
+
+ /**
+ * Add two durations
+ *
+ * @param other the duration to be added to this one
+ * @return the sum of the two durations
+ */
+
+ public DurationValue add(DurationValue other) throws XPathException {
+ XPathException err = new XPathException("Only subtypes of xs:duration can be added");
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ throw err;
+ }
+
+ /**
+ * Subtract two durations
+ *
+ * @param other the duration to be subtracted from this one
+ * @return the difference of the two durations
+ */
+
+ public DurationValue subtract(DurationValue other) throws XPathException {
+ XPathException err = new XPathException("Only subtypes of xs:duration can be subtracted");
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ throw err;
+ }
+
+ /**
+ * Negate a duration (same as subtracting from zero, but it preserves the type of the original duration)
+ *
+ * @return the original duration with its sign reversed, retaining its type
+ */
+
+ public DurationValue negate() {
+ return new DurationValue(negative, 0, months, 0, 0, 0, seconds, microseconds, typeLabel);
+ }
+
+ /**
+ * Multiply a duration by a number
+ *
+ * @param factor the number to multiply by
+ * @return the result of the multiplication
+ */
+
+ public DurationValue multiply(double factor) throws XPathException {
+ XPathException err = new XPathException("Only subtypes of xs:duration can be multiplied by a number");
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ throw err;
+ }
+
+ /**
+ * Divide a duration by a another duration
+ *
+ * @param other the duration to divide by
+ * @return the result of the division
+ */
+
+ public DecimalValue divide(DurationValue other) throws XPathException {
+ XPathException err = new XPathException("Only subtypes of xs:duration can be divided by another duration");
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ throw err;
+ }
+
+ /**
+ * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
+ * This implementation handles the ordering rules for durations in XML Schema.
+ * It is overridden for the two subtypes DayTimeDuration and YearMonthDuration.
+ *
+ * @return a suitable Comparable
+ */
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return getSchemaComparable(this);
+ }
+
+ /**
+ * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
+ * This implementation handles the ordering rules for durations in XML Schema.
+ *
+ * @param value the duration for which a comparison key is required
+ * @return a suitable Comparable
+ */
+
+ /*@NotNull*/ public static Comparable getSchemaComparable(/*@NotNull*/ DurationValue value) {
+ int m = value.months;
+ double s = value.seconds + ((double)value.microseconds / 1000000);
+ if (value.negative) {
+ s = -s;
+ m = -m;
+ }
+ return new DurationComparable(m, s);
+ }
+
+ /**
+ * DurationValueOrderingKey is a Comparable value that acts as a surrogate for a Duration,
+ * having ordering rules that implement the XML Schema specification.
+ */
+
+ private static class DurationComparable implements Comparable {
+
+ private int months;
+ private double seconds;
+
+ public DurationComparable(int m, double s) {
+ months = m;
+ seconds = s;
+ }
+
+ /**
+ * Compare two durations according to the XML Schema rules.
+ *
+ * @param o the other duration
+ * @return -1 if this duration is smaller; 0 if they are equal; +1 if this duration is greater;
+ * {@link net.sf.saxon.om.SequenceTool#INDETERMINATE_ORDERING} if there is no defined order
+ */
+
+ public int compareTo(Object o) {
+ DurationComparable other;
+ if (o instanceof DurationComparable) {
+ other = (DurationComparable)o;
+ } else if (o instanceof YearMonthDurationValue) {
+ other = (DurationComparable)getSchemaComparable((YearMonthDurationValue)o);
+ } else if (o instanceof DayTimeDurationValue) {
+ other = (DurationComparable)getSchemaComparable((DayTimeDurationValue)o);
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ if (months == other.months) {
+ return Double.compare(seconds, other.seconds);
+ } else if (seconds == other.seconds) {
+ return (months == other.months ? 0 : (months < other.months ? -1 : +1));
+ } else {
+ double oneDay = 24e0 * 60e0 * 60e0;
+ double min0 = monthsToDaysMinimum(months) * oneDay + seconds;
+ double max0 = monthsToDaysMaximum(months) * oneDay + seconds;
+ double min1 = monthsToDaysMinimum(other.months) * oneDay + other.seconds;
+ double max1 = monthsToDaysMaximum(other.months) * oneDay + other.seconds;
+ if (max0 < min1) {
+ return -1;
+ } else if (min0 > max1) {
+ return +1;
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ }
+ }
+
+ public boolean equals(Object o) {
+ return compareTo(o) == 0;
+ }
+
+ public int hashCode() {
+ return months ^ (int)seconds;
+ }
+
+ private int monthsToDaysMinimum(int months) {
+ if (months < 0) {
+ return -monthsToDaysMaximum(-months);
+ }
+ if (months < 12) {
+ int[] shortest = {0, 28, 59, 89, 120, 150, 181, 212, 242, 273, 303, 334};
+ return shortest[months];
+ } else {
+ int years = months / 12;
+ int remainingMonths = months % 12;
+ // the -1 is to allow for the fact that we might miss a leap day if we time the start badly
+ int yearDays = years * 365 + (years % 4) - (years % 100) + (years % 400) - 1;
+ return yearDays + monthsToDaysMinimum(remainingMonths);
+ }
+ }
+
+ private int monthsToDaysMaximum(int months) {
+ if (months < 0) {
+ return -monthsToDaysMinimum(-months);
+ }
+ if (months < 12) {
+ int[] longest = {0, 31, 62, 92, 123, 153, 184, 215, 245, 276, 306, 337};
+ return longest[months];
+ } else {
+ int years = months / 12;
+ int remainingMonths = months % 12;
+ // the +1 is to allow for the fact that we might miss a leap day if we time the start badly
+ int yearDays = years * 365 + (years % 4) - (years % 100) + (years % 400) + 1;
+ return yearDays + monthsToDaysMaximum(remainingMonths);
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/value/EmptySequence.java b/sf/saxon/value/EmptySequence.java
new file mode 100644
index 0000000..54f65c0
--- /dev/null
+++ b/sf/saxon/value/EmptySequence.java
@@ -0,0 +1,187 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.type.ErrorType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+/**
+* An EmptySequence object represents a sequence containing no members.
+*/
+
+
+public final class EmptySequence<T extends Item> implements GroundedValue {
+
+ // This class has a single instance
+ /*@NotNull*/ private static EmptySequence THE_INSTANCE = new EmptySequence();
+
+
+ /**
+ * Private constructor: only the predefined instances of this class can be used
+ */
+
+ private EmptySequence() {}
+
+ /**
+ * Get the implicit instance of this class
+ * @return the singular instances of this class: an empty sequence
+ */
+
+ /*@NotNull*/ public static <T extends Item> EmptySequence<T> getInstance() {
+ return THE_INSTANCE;
+ }
+
+ public String getStringValue() throws XPathException {
+ return "";
+ }
+
+ public CharSequence getStringValueCS() throws XPathException {
+ return "";
+ }
+
+ /**
+ * Get the first item in the sequence.
+ *
+ * @return the first item in the sequence if there is one, or null if the sequence
+ * is empty
+ * @throws net.sf.saxon.trans.XPathException
+ * in the situation where the sequence is evaluated lazily, and
+ * evaluation of the first item causes a dynamic error.
+ */
+ public Item head() throws XPathException {
+ return null;
+ }
+
+ /**
+ * Return an iteration over the sequence
+ */
+
+ /*@NotNull*/ public SequenceIterator<T> iterate() {
+ return EmptyIterator.emptyIterator();
+ }
+
+ /**
+ * Return the value in the form of an Item
+ * @return the value in the form of an Item
+ */
+
+ /*@Nullable*/ public Item asItem() {
+ return null;
+ }
+
+ /**
+ * Determine the item type
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(/*@NotNull*/ TypeHierarchy th) {
+ return ErrorType.getInstance();
+ }
+
+ /**
+ * Determine the static cardinality
+ */
+
+ public int getCardinality() {
+ return StaticProperty.EMPTY;
+ }
+
+ /**
+ * Get the length of the sequence
+ * @return always 0 for an empty sequence
+ */
+
+ public final int getLength() {
+ return 0;
+ }
+ /**
+ * Is this expression the same as another expression?
+ * @throws ClassCastException if the values are not comparable
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ if (!(other instanceof EmptySequence)) {
+ throw new ClassCastException("Cannot compare " + other.getClass() + " to empty sequence");
+ }
+ return true;
+ }
+
+ public int hashCode() {
+ return 42;
+ }
+
+ /**
+ * Get the effective boolean value - always false
+ */
+
+ public boolean effectiveBooleanValue() {
+ return false;
+ }
+
+
+ /**
+ * Get the n'th item in the sequence (starting from 0). This is defined for all
+ * Values, but its real benefits come for a sequence Value stored extensionally
+ * (or for a MemoClosure, once all the values have been read)
+ *
+ * @param n position of the required item, counting from zero.
+ * @return the n'th item in the sequence, where the first item in the sequence is
+ * numbered zero. If n is negative or >= the length of the sequence, returns null.
+ */
+
+ /*@Nullable*/ public T itemAt(int n) {
+ return null;
+ }
+
+ /**
+ * Get a subsequence of the value
+ *
+ *
+ * @param min the index of the first item to be included in the result, counting from zero.
+ * A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
+ * sequence is returned
+ * @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
+ * get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
+ * is returned. If the value goes off the end of the sequence, the result returns items up to the end
+ * of the sequence
+ * @return the required subsequence. If min is
+ */
+
+ /*@NotNull*/ public GroundedValue subsequence(int min, int length) {
+ return this;
+ }
+
+ /**
+ * Returns a string representation of the object.
+ */
+
+ /*@NotNull*/ public String toString() {
+ return "()";
+ }
+
+ /**
+ * Reduce the sequence to its simplest form. If the value is an empty sequence, the result will be
+ * EmptySequence.getInstance(). If the value is a single atomic value, the result will be an instance
+ * of AtomicValue. If the value is a single item of any other kind, the result will be an instance
+ * of SingletonItem. Otherwise, the result will typically be unchanged.
+ *
+ * @return the simplified sequence
+ */
+ public GroundedValue reduce() {
+ return this;
+ }
+}
+
diff --git a/sf/saxon/value/FloatValue.java b/sf/saxon/value/FloatValue.java
new file mode 100644
index 0000000..70c64ec
--- /dev/null
+++ b/sf/saxon/value/FloatValue.java
@@ -0,0 +1,342 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.sort.DoubleSortComparer;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.Converter;
+
+import java.math.BigDecimal;
+
+/**
+* A numeric (single precision floating point) value
+*/
+
+public final class FloatValue extends NumericValue {
+
+ public static final FloatValue ZERO = new FloatValue((float)0.0);
+ public static final FloatValue NEGATIVE_ZERO = new FloatValue((float)-0.0);
+ public static final FloatValue ONE = new FloatValue((float)1.0);
+ public static final FloatValue NaN = new FloatValue(Float.NaN);
+
+ private float value;
+
+ /**
+ * Constructor supplying a float
+ * @param value the value of the float
+ */
+
+ public FloatValue(float value) {
+ this.value = value;
+ typeLabel = BuiltInAtomicType.FLOAT;
+ }
+
+ /**
+ * Static factory method (for convenience in compiled bytecode)
+ * @param value the value of the float
+ * @return the FloatValue
+ */
+
+ public static FloatValue makeFloatValue(float value) {
+ return new FloatValue(value);
+ }
+
+ /**
+ * Constructor supplying a float and an AtomicType, for creating
+ * a value that belongs to a user-defined subtype of xs:float. It is
+ * the caller's responsibility to ensure that the supplied value conforms
+ * to the supplied type.
+ * @param value the value of the NumericValue
+ * @param type the type of the value. This must be a subtype of xs:float, and the
+ * value must conform to this type. The method does not check these conditions.
+ */
+
+ public FloatValue(float value, AtomicType type) {
+ this.value = value;
+ typeLabel = type;
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ FloatValue v = new FloatValue(value);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.FLOAT;
+ }
+
+ /**
+ * Get the value
+ */
+
+ public float getFloatValue() {
+ return value;
+ }
+
+ public double getDoubleValue() {
+ return (double)value;
+ }
+
+ /**
+ * Get the numeric value converted to a decimal
+ *
+ * @return a decimal representing this numeric value;
+ * @throws net.sf.saxon.trans.XPathException
+ * if the value cannot be converted, for example if it is NaN or infinite
+ */
+ @Override
+ public BigDecimal getDecimalValue() throws XPathException {
+ return new BigDecimal((double)value);
+ }
+
+ /**
+ * Return the numeric value as a Java long.
+ *
+ * @return the numeric value as a Java long. This performs truncation
+ * towards zero.
+ * @throws net.sf.saxon.trans.XPathException
+ * if the value cannot be converted
+ */
+ @Override
+ public long longValue() throws XPathException {
+ return (long)value;
+ }
+
+ /**
+ * Get the hashCode. This must conform to the rules for other NumericValue hashcodes
+ * @see NumericValue#hashCode
+ */
+
+ public int hashCode() {
+ if (value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) {
+ return (int)value;
+ } else {
+ return new Double(getDoubleValue()).hashCode();
+ }
+ }
+
+ /**
+ * Test whether the value is the double/float value NaN
+ */
+
+ public boolean isNaN() {
+ return Float.isNaN(value);
+ }
+
+ /**
+ * Get the effective boolean value
+ * @return true unless the value is zero or NaN
+ */
+ public boolean effectiveBooleanValue() {
+ return (value!=0.0 && !Float.isNaN(value));
+ }
+
+
+ /**
+ * Get the value as a String
+ * @return a String representation of the value
+ */
+
+// public String getStringValue() {
+// return getStringValueCS().toString();
+// }
+
+ /**
+ * Get the value as a String
+ * @return a String representation of the value
+ */
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+ return floatToString(value);
+ }
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules. For xs:float, the canonical
+ * representation always uses exponential notation.
+ */
+
+ public CharSequence getCanonicalLexicalRepresentation() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.TINY);
+ return FloatingPointConverter.appendFloat(fsb, value, true);
+ }
+
+ /**
+ * Internal method used for conversion of a float to a string
+ * @param value the actual value
+ * @return the value converted to a string, according to the XPath casting rules.
+ */
+
+ public static CharSequence floatToString(float value) {
+ return FloatingPointConverter.appendFloat(new FastStringBuffer(FastStringBuffer.TINY), value, false);
+ }
+
+ /**
+ * Negate the value
+ */
+
+ public NumericValue negate() {
+ return new FloatValue(-value);
+ }
+
+ /**
+ * Implement the XPath floor() function
+ */
+
+ public NumericValue floor() {
+ return new FloatValue((float)Math.floor(value));
+ }
+
+ /**
+ * Implement the XPath ceiling() function
+ */
+
+ public NumericValue ceiling() {
+ return new FloatValue((float)Math.ceil(value));
+ }
+
+ /**
+ * Implement the XPath round() function
+ */
+
+ public NumericValue round(int scale) {
+ if (Float.isNaN(value)) return this;
+ if (Float.isInfinite(value)) return this;
+ if (value==0.0) return this; // handles the negative zero case
+ if (value >= -0.5 && value < 0.0) return new FloatValue(-0.0f);
+ if (scale==0 && value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) {
+ return new FloatValue((float)Math.round(value));
+ }
+ DoubleValue d = new DoubleValue(getDoubleValue());
+ d = (DoubleValue)d.round(scale);
+ return new FloatValue(d.getFloatValue());
+ }
+
+ /**
+ * Implement the XPath round-to-half-even() function
+ */
+
+ public NumericValue roundHalfToEven(int scale) {
+ DoubleValue d = new DoubleValue(getDoubleValue());
+ d = (DoubleValue)d.roundHalfToEven(scale);
+ return new FloatValue(d.getFloatValue());
+ }
+
+ /**
+ * Determine whether the value is negative, zero, or positive
+ * @return -1 if negative, 0 if zero (including negative zero), +1 if positive, NaN if NaN
+ */
+
+ public int signum() {
+ if (Float.isNaN(value)) {
+ return 0;
+ }
+ if (value > 0) return 1;
+ if (value == 0) return 0;
+ return -1;
+ }
+
+ /**
+ * Determine whether the value is a whole number, that is, whether it compares
+ * equal to some integer
+ */
+
+ public boolean isWholeNumber() {
+ return value == Math.floor(value) && !Float.isInfinite(value);
+ }
+
+ /**
+ * Get the absolute value as defined by the XPath abs() function
+ * @return the absolute value
+ * @since 9.2
+ */
+
+ public NumericValue abs() {
+ if (value > 0.0) {
+ return this;
+ } else {
+ return new FloatValue(Math.abs(value));
+ }
+ }
+
+ public int compareTo(Object other) {
+ if (!(other instanceof NumericValue)) {
+ throw new ClassCastException("Numeric values are not comparable to " + other.getClass());
+ }
+ if (other instanceof FloatValue) {
+ float otherFloat = ((FloatValue)other).value;
+ if (value == otherFloat) return 0;
+ if (value < otherFloat) return -1;
+ return +1;
+ }
+ if (other instanceof DoubleValue) {
+ return super.compareTo(other);
+ }
+ return compareTo(Converter.NUMERIC_TO_FLOAT.convert(
+ (((NumericValue)other)).asAtomic()));
+ }
+
+ /**
+ * Compare the value to a long
+ * @param other the value to be compared with
+ * @return -1 if this is less, 0 if this is equal, +1 if this is greater or if this is NaN
+ */
+
+ public int compareTo(long other) {
+ float otherFloat = (float)other;
+ if (value == otherFloat) return 0;
+ if (value < otherFloat) return -1;
+ return +1;
+ }
+
+ /**
+ * Get an object that implements XML Schema comparison semantics
+ */
+
+ public Comparable getSchemaComparable() {
+ // convert negative to positive zero because Float.compareTo() does the wrong thing
+ // TODO: what shall we do with NaN?
+ return (value == 0.0f ? 0.0f : value);
+ }
+
+ /**
+ * Determine whether two atomic values are identical, as determined by XML Schema rules. This is a stronger
+ * test than equality (even schema-equality); for example two dateTime values are not identical unless
+ * they are in the same timezone.
+ * <p>Note that even this check ignores the type annotation of the value. The integer 3 and the short 3
+ * are considered identical, even though they are not fully interchangeable. "Identical" means the
+ * same point in the value space, regardless of type annotation.</p>
+ * <p>NaN is identical to itself.</p>
+ *
+ * @param v the other value to be compared with this one
+ * @return true if the two values are identical, false otherwise.
+ */
+
+ public boolean isIdentical(/*@NotNull*/ AtomicValue v) {
+ return v instanceof FloatValue && DoubleSortComparer.getInstance().comparesEqual(this, (FloatValue)v);
+ }
+}
+
diff --git a/sf/saxon/value/FloatingPointConverter.java b/sf/saxon/value/FloatingPointConverter.java
new file mode 100644
index 0000000..7291fe8
--- /dev/null
+++ b/sf/saxon/value/FloatingPointConverter.java
@@ -0,0 +1,632 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.math.BigInteger;
+
+/**
+ * This is a utility class that handles formatting of numbers as strings.
+ * <p>
+ * The algorithm for converting a floating point number to a string is taken from Guy L. Steele and
+ * Jon L. White, <i>How to Print Floating-Point Numbers Accurately</i>, ACM SIGPLAN 1990. It is algorithm
+ * (FPP)<sup>2</sup> from that paper. There are three separate implementations of the algorithm:
+ * <ul>
+ * <li>One using long arithmetic and generating non-exponential output representations
+ * <li>One using BigInteger arithmetic and generating non-exponential output representation
+ * <li>One using BigInteger arithmetic and generating exponential output representations
+ * </ul>
+ * <p>
+ * The choice of method depends on the value of the number being formatted.
+ * <p>
+ * The module contains some residual code (mainly the routine for formatting integers) from the class
+ * AppenderHelper by Jack Shirazi in the O'Reilly book <i>Java Performance Tuning</i>. The floating point routines
+ * in that module were found to be unsuitable, since they used floating point arithmetic which introduces
+ * rounding errors.
+ * <p>
+ * There are several reasons for doing this conversion within Saxon, rather than leaving it all to Java.
+ * Firstly, there are differences in the required output format, notably the absence of ".0" when formatting
+ * whole numbers, and the different rules for the range of numbers where exponential notation is used.
+ * Secondly, there are bugs in some Java implementations, for example JDK outputs 0.001 as 0.0010, and
+ * IKVM/GNU gets things very wrong sometimes. Finally, this implementation is faster for "everyday" numbers,
+ * though it is slower for more extreme numbers. It would probably be reasonable to hand over formatting
+ * to the Java platform (at least when running the Sun JDK) for exponents outside the range -7 to +7.
+ */
+
+public class FloatingPointConverter {
+
+ public static FloatingPointConverter THE_INSTANCE = new FloatingPointConverter();
+
+ private FloatingPointConverter(){}
+
+ /**
+ * char array holding the characters for the string "-Infinity".
+ */
+ private static final char[] NEGATIVE_INFINITY = {'-', 'I', 'N', 'F'};
+ /**
+ * char array holding the characters for the string "Infinity".
+ */
+ private static final char[] POSITIVE_INFINITY = {'I', 'N', 'F'};
+ /**
+ * char array holding the characters for the string "NaN".
+ */
+ private static final char[] NaN = {'N', 'a', 'N'};
+
+ private static final char[] charForDigit = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
+ };
+
+ private static final long doubleSignMask = 0x8000000000000000L;
+ private static final long doubleExpMask = 0x7ff0000000000000L;
+ private static final int doubleExpShift = 52;
+ private static final int doubleExpBias = 1023;
+ private static final long doubleFractMask = 0xfffffffffffffL;
+ private static final int floatSignMask = 0x80000000;
+ private static final int floatExpMask = 0x7f800000;
+ private static final int floatExpShift = 23;
+ private static final int floatExpBias = 127;
+ private static final int floatFractMask = 0x7fffff;
+
+ private static final BigInteger TEN = BigInteger.valueOf(10);
+ private static final BigInteger NINE = BigInteger.valueOf(9);
+
+ /**
+ * Format an integer, appending the string representation of the integer to a string buffer
+ * @param s the string buffer
+ * @param i the integer to be formatted
+ * @return the supplied string buffer, containing the appended integer
+ */
+
+ public static FastStringBuffer appendInt(FastStringBuffer s, int i) {
+ if (i < 0) {
+ if (i == Integer.MIN_VALUE) {
+ //cannot make this positive due to integer overflow
+ s.append("-2147483648");
+ return s;
+ }
+ s.append('-');
+ i = -i;
+ }
+ int c;
+ if (i < 10) {
+ //one digit
+ s.append(charForDigit[i]);
+ return s;
+ } else if (i < 100) {
+ //two digits
+ s.append(charForDigit[i / 10]);
+ s.append(charForDigit[i % 10]);
+ return s;
+ } else if (i < 1000) {
+ //three digits
+ s.append(charForDigit[i / 100]);
+ s.append(charForDigit[(c = i % 100) / 10]);
+ s.append(charForDigit[c % 10]);
+ return s;
+ } else if (i < 10000) {
+ //four digits
+ s.append(charForDigit[i / 1000]);
+ s.append(charForDigit[(c = i % 1000) / 100]);
+ s.append(charForDigit[(c %= 100) / 10]);
+ s.append(charForDigit[c % 10]);
+ return s;
+ } else if (i < 100000) {
+ //five digits
+ s.append(charForDigit[i / 10000]);
+ s.append(charForDigit[(c = i % 10000) / 1000]);
+ s.append(charForDigit[(c %= 1000) / 100]);
+ s.append(charForDigit[(c %= 100) / 10]);
+ s.append(charForDigit[c % 10]);
+ return s;
+ } else if (i < 1000000) {
+ //six digits
+ s.append(charForDigit[i / 100000]);
+ s.append(charForDigit[(c = i % 100000) / 10000]);
+ s.append(charForDigit[(c %= 10000) / 1000]);
+ s.append(charForDigit[(c %= 1000) / 100]);
+ s.append(charForDigit[(c %= 100) / 10]);
+ s.append(charForDigit[c % 10]);
+ return s;
+ } else if (i < 10000000) {
+ //seven digits
+ s.append(charForDigit[i / 1000000]);
+ s.append(charForDigit[(c = i % 1000000) / 100000]);
+ s.append(charForDigit[(c %= 100000) / 10000]);
+ s.append(charForDigit[(c %= 10000) / 1000]);
+ s.append(charForDigit[(c %= 1000) / 100]);
+ s.append(charForDigit[(c %= 100) / 10]);
+ s.append(charForDigit[c % 10]);
+ return s;
+ } else if (i < 100000000) {
+ //eight digits
+ s.append(charForDigit[i / 10000000]);
+ s.append(charForDigit[(c = i % 10000000) / 1000000]);
+ s.append(charForDigit[(c %= 1000000) / 100000]);
+ s.append(charForDigit[(c %= 100000) / 10000]);
+ s.append(charForDigit[(c %= 10000) / 1000]);
+ s.append(charForDigit[(c %= 1000) / 100]);
+ s.append(charForDigit[(c %= 100) / 10]);
+ s.append(charForDigit[c % 10]);
+ return s;
+ } else if (i < 1000000000) {
+ //nine digits
+ s.append(charForDigit[i / 100000000]);
+ s.append(charForDigit[(c = i % 100000000) / 10000000]);
+ s.append(charForDigit[(c %= 10000000) / 1000000]);
+ s.append(charForDigit[(c %= 1000000) / 100000]);
+ s.append(charForDigit[(c %= 100000) / 10000]);
+ s.append(charForDigit[(c %= 10000) / 1000]);
+ s.append(charForDigit[(c %= 1000) / 100]);
+ s.append(charForDigit[(c %= 100) / 10]);
+ s.append(charForDigit[c % 10]);
+ return s;
+ } else {
+ //ten digits
+ s.append(charForDigit[i / 1000000000]);
+ s.append(charForDigit[(c = i % 1000000000) / 100000000]);
+ s.append(charForDigit[(c %= 100000000) / 10000000]);
+ s.append(charForDigit[(c %= 10000000) / 1000000]);
+ s.append(charForDigit[(c %= 1000000) / 100000]);
+ s.append(charForDigit[(c %= 100000) / 10000]);
+ s.append(charForDigit[(c %= 10000) / 1000]);
+ s.append(charForDigit[(c %= 1000) / 100]);
+ s.append(charForDigit[(c %= 100) / 10]);
+ s.append(charForDigit[c % 10]);
+ return s;
+ }
+ }
+
+ /**
+ * Implementation of the (FPP)2 algorithm from Steele and White, for doubles in the range
+ * 0.01 to 1000000, and floats in the range 0.000001 to 1000000.
+ * In this range (a) XPath requires that the output should not be in exponential
+ * notation, and (b) the arithmetic can be handled using longs rather than BigIntegers
+ * @param sb the string buffer to which the formatted result is to be appended
+ * @param e the exponent of the floating point number
+ * @param f the fraction part of the floating point number, such that the "real" value of the
+ * number is f * 2^(e-p), with p>=0 and 0 lt f lt 2^p
+ * @param p the precision
+ */
+
+ private static void fppfpp(FastStringBuffer sb, int e, long f, int p) {
+ long R = f << Math.max(e-p, 0);
+ long S = 1L << Math.max(0, -(e-p));
+ long Mminus = 1L << Math.max(e-p, 0);
+ long Mplus = Mminus;
+ boolean initial = true;
+
+ // simpleFixup
+
+ if (f == 1L << (p-1)) {
+ Mplus = Mplus << 1;
+ R = R << 1;
+ S = S << 1;
+ }
+ int k = 0;
+ while (R < (S+9)/10) { // (S+9)/10 == ceiling(S/10)
+ k--;
+ R = R*10;
+ Mminus = Mminus * 10;
+ Mplus = Mplus * 10;
+ }
+ while (2*R + Mplus >= 2*S) {
+ S = S*10;
+ k++;
+ }
+
+ for (int z=k; z<0; z++) {
+ if (initial) {
+ sb.append("0.");
+ }
+ initial = false;
+ sb.append('0');
+ }
+
+ // end simpleFixup
+
+ //int H = k-1;
+
+ boolean low;
+ boolean high;
+ int U;
+ while (true) {
+ k--;
+ long R10 = R*10;
+ U = (int)(R10 / S);
+ R = R10 - (U * S); // = R*10 % S, but faster - saves a division
+ Mminus = Mminus * 10;
+ Mplus = Mplus * 10;
+ low = 2*R < Mminus;
+ high = 2*R > 2*S - Mplus;
+ if (low || high) break;
+ if (k == -1) {
+ if (initial) {
+ sb.append('0');
+ }
+ sb.append('.');
+ }
+ sb.append(charForDigit[U]);
+ initial = false;
+ }
+ if (high && (!low || 2*R > S)) {
+ U++;
+ }
+ if (k == -1) {
+ if (initial) {
+ sb.append('0');
+ }
+ sb.append('.');
+ }
+ sb.append(charForDigit[U]);
+ for (int z=0; z<k; z++) {
+ sb.append('0');
+ }
+ }
+
+ /**
+ * Implementation of the (FPP)2 algorithm from Steele and White, for doubles in the range
+ * 0.000001 to 0.01. In this range XPath requires that the output should not be in exponential
+ * notation, but the scale factors are large enough to exceed the capacity of long arithmetic.
+ * @param sb the string buffer to which the formatted result is to be appended
+ * @param e the exponent of the floating point number
+ * @param f the fraction part of the floating point number, such that the "real" value of the
+ * number is f * 2^(e-p), with p>=0 and 0 lt f lt 2^p
+ * @param p the precision
+ */
+
+ private static void fppfppBig(FastStringBuffer sb, int e, long f, int p) {
+ //long R = f << Math.max(e-p, 0);
+ BigInteger R = BigInteger.valueOf(f).shiftLeft(Math.max(e-p, 0));
+
+ //long S = 1L << Math.max(0, -(e-p));
+ BigInteger S = BigInteger.ONE.shiftLeft(Math.max(0, -(e-p)));
+
+ //long Mminus = 1 << Math.max(e-p, 0);
+ BigInteger Mminus = BigInteger.ONE.shiftLeft(Math.max(e-p, 0));
+
+ //long Mplus = Mminus;
+ BigInteger Mplus = Mminus;
+
+ boolean initial = true;
+
+ // simpleFixup
+
+ if (f == 1L << (p-1)) {
+ Mplus = Mplus.shiftLeft(1);
+ R = R.shiftLeft(1);
+ S = S.shiftLeft(1);
+ }
+ int k = 0;
+ while (R.compareTo(S.add(NINE).divide(TEN)) < 0) { // (S+9)/10 == ceiling(S/10)
+ k--;
+ R = R.multiply(TEN);
+ Mminus = Mminus.multiply(TEN);
+ Mplus = Mplus.multiply(TEN);
+ }
+ while (R.shiftLeft(1).add(Mplus).compareTo(S.shiftLeft(1)) >= 0) {
+ S = S.multiply(TEN);
+ k++;
+ }
+
+ for (int z=k; z<0; z++) {
+ if (initial) {
+ sb.append("0.");
+ }
+ initial = false;
+ sb.append('0');
+ }
+
+ // end simpleFixup
+
+ //int H = k-1;
+
+ boolean low;
+ boolean high;
+ int U;
+ while (true) {
+ k--;
+ BigInteger R10 = R.multiply(TEN);
+ U = R10.divide(S).intValue();
+ R = R10.mod(S);
+ Mminus = Mminus.multiply(TEN);
+ Mplus = Mplus.multiply(TEN);
+ BigInteger R2 = R.shiftLeft(1);
+ low = R2.compareTo(Mminus) < 0;
+ high = R2.compareTo(S.shiftLeft(1).subtract(Mplus)) > 0;
+ if (low || high) break;
+ if (k == -1) {
+ if (initial) {
+ sb.append('0');
+ }
+ sb.append('.');
+ }
+ sb.append(charForDigit[U]);
+ initial = false;
+ }
+ if (high && (!low || R.shiftLeft(1).compareTo(S) > 0)) {
+ U++;
+ }
+ if (k == -1) {
+ if (initial) {
+ sb.append('0');
+ }
+ sb.append('.');
+ }
+ sb.append(charForDigit[U]);
+ for (int z=0; z<k; z++) {
+ sb.append('0');
+ }
+ }
+
+
+ /**
+ * Implementation of the (FPP)2 algorithm from Steele and White, for numbers outside the range
+ * 0.000001 to 1000000. In this range XPath requires that the output should be in exponential
+ * notation
+ * @param sb the string buffer to which the formatted result is to be appended
+ * @param e the exponent of the floating point number
+ * @param f the fraction part of the floating point number, such that the "real" value of the
+ * number is f * 2^(e-p), with p>=0 and 0 lt f lt 2^p
+ * @param p the precision
+ */
+
+ private static void fppfppExponential(FastStringBuffer sb, int e, long f, int p) {
+ //long R = f << Math.max(e-p, 0);
+ BigInteger R = BigInteger.valueOf(f).shiftLeft(Math.max(e-p, 0));
+
+ //long S = 1L << Math.max(0, -(e-p));
+ BigInteger S = BigInteger.ONE.shiftLeft(Math.max(0, -(e-p)));
+
+ //long Mminus = 1 << Math.max(e-p, 0);
+ BigInteger Mminus = BigInteger.ONE.shiftLeft(Math.max(e-p, 0));
+
+ //long Mplus = Mminus;
+ BigInteger Mplus = Mminus;
+
+ boolean initial = true;
+ boolean doneDot = false;
+
+ // simpleFixup
+
+ if (f == 1L << (p-1)) {
+ Mplus = Mplus.shiftLeft(1);
+ R = R.shiftLeft(1);
+ S = S.shiftLeft(1);
+ }
+ int k = 0;
+ while (R.compareTo(S.add(NINE).divide(TEN)) < 0) { // (S+9)/10 == ceiling(S/10)
+ k--;
+ R = R.multiply(TEN);
+ Mminus = Mminus.multiply(TEN);
+ Mplus = Mplus.multiply(TEN);
+ }
+ while (R.shiftLeft(1).add(Mplus).compareTo(S.shiftLeft(1)) >= 0) {
+ S = S.multiply(TEN);
+ k++;
+ }
+
+ // end simpleFixup
+
+ int H = k-1;
+
+ boolean low;
+ boolean high;
+ int U;
+ while (true) {
+ k--;
+ BigInteger R10 = R.multiply(TEN);
+ U = R10.divide(S).intValue();
+ R = R10.mod(S);
+ Mminus = Mminus.multiply(TEN);
+ Mplus = Mplus.multiply(TEN);
+ BigInteger R2 = R.shiftLeft(1);
+ low = R2.compareTo(Mminus) < 0;
+ high = R2.compareTo(S.shiftLeft(1).subtract(Mplus)) > 0;
+ if (low || high) break;
+
+ sb.append(charForDigit[U]);
+ if (initial) {
+ sb.append('.');
+ doneDot = true;
+ }
+ initial = false;
+ }
+ if (high && (!low || R.shiftLeft(1).compareTo(S) > 0)) {
+ U++;
+ }
+ sb.append(charForDigit[U]);
+
+ if (!doneDot) {
+ sb.append(".0");
+ }
+ sb.append('E');
+ appendInt(sb, H);
+
+ }
+
+ /**
+ * Append a string representation of a double value to a string buffer
+ * @param s the string buffer to which the result will be appended
+ * @param d the double to be formatted
+ * @return the original string buffer, now containing the string representation of the supplied double
+ */
+
+ public static FastStringBuffer appendDouble(FastStringBuffer s, double d, boolean forceExponential) {
+ if (d == Double.NEGATIVE_INFINITY) {
+ s.append(NEGATIVE_INFINITY);
+ } else if (d == Double.POSITIVE_INFINITY) {
+ s.append(POSITIVE_INFINITY);
+ } else if (d != d) {
+ s.append(NaN);
+ } else if (d == 0.0) {
+ if ((Double.doubleToLongBits(d) & doubleSignMask) != 0) {
+ s.append('-');
+ }
+ s.append('0');
+ if (forceExponential) {
+ s.append(".0E0");
+ }
+ } else if (d == Double.MAX_VALUE) {
+ s.append("1.7976931348623157E308");
+ } else if (d == -Double.MAX_VALUE) {
+ s.append("-1.7976931348623157E308");
+ } else if (d == Double.MIN_VALUE) {
+ s.append("4.9E-324");
+ } else if (d == -Double.MIN_VALUE) {
+ s.append("-4.9E-324");
+ } else {
+ if (d < 0) {
+ s.append('-');
+ d = -d;
+ }
+ long bits = Double.doubleToLongBits(d);
+ long fraction = (1L<<52) | (bits & doubleFractMask);
+ long rawExp = (bits & doubleExpMask) >> doubleExpShift;
+ int exp = (int)rawExp - doubleExpBias;
+ if (rawExp == 0) {
+ // don't know how to handle this currently: hand it over to Java to deal with
+ s.append(Double.toString(d));
+ return s;
+ }
+ if (forceExponential || (d >= 1000000 || d < 0.000001)) {
+ fppfppExponential(s, exp, fraction, 52);
+ } else {
+ if (d <= 0.01) {
+ fppfppBig(s, exp, fraction, 52);
+ } else {
+ fppfpp(s, exp, fraction, 52);
+ }
+ }
+
+ // test code
+// try {
+// if (Double.parseDouble(s.toString()) != value) {
+// System.err.println("*** Round-trip failed: input " + value +
+// '(' + Double.doubleToLongBits(value) + ')' +
+// " != output " + s.toString() +
+// '(' + Double.doubleToLongBits(Double.parseDouble(s.toString())) + ')' );
+// }
+// } catch (NumberFormatException e) {
+// System.err.println("*** Bad float " + s.toString() + " for input " + value);
+// }
+ }
+ return s;
+ }
+
+ /**
+ * Append a string representation of a float value to a string buffer
+ * @param s the string buffer to which the result will be appended
+ * @param f the float to be formatted
+ * @param forceExponential forces exponential notation if set (if not set, exponential notation
+ * is used only for values outside the range 1e-6 to 1e+6)
+ * @return the original string buffer, now containing the string representation of the supplied float
+ */
+
+ /*@NotNull*/ public static FastStringBuffer appendFloat(FastStringBuffer s, float f, boolean forceExponential) {
+ if (f == Float.NEGATIVE_INFINITY) {
+ s.append(NEGATIVE_INFINITY);
+ } else if (f == Float.POSITIVE_INFINITY) {
+ s.append(POSITIVE_INFINITY);
+ } else if (f != f) {
+ s.append(NaN);
+ } else if (f == 0.0) {
+ if ((Float.floatToIntBits(f) & floatSignMask) != 0) {
+ s.append('-');
+ }
+ s.append('0');
+ } else if (f == Float.MAX_VALUE) {
+ s.append("3.4028235E38");
+ } else if (f == -Float.MAX_VALUE) {
+ s.append("-3.4028235E38");
+ } else if (f == Float.MIN_VALUE) {
+ s.append("1.4E-45");
+ } else if (f == -Float.MIN_VALUE) {
+ s.append("-1.4E-45");
+ } else {
+ if (f < 0) {
+ s.append('-');
+ f = -f;
+ }
+ int bits = Float.floatToIntBits(f);
+ int fraction = (1<<23) | (bits & floatFractMask);
+ int rawExp = ((bits & floatExpMask) >> floatExpShift);
+ int exp = rawExp - floatExpBias;
+ int precision = 23;
+ if (rawExp == 0) {
+ // don't know how to handle this currently: hand it over to Java to deal with
+ s.append(Float.toString(f));
+ return s;
+ }
+ if (forceExponential || (f >= 1000000 || f < 0.000001F)) {
+ fppfppExponential(s, exp, fraction, precision);
+ } else {
+ fppfpp(s, exp, fraction, precision);
+ }
+ }
+ return s;
+ }
+
+
+
+// public static void main(String[] args) {
+// if (args.length > 0 && args[0].equals("F")) {
+// if (args.length == 2) {
+// StringTokenizer tok = new StringTokenizer(args[1], ",");
+// while (tok.hasMoreElements()) {
+// String input = tok.nextToken();
+// float f = Float.parseFloat(input);
+// FastStringBuffer sb = new FastStringBuffer(20);
+// appendFloat(sb, f);
+// System.err.println("input: " + input + " output: " + sb.toString() + " java: " + f);
+// }
+// } else {
+// Random gen = new Random();
+// for (int i=1; i<1000; i++) {
+// int p=gen.nextInt(999*i*i);
+// int q=gen.nextInt(999*i*i);
+// String input = (p + "." + q);
+// float f = Float.parseFloat(input);
+// FastStringBuffer sb = new FastStringBuffer(20);
+// appendFloat(sb, f);
+// System.err.println("input: " + input + " output: " + sb.toString() + " java: " + f);
+// }
+// }
+// } else {
+// if (args.length == 2) {
+// StringTokenizer tok = new StringTokenizer(args[1], ",");
+// while (tok.hasMoreElements()) {
+// String input = tok.nextToken();
+// double f = Double.parseDouble(input);
+// FastStringBuffer sb = new FastStringBuffer(20);
+// appendDouble(sb, f);
+// System.err.println("input: " + input + " output: " + sb.toString() + " java: " + f);
+// }
+// } else {
+// long start = System.currentTimeMillis();
+// Random gen = new Random();
+// for (int i=1; i<100000; i++) {
+// //int p=gen.nextInt(999*i*i);
+// int q=gen.nextInt(999*i);
+// //String input = (p + "." + q);
+// String input = "0.000" + q;
+// double f = Double.parseDouble(input);
+// FastStringBuffer sb = new FastStringBuffer(20);
+// appendDouble(sb, f);
+// //System.err.println("input: " + input + " output: " + sb.toString() + " java: " + f);
+// }
+// System.err.println("** elapsed time " + (System.currentTimeMillis() - start));
+// }
+// }
+// }
+
+
+
+}
+
+
diff --git a/sf/saxon/value/GDateValue.java b/sf/saxon/value/GDateValue.java
new file mode 100644
index 0000000..5f53aac
--- /dev/null
+++ b/sf/saxon/value/GDateValue.java
@@ -0,0 +1,438 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.ComparisonKey;
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.util.*;
+
+/**
+ * Abstract superclass for the primitive types containing date components: xs:date, xs:gYear,
+ * xs:gYearMonth, xs:gMonth, xs:gMonthDay, xs:gDay
+ */
+public abstract class GDateValue extends CalendarValue {
+ protected int year; // unlike the lexical representation, includes a year zero
+ protected byte month;
+ protected byte day;
+ protected boolean xsd10rules;
+ /**
+ * Test whether a candidate date is actually a valid date in the proleptic Gregorian calendar
+ */
+
+ /*@NotNull*/ protected static byte[] daysPerMonth = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+ /*@NotNull*/ protected static final short[] monthData = {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
+
+ /**
+ * Get the year component of the date (in local form)
+ * @return the year component, as represented internally (allowing a year zero)
+ */
+
+ public int getYear() {
+ return year;
+ }
+
+ /**
+ * Get the month component of the date (in local form)
+ * @return the month component (1-12)
+ */
+
+ public byte getMonth() {
+ return month;
+ }
+
+ /**
+ * Get the day component of the date (in local form)
+ * @return the day component (1-31)
+ */
+
+ public byte getDay() {
+ return day;
+ }
+
+ /*@NotNull*/ public GregorianCalendar getCalendar() {
+
+ int tz = (hasTimezone() ? getTimezoneInMinutes() : 0);
+ TimeZone zone = new SimpleTimeZone(tz * 60000, "LLL");
+ GregorianCalendar calendar = new GregorianCalendar(zone);
+ calendar.setGregorianChange(new Date(Long.MIN_VALUE));
+ calendar.clear();
+ calendar.setLenient(false);
+ int yr = year;
+ if (year <= 0) {
+ yr = (xsd10rules ? 1 - year : 0 - year);
+ calendar.set(Calendar.ERA, GregorianCalendar.BC);
+ }
+ calendar.set(yr, month - 1, day);
+ calendar.set(Calendar.ZONE_OFFSET, tz * 60000);
+ calendar.set(Calendar.DST_OFFSET, 0);
+ calendar.getTime();
+ return calendar;
+ }
+
+ /**
+ * Initialize the DateValue using a character string in the format yyyy-mm-dd and an optional time zone.
+ * Input must have format [-]yyyy-mm-dd[([+|-]hh:mm | Z)]
+ * @param d the "raw" DateValue to be populated
+ * @param s the supplied string value
+ * @param rules
+ * @return either the supplied GDateValue, with its data initialized; or a ValidationFailure
+ */
+
+ /*@NotNull*/ protected static ConversionResult setLexicalValue(/*@NotNull*/ GDateValue d, CharSequence s, /*@NotNull*/ ConversionRules rules) {
+ d.xsd10rules = !rules.isAllowYearZero();
+ StringTokenizer tok = new StringTokenizer(Whitespace.trimWhitespace(s).toString(), "-:+TZ", true);
+ try {
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ String part = (String)tok.nextElement();
+ int era = +1;
+ if ("+".equals(part)) {
+ return badDate("Date must not start with '+' sign", s);
+ } else if ("-".equals(part)) {
+ era = -1;
+ if (!tok.hasMoreElements()) {
+ return badDate("No year after '-'", s);
+ }
+ part = (String)tok.nextElement();
+ }
+
+ if (part.length() < 4) {
+ return badDate("Year is less than four digits", s);
+ }
+ if (part.length() > 4 && part.charAt(0) == '0') {
+ return badDate("When year exceeds 4 digits, leading zeroes are not allowed", s);
+ }
+ int value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ if (value == -1) {
+ return badDate("Non-numeric year component", s);
+ } else {
+ return badDate("Year is outside the range that Saxon can handle", s, "FODT0001");
+ }
+ }
+ d.year = value * era;
+ if (d.year == 0 && !rules.isAllowYearZero()) {
+ return badDate("Year zero is not allowed", s);
+ }
+ if (era < 0 && !rules.isAllowYearZero()) {
+ d.year++; // if year zero not allowed, -0001 is the year before +0001, represented as 0 internally.
+ }
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ if (!"-".equals(tok.nextElement())) {
+ return badDate("Wrong delimiter after year", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badDate("Month must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric month component", s);
+ }
+ d.month = (byte)value;
+ if (d.month < 1 || d.month > 12) {
+ return badDate("Month is out of range", s);
+ }
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ if (!"-".equals(tok.nextElement())) {
+ return badDate("Wrong delimiter after month", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("Too short", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badDate("Day must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric day component", s);
+ }
+ d.day = (byte)value;
+ if (d.day < 1 || d.day > 31) {
+ return badDate("Day is out of range", s);
+ }
+
+ int tzOffset;
+ if (tok.hasMoreElements()) {
+
+ String delim = (String)tok.nextElement();
+
+ if ("T".equals(delim)) {
+ return badDate("Value includes time", s);
+ } else if ("Z".equals(delim)) {
+ tzOffset = 0;
+ if (tok.hasMoreElements()) {
+ return badDate("Continues after 'Z'", s);
+ }
+ d.setTimezoneInMinutes(tzOffset);
+
+ } else if (!(!"+".equals(delim) && !"-".equals(delim))) {
+ if (!tok.hasMoreElements()) {
+ return badDate("Missing timezone", s);
+ }
+ part = (String)tok.nextElement();
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric timezone hour component", s);
+ }
+ int tzhour = value;
+ if (part.length() != 2) {
+ return badDate("Timezone hour must be two digits", s);
+ }
+ if (tzhour > 14) {
+ return badDate("Timezone hour is out of range", s);
+ }
+ if (!tok.hasMoreElements()) {
+ return badDate("No minutes in timezone", s);
+ }
+ if (!":".equals(tok.nextElement())) {
+ return badDate("Wrong delimiter after timezone hour", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badDate("No minutes in timezone", s);
+ }
+ part = (String)tok.nextElement();
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badDate("Non-numeric timezone minute component", s);
+ }
+ int tzminute = value;
+ if (part.length() != 2) {
+ return badDate("Timezone minute must be two digits", s);
+ }
+ if (tzminute > 59) {
+ return badDate("Timezone minute is out of range", s);
+ }
+ if (tok.hasMoreElements()) {
+ return badDate("Continues after timezone", s);
+ }
+
+ tzOffset = (tzhour * 60 + tzminute);
+ if ("-".equals(delim)) {
+ tzOffset = -tzOffset;
+ }
+ d.setTimezoneInMinutes(tzOffset);
+
+ } else {
+ return badDate("Timezone format is incorrect", s);
+ }
+ }
+
+ if (!isValidDate(d.year, d.month, d.day)) {
+ return badDate("Non-existent date", s);
+ }
+
+ } catch (NumberFormatException err) {
+ return badDate("Non-numeric component", s);
+ }
+ return d;
+ }
+
+ private static ValidationFailure badDate(String msg, CharSequence value) {
+ ValidationFailure err = new ValidationFailure(
+ "Invalid date " + Err.wrap(value, Err.VALUE) + " (" + msg + ")");
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+
+ private static ValidationFailure badDate(String msg, CharSequence value, String errorCode) {
+ ValidationFailure err = new ValidationFailure(
+ "Invalid date " + Err.wrap(value, Err.VALUE) + " (" + msg + ")");
+ err.setErrorCode(errorCode);
+ return err;
+ }
+
+
+ /**
+ * Determine whether a given date is valid
+ * @param year the year (permitting year zero)
+ * @param month the month (1-12)
+ * @param day the day (1-31)
+ * @return true if this is a valid date
+ */
+
+ public static boolean isValidDate(int year, int month, int day) {
+ return month > 0 && month <= 12 && day > 0 && day <= daysPerMonth[month - 1]
+ || month == 2 && day == 29 && isLeapYear(year);
+ }
+
+ /**
+ * Test whether a year is a leap year
+ * @param year the year (permitting year zero)
+ * @return true if the supplied year is a leap year
+ */
+
+ public static boolean isLeapYear(int year) {
+ return (year % 4 == 0) && !(year % 100 == 0 && !(year % 400 == 0));
+ }
+
+ /**
+ * The equals() methods on atomic values is defined to follow the semantics of eq when applied
+ * to two atomic values. When the other operand is not an atomic value, the result is undefined
+ * (may be false, may be an exception). When the other operand is an atomic value that cannot be
+ * compared with this one, the method returns false.
+ * <p/>
+ * <p>The hashCode() method is consistent with equals().</p>
+ *
+ * <p>This implementation performs a context-free comparison: it fails with ClassCastException
+ * if one value has a timezone and the other does not.</p>
+ *
+ * @param o the other value
+ * @return true if the other operand is an atomic value and the two values are equal as defined
+ * by the XPath eq operator
+ * @throws ClassCastException if the values are not comparable
+ */
+
+ public boolean equals(Object o) {
+ if (o instanceof GDateValue) {
+ GDateValue gdv = (GDateValue)o;
+ return getPrimitiveType() == gdv.getPrimitiveType() && toDateTime().equals(gdv.toDateTime());
+ } else {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+ return DateTimeValue.hashCode(year, month, day, (byte)12, (byte)0, (byte)0, 0, getTimezoneInMinutes());
+ }
+
+ /**
+ * Compare this value to another value of the same type, using the supplied context object
+ * to get the implicit timezone if required. This method implements the XPath comparison semantics.
+ * @param other the value to be compared
+ * @param context the XPath dynamic evaluation context (needed only to get the implicit timezone)
+ * @return -1 if this value is less, 0 if equal, +1 if greater
+ */
+
+ public int compareTo(/*@NotNull*/ CalendarValue other, /*@Nullable*/ XPathContext context) throws NoDynamicContextException {
+ if (getPrimitiveType() != other.getPrimitiveType()) {
+ throw new ClassCastException("Cannot compare dates of different types");
+ // covers, for example, comparing a gYear to a gYearMonth
+ }
+ GDateValue v2 = (GDateValue)other;
+ if (getTimezoneInMinutes() == other.getTimezoneInMinutes()) {
+ // both values are in the same timezone (explicitly or implicitly)
+ if (year != v2.year) {
+ return IntegerValue.signum(year - v2.year);
+ }
+ if (month != v2.month) {
+ return IntegerValue.signum(month - v2.month);
+ }
+ if (day != v2.day) {
+ return IntegerValue.signum(day - v2.day);
+ }
+ return 0;
+ }
+ return toDateTime().compareTo(other.toDateTime(), context);
+ }
+
+ /**
+ * Convert to DateTime.
+ * @return the starting instant of the GDateValue (with the same timezone)
+ */
+
+ /*@NotNull*/ public DateTimeValue toDateTime() {
+ return new DateTimeValue(year, month, day, (byte)0, (byte)0, (byte)0, 0, getTimezoneInMinutes(),xsd10rules);
+ }
+
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return new GDateComparable();
+ }
+
+ /**
+ * Get a component of the value. Returns null if the timezone component is
+ * requested and is not present.
+ */
+
+ /*@Nullable*/ public AtomicValue getComponent(int component) throws XPathException {
+ switch (component) {
+ case Component.YEAR_ALLOWING_ZERO:
+ return Int64Value.makeIntegerValue(year);
+ case Component.YEAR:
+ return Int64Value.makeIntegerValue(year > 0 || !xsd10rules ? year : year-1);
+ case Component.MONTH:
+ return Int64Value.makeIntegerValue(month);
+ case Component.DAY:
+ return Int64Value.makeIntegerValue(day);
+ case Component.TIMEZONE:
+ if (hasTimezone()) {
+ return DayTimeDurationValue.fromMilliseconds(60000L * getTimezoneInMinutes());
+ } else {
+ return null;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown component for date: " + component);
+ }
+ }
+
+ private class GDateComparable implements Comparable {
+
+ /*@NotNull*/ public GDateValue asGDateValue() {
+ return GDateValue.this;
+ }
+
+ public int compareTo(/*@NotNull*/ Object o) {
+ if (o instanceof GDateComparable) {
+ if (asGDateValue().getPrimitiveType() != ((GDateComparable)o).asGDateValue().getPrimitiveType()) {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ DateTimeValue dt0 = GDateValue.this.toDateTime();
+ DateTimeValue dt1 = ((GDateComparable)o).asGDateValue().toDateTime();
+ return dt0.getSchemaComparable().compareTo(dt1.getSchemaComparable());
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ }
+
+ public boolean equals(/*@NotNull*/ Object o) {
+ return compareTo(o) == 0;
+ }
+
+ public int hashCode() {
+ return GDateValue.this.toDateTime().getSchemaComparable().hashCode();
+ }
+ }
+
+ /**
+ * Get a comparison key for this value. Two values are equal if and only if they their comparison
+ * keys are equal
+ * @param context XPath dynamic evaluation context
+ * @throws NoDynamicContextException if the implicit timezone is required and is not available
+ * (because the method is being called at compile time)
+ */
+
+
+ /*@NotNull*/ public ComparisonKey getComparisonKey(/*@NotNull*/ XPathContext context) throws NoDynamicContextException {
+ return new ComparisonKey(StandardNames.XS_DATE, toDateTime().normalize(context));
+ }
+
+}
+
diff --git a/sf/saxon/value/GDayValue.java b/sf/saxon/value/GDayValue.java
new file mode 100644
index 0000000..c8b6a53
--- /dev/null
+++ b/sf/saxon/value/GDayValue.java
@@ -0,0 +1,123 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Implementation of the xs:gDay data type
+ */
+
+public class GDayValue extends GDateValue {
+
+ private static Pattern regex =
+ Pattern.compile("---([0-9][0-9])(Z|[+-][0-9][0-9]:[0-9][0-9])?");
+
+ private GDayValue(){}
+
+ public static ConversionResult makeGDayValue(CharSequence value, ConversionRules rules) {
+ Matcher m = regex.matcher(Whitespace.trimWhitespace(value));
+ if (!m.matches()) {
+ return new ValidationFailure("Cannot convert '" + value + "' to a gDay");
+ }
+ GDayValue g = new GDayValue();
+ String base = m.group(1);
+ String tz = m.group(2);
+ String date = "2000-01-" + base + (tz==null ? "" : tz);
+ g.typeLabel = BuiltInAtomicType.G_DAY;
+ return setLexicalValue(g, date, rules);
+ }
+
+ public GDayValue(byte day, int tz) {
+ this(day, tz, BuiltInAtomicType.G_DAY);
+ }
+
+ public GDayValue(byte day, int tz, AtomicType type) {
+ this.year = 2000;
+ this.month = 1;
+ this.day = day;
+ setTimezoneInMinutes(tz);
+ this.typeLabel = type;
+ }
+
+ /**
+ * Make a copy of this date, time, or dateTime value
+ * @param typeLabel
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ GDayValue v = new GDayValue(day, getTimezoneInMinutes());
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.G_DAY;
+ }
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+
+ sb.append("---");
+ appendTwoDigits(sb, day);
+
+ if (hasTimezone()) {
+ appendTimezone(sb);
+ }
+
+ return sb;
+
+ }
+
+ /**
+ * Add a duration to this date/time value
+ *
+ * @param duration the duration to be added (which might be negative)
+ * @return a new date/time value representing the result of adding the duration. The original
+ * object is not modified.
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ public CalendarValue add(DurationValue duration) throws XPathException {
+ XPathException err = new XPathException("Cannot add a duration to an xs:gDay");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+
+ /**
+ * Return a new date, time, or dateTime with the same normalized value, but
+ * in a different timezone
+ *
+ * @param tz the new timezone, in minutes
+ * @return the date/time in the new timezone
+ */
+
+ public CalendarValue adjustTimezone(int tz) {
+ DateTimeValue dt = (DateTimeValue)toDateTime().adjustTimezone(tz);
+ return new GDayValue(dt.getDay(), dt.getTimezoneInMinutes());
+ }
+}
+
diff --git a/sf/saxon/value/GMonthDayValue.java b/sf/saxon/value/GMonthDayValue.java
new file mode 100644
index 0000000..1e0f0b2
--- /dev/null
+++ b/sf/saxon/value/GMonthDayValue.java
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Implementation of the xs:gYear data type
+ */
+
+public class GMonthDayValue extends GDateValue {
+
+ private static Pattern regex =
+ Pattern.compile("--([0-9][0-9]-[0-9][0-9])(Z|[+-][0-9][0-9]:[0-9][0-9])?");
+
+ private GMonthDayValue(){}
+
+ public static ConversionResult makeGMonthDayValue(CharSequence value, ConversionRules rules) {
+ Matcher m = regex.matcher(Whitespace.trimWhitespace(value));
+ if (!m.matches()) {
+ return new ValidationFailure("Cannot convert '" + value + "' to a gMonthDay");
+ }
+ GMonthDayValue g = new GMonthDayValue();
+ String base = m.group(1);
+ String tz = m.group(2);
+ String date = "2000-" + base + (tz==null ? "" : tz);
+ g.typeLabel = BuiltInAtomicType.G_MONTH_DAY;
+ return setLexicalValue(g, date, rules);
+ }
+
+ public GMonthDayValue(byte month, byte day, int tz) {
+ this(month, day, tz, BuiltInAtomicType.G_MONTH_DAY);
+ }
+
+ public GMonthDayValue(byte month, byte day, int tz, AtomicType type) {
+ this.year = 2000;
+ this.month = month;
+ this.day = day;
+ setTimezoneInMinutes(tz);
+ this.typeLabel = type;
+ }
+
+ /**
+ * Make a copy of this date, time, or dateTime value
+ * @param typeLabel
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ GMonthDayValue v = new GMonthDayValue(month, day, getTimezoneInMinutes());
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.G_MONTH_DAY;
+ }
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+
+ sb.append("--");
+ appendTwoDigits(sb, month);
+ sb.append('-');
+ appendTwoDigits(sb, day);
+
+ if (hasTimezone()) {
+ appendTimezone(sb);
+ }
+
+ return sb;
+
+ }
+
+ /**
+ * Add a duration to this date/time value
+ *
+ * @param duration the duration to be added (which might be negative)
+ * @return a new date/time value representing the result of adding the duration. The original
+ * object is not modified.
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ public CalendarValue add(DurationValue duration) throws XPathException {
+ XPathException err = new XPathException("Cannot add a duration to an xs:gMonthDay");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+
+ /**
+ * Return a new date, time, or dateTime with the same normalized value, but
+ * in a different timezone
+ *
+ * @param tz the new timezone, in minutes
+ * @return the date/time in the new timezone
+ */
+
+ public CalendarValue adjustTimezone(int tz) {
+ DateTimeValue dt = (DateTimeValue)toDateTime().adjustTimezone(tz);
+ return new GMonthDayValue(dt.getMonth(), dt.getDay(), dt.getTimezoneInMinutes());
+ }
+}
+
diff --git a/sf/saxon/value/GMonthValue.java b/sf/saxon/value/GMonthValue.java
new file mode 100644
index 0000000..c0d328b
--- /dev/null
+++ b/sf/saxon/value/GMonthValue.java
@@ -0,0 +1,125 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Implementation of the xs:gMonth data type
+ */
+
+public class GMonthValue extends GDateValue {
+
+ private static Pattern regex =
+ //Pattern.compile("--([0-9][0-9])(--)?(Z|[+-][0-9][0-9]:[0-9][0-9])?");
+ Pattern.compile("--([0-9][0-9])(Z|[+-][0-9][0-9]:[0-9][0-9])?");
+ // The commented-out pattern tolerates the bogus format --MM-- which was wrongly permitted by the original schema spec
+
+ private GMonthValue(){}
+
+ public static ConversionResult makeGMonthValue(CharSequence value, ConversionRules rules) {
+ GMonthValue g = new GMonthValue();
+ Matcher m = regex.matcher(Whitespace.trimWhitespace(value));
+ if (!m.matches()) {
+ return new ValidationFailure("Cannot convert '" + value + "' to a gMonth");
+ }
+ String base = m.group(1);
+ String tz = m.group(2);
+ String date = "2000-" + base + "-01" + (tz==null ? "" : tz);
+ g.typeLabel = BuiltInAtomicType.G_MONTH;
+ return setLexicalValue(g, date, rules);
+ }
+
+ public GMonthValue(byte month, int tz) {
+ this(month, tz, BuiltInAtomicType.G_MONTH);
+ }
+
+ public GMonthValue(byte month, int tz, AtomicType type) {
+ this.year = 2000;
+ this.month = month;
+ this.day = 1;
+ setTimezoneInMinutes(tz);
+ this.typeLabel = type;
+ }
+
+ /**
+ * Make a copy of this date, time, or dateTime value
+ * @param typeLabel
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ GMonthValue v = new GMonthValue(month, getTimezoneInMinutes());
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.G_MONTH;
+ }
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+
+ sb.append("--");
+ appendTwoDigits(sb, month);
+
+ if (hasTimezone()) {
+ appendTimezone(sb);
+ }
+
+ return sb;
+
+ }
+
+ /**
+ * Add a duration to this date/time value
+ *
+ * @param duration the duration to be added (which might be negative)
+ * @return a new date/time value representing the result of adding the duration. The original
+ * object is not modified.
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ public CalendarValue add(DurationValue duration) throws XPathException {
+ XPathException err = new XPathException("Cannot add a duration to an xs:gMonth");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+
+ /**
+ * Return a new date, time, or dateTime with the same normalized value, but
+ * in a different timezone
+ *
+ * @param tz the new timezone, in minutes
+ * @return the date/time in the new timezone
+ */
+
+ public CalendarValue adjustTimezone(int tz) {
+ DateTimeValue dt = (DateTimeValue)toDateTime().adjustTimezone(tz);
+ return new GMonthValue(dt.getMonth(), dt.getTimezoneInMinutes());
+ }
+}
+
diff --git a/sf/saxon/value/GYearMonthValue.java b/sf/saxon/value/GYearMonthValue.java
new file mode 100644
index 0000000..1b4a05d
--- /dev/null
+++ b/sf/saxon/value/GYearMonthValue.java
@@ -0,0 +1,132 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Implementation of the xs:gYearMonth data type
+ */
+
+public class GYearMonthValue extends GDateValue {
+
+ private static Pattern regex =
+ Pattern.compile("(-?[0-9]+-[0-9][0-9])(Z|[+-][0-9][0-9]:[0-9][0-9])?");
+
+ private GYearMonthValue(){}
+
+ public static ConversionResult makeGYearMonthValue(CharSequence value, ConversionRules rules) {
+ Matcher m = regex.matcher(Whitespace.trimWhitespace(value));
+ if (!m.matches()) {
+ return new ValidationFailure("Cannot convert '" + value + "' to a gYearMonth");
+ }
+ GYearMonthValue g = new GYearMonthValue();
+ String base = m.group(1);
+ String tz = m.group(2);
+ String date = base + "-01" + (tz==null ? "" : tz);
+ g.typeLabel = BuiltInAtomicType.G_YEAR_MONTH;
+ return setLexicalValue(g, date, rules);
+ }
+
+ public GYearMonthValue(int year, byte month, int tz,boolean xsd10) {
+ this(year, month, tz, BuiltInAtomicType.G_YEAR_MONTH);
+ this.xsd10rules = xsd10;
+ }
+
+ public GYearMonthValue(int year, byte month, int tz, AtomicType type) {
+ this.year = year;
+ this.month = month;
+ day = 1;
+ setTimezoneInMinutes(tz);
+ typeLabel = type;
+ }
+
+ /**
+ * Make a copy of this date, time, or dateTime value
+ * @param typeLabel
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ GYearMonthValue v = new GYearMonthValue(year, month, getTimezoneInMinutes(),xsd10rules);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.G_YEAR_MONTH;
+ }
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+ int yr = year;
+ if (year <= 0) {
+ yr = -yr + (xsd10rules ? 1 : 0); // no year zero in lexical space for XSD 1.0
+ if(yr!=0){
+ sb.append('-');
+ }
+ }
+ appendString(sb, yr, (yr>9999 ? (yr+"").length() : 4));
+
+ sb.append('-');
+ appendTwoDigits(sb, month);
+
+ if (hasTimezone()) {
+ appendTimezone(sb);
+ }
+
+ return sb;
+
+ }
+
+ /**
+ * Add a duration to this date/time value
+ *
+ * @param duration the duration to be added (which might be negative)
+ * @return a new date/time value representing the result of adding the duration. The original
+ * object is not modified.
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ public CalendarValue add(DurationValue duration) throws XPathException {
+ XPathException err = new XPathException("Cannot add a duration to an xs:gYearMonth");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+
+ /**
+ * Return a new date, time, or dateTime with the same normalized value, but
+ * in a different timezone
+ *
+ * @param tz the new timezone, in minutes
+ * @return the date/time in the new timezone
+ */
+
+ public CalendarValue adjustTimezone(int tz) {
+ DateTimeValue dt = (DateTimeValue)toDateTime().adjustTimezone(tz);
+ return new GYearMonthValue(dt.getYear(), dt.getMonth(), dt.getTimezoneInMinutes(),xsd10rules);
+ }
+}
+
diff --git a/sf/saxon/value/GYearValue.java b/sf/saxon/value/GYearValue.java
new file mode 100644
index 0000000..ace13cb
--- /dev/null
+++ b/sf/saxon/value/GYearValue.java
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Implementation of the xs:gYear data type
+ */
+
+public class GYearValue extends GDateValue {
+
+ private static Pattern regex =
+ Pattern.compile("(-?[0-9]+)(Z|[+-][0-9][0-9]:[0-9][0-9])?");
+
+ private GYearValue(){}
+
+ public static ConversionResult makeGYearValue(CharSequence value, ConversionRules rules) {
+ GYearValue g = new GYearValue();
+ Matcher m = regex.matcher(Whitespace.trimWhitespace(value));
+ if (!m.matches()) {
+ return new ValidationFailure("Cannot convert '" + value + "' to a gYear");
+ }
+ String base = m.group(1);
+ String tz = m.group(2);
+ String date = base + "-01-01" + (tz==null ? "" : tz);
+ g.typeLabel = BuiltInAtomicType.G_YEAR;
+ return setLexicalValue(g, date, rules);
+ }
+
+ public GYearValue(int year, int tz, boolean xsd10) {
+ this(year, tz, BuiltInAtomicType.G_YEAR);
+ this.xsd10rules = xsd10;
+ }
+
+ public GYearValue(int year, int tz, AtomicType type) {
+ this.year = year;
+ this.month = 1;
+ this.day = 1;
+ setTimezoneInMinutes(tz);
+ this.typeLabel = type;
+ }
+
+ /**
+ * Make a copy of this date, time, or dateTime value
+ * @param typeLabel
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ GYearValue v = new GYearValue(year, getTimezoneInMinutes(), true);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.G_YEAR;
+ }
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+ int yr = year;
+ if (year <= 0) {
+ yr = -yr + (xsd10rules ? 1 : 0); // no year zero in lexical space for XSD 1.0
+ if(yr!=0){
+ sb.append('-');
+ }
+ }
+ appendString(sb, yr, (yr>9999 ? (yr+"").length() : 4));
+
+ if (hasTimezone()) {
+ appendTimezone(sb);
+ }
+
+ return sb;
+
+ }
+
+ /**
+ * Add a duration to this date/time value
+ *
+ * @param duration the duration to be added (which might be negative)
+ * @return a new date/time value representing the result of adding the duration. The original
+ * object is not modified.
+ * @throws net.sf.saxon.trans.XPathException
+ *
+ */
+
+ public CalendarValue add(DurationValue duration) throws XPathException {
+ XPathException err = new XPathException("Cannot add a duration to an xs:gYear");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+
+ /**
+ * Return a new date, time, or dateTime with the same normalized value, but
+ * in a different timezone
+ *
+ * @param tz the new timezone, in minutes
+ * @return the date/time in the new timezone
+ */
+
+ public CalendarValue adjustTimezone(int tz) {
+ DateTimeValue dt = (DateTimeValue)toDateTime().adjustTimezone(tz);
+ return new GYearValue(dt.getYear(), dt.getTimezoneInMinutes(), xsd10rules);
+ }
+}
+
diff --git a/sf/saxon/value/HexBinaryValue.java b/sf/saxon/value/HexBinaryValue.java
new file mode 100644
index 0000000..0bfc0e9
--- /dev/null
+++ b/sf/saxon/value/HexBinaryValue.java
@@ -0,0 +1,260 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+
+import java.util.Arrays;
+
+/**
+ * A value of type xs:hexBinary
+ */
+
+public class HexBinaryValue extends AtomicValue {
+
+ private byte[] binaryValue;
+
+
+ /**
+ * Constructor: create a hexBinary value from a supplied string, in which
+ * each octet is represented by a pair of values from 0-9, a-f, A-F
+ *
+ * @param in character representation of the hexBinary value
+ */
+
+ public HexBinaryValue(CharSequence in) throws XPathException {
+ CharSequence s = Whitespace.trimWhitespace(in);
+ if ((s.length() & 1) != 0) {
+ XPathException err = new XPathException("A hexBinary value must contain an even number of characters");
+ err.setErrorCode("FORG0001");
+ throw err;
+ }
+ binaryValue = new byte[s.length() / 2];
+ for (int i = 0; i < binaryValue.length; i++) {
+ binaryValue[i] = (byte)((fromHex(s.charAt(2 * i)) << 4) +
+ (fromHex(s.charAt(2 * i + 1))));
+ }
+ typeLabel = BuiltInAtomicType.HEX_BINARY;
+ }
+
+ /**
+ * Constructor: create a HexBinary value from a supplied string in hexBinary encoding,
+ * with a specified type. This method throws no checked exceptions; the caller is expected
+ * to ensure that the string is a valid Base64 lexical representation, that it conforms
+ * to the specified type, and that the type is indeed a subtype of xs:base64Binary.
+ * An unchecked exception such as an IllegalArgumentException may be thrown if these
+ * conditions are not satisfied, but this is not guaranteed.
+ *
+ * @param s the value in hexBinary encoding, with no leading or trailing whitespace
+ * @param type the atomic type. This must be xs:base64binary or a subtype.
+ */
+
+ public HexBinaryValue(/*@NotNull*/ CharSequence s, AtomicType type) {
+ if ((s.length() & 1) != 0) {
+ throw new IllegalArgumentException(
+ "A hexBinary value must contain an even number of characters");
+ }
+ binaryValue = new byte[s.length() / 2];
+ try {
+ for (int i = 0; i < binaryValue.length; i++) {
+ binaryValue[i] = (byte)((fromHex(s.charAt(2 * i)) << 4) +
+ (fromHex(s.charAt(2 * i + 1))));
+ }
+ } catch (XPathException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ typeLabel = type;
+ }
+
+ /**
+ * Constructor: create a hexBinary value from a given array of bytes
+ *
+ * @param value the value as an array of bytes
+ */
+
+ public HexBinaryValue(byte[] value) {
+ binaryValue = value;
+ typeLabel = BuiltInAtomicType.HEX_BINARY;
+ }
+
+ /**
+ * Create a primitive copy of this atomic value (usually so that the type label can be changed).
+ *
+ * @param typeLabel the target type (a derived type from hexBinary)
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ HexBinaryValue v = new HexBinaryValue(binaryValue);
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ /*@NotNull*/ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.HEX_BINARY;
+ }
+
+ /**
+ * Get the binary value
+ *
+ * @return the binary value, as a byte array
+ */
+
+ public byte[] getBinaryValue() {
+ return binaryValue;
+ }
+
+ /**
+ * Decode a single hex digit
+ *
+ * @param c the hex digit
+ * @return the numeric value of the hex digit
+ * @throws XPathException if it isn't a hex digit
+ */
+
+ private int fromHex(char c) throws XPathException {
+ int d = "0123456789ABCDEFabcdef".indexOf(c);
+ if (d > 15) {
+ d = d - 6;
+ }
+ if (d < 0) {
+ XPathException err = new XPathException("Invalid hexadecimal digit");
+ err.setErrorCode("FORG0001");
+ throw err;
+ }
+ return d;
+ }
+
+ /**
+ * Convert to string
+ *
+ * @return the canonical representation.
+ */
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+ String digits = "0123456789ABCDEF";
+ FastStringBuffer sb = new FastStringBuffer(binaryValue.length * 2);
+ for (int i = 0; i < binaryValue.length; i++) {
+ sb.append(digits.charAt((binaryValue[i] >> 4) & 0xf));
+ sb.append(digits.charAt(binaryValue[i] & 0xf));
+ }
+ return sb;
+ }
+
+
+ /**
+ * Get the number of octets in the value
+ *
+ * @return the number of octets (bytes) in the value
+ */
+
+ public int getLengthInOctets() {
+ return binaryValue.length;
+ }
+
+ /**
+ * Convert to Java object (for passing to external functions)
+ */
+
+// public Object convertAtomicToJava(Class target, XPathContext context) throws XPathException {
+//
+// if (target.isAssignableFrom(HexBinaryValue.class)) {
+// return this;
+// } else if (target == Object.class) {
+// return getStringValue();
+// } else {
+// Object o = super.convertSequenceToJava(target, context);
+// if (o == null) {
+// throw new XPathException("Conversion of hexBinary to " + target.getName() +
+// " is not supported");
+// }
+// return o;
+// }
+// }
+
+ /**
+ * Support XML Schema comparison semantics
+ */
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return new HexBinaryComparable();
+ }
+
+ private class HexBinaryComparable implements Comparable {
+
+ /*@NotNull*/ public HexBinaryValue getHexBinaryValue() {
+ return HexBinaryValue.this;
+ }
+
+ public int compareTo(/*@NotNull*/ Object o) {
+ if (o instanceof HexBinaryComparable &&
+ Arrays.equals(getHexBinaryValue().binaryValue,
+ ((HexBinaryComparable)o).getHexBinaryValue().binaryValue)) {
+ return 0;
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ }
+
+ public boolean equals(/*@NotNull*/ Object o) {
+ return compareTo(o) == 0;
+ }
+
+ public int hashCode() {
+ return HexBinaryValue.this.hashCode();
+ }
+ }
+
+
+ /**
+ * Get an object value that implements the XPath equality and ordering comparison semantics for this value.
+ * If the ordered parameter is set to true, the result will be a Comparable and will support a compareTo()
+ * method with the semantics of the XPath lt/gt operator, provided that the other operand is also obtained
+ * using the getXPathComparable() method. In all cases the result will support equals() and hashCode() methods
+ * that support the semantics of the XPath eq operator, again provided that the other operand is also obtained
+ * using the getXPathComparable() method. A context argument is supplied for use in cases where the comparison
+ * semantics are context-sensitive, for example where they depend on the implicit timezone or the default
+ * collation.
+ *
+ * @param ordered true if an ordered comparison is required. In this case the result is null if the
+ * type is unordered; in other cases the returned value will be a Comparable.
+ * @param collator
+ * @param context the XPath dynamic evaluation context, used in cases where the comparison is context
+* sensitive @return an Object whose equals() and hashCode() methods implement the XPath comparison semantics
+ */
+
+ /*@Nullable*/ public Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context) {
+ return (ordered ? null : this);
+ }
+
+ /**
+ * Test if the two hexBinary or Base64Binaryvalues are equal.
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ return other instanceof HexBinaryValue && Arrays.equals(binaryValue, ((HexBinaryValue)other).binaryValue);
+ }
+
+ public int hashCode() {
+ return Base64BinaryValue.byteArrayHashCode(binaryValue);
+ }
+
+}
+
diff --git a/sf/saxon/value/Int64Value.java b/sf/saxon/value/Int64Value.java
new file mode 100644
index 0000000..a3f9196
--- /dev/null
+++ b/sf/saxon/value/Int64Value.java
@@ -0,0 +1,669 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.Calculator;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * An integer value: note this is a subtype of decimal in XML Schema, not a primitive type.
+ * This class supports integer values in the range permitted by a Java "long",
+ * and also supports the built-in subtypes of xs:integer.
+ */
+
+public final class Int64Value extends IntegerValue {
+
+ private long value;
+
+/**
+ * Array of small integer values
+ */
+ /*@NotNull*/ private static final Int64Value[] SMALL_INTEGERS = {
+ new Int64Value(0),
+ new Int64Value(1),
+ new Int64Value(2),
+ new Int64Value(3),
+ new Int64Value(4),
+ new Int64Value(5),
+ new Int64Value(6),
+ new Int64Value(7),
+ new Int64Value(8),
+ new Int64Value(9),
+ new Int64Value(10),
+ new Int64Value(11),
+ new Int64Value(12),
+ new Int64Value(13),
+ new Int64Value(14),
+ new Int64Value(15),
+ new Int64Value(16),
+ new Int64Value(17),
+ new Int64Value(18),
+ new Int64Value(19),
+ new Int64Value(20)
+ };
+
+ /**
+ * Constructor supplying a long
+ *
+ * @param value the value of the IntegerValue
+ */
+
+ public Int64Value(long value) {
+ this.value = value;
+ typeLabel = BuiltInAtomicType.INTEGER;
+ }
+
+ /**
+ * Constructor for a subtype, supplying a long and a type label.
+ *
+ * @param val The supplied value, as an integer
+ * @param type the required item type, a subtype of xs:integer
+ * @param check Set to true if the method is required to check that the value is in range;
+ * false if the caller can guarantee that the value has already been checked.
+ * @throws XPathException if the supplied value is out of range for the
+ * target type
+ */
+
+ public Int64Value(long val, /*@NotNull*/ BuiltInAtomicType type, boolean check) throws XPathException {
+ value = val;
+ typeLabel = type;
+ if (check && !checkRange(value, type)) {
+ XPathException err = new XPathException("Integer value " + val +
+ " is out of range for the requested type " + type.getDescription());
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+
+ /**
+ * Factory method: allows Int64Value objects to be reused. Note that
+ * a value obtained using this method must not be modified to set a type label, because
+ * the value is in general shared.
+ * @param value the integer value
+ * @return an Int64Value with this integer value
+ */
+
+ public static Int64Value makeIntegerValue(long value) {
+ if (value <= 20 && value >= 0) {
+ return SMALL_INTEGERS[(int)value];
+ } else {
+ return new Int64Value(value);
+ }
+ }
+
+ /**
+ * Factory method to create a derived value, with no checking of the value against the
+ * derived type
+ * @param val the integer value
+ * @param type the subtype of xs:integer
+ * @return the constructed value
+ */
+
+ /*@NotNull*/ public static Int64Value makeDerived(long val, AtomicType type) {
+ Int64Value v = new Int64Value(val);
+ v.typeLabel = type;
+ return v;
+ }
+
+ /**
+ * Factory method returning the integer -1, 0, or +1 according as the argument
+ * is negative, zero, or positive
+ * @param val the value to be tested
+ * @return the Int64Value representing -1, 0, or +1
+ */
+
+ public static Int64Value signum(long val) {
+ if (val == 0) {
+ return IntegerValue.ZERO;
+ } else {
+ return (val < 0 ? IntegerValue.MINUS_ONE : IntegerValue.PLUS_ONE);
+ }
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(/*@NotNull*/ AtomicType typeLabel) {
+ if (typeLabel.getPrimitiveType() == StandardNames.XS_INTEGER) {
+ Int64Value v = new Int64Value(value);
+ v.typeLabel = typeLabel;
+ return v;
+ } else {
+ return new DecimalValue(value);
+ }
+ }
+
+ /**
+ * Convert the value to a subtype of xs:integer
+ * @param subtype the target subtype
+ * @param validate true if validation is required; false if the caller already knows that the value is valid
+ * @return null if the conversion succeeds; a ValidationFailure describing the failure if it fails. Note
+ * that the exception is returned, not thrown.
+ */
+
+ /*@Nullable*/ public ValidationFailure convertToSubType(/*@NotNull*/ BuiltInAtomicType subtype, boolean validate) {
+ if (!validate) {
+ setSubType(subtype);
+ return null;
+ } else if (checkRange(subtype)) {
+ return null;
+ } else {
+ ValidationFailure err = new ValidationFailure("String " + value +
+ " cannot be converted to integer subtype " + subtype.getDescription());
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+ }
+
+
+ /**
+ * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
+ * This method sets the required type label. Note that this method modifies the value in situ.
+ *
+ * @param type the subtype of integer required
+ * @return null if the operation succeeds, or a ValidationException if the value is out of range
+ */
+
+ /*@Nullable*/ public ValidationFailure validateAgainstSubType(/*@NotNull*/ BuiltInAtomicType type) {
+ if (checkRange(value, type)) {
+ return null;
+ } else {
+ ValidationFailure err = new ValidationFailure("Value " + value +
+ " cannot be converted to integer subtype " + type.getDescription());
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+ }
+
+ /**
+ * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
+ * This method sets the required type label. It is the caller's responsibility to check that
+ * the value is within range.
+ * @param type the type label to be assigned
+ */
+ public void setSubType(AtomicType type) {
+ typeLabel = type;
+ }
+
+ /**
+ * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
+ * This method checks that the value is within range, and also sets the type label.
+ * @param type the subtype of integer required
+ * @return true if successful, false if value is out of range for the subtype
+ */
+ public boolean checkRange(BuiltInAtomicType type) {
+ typeLabel = type;
+ return checkRange(value, type);
+ }
+
+ /**
+ * Get an object that implements XML Schema comparison semantics
+ */
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return new Int64Comparable(this);
+ }
+
+ /**
+ * A Comparable that performs comparison of an Int64Value either with another
+ * Int64Value or with some other representation of an XPath numeric value
+ */
+
+ protected static class Int64Comparable implements Comparable {
+
+ protected Int64Value value;
+
+ public Int64Comparable(Int64Value value) {
+ this.value = value;
+ }
+
+ public long asLong() {
+ return value.longValue();
+ }
+
+ public int compareTo(/*@NotNull*/ Object o) {
+ if (o instanceof Int64Comparable) {
+ long long0 = value.longValue();
+ long long1 = ((Int64Comparable)o).value.longValue();
+ if (long0 <= long1) {
+ if (long0 == long1) {
+ return 0;
+ } else {
+ return -1;
+ }
+ } else {
+ return 1;
+ }
+ } else if (o instanceof BigIntegerValue.BigIntegerComparable) {
+ return value.asBigInteger().compareTo(((BigIntegerValue.BigIntegerComparable)o).asBigInteger());
+ } else if (o instanceof DecimalValue.DecimalComparable) {
+ return value.getDecimalValue().compareTo(((DecimalValue.DecimalComparable)o).asBigDecimal());
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ }
+
+ public boolean equals(/*@NotNull*/ Object o) {
+ if (o instanceof Int64Comparable) {
+ return (asLong() == ((Int64Comparable)o).asLong());
+ } else {
+ return compareTo(o) == 0;
+ }
+ }
+ public int hashCode() {
+ // Must align with hashCodes for other subtypes of xs:decimal
+ return (int)asLong();
+ }
+
+ }
+
+ /**
+ * Get the hashCode. This must conform to the rules for other NumericValue hashcodes
+ * @see NumericValue#hashCode
+ */
+
+ public int hashCode() {
+ if (value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) {
+ return (int) value;
+ } else {
+ return new Double(getDoubleValue()).hashCode();
+ }
+ }
+
+ /**
+ * Get the value
+ * @return the value of the xs:integer, as a Java long
+ */
+
+ public long longValue() {
+ return value;
+ }
+
+ /**
+ * Return the effective boolean value of this integer
+ * @return false if the integer is zero, otherwise true
+ */
+ public boolean effectiveBooleanValue() {
+ return value != 0;
+ }
+
+ /**
+ * Compare the value to another numeric value
+ * @param other the numeric value to be compared to this value
+ * @return -1 if this value is less than the other, 0 if they are equal,
+ * +1 if this value is greater
+ */
+
+ public int compareTo(/*@NotNull*/ Object other) {
+ if (other instanceof Int64Value) {
+ long val2 = ((Int64Value) other).value;
+ if (value == val2) return 0;
+ if (value < val2) return -1;
+ return 1;
+ } else if (other instanceof BigIntegerValue) {
+ return BigInteger.valueOf(value).compareTo(((BigIntegerValue)other).asBigInteger());
+ } else {
+ return super.compareTo(other);
+ }
+ }
+
+ /**
+ * Compare the value to a long
+ * @param other the value to be compared with
+ * @return -1 if this is less, 0 if this is equal, +1 if this is greater or if this is NaN
+ */
+
+ public int compareTo(long other) {
+ if (value == other) return 0;
+ if (value < other) return -1;
+ return 1;
+ }
+
+ /**
+ * Get the value as a String
+ *
+ * @return a String representation of the value
+ */
+
+ public String getPrimitiveStringValue() {
+ return Long.toString(value);
+ }
+
+ /**
+ * Get the numeric value as a double
+ *
+ * @return A double representing this numeric value; NaN if it cannot be
+ * converted
+ */
+ public double getDoubleValue() {
+ return (double) value;
+ }
+
+ /**
+ * Get the numeric value converted to a float
+ *
+ * @return a float representing this numeric value; NaN if it cannot be converted
+ */
+
+ public float getFloatValue() {
+ return (float)value;
+ }
+
+ /**
+ * Get the numeric value converted to a decimal
+ *
+ * @return a decimal representing this numeric value;
+ */
+
+ public BigDecimal getDecimalValue() {
+ return BigDecimal.valueOf(value);
+ }
+
+ /**
+ * Negate the value
+ *
+ * @return the result of inverting the sign of the value
+ */
+
+ public NumericValue negate() {
+ if (value == Long.MIN_VALUE) {
+ return BigIntegerValue.makeIntegerValue(BigInteger.valueOf(value)).negate();
+ } else {
+ return new Int64Value(-value);
+ }
+ }
+
+ /**
+ * Implement the XPath floor() function
+ * @return the integer value, unchanged
+ */
+
+ /*@NotNull*/ public NumericValue floor() {
+ return this;
+ }
+
+ /**
+ * Implement the XPath ceiling() function
+ * @return the integer value, unchanged
+ */
+
+ /*@NotNull*/ public NumericValue ceiling() {
+ return this;
+ }
+
+ /**
+ * Implement the XPath round() function
+ * @param scale the scale (for example scale=2 rounds to 2 decimal places, scale=-2
+ * rounds to a multiple of 100); default value is zero which rounds to an integer
+ * @return the integer value, unchanged
+ */
+
+ public NumericValue round(int scale) {
+ if (scale >= 0 || value == 0) {
+ return this;
+ } else {
+ if (scale < -15) {
+ return new BigIntegerValue(value).round(scale);
+ }
+
+ long absolute = Math.abs(value);
+ long factor = 1;
+ for (long i = 1; i <= -scale; i++) {
+ factor *= 10;
+ }
+ long modulus = absolute % factor;
+ long rval = absolute - modulus;
+ long d = modulus * 2;
+
+ if (value > 0) {
+ if (d >= factor) {
+ rval += factor;
+ }
+ } else {
+ if (d > factor) {
+ rval += factor;
+ }
+ rval = -rval;
+ }
+ return new Int64Value(rval);
+ }
+ }
+
+ /**
+ * Implement the XPath round-to-half-even() function
+ *
+ * @param scale number of digits required after the decimal point; the
+ * value -2 (for example) means round to a multiple of 100
+ * @return if the scale is >=0, return this value unchanged. Otherwise
+ * round it to a multiple of 10**-scale
+ */
+
+ public NumericValue roundHalfToEven(int scale) {
+ if (scale >= 0) {
+ return this;
+ } else {
+ if (scale < -15) {
+ return new BigIntegerValue(value).roundHalfToEven(scale);
+ }
+ long absolute = Math.abs(value);
+ long factor = 1;
+ for (long i = 1; i <= -scale; i++) {
+ factor *= 10;
+ }
+ long modulus = absolute % factor;
+ long rval = absolute - modulus;
+ long d = modulus * 2;
+ if (d > factor) {
+ rval += factor;
+ } else if (d < factor) {
+ // no-op
+ } else {
+ // round to even
+ if (rval % (2 * factor) == 0) {
+ // no-op
+ } else {
+ rval += factor;
+ }
+ }
+ if (value < 0) rval = -rval;
+ return new Int64Value(rval);
+ }
+ }
+
+ /**
+ * Determine whether the value is negative, zero, or positive
+ * @return -1 if negative, 0 if zero, +1 if positive, NaN if NaN
+ */
+
+ public int signum() {
+ if (value > 0) return +1;
+ if (value == 0) return 0;
+ return -1;
+ }
+
+ /**
+ * Get the absolute value as defined by the XPath abs() function
+ * @return the absolute value
+ */
+
+ public NumericValue abs() {
+ if (value > 0) {
+ return this;
+ } else if (value == Long.MIN_VALUE) {
+ return new BigIntegerValue(new BigInteger("9223372036854775808"));
+ } else {
+ return makeIntegerValue(-value);
+ }
+ }
+
+ /**
+ * Add another integer
+ */
+
+ public IntegerValue plus(/*@NotNull*/ IntegerValue other) {
+ // if either of the values is large, we use BigInteger arithmetic to be on the safe side
+ if (other instanceof Int64Value) {
+ long topa = (value >> 60) & 0xf;
+ if (topa != 0 && topa != 0xf) {
+ return new BigIntegerValue(value).plus(new BigIntegerValue(((Int64Value)other).value));
+ }
+ long topb = (((Int64Value)other).value >> 60) & 0xf;
+ if (topb != 0 && topb != 0xf) {
+ return new BigIntegerValue(value).plus(new BigIntegerValue(((Int64Value)other).value));
+ }
+ return makeIntegerValue(value + ((Int64Value)other).value);
+ } else {
+ return new BigIntegerValue(value).plus(other);
+ }
+ }
+
+ /**
+ * Subtract another integer
+ */
+
+ public IntegerValue minus(/*@NotNull*/ IntegerValue other) {
+ // if either of the values is large, we use BigInteger arithmetic to be on the safe side
+ if (other instanceof Int64Value) {
+ long topa = (value >> 60) & 0xf;
+ if (topa != 0 && topa != 0xf) {
+ return new BigIntegerValue(value).minus(new BigIntegerValue(((Int64Value)other).value));
+ }
+ long topb = (((Int64Value)other).value >> 60) & 0xf;
+ if (topb != 0 && topb != 0xf) {
+ return new BigIntegerValue(value).minus(new BigIntegerValue(((Int64Value)other).value));
+ }
+ return makeIntegerValue(value - ((Int64Value)other).value);
+ } else {
+ return new BigIntegerValue(value).minus(other);
+ }
+ }
+
+ /**
+ * Multiply by another integer
+ */
+
+ public IntegerValue times(/*@NotNull*/ IntegerValue other) {
+ // if either of the values is large, we use BigInteger arithmetic to be on the safe side
+ if (other instanceof Int64Value) {
+ if (isLong() || ((Int64Value)other).isLong()) {
+ return new BigIntegerValue(value).times(new BigIntegerValue(((Int64Value)other).value));
+ } else {
+ return makeIntegerValue(value * ((Int64Value)other).value);
+ }
+ } else {
+ return new BigIntegerValue(value).times(other);
+ }
+ }
+
+ /**
+ * Divide by another integer
+ */
+
+ public NumericValue div(/*@NotNull*/ IntegerValue other) throws XPathException {
+ // if either of the values is large, we use BigInteger arithmetic to be on the safe side
+ if (other instanceof Int64Value) {
+ long quotient = ((Int64Value)other).value;
+ if (quotient == 0) {
+ throw new XPathException("Integer division by zero", "FOAR0001");
+ }
+ if (isLong() || ((Int64Value)other).isLong()) {
+ return new BigIntegerValue(value).div(new BigIntegerValue(quotient));
+ }
+
+ // the result of dividing two integers is a decimal; but if
+ // one divides exactly by the other, we implement it as an integer
+ if (value % quotient == 0) {
+ return makeIntegerValue(value / quotient);
+ } else {
+ return Calculator.decimalDivide(new DecimalValue(value), new DecimalValue(quotient));
+ }
+ } else {
+ return new BigIntegerValue(value).div(other);
+ }
+ }
+
+ /**
+ * Take modulo another integer
+ */
+
+ public IntegerValue mod(/*@NotNull*/ IntegerValue other) throws XPathException {
+ // if either of the values is large, we use BigInteger arithmetic to be on the safe side
+ if (other instanceof Int64Value) {
+ long quotient = ((Int64Value) other).value;
+ if (quotient == 0) {
+ throw new XPathException("Integer modulo zero", "FOAR0001");
+ }
+ if (isLong() || ((Int64Value)other).isLong()) {
+ return new BigIntegerValue(value).mod(new BigIntegerValue(((Int64Value)other).value));
+ } else {
+ return makeIntegerValue(value % quotient);
+ }
+ } else {
+ return new BigIntegerValue(value).mod(other);
+ }
+ }
+
+ /**
+ * Integer divide by another integer
+ */
+
+ public IntegerValue idiv(/*@NotNull*/ IntegerValue other) throws XPathException {
+ // if either of the values is large, we use BigInteger arithmetic to be on the safe side
+ if (other instanceof Int64Value) {
+ if (isLong() || ((Int64Value)other).isLong()) {
+ return new BigIntegerValue(value).idiv(new BigIntegerValue(((Int64Value)other).value));
+ }
+ try {
+ return makeIntegerValue(value / ((Int64Value) other).value);
+ } catch (ArithmeticException err) {
+ XPathException e;
+ if ("/ by zero".equals(err.getMessage())) {
+ e = new XPathException("Integer division by zero", "FOAR0001");
+ } else {
+ e = new XPathException("Integer division failure", err);
+ }
+ throw e;
+ }
+ } else {
+ return new BigIntegerValue(value).idiv(other);
+ }
+ }
+
+ /**
+ * Test whether this value needs a long to hold it. Specifically, whether
+ * the absolute value is > 2^31.
+ */
+
+ private boolean isLong() {
+ long top = value >> 31;
+ return top != 0 && top != 0x1ffffffffL;
+ }
+
+ /**
+ * Get the value as a BigInteger
+ */
+
+ public BigInteger asBigInteger() {
+ return BigInteger.valueOf(value);
+ }
+
+}
+
diff --git a/sf/saxon/value/IntegerRange.java b/sf/saxon/value/IntegerRange.java
new file mode 100644
index 0000000..343a993
--- /dev/null
+++ b/sf/saxon/value/IntegerRange.java
@@ -0,0 +1,225 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.RangeIterator;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+/**
+ * This class represents a sequence of consecutive ascending integers, for example 1 to 50.
+ * The integers must be within the range of a Java long.
+ */
+
+public class IntegerRange implements AtomicSequence, GroundedValue {
+
+ public long start;
+ public long end;
+
+ /**
+ * Construct an integer range expression
+ * @param start the first integer in the sequence (inclusive)
+ * @param end the last integer in the sequence (inclusive). Must be >= start
+ */
+
+ public IntegerRange(long start, long end) {
+ if (end < start) {
+ throw new IllegalArgumentException("end < start in IntegerRange");
+ }
+ if (end - start > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("Maximum length of sequence in Saxon is " + Integer.MAX_VALUE);
+ }
+ this.start = start;
+ this.end = end;
+ }
+
+ /**
+ * Get the first integer in the sequence (inclusive)
+ * @return the first integer in the sequence (inclusive)
+ */
+
+ public long getStart() {
+ return start;
+ }
+
+ /**
+ * Get the last integer in the sequence (inclusive)
+ * @return the last integer in the sequence (inclusive)
+ */
+
+ public long getEnd() {
+ return end;
+ }
+
+
+
+ /**
+ * Return an Iterator to iterate over the values of a sequence. The value of every
+ * expression can be regarded as a sequence, so this method is supported for all
+ * expressions. This default implementation handles iteration for expressions that
+ * return singleton values: for non-singleton expressions, the subclass must
+ * provide its own implementation.
+ *
+ * @return a SequenceIterator that can be used to iterate over the result
+ * of the expression
+ * @throws net.sf.saxon.trans.XPathException
+ * if any dynamic error occurs evaluating the
+ * expression
+ */
+
+ /*@NotNull*/ public SequenceIterator<IntegerValue> iterate() {
+ try {
+ return new RangeIterator(start, end);
+ } catch (XPathException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ /**
+ * Determine the data type of the items in the expression, if possible
+ *
+ * @return AnyItemType (not known)
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/ public ItemType getItemType(TypeHierarchy th) {
+ return BuiltInAtomicType.INTEGER;
+ }
+
+ /**
+ * Determine the cardinality
+ */
+
+ public int getCardinality() {
+ return StaticProperty.ALLOWS_MANY;
+ }
+
+ /**
+ * Get the n'th item in the sequence (starting from 0). This is defined for all
+ * Values, but its real benefits come for a sequence Value stored extensionally
+ * (or for a MemoClosure, once all the values have been read)
+ */
+
+ /*@Nullable*/ public IntegerValue itemAt(int n) {
+ if (n < 0 || n > (end-start)) {
+ return null;
+ }
+ return Int64Value.makeIntegerValue(start + n);
+ }
+
+
+ /**
+ * Get a subsequence of the value
+ *
+ *
+ * @param start the index of the first item to be included in the result, counting from zero.
+ * A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
+ * sequence is returned
+ * @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
+ * get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
+ * is returned. If the value goes off the end of the sequence, the result returns items up to the end
+ * of the sequence
+ * @return the required subsequence.
+ */
+
+ /*@NotNull*/ public GroundedValue subsequence(int start, int length) {
+ if (length <= 0) {
+ return EmptySequence.getInstance();
+ }
+ long newStart = this.start + (start > 0 ? start : 0);
+ long newEnd = newStart + length - 1;
+ if (newEnd > end) {
+ newEnd = end;
+ }
+ if (newEnd >= newStart) {
+ return new IntegerRange(newStart, newEnd);
+ } else {
+ return EmptySequence.getInstance();
+ }
+ }
+
+ /**
+ * Get the length of the sequence
+ */
+
+ public int getLength() {
+ return (int)(end - start + 1);
+ }
+
+ public AtomicValue head() {
+ return new Int64Value(start);
+ }
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules.
+ *
+ * @return the canonical lexical representation if defined in XML Schema; otherwise, the result
+ * of casting to string according to the XPath 2.0 rules
+ */
+ public CharSequence getCanonicalLexicalRepresentation() {
+ return getStringValueCS();
+ }
+
+ /**
+ * Get a Comparable value that implements the XML Schema ordering comparison semantics for this value.
+ * The default implementation is written to compare sequences of atomic values.
+ * This method is overridden for AtomicValue and its subclasses.
+ * <p/>
+ * <p>In the case of data types that are partially ordered, the returned Comparable extends the standard
+ * semantics of the compareTo() method by returning the value {@link net.sf.saxon.om.SequenceTool#INDETERMINATE_ORDERING} when there
+ * is no defined order relationship between two given values.</p>
+ *
+ * @return a Comparable that follows XML Schema comparison rules
+ */
+ public Comparable getSchemaComparable() {
+ try {
+ return new AtomicArray(iterate()).getSchemaComparable();
+ } catch (XPathException err) {
+ throw new AssertionError(err);
+ }
+ }
+
+ public CharSequence getStringValueCS() {
+ try {
+ return SequenceTool.getStringValue(this);
+ } catch (XPathException err) {
+ throw new AssertionError(err);
+ }
+ }
+
+ public String getStringValue() {
+ return getStringValueCS().toString();
+ }
+
+ public boolean effectiveBooleanValue() throws XPathException {
+ return ExpressionTool.effectiveBooleanValue(iterate());
+ }
+
+ /**
+ * Reduce the sequence to its simplest form. If the value is an empty sequence, the result will be
+ * EmptySequence.getInstance(). If the value is a single atomic value, the result will be an instance
+ * of AtomicValue. If the value is a single item of any other kind, the result will be an instance
+ * of SingletonItem. Otherwise, the result will typically be unchanged.
+ *
+ * @return the simplified sequence
+ */
+ public GroundedValue reduce() {
+ if (start == end) {
+ return itemAt(0);
+ } else {
+ return this;
+ }
+ }
+}
+
diff --git a/sf/saxon/value/IntegerValue.java b/sf/saxon/value/IntegerValue.java
new file mode 100644
index 0000000..c8e1d80
--- /dev/null
+++ b/sf/saxon/value/IntegerValue.java
@@ -0,0 +1,453 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.functions.FormatNumber;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * This class represents the XPath built-in type xs:integer. It is used for all
+ * subtypes of xs:integer, other than user-defined subtypes. There are two implementations
+ * of IntegerValue: {@link Int64Value}, which accommodates values up to 2^63, and
+ * {@link BigIntegerValue}, which accommodates unlimited-length integers.
+ */
+
+public abstract class IntegerValue extends NumericValue {
+
+ Integer x;
+
+ /**
+ * IntegerValue representing the value -1
+ */
+ /*@NotNull*/ public static final Int64Value MINUS_ONE = new Int64Value(-1);
+ /**
+ * IntegerValue representing the value zero
+ */
+ /*@NotNull*/ public static final Int64Value ZERO = new Int64Value(0);
+ /**
+ * IntegerValue representing the value +1
+ */
+ /*@NotNull*/ public static final Int64Value PLUS_ONE = new Int64Value(+1);
+
+ /**
+ * IntegerValue representing the maximum value for a long
+ */
+ /*@NotNull*/ public static final Int64Value MAX_LONG = new Int64Value(Long.MAX_VALUE);
+ /**
+ * IntegerValue representing the minimum value for a long
+ */
+ /*@NotNull*/ public static final Int64Value MIN_LONG = new Int64Value(Long.MIN_VALUE);
+ /**
+ * Static data identifying the min and max values for each built-in subtype of xs:integer.
+ * This is a sequence of triples, each holding the fingerprint of the type, the minimum
+ * value, and the maximum value. The special value NO_LIMIT indicates that there is no
+ * minimum (or no maximum) for this type. The special value MAX_UNSIGNED_LONG represents the
+ * value 2^64-1
+ */
+ private static long NO_LIMIT = -9999;
+ private static long MAX_UNSIGNED_LONG = -9998;
+
+ /*@NotNull*/ private static long[] ranges = {
+ // arrange so the most frequently used types are near the start
+ StandardNames.XS_INTEGER, NO_LIMIT, NO_LIMIT,
+ StandardNames.XS_LONG, Long.MIN_VALUE, Long.MAX_VALUE,
+ StandardNames.XS_INT, Integer.MIN_VALUE, Integer.MAX_VALUE,
+ StandardNames.XS_SHORT, Short.MIN_VALUE, Short.MAX_VALUE,
+ StandardNames.XS_BYTE, Byte.MIN_VALUE, Byte.MAX_VALUE,
+ StandardNames.XS_NON_NEGATIVE_INTEGER, 0, NO_LIMIT,
+ StandardNames.XS_POSITIVE_INTEGER, 1, NO_LIMIT,
+ StandardNames.XS_NON_POSITIVE_INTEGER, NO_LIMIT, 0,
+ StandardNames.XS_NEGATIVE_INTEGER, NO_LIMIT, -1,
+ StandardNames.XS_UNSIGNED_LONG, 0, MAX_UNSIGNED_LONG,
+ StandardNames.XS_UNSIGNED_INT, 0, 4294967295L,
+ StandardNames.XS_UNSIGNED_SHORT, 0, 65535,
+ StandardNames.XS_UNSIGNED_BYTE, 0, 255};
+
+ /**
+ * Factory method: makes either an Int64Value or a BigIntegerValue depending on the value supplied
+ * @param value the supplied integer value
+ * @return the value as a BigIntegerValue or Int64Value as appropriate
+ */
+
+ public static IntegerValue makeIntegerValue(/*@NotNull*/ BigInteger value) {
+ if (value.compareTo(BigIntegerValue.MAX_LONG) > 0 || value.compareTo(BigIntegerValue.MIN_LONG) < 0) {
+ return new BigIntegerValue(value);
+ } else {
+ return Int64Value.makeIntegerValue(value.longValue());
+ }
+ }
+
+ /**
+ * Convert a double to an integer
+ * @param value the double to be converted
+ * @return the result of the conversion, or the validation failure if the input is NaN or infinity
+ */
+
+ public static ConversionResult makeIntegerValue(double value) {
+ if (Double.isNaN(value)) {
+ ValidationFailure err = new ValidationFailure("Cannot convert double NaN to an integer");
+ err.setErrorCode("FOCA0002");
+ return err;
+ }
+ if (Double.isInfinite(value)) {
+ ValidationFailure err = new ValidationFailure("Cannot convert double INF to an integer");
+ err.setErrorCode("FOCA0002");
+ return err;
+ }
+ if (value > Long.MAX_VALUE || value < Long.MIN_VALUE) {
+ if (value == Math.floor(value)) {
+ return new BigIntegerValue(FormatNumber.adjustToDecimal(value, 2).toBigInteger());
+ } else {
+ return new BigIntegerValue(new BigDecimal(value).toBigInteger());
+ }
+ }
+ return Int64Value.makeIntegerValue((long)value);
+ }
+
+ /**
+ * Convert a double to an integer
+ * @param doubleValue the double to be converted
+ * @return the result of the conversion, or the validation failure if the input is NaN or infinity
+ */
+
+ public static ConversionResult makeIntegerValue(/*@NotNull*/ DoubleValue doubleValue) {
+ double value = doubleValue.getDoubleValue();
+ return makeIntegerValue(value);
+ }
+
+ /**
+ * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
+ * This method sets the required type label. Note that this method modifies the value in situ.
+ * @param type the subtype of integer required
+ * @param validate true if validation is required, false if the caller warrants that the value
+ * is valid for the subtype
+ * @return null if the operation succeeds, or a ValidationException if the value is out of range
+ */
+
+ /*@Nullable*/ public abstract ValidationFailure convertToSubType(BuiltInAtomicType type, boolean validate);
+
+ /**
+ * This class allows subtypes of xs:integer to be held, as well as xs:integer values.
+ * This method sets the required type label. Note that this method modifies the value in situ.
+ * @param type the subtype of integer required
+ * @return null if the operation succeeds, or a ValidationException if the value is out of range
+ */
+
+ /*@Nullable*/ public abstract ValidationFailure validateAgainstSubType(BuiltInAtomicType type);
+
+ /**
+ * Check that a value is in range for the specified subtype of xs:integer
+ *
+ * @param value the value to be checked
+ * @param type the required item type, a subtype of xs:integer
+ * @return true if successful, false if value is out of range for the subtype
+ */
+
+ public static boolean checkRange(long value, /*@NotNull*/ BuiltInAtomicType type) {
+ int fp = type.getFingerprint();
+ for (int i = 0; i < ranges.length; i += 3) {
+ if (ranges[i] == fp) {
+ long min = ranges[i+1];
+ if (min != NO_LIMIT && value < min) {
+ return false;
+ }
+ long max = ranges[i+2];
+ return (max == NO_LIMIT || max == MAX_UNSIGNED_LONG || value <= max);
+ }
+ }
+ throw new IllegalArgumentException(
+ "No range information found for integer subtype " + type.getDescription());
+ }
+
+ /**
+ * Get the minInclusive facet for a built-in integer subtype
+ * @param type the built-in type, which must be derived from xs:integer
+ * @return the value of the minInclusive facet if there is a lower limit, or null if not
+ */
+
+ public static IntegerValue getMinInclusive(BuiltInAtomicType type) {
+ int fp = type.getFingerprint();
+ for (int i = 0; i < ranges.length; i += 3) {
+ if (ranges[i] == fp) {
+ long min = ranges[i+1];
+ if (min == NO_LIMIT) {
+ return null;
+ } else {
+ return Int64Value.makeIntegerValue(min);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the maxInclusive facet for a built-in integer subtype
+ * @param type the built-in type, which must be derived from xs:integer
+ * @return the value of the minInclusive facet if there is a lower limit, or null if not
+ */
+
+ public static IntegerValue getMaxInclusive(BuiltInAtomicType type) {
+ int fp = type.getFingerprint();
+ for (int i = 0; i < ranges.length; i += 3) {
+ if (ranges[i] == fp) {
+ long max = ranges[i+2];
+ if (max == NO_LIMIT) {
+ return null;
+ } else if (max == MAX_UNSIGNED_LONG) {
+ return IntegerValue.makeIntegerValue(BigIntegerValue.MAX_UNSIGNED_LONG);
+ } else {
+ return Int64Value.makeIntegerValue(max);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check that a BigInteger is within the required range for a given integer subtype.
+ * This method is expensive, so it should not be used unless the BigInteger is outside the range of a long.
+ * @param big the supplied BigInteger
+ * @param type the derived type (a built-in restriction of xs:integer) to check the value against
+ * @return true if the value is within the range for the derived type
+ */
+
+ public static boolean checkBigRange(BigInteger big, /*@NotNull*/ BuiltInAtomicType type) {
+
+ for (int i = 0; i < ranges.length; i += 3) {
+ if (ranges[i] == type.getFingerprint()) {
+ long min = ranges[i+1];
+ if (min != NO_LIMIT && BigInteger.valueOf(min).compareTo(big) > 0) {
+ return false;
+ }
+ long max = ranges[i+2];
+ if (max == NO_LIMIT) {
+ return true;
+ } else if (max == MAX_UNSIGNED_LONG) {
+ return BigIntegerValue.MAX_UNSIGNED_LONG.compareTo(big) >= 0;
+ } else {
+ return BigInteger.valueOf(max).compareTo(big) >= 0;
+ }
+ }
+ }
+ throw new IllegalArgumentException(
+ "No range information found for integer subtype " + type.getDescription());
+ }
+
+ /**
+ * Static factory method to convert strings to integers.
+ * @param s CharSequence representing the string to be converted
+ * @return either an Int64Value or a BigIntegerValue representing the value of the String, or
+ * a ValidationFailure encapsulating an Exception if the value cannot be converted.
+ */
+
+ public static ConversionResult stringToInteger(/*@NotNull*/ CharSequence s) {
+
+ int len = s.length();
+ int start = 0;
+ int last = len - 1;
+ while (start < len && s.charAt(start) <= 0x20) {
+ start++;
+ }
+ while (last > start && s.charAt(last) <= 0x20) {
+ last--;
+ }
+ if (start > last) {
+ return new ValidationFailure("Cannot convert zero-length string to an integer");
+ }
+ if (last - start < 16) {
+ // for short numbers, we do the conversion ourselves, to avoid throwing unnecessary exceptions
+ boolean negative = false;
+ long value = 0;
+ int i=start;
+ if (s.charAt(i) == '+') {
+ i++;
+ } else if (s.charAt(i) == '-') {
+ negative = true;
+ i++;
+ }
+ if (i > last) {
+ return new ValidationFailure("Cannot convert string " + Err.wrap(s, Err.VALUE) +
+ " to integer: no digits after the sign");
+ }
+ while (i <= last) {
+ char d = s.charAt(i++);
+ if (d >= '0' && d <= '9') {
+ value = 10*value + (d-'0');
+ } else {
+ return new ValidationFailure("Cannot convert string " + Err.wrap(s, Err.VALUE) + " to an integer");
+ }
+ }
+ return Int64Value.makeIntegerValue((negative ? -value : value));
+ } else {
+ // for longer numbers, rely on library routines
+ try {
+ CharSequence t = Whitespace.trimWhitespace(s);
+ if (t.charAt(0) == '+') {
+ t = t.subSequence(1, t.length());
+ }
+ if (t.length() < 16) {
+ return new Int64Value(Long.parseLong(t.toString()));
+ } else {
+ return new BigIntegerValue(new BigInteger(t.toString()));
+ }
+ } catch (NumberFormatException err) {
+ return new ValidationFailure("Cannot convert string " + Err.wrap(s, Err.VALUE) + " to an integer");
+ }
+ }
+ }
+
+ /**
+ * Determine whether a string is castable as an integer
+ * @param input the string to be tested
+ * @return null if the string is castable to an integer, or a validation failure otherwise
+ */
+
+ /*@Nullable*/ public static ValidationFailure castableAsInteger(CharSequence input) {
+ CharSequence s = Whitespace.trimWhitespace(input);
+
+ int last = s.length()-1;
+ if (last < 0) {
+ return new ValidationFailure("Cannot convert empty string to an integer");
+ }
+ int i=0;
+ if (s.charAt(i) == '+' || s.charAt(i) == '-') {
+ i++;
+ }
+ if (i > last) {
+ return new ValidationFailure("Cannot convert string " + Err.wrap(s, Err.VALUE) +
+ " to integer: no digits after the sign");
+ }
+ while (i <= last) {
+ char d = s.charAt(i++);
+ if (d >= '0' && d <= '9') {
+ // OK
+ } else {
+ return new ValidationFailure("Cannot convert string " + Err.wrap(s, Err.VALUE) + " to an integer: contains a character that is not a digit");
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ /*@NotNull*/ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.INTEGER;
+ }
+
+ /**
+ * Determine whether the value is a whole number, that is, whether it compares
+ * equal to some integer
+ *
+ * @return always true for this implementation
+ */
+
+ public boolean isWholeNumber() {
+ return true;
+ }
+
+ /**
+ * Add another integer
+ * @param other the other integer
+ * @return the result of the addition
+ */
+
+ public abstract IntegerValue plus(IntegerValue other);
+
+ /**
+ * Subtract another integer
+ * @param other the other integer
+ * @return the result of the subtraction
+ */
+
+ public abstract IntegerValue minus(IntegerValue other);
+
+ /**
+ * Multiply by another integer
+ * @param other the other integer
+ * @return the result of the multiplication
+ */
+
+ public abstract IntegerValue times(IntegerValue other);
+
+ /**
+ * Divide by another integer
+ * @param other the other integer
+ * @return the result of the division
+ * @throws XPathException if the other integer is zero
+ */
+
+ public abstract NumericValue div(IntegerValue other) throws XPathException;
+
+ /**
+ * Take modulo another integer
+ * @param other the other integer
+ * @return the result of the modulo operation (the remainder)
+ * @throws XPathException if the other integer is zero
+ */
+
+ public abstract IntegerValue mod(IntegerValue other) throws XPathException;
+
+ /**
+ * Integer divide by another integer
+ * @param other the other integer
+ * @return the result of the integer division
+ * @throws XPathException if the other integer is zero
+ */
+
+ public abstract IntegerValue idiv(IntegerValue other) throws XPathException;
+
+ /**
+ * Get the value as a BigInteger
+ * @return the value, as a BigInteger
+ */
+
+ public abstract BigInteger asBigInteger();
+
+ /**
+ * Get the signum of an int
+ * @param i the int
+ * @return -1 if the integer is negative, 0 if it is zero, +1 if it is positive
+ */
+
+ protected static int signum(int i) {
+ return (i >> 31) | (-i >>> 31);
+ }
+
+ /**
+ * Determine whether two atomic values are identical, as determined by XML Schema rules. This is a stronger
+ * test than equality (even schema-equality); for example two dateTime values are not identical unless
+ * they are in the same timezone.
+ * <p>Note that even this check ignores the type annotation of the value. The integer 3 and the short 3
+ * are considered identical, even though they are not fully interchangeable. "Identical" means the
+ * same point in the value space, regardless of type annotation.</p>
+ * <p>NaN is identical to itself.</p>
+ *
+ * @param v the other value to be compared with this one
+ * @return true if the two values are identical, false otherwise.
+ */
+
+ public boolean isIdentical(/*@NotNull*/ AtomicValue v) {
+ return (v instanceof IntegerValue) && equals(v);
+ }
+
+
+}
+
diff --git a/sf/saxon/value/MemoClosure.java b/sf/saxon/value/MemoClosure.java
new file mode 100644
index 0000000..9da3857
--- /dev/null
+++ b/sf/saxon/value/MemoClosure.java
@@ -0,0 +1,455 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.Controller;
+import net.sf.saxon.event.SequenceOutputter;
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.event.TeeOutputter;
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.XPathContextMajor;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ArrayIterator;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.tree.iter.SingletonIterator;
+
+import java.util.List;
+
+/**
+ * A MemoClosure represents a value that has not yet been evaluated: the value is represented
+ * by an expression, together with saved values of all the context variables that the
+ * expression depends on.
+ * <p/>
+ * <p>The MemoClosure is designed for use when the value is only read several times. The
+ * value is saved on the first evaluation and remembered for later use.</p>
+ * <p/>
+ * <p>The MemoClosure maintains a reservoir containing those items in the value that have
+ * already been read. When a new iterator is requested to read the value, this iterator
+ * first examines and returns any items already placed in the reservoir by previous
+ * users of the MemoClosure. When the reservoir is exhausted, it then uses an underlying
+ * Input Iterator to read further values of the underlying expression. If the value is
+ * not read to completion (for example, if the first user did exists($expr), then the
+ * Input Iterator is left positioned where this user abandoned it. The next user will read
+ * any values left in the reservoir by the first user, and then pick up iterating the
+ * base expression where the first user left off. Eventually, all the values of the
+ * expression will find their way into the reservoir, and future users simply iterate
+ * over the reservoir contents. Alternatively, of course, the values may be left unread.</p>
+ * <p/>
+ * <p>Delayed evaluation is used only for expressions with a static type that allows
+ * more than one item, so the evaluateItem() method will not normally be used, but it is
+ * supported for completeness.</p>
+ * <p/>
+ * <p>The expression may depend on local variables and on the context item; these values
+ * are held in the saved XPathContext object that is kept as part of the Closure, and they
+ * will always be read from that object. The expression may also depend on global variables;
+ * these are unchanging, so they can be read from the Bindery in the normal way. Expressions
+ * that depend on other contextual information, for example the values of position(), last(),
+ * current(), current-group(), should not be evaluated using this mechanism: they should
+ * always be evaluated eagerly. This means that the Closure does not need to keep a copy
+ * of these context variables.</p>
+ * <p/>
+ * <p>In Saxon-EE, a for-each loop can be multithreaded. If a variable declared outside
+ * the loop is evaluated as a MemoClosure, then a reference to the variable within the
+ * loop can result in concurrent attempts to evaluate the variable incrementally. This
+ * is prevented by synchronizing the evaluation methods.</p>
+ */
+
+public class MemoClosure<T extends Item> extends Closure<T> {
+
+ /*@Nullable*/ private T[] reservoir = null;
+ private int used;
+ protected int state;
+
+ // State in which no items have yet been read
+ private static final int UNREAD = 0;
+
+ // State in which zero or more items are in the reservoir and it is not known
+ // whether more items exist
+ private static final int MAYBE_MORE = 1;
+
+ // State in which all the items are in the reservoir
+ private static final int ALL_READ = 3;
+
+ // State in which we are getting the base iterator. If the closure is called in this state,
+ // it indicates a recursive entry, which is only possible on an error path
+ private static final int BUSY = 4;
+
+ // State in which we know that the value is an empty sequence
+ protected static final int EMPTY = 5;
+
+ /**
+ * Constructor should not be called directly, instances should be made using the make() method.
+ */
+
+ //private static int closureCount = 0;
+ public MemoClosure() {
+ //System.err.println("************** Creating MemoClosure " + closureCount);
+ //closureCount++; if ((closureCount % 1000) == 0) System.err.println("MemoClosures: " + closureCount);
+ }
+
+ /**
+ * Evaluate the expression in a given context to return an iterator over a sequence
+ *
+ */
+
+ /*@NotNull*/
+ public synchronized SequenceIterator<T> iterate() throws XPathException {
+
+ switch (state) {
+ case UNREAD:
+ state = BUSY;
+ inputIterator = (SequenceIterator<T>)expression.iterate(savedXPathContext);
+ if (inputIterator instanceof EmptyIterator) {
+ state = EMPTY;
+ return inputIterator;
+ }
+ reservoir = (T[])new Item[50];
+ used = 0;
+ state = MAYBE_MORE;
+ return new ProgressiveIterator();
+
+ case MAYBE_MORE:
+ return new ProgressiveIterator();
+
+ case ALL_READ:
+ switch (used) {
+ case 0:
+ state = EMPTY;
+ return EmptyIterator.emptyIterator();
+ case 1:
+ assert reservoir != null;
+ return SingletonIterator.makeIterator(reservoir[0]);
+ default:
+ return new ArrayIterator<T>(reservoir, 0, used);
+ }
+
+ case BUSY:
+ // recursive entry: can happen if there is a circularity involving variable and function definitions
+ // Can also happen if variable evaluation is attempted in a debugger, hence the cautious message
+ XPathException de = new XPathException("Attempt to access a variable while it is being evaluated");
+ de.setErrorCode("XTDE0640");
+ //de.setXPathContext(context);
+ throw de;
+
+ case EMPTY:
+ return EmptyIterator.emptyIterator();
+
+ default:
+ throw new IllegalStateException("Unknown iterator state");
+
+ }
+ }
+
+ /**
+ * Process the expression by writing the value to the current Receiver
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public synchronized void process(/*@NotNull*/ XPathContext context) throws XPathException {
+ // To evaluate the closure in push mode, we need to use the original context of the
+ // expression for everything except the current output destination, which is taken from the
+ // context supplied at evaluation time
+ if (state == EMPTY) {
+ return; // we know there is nothing to do
+ } else if (state == BUSY) {
+ // recursive entry: can happen if there is a circularity involving variable and function definitions
+ XPathException de = new XPathException("Attempt to access a variable while it is being evaluated");
+ de.setErrorCode("XTDE0640");
+ de.setXPathContext(context);
+ throw de;
+ }
+ if (reservoir != null) {
+ SequenceIterator iter = iterate();
+ SequenceReceiver out = context.getReceiver();
+ while (true) {
+ Item it = iter.next();
+ if (it==null) break;
+ out.append(it, 0, NodeInfo.ALL_NAMESPACES);
+ }
+ } else {
+ state = BUSY;
+ Controller controller = context.getController();
+ XPathContextMajor c2 = savedXPathContext.newContext();
+ // Fork the output: one copy goes to a SequenceOutputter which remembers the contents for
+ // use next time the variable is referenced; another copy goes to the current output destination.
+ SequenceOutputter seq = controller.allocateSequenceOutputter(20);
+ seq.open();
+ TeeOutputter tee = new TeeOutputter(context.getReceiver(), seq);
+ tee.setPipelineConfiguration(controller.makePipelineConfiguration());
+ c2.setReceiver(tee);
+ c2.setTemporaryOutputState(true);
+
+ expression.process(c2);
+
+ seq.close();
+ List list = seq.getList();
+ if (list.isEmpty()) {
+ state = EMPTY;
+ } else {
+ reservoir = (T[])new Item[list.size()];
+ reservoir = (T[])list.toArray(reservoir);
+ used = list.size();
+ state = ALL_READ;
+ }
+ // give unwanted stuff to the garbage collector
+ savedXPathContext = null;
+ seq.reset();
+ }
+
+ }
+
+ /**
+ * Get the n'th item in the sequence (starting from 0). This is defined for all
+ * SequenceValues, but its real benefits come for a SequenceValue stored extensionally
+ */
+
+ /**
+ * Append an item to the reservoir
+ * @param item the item to be added
+ */
+
+ private void append(T item) {
+ assert reservoir != null;
+ if (used >= reservoir.length) {
+ T[] r2 = (T[])new Item[used*2];
+ System.arraycopy(reservoir, 0, r2, 0, used);
+ reservoir = r2;
+ }
+ reservoir[used++] = item;
+ }
+
+ /**
+ * Release unused space in the reservoir (provided the amount of unused space is worth reclaiming)
+ */
+
+ private void condense() {
+ if (reservoir != null && reservoir.length - used > 30) {
+ T[] r2 = (T[])new Item[used];
+ System.arraycopy(reservoir, 0, r2, 0, used);
+ reservoir = r2;
+ }
+ // give unwanted stuff to the garbage collector
+ savedXPathContext = null;
+// inputIterator = null;
+// expression = null;
+ }
+
+ /**
+ * Determine whether the contents of the MemoClosure have been fully read
+ * @return true if the contents have been fully read
+ */
+
+ public boolean isFullyRead() {
+ return state==EMPTY || state==ALL_READ;
+ }
+
+ /**
+ * Return a value containing all the items in the sequence returned by this
+ * SequenceIterator
+ *
+ * @return the corresponding value
+ * @throws net.sf.saxon.trans.XPathException if a failure occurs reading the input
+ */
+
+ /*@Nullable*/ public GroundedValue materialize() throws XPathException {
+ if (state == ALL_READ) {
+ return new SequenceExtent<T>(reservoir, 0, used);
+ } else if (state == EMPTY) {
+ return EmptySequence.getInstance();
+ }
+ return new SequenceExtent<T>(iterate());
+ }
+
+ /**
+ * A ProgressiveIterator starts by reading any items already held in the reservoir;
+ * when the reservoir is exhausted, it reads further items from the inputIterator,
+ * copying them into the reservoir as they are read.
+ */
+
+ public final class ProgressiveIterator implements SequenceIterator<T>, LastPositionFinder<T>, GroundedIterator<T> {
+
+ int position = -1; // zero-based position in the reservoir of the
+ // item most recently read
+
+ /**
+ * Create a ProgressiveIterator
+ */
+
+ public ProgressiveIterator() {
+ }
+
+ /*@Nullable*/ public T next() throws XPathException {
+ synchronized (MemoClosure.this) {
+ // synchronized for the case where a multi-threaded xsl:for-each is reading the variable
+ if (position == -2) { // means we've already returned null once, keep doing so if called again.
+ return null;
+ }
+ if (++position < used) {
+ assert reservoir != null;
+ return reservoir[position];
+ } else if (state == ALL_READ) {
+ // someone else has read the input to completion in the meantime
+ position = -2;
+ return null;
+ } else {
+ assert inputIterator != null;
+ T i = inputIterator.next();
+ if (i == null) {
+ state = ALL_READ;
+ condense();
+ position = -2;
+ return null;
+ }
+ position = used;
+ append(i);
+ state = MAYBE_MORE;
+ return i;
+ }
+ }
+ }
+
+ /*@Nullable*/ public T current() {
+ if (position < 0) {
+ return null;
+ }
+ assert reservoir != null;
+ return reservoir[position];
+ }
+
+ public int position() {
+ return position + 1; // return one-based position
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/ public ProgressiveIterator getAnother() {
+ return new ProgressiveIterator();
+ }
+
+ /**
+ * Get the last position (that is, the number of items in the sequence)
+ */
+
+ public int getLength() throws XPathException {
+ if (state == ALL_READ) {
+ return used;
+ } else if (state == EMPTY) {
+ return 0;
+ } else {
+ // save the current position
+ int savePos = position;
+ // fill the reservoir
+ while (true) {
+ Item item = next();
+ if (item == null) {
+ break;
+ }
+ }
+ // reset the current position
+ position = savePos;
+ // return the total number of items
+ return used;
+ }
+ }
+
+ /**
+ * Return a value containing all the items in the sequence returned by this
+ * SequenceIterator
+ *
+ * @return the corresponding value
+ */
+
+ /*@Nullable*/ public GroundedValue materialize() throws XPathException {
+ if (state == ALL_READ) {
+ assert reservoir != null;
+ return new SequenceExtent<Item>(reservoir, 0, used);
+ } else if (state == EMPTY) {
+ return EmptySequence.getInstance();
+ } else {
+ return new SequenceExtent<T>(iterate());
+ //throw new IllegalStateException("Progressive iterator is not grounded until all items are read");
+ }
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED} and {@link #LAST_POSITION_FINDER}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ */
+
+ public int getProperties() {
+ // bug 1740 shows that it is better to report the iterator as grounded even though this
+ // may trigger eager evaluation of the underlying sequence.
+ //if (state == EMPTY || state == ALL_READ) {
+ return GROUNDED | LAST_POSITION_FINDER;
+ //} else {
+ // return 0;
+ //}
+ }
+
+ /**
+ * Get the n'th item in the sequence, zero-based
+ */
+
+ public synchronized T itemAt(int n) throws XPathException {
+ if (n < 0) {
+ return null;
+ }
+ if (reservoir != null && n < used) {
+ return reservoir[n];
+ }
+ if (state == ALL_READ || state == EMPTY) {
+ return null;
+ }
+ if (state == UNREAD) {
+ T item = inputIterator.next();
+ state = MAYBE_MORE;
+ if (item == null) {
+ state = EMPTY;
+ return null;
+ } else {
+ state = MAYBE_MORE;
+ reservoir = (T[])new Item[50];
+ used = 0;
+ append(item);
+ if (n == 0) {
+ return item;
+ }
+ }
+ }
+ // We have read some items from the input sequence but not enough. Read as many more as are needed.
+ int diff = n - used + 1;
+ while (diff-- > 0) {
+ T i = inputIterator.next();
+ if (i == null) {
+ state = ALL_READ;
+ condense();
+ return null;
+ }
+ append(i);
+ state = MAYBE_MORE;
+ }
+ //noinspection ConstantConditions
+ return reservoir[n];
+ }
+
+ }
+
+}
+
diff --git a/sf/saxon/value/NotationValue.java b/sf/saxon/value/NotationValue.java
new file mode 100644
index 0000000..2833a22
--- /dev/null
+++ b/sf/saxon/value/NotationValue.java
@@ -0,0 +1,164 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+
+
+/**
+ * An xs:NOTATION value.
+ */
+
+public final class NotationValue extends QualifiedNameValue {
+
+ /**
+ * Constructor
+ * @param prefix The prefix part of the QName (not used in comparisons). Use null or "" to represent the
+ * default prefix.
+ * @param uri The namespace part of the QName. Use null or "" to represent the null namespace.
+ * @param localName The local part of the QName
+ * @param checker Used for checking names against XML 1.0 or XML 1.1 syntax rules
+ */
+
+ public NotationValue(String prefix, String uri, String localName, /*@Nullable*/ NameChecker checker) throws XPathException {
+ if (checker != null && !checker.isValidNCName(localName)) {
+ XPathException err = new XPathException("Malformed local name in NOTATION: '" + localName + '\'');
+ err.setErrorCode("FORG0001");
+ throw err;
+ }
+ prefix = (prefix==null ? "" : prefix);
+ uri = (uri==null ? "" : uri);
+ if (checker != null && uri.length() == 0 && prefix.length() != 0) {
+ XPathException err = new XPathException("NOTATION has null namespace but non-empty prefix");
+ err.setErrorCode("FOCA0002");
+ throw err;
+ }
+ qName = new StructuredQName(prefix, uri, localName);
+ typeLabel = BuiltInAtomicType.NOTATION;
+ }
+
+ /**
+ * Constructor for a value that is known to be valid
+ * @param prefix The prefix part of the QName (not used in comparisons). Use null or "" to represent the
+ * default prefix.
+ * @param uri The namespace part of the QName. Use null or "" to represent the null namespace.
+ * @param localName The local part of the QName
+ */
+
+ public NotationValue(String prefix, String uri, String localName) {
+ qName = new StructuredQName(prefix, uri, localName);
+ typeLabel = BuiltInAtomicType.NOTATION;
+ }
+
+ /**
+ * Constructor for a value that is known to be valid
+ * @param prefix The prefix part of the QName (not used in comparisons). Use null or "" to represent the
+ * default prefix.
+ * @param uri The namespace part of the QName. Use null or "" to represent the null namespace.
+ * @param localName The local part of the QName
+ * @param typeLabel A type derived from xs:NOTATION to be used for the new value
+ */
+
+ public NotationValue(String prefix, String uri, String localName, AtomicType typeLabel) {
+ qName = new StructuredQName(prefix, uri, localName);
+ this.typeLabel = typeLabel;
+ }
+
+ /**
+ * Constructor
+ * @param qName the name as a StructuredQName
+ * @param typeLabel idenfies a subtype of xs:QName
+ */
+
+ public NotationValue(/*@Nullable*/ StructuredQName qName, /*@Nullable*/ AtomicType typeLabel) {
+ if (qName == null) {
+ throw new NullPointerException("qName");
+ }
+ if (typeLabel == null) {
+ throw new NullPointerException("typeLabel");
+ }
+ this.qName = qName;
+ this.typeLabel = typeLabel;
+ }
+
+
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ NotationValue v = new NotationValue(getPrefix(), getNamespaceURI(), getLocalName());
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ /*@NotNull*/ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.NOTATION;
+ }
+
+ /**
+ * Determine if two Notation values are equal. This comparison ignores the prefix part
+ * of the value.
+ * @throws ClassCastException if they are not comparable
+ * @throws IllegalStateException if the two QNames are in different name pools
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ return other instanceof NotationValue && qName.equals(((NotationValue)other).qName);
+ }
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return new NotationComparable();
+ }
+
+ private class NotationComparable implements Comparable {
+
+ /*@NotNull*/ public NotationValue getNotationValue() {
+ return NotationValue.this;
+ }
+
+ public int compareTo(/*@NotNull*/ Object o) {
+ return equals(o) ? 0 : SequenceTool.INDETERMINATE_ORDERING;
+ }
+
+ public boolean equals(/*@NotNull*/ Object o) {
+ return (o instanceof NotationComparable && qName.equals(((NotationComparable)o).getNotationValue().qName));
+ }
+
+ public int hashCode() {
+ return qName.hashCode();
+ }
+ }
+
+ /**
+ * The toString() method returns the name in the form QName("uri", "local")
+ * @return the name in Clark notation: {uri}local
+ */
+
+ /*@NotNull*/ public String toString() {
+ return "NOTATION(" + getClarkName() + ')';
+ }
+
+}
+
diff --git a/sf/saxon/value/NumericValue.java b/sf/saxon/value/NumericValue.java
new file mode 100644
index 0000000..64a4191
--- /dev/null
+++ b/sf/saxon/value/NumericValue.java
@@ -0,0 +1,255 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.math.BigDecimal;
+
+/**
+ * NumericValue is an abstract superclass for IntegerValue, DecimalValue,
+ * FloatValue, and DoubleValue
+ */
+
+public abstract class NumericValue extends AtomicValue implements Comparable {
+
+ /**
+ * Get a numeric value by parsing a string; the type of numeric value depends
+ * on the lexical form of the string, following the rules for XPath numeric
+ * literals.
+ * @param in the input string
+ * @return a NumericValue representing the value of the string. Returns Double.NaN if the
+ * value cannot be parsed as a string.
+ */
+
+ /*@NotNull*/ public static NumericValue parseNumber(/*@NotNull*/ String in) {
+ if (in.indexOf('e') >= 0 || in.indexOf('E') >= 0) {
+ try {
+ return new DoubleValue(Double.parseDouble(in));
+ } catch (NumberFormatException e) {
+ return DoubleValue.NaN;
+ }
+ } else if (in.indexOf('.') >= 0) {
+ ConversionResult v = DecimalValue.makeDecimalValue(in, true);
+ if (v instanceof ValidationFailure) {
+ return DoubleValue.NaN;
+ } else {
+ return (NumericValue)v;
+ }
+ } else {
+ ConversionResult v = Int64Value.stringToInteger(in);
+ if (v instanceof ValidationFailure) {
+ return DoubleValue.NaN;
+ } else {
+ return (NumericValue)v;
+ }
+ }
+ }
+ /**
+ * Get the numeric value as a double
+ *
+ * @return A double representing this numeric value; NaN if it cannot be
+ * converted
+ */
+ public abstract double getDoubleValue();
+
+ /**
+ * Get the numeric value converted to a float
+ * @return a float representing this numeric value; NaN if it cannot be converted
+ */
+
+ public abstract float getFloatValue();
+
+ /**
+ * Get the numeric value converted to a decimal
+ * @return a decimal representing this numeric value;
+ * @throws XPathException if the value cannot be converted, for example if it is NaN or infinite
+ */
+
+ public abstract BigDecimal getDecimalValue() throws XPathException;
+
+
+
+
+ /**
+ * Test whether a value is an integer (an instance of a subtype of xs:integer)
+ * @param value the value being tested
+ * @return true if the value is an instance of xs:integer or a type derived therefrom
+ */
+
+ public static boolean isInteger(AtomicValue value) {
+ return (value instanceof IntegerValue);
+ }
+
+ /**
+ * Return the numeric value as a Java long.
+ *
+ * @exception net.sf.saxon.trans.XPathException if the value cannot be converted
+ * @return the numeric value as a Java long. This performs truncation
+ * towards zero.
+ */
+ public abstract long longValue() throws XPathException;
+
+ /**
+ * Change the sign of the number
+ *
+ * @return a value, of the same type as the original, with its sign
+ * inverted
+ */
+
+ public abstract NumericValue negate();
+
+ /**
+ * Implement the XPath floor() function
+ *
+ * @return a value, of the same type as that supplied, rounded towards
+ * minus infinity
+ */
+
+ /*@NotNull*/ public abstract NumericValue floor();
+
+ /**
+ * Implement the XPath ceiling() function
+ *
+ * @return a value, of the same type as that supplied, rounded towards
+ * plus infinity
+ */
+
+ /*@NotNull*/ public abstract NumericValue ceiling();
+
+ /**
+ * Implement the XPath round() function
+ *
+ * @return a value, of the same type as that supplied, rounded towards the
+ * nearest whole number (0.5 rounded up)
+ */
+
+ public abstract NumericValue round(int scale);
+
+ /**
+ * Implement the XPath 2.0 round-half-to-even() function
+ *
+ * @param scale the decimal position for rounding: e.g. 2 rounds to a
+ * multiple of 0.01, while -2 rounds to a multiple of 100
+ * @return a value, of the same type as the original, rounded towards the
+ * nearest multiple of 10**(-scale), with rounding towards the nearest
+ * even number if two values are equally near
+ */
+
+ public abstract NumericValue roundHalfToEven(int scale);
+
+ /**
+ * Determine whether the value is negative, zero, or positive
+ * @return -1 if negative, 0 if zero (including negative zero) or NaN, +1 if positive
+ */
+
+ public abstract int signum();
+
+ /**
+ * Determine whether the value is a whole number, that is, whether it compares
+ * equal to some integer
+ *
+ * @return true if the value is a whole number
+ */
+
+ public abstract boolean isWholeNumber();
+
+ /**
+ * Get the absolute value as defined by the XPath abs() function
+ * @return the absolute value
+ * @since 9.2
+ */
+
+ public abstract NumericValue abs();
+
+ /**
+ * Get a Comparable value that implements the XPath ordering comparison semantics for this value.
+ * Returns null if the value is not comparable according to XPath rules. The implementation
+ * for all kinds of NumericValue returns the value itself.
+ * @param ordered
+ * @param collator
+ * @param context
+ */
+
+ /*@NotNull*/ public final Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context) {
+ return this;
+ }
+
+ /**
+ * Compare the value to another numeric value
+ *
+ * @exception ClassCastException if the other value is not a NumericValue
+ * (the parameter is declared as Object to satisfy the Comparable
+ * interface)
+ * @param other The other numeric value
+ * @return -1 if this one is the lower, 0 if they are numerically equal,
+ * +1 if this one is the higher, or if either value is NaN. Where NaN values are
+ * involved, they should be handled by the caller before invoking this method.
+ */
+
+ // This is the default implementation. Subclasses of number avoid the conversion to double
+ // when comparing with another number of the same type.
+
+ public int compareTo(/*@NotNull*/ Object other) {
+ double a = getDoubleValue();
+ double b = ((NumericValue)other).getDoubleValue();
+ if (a == b) return 0;
+ if (a < b) return -1;
+ return +1;
+ }
+
+ /**
+ * Compare the value to a long
+ * @param other the value to be compared with
+ * @return -1 if this is less, 0 if this is equal, +1 if this is greater or if this is NaN
+ */
+
+ public abstract int compareTo(long other);
+
+ /**
+ * The equals() function compares numeric equality among integers, decimals, floats, doubles, and
+ * their subtypes
+ *
+ * @param other the value to be compared with this one
+ * @return true if the two values are numerically equal
+ */
+
+ public final boolean equals(/*@NotNull*/ Object other) {
+ return compareTo(other) == 0;
+ }
+
+ /**
+ * hashCode() must be the same for two values that are equal. One
+ * way to ensure this is to convert the value to a double, and take the
+ * hashCode of the double. But this is expensive in the common case where
+ * we are comparing integers. So we adopt the rule: for values that are in
+ * the range of a Java Integer, we use the int value as the hashcode. For
+ * values outside that range, we convert to a double and take the hashCode of
+ * the double. This method needs to have a compatible implementation in
+ * each subclass.
+ *
+ * @return the hash code of the numeric value
+ */
+
+ public abstract int hashCode();
+
+ /**
+ * Produce a string representation of the value
+ * @return The result of casting the number to a string
+ */
+
+ public String toString() {
+ return getStringValue();
+ }
+
+}
+
diff --git a/sf/saxon/value/ObjectValue.java b/sf/saxon/value/ObjectValue.java
new file mode 100644
index 0000000..433c425
--- /dev/null
+++ b/sf/saxon/value/ObjectValue.java
@@ -0,0 +1,162 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.om.AbstractItem;
+import net.sf.saxon.type.ExternalObjectType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.TypeHierarchy;
+
+
+/**
+ * An XPath value that encapsulates a Java object. Such a value can only be constructed from
+ * Java code either in a Java extension function, or in the calling application.
+ *
+ * <p>Until Saxon 9.4, ObjectValue was a subclass of AtomicValue, and external Java objects were
+ * considered to be atomic. From 9.5, this has been changed so that an ObjectValue is another
+ * kind of Item: a fourth kind, at the same level as nodes, atomic values, and function items.</p>
+ *
+ * <p>In the same way as ObjectValue is a wrapper around a Java instance object, the type of the
+ * value is a wrapper around the corresponding Java class. These types have subtype-supertype
+ * relationships that correspond to the Java class hierarchy. The top type in this branch of
+ * the class hierarchy, which wraps the Java class "Object", has item() as its superclass.
+ * These types can be referred to in the SequenceType syntax using QNames of the form
+ * jt:full.package.name.ClassName, where the convetional prefix jt maps to the namespace
+ * URI http://saxon.sf.net/java-type.</p>
+ *
+ * <p>An ObjectValue is no longer permitted to wrap a Java null. An empty sequence should be
+ * used in this situation.</p>
+ *
+ * <p>The effective boolean value of a wrapped Java object, like that of a node, is always true.</p>
+ *
+ * <p>Atomizing a wrapped Java object is not allowed (fails with a dynamic error). The string()
+ * function calls the wrapped object's toString() method, unless the wrapped object is null,
+ * in which case it returns the zero-length string.</p>
+*/
+
+public class ObjectValue extends AbstractItem {
+
+ /*@NotNull*/ private Object value;
+
+ /**
+ * Constructor
+ * @param object the object to be encapsulated
+ */
+
+ public ObjectValue(/*@NotNull*/ Object object) {
+ if (object == null) {
+ throw new NullPointerException("External object cannot wrap a Java null");
+ }
+ value = object;
+ }
+
+ /**
+ * Get the value of the item as a string. For nodes, this is the string value of the
+ * node as defined in the XPath 2.0 data model, except that all nodes are treated as being
+ * untyped: it is not an error to get the string value of a node with a complex type.
+ * For atomic values, the method returns the result of casting the atomic value to a string.
+ * <p/>
+ * If the calling code can handle any CharSequence, the method {@link #getStringValueCS} should
+ * be used. If the caller requires a string, this method is preferred.
+ *
+ * @return the string value of the item
+ * @throws UnsupportedOperationException if the item is a function item (an unchecked exception
+ * is used here to avoid introducing exception handling to a large number of paths where it is not
+ * needed)
+ * @see #getStringValueCS
+ * @since 8.4
+ */
+ public String getStringValue() {
+ return value.toString();
+ }
+
+ /**
+ * Get the string value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String. The method satisfies the rule that
+ * <code>X.getStringValueCS().toString()</code> returns a string that is equal to
+ * <code>X.getStringValue()</code>.
+ * <p/>
+ * Note that two CharSequence values of different types should not be compared using equals(), and
+ * for the same reason they should not be used as a key in a hash table.
+ * <p/>
+ * If the calling code can handle any CharSequence, this method should
+ * be used. If the caller requires a string, the {@link #getStringValue} method is preferred.
+ *
+ * @return the string value of the item
+ * @throws UnsupportedOperationException if the item is a function item (an unchecked exception
+ * is used here to avoid introducing exception handling to a large number of paths where it is not
+ * needed)
+ * @see #getStringValue
+ * @since 8.4
+ */
+ public CharSequence getStringValueCS() {
+ return value.toString();
+ }
+
+ /**
+ * Determine the data type of the items in the expression, if possible
+ *
+ * @param th The TypeHierarchy.
+ * @return for the default implementation: AnyItemType (not known)
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(/*@Nullable*/ TypeHierarchy th) {
+ return new ExternalObjectType(value.getClass());
+ }
+
+ /**
+ * Display the type name for use in error messages
+ * @return the type name. This will be in the form "java-type:" followed by the full class name of the
+ * wrapped Java object.
+ */
+
+ /*@NotNull*/ public String displayTypeName() {
+ return "java-type:" + value.getClass().getName();
+ }
+
+ /**
+ * Get the effective boolean value of the value
+ * @return true (always)
+ */
+ public boolean effectiveBooleanValue() {
+ return true;
+ }
+
+ /**
+ * Get the encapsulated object
+ * @return the Java object that this external object wraps
+ */
+
+ public Object getObject() {
+ return value;
+ }
+
+ /**
+ * Determine if two ObjectValues are equal
+ * @return true if the other object is also a wrapped Java object and if the two wrapped objects compare
+ * equal using the Java equals() method.
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ if (other instanceof ObjectValue) {
+ Object o = ((ObjectValue)other).value;
+ return value.equals(o);
+ } else {
+ return false;
+ }
+ }
+
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+
+
+}
+
diff --git a/sf/saxon/value/QNameValue.java b/sf/saxon/value/QNameValue.java
new file mode 100644
index 0000000..fcb0902
--- /dev/null
+++ b/sf/saxon/value/QNameValue.java
@@ -0,0 +1,207 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.om.NameChecker;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+
+/**
+ * A QName value. This implements the so-called "triples proposal", in which the prefix is retained as
+ * part of the value. The prefix is not used in any operation on a QName other than conversion of the
+ * QName to a string.
+ */
+
+public class QNameValue extends QualifiedNameValue {
+
+
+ /**
+ * Constructor for a QName that is known to be valid. No validation takes place.
+ * @param prefix The prefix part of the QName (not used in comparisons). Use "" to represent the
+ * default prefix.
+ * @param uri The namespace part of the QName. Use "" to represent the non-namespace.
+ * @param localName The local part of the QName
+ */
+
+ public QNameValue(String prefix, String uri, String localName) {
+ this(prefix, uri, localName, BuiltInAtomicType.QNAME);
+ }
+
+ /**
+ * Constructor for a QName that is known to be valid, allowing a user-defined subtype of QName
+ * to be specified. No validation takes place.
+ * @param prefix The prefix part of the QName (not used in comparisons). Use "" to represent the
+ * default prefix (but null is also accepted)
+ * @param uri The namespace part of the QName. Use null to represent the non-namespace (but "" is also
+ * accepted).
+ * @param localName The local part of the QName
+ * @param type The type label, xs:QName or a subtype of xs:QName
+ */
+
+ public QNameValue(String prefix, String uri, String localName, /*@Nullable*/ AtomicType type) {
+ qName = new StructuredQName(prefix, uri, localName);
+ if (type == null) {
+ type = BuiltInAtomicType.QNAME;
+ }
+ typeLabel = type;
+ }
+
+ /**
+ * Constructor starting from a NamePool namecode
+ * @param namePool The name pool containing the specified name code
+ * @param nameCode The name code identifying this name in the name pool
+ */
+
+ public QNameValue(/*@NotNull*/ NamePool namePool, int nameCode) {
+ String prefix = namePool.getPrefix(nameCode);
+ String uri = namePool.getURI(nameCode);
+ String localPart = namePool.getLocalName(nameCode);
+ qName = new StructuredQName(prefix, uri, localPart);
+ typeLabel = BuiltInAtomicType.QNAME;
+ }
+
+
+ /**
+ * Constructor. This constructor validates that the local part is a valid NCName.
+ * @param prefix The prefix part of the QName (not used in comparisons). Use "" to represent the
+ * default prefix (but null is also accepted).
+ * Note that the prefix is not checked for lexical correctness, because in most cases
+ * it will already have been matched against in-scope namespaces. Where necessary the caller must
+ * check the prefix.
+ * @param uri The namespace part of the QName. Use null to represent the non-namespace (but "" is also
+ * accepted).
+ * @param localName The local part of the QName
+ * @param type The atomic type, which must be either xs:QName, or a
+ * user-defined type derived from xs:QName by restriction
+ * @param checker NameChecker used to check the name against XML 1.0 or XML 1.1 rules. Supply null
+ * if the name does not need to be checked (the caller asserts that it is known to be valid)
+ * @throws XPathException if the local part of the name is malformed or if the name has a null
+ * namespace with a non-empty prefix
+ */
+
+ public QNameValue(String prefix, String uri, String localName, AtomicType type, /*@Nullable*/ NameChecker checker) throws XPathException {
+ if (checker != null && !checker.isValidNCName(localName)) {
+ XPathException err = new XPathException("Malformed local name in QName: '" + localName + '\'');
+ err.setErrorCode("FORG0001");
+ throw err;
+ }
+ prefix = (prefix==null ? "" : prefix);
+ uri = ("".equals(uri) ? null : uri);
+ if (checker != null && uri == null && prefix.length() != 0) {
+ XPathException err = new XPathException("QName has null namespace but non-empty prefix");
+ err.setErrorCode("FOCA0002");
+ throw err;
+ }
+ qName = new StructuredQName(prefix, uri, localName);
+ typeLabel = type;
+ }
+
+ /**
+ * Constructor
+ * @param qName the name as a StructuredQName
+ * @param typeLabel idenfies a subtype of xs:QName
+ */
+
+ public QNameValue(/*@Nullable*/ StructuredQName qName, /*@Nullable*/ AtomicType typeLabel) {
+ if (qName == null) {
+ throw new NullPointerException("qName");
+ }
+ if (typeLabel == null) {
+ throw new NullPointerException("typeLabel");
+ }
+ this.qName = qName;
+ this.typeLabel = typeLabel;
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ return new QNameValue(qName, typeLabel);
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ /*@NotNull*/ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.QNAME;
+ }
+
+ /**
+ * Get a component. Returns a zero-length string if the namespace-uri component is
+ * requested and is not present.
+ * @param part either Component.LOCALNAME or Component.NAMESPACE indicating which
+ * component of the value is required
+ * @return either the local name or the namespace URI, in each case as a StringValue
+ */
+
+ /*@Nullable*/ public AtomicValue getComponent(int part) {
+ if (part == Component.LOCALNAME) {
+ return new StringValue(getLocalName(), BuiltInAtomicType.NCNAME);
+ } else if (part == Component.NAMESPACE) {
+ return new AnyURIValue(getNamespaceURI());
+ } else if (part == Component.PREFIX) {
+ String prefix = getPrefix();
+ if (prefix.length() == 0) {
+ return null;
+ } else {
+ return new StringValue(prefix, BuiltInAtomicType.NCNAME);
+ }
+ } else {
+ throw new UnsupportedOperationException("Component of QName must be URI, Local Name, or Prefix");
+ }
+ }
+
+ /**
+ * Determine if two QName values are equal. This comparison ignores the prefix part
+ * of the value.
+ * @throws ClassCastException if they are not comparable
+ */
+
+ public boolean equals(/*@NotNull*/ Object other) {
+ return other instanceof QNameValue && qName.equals(((QNameValue)other).qName);
+ }
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return new QNameComparable();
+ }
+
+ private class QNameComparable implements Comparable {
+
+ /*@NotNull*/ public QNameValue getQNameValue() {
+ return QNameValue.this;
+ }
+
+ public int compareTo(/*@NotNull*/ Object o) {
+ return equals(o) ? 0 : SequenceTool.INDETERMINATE_ORDERING;
+ }
+
+ public boolean equals(/*@NotNull*/ Object o) {
+ return (o instanceof QNameComparable && qName.equals(((QNameComparable)o).getQNameValue().qName));
+ }
+
+ public int hashCode() {
+ return qName.hashCode();
+ }
+ }
+
+}
+
diff --git a/sf/saxon/value/QualifiedNameValue.java b/sf/saxon/value/QualifiedNameValue.java
new file mode 100644
index 0000000..9d06ac5
--- /dev/null
+++ b/sf/saxon/value/QualifiedNameValue.java
@@ -0,0 +1,195 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.ConversionRules;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ValidationFailure;
+
+import javax.xml.namespace.QName;
+
+
+/**
+ * A qualified name: this is an abstract superclass for QNameValue and NotationValue, representing the
+ * XPath primitive types xs:QName and xs:NOTATION respectively
+ */
+
+public abstract class QualifiedNameValue extends AtomicValue {
+
+ /*@NotNull*/ protected StructuredQName qName;
+
+ /**
+ * Factory method to construct either a QName or a NOTATION value, or a subtype of either of these.
+ * Note that it is the caller's responsibility to resolve the QName prefix into a URI
+ * @param prefix the prefix part of the value. Use "" or null for the empty prefix.
+ * @param uri the namespace URI part of the value. Use "" or null for the non-namespace
+ * @param local the local part of the value
+ * @param targetType the target type, which must be xs:QName or a subtype of xs:NOTATION or xs:QName
+ * @param lexicalForm the original lexical form of the value. This is needed in case there are facets
+ * such as pattern that check the lexical form
+ * @param rules the conversion rules to be applied
+ * @return the converted value
+ * @throws XPathException if the value cannot be converted.
+ */
+
+ /*@Nullable*/ public static AtomicValue makeQName(String prefix, String uri, String local,
+ /*@NotNull*/ AtomicType targetType, CharSequence lexicalForm, ConversionRules rules)
+ throws XPathException {
+
+ if (targetType.getFingerprint() == StandardNames.XS_QNAME) {
+ return new QNameValue(prefix, uri, local, BuiltInAtomicType.QNAME, null);
+ } else {
+ QualifiedNameValue qnv;
+
+ if (targetType.getPrimitiveType() == StandardNames.XS_QNAME) {
+ qnv = new QNameValue(prefix, uri, local, targetType, null);
+ } else {
+ qnv = new NotationValue(prefix, uri, local, (AtomicType)null);
+ }
+ ValidationFailure vf = targetType.validate(qnv, lexicalForm, rules);
+ if (vf != null) {
+ throw vf.makeException();
+ }
+ qnv.setTypeLabel(targetType);
+ return qnv;
+ }
+ }
+
+
+ /**
+ * Get the string value as a String. Returns the QName as a lexical QName, retaining the original
+ * prefix if available.
+ */
+
+ public final String getPrimitiveStringValue() {
+ return qName.getDisplayName();
+ }
+
+ /**
+ * Convert to a StructuredQName
+ * @return the name as a StructuredQName
+ */
+
+ /*@Nullable*/ public StructuredQName toStructuredQName() {
+ return qName;
+ }
+
+ /**
+ * Get the QName in Clark notation, that is "{uri}local" if in a namespace, or "local" otherwise
+ * @return the name in Clark notation
+ */
+
+ public final String getClarkName() {
+ return qName.getClarkName();
+ }
+
+ /**
+ * Get the local part
+ * @return the local part of the name (the part after the colon)
+ */
+
+ /*@NotNull*/
+ public final String getLocalName() {
+ return qName.getLocalPart();
+ }
+
+ /**
+ * Get the namespace part. Returns the empty string for a name in no namespace.
+ * @return the namespace URI component of the name, or "" for a no-namespace name
+ */
+
+ /*@NotNull*/
+ public final String getNamespaceURI() {
+ return qName.getURI();
+ }
+
+ /**
+ * Get the prefix. Returns the empty string if the name is unprefixed.
+ * @return the prefix, or "" to indicate no prefix
+ */
+
+ /*@NotNull*/
+ public final String getPrefix() {
+ return qName.getPrefix();
+ }
+
+ /**
+ * Allocate a nameCode for this QName in the NamePool
+ * @param pool the NamePool to be used
+ * @return the allocated nameCode
+ */
+
+ public int allocateNameCode(/*@NotNull*/ NamePool pool) {
+ return pool.allocate(getPrefix(), getNamespaceURI(), getLocalName());
+ }
+
+
+ /**
+ * Get an object value that implements the XPath equality and ordering comparison semantics for this value.
+ * If the ordered parameter is set to true, the result will be a Comparable and will support a compareTo()
+ * method with the semantics of the XPath lt/gt operator, provided that the other operand is also obtained
+ * using the getXPathComparable() method. In all cases the result will support equals() and hashCode() methods
+ * that support the semantics of the XPath eq operator, again provided that the other operand is also obtained
+ * using the getXPathComparable() method. A context argument is supplied for use in cases where the comparison
+ * semantics are context-sensitive, for example where they depend on the implicit timezone or the default
+ * collation.
+ *
+ * @param ordered true if an ordered comparison is required. In this case the result is null if the
+ * type is unordered; in other cases the returned value will be a Comparable.
+ * @param collator the collation to be used for the comparison
+ * @param context the XPath dynamic evaluation context, used in cases where the comparison is context
+* sensitive @return an Object whose equals() and hashCode() methods implement the XPath comparison semantics
+ */
+
+ /*@Nullable*/ public Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context) {
+ return (ordered ? null : this);
+ }
+
+ public int hashCode() {
+ return qName.hashCode();
+ }
+
+ public boolean isIdentical(/*@NotNull*/ AtomicValue v) {
+ return super.isIdentical(v) && qName.getPrefix().equals(((QualifiedNameValue)v).getPrefix());
+ }
+
+ /**
+ * The toString() method returns the name in the form QName("uri", "local")
+ * @return the name in in the form QName("uri", "local")
+ */
+
+ /*@NotNull*/ public String toString() {
+ return "QName(\"" + getNamespaceURI() + "\", \"" + getLocalName() + "\")";
+ }
+
+ /**
+ * Construct a javax.xml.namespace.QName from this QualifiedNameValue
+ * @return an equivalent instance of the JAXP QName class
+ */
+
+ public QName toJaxpQName() {
+ return qName.toJaxpQName();
+ // Note JDK 1.5 dependency
+ }
+
+ /**
+ * Get the equivalent StructuredQName
+ * @return the equivalent StructuredQName
+ */
+
+ /*@NotNull*/ public StructuredQName getStructuredQName() {
+ return qName;
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/value/SequenceExtent.java b/sf/saxon/value/SequenceExtent.java
new file mode 100644
index 0000000..0ab676d
--- /dev/null
+++ b/sf/saxon/value/SequenceExtent.java
@@ -0,0 +1,459 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.LastPositionFinder;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.ArrayIterator;
+import net.sf.saxon.tree.iter.GroundedIterator;
+import net.sf.saxon.tree.iter.ReverseArrayIterator;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * A sequence value implemented extensionally. That is, this class represents a sequence
+ * by allocating memory to each item in the sequence.
+ */
+
+public class SequenceExtent<T extends Item> implements GroundedValue {
+ private T[] value;
+ private int start = 0; // zero-based offset of the start
+ private int end; // the 0-based index of the first item that is NOT included
+ // If start=0 this is the length of the sequence
+ /*@Nullable*/ private ItemType itemType = null; // memoized
+
+// private static int instances = 0;
+
+ /**
+ * Construct an sequence from an array of items. Note, the array of items is used as is,
+ * which means the caller must not subsequently change its contents.
+ *
+ * @param items the array of items to be included in the sequence
+ */
+
+ public SequenceExtent(/*@NotNull*/ T[] items) {
+ value = items;
+ end = items.length;
+ }
+
+ /**
+ * Construct a SequenceExtent from part of an array of items
+ * @param value The array
+ * @param start zero-based offset of the first item in the array
+ * that is to be included in the new SequenceExtent
+ * @param length The number of items in the new SequenceExtent
+ */
+
+ public SequenceExtent(T[] value, int start, int length) {
+ this.value = value;
+ this.start = start;
+ end = this.start + length;
+ }
+
+
+ /**
+ * Construct a SequenceExtent as a view of another SequenceExtent
+ * @param ext The existing SequenceExtent
+ * @param start zero-based offset of the first item in the existing SequenceExtent
+ * that is to be included in the new SequenceExtent
+ * @param length The number of items in the new SequenceExtent
+ */
+
+ public SequenceExtent(/*@NotNull*/ SequenceExtent<T> ext, int start, int length) {
+ value = ext.value;
+ this.start = ext.start + start;
+ end = this.start + length;
+ }
+
+ /**
+ * Construct a SequenceExtent from a List. The members of the list must all
+ * be Items
+ *
+ * @param list the list of items to be included in the sequence
+ */
+
+ public SequenceExtent(/*@NotNull*/ List<? extends T> list) {
+ @SuppressWarnings({"unchecked"})
+ T[] array = (T[])new Item[list.size()];
+ value = list.toArray(array);
+ end = value.length;
+ }
+
+ /**
+ * Construct a sequence containing all the items in a SequenceIterator.
+ *
+ * @exception net.sf.saxon.trans.XPathException if reading the items using the
+ * SequenceIterator raises an error
+ * @param iter The supplied sequence of items. This must be positioned at
+ * the start, so that hasNext() returns true if there are any nodes in
+ * the node-set, and next() returns the first node.
+ */
+
+ public SequenceExtent(/*@NotNull*/ SequenceIterator<T> iter) throws XPathException {
+ if ((iter.getProperties() & SequenceIterator.LAST_POSITION_FINDER) == 0) {
+ List<T> list = new ArrayList<T>(20);
+ while (true) {
+ T it = iter.next();
+ if (it == null) {
+ break;
+ }
+ list.add(it);
+ }
+ @SuppressWarnings({"unchecked"})
+ T[] array = (T[])new Item[list.size()];
+ try {
+ value = list.toArray(array);
+ } catch (ArrayStoreException e) {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ end = value.length;
+ } else {
+ end = ((LastPositionFinder)iter).getLength();
+ @SuppressWarnings({"unchecked"})
+ T[] array = (T[])new Item[end];
+ value = array;
+ int i = 0;
+ while (true) {
+ T it = iter.next();
+ if (it == null) {
+ break;
+ }
+ value[i++] = it;
+ }
+ }
+ }
+
+ /**
+ * Factory method to make a Value holding the contents of any SequenceIterator
+ * @param iter a Sequence iterator that will be consumed to deliver the items in the sequence
+ * @return a ValueRepresentation holding the items delivered by the SequenceIterator. If the
+ * sequence is empty the result will be an instance of {@link EmptySequence}. If it is of length
+ * one, the result will be an {@link Item}. In all other cases, it will be an instance of
+ * {@link SequenceExtent}.
+ * @throws net.sf.saxon.trans.XPathException if an error occurs processing the values from
+ * the iterator.
+ */
+
+ /*@NotNull*/
+ public static <T extends Item> GroundedValue makeSequenceExtent(/*@NotNull*/ SequenceIterator<T> iter) throws XPathException {
+ if ((iter.getProperties() & SequenceIterator.GROUNDED) != 0) {
+ return ((GroundedIterator<T>)iter).materialize();
+ }
+ SequenceExtent<T> extent = new SequenceExtent<T>(iter);
+ return extent.reduce();
+ }
+
+ /**
+ * Factory method to make a Value holding the contents of any List of items
+ * @param input a List containing the items in the sequence
+ * @return a ValueRepresentation holding the items in the list. If the
+ * sequence is empty the result will be an instance of {@link EmptySequence}. If it is of length
+ * one, the result will be an {@link Item}. In all other cases, it will be an instance of
+ * {@link SequenceExtent}.
+ */
+
+ public static <T extends Item> Sequence makeSequenceExtent(/*@NotNull*/ List<T> input) {
+ int len = input.size();
+ if (len==0) {
+ return EmptySequence.getInstance();
+ } else if (len==1) {
+ return input.get(0);
+ } else if (allAtomicLiterals(input)) {
+ AtomicValue[] a = new AtomicValue[input.size()];
+ input.toArray(a);
+ return new AtomicArray(a);
+ } else {
+ return new SequenceExtent<T>(input);
+ }
+ }
+
+ private static boolean allAtomicLiterals(List<? extends Item> input) {
+ for (Item item : input) {
+ if (!(item instanceof AtomicValue)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public String getStringValue() throws XPathException {
+ return SequenceTool.getStringValue(this);
+ }
+
+ public CharSequence getStringValueCS() throws XPathException {
+ return SequenceTool.getStringValue(this);
+ }
+
+ /**
+ * Get the first item in the sequence.
+ *
+ * @return the first item in the sequence if there is one, or null if the sequence
+ * is empty
+ * @throws net.sf.saxon.trans.XPathException
+ * in the situation where the sequence is evaluated lazily, and
+ * evaluation of the first item causes a dynamic error.
+ */
+ public Item head() throws XPathException {
+ return itemAt(0);
+ }
+
+ /**
+ * Simplify this SequenceExtent
+ * @return a Value holding the items delivered by the SequenceIterator. If the
+ * sequence is empty the result will be an instance of {@link EmptySequence}. If it is of length
+ * one, the result will be an {@link AtomicValue} or a {@link SingletonItem}.
+ * In all other cases, the {@link SequenceExtent} will be returned unchanged.
+ */
+
+// public Value<T> simplify() {
+// int n = getLength();
+// if (n == 0) {
+// return EmptySequence.getInstance();
+// } else if (n == 1) {
+// return SequenceTool.asValue(itemAt(0));
+// } else {
+// return this;
+// }
+// }
+
+ /**
+ * Reduce a value to its simplest form. If the value is a closure or some other form of deferred value
+ * such as a FunctionCallPackage, then it is reduced to a SequenceExtent. If it is a SequenceExtent containing
+ * a single item, then it is reduced to that item. One consequence that is exploited by class FilterExpression
+ * is that if the value is a singleton numeric value, then the result will be an instance of NumericValue
+ */
+
+// public Value<T> reduce() {
+// return simplify();
+// }
+
+ /**
+ * Get the number of items in the sequence
+ *
+ * @return the number of items in the sequence
+ */
+
+ public int getLength() {
+ return end - start;
+ }
+
+ /**
+ * Determine the cardinality
+ *
+ * @return the cardinality of the sequence, using the constants defined in
+ * net.sf.saxon.value.Cardinality
+ * @see net.sf.saxon.value.Cardinality
+ */
+
+ public int getCardinality() {
+ switch (end - start) {
+ case 0:
+ return StaticProperty.EMPTY;
+ case 1:
+ return StaticProperty.EXACTLY_ONE;
+ default:
+ return StaticProperty.ALLOWS_ONE_OR_MORE;
+ }
+ }
+
+ /**
+ * Get the (lowest common) item type
+ *
+ * @return integer identifying an item type to which all the items in this
+ * sequence conform
+ * @param th the type hierarchy cache. If null, the returned value may be less precise
+ */
+
+ /*@NotNull*/
+ public ItemType getItemType(/*@Nullable*/ TypeHierarchy th) {
+ ItemType it = itemType;
+ if (it != null) {
+ // only calculate it the first time
+ return it;
+ } else if (end == start) {
+ it = AnyItemType.getInstance();
+ } else if (th == null) {
+ for (int i=start; i<end; i++) {
+ if (value[i] instanceof NodeInfo) {
+ return getItemType(((NodeInfo)value[i]).getConfiguration().getTypeHierarchy());
+ }
+ }
+ it = Type.getItemType(value[start], null);
+ for (int i=start+1; i<end; i++) {
+ if (it == AnyItemType.getInstance()) {
+ // make a quick exit
+ return it;
+ }
+ it = Type.getCommonSuperType(it, Type.getItemType(value[i], null), th);
+ }
+ } else {
+ it = Type.getItemType(value[start], th);
+ for (int i=start+1; i<end; i++) {
+ if (it == AnyItemType.getInstance()) {
+ // make a quick exit
+ return it;
+ }
+ it = Type.getCommonSuperType(it, Type.getItemType(value[i], th), th);
+ }
+ }
+ return (itemType = it);
+ }
+
+ /**
+ * Get the n'th item in the sequence (starting with 0 as the first item)
+ *
+ * @param n the position of the required item
+ * @return the n'th item in the sequence
+ */
+
+ /*@Nullable*/ public T itemAt(int n) {
+ if (n<0 || n>=getLength()) {
+ return null;
+ } else {
+ return value[start+n];
+ }
+ }
+
+ /**
+ * Swap two items (needed to support sorting)
+ *
+ * @param a the position of the first item to be swapped
+ * @param b the position of the second item to be swapped
+ */
+
+ public void swap(int a, int b) {
+ T temp = value[start+a];
+ value[start+a] = value[start+b];
+ value[start+b] = temp;
+ }
+
+ /**
+ * Return an iterator over this sequence.
+ *
+ * @return the required SequenceIterator, positioned at the start of the
+ * sequence
+ */
+
+ /*@NotNull*/ public ArrayIterator<T> iterate() {
+ return new ArrayIterator<T>(value, start, end);
+ }
+
+ /**
+ * Return an enumeration of this sequence in reverse order (used for reverse axes)
+ *
+ * @return an AxisIterator that processes the items in reverse order
+ */
+
+ /*@NotNull*/ public UnfailingIterator<T> reverseIterate() {
+ return new ReverseArrayIterator<T>(value, start, end);
+ }
+
+ /**
+ * Get the effective boolean value
+ */
+
+ public boolean effectiveBooleanValue() throws XPathException {
+ int len = getLength();
+ if (len == 0) {
+ return false;
+ } else if (value[start] instanceof NodeInfo) {
+ return true;
+ } else if (len > 1) {
+ // this is a type error - reuse the error messages
+ return ExpressionTool.effectiveBooleanValue(iterate());
+ } else {
+ return ((AtomicValue)value[start]).effectiveBooleanValue();
+ }
+ }
+
+
+ /**
+ * Get a subsequence of the value
+ *
+ *
+ * @param start the index of the first item to be included in the result, counting from zero.
+ * A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
+ * sequence is returned
+ * @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
+ * get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
+ * is returned. If the value goes off the end of the sequence, the result returns items up to the end
+ * of the sequence
+ * @return the required subsequence. If min is
+ */
+
+ /*@NotNull*/ public GroundedValue subsequence(int start, int length) {
+ int newStart;
+ if (start < 0) {
+ start = 0;
+ } else if (start >= end) {
+ return EmptySequence.getInstance();
+ }
+ newStart = this.start + start;
+ int newEnd;
+ if (length == Integer.MAX_VALUE) {
+ newEnd = end;
+ } else if (length < 0) {
+ return EmptySequence.getInstance();
+ } else {
+ newEnd = newStart + length;
+ if (newEnd > end) {
+ newEnd = end;
+ }
+ }
+ return new SequenceExtent<T>(value, newStart, newEnd - newStart);
+ }
+
+ /*@NotNull*/ public String toString() {
+ FastStringBuffer fsb = new FastStringBuffer(FastStringBuffer.SMALL);
+ fsb.append('(');
+ for (int i=start; i<end; i++) {
+ fsb.append(value[i].toString());
+ if (i != end-1) {
+ fsb.append(", ");
+ }
+ }
+ fsb.append(')');
+ return fsb.toString();
+ }
+
+ /**
+ * Reduce the sequence to its simplest form. If the value is an empty sequence, the result will be
+ * EmptySequence.getInstance(). If the value is a single atomic value, the result will be an instance
+ * of AtomicValue. If the value is a single item of any other kind, the result will be an instance
+ * of SingletonItem. Otherwise, the result will typically be unchanged.
+ *
+ * @return the simplified sequence
+ */
+ public GroundedValue reduce() {
+ int len = getLength();
+ if (len == 0) {
+ return EmptySequence.getInstance();
+ } else if (len == 1) {
+ Item item = itemAt(0);
+ if (item instanceof GroundedValue) {
+ return ((GroundedValue)item);
+ } else {
+ return new SingletonItem(item);
+ }
+ } else {
+ return this;
+ }
+ }
+}
+
diff --git a/sf/saxon/value/SequenceType.java b/sf/saxon/value/SequenceType.java
new file mode 100644
index 0000000..ed4338e
--- /dev/null
+++ b/sf/saxon/value/SequenceType.java
@@ -0,0 +1,482 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.pattern.AnyNodeTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AnyItemType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ErrorType;
+import net.sf.saxon.type.ItemType;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * SequenceType: a sequence type consists of a primary type, which indicates the type of item,
+ * and a cardinality, which indicates the number of occurrences permitted. Where the primary type
+ * is element or attribute, there may also be a content type, indicating the required type
+ * annotation on the element or attribute content.
+ */
+
+public final class SequenceType implements Serializable {
+
+ private ItemType primaryType; // the primary type of the item, e.g. "element", "comment", or "integer"
+ private int cardinality; // the required cardinality
+
+ private static Map pool = Collections.synchronizedMap(new HashMap(50));
+
+ /**
+ * A type that allows any sequence of items
+ */
+
+ public static final SequenceType ANY_SEQUENCE =
+ makeSequenceType(AnyItemType.getInstance(), StaticProperty.ALLOWS_ZERO_OR_MORE);
+
+ /**
+ * A type that allows exactly one item, of any kind
+ */
+
+ public static final SequenceType SINGLE_ITEM =
+ makeSequenceType(AnyItemType.getInstance(), StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows zero or one items, of any kind
+ */
+
+ public static final SequenceType OPTIONAL_ITEM =
+ makeSequenceType(AnyItemType.getInstance(), StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows exactly one atomic value
+ */
+
+ public static final SequenceType SINGLE_ATOMIC =
+ makeSequenceType(BuiltInAtomicType.ANY_ATOMIC,
+ StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows zero or one atomic values
+ */
+
+ public static final SequenceType OPTIONAL_ATOMIC =
+ makeSequenceType(BuiltInAtomicType.ANY_ATOMIC,
+ StaticProperty.ALLOWS_ZERO_OR_ONE);
+ /**
+ * A type that allows zero or more atomic values
+ */
+
+ public static final SequenceType ATOMIC_SEQUENCE =
+ makeSequenceType(BuiltInAtomicType.ANY_ATOMIC,
+ StaticProperty.ALLOWS_ZERO_OR_MORE);
+
+ /**
+ * A type that allows a single string
+ */
+
+ public static final SequenceType SINGLE_STRING =
+ makeSequenceType(BuiltInAtomicType.STRING, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single untyped atomic
+ */
+
+ public static final SequenceType SINGLE_UNTYPED_ATOMIC =
+ makeSequenceType(BuiltInAtomicType.UNTYPED_ATOMIC, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single optional string
+ */
+
+ public static final SequenceType OPTIONAL_STRING =
+ makeSequenceType(BuiltInAtomicType.STRING, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single boolean
+ */
+
+ public static final SequenceType SINGLE_BOOLEAN =
+ makeSequenceType(BuiltInAtomicType.BOOLEAN, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single optional integer
+ */
+
+ public static final SequenceType OPTIONAL_BOOLEAN =
+ makeSequenceType(BuiltInAtomicType.BOOLEAN, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single integer
+ */
+
+ public static final SequenceType SINGLE_INTEGER =
+ makeSequenceType(BuiltInAtomicType.INTEGER, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single optional integer
+ */
+
+ public static final SequenceType OPTIONAL_INTEGER =
+ makeSequenceType(BuiltInAtomicType.INTEGER, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single long
+ */
+
+ public static final SequenceType SINGLE_LONG =
+ makeSequenceType(BuiltInAtomicType.LONG, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single URI
+ */
+
+ public static final SequenceType SINGLE_ANY_URI =
+ makeSequenceType(BuiltInAtomicType.ANY_URI, StaticProperty.EXACTLY_ONE);
+
+
+ /**
+ * A type that allows a single optional long
+ */
+
+ public static final SequenceType OPTIONAL_LONG =
+ makeSequenceType(BuiltInAtomicType.LONG, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single int
+ */
+
+ public static final SequenceType SINGLE_INT =
+ makeSequenceType(BuiltInAtomicType.INT, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single optional int
+ */
+
+ public static final SequenceType OPTIONAL_INT =
+ makeSequenceType(BuiltInAtomicType.INT, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single short
+ */
+
+ public static final SequenceType SINGLE_SHORT =
+ makeSequenceType(BuiltInAtomicType.SHORT, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single optional short
+ */
+
+ public static final SequenceType OPTIONAL_SHORT =
+ makeSequenceType(BuiltInAtomicType.SHORT, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single short
+ */
+
+ public static final SequenceType SINGLE_BYTE =
+ makeSequenceType(BuiltInAtomicType.BYTE, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single optional byte
+ */
+
+ public static final SequenceType OPTIONAL_BYTE =
+ makeSequenceType(BuiltInAtomicType.BYTE, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+
+ /**
+ * A type that allows a single double
+ */
+
+ public static final SequenceType SINGLE_DOUBLE =
+ makeSequenceType(BuiltInAtomicType.DOUBLE, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single optional double
+ */
+
+ public static final SequenceType OPTIONAL_DOUBLE =
+ makeSequenceType(BuiltInAtomicType.DOUBLE, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single float
+ */
+
+ public static final SequenceType SINGLE_FLOAT =
+ makeSequenceType(BuiltInAtomicType.FLOAT, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single optional float
+ */
+
+ public static final SequenceType OPTIONAL_FLOAT =
+ makeSequenceType(BuiltInAtomicType.FLOAT, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single optional decimal
+ */
+
+ public static final SequenceType OPTIONAL_DECIMAL =
+ makeSequenceType(BuiltInAtomicType.DECIMAL, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single optional anyURI
+ */
+
+ public static final SequenceType OPTIONAL_ANY_URI =
+ makeSequenceType(BuiltInAtomicType.ANY_URI, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single optional dateTime
+ */
+
+ public static final SequenceType OPTIONAL_DATE_TIME =
+ makeSequenceType(BuiltInAtomicType.DATE_TIME, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+
+ /**
+ * A type that allows a single xs:QName
+ */
+
+ public static final SequenceType SINGLE_QNAME =
+ makeSequenceType(BuiltInAtomicType.QNAME, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single optional xs:QName
+ */
+
+ public static final SequenceType OPTIONAL_QNAME =
+ makeSequenceType(BuiltInAtomicType.QNAME, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+
+ /**
+ * A type that allows an optional numeric value
+ */
+
+ public static final SequenceType OPTIONAL_NUMERIC =
+ makeSequenceType(BuiltInAtomicType.NUMERIC, StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ public static final SequenceType SINGLE_NUMERIC =
+ makeSequenceType(BuiltInAtomicType.NUMERIC, StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows zero or one nodes
+ */
+
+ public static final SequenceType OPTIONAL_NODE =
+ makeSequenceType(AnyNodeTest.getInstance(),
+ StaticProperty.ALLOWS_ZERO_OR_ONE);
+
+ /**
+ * A type that allows a single node
+ */
+
+ public static final SequenceType SINGLE_NODE =
+ makeSequenceType(AnyNodeTest.getInstance(),
+ StaticProperty.EXACTLY_ONE);
+
+ /**
+ * A type that allows a single document node
+ */
+
+ public static final SequenceType OPTIONAL_DOCUMENT_NODE =
+ makeSequenceType(NodeKindTest.DOCUMENT,
+ StaticProperty.ALLOWS_ZERO_OR_ONE);
+ /**
+ * A type that allows a single element node
+ */
+
+ public static final SequenceType SINGLE_ELEMENT_NODE =
+ makeSequenceType(NodeKindTest.ELEMENT,
+ StaticProperty.EXACTLY_ONE);
+
+
+ /**
+ * A type that allows a sequence of zero or more nodes
+ */
+
+ public static final SequenceType NODE_SEQUENCE =
+ makeSequenceType(AnyNodeTest.getInstance(),
+ StaticProperty.ALLOWS_ZERO_OR_MORE);
+
+ /**
+ * A type that allows a sequence of zero or more numeric values
+ */
+
+ public static final SequenceType NUMERIC_SEQUENCE =
+ makeSequenceType(BuiltInAtomicType.NUMERIC, StaticProperty.ALLOWS_ZERO_OR_MORE);
+
+
+ /**
+ * A type that allows a sequence of zero or more string values
+ */
+ public static final SequenceType STRING_SEQUENCE =
+ makeSequenceType(BuiltInAtomicType.STRING, StaticProperty.ALLOWS_ZERO_OR_MORE);
+
+ /**
+ * A type that only permits the empty sequence
+ */
+
+ public static final SequenceType EMPTY_SEQUENCE =
+ makeSequenceType(ErrorType.getInstance(), StaticProperty.EMPTY);
+
+ /**
+ * A type that only permits a non-empty sequence
+ */
+
+ public static final SequenceType NON_EMPTY_SEQUENCE =
+ makeSequenceType(AnyItemType.getInstance(), StaticProperty.ALLOWS_ONE_OR_MORE);
+
+ /**
+ * Construct an instance of SequenceType. This is a private constructor: all external
+ * clients use the factory method makeSequenceType(), to allow object pooling.
+ *
+ * @param primaryType The item type
+ * @param cardinality The required cardinality
+ */
+ private SequenceType(ItemType primaryType, int cardinality) {
+ this.primaryType = primaryType;
+ if (primaryType instanceof ErrorType) {
+ this.cardinality = StaticProperty.EMPTY;
+ } else {
+ this.cardinality = cardinality;
+ }
+ }
+
+ /**
+ * Construct an instance of SequenceType. This is a factory method: it maintains a
+ * pool of SequenceType objects to reduce the amount of object creation.
+ *
+ * @param primaryType The item type
+ * @param cardinality The required cardinality. This must be one of the constants {@link StaticProperty#EXACTLY_ONE},
+ * {@link StaticProperty#ALLOWS_ONE_OR_MORE}, etc
+ * @return the SequenceType (either a newly created object, or an existing one from the cache)
+ */
+
+ public static SequenceType makeSequenceType(ItemType primaryType, int cardinality) {
+
+ if (!(primaryType instanceof BuiltInAtomicType)) {
+ return new SequenceType(primaryType, cardinality);
+ }
+
+ // For each ItemType, there is an array of 8 SequenceTypes, one for each possible
+ // cardinality (including impossible cardinalities, such as "0 or many"). The pool
+ // is a static HashMap that obtains this array, given an ItemType. The array contains null
+ // entries for cardinalities that have not been requested.
+
+ SequenceType[] array = (SequenceType[]) pool.get(primaryType);
+ if (array == null) {
+ array = new SequenceType[8];
+ pool.put(primaryType, array);
+ }
+ int code = StaticProperty.getCardinalityCode(cardinality);
+ if (array[code] == null) {
+ SequenceType s = new SequenceType(primaryType, cardinality);
+ array[code] = s;
+ return s;
+ } else {
+ return array[code];
+ }
+ }
+
+ /**
+ * Get the "primary" part of this required type. E.g. for type element(*, xs:date) the "primary type" is element()
+ *
+ * @return The item type code of the primary type
+ */
+ public ItemType getPrimaryType() {
+ return primaryType;
+ }
+
+ /**
+ * Get the cardinality component of this SequenceType. This is one of the constants {@link StaticProperty#EXACTLY_ONE},
+ * {@link StaticProperty#ALLOWS_ONE_OR_MORE}, etc
+ *
+ * @return the required cardinality
+ * @see net.sf.saxon.value.Cardinality
+ */
+ public int getCardinality() {
+ return cardinality;
+ }
+
+ /**
+ * Determine whether a given value is a valid instance of this SequenceType
+ *
+ * @param value the value to be tested
+ * @return true if the value is a valid instance of this type
+ */
+
+ public boolean matches(Sequence value, Configuration config) throws XPathException {
+ if (value instanceof Item) {
+ return primaryType.matchesItem((Item)value, false, config);
+ }
+ int count = 0;
+ SequenceIterator iter = value.iterate();
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ if (count == 0 && !Cardinality.allowsZero(cardinality)) {
+ return false;
+ }
+ if (count > 1 && !Cardinality.allowsMany(cardinality)) {
+ return false;
+ }
+ return true;
+ }
+ count++;
+ if (!primaryType.matchesItem(item, false, config)) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Return a string representation of this SequenceType
+ *
+ * @return the string representation as an instance of the XPath
+ * SequenceType construct
+ */
+ public String toString() {
+ String s = primaryType.toString();
+ if (cardinality == StaticProperty.ALLOWS_ONE_OR_MORE) {
+ s = s + '+';
+ } else if (cardinality == StaticProperty.ALLOWS_ZERO_OR_MORE) {
+ s = s + '*';
+ } else if (cardinality == StaticProperty.ALLOWS_ZERO_OR_ONE) {
+ s = s + '?';
+ }
+ return s;
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ */
+ public int hashCode() {
+ return primaryType.hashCode() ^ cardinality;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ */
+ public boolean equals(/*@NotNull*/ Object obj) {
+ return obj instanceof SequenceType &&
+ this.primaryType.equals(((SequenceType) obj).primaryType) &&
+ this.cardinality == ((SequenceType) obj).cardinality;
+ }
+
+
+}
+
diff --git a/sf/saxon/value/SingletonClosure.java b/sf/saxon/value/SingletonClosure.java
new file mode 100644
index 0000000..99af5f8
--- /dev/null
+++ b/sf/saxon/value/SingletonClosure.java
@@ -0,0 +1,123 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.event.SequenceReceiver;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+
+/**
+ * A SingletonClosure represents a value that has not yet been evaluated: the value is represented
+ * by an expression, together with saved values of all the context variables that the
+ * expression depends on. The value of a SingletonClosure is always either a single item
+ * or an empty sequence.
+ *
+ * <p>The expression may depend on local variables and on the context item; these values
+ * are held in the saved XPathContext object that is kept as part of the Closure, and they
+ * will always be read from that object. The expression may also depend on global variables;
+ * these are unchanging, so they can be read from the Bindery in the normal way. Expressions
+ * that depend on other contextual information, for example the values of position(), last(),
+ * current(), current-group(), should not be evaluated using this mechanism: they should
+ * always be evaluated eagerly. This means that the Closure does not need to keep a copy
+ * of these context variables.</p>
+ */
+
+public class SingletonClosure extends Closure {
+
+ private boolean built = false;
+ /*@Nullable*/ private Item value = null;
+
+ /**
+ * Constructor should not be called directly, instances should be made using the make() method.
+ * @param exp the expression to be lazily evaluated
+ * @param context the context in which the expression should be evaluated
+ * @throws XPathException if an error occurs saving the dynamic context
+ */
+
+ public SingletonClosure(/*@NotNull*/ Expression exp, /*@NotNull*/ XPathContext context) throws XPathException {
+ expression = exp;
+ savedXPathContext = context.newContext();
+ saveContext(exp, context);
+ //System.err.println("Creating SingletonClosure");
+ }
+
+ /**
+ * Evaluate the expression in a given context to return an iterator over a sequence
+ */
+
+ /*@NotNull*/ public SequenceIterator iterate() throws XPathException {
+ return SingletonIterator.makeIterator(asItem());
+ }
+
+ /**
+ * Process the expression by writing the value to the current Receiver
+ *
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(/*@NotNull*/ XPathContext context) throws XPathException {
+ SequenceReceiver out = context.getReceiver();
+ Item item = asItem();
+ if (item != null) {
+ out.append(item, 0, NodeInfo.ALL_NAMESPACES);
+ }
+ }
+
+
+ /**
+ * Return the value in the form of an Item
+ *
+ * @return the value in the form of an Item
+ */
+
+ /*@Nullable*/ public Item asItem() throws XPathException {
+ if (!built) {
+ value = expression.evaluateItem(savedXPathContext);
+ built = true;
+ savedXPathContext = null; // release variables saved in the context to the garbage collector
+ }
+ return value;
+ }
+
+ /**
+ * Get the n'th item in the sequence (starting from 0). This is defined for all
+ * SequenceValues, but its real benefits come for a SequenceValue stored extensionally
+ */
+
+ /*@Nullable*/ public Item itemAt(int n) throws XPathException {
+ if (n != 0) {
+ return null;
+ }
+ return asItem();
+ }
+
+ /**
+ * Get the length of the sequence
+ */
+
+ public int getLength() throws XPathException {
+ return (asItem() == null ? 0 : 1);
+ }
+
+ /**
+ * Return a value containing all the items in the sequence returned by this
+ * SequenceIterator
+ *
+ * @return the corresponding value
+ */
+
+ public GroundedValue materialize() throws XPathException {
+ return new SequenceExtent(iterate());
+ }
+
+}
+
diff --git a/sf/saxon/value/SingletonItem.java b/sf/saxon/value/SingletonItem.java
new file mode 100644
index 0000000..39a8d48
--- /dev/null
+++ b/sf/saxon/value/SingletonItem.java
@@ -0,0 +1,180 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+
+/**
+* A value that is a sequence containing zero or one items. Used only for nodes.
+*/
+
+public class SingletonItem<T extends Item> implements GroundedValue {
+
+ /*@Nullable*/ protected T item = null;
+
+
+ /**
+ * Create a sequence containing zero or one items
+ * @param item The node or function-item to be contained in the sequence, or null if the sequence
+ * is to be empty
+ */
+
+ public SingletonItem(T item) {
+ this.item = item;
+ }
+
+ public CharSequence getStringValueCS() throws XPathException {
+ return item.getStringValueCS();
+ }
+
+ /**
+ * Get the first item in the sequence.
+ *
+ * @return the first item in the sequence if there is one, or null if the sequence
+ * is empty
+ * @throws net.sf.saxon.trans.XPathException
+ * in the situation where the sequence is evaluated lazily, and
+ * evaluation of the first item causes a dynamic error.
+ */
+ public Item head() throws XPathException {
+ return item;
+ }
+
+ /**
+ * Return the value in the form of an Item
+ * @return the value in the form of an Item
+ */
+
+ /*@Nullable*/ public T asItem() {
+ return item;
+ }
+
+ /**
+ * Process the instruction, without returning any tail calls
+ * @param context The dynamic context, giving access to the current node,
+ * the current variables, etc.
+ */
+
+ public void process(/*@NotNull*/ XPathContext context) throws XPathException {
+ if (item != null) {
+ context.getReceiver().append(item, 0, NodeInfo.ALL_NAMESPACES);
+ }
+ }
+
+ /**
+ * Determine the static cardinality
+ */
+
+ public int getCardinality() {
+ if (item ==null) {
+ return StaticProperty.EMPTY;
+ } else {
+ return StaticProperty.EXACTLY_ONE;
+ }
+ }
+
+ /**
+ * Get the length of the sequence
+ */
+
+ public int getLength() {
+ return (item ==null ? 0 : 1);
+ }
+
+ /**
+ * Get the n'th item in the sequence (starting from 0). This is defined for all
+ * SequenceValues, but its real benefits come for a SequenceValue stored extensionally
+ * (or for a MemoClosure, once all the values have been read)
+ */
+
+ /*@Nullable*/ public T itemAt(int n) {
+ if (n==0 && item !=null) {
+ return item;
+ } else {
+ return null;
+ }
+ }
+
+
+ /**
+ * Get a subsequence of the value
+ *
+ *
+ * @param start the index of the first item to be included in the result, counting from zero.
+ * A negative value is taken as zero. If the value is beyond the end of the sequence, an empty
+ * sequence is returned
+ * @param length the number of items to be included in the result. Specify Integer.MAX_VALUE to
+ * get the subsequence up to the end of the base sequence. If the value is negative, an empty sequence
+ * is returned. If the value goes off the end of the sequence, the result returns items up to the end
+ * of the sequence
+ * @return the required subsequence. If min is
+ */
+
+ /*@NotNull*/ public GroundedValue subsequence(int start, int length) {
+ if (item != null && start <= 0 && start+length > 0) {
+ return this;
+ } else {
+ return EmptySequence.getInstance();
+ }
+ }
+
+ /**
+ * Return an enumeration of this nodeset value.
+ */
+
+ /*@NotNull*/ public SequenceIterator<T> iterate() {
+ return SingletonIterator.makeIterator(item);
+ }
+
+ /**
+ * Get the effective boolean value
+ */
+
+ public boolean effectiveBooleanValue() {
+ return (item != null);
+ }
+
+ /**
+ * Convert the value to a string, using the serialization rules.
+ * For atomic values this is the same as a cast; for sequence values
+ * it gives a space-separated list. For QNames and NOTATIONS, or lists
+ * containing them, it fails.
+ */
+
+ /*@NotNull*/ public String getStringValue() {
+ return (item ==null ? "" : item.getStringValue());
+ }
+
+ /**
+ * Returns a string representation of the object.
+ * @return a string representation of the object.
+ */
+ @Override
+ public String toString() {
+ return item.toString();
+ }
+
+ /**
+ * Reduce the sequence to its simplest form. If the value is an empty sequence, the result will be
+ * EmptySequence.getInstance(). If the value is a single atomic value, the result will be an instance
+ * of AtomicValue. If the value is a single item of any other kind, the result will be an instance
+ * of SingletonItem. Otherwise, the result will typically be unchanged.
+ *
+ * @return the simplified sequence
+ */
+ public GroundedValue reduce() {
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/value/StringToDouble11.java b/sf/saxon/value/StringToDouble11.java
new file mode 100644
index 0000000..e46da71
--- /dev/null
+++ b/sf/saxon/value/StringToDouble11.java
@@ -0,0 +1,35 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.type.StringToDouble;
+
+/**
+ * Convert a string to a double using the rules of XML Schema 1.1
+ */
+public class StringToDouble11 extends StringToDouble {
+
+ private static StringToDouble11 THE_INSTANCE = new StringToDouble11();
+
+ /**
+ * Get the singleton instance
+ * @return the singleton instance of this class
+ */
+
+ /*@NotNull*/ public static StringToDouble11 getInstance() {
+ return THE_INSTANCE;
+ }
+
+ protected StringToDouble11() {}
+
+ @Override
+ protected double signedPositiveInfinity() {
+ return Double.POSITIVE_INFINITY;
+ }
+}
+
diff --git a/sf/saxon/value/StringValue.java b/sf/saxon/value/StringValue.java
new file mode 100644
index 0000000..a6ca996
--- /dev/null
+++ b/sf/saxon/value/StringValue.java
@@ -0,0 +1,490 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.regex.BMPString;
+import net.sf.saxon.regex.UnicodeString;
+import net.sf.saxon.tree.iter.UnfailingIterator;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+
+
+/**
+ * An atomic value of type xs:string. This class is also used for types derived from xs:string.
+ * Subclasses of StringValue are used for xs:untypedAtomic and xs:anyURI values.
+ */
+
+public class StringValue extends AtomicValue {
+
+ /*@NotNull*/ public static final StringValue EMPTY_STRING = new StringValue("");
+ /*@NotNull*/ public static final StringValue SINGLE_SPACE = new StringValue(" ");
+ /*@NotNull*/ public static final StringValue TRUE = new StringValue("true");
+ /*@NotNull*/ public static final StringValue FALSE = new StringValue("false");
+
+ static {
+ EMPTY_STRING.unicodeString = new BMPString("");
+ SINGLE_SPACE.unicodeString = new BMPString(" ");
+ TRUE.unicodeString = new BMPString("true");
+ FALSE.unicodeString = new BMPString("false");
+ }
+
+ // We hold the value as a CharSequence (it may be a StringBuffer rather than a string)
+ // But the first time this is converted to a string, we keep it as a string
+
+ protected CharSequence value; // may be zero-length, will never be null
+ protected UnicodeString unicodeString = null;
+
+ /**
+ * Protected constructor for use by subtypes
+ */
+
+ protected StringValue() {
+ value = "";
+ typeLabel = BuiltInAtomicType.STRING;
+ }
+
+ /**
+ * Constructor. Note that although a StringValue may wrap any kind of CharSequence
+ * (usually a String, but it can also be, for example, a StringBuffer), the caller
+ * is responsible for ensuring that the value is immutable.
+ * @param value the String value. Null is taken as equivalent to "".
+ */
+
+ public StringValue(/*@Nullable*/ CharSequence value) {
+ this.value = (value == null ? "" : value);
+ typeLabel = BuiltInAtomicType.STRING;
+ }
+
+ /**
+ * Constructor. Note that although a StringValue may wrap any kind of CharSequence
+ * (usually a String, but it can also be, for example, a StringBuffer), the caller
+ * is responsible for ensuring that the value is immutable.
+ * @param value the String value.
+ * @param typeLabel the type of the value to be created. The caller must ensure that this is
+ * a type derived from string and that the string is valid against this type.
+ */
+
+ public StringValue(CharSequence value, AtomicType typeLabel) {
+ this.value = value;
+ this.typeLabel = typeLabel;
+ }
+
+
+ /**
+ * Assert that the string is known to contain no surrogate pairs
+ */
+
+ public void setContainsNoSurrogates() {
+ unicodeString = new BMPString(value);
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ StringValue v = new StringValue(value);
+ v.unicodeString = unicodeString;
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.STRING;
+ }
+
+ /**
+ * Factory method. Unlike the constructor, this avoids creating a new StringValue in the case
+ * of a zero-length string (and potentially other strings, in future)
+ * @param value the String value. Null is taken as equivalent to "".
+ * @return the corresponding StringValue
+ */
+
+ /*@NotNull*/ public static StringValue makeStringValue(/*@Nullable*/ CharSequence value) {
+ if (value == null || value.length() == 0) {
+ return StringValue.EMPTY_STRING;
+ } else {
+ return new StringValue(value);
+ }
+ }
+
+ public static StringValue makeStringValue(UnicodeString unicode) {
+ if (unicode.length() == 0) {
+ return EMPTY_STRING;
+ }
+ StringValue sv = new StringValue(unicode.getCharSequence());
+ sv.unicodeString = unicode;
+ return sv;
+ }
+
+ /**
+ * Get the string value as a CharSequence
+ */
+
+ public final CharSequence getPrimitiveStringValue() {
+ return value;
+ }
+
+ /**
+ * Set the value of the item as a CharSequence.
+ * <p><b>For system use only. In principle, a StringValue is immutable. However, in special circumstances,
+ * if it is newly constructed, the content can be changed to reflect the effect of the whiteSpace facet.</b></p>
+ * @param value the value of the string
+ */
+
+ public final void setStringValueCS(CharSequence value) {
+ this.value = value;
+ }
+
+ /**
+ * Get the length of this string, as defined in XPath. This is not the same as the Java length,
+ * as a Unicode surrogate pair counts as a single character
+ * @return the length of the string in Unicode code points
+ */
+
+ public int getStringLength() {
+ if (unicodeString == null) {
+ makeUnicodeString();
+ }
+ return unicodeString.length();
+ }
+
+ public void makeUnicodeString() {
+ unicodeString = UnicodeString.makeUnicodeString(value);
+ }
+
+ public UnicodeString getUnicodeString() {
+ if (unicodeString == null) {
+ makeUnicodeString();
+ }
+ return unicodeString;
+ }
+
+ /**
+ * Get the length of a string, as defined in XPath. This is not the same as the Java length,
+ * as a Unicode surrogate pair counts as a single character.
+ * @param s The string whose length is required
+ * @return the length of the string in Unicode code points
+ */
+
+ public static int getStringLength(/*@NotNull*/ CharSequence s) {
+ int n = 0;
+ for (int i = 0; i < s.length(); i++) {
+ int c = (int) s.charAt(i);
+ if (c < 55296 || c > 56319) n++; // don't count high surrogates, i.e. D800 to DBFF
+ }
+ return n;
+ }
+
+
+ /**
+ * Determine whether the string is a zero-length string. This may
+ * be more efficient than testing whether the length is equal to zero
+ * @return true if the string is zero length
+ */
+
+ public boolean isZeroLength() {
+ return value.length() == 0;
+ }
+
+ /**
+ * Determine whether the string contains surrogate pairs
+ * @return true if the string contains any non-BMP characters
+ */
+
+ public boolean containsSurrogatePairs() {
+ if (unicodeString != null) {
+ return unicodeString.length() != value.length();
+ }
+ return UnicodeString.containsSurrogatePairs(value);
+ }
+
+ /**
+ * Ask whether the string is known to contain no surrogate pairs.
+ * @return true if it is known to contain no surrogates, false if the answer is not known
+ */
+
+ public boolean isKnownToContainNoSurrogates() {
+ return unicodeString instanceof BMPString;
+ }
+
+ /**
+ * Iterate over a string, returning a sequence of integers representing the Unicode code-point values
+ * @return an iterator over the characters (Unicode code points) in the string
+ */
+
+ /*@NotNull*/ public UnfailingIterator iterateCharacters() {
+ return new CharacterIterator();
+ }
+
+ /**
+ * Expand a string containing surrogate pairs into an array of 32-bit characters
+ * @param s the string to be expanded
+ * @return an array of integers representing the Unicode code points
+ */
+
+ /*@NotNull*/ public static int[] expand(/*@NotNull*/ CharSequence s) {
+ int[] array = new int[getStringLength(s)];
+ int o = 0;
+ for (int i = 0; i < s.length(); i++) {
+ int charval;
+ int c = s.charAt(i);
+ if (c >= 55296 && c <= 56319) {
+ // we'll trust the data to be sound
+ charval = ((c - 55296) * 1024) + ((int) s.charAt(i + 1) - 56320) + 65536;
+ i++;
+ } else {
+ charval = c;
+ }
+ array[o++] = charval;
+ }
+ return array;
+ }
+
+ /**
+ * Contract an array of integers containing Unicode codepoints into a Java string
+ * @param codes an array of integers representing the Unicode code points
+ * @param used the number of items in the array that are actually used
+ * @return the constructed string
+ */
+
+ /*@NotNull*/ public static CharSequence contract(/*@NotNull*/ int[] codes, int used) {
+ FastStringBuffer sb = new FastStringBuffer(codes.length);
+ for (int i=0; i<used; i++) {
+ sb.appendWideChar(codes[i]);
+ }
+ return sb;
+ }
+
+
+ /**
+ * Get an object value that implements the XPath equality and ordering comparison semantics for this value.
+ * If the ordered parameter is set to true, the result will be a Comparable and will support a compareTo()
+ * method with the semantics of the XPath lt/gt operator, provided that the other operand is also obtained
+ * using the getXPathComparable() method. In all cases the result will support equals() and hashCode() methods
+ * that support the semantics of the XPath eq operator, again provided that the other operand is also obtained
+ * using the getXPathComparable() method. A context argument is supplied for use in cases where the comparison
+ * semantics are context-sensitive, for example where they depend on the implicit timezone or the default
+ * collation.
+ *
+ * @param ordered true if an ordered comparison is required. In this case the result is null if the
+ * type is unordered; in other cases the returned value will be a Comparable.
+ * @param collator Collation to be used for comparing strings
+ * @param context the XPath dynamic evaluation context, used in cases where the comparison is context
+ * sensitive
+ * @return an Object whose equals() and hashCode() methods implement the XPath comparison semantics
+ * with respect to this atomic value. If ordered is specified, the result will either be null if
+ * no ordering is defined, or will be a Comparable
+ */
+
+ public Object getXPathComparable(boolean ordered, /*@NotNull*/ StringCollator collator, XPathContext context) {
+ return collator.getCollationKey(asString());
+ }
+
+ /**
+ * Determine if two AtomicValues are equal, according to XPath rules. (This method
+ * is not used for string comparisons, which are always under the control of a collation.
+ * If we get here, it's because there's a type error in the comparison.)
+ * @throws ClassCastException always
+ */
+
+ @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
+ public boolean equals(Object other) {
+ throw new ClassCastException("equals on StringValue is not allowed");
+ }
+
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ /**
+ * Returns the underlying string, with the side-effect of storing the string internally for future use,
+ * potentially reducing the cost if it is ever needed again
+ * @return the value as a String
+ */
+
+ public String asString() {
+ if(value instanceof String) {
+ return (String)value;
+ }
+ String s = value.toString();
+ value = s;
+ return s;
+ }
+
+ /**
+ * Test whether this StringValue is equal to another under the rules of the codepoint collation
+ * @param other the value to be compared with this value
+ * @return true if the strings are equal on a codepoint-by-codepoint basis
+ */
+
+ public boolean codepointEquals(/*@NotNull*/ StringValue other) {
+ // avoid conversion of CharSequence to String if values are different lengths
+ return value.length() == other.value.length() &&
+ asString().equals(other.asString());
+ // It might be better to do character-by-character comparison in all cases; or it might not.
+ // We do it this way in the hope that string comparison compiles to native code.
+ }
+
+ /**
+ * Get the effective boolean value of a string
+ * @return true if the string has length greater than zero
+ */
+
+ public boolean effectiveBooleanValue() {
+ return value.length() > 0;
+ }
+
+
+ /*@NotNull*/ public String toString() {
+ return "\"" + value + '\"';
+ }
+
+ /**
+ * Get a Comparable value that implements the XML Schema comparison semantics for this value.
+ * Returns null if the value is not comparable according to XML Schema rules. This implementation
+ * returns the underlying Java string, which works because strings will only be compared for
+ * equality, not for ordering, and the equality rules for strings in XML schema are the same as in Java.
+ */
+
+ public Comparable getSchemaComparable() {
+ return asString();
+ }
+
+ /**
+ * Determine whether two atomic values are identical, as determined by XML Schema rules. This is a stronger
+ * test than equality (even schema-equality); for example two dateTime values are not identical unless
+ * they are in the same timezone.
+ * <p>Note that even this check ignores the type annotation of the value. The integer 3 and the short 3
+ * are considered identical, even though they are not fully interchangeable. "Identical" means the
+ * same point in the value space, regardless of type annotation.</p>
+ * <p>NaN is identical to itself.</p>
+ *
+ * @param v the other value to be compared with this one
+ * @return true if the two values are identical, false otherwise.
+ */
+
+ public boolean isIdentical(/*@NotNull*/ AtomicValue v) {
+ return v instanceof StringValue &&
+ (this instanceof AnyURIValue == v instanceof AnyURIValue) &&
+ (this instanceof UntypedAtomicValue == v instanceof UntypedAtomicValue) &&
+ asString().equals(((StringValue)v).asString());
+ }
+
+ /**
+ * Produce a diagnostic representation of the contents of the string
+ * @param s the string
+ * @return a string in which non-Ascii-printable characters are replaced by \ uXXXX escapes
+ */
+
+ /*@NotNull*/ public static String diagnosticDisplay(/*@NotNull*/ String s) {
+ FastStringBuffer fsb = new FastStringBuffer(s.length());
+ for (int i = 0, len = s.length(); i < len; i++) {
+ char c = s.charAt(i);
+ if (c >= 0x20 && c <= 0x7e) {
+ fsb.append(c);
+ } else {
+ fsb.append("\\u");
+ for (int shift = 12; shift >= 0; shift -= 4) {
+ fsb.append("0123456789ABCDEF".charAt((c >> shift) & 0xF));
+ }
+ }
+ }
+ return fsb.toString();
+ }
+
+ /**
+ * CharacterIterator is used to iterate over the characters in a string,
+ * returning them as integers representing the Unicode code-point.
+ */
+
+
+ public final class CharacterIterator implements UnfailingIterator<Int64Value> {
+
+ int inpos = 0; // 0-based index of the current Java char
+ int outpos = 0; // 1-based value of position() function
+ int current = -1; // Unicode codepoint most recently returned
+
+ /**
+ * Create an iterator over a string
+ */
+
+ public CharacterIterator() {
+ }
+
+ /*@Nullable*/ public Int64Value next() {
+ if (inpos < value.length()) {
+ int c = value.charAt(inpos++);
+ if (c >= 55296 && c <= 56319) {
+ // we'll trust the data to be sound
+ try {
+ current = ((c - 55296) * 1024) + ((int) value.charAt(inpos++) - 56320) + 65536;
+ } catch (StringIndexOutOfBoundsException e) {
+ System.err.println("Invalid surrogate at end of string");
+ System.err.println(diagnosticDisplay(value.toString()));
+ e.printStackTrace();
+ throw e;
+ }
+ } else {
+ current = c;
+ }
+ outpos++;
+ return new Int64Value(current);
+ } else {
+ outpos = -1;
+ return null;
+ }
+ }
+
+ /*@Nullable*/ public Int64Value current() {
+ if (outpos < 1) {
+ return null;
+ }
+ return new Int64Value(current);
+ }
+
+ public int position() {
+ return outpos;
+ }
+
+ public void close() {
+ }
+
+ /*@NotNull*/ public UnfailingIterator<Int64Value> getAnother() {
+ return new CharacterIterator();
+ }
+
+ /**
+ * Get properties of this iterator, as a bit-significant integer.
+ *
+ * @return the properties of this iterator. This will be some combination of
+ * properties such as {@link #GROUNDED} and {@link #LAST_POSITION_FINDER}. It is always
+ * acceptable to return the value zero, indicating that there are no known special properties.
+ */
+
+ public int getProperties() {
+ return 0;
+ }
+ }
+
+
+}
+
diff --git a/sf/saxon/value/TextFragmentValue.java b/sf/saxon/value/TextFragmentValue.java
new file mode 100644
index 0000000..7f4c7b1
--- /dev/null
+++ b/sf/saxon/value/TextFragmentValue.java
@@ -0,0 +1,1162 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.*;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.tree.util.Navigator;
+import net.sf.saxon.type.SchemaType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.Untyped;
+
+import javax.xml.transform.SourceLocator;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+* This class represents a temporary tree whose root document node owns a single text node. <BR>
+*/
+
+public final class TextFragmentValue implements DocumentInfo, SourceLocator {
+
+ private CharSequence text;
+ private String baseURI;
+ private String documentURI;
+ /*@Nullable*/ private TextFragmentTextNode textNode = null; // created on demand
+ private Configuration config;
+ private long documentNumber;
+ private HashMap<String, Object> userData;
+
+ /**
+ * Constructor: create a result tree fragment containing a single text node
+ * @param value a String containing the value
+ * @param baseURI the base URI of the document node
+ */
+
+ public TextFragmentValue(CharSequence value, String baseURI) {
+ this.text = value;
+ this.baseURI = baseURI;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns the item itself
+ * @return this item
+ */
+
+ public Item head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ * @return a singleton iterator that returns this item
+ */
+
+ public SequenceIterator iterate() {
+ return SingletonIterator.makeIterator(this);
+ }
+
+ /**
+ * Set the configuration (containing the name pool used for all names in this document)
+ */
+
+ public void setConfiguration(Configuration config) {
+ this.config = config;
+ documentNumber = -1; // the document number is allocated lazily because it can cause
+ // contention on the NamePool and is often not needed.
+ }
+
+ /**
+ * Get the configuration previously set using setConfiguration
+ * (or the default configuraton allocated automatically)
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the name pool used for the names in this document
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ /**
+ * Ask whether the document contains any nodes whose type annotation is anything other than
+ * UNTYPED
+ *
+ * @return true if the document contains elements whose type is other than UNTYPED
+ */
+ public boolean isTyped() {
+ return false;
+ }
+
+ /**
+ * Get the unique document number
+ */
+
+ public long getDocumentNumber() {
+ if (documentNumber == -1) {
+ documentNumber = config.getDocumentNumberAllocator().allocateDocumentNumber();
+ // technically this isn't thread-safe; however, TextFragmentValues are invariably used within
+ // a single thread
+ }
+ return documentNumber;
+ }
+
+ /**
+ * Return the type of node.
+ * @return Type.DOCUMENT (always)
+ */
+
+ public final int getNodeKind() {
+ return Type.DOCUMENT;
+ }
+
+ /**
+ * Get the String Value
+ */
+
+ public String getStringValue() {
+ return text.toString();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ return text;
+ }
+
+ /**
+ * Determine whether this is the same node as another node
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(NodeInfo other) {
+ return this==other;
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node
+ * @param buffer the buffer to contain the generated ID
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ buffer.append("tt");
+ buffer.append(Long.toString(getDocumentNumber()));
+ }
+
+ /**
+ * Set the system ID (that is, the document URI property) for the document node.
+ * @throws UnsupportedOperationException (always). This kind of tree does not have a document URI.
+ */
+
+ public void setSystemId(String systemId) {
+ documentURI = systemId;
+ }
+
+ /**
+ * Get the system ID (the document URI) of the document node.
+ */
+
+ public String getSystemId() {
+ return documentURI;
+ }
+
+ /**
+ * Get the base URI for the document node.
+ */
+
+ public String getBaseURI() {
+ return baseURI;
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ * @param other The other node, whose position is to be compared with this node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public int compareOrder(NodeInfo other) {
+ if (this==other) return 0;
+ return -1;
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+ if (this==other) {
+ return AxisInfo.SELF;
+ }
+ if (textNode==other) {
+ return AxisInfo.ANCESTOR;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Get the name code of the node, used for displaying names
+ */
+
+ public int getNameCode() {
+ return -1;
+ }
+
+ /**
+ * Get the fingerprint of the node, used for matching names
+ */
+
+ public int getFingerprint() {
+ return -1;
+ }
+
+ /**
+ * Get the prefix part of the name of this node. This is the name before the ":" if any.
+ * @return the prefix part of the name. For an unnamed node, return "".
+ */
+
+ /*@NotNull*/ public String getPrefix() {
+ return "";
+ }
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ * @return The URI of the namespace of this node. For an unnamed node, or for
+ * an element or attribute in the default namespace, return an empty string.
+ */
+
+ /*@NotNull*/ public String getURI() {
+ return "";
+ }
+
+ /**
+ * Get the display name of this node. For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ * @return The display name of this node.
+ * For a node with no name, return an empty string.
+ */
+
+ /*@NotNull*/ public String getDisplayName() {
+ return "";
+ }
+
+ /**
+ * Get the local name of this node.
+ * @return The local name of this node.
+ * For a node with no name, return "".
+ */
+
+ /*@NotNull*/ public String getLocalPart() {
+ return "";
+ }
+
+ /**
+ * Determine whether the node has any children.
+ * @return <code>true</code> if this node has any attributes,
+ * <code>false</code> otherwise.
+ */
+
+ public boolean hasChildNodes() {
+ return !("".equals(text));
+ }
+
+ /**
+ * Get line number
+ *
+ * @return the line number of the node in its original source document; or
+ * -1 if not available
+ */
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+ /**
+ * Get the type annotation of this node, if any.
+ * Returns XS_UNTYPED for kinds of nodes that have no annotation, and for elements annotated as
+ * untyped, and attributes annotated as untypedAtomic.
+ *
+ * @return the type annotation of the node.
+ * @see net.sf.saxon.type.Type
+ */
+
+ public int getTypeAnnotation() {
+ return StandardNames.XS_UNTYPED;
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ public SchemaType getSchemaType() {
+ return Untyped.getInstance();
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ /*@Nullable*/ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return null;
+ }
+
+ /**
+ * Get the typed value.
+ *
+ * @return the typed value. If requireSingleton is set to true, the result will always be an
+ * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize() {
+ return new UntypedAtomicValue(text);
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ * <p/>
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup that
+ * triggered the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Return the character position where the current document event ends.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of error
+ * reporting; it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.</p>
+ * <p/>
+ * <p>The return value is an approximation of the column number
+ * in the document entity or external parsed entity where the
+ * markup that triggered the event appears.</p>
+ *
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) {
+ return null;
+ }
+
+ /**
+ * Return an iteration over the nodes reached by the given axis from this node
+ * @param axisNumber The axis to be iterated over
+ * @return a AxisIterator that scans the nodes reached by the axis in turn.
+ * @see net.sf.saxon.om.AxisInfo
+ */
+
+ /*@NotNull*/ public AxisIterator iterateAxis(byte axisNumber) {
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR:
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.FOLLOWING:
+ case AxisInfo.FOLLOWING_SIBLING:
+ case AxisInfo.NAMESPACE:
+ case AxisInfo.PARENT:
+ case AxisInfo.PRECEDING:
+ case AxisInfo.PRECEDING_SIBLING:
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ return EmptyAxisIterator.emptyAxisIterator();
+
+ case AxisInfo.SELF:
+ case AxisInfo.ANCESTOR_OR_SELF:
+ return SingleNodeIterator.makeIterator(this);
+
+ case AxisInfo.CHILD:
+ case AxisInfo.DESCENDANT:
+ return SingleNodeIterator.makeIterator(getTextNode());
+
+ case AxisInfo.DESCENDANT_OR_SELF:
+ NodeInfo[] nodes = {this, getTextNode()};
+ return new AxisIteratorOverSequence<NodeInfo>(new ArrayIterator<NodeInfo>(nodes));
+
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+ /**
+ * Return an enumeration over the nodes reached by the given axis from this node
+ * @param axisNumber The axis to be iterated over
+ * @param nodeTest A pattern to be matched by the returned nodes
+ * @return a AxisIterator that scans the nodes reached by the axis in turn.
+ * @see net.sf.saxon.om.AxisInfo
+ */
+
+ /*@NotNull*/ public AxisIterator iterateAxis(byte axisNumber, /*@NotNull*/ NodeTest nodeTest) {
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR:
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.FOLLOWING:
+ case AxisInfo.FOLLOWING_SIBLING:
+ case AxisInfo.NAMESPACE:
+ case AxisInfo.PARENT:
+ case AxisInfo.PRECEDING:
+ case AxisInfo.PRECEDING_SIBLING:
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ return EmptyAxisIterator.emptyAxisIterator();
+
+ case AxisInfo.SELF:
+ case AxisInfo.ANCESTOR_OR_SELF:
+ return Navigator.filteredSingleton(this, nodeTest);
+
+ case AxisInfo.CHILD:
+ case AxisInfo.DESCENDANT:
+ return Navigator.filteredSingleton(getTextNode(), nodeTest);
+
+ case AxisInfo.DESCENDANT_OR_SELF:
+ boolean b1 = nodeTest.matches(this);
+ NodeInfo textNode2 = getTextNode();
+ boolean b2 = nodeTest.matches(textNode2);
+ if (b1) {
+ if (b2) {
+ NodeInfo[] pair = {this, textNode2};
+ return new AxisIteratorOverSequence<NodeInfo>(new ArrayIterator<NodeInfo>(pair));
+ } else {
+ return SingleNodeIterator.makeIterator(this);
+ }
+ } else {
+ if (b2) {
+ return SingleNodeIterator.makeIterator(textNode2);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+ }
+
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+ /**
+ * Find the parent node of this node.
+ * @return The Node object describing the containing element or root node.
+ */
+
+ /*@Nullable*/ public NodeInfo getParent() {
+ return null;
+ }
+
+ /**
+ * Get the root node
+ * @return the NodeInfo representing the root of this tree
+ */
+
+ /*@NotNull*/ public NodeInfo getRoot() {
+ return this;
+ }
+
+ /**
+ * Get the root (document) node
+ * @return the DocumentInfo representing the containing document
+ */
+
+ /*@NotNull*/ public DocumentInfo getDocumentRoot() {
+ return this;
+ }
+
+ /**
+ * Copy the result tree fragment value to a given Outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId)
+ throws XPathException {
+ out.characters(text, 0, 0);
+ }
+
+ /**
+ * Get the element with a given ID.
+ * @param id The unique ID of the required element
+ * @param getParent
+ * @return null (this kind of tree contains no elements)
+ */
+
+ /*@Nullable*/ public NodeInfo selectID(String id, boolean getParent) {
+ return null;
+ }
+
+ /**
+ * Get the list of unparsed entities defined in this document
+ * @return an Iterator, whose items are of type String, containing the names of all
+ * unparsed entities defined in this document. If there are no unparsed entities or if the
+ * information is not available then an empty iterator is returned
+ */
+
+ public Iterator<String> getUnparsedEntityNames() {
+ return Collections.EMPTY_LIST.iterator();
+ }
+
+ /**
+ * Get the unparsed entity with a given name
+ * @param name the name of the entity
+ * @return the URI and public ID of the entity if there is one, or null if not
+ */
+
+ /*@Nullable*/ public String[] getUnparsedEntity(String name) {
+ return null;
+ }
+
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ return false;
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return false;
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return false;
+ }
+
+ /**
+ * Make an instance of the text node
+ */
+
+ /*@Nullable*/ private TextFragmentTextNode getTextNode() {
+ if (textNode==null) {
+ textNode = new TextFragmentTextNode();
+ }
+ return textNode;
+ }
+
+ /**
+ * Inner class representing the text node; this is created on demand
+ */
+
+ private class TextFragmentTextNode implements NodeInfo, SourceLocator {
+
+ /**
+ * To implement {@link Sequence}, this method returns the item itself
+ * @return this item
+ */
+
+ public Item head() {
+ return this;
+ }
+
+ /**
+ * To implement {@link Sequence}, this method returns a singleton iterator
+ * that delivers this item in the form of a sequence
+ * @return a singleton iterator that returns this item
+ */
+
+ public SequenceIterator iterate() {
+ return SingletonIterator.makeIterator(this);
+ }
+
+ /**
+ * Set the system ID for the entity containing the node.
+ */
+
+ public void setSystemId(String systemId) {}
+
+ /**
+ * Get the configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Get the name pool for this node
+ * @return the NamePool
+ */
+
+ public NamePool getNamePool() {
+ return config.getNamePool();
+ }
+
+ /**
+ * Return the type of node.
+ * @return Type.TEXT (always)
+ */
+
+ public final int getNodeKind() {
+ return Type.TEXT;
+ }
+
+ /**
+ * Get the String Value
+ */
+
+ public String getStringValue() {
+ return text.toString();
+ }
+
+ /**
+ * Get the value of the item as a CharSequence. This is in some cases more efficient than
+ * the version of the method that returns a String.
+ */
+
+ public CharSequence getStringValueCS() {
+ return text;
+ }
+
+ /**
+ * Determine whether this is the same node as another node
+ * @return true if this Node object and the supplied Node object represent the
+ * same node in the tree.
+ */
+
+ public boolean isSameNodeInfo(NodeInfo other) {
+ return this==other;
+ }
+
+ /**
+ * Get a character string that uniquely identifies this node
+ */
+
+ public void generateId(/*@NotNull*/ FastStringBuffer buffer) {
+ buffer.append("tt");
+ buffer.append(Long.toString(getDocumentNumber()));
+ buffer.append("t1");
+ }
+
+ /**
+ * Get the system ID for the entity containing the node.
+ */
+
+ /*@Nullable*/ public String getSystemId() {
+ return null;
+ }
+
+ /**
+ * Get the base URI for the node. Default implementation for child nodes gets
+ * the base URI of the parent node.
+ */
+
+ public String getBaseURI() {
+ return baseURI;
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order.
+ * The other node will always be in the same document.
+ * @param other The other node, whose position is to be compared with this node
+ * @return -1 if this node precedes the other node, +1 if it follows the other
+ * node, or 0 if they are the same node. (In this case, isSameNode() will always
+ * return true, and the two nodes will produce the same result for generateId())
+ */
+
+ public int compareOrder(NodeInfo other) {
+ if (this==other) return 0;
+ return +1;
+ }
+
+ /**
+ * Determine the relative position of this node and another node, in document order,
+ * distinguishing whether the first node is a preceding, following, descendant, ancestor,
+ * or the same node as the second.
+ * <p/>
+ * The other node must always be in the same tree; the effect of calling this method
+ * when the two nodes are in different trees is undefined. If either node is a namespace
+ * or attribute node, the method should throw UnsupportedOperationException.
+ *
+ * @param other The other node, whose position is to be compared with this
+ * node
+ * @return {@link net.sf.saxon.om.AxisInfo#PRECEDING} if this node is on the preceding axis of the other node;
+ * {@link net.sf.saxon.om.AxisInfo#FOLLOWING} if it is on the following axis; {@link net.sf.saxon.om.AxisInfo#ANCESTOR} if the first node is an
+ * ancestor of the second; {@link net.sf.saxon.om.AxisInfo#DESCENDANT} if the first is a descendant of the second;
+ * {@link net.sf.saxon.om.AxisInfo#SELF} if they are the same node.
+ * @throws UnsupportedOperationException if either node is an attribute or namespace
+ * @since 9.5
+ */
+ public int comparePosition(NodeInfo other) {
+ if (this == other) {
+ return AxisInfo.SELF;
+ }
+ if (TextFragmentValue.this == other) {
+ return AxisInfo.DESCENDANT;
+ }
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Get the name code of the node, used for displaying names
+ */
+
+ public int getNameCode() {
+ return -1;
+ }
+
+ /**
+ * Get the fingerprint of the node, used for matching names
+ */
+
+ public int getFingerprint() {
+ return -1;
+ }
+
+
+ /**
+ * Get the prefix part of the name of this node. This is the name before the ":" if any.
+ * @return the prefix part of the name. For an unnamed node, return "".
+ */
+
+ /*@NotNull*/ public String getPrefix() {
+ return "";
+ }
+
+ /**
+ * Get the URI part of the name of this node. This is the URI corresponding to the
+ * prefix, or the URI of the default namespace if appropriate.
+ * @return The URI of the namespace of this node. For an unnamed node, or for
+ * an element or attribute in the default namespace, return an empty string.
+ */
+
+ /*@NotNull*/ public String getURI() {
+ return "";
+ }
+
+ /**
+ * Get the display name of this node. For elements and attributes this is [prefix:]localname.
+ * For unnamed nodes, it is an empty string.
+ * @return The display name of this node.
+ * For a node with no name, return an empty string.
+ */
+
+ /*@NotNull*/ public String getDisplayName() {
+ return "";
+ }
+
+ /**
+ * Get the local name of this node.
+ * @return The local name of this node.
+ * For a node with no name, return "".
+ */
+
+ /*@NotNull*/ public String getLocalPart() {
+ return "";
+ }
+
+ /**
+ * Determine whether the node has any children.
+ * @return <code>true</code> if this node has any attributes,
+ * <code>false</code> otherwise.
+ */
+
+ public boolean hasChildNodes() {
+ return false;
+ }
+
+ /**
+ * Get the string value of a given attribute of this node
+ *
+ * @param uri the namespace URI of the attribute name. Supply the empty string for an attribute
+ * that is in no namespace
+ * @param local the local part of the attribute name.
+ * @return the attribute value if it exists, or null if it does not exist. Always returns null
+ * if this node is not an element.
+ * @since 9.4
+ */
+ public String getAttributeValue(/*@NotNull*/ String uri, /*@NotNull*/ String local) {
+ return null;
+ }
+
+ /**
+ * Get line number
+ *
+ * @return the line number of the node in its original source document; or
+ * -1 if not available
+ */
+
+ public int getLineNumber() {
+ return -1;
+ }
+
+ /**
+ * Get the type annotation of this node, if any.
+ * <p>The result is undefined for nodes other than elements and attributes.</p>
+ *
+ * @return the type annotation of the node.
+ * @see net.sf.saxon.type.Type
+ */
+
+ public int getTypeAnnotation() {
+ return -1;
+ }
+
+ /**
+ * Get the type annotation of this node, if any. The type annotation is represented as
+ * SchemaType object.
+ * <p/>
+ * <p>Types derived from a DTD are not reflected in the result of this method.</p>
+ *
+ * @return For element and attribute nodes: the type annotation derived from schema
+ * validation (defaulting to xs:untyped and xs:untypedAtomic in the absence of schema
+ * validation). For comments, text nodes, processing instructions, and namespaces: null.
+ * For document nodes, either xs:untyped if the document has not been validated, or
+ * xs:anyType if it has.
+ * @since 9.4
+ */
+ public SchemaType getSchemaType() {
+ return null;
+ }
+
+ /**
+ * Get the document number of the document containing this node. For a free-standing
+ * orphan node, just return the hashcode.
+ */
+
+ public long getDocumentNumber() {
+ return getDocumentRoot().getDocumentNumber();
+ }
+
+ /**
+ * Get all namespace undeclarations and undeclarations defined on this element.
+ *
+ * @param buffer If this is non-null, and the result array fits in this buffer, then the result
+ * may overwrite the contents of this array, to avoid the cost of allocating a new array on the heap.
+ * @return An array of integers representing the namespace declarations and undeclarations present on
+ * this element. For a node other than an element, return null. Otherwise, the returned array is a
+ * sequence of namespace codes, whose meaning may be interpreted by reference to the name pool. The
+ * top half word of each namespace code represents the prefix, the bottom half represents the URI.
+ * If the bottom half is zero, then this is a namespace undeclaration rather than a declaration.
+ * The XML namespace is never included in the list. If the supplied array is larger than required,
+ * then the first unused entry will be set to -1.
+ * <p/>
+ * <p>For a node other than an element, the method returns null.</p>
+ */
+
+ /*@Nullable*/ public NamespaceBinding[] getDeclaredNamespaces(NamespaceBinding[] buffer) {
+ return null;
+ }
+
+ /**
+ * Get the typed value.
+ * @return the typed value. If requireSingleton is set to true, the result will always be an
+ * AtomicValue. In other cases it may be a Value representing a sequence whose items are atomic
+ * values.
+ * @since 8.5
+ */
+
+ /*@NotNull*/ public AtomicSequence atomize() throws XPathException {
+ return new UntypedAtomicValue(text);
+ }
+
+ /**
+ * Return the public identifier for the current document event.
+ * <p/>
+ * <p>The return value is the public identifier of the document
+ * entity or of the external parsed entity in which the markup that
+ * triggered the event appears.</p>
+ *
+ * @return A string containing the public identifier, or
+ * null if none is available.
+ * @see #getSystemId
+ */
+ /*@Nullable*/ public String getPublicId() {
+ return null;
+ }
+
+ /**
+ * Return the character position where the current document event ends.
+ * <p/>
+ * <p><strong>Warning:</strong> The return value from the method
+ * is intended only as an approximation for the sake of error
+ * reporting; it is not intended to provide sufficient information
+ * to edit the character content of the original XML document.</p>
+ * <p/>
+ * <p>The return value is an approximation of the column number
+ * in the document entity or external parsed entity where the
+ * markup that triggered the event appears.</p>
+ *
+ * @return The column number, or -1 if none is available.
+ * @see #getLineNumber
+ */
+ public int getColumnNumber() {
+ return -1;
+ }
+
+ /**
+ * Return an enumeration over the nodes reached by the given axis from this node
+ * @param axisNumber the axis to be iterated over
+ * @return a AxisIterator that scans the nodes reached by the axis in turn.
+ */
+
+ /*@NotNull*/ public AxisIterator iterateAxis(byte axisNumber) {
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR:
+ case AxisInfo.PARENT:
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ return SingleNodeIterator.makeIterator(TextFragmentValue.this);
+
+ case AxisInfo.ANCESTOR_OR_SELF:
+ NodeInfo[] nodes = {this, TextFragmentValue.this};
+ return new AxisIteratorOverSequence<NodeInfo>(new ArrayIterator<NodeInfo>(nodes));
+
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.CHILD:
+ case AxisInfo.DESCENDANT:
+ case AxisInfo.FOLLOWING:
+ case AxisInfo.FOLLOWING_SIBLING:
+ case AxisInfo.NAMESPACE:
+ case AxisInfo.PRECEDING:
+ case AxisInfo.PRECEDING_SIBLING:
+ return EmptyAxisIterator.emptyAxisIterator();
+
+ case AxisInfo.SELF:
+ case AxisInfo.DESCENDANT_OR_SELF:
+ return SingleNodeIterator.makeIterator(this);
+
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+
+ /**
+ * Return an enumeration over the nodes reached by the given axis from this node
+ * @param axisNumber the axis to be iterated over
+ * @param nodeTest A pattern to be matched by the returned nodes
+ * @return a AxisIterator that scans the nodes reached by the axis in turn.
+ */
+
+ /*@NotNull*/ public AxisIterator iterateAxis( byte axisNumber, /*@NotNull*/ NodeTest nodeTest) {
+ switch (axisNumber) {
+ case AxisInfo.ANCESTOR:
+ case AxisInfo.PARENT:
+ case AxisInfo.PRECEDING_OR_ANCESTOR:
+ return Navigator.filteredSingleton(TextFragmentValue.this, nodeTest);
+
+ case AxisInfo.ANCESTOR_OR_SELF:
+ boolean matchesDoc = nodeTest.matches(TextFragmentValue.this);
+ boolean matchesText = nodeTest.matches(this);
+ if (matchesDoc && matchesText) {
+ NodeInfo[] nodes = {this, TextFragmentValue.this};
+ return new AxisIteratorOverSequence<NodeInfo>(new ArrayIterator<NodeInfo>(nodes));
+ } else if (matchesDoc && !matchesText) {
+ return SingleNodeIterator.makeIterator(TextFragmentValue.this);
+ } else if (matchesText && !matchesDoc) {
+ return SingleNodeIterator.makeIterator(this);
+ } else {
+ return EmptyAxisIterator.emptyAxisIterator();
+ }
+
+ case AxisInfo.ATTRIBUTE:
+ case AxisInfo.CHILD:
+ case AxisInfo.DESCENDANT:
+ case AxisInfo.FOLLOWING:
+ case AxisInfo.FOLLOWING_SIBLING:
+ case AxisInfo.NAMESPACE:
+ case AxisInfo.PRECEDING:
+ case AxisInfo.PRECEDING_SIBLING:
+ return EmptyAxisIterator.emptyAxisIterator();
+
+ case AxisInfo.SELF:
+ case AxisInfo.DESCENDANT_OR_SELF:
+ return Navigator.filteredSingleton(this, nodeTest);
+
+ default:
+ throw new IllegalArgumentException("Unknown axis number " + axisNumber);
+ }
+ }
+
+ /**
+ * Find the parent node of this node.
+ * @return The Node object describing the containing element or root node.
+ */
+
+ /*@NotNull*/ public NodeInfo getParent() {
+ return TextFragmentValue.this;
+ }
+
+ /**
+ * Get the root node
+ * @return the NodeInfo representing the root of this tree
+ */
+
+ /*@NotNull*/ public NodeInfo getRoot() {
+ return TextFragmentValue.this;
+ }
+
+ /**
+ * Get the root (document) node
+ * @return the DocumentInfo representing the containing document
+ */
+
+ /*@NotNull*/ public DocumentInfo getDocumentRoot() {
+ return TextFragmentValue.this;
+ }
+
+ /**
+ * Copy the node to a given Outputter
+ */
+
+ public void copy(/*@NotNull*/ Receiver out, int copyOptions, int locationId)
+ throws XPathException {
+ out.characters(text, 0, 0);
+ }
+
+
+ /**
+ * Determine whether this node has the is-id property
+ *
+ * @return true if the node is an ID
+ */
+
+ public boolean isId() {
+ return false;
+ }
+
+ /**
+ * Determine whether this node has the is-idref property
+ *
+ * @return true if the node is an IDREF or IDREFS element or attribute
+ */
+
+ public boolean isIdref() {
+ return false;
+ }
+
+ /**
+ * Determine whether the node has the is-nilled property
+ *
+ * @return true if the node has the is-nilled property
+ */
+
+ public boolean isNilled() {
+ return false;
+ }
+ }
+
+ /**
+ * Set user data on the document node. The user data can be retrieved subsequently
+ * using {@link #getUserData}
+ * @param key A string giving the name of the property to be set. Clients are responsible
+ * for choosing a key that is likely to be unique. Must not be null. Keys used internally
+ * by Saxon are prefixed "saxon:".
+ * @param value The value to be set for the property. May be null, which effectively
+ * removes the existing value for the property.
+ */
+
+ public void setUserData(String key, /*@Nullable*/ Object value) {
+ if (userData == null) {
+ userData = new HashMap(4);
+ }
+ if (value == null) {
+ userData.remove(key);
+ } else {
+ userData.put(key, value);
+ }
+ }
+
+ /**
+ * Get user data held in the document node. This retrieves properties previously set using
+ * {@link #setUserData}
+ * @param key A string giving the name of the property to be retrieved.
+ * @return the value of the property, or null if the property has not been defined.
+ */
+
+ /*@Nullable*/ public Object getUserData(String key) {
+ if (userData == null) {
+ return null;
+ } else {
+ return userData.get(key);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/value/TimeValue.java b/sf/saxon/value/TimeValue.java
new file mode 100644
index 0000000..068e9dc
--- /dev/null
+++ b/sf/saxon/value/TimeValue.java
@@ -0,0 +1,649 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.ComparisonKey;
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.Err;
+import net.sf.saxon.trans.NoDynamicContextException;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * A value of type xs:time
+ */
+
+public final class TimeValue extends CalendarValue implements Comparable {
+
+ private byte hour;
+ private byte minute;
+ private byte second;
+ private int microsecond;
+
+ private TimeValue() {
+ }
+
+ /**
+ * Construct a time value given the hour, minute, second, and microsecond components.
+ * This constructor performs no validation.
+ *
+ * @param hour the hour value, 0-23
+ * @param minute the minutes value, 0-59
+ * @param second the seconds value, 0-59
+ * @param microsecond the number of microseconds, 0-999999
+ * @param tz the timezone displacement in minutes from UTC. Supply the value
+ * {@link CalendarValue#NO_TIMEZONE} if there is no timezone component.
+ */
+
+ public TimeValue(byte hour, byte minute, byte second, int microsecond, int tz) {
+ this.hour = hour;
+ this.minute = minute;
+ this.second = second;
+ this.microsecond = microsecond;
+ setTimezoneInMinutes(tz);
+ typeLabel = BuiltInAtomicType.TIME;
+ }
+
+ /**
+ * Constructor: create a time value given a Java calendar object
+ *
+ * @param calendar holds the date and time
+ * @param tz the timezone offset in minutes, or NO_TIMEZONE indicating that there is no timezone
+ */
+
+ public TimeValue(/*@NotNull*/ GregorianCalendar calendar, int tz) {
+ hour = (byte)(calendar.get(Calendar.HOUR_OF_DAY));
+ minute = (byte)(calendar.get(Calendar.MINUTE));
+ second = (byte)(calendar.get(Calendar.SECOND));
+ microsecond = calendar.get(Calendar.MILLISECOND) * 1000;
+ setTimezoneInMinutes(tz);
+ typeLabel = BuiltInAtomicType.TIME;
+ }
+
+ /**
+ * Static factory method: create a time value from a supplied string, in
+ * ISO 8601 format
+ *
+ * @param s the time in the lexical format hh:mm:ss[.ffffff] followed optionally by
+ * timezone in the form [+-]hh:mm or Z
+ * @return either a TimeValue corresponding to the xs:time, or a ValidationFailure
+ * if the supplied value was invalid
+ */
+
+ /*@NotNull*/ public static ConversionResult makeTimeValue(CharSequence s) {
+ // input must have format hh:mm:ss[.fff*][([+|-]hh:mm | Z)]
+ TimeValue tv = new TimeValue();
+ StringTokenizer tok = new StringTokenizer(Whitespace.trimWhitespace(s).toString(), "-:.+Z", true);
+ if (!tok.hasMoreElements()) {
+ return badTime("too short", s);
+ }
+ String part = (String)tok.nextElement();
+
+ if (part.length() != 2) {
+ return badTime("hour must be two digits", s);
+ }
+ int value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badTime("Non-numeric hour component", s);
+ }
+ tv.hour = (byte)value;
+ if (tv.hour > 24) {
+ return badTime("hour is out of range", s);
+ }
+ if (!tok.hasMoreElements()) {
+ return badTime("too short", s);
+ }
+ if (!":".equals(tok.nextElement())) {
+ return badTime("wrong delimiter after hour", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badTime("too short", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badTime("minute must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badTime("Non-numeric minute component", s);
+ }
+ tv.minute = (byte)value;
+ if (tv.minute > 59) {
+ return badTime("minute is out of range", s);
+ }
+ if (tv.hour == 24 && tv.minute != 0) {
+ return badTime("If hour is 24, minute must be 00", s);
+ }
+ if (!tok.hasMoreElements()) {
+ return badTime("too short", s);
+ }
+ if (!":".equals(tok.nextElement())) {
+ return badTime("wrong delimiter after minute", s);
+ }
+
+ if (!tok.hasMoreElements()) {
+ return badTime("too short", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badTime("second must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badTime("Non-numeric second component", s);
+ }
+ tv.second = (byte)value;
+ if (tv.second > 59) {
+ return badTime("second is out of range", s);
+ }
+ if (tv.hour == 24 && tv.second != 0) {
+ return badTime("If hour is 24, second must be 00", s);
+ }
+
+ int tz = 0;
+
+ int state = 0;
+ while (tok.hasMoreElements()) {
+ if (state == 9) {
+ return badTime("characters after the end", s);
+ }
+ String delim = (String)tok.nextElement();
+ if (".".equals(delim)) {
+ if (state != 0) {
+ return badTime("decimal separator occurs twice", s);
+ }
+ if (!tok.hasMoreElements()) {
+ return badTime("decimal point must be followed by digits", s);
+ }
+ part = (String)tok.nextElement();
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badTime("Non-numeric fractional seconds component", s);
+ }
+ double fractionalSeconds = Double.parseDouble('.' + part);
+ tv.microsecond = (int)(Math.round(fractionalSeconds * 1000000));
+ if (tv.hour == 24 && tv.microsecond != 0) {
+ return badTime("If hour is 24, fractional seconds must be 0", s);
+ }
+ state = 1;
+ } else if ("Z".equals(delim)) {
+ if (state > 1) {
+ return badTime("Z cannot occur here", s);
+ }
+ tz = 0;
+ state = 9; // we've finished
+ tv.setTimezoneInMinutes(0);
+ } else if ("+".equals(delim) || "-".equals(delim)) {
+ if (state > 1) {
+ return badTime(delim + " cannot occur here", s);
+ }
+ state = 2;
+ if (!tok.hasMoreElements()) {
+ return badTime("missing timezone", s);
+ }
+ part = (String)tok.nextElement();
+ if (part.length() != 2) {
+ return badTime("timezone hour must be two digits", s);
+ }
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badTime("Non-numeric timezone hour component", s);
+ }
+ tz = value * 60;
+ if (tz > 14 * 60) {
+ return badTime("timezone hour is out of range", s);
+ }
+ //if (tz > 12 * 60) return badTime("Because of Java limitations, Saxon currently limits the timezone to +/- 12 hours");
+ if ("-".equals(delim)) {
+ tz = -tz;
+ }
+ } else if (":".equals(delim)) {
+ if (state != 2) {
+ return badTime("colon cannot occur here", s);
+ }
+ state = 9;
+ part = (String)tok.nextElement();
+ value = DurationValue.simpleInteger(part);
+ if (value < 0) {
+ return badTime("Non-numeric timezone minute component", s);
+ }
+ int tzminute = value;
+ if (part.length() != 2) {
+ return badTime("timezone minute must be two digits", s);
+ }
+ if (tzminute > 59) {
+ return badTime("timezone minute is out of range", s);
+ }
+ if (tz < 0) {
+ tzminute = -tzminute;
+ }
+ tz += tzminute;
+ tv.setTimezoneInMinutes(tz);
+ } else {
+ return badTime("timezone format is incorrect", s);
+ }
+ }
+
+ if (state == 2 || state == 3) {
+ return badTime("timezone incomplete", s);
+ }
+
+ if (tv.hour == 24) {
+ tv.hour = 0;
+ }
+
+ tv.typeLabel = BuiltInAtomicType.TIME;
+ return tv;
+ }
+
+ /*@NotNull*/ private static ValidationFailure badTime(String msg, CharSequence value) {
+ ValidationFailure err = new ValidationFailure(
+ "Invalid time " + Err.wrap(value, Err.VALUE) + " (" + msg + ")");
+ err.setErrorCode("FORG0001");
+ return err;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ /*@NotNull*/ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.TIME;
+ }
+
+ /**
+ * Get the hour component, 0-23
+ *
+ * @return the hour
+ */
+
+ public byte getHour() {
+ return hour;
+ }
+
+ /**
+ * Get the minute component, 0-59
+ *
+ * @return the minute
+ */
+
+ public byte getMinute() {
+ return minute;
+ }
+
+ /**
+ * Get the second component, 0-59
+ *
+ * @return the second
+ */
+
+ public byte getSecond() {
+ return second;
+ }
+
+ /**
+ * Get the microsecond component, 0-999999
+ *
+ * @return the microseconds
+ */
+
+ public int getMicrosecond() {
+ return microsecond;
+ }
+
+
+ /**
+ * Convert to string
+ *
+ * @return ISO 8601 representation, in the localized timezone
+ * (the timezone held within the value).
+ */
+
+ /*@NotNull*/ public CharSequence getPrimitiveStringValue() {
+
+ FastStringBuffer sb = new FastStringBuffer(FastStringBuffer.TINY);
+
+ appendTwoDigits(sb, hour);
+ sb.append(':');
+ appendTwoDigits(sb, minute);
+ sb.append(':');
+ appendTwoDigits(sb, second);
+ if (microsecond != 0) {
+ sb.append('.');
+ int ms = microsecond;
+ int div = 100000;
+ while (ms > 0) {
+ int d = ms / div;
+ sb.append((char)(d + '0'));
+ ms = ms % div;
+ div /= 10;
+ }
+ }
+
+ if (hasTimezone()) {
+ appendTimezone(sb);
+ }
+
+ return sb;
+
+ }
+
+
+ /**
+ * Get the canonical lexical representation as defined in XML Schema. This is not always the same
+ * as the result of casting to a string according to the XPath rules. For an xs:time it is the
+ * time adjusted to UTC
+ *
+ * @return the canonical lexical representation if defined in XML Schema
+ */
+
+ public CharSequence getCanonicalLexicalRepresentation() {
+ if (hasTimezone() && getTimezoneInMinutes() != 0) {
+ return adjustTimezone(0).getStringValueCS();
+ } else {
+ return getStringValueCS();
+ }
+ }
+
+
+
+ /**
+ * Convert to a DateTime value. The date components represent a reference date, as defined
+ * in the spec for comparing times.
+ */
+
+ /*@NotNull*/ public DateTimeValue toDateTime() {
+ return new DateTimeValue(1972, (byte)12, (byte)31, hour, minute, second, microsecond, getTimezoneInMinutes(), true);
+ }
+
+ /**
+ * Get a Java Calendar object corresponding to this time, on a reference date
+ */
+
+ /*@NotNull*/ public GregorianCalendar getCalendar() {
+ // create a calendar using the specified timezone
+ int tz = (hasTimezone() ? getTimezoneInMinutes() : 0);
+ TimeZone zone = new SimpleTimeZone(tz * 60000, "LLL");
+ GregorianCalendar calendar = new GregorianCalendar(zone);
+ calendar.setLenient(false);
+
+ // use a reference date of 1972-12-31
+ int year = 1972;
+ int month = 11;
+ int day = 31;
+
+
+ calendar.set(year, month, day, hour, minute, second);
+ calendar.set(Calendar.MILLISECOND, microsecond / 1000);
+ calendar.set(Calendar.ZONE_OFFSET, tz * 60000);
+ calendar.set(Calendar.DST_OFFSET, 0);
+
+ calendar.getTime();
+ return calendar;
+ }
+
+ /**
+ * Make a copy of this time value,
+ * but with a different type label
+ *
+ * @param typeLabel the new type label. This must be a subtype of xs:time.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ TimeValue v = new TimeValue(hour, minute, second, microsecond, getTimezoneInMinutes());
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Return a new time with the same normalized value, but
+ * in a different timezone. This is called only for a TimeValue that has an explicit timezone
+ *
+ * @param timezone the new timezone offset, in minutes
+ * @return the time in the new timezone. This will be a new TimeValue unless no change
+ * was required to the original value
+ */
+
+ /*@NotNull*/ public CalendarValue adjustTimezone(int timezone) {
+ DateTimeValue dt = (DateTimeValue)toDateTime().adjustTimezone(timezone);
+ return new TimeValue(dt.getHour(), dt.getMinute(), dt.getSecond(),
+ dt.getMicrosecond(), dt.getTimezoneInMinutes());
+ }
+
+
+ /**
+ * Convert to Java object (for passing to external functions)
+ */
+
+// public Object convertAtomicToJava(Class target, XPathContext context) throws XPathException {
+// if (target.isAssignableFrom(TimeValue.class)) {
+// return this;
+// } else if (target == String.class) {
+// return getStringValue();
+// } else if (target == Object.class) {
+// return getStringValue();
+// } else {
+// Object o = super.convertSequenceToJava(target, context);
+// if (o == null) {
+// throw new XPathException("Conversion of time to " + target.getName() +
+// " is not supported");
+// }
+// return o;
+// }
+// }
+//
+ /**
+ * Get a component of the value. Returns null if the timezone component is
+ * requested and is not present.
+ */
+
+ /*@Nullable*/ public AtomicValue getComponent(int component) throws XPathException {
+ switch (component) {
+ case Component.HOURS:
+ return Int64Value.makeIntegerValue(hour);
+ case Component.MINUTES:
+ return Int64Value.makeIntegerValue(minute);
+ case Component.SECONDS:
+ BigDecimal d = BigDecimal.valueOf(microsecond);
+ d = d.divide(DecimalValue.BIG_DECIMAL_ONE_MILLION, 6, BigDecimal.ROUND_HALF_UP);
+ d = d.add(BigDecimal.valueOf(second));
+ return new DecimalValue(d);
+ case Component.WHOLE_SECONDS: //(internal use only)
+ return Int64Value.makeIntegerValue(second);
+ case Component.MICROSECONDS:
+ return new Int64Value(microsecond);
+ case Component.TIMEZONE:
+ if (hasTimezone()) {
+ return DayTimeDurationValue.fromMilliseconds(60000L * getTimezoneInMinutes());
+ } else {
+ return null;
+ }
+ default:
+ throw new IllegalArgumentException("Unknown component for time: " + component);
+ }
+ }
+
+ /**
+ * Get a Comparable value that implements the XPath ordering comparison semantics for this value.
+ * Returns null if the value is not comparable according to XPath rules. The default implementation
+ * returns null. This is overridden for types that allow ordered comparisons in XPath: numeric, boolean,
+ * string, date, time, dateTime, yearMonthDuration, dayTimeDuration, and anyURI.
+ * @param ordered true if an ordered comparison is required
+ * @param collator collation to be used for strings
+ * @param context XPath dynamic evaluation context
+ */
+
+// public Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context) {
+// return this;
+// }
+
+ /**
+ * Compare the value to another dateTime value
+ *
+ * @param other The other dateTime value
+ * @return negative value if this one is the earler, 0 if they are chronologically equal,
+ * positive value if this one is the later. For this purpose, dateTime values with an unknown
+ * timezone are considered to be UTC values (the Comparable interface requires
+ * a total ordering).
+ * @throws ClassCastException if the other value is not a TimeValue (the parameter
+ * is declared as Object to satisfy the Comparable interface)
+ */
+
+ public int compareTo(Object other) {
+ TimeValue otherTime = (TimeValue)other;
+ if (getTimezoneInMinutes() == otherTime.getTimezoneInMinutes()) {
+ if (hour != otherTime.hour) {
+ return IntegerValue.signum(hour - otherTime.hour);
+ } else if (minute != otherTime.minute) {
+ return IntegerValue.signum(minute - otherTime.minute);
+ } else if (second != otherTime.second) {
+ return IntegerValue.signum(second - otherTime.second);
+ } else if (microsecond != otherTime.microsecond) {
+ return IntegerValue.signum(microsecond - otherTime.microsecond);
+ } else {
+ return 0;
+ }
+ } else {
+ return toDateTime().compareTo(otherTime.toDateTime());
+ }
+ }
+
+ /**
+ * Compare the value to another dateTime value
+ *
+ * @param other The other dateTime value
+ * @param context the XPath dynamic evaluation context
+ * @return negative value if this one is the earler, 0 if they are chronologically equal,
+ * positive value if this one is the later. For this purpose, dateTime values with an unknown
+ * timezone are considered to be UTC values (the Comparable interface requires
+ * a total ordering).
+ * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
+ * is declared as Object to satisfy the Comparable interface)
+ * @throws NoDynamicContextException if the implicit timezone is required and is not available
+ * (because the function is called at compile time)
+ */
+
+ public int compareTo(/*@NotNull*/ CalendarValue other, /*@NotNull*/ XPathContext context) throws NoDynamicContextException {
+ if (!(other instanceof TimeValue)) {
+ throw new ClassCastException("Time values are not comparable to " + other.getClass());
+ }
+ TimeValue otherTime = (TimeValue)other;
+ if (getTimezoneInMinutes() == otherTime.getTimezoneInMinutes()) {
+ // The values have the same time zone, or neither has a timezone
+ return compareTo(other);
+ } else {
+ return toDateTime().compareTo(otherTime.toDateTime(), context);
+ }
+ }
+
+
+ /*@NotNull*/ public Comparable getSchemaComparable() {
+ return new TimeComparable();
+ }
+
+ private class TimeComparable implements Comparable {
+
+ /*@NotNull*/ public TimeValue asTimeValue() {
+ return TimeValue.this;
+ }
+ public int compareTo(/*@NotNull*/ Object o) {
+ if (o instanceof TimeComparable) {
+ DateTimeValue dt0 = asTimeValue().toDateTime();
+ DateTimeValue dt1 = ((TimeComparable)o).asTimeValue().toDateTime();
+ return dt0.getSchemaComparable().compareTo(dt1.getSchemaComparable());
+ } else {
+ return SequenceTool.INDETERMINATE_ORDERING;
+ }
+ }
+ public boolean equals(/*@NotNull*/ Object o) {
+ return compareTo(o) == 0;
+ }
+ public int hashCode() {
+ return TimeValue.this.toDateTime().getSchemaComparable().hashCode();
+ }
+ }
+
+ /**
+ * Get a comparison key for this value. Two values are equal if and only if they their comparison
+ * keys are equal
+ * @param context XPath dynamic context
+ * @throws NoDynamicContextException if the implicit timezone is required and is not available
+ */
+
+ /*@NotNull*/ public ComparisonKey getComparisonKey(/*@NotNull*/ XPathContext context) throws NoDynamicContextException {
+ return new ComparisonKey(StandardNames.XS_TIME, toDateTime().normalize(context));
+ }
+
+
+ public boolean equals(Object other) {
+ return other instanceof TimeValue && compareTo(other) == 0;
+ }
+
+ public int hashCode() {
+ return DateTimeValue.hashCode(
+ 1951, (byte)10, (byte)11, hour, minute, second, microsecond, getTimezoneInMinutes());
+ }
+
+ /**
+ * Add a duration to a dateTime
+ *
+ * @param duration the duration to be added (may be negative)
+ * @return the new date
+ * @throws net.sf.saxon.trans.XPathException
+ * if the duration is an xs:duration, as distinct from
+ * a subclass thereof
+ */
+
+ /*@NotNull*/ public CalendarValue add(/*@NotNull*/ DurationValue duration) throws XPathException {
+ if (duration instanceof DayTimeDurationValue) {
+ DateTimeValue dt = (DateTimeValue)toDateTime().add(duration);
+ return new TimeValue(dt.getHour(), dt.getMinute(), dt.getSecond(),
+ dt.getMicrosecond(), getTimezoneInMinutes());
+ } else {
+ XPathException err = new XPathException("Time+Duration arithmetic is supported only for xs:dayTimeDuration");
+ err.setErrorCode("XPTY0004");
+ err.setIsTypeError(true);
+ throw err;
+ }
+ }
+
+ /**
+ * Determine the difference between two points in time, as a duration
+ *
+ * @param other the other point in time
+ * @param context XPath dynamic evaluation context
+ * @return the duration as an xs:dayTimeDuration
+ * @throws XPathException for example if one value is a date and the other is a time
+ */
+
+ public DayTimeDurationValue subtract(/*@NotNull*/ CalendarValue other, XPathContext context) throws XPathException {
+ if (!(other instanceof TimeValue)) {
+ XPathException err = new XPathException("First operand of '-' is a time, but the second is not");
+ err.setIsTypeError(true);
+ throw err;
+ }
+ return super.subtract(other, context);
+ }
+
+
+}
+
diff --git a/sf/saxon/value/UntypedAtomicValue.java b/sf/saxon/value/UntypedAtomicValue.java
new file mode 100644
index 0000000..168c51a
--- /dev/null
+++ b/sf/saxon/value/UntypedAtomicValue.java
@@ -0,0 +1,116 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.expr.sort.CodepointCollator;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.type.*;
+
+/**
+* An Untyped Atomic value. This inherits from StringValue for implementation convenience, even
+* though an untypedAtomic value is not a String in the data model type hierarchy.
+*/
+
+public class UntypedAtomicValue extends StringValue {
+
+ /*@NotNull*/ public static final UntypedAtomicValue ZERO_LENGTH_UNTYPED =
+ new UntypedAtomicValue("");
+
+ // If the value is used once as a number, it's likely that it will be used
+ // repeatedly as a number, so we cache the result of conversion
+
+ /*@Nullable*/ DoubleValue doubleValue = null;
+
+ /**
+ * Constructor
+ * @param value the String value. Null is taken as equivalent to "".
+ */
+
+ public UntypedAtomicValue(/*@Nullable*/ CharSequence value) {
+ this.value = (value==null ? "" : value);
+ typeLabel = BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ UntypedAtomicValue v = new UntypedAtomicValue(value);
+ v.unicodeString = unicodeString;
+ v.doubleValue = doubleValue;
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ /*@NotNull*/ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.UNTYPED_ATOMIC;
+ }
+
+ /**
+ * Compare an untypedAtomic value with another value, using a given collator to perform
+ * any string comparisons. This works by converting the untypedAtomic value to the type
+ * of the other operand, which is the correct behavior for operators like "=" and "!=",
+ * but not for "eq" and "ne": in the latter case, the untypedAtomic value is converted
+ * to a string and this method is therefore not used.
+ * @param other the value to compare this value with
+ * @param collator the collation to be used for comparing strings
+ * @param context the XPath dynamic context
+ *
+ * @return -1 if the this value is less than the other, 0 if they are equal, +1 if this
+ * value is greater.
+ * @throws ClassCastException if the value cannot be cast to the type of the other operand
+ */
+
+ public int compareTo(/*@NotNull*/ AtomicValue other, /*@NotNull*/ StringCollator collator, /*@NotNull*/ XPathContext context) {
+ if (other instanceof NumericValue) {
+ if (doubleValue == null) {
+ try {
+ doubleValue = new DoubleValue(context.getConfiguration().getConversionRules()
+ .getStringToDoubleConverter().stringToNumber(value));
+ } catch (NumberFormatException e) {
+ throw new ClassCastException("Cannot convert untyped atomic value '" + getStringValue()
+ + "' to a double ");
+ }
+ }
+ return doubleValue.compareTo(other);
+ } else if (other instanceof StringValue) {
+ if (collator instanceof CodepointCollator) {
+ // This optimization avoids creating String objects for the purpose of the comparison
+ return ((CodepointCollator)collator).compareCS(getStringValueCS(),
+ other.getStringValueCS());
+ } else {
+ return collator.compareStrings(getStringValue(), other.getStringValue());
+ }
+ } else {
+ final Configuration config = context.getConfiguration();
+ Converter converter = config.getConversionRules().getConverter(BuiltInAtomicType.UNTYPED_ATOMIC, (AtomicType) other.getItemType());
+ ConversionResult result = converter.convert(this);
+ if (result instanceof ValidationFailure) {
+ throw new ClassCastException("Cannot convert untyped atomic value '" + getStringValue()
+ + "' to type " + other.getItemType());
+ }
+ return ((Comparable) result).compareTo(other);
+
+ }
+ }
+
+}
+
diff --git a/sf/saxon/value/Whitespace.java b/sf/saxon/value/Whitespace.java
new file mode 100644
index 0000000..1bf9e14
--- /dev/null
+++ b/sf/saxon/value/Whitespace.java
@@ -0,0 +1,330 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.tree.tiny.CompressedWhitespace;
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+/**
+ * This class provides helper methods and constants for handling whitespace
+ */
+public class Whitespace {
+
+ private Whitespace() {}
+
+
+ /**
+ * The values PRESERVE, REPLACE, and COLLAPSE represent the three options for whitespace
+ * normalization. They are deliberately chosen in ascending strength order; given a number
+ * of whitespace facets, only the strongest needs to be carried out. The option TRIM is
+ * used instead of COLLAPSE when all valid values have no interior whitespace; trimming
+ * leading and trailing whitespace is then equivalent to the action of COLLAPSE, but faster.
+ */
+
+ public static final int PRESERVE = 0;
+ public static final int REPLACE = 1;
+ public static final int COLLAPSE = 2;
+ public static final int TRIM = 3;
+
+ /**
+ * The values NONE, IGNORABLE, and ALL identify which kinds of whitespace text node
+ * should be stripped when building a source tree. UNSPECIFIED indicates that no
+ * particular request has been made. XSLT indicates that whitespace should be stripped
+ * as defined by the xsl:strip-space and xsl:preserve-space declarations in the stylesheet
+ */
+
+ public static final int NONE = 0;
+ public static final int IGNORABLE = 1;
+ public static final int ALL = 2;
+ public static final int UNSPECIFIED = 3;
+ public static final int XSLT = 4;
+
+ /**
+ * Test whether a character is whitespace
+ * @param ch the character (Unicode codepoint) to be tested
+ * @return true if the character is one of tab, newline, carriage return, or space
+ */
+
+ public static boolean isWhitespace(int ch) {
+ switch (ch) {
+ case 9:
+ case 10:
+ case 13:
+ case 32:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Apply schema-defined whitespace normalization to a string
+ * @param action the action to be applied: one of PRESERVE, REPLACE, or COLLAPSE
+ * @param value the value to be normalized
+ * @return the value after normalization
+ */
+
+ public static CharSequence applyWhitespaceNormalization(int action, /*@NotNull*/ CharSequence value) {
+ switch (action) {
+ case PRESERVE:
+ return value;
+ case REPLACE:
+ FastStringBuffer sb = new FastStringBuffer(value.length());
+ for (int i=0; i<value.length(); i++) {
+ char c = value.charAt(i);
+ switch (c) {
+ case '\n':
+ case '\r':
+ case '\t':
+ sb.append(' ');
+ break;
+ default:
+ sb.append(c);
+ }
+ }
+ return sb;
+ case COLLAPSE:
+ return collapseWhitespace(value);
+ case TRIM:
+ return trimWhitespace(value);
+ default:
+ throw new IllegalArgumentException("Unknown whitespace facet value");
+ }
+ }
+
+ /**
+ * Remove all whitespace characters from a string
+ * @param value the string from which whitespace is to be removed
+ * @return the string without its whitespace. This may be the original value
+ * if it contained no whitespace
+ */
+
+ /*@NotNull*/ public static CharSequence removeAllWhitespace(/*@NotNull*/ CharSequence value) {
+ if (containsWhitespace(value)) {
+ FastStringBuffer sb = new FastStringBuffer(value.length());
+ for (int i=0; i<value.length(); i++) {
+ char c = value.charAt(i);
+ if (c > 32 || !C0WHITE[c]) {
+ sb.append(c);
+ }
+ }
+ return sb;
+ } else {
+ return value;
+ }
+ }
+
+ /**
+ * Remove leading whitespace characters from a string
+ * @param value the string whose leading whitespace is to be removed
+ * @return the string with leading whitespace removed. This may be the
+ * original string if there was no leading whitespace
+ */
+
+ public static CharSequence removeLeadingWhitespace(/*@NotNull*/ CharSequence value) {
+ int start = -1;
+ final int len = value.length();
+ for (int i=0; i<len; i++) {
+ char c = value.charAt(i);
+ if (c > 32 || !C0WHITE[c]) {
+ start = i;
+ break;
+ }
+ }
+ if (start == 0) {
+ return value;
+ } else if (start < 0 || start == len - 1) {
+ return "";
+ } else {
+ return value.subSequence(start, len);
+ }
+ }
+
+ /**
+ * Determine if a string contains any whitespace
+ * @param value the string to be tested
+ * @return true if the string contains a character that is XML whitespace, that is
+ * tab, newline, carriage return, or space
+ */
+
+ public static boolean containsWhitespace(/*@NotNull*/ CharSequence value) {
+ final int len = value.length();
+ for (int i=0; i<len; ) {
+ char c = value.charAt(i++);
+ if (c <= 32 && C0WHITE[c]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine if a string is all-whitespace
+ *
+ * @param content the string to be tested
+ * @return true if the supplied string contains no non-whitespace
+ * characters
+ */
+
+ public static boolean isWhite(/*@NotNull*/ CharSequence content) {
+ if (content instanceof CompressedWhitespace) {
+ return true;
+ }
+ final int len = content.length();
+ for (int i=0; i<len;) {
+ // all valid XML 1.0 whitespace characters, and only whitespace characters, are <= 0x20
+ // But XML 1.1 allows non-white characters that are also < 0x20, so we need a specific test for these
+ char c = content.charAt(i++);
+ if (c > 32 || !C0WHITE[c]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*@NotNull*/ private static boolean[] C0WHITE = {
+ false, false, false, false, false, false, false, false, // 0-7
+ false, true, true, false, false, true, false, false, // 8-15
+ false, false, false, false, false, false, false, false, // 16-23
+ false, false, false, false, false, false, false, false, // 24-31
+ true // 32
+ };
+
+ /**
+ * Determine if a character is whitespace
+ * @param c the character to be tested
+ * @return true if the character is a whitespace character
+ */
+
+ public final static boolean isWhite(char c) {
+ return c<=32 && C0WHITE[c];
+ }
+
+ /**
+ * Normalize whitespace as defined in XML Schema. Note that this is not the same
+ * as the XPath normalize-space() function, which is supported by the
+ * {@link #collapseWhitespace} method
+ * @param in the string to be normalized
+ * @return a copy of the string in which any whitespace character is replaced by
+ * a single space character
+ */
+
+ /*@NotNull*/ public static CharSequence normalizeWhitespace(/*@NotNull*/ CharSequence in) {
+ FastStringBuffer sb = new FastStringBuffer(in.length());
+ for (int i=0; i<in.length(); i++) {
+ char c = in.charAt(i);
+ switch (c) {
+ case '\n':
+ case '\r':
+ case '\t':
+ sb.append(' ');
+ break;
+ default:
+ sb.append(c);
+ break;
+ }
+ }
+ return sb;
+ }
+
+ /**
+ * Collapse whitespace as defined in XML Schema. This is equivalent to the
+ * XPath normalize-space() function
+ * @param in the string whose whitespace is to be collapsed
+ * @return the string with any leading or trailing whitespace removed, and any
+ * internal sequence of whitespace characters replaced with a single space character.
+ */
+
+ /*@NotNull*/ public static CharSequence collapseWhitespace(/*@NotNull*/ CharSequence in) {
+ int len = in.length();
+ if (len==0 || !containsWhitespace(in)) {
+ return in;
+ }
+
+ FastStringBuffer sb = new FastStringBuffer(len);
+ boolean inWhitespace = true;
+ int i = 0;
+ for (; i<len; i++) {
+ char c = in.charAt(i);
+ switch (c) {
+ case '\n':
+ case '\r':
+ case '\t':
+ case ' ':
+ if (inWhitespace) {
+ // remove the whitespace
+ } else {
+ sb.append(' ');
+ inWhitespace = true;
+ }
+ break;
+ default:
+ sb.append(c);
+ inWhitespace = false;
+ break;
+ }
+ }
+ int nlen = sb.length();
+ if (nlen>0 && sb.charAt(nlen-1)==' ') {
+ sb.setLength(nlen-1);
+ }
+ return sb;
+ }
+
+ /**
+ * Remove leading and trailing whitespace. This has the same effect as collapseWhitespace,
+ * but is cheaper, for use by data types that do not allow internal whitespace.
+ * @param in the input string whose whitespace is to be removed
+ * @return the result of removing excess whitespace
+ */
+ public static CharSequence trimWhitespace(/*@NotNull*/ CharSequence in) {
+ if (in.length()==0) {
+ return in;
+ }
+ int first = 0;
+ int last = in.length()-1;
+ while (true) {
+ final char x = in.charAt(first);
+ if (x > 32 || !C0WHITE[x]) {
+ break;
+ }
+ if (first++ >= last) {
+ return "";
+ }
+ }
+ while (true) {
+ final char x = in.charAt(last);
+ if (x > 32 || !C0WHITE[x]) {
+ break;
+ }
+ last--;
+ }
+ if (first == 0 && last == in.length()-1) {
+ return in;
+ } else {
+ return in.subSequence(first, last+1);
+ }
+ }
+
+ /**
+ * Trim leading and trailing whitespace from a string, returning a string.
+ * This differs from the Java trim() method in that the only characters treated as
+ * whitespace are space, \n, \r, and \t. The String#trim() method removes all C0
+ * control characters (which is not the same thing under XML 1.1).
+ * @param s the string to be trimmed. If null is supplied, null is returned.
+ * @return the string with leading and trailing whitespace removed.
+ * Returns null if and only if the supplied argument is null.
+ */
+
+ /*@Nullable*/ public static String trim(/*@Nullable*/ CharSequence s) {
+ if (s == null) {
+ return null;
+ }
+ return trimWhitespace(s).toString();
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/value/YearMonthDurationValue.java b/sf/saxon/value/YearMonthDurationValue.java
new file mode 100644
index 0000000..65fe852
--- /dev/null
+++ b/sf/saxon/value/YearMonthDurationValue.java
@@ -0,0 +1,251 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.value;
+
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.lib.StringCollator;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.util.FastStringBuffer;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ConversionResult;
+import net.sf.saxon.type.ValidationFailure;
+
+import java.math.BigDecimal;
+
+/**
+ * A value of type xs:yearMonthDuration
+ */
+
+public final class YearMonthDurationValue extends DurationValue implements Comparable {
+
+ /**
+ * Private constructor for internal use
+ */
+
+ private YearMonthDurationValue() {
+ typeLabel = BuiltInAtomicType.YEAR_MONTH_DURATION;
+ }
+
+ /**
+ * Static factory: create a year-month duration value from a supplied string, in
+ * ISO 8601 format [+|-]PnYnM
+ *
+ * @param s a string in the lexical space of xs:yearMonthDuration.
+ * @return either a YearMonthDurationValue, or a ValidationFailure if the string was
+ * not in the lexical space of xs:yearMonthDuration.
+ */
+
+ public static ConversionResult makeYearMonthDurationValue(CharSequence s) {
+ ConversionResult d = DurationValue.makeDuration(s, true, false);
+ if (d instanceof ValidationFailure) {
+ return d;
+ }
+ DurationValue dv = (DurationValue)d;
+ return YearMonthDurationValue.fromMonths((dv.getYears()*12 + dv.getMonths()) * dv.signum());
+ }
+
+ /**
+ * Create a copy of this atomic value, with a different type label
+ *
+ * @param typeLabel the type label of the new copy. The caller is responsible for checking that
+ * the value actually conforms to this type.
+ */
+
+ /*@NotNull*/ public AtomicValue copyAsSubType(AtomicType typeLabel) {
+ YearMonthDurationValue v = YearMonthDurationValue.fromMonths(getLengthInMonths());
+ v.typeLabel = typeLabel;
+ return v;
+ }
+
+ /**
+ * Determine the primitive type of the value. This delivers the same answer as
+ * getItemType().getPrimitiveItemType(). The primitive types are
+ * the 19 primitive types of XML Schema, plus xs:integer, xs:dayTimeDuration and xs:yearMonthDuration,
+ * and xs:untypedAtomic. For external objects, the result is AnyAtomicType.
+ */
+
+ public BuiltInAtomicType getPrimitiveType() {
+ return BuiltInAtomicType.YEAR_MONTH_DURATION;
+ }
+
+ /**
+ * Convert to string
+ *
+ * @return ISO 8601 representation.
+ */
+
+ public CharSequence getPrimitiveStringValue() {
+
+ // The canonical representation has months in the range 0-11
+
+ int y = getYears();
+ int m = getMonths();
+
+ FastStringBuffer sb = new FastStringBuffer(32);
+ if (negative) {
+ sb.append('-');
+ }
+ sb.append('P');
+ if (y != 0) {
+ sb.append(y + "Y");
+ }
+ if (m != 0 || y == 0) {
+ sb.append(m + "M");
+ }
+ return sb;
+
+ }
+
+ /**
+ * Get the number of months in the duration
+ *
+ * @return the number of months in the duration
+ */
+
+ public int getLengthInMonths() {
+ return (months) * (negative ? -1 : +1);
+ }
+
+ /**
+ * Construct a duration value as a number of months.
+ *
+ * @param months the number of months (may be negative)
+ * @return the corresponding xs:yearMonthDuration value
+ */
+
+ public static YearMonthDurationValue fromMonths(int months) {
+ YearMonthDurationValue mdv = new YearMonthDurationValue();
+ mdv.negative = (months < 0);
+ mdv.months = (months < 0 ? -months : months);
+ mdv.seconds = 0;
+ mdv.microseconds = 0;
+ return mdv;
+ }
+
+ /**
+ * Multiply duration by a number. Also used when dividing a duration by a number
+ */
+
+ public DurationValue multiply(double n) throws XPathException {
+ if (Double.isNaN(n)) {
+ XPathException err = new XPathException("Cannot multiply/divide a duration by NaN");
+ err.setErrorCode("FOCA0005");
+ throw err;
+ }
+ double m = (double)getLengthInMonths();
+ double product = n * m;
+ if (Double.isInfinite(product) || product > Integer.MAX_VALUE || product < Integer.MIN_VALUE) {
+ XPathException err = new XPathException("Overflow when multiplying/dividing a duration by a number");
+ err.setErrorCode("FODT0002");
+ throw err;
+ }
+ return fromMonths((int)Math.round(product));
+ }
+
+ /**
+ * Find the ratio between two durations
+ *
+ * @param other the dividend
+ * @return the ratio, as a decimal
+ * @throws XPathException
+ */
+
+ public DecimalValue divide(DurationValue other) throws XPathException {
+ if (other instanceof YearMonthDurationValue) {
+ BigDecimal v1 = BigDecimal.valueOf(getLengthInMonths());
+ BigDecimal v2 = BigDecimal.valueOf(((YearMonthDurationValue)other).getLengthInMonths());
+ if (v2.signum() == 0) {
+ XPathException err = new XPathException("Divide by zero (durations)");
+ err.setErrorCode("FOAR0001");
+ throw err;
+ }
+ return new DecimalValue(v1.divide(v2, 20, BigDecimal.ROUND_HALF_EVEN));
+ } else {
+ XPathException err = new XPathException("Cannot divide two durations of different type");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+ }
+
+ /**
+ * Add two year-month-durations
+ */
+
+ public DurationValue add(DurationValue other) throws XPathException {
+ if (other instanceof YearMonthDurationValue) {
+ return fromMonths(getLengthInMonths() +
+ ((YearMonthDurationValue)other).getLengthInMonths());
+ } else {
+ XPathException err = new XPathException("Cannot add two durations of different type");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+ }
+
+ /**
+ * Subtract two year-month-durations
+ */
+
+ public DurationValue subtract(DurationValue other) throws XPathException {
+ if (other instanceof YearMonthDurationValue) {
+ return fromMonths(getLengthInMonths() -
+ ((YearMonthDurationValue)other).getLengthInMonths());
+ } else {
+ XPathException err = new XPathException("Cannot subtract two durations of different type");
+ err.setErrorCode("XPTY0004");
+ throw err;
+ }
+ }
+
+ /**
+ * Negate a duration (same as subtracting from zero, but it preserves the type of the original duration)
+ */
+
+ public DurationValue negate() {
+ return fromMonths(-getLengthInMonths());
+ }
+
+ /**
+ * Compare the value to another duration value
+ *
+ * @param other The other dateTime value
+ * @return negative value if this one is the earler, 0 if they are chronologically equal,
+ * positive value if this one is the later. For this purpose, dateTime values with an unknown
+ * timezone are considered to be UTC values (the Comparable interface requires
+ * a total ordering).
+ * @throws ClassCastException if the other value is not a DateTimeValue (the parameter
+ * is declared as Object to satisfy the Comparable interface)
+ */
+
+ public int compareTo(Object other) {
+ if (other instanceof YearMonthDurationValue) {
+ return getLengthInMonths() - ((YearMonthDurationValue)other).getLengthInMonths();
+ } else {
+ throw new ClassCastException("Cannot compare a yearMonthDuration to an object of class "
+ + other.getClass());
+ }
+ }
+
+ /**
+ * Get a Comparable value that implements the XPath ordering comparison semantics for this value.
+ * Returns null if the value is not comparable according to XPath rules. The default implementation
+ * returns the value itself. This is modified for types such as
+ * xs:duration which allow ordering comparisons in XML Schema, but not in XPath.
+ * @param ordered
+ * @param collator
+ * @param context
+ */
+
+ public Object getXPathComparable(boolean ordered, StringCollator collator, XPathContext context) {
+ return this;
+ }
+
+
+}
+
diff --git a/sf/saxon/value/package.html b/sf/saxon/value/package.html
new file mode 100644
index 0000000..8cd3932
--- /dev/null
+++ b/sf/saxon/value/package.html
@@ -0,0 +1,51 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.value</title>
+</head>
+
+<body>
+
+<p>This package provides classes representing XPath values (that is, the results of
+an expression). There are different classes for different types of value.</p>
+
+<p>The principal class is:</p>
+
+<p><b>Value</b>: <br>
+This represents the result of evaluating an expression. But a Value is also an expression in its
+own right, reflecting the fact that literal values can be used syntactically wherever expressions
+can be used. <code>AtomicValue</code> is a subclass of <code>Value</code>
+that represents an atomic value: this in turn has subclasses for the different built-in data types:
+StringValue, NumericValue, BooleanValue, DateValue, and so on.</p>
+
+<p>There are two classes used to represent integer values: <code>IntegerValue</code> for integers that
+fit comfortably in 64 bits, and <code>BigIntegerValue</code> for anything longer. Built-in subtypes
+of xs:integer, such as <code>xs:int</code> and <code>xs:short</code>, are always represented using
+an <code>IntegerValue</code>, except for <code>xs:unsignedLong</code>, which uses a <code>BigIntegerValue</code>.</p>
+
+<p>In general a value is a sequence. A sequence that is instantiated in memory is represented by a
+<code>SequenceExtent</code> object. The code tries to pipeline execution so that a <code>SequenceExtent</code>
+is created as rarely as possible.</p>
+
+<p>When an expression is evaluated lazily, the result is a <code>Closure</code>. A Closure contains the original
+expression, and a copy of the run-time context at the time evaluation was requested: that is, all the local variables
+and other information that it depends on, such as the context item. There are two kinds of Closure, and the system
+decides at compile time which to use. An ordinary Closure re-evaluates the expression every time the value is referenced;
+this is used only when the compiler decides that the value is likely to be referenced only once. A MemoClosure remembers
+the value the first time it is needed, so that subsequent references do not cause repeated evaluation of the
+expression.</p>
+
+<hr>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+9 February 2005</i></p>
+</body>
+</html>
diff --git a/sf/saxon/xpath/JAXPVariable.java b/sf/saxon/xpath/JAXPVariable.java
new file mode 100644
index 0000000..a3ca065
--- /dev/null
+++ b/sf/saxon/xpath/JAXPVariable.java
@@ -0,0 +1,155 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Binding;
+import net.sf.saxon.expr.BindingReference;
+import net.sf.saxon.expr.JPConverter;
+import net.sf.saxon.expr.XPathContext;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.EmptySequence;
+import net.sf.saxon.value.IntegerValue;
+import net.sf.saxon.value.QNameValue;
+import net.sf.saxon.value.SequenceType;
+
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathVariableResolver;
+
+
+/**
+* An object representing an XPath variable for use in the JAXP XPath API. The object
+ * is created at compile time when the parser tries to bind a variable reference; the
+ * value is fetched at run-time from the XPathVariableResolver. With this interface,
+ * there is no way of reporting a static error if the variable has not been declared.
+ * <p>
+ * In Saxon terms, this class is both a VariableDeclaration and a Binding. Unlike
+ * a normal VariableDeclaration, it isn't created in advance, but is created on demand
+ * when the parser encounters a variable reference. This actually means that if the
+ * XPath expression contains two references to the same variable, two VariableDeclarations
+ * will be created; however, they will be indistinguishable to the VariableResolver.
+ * Acting as a VariableDeclaration, the object goes through the motions of fixing up
+ * a binding to a variable reference (in practice, of course, there is exactly one
+ * reference to the variable). Acting as a run-time binding, it then evaluates the
+ * variable by calling the XPathVariableResolver supplied by the API caller. If no
+ * XPathVariableResolver was supplied, an error is reported when a variable is encountered;
+ * but if the variable resolver doesn't recognize the variable name, it returns null,
+ * which is treated as an empty sequence.
+ * </p>
+*/
+
+public final class JAXPVariable implements Binding {
+
+ private StructuredQName name;
+ private XPathVariableResolver resolver;
+
+ /**
+ * Private constructor: for use only be the protected factory method make()
+ * @param name the name of the variable
+ * @param resolver the resolver used in conjunction with this variable
+ */
+
+ protected JAXPVariable(StructuredQName name, XPathVariableResolver resolver) {
+ this.name = name;
+ this.resolver = resolver;
+ }
+
+
+ public SequenceType getRequiredType() {
+ return SequenceType.ANY_SEQUENCE;
+ }
+
+ /**
+ * Indicate whether the binding is local or global. A global binding is one that has a fixed
+ * value for the life of a query or transformation; any other binding is local.
+ */
+
+ public boolean isGlobal() {
+ return true;
+ }
+
+ /**
+ * Test whether it is permitted to assign to the variable using the saxon:assign
+ * extension element. This will only be for an XSLT global variable where the extra
+ * attribute saxon:assignable="yes" is present.
+ */
+
+ public final boolean isAssignable() {
+ return false;
+ }
+
+ /**
+ * If this is a local variable held on the local stack frame, return the corresponding slot number.
+ * In other cases, return -1.
+ */
+
+ public int getLocalSlotNumber() {
+ return -1;
+ }
+
+ /**
+ * Get the name of the variable as a structured QName
+ */
+
+ public StructuredQName getVariableQName() {
+ return name;
+ }
+
+ public void addReference(boolean isLoopingReference) {
+
+ }
+
+ /**
+ * Method called by the XPath expression parser to register a reference to this variable.
+ * This method should not be called by users of the API.
+ */
+
+ public void registerReference(/*@NotNull*/ BindingReference ref) {
+ ref.setStaticType(SequenceType.ANY_SEQUENCE, null, 0);
+ ref.fixup(this);
+ }
+
+ /**
+ * Get the value of the variable. This method is used by the XPath execution engine
+ * to retrieve the value.
+ * @param context The dynamic evaluation context
+ * @return The value of the variable
+ */
+
+ public Sequence evaluateVariable(/*@NotNull*/ XPathContext context) throws XPathException {
+ Configuration config = context.getConfiguration();
+ Object value = resolver.resolveVariable(name.toJaxpQName());
+ if (value == null) {
+ return EmptySequence.getInstance();
+ }
+ JPConverter converter = JPConverter.allocate(value.getClass(), config);
+ return converter.convert(value, context);
+ //return Value.convertJavaObjectToXPath(value, SequenceType.ANY_SEQUENCE, context);
+ }
+
+ /**
+ * If the variable is bound to an integer, get the minimum and maximum possible values.
+ * Return null if unknown or not applicable
+ */
+ /*@Nullable*/ public IntegerValue[] getIntegerBoundsForVariable() {
+ return null;
+ }
+
+ /**
+ * Construct a JAXP QName from a Saxon QNameValue
+ * @param in the Saxon QNameValue
+ * @return the JAXP QName
+ */
+
+ /*@NotNull*/ QName makeQName(/*@NotNull*/ QNameValue in) {
+ return new QName(in.getNamespaceURI(), in.getLocalName(), in.getPrefix());
+ }
+}
+
diff --git a/sf/saxon/xpath/JAXPXPathStaticContext.java b/sf/saxon/xpath/JAXPXPathStaticContext.java
new file mode 100644
index 0000000..cdcc96e
--- /dev/null
+++ b/sf/saxon/xpath/JAXPXPathStaticContext.java
@@ -0,0 +1,326 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.expr.VariableReference;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.NamespaceResolver;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.sxpath.AbstractStaticContext;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.SchemaException;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.transform.Source;
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathVariableResolver;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+* A JAXPXPathStaticContext provides a context for parsing an XPath expression
+* in a context other than a stylesheet. In particular, it is used to support
+* the JAXP 1.3 XPath API. The JAXP API does not actually expose the StaticContext
+* object directly; rather, the static context (namespaces, variables, and functions)
+ * is manipulated through the XPath object, implemented in Saxon by the {@link XPathEvaluator}
+*/
+
+public class JAXPXPathStaticContext extends AbstractStaticContext
+ implements StaticContext, NamespaceResolver {
+
+ private SlotManager stackFrameMap;
+ private XPathFunctionLibrary xpathFunctionLibrary;
+
+ private NamespaceContext namespaceContext = new MinimalNamespaceContext();
+ private XPathVariableResolver variableResolver;
+
+ /**
+ * Create a JAXPXPathStaticContext using a specific Configuration.
+ * @param config the Configuration. For schema-aware XPath expressions, this must be an EnterpriseConfiguration.
+ */
+
+ public JAXPXPathStaticContext(/*@NotNull*/ Configuration config) {
+ setConfiguration(config);
+ stackFrameMap = config.makeSlotManager();
+ setDefaultFunctionLibrary();
+ xpathFunctionLibrary = new XPathFunctionLibrary();
+ addFunctionLibrary(xpathFunctionLibrary);
+ }
+
+ /**
+ * Get the granularity of the container.
+ * @return 0 for a temporary container created during parsing; 1 for a container
+ * that operates at the level of an XPath expression; 2 for a container at the level
+ * of a global function or template
+ */
+
+ public int getContainerGranularity() {
+ return 1;
+ }
+
+ /**
+ * Supply the NamespaceContext used to resolve namespaces.
+ */
+
+ public void setNamespaceContext(NamespaceContext context) {
+ this.namespaceContext = context;
+ }
+
+ /**
+ * Get the NamespaceContext that was set using {@link #setNamespaceContext}
+ */
+
+ public NamespaceContext getNamespaceContext() {
+ return namespaceContext;
+ }
+
+ /**
+ * Get the stack frame map containing the slot number allocations for the variables declared
+ * in this static context
+ */
+
+ public SlotManager getStackFrameMap() {
+ return stackFrameMap;
+ }
+
+ /**
+ * Set an XPathVariableResolver. This is used to resolve variable references
+ * if no variable has been explicitly declared.
+ * @param resolver A JAXP 1.3 XPathVariableResolver
+ */
+
+ public void setXPathVariableResolver(XPathVariableResolver resolver) {
+ this.variableResolver = resolver;
+ }
+
+ /**
+ * Get the XPathVariableResolver
+ */
+
+ public XPathVariableResolver getXPathVariableResolver() {
+ return variableResolver;
+ }
+
+ public void setXPathFunctionResolver(XPathFunctionResolver xPathFunctionResolver) {
+ if (xpathFunctionLibrary != null) {
+ xpathFunctionLibrary.setXPathFunctionResolver(xPathFunctionResolver);
+ }
+ }
+
+ /*@Nullable*/ public XPathFunctionResolver getXPathFunctionResolver() {
+ if (xpathFunctionLibrary != null) {
+ return xpathFunctionLibrary.getXPathFunctionResolver();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the URI for a prefix, using the declared namespaces as
+ * the context for namespace resolution. The default namespace is NOT used
+ * when the prefix is empty.
+ * This method is provided for use by the XPath parser.
+ * @param prefix The prefix
+ * @throws net.sf.saxon.trans.XPathException if the prefix is not declared
+ */
+
+ public String getURIForPrefix(/*@NotNull*/ String prefix) throws XPathException {
+ String uri = getURIForPrefix(prefix, false);
+ if (uri==null) {
+ throw new XPathException("Prefix " + prefix + " has not been declared", "XPST0081");
+ }
+ return uri;
+ }
+
+ /*@NotNull*/ public NamespaceResolver getNamespaceResolver() {
+ return this;
+ }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope. This method searches
+ * any namespace context supplied using {@link #setNamespaceContext(javax.xml.namespace.NamespaceContext)}.
+ * @param prefix the namespace prefix
+ * @param useDefault true if the default namespace for elements and types is to be used when the
+ * prefix is ""
+ * @return the uri for the namespace, or null if the prefix is not in scope.
+ * Return "" if the prefix maps to the null namespace.
+ */
+
+ public String getURIForPrefix(/*@NotNull*/ String prefix, boolean useDefault) {
+ if (prefix.length()==0) {
+ if (useDefault) {
+ return getDefaultElementNamespace();
+ } else {
+ return NamespaceConstant.NULL;
+ }
+ } else {
+ return namespaceContext.getNamespaceURI(prefix);
+ }
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This method is implemented
+ * only in the case where the NamespaceContext supplied using {@link #setNamespaceContext} is an
+ * instance of Saxon's {@link NamespaceResolver} class. In other cases the method throws an
+ * UnsupportedOperationException
+ * @return an iterator over all the inscope namespace prefixes, if available
+ * @throws UnsupportedOperationException if the NamespaceContext object is not a NamespaceResolver.
+ */
+
+ public Iterator<String> iteratePrefixes() {
+ if (namespaceContext instanceof NamespaceResolver) {
+ return ((NamespaceResolver)namespaceContext).iteratePrefixes();
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ /**
+ * Bind a variable used in an XPath Expression to the XSLVariable element in which it is declared.
+ * This method is provided for use by the XPath parser, and it should not be called by the user of
+ * the API.
+ *
+ * @throws XPathException if no VariableResolver has been supplied.
+ * @param qName
+ */
+
+ /*@NotNull*/ public final Expression bindVariable(StructuredQName qName) throws XPathException {
+ // bindVariable is called at compile time, but the JAXP variable resolver
+ // is designed to be called at run time. So we need to create a variable now,
+ // which will call the variableResolver when called upon to return the run-time value
+ if (variableResolver != null) {
+ return new VariableReference(new JAXPVariable(qName, variableResolver));
+ } else {
+ throw new XPathException(
+ "Variable is used in XPath expression, but no JAXP VariableResolver is available");
+ }
+ }
+
+ /**
+ * Import a schema. This is possible only if Saxon-EE is being used,
+ * and if the Configuration is a EnterpriseConfiguration. Having imported a schema, the types
+ * defined in that schema become part of the static context.
+ * @param source A Source object identifying the schema document to be loaded
+ * @throws net.sf.saxon.type.SchemaException if the schema contained in this document is invalid
+ * @throws UnsupportedOperationException if the configuration is not schema-aware
+ */
+
+ public void importSchema(Source source) throws SchemaException {
+ getConfiguration().addSchemaSource(source, getConfiguration().getErrorListener());
+ setSchemaAware(true);
+ }
+
+ /**
+ * Determine whether a Schema for a given target namespace has been imported. Note that the
+ * in-scope element declarations, attribute declarations and schema types are the types registered
+ * with the (schema-aware) configuration, provided that their namespace URI is registered
+ * in the static context as being an imported schema namespace. (A consequence of this is that
+ * within a Configuration, there can only be one schema for any given namespace, including the
+ * null namespace).
+ * @return true if schema components for the given namespace have been imported into the
+ * schema-aware configuration
+ */
+
+ public boolean isImportedSchema(String namespace) {
+ return getConfiguration().isSchemaAvailable(namespace);
+ }
+
+ /**
+ * Get the set of imported schemas
+ *
+ * @return a Set, the set of URIs representing the names of imported schemas
+ */
+
+ public Set getImportedSchemaNamespaces() {
+ return getConfiguration().getImportedNamespaces();
+ }
+
+ /**
+ * Define a minimal namespace context for use when no user-defined namespace context has been
+ * registered
+ */
+
+ private static class MinimalNamespaceContext implements NamespaceContext, NamespaceResolver, Serializable {
+
+ /**
+ * Get the namespace URI bound to a prefix in the current scope.</p>
+ * @param prefix the prefix to look up
+ * @return Namespace URI bound to prefix in the current scope
+ */
+ /*@Nullable*/ public String getNamespaceURI(/*@Nullable*/ String prefix) {
+ if (prefix == null) {
+ throw new IllegalArgumentException("prefix");
+ } else if (prefix.equals(XMLConstants.DEFAULT_NS_PREFIX)) {
+ return ""; //XMLConstants.NULL_NS_URI;
+ } else if (prefix.equals("xml")) {
+ return NamespaceConstant.XML;
+ } else if (prefix.equals("xs")) {
+ return NamespaceConstant.SCHEMA;
+ } else if (prefix.equals("xsi")) {
+ return NamespaceConstant.SCHEMA_INSTANCE;
+ } else if (prefix.equals("saxon")) {
+ return NamespaceConstant.SAXON;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * <p>Get prefix bound to Namespace URI in the current scope.</p>
+ * @throws UnsupportedOperationException (always)
+ */
+ /*@NotNull*/ public String getPrefix(String namespaceURI) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * <p>Get all prefixes bound to a Namespace URI in the current
+ * @throws UnsupportedOperationException (always)
+ */
+ /*@NotNull*/ public Iterator getPrefixes(String namespaceURI) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Get an iterator over all the prefixes declared in this namespace context. This will include
+ * the default namespace (prefix="") and the XML namespace where appropriate
+ */
+
+ public Iterator iteratePrefixes() {
+ String[] prefixes = {"", "xml", "xs", "xsi", "saxon"};
+ return Arrays.asList(prefixes).iterator();
+ }
+
+ /**
+ * Get the namespace URI corresponding to a given prefix. Return null
+ * if the prefix is not in scope.
+ *
+ * @param prefix the namespace prefix. May be the zero-length string, indicating
+ * that there is no prefix. This indicates either the default namespace or the
+ * null namespace, depending on the value of useDefault.
+ * @param useDefault true if the default namespace is to be used when the
+ * prefix is "". If false, the method returns "" when the prefix is "".
+ * @return the uri for the namespace, or null if the prefix is not in scope.
+ * The "null namespace" is represented by the pseudo-URI "".
+ */
+
+ /*@Nullable*/ public String getURIForPrefix(String prefix, boolean useDefault) {
+ return getNamespaceURI(prefix);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/xpath/XPathEvaluator.java b/sf/saxon/xpath/XPathEvaluator.java
new file mode 100644
index 0000000..ea5bccf
--- /dev/null
+++ b/sf/saxon/xpath/XPathEvaluator.java
@@ -0,0 +1,552 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.dom.DOMNodeWrapper;
+import net.sf.saxon.dom.DocumentWrapper;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.parser.ExpressionTool;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.lib.ParseOptions;
+import net.sf.saxon.om.AllElementsSpaceStrippingRule;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.sxpath.SimpleContainer;
+import net.sf.saxon.trans.SaxonErrorCode;
+import net.sf.saxon.tree.wrapper.SpaceStrippedDocument;
+import net.sf.saxon.type.SchemaException;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.value.DecimalValue;
+import net.sf.saxon.value.Whitespace;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.xpath.*;
+import java.io.File;
+import java.util.List;
+
+/**
+ * <p>XPathEvaluator implements the JAXP API for standalone XPath processing (that is,
+ * executing XPath expressions in the absence of an XSLT stylesheet). It is an implementation
+ * of the JAXP 1.3 XPath interface, with additional methods provided (a) for backwards
+ * compatibility (b) to give extra control over the XPath evaluation, and (c) to support
+ * XPath 2.0.</p>
+ *
+ * <p>It is intended to evolve this so that it only supports the JAXP style of operation.
+ * Some of the methods are therefore marked as deprecated in this release, and will be
+ * dropped in a future release.</p>
+ *
+ * <p>For an alternative XPath API, offering more direct access to Saxon capabilities,
+ * see {@link net.sf.saxon.sxpath.XPathEvaluator}.</p>
+ *
+ * <p>Note that the <code>XPathEvaluator</code> links to a Saxon {@link Configuration}
+ * object. By default a new <code>Configuration</code> is created automatically. In many
+ * applications, however, it is desirable to share a configuration. The default configuration
+ * is not schema aware. All source documents used by XPath expressions under this evaluator
+ * must themselves be built using the <code>Configuration</code> used by this evaluator.</p>
+ *
+ * @author Michael H. Kay
+ */
+
+public class XPathEvaluator implements XPath {
+
+ private Configuration config;
+ /*@Nullable*/ private NodeInfo contextNode = null;
+ private JAXPXPathStaticContext staticContext;
+ private boolean stripSpace = false;
+
+ /**
+ * Default constructor. Creates an XPathEvaluator with Configuration appropriate
+ * to the version of the Saxon software being run.
+ */
+
+ public XPathEvaluator() {
+ this(Configuration.newConfiguration());
+ }
+
+ /**
+ * Construct an XPathEvaluator with a specified configuration.
+ * @param config the configuration to be used. If schema-aware XPath expressions are to be used,
+ * this must be an EnterpriseConfiguration.
+ */
+ public XPathEvaluator(/*@NotNull*/ Configuration config) {
+ this.config = config;
+ staticContext = new JAXPXPathStaticContext(config);
+ }
+
+ /**
+ * Construct an XPathEvaluator to process a particular source document. This is equivalent to
+ * using the default constructor and immediately calling setSource().
+ * @param source The source document (or a specific node within it).
+ */
+
+ public XPathEvaluator(/*@NotNull*/ Source source) throws net.sf.saxon.trans.XPathException {
+ if (source instanceof NodeInfo) {
+ config = ((NodeInfo)source).getDocumentRoot().getConfiguration();
+ } else {
+ config = new Configuration();
+ }
+ staticContext = new JAXPXPathStaticContext(config);
+ setSource(source);
+ }
+
+ /**
+ * Set the XPath language level to be supported. The values recognized are
+ * the strings "1.0", "2.0", and "3.0". The default is "2.0". The value "1.0"
+ * does not disable 2.0 features, but switches on 1.0 compatibility mode. The value
+ * "3.0" is necessary to enable XPath 3.0 features. Setting the value to "3.0"
+ * switches backwards compatibility off.
+ * @param level one of "1.0", "2.0", or "3.0".
+ * @since 9.5
+ */
+
+ public void setXPathLanguageLevel(String level) {
+ if ("1.0".equals(level)) {
+ staticContext.setBackwardsCompatibilityMode(true);
+ staticContext.setXPathLanguageLevel(DecimalValue.TWO);
+ } else if ("2.0".equals(level)) {
+ staticContext.setBackwardsCompatibilityMode(false);
+ staticContext.setXPathLanguageLevel(DecimalValue.TWO);
+ } else if ("3.0".equals(level)) {
+ staticContext.setBackwardsCompatibilityMode(false);
+ staticContext.setXPathLanguageLevel(DecimalValue.THREE);
+ } else {
+ throw new IllegalArgumentException(level);
+ }
+ }
+
+ /**
+ * Get the Configuration used by this XPathEvaluator
+ * @return the Configuration used by this XPathEvaluator
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Indicate whether all whitespace text nodes in the source document are to be
+ * removed. This option has no effect unless it is called before the call on setSource(),
+ * and unless the Source supplied to setSource() is a SAXSource or StreamSource.
+ * @param strip True if all whitespace text nodes are to be stripped from the source document,
+ * false otherwise. The default if the method is not called is false.
+ * @deprecated since 8.9. The preferred way to define options for the way in which source
+ * documents are built is to use the class {@link net.sf.saxon.lib.AugmentedSource} for any
+ * of the methods expecting a {@link Source} object.
+ */
+
+ public void setStripSpace(boolean strip) {
+ stripSpace = strip;
+ }
+
+ /**
+ * Supply a document against which XPath expressions are to be executed, converting it to a
+ * Saxon NodeInfo object.
+ * <p>If the supplied source is a <code>NodeInfo</code>, it is returned unchanged.</p>
+ * <p>If the supplied source is a <code>DOMSource</code>, the result is a Saxon <code>NodeInfo</code>
+ * wrapper around the DOM Node contained by the DOMSource.</p>
+ * <p>In all other cases, the result is a document node, and is the same as the result of calling
+ * {@link Configuration#buildDocument(javax.xml.transform.Source)} with the same argument;
+ * except that when whitespace stripping has been requested using {@link #setStripSpace(boolean)},
+ * this request is passed on.</p>
+ * <p>Despite the name of this method, it does not change the state of the <code>XPathEvaluator</code>
+ * in any way.</p>
+ * @param source Any javax.xml.transform.Source object representing the document against
+ * which XPath expressions will be executed. Note that a Saxon {@link net.sf.saxon.om.DocumentInfo DocumentInfo}
+ * (indeed any {@link net.sf.saxon.om.NodeInfo NodeInfo})
+ * can be used as a Source. To use a third-party DOM Document as a source, create an instance of
+ * {@link javax.xml.transform.dom.DOMSource DOMSource} to wrap it.
+ * <p>The Source object supplied also determines the initial setting
+ * of the context item. In most cases the context node will be the root of the supplied document;
+ * however, if a NodeInfo or DOMSource is supplied it can be any node in the document. </p>
+ * @return the NodeInfo of the start node in the resulting document object.
+ * @throws XPathException if the supplied Source is a NodeInfo object that was built using an incompatible
+ * Configuration (that is, a Configuration using a different NamePool). Also, if any error occurs parsing
+ * the document supplied as the Source.
+ */
+
+ public NodeInfo setSource(/*@NotNull*/ Source source) throws net.sf.saxon.trans.XPathException {
+ if (source instanceof DOMSource) {
+ Node node = ((DOMSource)source).getNode();
+ String baseURI = source.getSystemId();
+ DocumentWrapper documentWrapper = new DocumentWrapper(node.getOwnerDocument(), baseURI, config);
+ DOMNodeWrapper nodeWrapper = documentWrapper.wrap(node);
+ if (stripSpace) {
+ SpaceStrippedDocument sdoc = new SpaceStrippedDocument(documentWrapper, AllElementsSpaceStrippingRule.getInstance());
+ return sdoc.wrap(nodeWrapper);
+ } else {
+ return nodeWrapper;
+ }
+ } else if (source instanceof NodeInfo) {
+ NodeInfo origin = (NodeInfo)source;
+ if (!origin.getConfiguration().isCompatible(config)) {
+ throw new net.sf.saxon.trans.XPathException(
+ "Supplied node must be built using the same or a compatible Configuration",
+ SaxonErrorCode.SXXP0004);
+ }
+ if (stripSpace) {
+ SpaceStrippedDocument sdoc = new SpaceStrippedDocument(origin.getDocumentRoot(), AllElementsSpaceStrippingRule.getInstance());
+ return sdoc.wrap(origin);
+ } else {
+ return origin;
+ }
+ } else {
+ ParseOptions options = new ParseOptions();
+ if (stripSpace) {
+ options.setStripSpace(Whitespace.ALL);
+ }
+ return config.buildDocument(source, options);
+ }
+ }
+
+ /**
+ * Set the static context for compiling XPath expressions. This provides control over the
+ * environment in which the expression is compiled, for example it allows namespace prefixes to
+ * be declared, variables to be bound and functions to be defined. For most purposes, the static
+ * context can be defined by providing and tailoring an instance of the JAXPXPathStaticContext class.
+ * Until this method is called, a default static context is used, in which no namespaces are defined
+ * other than the standard ones (xml, xslt, and saxon), and no variables or functions (other than the
+ * core XPath functions) are available.
+ * @param context the static context
+ * @throws IllegalArgumentException if the supplied static context uses a different and incompatible
+ * Configuration from the one used in this XPathEvaluator
+ */
+
+ public void setStaticContext(/*@NotNull*/ JAXPXPathStaticContext context) {
+ if (!config.isCompatible(context.getConfiguration())) {
+ throw new IllegalArgumentException("Supplied static context uses a different and incompatible Configuration");
+ }
+ staticContext = context;
+ }
+
+ /**
+ * Get the current static context
+ * @return the static context
+ */
+
+ public JAXPXPathStaticContext getStaticContext() {
+ return staticContext;
+ }
+
+ /**
+ * Get the executable
+ * @return the executable
+ */
+
+// public Executable getExecutable() {
+// return staticContext.getExecutable();
+// }
+
+ /**
+ * Prepare an XPath expression for subsequent evaluation.
+ * @param expression The XPath expression to be evaluated, supplied as a string.
+ * @return an XPathExpression object representing the prepared expression
+ * @throws net.sf.saxon.trans.XPathException if the syntax of the expression is wrong, or if it references namespaces,
+ * variables, or functions that have not been declared.
+ * @deprecated since Saxon 8.9 - use {@link #compile(String)}
+ */
+
+ /*@NotNull*/ public XPathExpressionImpl createExpression(String expression) throws net.sf.saxon.trans.XPathException {
+ return createExpressionInternal(expression);
+ }
+
+ /*@NotNull*/ private XPathExpressionImpl createExpressionInternal(String expression) throws net.sf.saxon.trans.XPathException {
+ Executable exec = new Executable(getConfiguration());
+ SimpleContainer container = new SimpleContainer(exec);
+ Expression exp = ExpressionTool.make(expression, staticContext, container, 0, -1, 1, null);
+ ExpressionVisitor visitor = ExpressionVisitor.make(staticContext, exec);
+ visitor.setExecutable(exec);
+ final ExpressionVisitor.ContextItemType contextItemType = new ExpressionVisitor.ContextItemType(Type.ITEM_TYPE, true);
+ exp = visitor.typeCheck(exp, contextItemType);
+ exp = visitor.optimize(exp, contextItemType);
+ SlotManager map = staticContext.getConfiguration().makeSlotManager();
+ ExpressionTool.allocateSlots(exp, 0, map);
+ exp.setContainer(container);
+ XPathExpressionImpl xpe = new XPathExpressionImpl(exp, exec);
+ xpe.setStackFrameMap(map);
+ if (contextNode != null) {
+ xpe.privatelySetContextNode(contextNode);
+ }
+ return xpe;
+ }
+
+ /**
+ * Set the context node. This provides the context node for any expressions executed after this
+ * method is called, including expressions that were prepared before it was called.
+ * @param node The node to be used as the context node. The node must be within a tree built using
+ * the same Saxon {@link Configuration} as used by this XPathEvaluator.
+ * @deprecated since Saxon 8.9 - use the various method defined in the JAXP interface definition,
+ * which allow a NodeInfo object to be supplied as the value of the Source argument
+ * @throws IllegalArgumentException if the supplied node was built using the wrong Configuration
+ */
+
+ public void setContextNode(/*@NotNull*/ NodeInfo node) {
+ if (!node.getConfiguration().isCompatible(config)) {
+ throw new IllegalArgumentException(
+ "Supplied node must be built using the same or a compatible Configuration");
+ }
+ contextNode = node;
+ }
+
+ public void reset() {
+ contextNode = null;
+ stripSpace = false;
+ staticContext = new JAXPXPathStaticContext(config);
+ }
+
+ /**
+ * Set XPath 1.0 compatibility mode on or off (by default, it is false). This applies
+ * to any XPath expression compiled while this option is in force.
+ * @param compatible true if XPath 1.0 compatibility mode is to be set to true, false
+ * if it is to be set to false.
+ */
+
+ public void setBackwardsCompatible(boolean compatible) {
+ staticContext.setBackwardsCompatibilityMode(compatible);
+ }
+
+ /**
+ * Get the value of XPath 1.0 compatibility mode
+ * @return true if XPath 1.0 compatibility mode is set
+ */
+
+ public boolean isBackwardsCompatible() {
+ return staticContext.isInBackwardsCompatibleMode();
+ }
+
+ /**
+ * Set the resolver for XPath variables
+ * @param xPathVariableResolver a resolver for variables
+ */
+
+ public void setXPathVariableResolver(XPathVariableResolver xPathVariableResolver) {
+ staticContext.setXPathVariableResolver(xPathVariableResolver);
+ }
+
+ /**
+ * Get the resolver for XPath variables
+ * @return the resolver, if one has been set
+ */
+ public XPathVariableResolver getXPathVariableResolver() {
+ return staticContext.getXPathVariableResolver();
+ }
+
+ /**
+ * Set the resolver for XPath functions
+ * @param xPathFunctionResolver a resolver for XPath function calls
+ */
+
+ public void setXPathFunctionResolver(XPathFunctionResolver xPathFunctionResolver) {
+ staticContext.setXPathFunctionResolver(xPathFunctionResolver);
+ }
+
+ /**
+ * Get the resolver for XPath functions
+ * @return the resolver, if one has been set
+ */
+
+ /*@Nullable*/ public XPathFunctionResolver getXPathFunctionResolver() {
+ return staticContext.getXPathFunctionResolver();
+ }
+
+ /**
+ * Set the namespace context to be used.
+ * @param namespaceContext The namespace context
+ */
+
+ public void setNamespaceContext(NamespaceContext namespaceContext) {
+ staticContext.setNamespaceContext(namespaceContext);
+ }
+
+ /**
+ * Get the namespace context, if one has been set using {@link #setNamespaceContext}
+ * @return the namespace context if set, or null otherwise
+ */
+
+ public NamespaceContext getNamespaceContext() {
+ return staticContext.getNamespaceContext();
+ }
+
+ /**
+ * Import a schema. This is possible only if Saxon-EE is being used,
+ * and if the Configuration is an EnterpriseConfiguration. Having imported a schema, the types
+ * defined in that schema become part of the static context.
+ * @param source A Source object identifying the schema document to be loaded
+ * @throws net.sf.saxon.type.SchemaException if the schema contained in this document is invalid
+ * @throws UnsupportedOperationException if the configuration is not schema-aware
+ */
+
+ public void importSchema(Source source) throws SchemaException {
+ staticContext.importSchema(source);
+ staticContext.setSchemaAware(true);
+ }
+
+ /**
+ * Compile an XPath 2.0 expression
+ * @param expr the XPath 2.0 expression to be compiled, as a string
+ * @return the compiled form of the expression
+ * @throws XPathExpressionException if there are any static errors in the expression.
+ * Note that references to undeclared variables are not treated as static errors, because
+ * variables are not pre-declared using this API.
+ */
+ /*@NotNull*/ public XPathExpression compile(/*@Nullable*/ String expr) throws XPathExpressionException {
+ if (expr == null) {
+ throw new NullPointerException("expr");
+ }
+ try {
+ return createExpressionInternal(expr);
+ } catch (net.sf.saxon.trans.XPathException e) {
+ throw new XPathExpressionException(e);
+ }
+ }
+
+ /**
+ * Single-shot method to compile and execute an XPath 2.0 expression.
+ * @param expr The XPath 2.0 expression to be compiled and executed
+ * @param node The context node for evaluation of the expression.
+ *
+ * <p>This may be a NodeInfo object, representing a node in Saxon's native
+ * implementation of the data model, or it may be a node in any supported
+ * external object model: DOM, JDOM, DOM4J, or XOM, or any other model for
+ * which support has been configured in the Configuration. Note that the
+ * supporting libraries for the chosen model must be on the class path.</p>
+ *
+ * <p><b>Contrary to the interface specification, Saxon does not supply an empty
+ * document when the value is null. This is because Saxon supports multiple object models,
+ * and it's unclear what kind of document node would be appropriate. Instead, Saxon uses
+ * the node supplied to the {@link #setContextNode} method if available, and if none
+ * is available, executes the XPath expression with the context item undefined.</p></p>
+ *
+ * @param qName The type of result required. For details, see
+ * {@link XPathExpressionImpl#evaluate(Object, javax.xml.namespace.QName)}
+ * @return the result of evaluating the expression, returned as described in
+ * {@link XPathExpressionImpl#evaluate(Object, javax.xml.namespace.QName)}
+ * @throws XPathExpressionException if any static or dynamic error occurs
+ * in evaluating the expression.
+ */
+
+ public Object evaluate(String expr, Object node, QName qName) throws XPathExpressionException {
+ XPathExpression exp = compile(expr);
+ return exp.evaluate(node, qName);
+ }
+
+ /**
+ * Single-shot method to compile an execute an XPath 2.0 expression, returning
+ * the result as a string.
+ * @param expr The XPath 2.0 expression to be compiled and executed
+ * @param node The context node for evaluation of the expression
+ *
+ * <p>This may be a NodeInfo object, representing a node in Saxon's native
+ * implementation of the data model, or it may be a node in any supported
+ * external object model: DOM, JDOM, DOM4J, or XOM, or any other model for
+ * which support has been configured in the Configuration. Note that the
+ * supporting libraries for the chosen model must be on the class path.</p>
+ *
+ * <p><b>Contrary to the interface specification, Saxon does not supply an empty
+ * document when the value is null. This is because Saxon supports multiple object models,
+ * and it's unclear what kind of document node would be appropriate. Instead, Saxon uses
+ * the node supplied to the {@link #setContextNode} method if available, and if none
+ * is available, executes the XPath expression with the context item undefined.</p></p>
+ * @return the result of evaluating the expression, converted to a string as if
+ * by calling the XPath string() function
+ * @throws XPathExpressionException if any static or dynamic error occurs
+ * in evaluating the expression.
+ */
+
+ public String evaluate(String expr, Object node) throws XPathExpressionException {
+ XPathExpression exp = compile(expr);
+ return exp.evaluate(node);
+ }
+
+ /**
+ * Single-shot method to parse and build a source document, and
+ * compile an execute an XPath 2.0 expression, against that document
+ * @param expr The XPath 2.0 expression to be compiled and executed
+ * @param inputSource The source document: this will be parsed and built into a tree,
+ * and the XPath expression will be executed with the root node of the tree as the
+ * context node.
+ * @param qName The type of result required. For details, see
+ * {@link XPathExpressionImpl#evaluate(Object, javax.xml.namespace.QName)}
+ * @return the result of evaluating the expression, returned as described in
+ * {@link XPathExpressionImpl#evaluate(Object, javax.xml.namespace.QName)}
+ * @throws XPathExpressionException if any static or dynamic error occurs
+ * in evaluating the expression.
+ * @throws NullPointerException if any of the three arguments is null
+ */
+
+ public Object evaluate(/*@Nullable*/ String expr, /*@Nullable*/ InputSource inputSource, /*@Nullable*/ QName qName) throws XPathExpressionException {
+ if (expr == null) {
+ throw new NullPointerException("expr");
+ }
+ if (inputSource == null) {
+ throw new NullPointerException("inputSource");
+ }
+ if (qName == null) {
+ throw new NullPointerException("qName");
+ }
+ XPathExpression exp = compile(expr);
+ return exp.evaluate(inputSource, qName);
+ }
+
+ /**
+ * Single-shot method to parse and build a source document, and
+ * compile an execute an XPath 2.0 expression, against that document,
+ * returning the result as a string
+ * @param expr The XPath 2.0 expression to be compiled and executed
+ * @param inputSource The source document: this will be parsed and built into a tree,
+ * and the XPath expression will be executed with the root node of the tree as the
+ * context node
+ * @return the result of evaluating the expression, converted to a string as
+ * if by calling the XPath string() function
+ * @throws XPathExpressionException if any static or dynamic error occurs
+ * in evaluating the expression.
+ * @throws NullPointerException if either of the two arguments is null
+ */
+
+ public String evaluate(/*@Nullable*/ String expr, /*@Nullable*/ InputSource inputSource) throws XPathExpressionException {
+ if (expr == null) {
+ throw new NullPointerException("expr");
+ }
+ if (inputSource == null) {
+ throw new NullPointerException("inputSource");
+ }
+ XPathExpression exp = compile(expr);
+ return exp.evaluate(inputSource);
+ }
+
+ /**
+ * A simple command-line interface for the XPathEvaluator (not documented).
+ * @param args command line arguments.
+ * First parameter is the filename containing the source document, second
+ * parameter is the XPath expression.
+ * @throws Exception if any error occurs
+ */
+
+ public static void main(/*@NotNull*/ String[] args) throws Exception {
+ if (args.length != 2) {
+ System.err.println("format: java XPathEvaluator source.xml \"expression\"");
+ return;
+ }
+ XPathEvaluator xpe = new XPathEvaluator();
+ List results = (List)xpe.evaluate(args[1], new StreamSource(new File(args[0])), XPathConstants.NODESET);
+ for (int i = 0; i < results.size(); i++) {
+ Object o = results.get(i);
+ System.err.println(o);
+ }
+
+ }
+
+}
+
diff --git a/sf/saxon/xpath/XPathExpressionImpl.java b/sf/saxon/xpath/XPathExpressionImpl.java
new file mode 100644
index 0000000..564862c
--- /dev/null
+++ b/sf/saxon/xpath/XPathExpressionImpl.java
@@ -0,0 +1,467 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.instruct.Executable;
+import net.sf.saxon.expr.instruct.SlotManager;
+import net.sf.saxon.expr.sort.AtomicComparer;
+import net.sf.saxon.expr.sort.SortKeyDefinition;
+import net.sf.saxon.expr.sort.SortKeyEvaluator;
+import net.sf.saxon.expr.sort.SortedIterator;
+import net.sf.saxon.functions.NumberFn;
+import net.sf.saxon.om.*;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.wrapper.VirtualNode;
+import net.sf.saxon.value.*;
+import org.xml.sax.InputSource;
+
+import javax.xml.namespace.QName;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import java.util.List;
+
+/**
+ * <p>The JAXP XPathExpression interface represents a compiled XPath expression that can be repeatedly
+ * evaluated. This class is Saxon's implementation of that interface.</p>
+ *
+ * <p>The class also includes some methods retained from Saxon's original XPath API. When these methods
+ * are used, the object contains the context node and other state, so it is not thread-safe.</p>
+ *
+ * @author Michael H. Kay
+ */
+
+
+public class XPathExpressionImpl implements XPathExpression, SortKeyEvaluator {
+
+ private Configuration config;
+ private Executable executable;
+ private Expression expression;
+ private Expression atomizer;
+ /*@Nullable*/ private NodeInfo contextNode;
+ private SlotManager stackFrameMap;
+ /*@Nullable*/ private XPathExpressionImpl sortKey = null;
+
+ /**
+ * The constructor is protected, to ensure that instances can only be
+ * created using the createExpression() method of XPathEvaluator
+ * @param exp the compiled expression
+ * @param exec the executable
+ */
+
+ protected XPathExpressionImpl(Expression exp, /*@NotNull*/ Executable exec) {
+ expression = exp;
+ executable = exec;
+ config = exec.getConfiguration();
+ }
+
+ /**
+ * Define the number of slots needed for local variables within the expression.
+ * This method is for internal use only.
+ * @param map description of the stack frame
+ */
+
+ protected void setStackFrameMap(SlotManager map) {
+ stackFrameMap = map;
+ }
+
+ /**
+ * Get the stack frame map. This holds information about the allocation of slots to variables.
+ * This is needed by applications using low-level interfaces for evaluating the expression
+ * @return a description of the stack frame
+ */
+
+ public SlotManager getStackFrameMap() {
+ return stackFrameMap;
+ }
+
+ /**
+ * Get the Configuration under which this XPath expression was compiled
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Define the sort order for the results of the expression. If this method is called, then
+ * the list returned by a subsequent call on the evaluate() method will first be sorted.
+ * @param sortKey an XPathExpression, which will be applied to each item in the sequence;
+ * the result of this expression determines the ordering of the list returned by the evaluate()
+ * method. The sortKey can be null, to clear a previous sort key. Note that the expression is
+ * not automatically atomized; if it selects nodes, these should be explicitly converted to
+ * atomic values by calling the string() or data() functions.
+ * @deprecated since 9.0. This method is not present in the JAXP interface. The recommended
+ * way to get a sorted result is to use XQuery instead of XPath.
+ */
+
+ public void setSortKey(XPathExpressionImpl sortKey) {
+ this.sortKey = sortKey;
+ }
+
+ /**
+ * Set the context node for evaluating the expression. If this method is not called,
+ * the context node will be the root of the document to which the prepared expression is
+ * bound.
+ * @param node the context node
+ * @deprecated since 9.0. Using this method is not thread-safe. Use a method instead
+ * such as {@link #evaluate(Object, QName)} that allows the context node to be specified
+ * as a parameter to the call.
+ */
+
+ public void setContextNode(/*@Nullable*/ NodeInfo node) {
+ if (node==null) {
+ throw new NullPointerException("Context node cannot be null");
+ }
+ if (node.getConfiguration() != config) {
+ throw new IllegalArgumentException("Supplied node uses the wrong Configuration");
+ }
+ contextNode = node;
+ }
+
+ /**
+ * Protected, undeprecated version of setContextNode() for use by deprecated paths within the package
+ * (exists to avoid deprecation warnings when compiling Saxon)
+ * @param node the context node
+ */
+
+ protected void privatelySetContextNode(/*@Nullable*/ NodeInfo node) {
+ if (node==null) {
+ throw new NullPointerException("Context node cannot be null");
+ }
+ if (node.getConfiguration() != config) {
+ throw new IllegalArgumentException("Supplied node uses the wrong Configuration");
+ }
+ contextNode = node;
+ }
+
+
+ /**
+ * Execute a prepared XPath expression, returning the results as a List. The context
+ * node must have been set previously using {@link #setContextNode(net.sf.saxon.om.NodeInfo)}.
+ * @return The results of the expression, as a List. The List represents the sequence
+ * of items returned by the expression. Each item in the list will either be an instance
+ * of net.sf.saxon.om.NodeInfo, representing a node, or a Java object representing an atomic value.
+ * For the types of Java object that may be returned, see {@link #evaluate(Object, javax.xml.namespace.QName)}
+ * with the second argument set to NODESET.
+ * @deprecated since 9.0. This method is not present in the JAXP interface. Either use
+ * the JAXP methods such as {@link #evaluate(Object, QName)}, or use the Saxon XPath
+ * API instead of JAXP.
+ */
+
+ /*@NotNull*/ public List evaluate() throws XPathException {
+ XPathContextMajor context = new XPathContextMajor(contextNode, executable);
+ context.openStackFrame(stackFrameMap);
+ SequenceIterator iter = expression.iterate(context);
+ SequenceExtent extent = new SequenceExtent(iter);
+ return (List)PJConverter.ToCollection.INSTANCE.convert(extent, List.class, context);
+ }
+
+ /**
+ * Execute a prepared XPath expression, returning the first item in the result.
+ * This is useful where it is known that the expression will only return
+ * a singleton value (for example, a single node, or a boolean). The context node
+ * must be set previously using {@link #setContextNode(net.sf.saxon.om.NodeInfo)}.
+ * @return The first item in the sequence returned by the expression. If the expression
+ * returns an empty sequence, this method returns null. Otherwise, it returns the first
+ * item in the result sequence, represented as a Java object using the same mapping as for
+ * the evaluate() method
+ * @deprecated since 9.0. This method is not present in the JAXP interface. Either use
+ * the JAXP methods such as {@link #evaluate(Object, QName)}, or use the Saxon XPath
+ * API instead of JAXP.
+ */
+
+ /*@Nullable*/ public Object evaluateSingle() throws XPathException {
+ XPathContextMajor context = new XPathContextMajor(contextNode, executable);
+ context.openStackFrame(stackFrameMap);
+ SequenceIterator iterator = expression.iterate(context);
+ Item item = iterator.next();
+ if (item == null) {
+ return null;
+ } else {
+ return SequenceTool.convertToJava(item);
+ }
+ }
+
+ /**
+ * Get a raw iterator over the results of the expression. This returns results without
+ * any conversion of the returned items to "native" Java classes. This method is intended
+ * for use by applications that need to process the results of the expression using
+ * internal Saxon interfaces.
+ * @param contextItem the context item for evaluating the expression
+ * @return an iterator over the results of the expression, with no conversion of returned items
+ * @since 9.0
+ */
+
+ /*@Nullable*/ public SequenceIterator rawIterator(Item contextItem) throws XPathException {
+ XPathContextMajor context = new XPathContextMajor(contextItem, executable);
+ return rawIterator(context);
+ }
+
+ /*@Nullable*/ private SequenceIterator rawIterator(/*@NotNull*/ XPathContextMajor context) throws XPathException {
+ context.openStackFrame(stackFrameMap);
+ SequenceIterator iterator = expression.iterate(context);
+ if (sortKey != null) {
+ Expression key = sortKey.expression;
+ if (key.getItemType(config.getTypeHierarchy()) instanceof NodeTest) {
+ sortKey.expression = Atomizer.makeAtomizer(key);
+ }
+
+ SortKeyDefinition sk = new SortKeyDefinition();
+ sk.setSortKey(sortKey.expression, true);
+ AtomicComparer comp = sk.makeComparator(context);
+ AtomicComparer[] comps = {comp};
+
+ iterator = new SortedIterator(context, iterator, this, comps, true);
+ ((SortedIterator)iterator).setHostLanguage(Configuration.XPATH);
+ }
+ return iterator;
+ }
+
+
+ /**
+ * JAXP 1.3 evaluate() method
+ * @param node The context node. This must use a representation of nodes that this implementation understands.
+ * This may be a Saxon NodeInfo, or a node in one of the external object models supported, for example
+ * DOM, DOM4J, JDOM, or XOM, provided the support module for that object model is loaded.
+ *
+ * <p><b>Contrary to the interface specification, Saxon does not supply an empty
+ * document when the value is null. This is because Saxon supports multiple object models,
+ * and it's unclear what kind of document node would be appropriate. Instead, Saxon uses
+ * the node supplied to the {@link #setContextNode} method if available, and if none
+ * is available, executes the XPath expression with the context item undefined.</p></p>
+ * <p><b>Saxon does not allow a NodeList to be supplied for this parameter. It's not clear
+ * what this would be intended to mean.</b></p>
+ * @param qName Indicates the type of result required. This must be one of the constants defined in
+ * the JAXP {@link XPathConstants} class.
+ * Saxon will attempt to convert the actual result of the expression to the required type using the
+ * XPath 1.0 conversion rules.
+ * @return the result of the evaluation, as a Java object of the appropriate type. Saxon interprets the
+ * rules as follows:
+ * <table>
+ * <thead><tr><td>QName</td><td>Return Value</td></thead>
+ * <tbody>
+ * <tr><td valign="top">BOOLEAN</td>
+ * <td>The effective boolean value of the actual result,
+ * as a Java Boolean object</td></tr>
+ * <tr><td valign="top">STRING</td>
+ * <td>The result of applying the string() function to the actual result,
+ * as a Java String object</td></tr>
+ * <tr><td valign="top">NUMBER</td>
+ * <td>The result of applying the number() function to the actual result,
+ * as a Java Double object</td></tr>
+ * <tr><td valign="top">NODE</td>
+ * <td>A single node, in the native data model supplied as input. If the
+ * expression returns more than one node, the first is returned. If
+ * the expression returns an empty sequence, null is returned. If the
+ * expression returns an atomic value, or if the first item in the
+ * result sequence is an atomic value, an exception is thrown.</td></tr>
+ * <tr><td valign="top">NODESET</td>
+ * <td>This is interpreted as allowing any sequence, of nodes or atomic values.
+ * If the first argument is a wrapper around a DOM Node, then the result is
+ * returned as a DOM NodeList, and an exception is then thrown if the result sequence
+ * contains a value that is not a DOM Node. In all other cases
+ * the result is returned as a Java List object, unless it is empty, in which
+ * case null is returned. The contents of the list may be node objects (in the
+ * native data model supplied as input), or Java objects representing the XPath
+ * atomic values in the actual result: String for an xs:string, Double for a xs:double,
+ * Long for an xs:integer, and so on. (For safety, cast the values to a type
+ * such as xs:string within the XPath expression). </td></tr></table>
+ *
+ * @throws XPathExpressionException if evaluation of the expression fails or if the
+ * result cannot be converted to the requested type.
+ */
+ /*@Nullable*/ public Object evaluate(/*@Nullable*/ Object node, /*@NotNull*/ QName qName) throws XPathExpressionException {
+ NodeInfo contextNode = this.contextNode;
+ if (node != null) {
+ if (node instanceof SingletonItem) {
+ node = ((SingletonItem)node).asItem();
+ }
+ if (node instanceof NodeInfo) {
+ if (!((NodeInfo)node).getConfiguration().isCompatible(config)) {
+ throw new XPathExpressionException(
+ "Supplied node must be built using the same or a compatible Configuration");
+ }
+ if (node instanceof DocumentInfo && ((DocumentInfo)node).isTyped() && !executable.isSchemaAware()) {
+ throw new XPathExpressionException(
+ "The expression was compiled to handled untyped data, but the input is typed");
+ }
+ contextNode = ((NodeInfo)node);
+ } else {
+ JPConverter converter = JPConverter.allocate(node.getClass(), config);
+ Sequence val;
+ try {
+ val = converter.convert(node, new EarlyEvaluationContext(config, null));
+ } catch (XPathException e) {
+ throw new XPathExpressionException(
+ "Failure converting a node of class " + node.getClass().getName() +
+ ": " + e.getMessage());
+ }
+ if (val instanceof NodeInfo) {
+ if (!((NodeInfo)val).getConfiguration().isCompatible(config)) {
+ throw new XPathExpressionException(
+ "Supplied node must be built using the same or a compatible Configuration");
+ }
+ if (node instanceof DocumentInfo && ((DocumentInfo)node).isTyped() && !executable.isSchemaAware()) {
+ throw new XPathExpressionException(
+ "The expression was compiled to handled untyped data, but the input is typed");
+ }
+ contextNode = (NodeInfo)val;
+ } else {
+ throw new XPathExpressionException(
+ "Cannot locate an object model implementation for nodes of class "
+ + node.getClass().getName());
+ }
+ }
+ }
+ XPathContextMajor context = new XPathContextMajor(contextNode, executable);
+ context.openStackFrame(stackFrameMap);
+ try {
+ if (qName.equals(XPathConstants.BOOLEAN)) {
+ return Boolean.valueOf(expression.effectiveBooleanValue(context));
+ } else if (qName.equals(XPathConstants.STRING)) {
+ SequenceIterator iter = expression.iterate(context);
+
+ Item first = iter.next();
+ if (first == null) {
+ return "";
+ }
+ return first.getStringValue();
+
+ } else if (qName.equals(XPathConstants.NUMBER)) {
+ if (atomizer == null) {
+ atomizer = Atomizer.makeAtomizer(expression);
+ }
+ SequenceIterator iter = atomizer.iterate(context);
+
+ Item first = iter.next();
+ if (first == null) {
+ return new Double(Double.NaN);
+ }
+ if (first instanceof NumericValue) {
+ return new Double(((NumericValue)first).getDoubleValue());
+ } else {
+ DoubleValue v = NumberFn.convert((AtomicValue)first, getConfiguration());
+ return new Double(v.getDoubleValue());
+ }
+
+ } else if (qName.equals(XPathConstants.NODE)) {
+ SequenceIterator iter = expression.iterate(context);
+ Item first = iter.next();
+ if (first instanceof VirtualNode) {
+ return ((VirtualNode)first).getRealNode();
+ }
+ if (first == null || first instanceof NodeInfo) {
+ return first;
+ }
+ throw new XPathExpressionException("Expression result is not a node");
+ } else if (qName.equals(XPathConstants.NODESET)) {
+ //SequenceIterator iter = expression.iterate(context);
+ SequenceIterator iter = rawIterator(context);
+ SequenceExtent extent = new SequenceExtent(iter);
+ PJConverter converter = PJConverter.allocateNodeListCreator(config, node);
+ return converter.convert(extent, Object.class, context);
+ } else {
+ throw new IllegalArgumentException("qName: Unknown type for expected result");
+ }
+ } catch (XPathException e) {
+ throw new XPathExpressionException(e);
+ }
+ }
+
+ /**
+ * Evaluate the expression to return a string value
+ * @param node the initial context node. This must be either an instance of NodeInfo or a node
+ * recognized by a known external object model.
+ * <p><b>Contrary to the interface specification, Saxon does not supply an empty
+ * document when the value is null. This is because Saxon supports multiple object models,
+ * and it's unclear what kind of document node would be appropriate. Instead, Saxon uses
+ * the node supplied to the {@link #setContextNode} method if available, and if none
+ * is available, executes the XPath expression with the context item undefined.</p></p>
+ * @return the results of the expression, converted to a String
+ * @throws XPathExpressionException if evaluation fails
+ */
+
+ /*@NotNull*/ public String evaluate(Object node) throws XPathExpressionException {
+ return (String)evaluate(node, XPathConstants.STRING);
+ }
+
+ /**
+ * Evaluate the XPath expression against an input source to obtain a result of a specified type
+ * @param inputSource The input source document against which the expression is evaluated.
+ * (Note that there is no caching. This will be parsed, and the parsed result will be discarded.)
+ * If the supplied value is null then (contrary to the JAXP specifications), the XPath expression
+ * is evaluated with the context item undefined.
+ * @param qName The type required, identified by a constant in {@link XPathConstants}
+ * @return the result of the evaluation, as a Java object of the appropriate type:
+ * see {@link #evaluate(Object, javax.xml.namespace.QName)}
+ * @throws XPathExpressionException
+ */
+ /*@Nullable*/ public Object evaluate(/*@Nullable*/ InputSource inputSource, /*@Nullable*/ QName qName) throws XPathExpressionException {
+ if (qName == null) {
+ throw new NullPointerException("qName");
+ }
+ try {
+ NodeInfo doc = null;
+ if (inputSource != null) {
+ doc = config.buildDocument(new SAXSource(inputSource));
+ }
+ return evaluate(doc, qName);
+ } catch (XPathException e) {
+ throw new XPathExpressionException(e);
+ }
+ }
+
+ /**
+ * Evaluate the XPath expression against an input source to obtain a string result
+ * @param inputSource The input source document against which the expression is evaluated.
+ * (Note that there is no caching. This will be parsed, and the parsed result will be discarded.)
+ * @return the result of the evaluation, converted to a String
+ * @throws XPathExpressionException in the event of an XPath dynamic error
+ * @throws NullPointerException If <code>inputSource</code> is <code>null</code>.
+ */
+
+ /*@NotNull*/ public String evaluate(/*@Nullable*/ InputSource inputSource) throws XPathExpressionException {
+ if (inputSource == null) {
+ throw new NullPointerException("inputSource");
+ }
+ try {
+ NodeInfo doc = config.buildDocument(new SAXSource(inputSource));
+ return (String)evaluate(doc, XPathConstants.STRING);
+ } catch (XPathException e) {
+ throw new XPathExpressionException(e);
+ }
+ }
+
+ /**
+ * Callback for evaluating the sort keys. For internal use only.
+ */
+
+ /*@Nullable*/ public AtomicValue evaluateSortKey(int n, XPathContext c) throws XPathException {
+ return (AtomicValue)sortKey.getInternalExpression().evaluateItem(c);
+ }
+
+
+ /**
+ * Low-level method to get the internal Saxon expression object. This exposes a wide range of
+ * internal methods that may be needed by specialized applications, and allows greater control
+ * over the dynamic context for evaluating the expression.
+ * @return the underlying Saxon expression object.
+ */
+
+ public Expression getInternalExpression() {
+ return expression;
+ }
+
+}
+
diff --git a/sf/saxon/xpath/XPathFactoryImpl.java b/sf/saxon/xpath/XPathFactoryImpl.java
new file mode 100644
index 0000000..70b01e1
--- /dev/null
+++ b/sf/saxon/xpath/XPathFactoryImpl.java
@@ -0,0 +1,199 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.Validation;
+
+import javax.xml.XMLConstants;
+import javax.xml.xpath.*;
+
+/**
+ * Saxon implementation of the JAXP 1.3 XPathFactory
+ */
+public class XPathFactoryImpl extends XPathFactory {
+
+ private Configuration config;
+ private XPathVariableResolver variableResolver;
+ private XPathFunctionResolver functionResolver;
+
+ /**
+ * Default constructor: this creates a Configuration as well as creating the XPathFactory. Any documents
+ * accessed using this XPathFactory must be built using this same Configuration.
+ */
+
+ public XPathFactoryImpl() {
+ config = Configuration.newConfiguration();
+ setConfiguration(config);
+ Configuration.getPlatform().registerAllBuiltInObjectModels(config);
+ }
+
+ /**
+ * Constructor using a user-supplied Configuration.
+ * This constructor is useful if the document to be queried already exists, as it allows the configuration
+ * associated with the document to be used with this XPathFactory.
+ * @param config the Saxon configuration
+ */
+
+ public XPathFactoryImpl(Configuration config) {
+ this.config = config;
+ config.setProcessor(this);
+ }
+
+ /**
+ * Set the Configuration for the factory
+ * @param config the Saxon Configuration to be used
+ */
+
+ public void setConfiguration(Configuration config) {
+ this.config = config;
+ config.setProcessor(this);
+ }
+
+ /**
+ * Get the Configuration object used by this XPathFactory
+ * @return the Saxon configuration
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /**
+ * Test whether a given object model is supported. Returns true if the object model
+ * is the Saxon object model, DOM, JDOM, DOM4J, or XOM
+ * @param model The URI identifying the object model.
+ * @return true if the object model is one of the following (provided that the supporting
+ * JAR file is available on the classpath)
+ * {@link net.sf.saxon.lib.NamespaceConstant#OBJECT_MODEL_SAXON},
+ * {@link XPathConstants#DOM_OBJECT_MODEL},
+ * {@link net.sf.saxon.lib.NamespaceConstant#OBJECT_MODEL_JDOM}, or
+ * {@link NamespaceConstant#OBJECT_MODEL_XOM}, or
+ * {@link net.sf.saxon.lib.NamespaceConstant#OBJECT_MODEL_DOM4J}.
+ * Saxon also allows user-defined external object models to be registered with the Configuration, and
+ * this method will return true in respect of any such model.
+ */
+ public boolean isObjectModelSupported(String model) {
+ boolean debug = System.getProperty("jaxp.debug") != null;
+ if (debug) {
+ System.err.println("JAXP: Calling " + getClass().getName() + ".isObjectModelSupported(\"" + model + "\")");
+ System.err.println("JAXP: -- returning " + silentIsObjectModelSupported(model));
+ }
+ return silentIsObjectModelSupported(model);
+ }
+
+ private boolean silentIsObjectModelSupported(String model) {
+ return model.equals(NamespaceConstant.OBJECT_MODEL_SAXON) || config.getExternalObjectModel(model) != null;
+ }
+
+ /**
+ * Set a feature of this XPath implementation. The features currently
+ * recognized are:
+ * <ul>
+ * <li> {@link XMLConstants#FEATURE_SECURE_PROCESSING} </li>
+ * <li> {@link net.sf.saxon.lib.FeatureKeys#SCHEMA_VALIDATION}: requests schema validation of source documents.
+ * The property is rejected if the configuration is not schema-aware. </li>
+ * </ul>
+ * <p>In addition, any Saxon configuration feature (listed in {@link FeatureKeys} can be used
+ * provided the value is a boolean. (For non-boolean configuration properties, drop down to the underlying
+ * Saxon {@link Configuration} object and call <code>setConfigurationProperty()</code>)</p>
+ * @param feature a URI identifying the feature
+ * @param b true to set the feature on, false to set it off
+ * @throws XPathFactoryConfigurationException if the feature name is not recognized
+ */
+
+ public void setFeature(String feature, boolean b) throws XPathFactoryConfigurationException {
+ if (feature.equals(FEATURE_SECURE_PROCESSING)) {
+ config.setBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS, !b);
+ } else if (feature.equals(FeatureKeys.SCHEMA_VALIDATION)) {
+ config.setSchemaValidationMode(b ? Validation.STRICT : Validation.STRIP);
+ } else {
+ try {
+ config.setBooleanProperty(feature, b);
+ } catch (IllegalArgumentException err) {
+ throw new XPathFactoryConfigurationException("Unknown or non-boolean feature: " + feature);
+ }
+ }
+ }
+
+ /**
+ * Get a feature of this XPath implementation. The only features currently
+ * recognized are:
+ * <ul>
+ * <li> {@link #FEATURE_SECURE_PROCESSING} </li>
+ * <li> {@link net.sf.saxon.lib.FeatureKeys#SCHEMA_VALIDATION}: requests schema validation of source documents. </li>
+ * </ul>
+ * <p>In addition, any Saxon configuration feature (listed in {@link FeatureKeys} can be used
+ * provided the value is a boolean. (For non-boolean configuration properties, drop down to the underlying
+ * Saxon {@link Configuration} object and call <code>getConfigurationProperty()</code>)</p>
+ * @param feature a URI identifying the feature
+ * @return true if the feature is on, false if it is off
+ * @throws XPathFactoryConfigurationException if the feature name is not recognized
+ */
+
+ public boolean getFeature(String feature) throws XPathFactoryConfigurationException {
+ if (feature.equals(FEATURE_SECURE_PROCESSING)) {
+ return !config.getBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS);
+ } else if (feature.equals(FeatureKeys.SCHEMA_VALIDATION)) {
+ return config.getSchemaValidationMode() == Validation.STRICT;
+ } else {
+ try {
+ Object o = config.getConfigurationProperty(feature);
+ if (o instanceof Boolean) {
+ return ((Boolean)o);
+ } else {
+ throw new XPathFactoryConfigurationException(
+ "Configuration property " + feature + " is not a boolean (it is an instance of " + o.getClass() + ")");
+ }
+ } catch (IllegalArgumentException e) {
+ throw new XPathFactoryConfigurationException("Unknown feature: " + feature);
+ }
+ }
+ }
+
+ /**
+ * Set a resolver for XPath variables. This will be used to obtain the value of
+ * any variable referenced in an XPath expression. The variable resolver must be allocated
+ * before the expression is compiled, but it will only be called when the expression
+ * is evaluated.
+ * @param xPathVariableResolver The object used to resolve references to variables.
+ */
+ public void setXPathVariableResolver(XPathVariableResolver xPathVariableResolver) {
+ variableResolver = xPathVariableResolver;
+ }
+
+ /**
+ * Set a resolver for XPath functions. This will be used to obtain an implementation
+ * of any external function referenced in an XPath expression. This is not required for
+ * system functions, Saxon extension functions, constructor functions named after types,
+ * or extension functions bound using a namespace that maps to a Java class.
+ * @param xPathFunctionResolver The object used to resolve references to external functions.
+ */
+
+ public void setXPathFunctionResolver(XPathFunctionResolver xPathFunctionResolver) {
+ functionResolver = xPathFunctionResolver;
+ }
+
+ /**
+ * Create an XPath evaluator
+ * @return an XPath object, which can be used to compile and execute XPath expressions.
+ */
+ /*@NotNull*/ public XPath newXPath() {
+ XPathEvaluator xpath = new XPathEvaluator(config);
+ xpath.setXPathFunctionResolver(functionResolver);
+ xpath.setXPathVariableResolver(variableResolver);
+ return xpath;
+ }
+
+ private static String FEATURE_SECURE_PROCESSING = XMLConstants.FEATURE_SECURE_PROCESSING;
+ // "http://javax.xml.XMLConstants/feature/secure-processing";
+
+}
+
diff --git a/sf/saxon/xpath/XPathFunctionCall.java b/sf/saxon/xpath/XPathFunctionCall.java
new file mode 100644
index 0000000..41ff536
--- /dev/null
+++ b/sf/saxon/xpath/XPathFunctionCall.java
@@ -0,0 +1,201 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xpath;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.*;
+import net.sf.saxon.expr.parser.ExpressionVisitor;
+import net.sf.saxon.expr.parser.PathMap;
+import net.sf.saxon.om.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.type.Type;
+import net.sf.saxon.type.TypeHierarchy;
+import net.sf.saxon.value.EmptySequence;
+
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+import java.util.ArrayList;
+import java.util.List;
+
+
+
+/**
+* This class is an expression that calls an external function supplied using the
+ * JAXP XPathFunction interface
+*/
+
+public class XPathFunctionCall extends FunctionCall implements Callable {
+
+ private XPathFunction function;
+ /**
+ * Default constructor
+ */
+
+ public XPathFunctionCall(XPathFunction function) {
+ this.function = function;
+ }
+
+ /**
+ * preEvaluate: this method suppresses compile-time evaluation by doing nothing
+ * (because the external function might have side-effects and might use the context)
+ * @param visitor an expression visitor
+ */
+
+ /*@NotNull*/ public Expression preEvaluate(ExpressionVisitor visitor) {
+ return this;
+ }
+
+
+ /**
+ * Method called by the expression parser when all arguments have been supplied
+ */
+
+ public void checkArguments(/*@NotNull*/ ExpressionVisitor visitor) throws XPathException {
+ }
+
+
+ /**
+ * Determine which aspects of the context the expression depends on. XPath external
+ * functions are given no access to context information so they cannot have any
+ * dependencies on it.
+ */
+
+ public int getIntrinsicDependencies() {
+ return 0;
+ }
+
+
+ /**
+ * Copy an expression. This makes a deep copy.
+ *
+ * @return the copy of the original expression
+ */
+
+ /*@NotNull*/ public Expression copy() {
+ throw new UnsupportedOperationException("XPathFunctionCall.copy()");
+ }
+
+
+ /**
+ * Add a representation of this expression to a PathMap. The PathMap captures a map of the nodes visited
+ * by an expression in a source tree.
+ * <p/>
+ * <p>The default implementation of this method assumes that an expression does no navigation other than
+ * the navigation done by evaluating its subexpressions, and that the subexpressions are evaluated in the
+ * same context as the containing expression. The method must be overridden for any expression
+ * where these assumptions do not hold. For example, implementations exist for AxisExpression, ParentExpression,
+ * and RootExpression (because they perform navigation), and for the doc(), document(), and collection()
+ * functions because they create a new navigation root. Implementations also exist for PathExpression and
+ * FilterExpression because they have subexpressions that are evaluated in a different context from the
+ * calling expression.</p>
+ *
+ * @param pathMap the PathMap to which the expression should be added
+ * @param pathMapNodeSet
+ * @return the pathMapNode representing the focus established by this expression, in the case where this
+ * expression is the first operand of a path expression or filter expression. For an expression that does
+ * navigation, it represents the end of the arc in the path map that describes the navigation route. For other
+ * expressions, it is the same as the input pathMapNode.
+ */
+
+ /*@NotNull*/ public PathMap.PathMapNodeSet addToPathMap(/*@NotNull*/ PathMap pathMap, PathMap.PathMapNodeSet pathMapNodeSet) {
+ return addExternalFunctionCallToPathMap(pathMap, pathMapNodeSet);
+ }
+
+ /**
+ * Evaluate the function. <br>
+ * @param context The context in which the function is to be evaluated
+ * @return a Value representing the result of the function.
+ * @throws XPathException if the function cannot be evaluated.
+ */
+
+ /*@NotNull*/
+ public SequenceIterator iterate(/*@NotNull*/ XPathContext context) throws XPathException {
+ Sequence[] argValues = new Sequence[argument.length];
+ for (int i=0; i<argValues.length; i++) {
+ argValues[i] = SequenceTool.toLazySequence(argument[i].iterate(context));
+ }
+ return call(context, argValues).iterate();
+ }
+
+
+ /**
+ * Call an extension function previously identified using the bind() method. A subclass
+ * can override this method.
+ *
+ *
+ * @param context The XPath dynamic context
+ * @param argValues The values of the arguments
+ * @return The value returned by the extension function
+ */
+
+ /*@Nullable*/ public Sequence call(/*@NotNull*/ XPathContext context, Sequence[] argValues /*@NotNull*/) throws XPathException {
+ // An argument is supplied to the extension function as a List, unless it is a singleton.
+ // The items within the list are converted to the "natural Java representation", for example
+ // a double is passed as a Double, a string as a String.
+ List convertedArgs = new ArrayList(argValues.length);
+ Configuration config = context.getConfiguration();
+ for (int i=0; i<argValues.length; i++) {
+ List target = new ArrayList();
+ SequenceIterator iter = argValues[i].iterate();
+ while (true) {
+ Item item = iter.next();
+ if (item == null) {
+ break;
+ }
+ PJConverter converter = PJConverter.allocate(
+ config, Type.getItemType(item, config.getTypeHierarchy()), StaticProperty.ALLOWS_ONE, Object.class);
+ target.add(converter.convert(item, Object.class, context));
+ }
+ if (target.size() == 1) {
+ convertedArgs.add(target.get(0));
+ } else {
+ convertedArgs.add(target);
+ }
+ }
+
+ try {
+ Object result = function.evaluate(convertedArgs);
+ if (result == null) {
+ return EmptySequence.getInstance();
+ }
+ JPConverter converter = JPConverter.allocate(result.getClass(), config);
+ return converter.convert(result, context);
+ } catch (XPathFunctionException e) {
+ throw new XPathException(e);
+ }
+ }
+
+ /**
+ * Determine the data type of the expression, if possible. All expressions return
+ * sequences, in general; this method determines the type of the items within the
+ * sequence, assuming that (a) this is known in advance, and (b) it is the same for
+ * all items in the sequence.
+ *
+ * <p>This method will always return a result, though it may be the best approximation
+ * that is available at the time.</p>
+ *
+ * @return the item type
+ * @param th the type hierarchy cache
+ */
+
+ /*@NotNull*/ public ItemType getItemType(TypeHierarchy th) {
+ return Type.ITEM_TYPE;
+ }
+
+ /**
+ * Determine the cardinality of the result
+ * @return ZERO_OR_MORE (we don't know)
+ */
+ public int computeCardinality() {
+ return StaticProperty.ALLOWS_ZERO_OR_MORE;
+ }
+
+
+}
+
diff --git a/sf/saxon/xpath/XPathFunctionLibrary.java b/sf/saxon/xpath/XPathFunctionLibrary.java
new file mode 100644
index 0000000..0ff72d7
--- /dev/null
+++ b/sf/saxon/xpath/XPathFunctionLibrary.java
@@ -0,0 +1,160 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xpath;
+
+import com.saxonica.functions.hof.CallableFunctionItem;
+import com.saxonica.functions.hof.SpecificFunctionType;
+import net.sf.saxon.expr.Container;
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.StaticContext;
+import net.sf.saxon.functions.FunctionLibrary;
+import net.sf.saxon.om.FunctionItem;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.FunctionItemType;
+import net.sf.saxon.value.SequenceType;
+
+import javax.xml.namespace.QName;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionResolver;
+import java.util.Arrays;
+
+/**
+ * The XPathFunctionLibrary is a FunctionLibrary that supports binding of XPath function
+ * calls to instances of the JAXP XPathFunction interface returned by an XPathFunctionResolver.
+ */
+
+public class XPathFunctionLibrary implements FunctionLibrary {
+
+ private XPathFunctionResolver resolver;
+
+ /**
+ * Construct a XPathFunctionLibrary
+ */
+
+ public XPathFunctionLibrary() {
+ }
+
+ /**
+ * Set the resolver
+ * @param resolver The XPathFunctionResolver wrapped by this FunctionLibrary
+ */
+
+ public void setXPathFunctionResolver(XPathFunctionResolver resolver) {
+ this.resolver = resolver;
+ }
+
+ /**
+ * Get the resolver
+ * @return the XPathFunctionResolver wrapped by this FunctionLibrary
+ */
+
+ public XPathFunctionResolver getXPathFunctionResolver() {
+ return resolver;
+ }
+
+ /**
+ * Bind a function, given the URI and local parts of the function name,
+ * and the list of expressions supplied as arguments. This method is called at compile
+ * time.
+ *
+ *
+ * @param functionName
+ * @param arity
+ * @param staticArgs The expressions supplied statically in the function call. The intention is
+ * that the static type of the arguments (obtainable via getItemType() and getCardinality() may
+ * be used as part of the binding algorithm.
+ * @param env
+ * @param container
+ * @return An object representing the extension function to be called, if one is found;
+ * null if no extension function was found matching the required name, arity, or signature.
+ */
+
+ /*@Nullable*/ public Expression bind(/*@NotNull*/ StructuredQName functionName, /*@NotNull*/ int arity, Expression[] staticArgs, StaticContext env, Container container)
+ throws XPathException {
+ if (resolver == null) {
+ return null;
+ }
+ QName name = new QName(functionName.getURI(), functionName.getLocalPart());
+ XPathFunction function = resolver.resolveFunction(name, staticArgs.length);
+ if (function == null) {
+ return null;
+ }
+ XPathFunctionCall fc = new XPathFunctionCall(function);
+ fc.setArguments(staticArgs);
+ return fc;
+ }
+
+//#ifdefined HOF
+ /**
+ * Test whether a function with a given name and arity is available; if so, return a function
+ * item that can be dynamically called.
+ * <p/>
+ * <p>This supports the function-lookup() function in XPath 3.0.</p>
+ *
+ *
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @param staticContext the static context to be used by the function, in the event that
+ * it is a system function with dependencies on the static context
+ * @return if a function of this name and arity is available for calling, then a corresponding
+ * function item; or null if the function does not exist
+ * @throws net.sf.saxon.trans.XPathException
+ * in the event of certain errors, for example attempting to get a function
+ * that is private
+ */
+ public FunctionItem getFunctionItem(StructuredQName functionName, int arity, StaticContext staticContext) throws XPathException {
+ if (resolver == null) {
+ return null;
+ }
+ QName name = new QName(functionName.getURI(), functionName.getLocalPart());
+ XPathFunction function = resolver.resolveFunction(name, arity);
+ if (function == null) {
+ return null;
+ }
+ XPathFunctionCall functionCall = new XPathFunctionCall(function);
+ SequenceType[] argTypes = new SequenceType[arity];
+ Arrays.fill(argTypes, SequenceType.ANY_SEQUENCE);
+ FunctionItemType functionType = new SpecificFunctionType(argTypes, SequenceType.ANY_SEQUENCE);
+ return new CallableFunctionItem(functionName, arity, functionCall, functionType);
+ }
+//#endif
+
+
+ /**
+ * Test whether a function with a given name and arity is available
+ * <p>This supports the function-available() function in XSLT.</p>
+ *
+ * @param functionName the qualified name of the function being called
+ * @param arity The number of arguments.
+ * @return true if a function of this name and arity is available for calling
+ */
+ public boolean isAvailable(StructuredQName functionName, int arity) {
+ return resolver != null &&
+ resolver.resolveFunction(
+ new QName(functionName.getURI(), functionName.getLocalPart()), arity) != null;
+ }
+
+ /**
+ * This method creates a copy of a FunctionLibrary: if the original FunctionLibrary allows
+ * new functions to be added, then additions to this copy will not affect the original, or
+ * vice versa.
+ *
+ * @return a copy of this function library. This must be an instance of the original class.
+ */
+
+ /*@NotNull*/ public FunctionLibrary copy() {
+ XPathFunctionLibrary xfl = new XPathFunctionLibrary();
+ xfl.resolver = resolver;
+ return xfl;
+ }
+
+
+}
+
diff --git a/sf/saxon/xpath/package.html b/sf/saxon/xpath/package.html
new file mode 100644
index 0000000..8f5b721
--- /dev/null
+++ b/sf/saxon/xpath/package.html
@@ -0,0 +1,47 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.xpath</title>
+</head>
+
+<body>
+
+<p>This package is Saxon's implementation of the JAXP API designed for executing XPath 1.0 expressions
+directly from a Java application. Saxon extends the interface to handle XPath 2.0, though if the application
+ makes extensive use of XPath 2.0 features, then the <b>s9api</b> interface offers a better fit
+ to the XPath 2.0 data model. The API can be used either in a free-standing
+Java application (that is, where there is no XSLT stylesheet), or it can be
+used from within Java extension functions called from XPath expressions within
+a stylesheet.</p>
+
+<p>The API itself is defined by JAXP 1.3, in interfaces such as <code>javax.xml.xpath.XPath</code>.<
+These interfaces are included in Java Standard Edition from JDK 1.5 onwards.</p>
+
+
+<p>The interfaces provided by Saxon extend the JAXP 1.3 interfaces in various ways. There
+are three reasons for this:</p>
+
+<ul>
+<li><p>Saxon supports XPath 2.0 rather than 1.0</p></li>
+<li><p>The package retains support for some interfaces that were provided before JAXP 1.3 was released.
+(These might disappear in the course of time).</p></li>
+<li><p>There are methods that allow an escape into Saxon's more low-level APIs, needed by
+anyone doing serious software integration.</p></li>
+</ul>
+
+<p>An alternative XPath interface, which is not dependent on JAXP 1.3, is available in the
+package <code>net.sf.saxon.sxpath</code>. However, for most applications the preferred interface is
+the s9api {@link net.sf.saxon.s9api.XPathCompiler}</p>
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+30 July 2010</i></p>
+</body>
+</html>
diff --git a/sf/saxon/xqj/Closable.java b/sf/saxon/xqj/Closable.java
new file mode 100644
index 0000000..04cd8bc
--- /dev/null
+++ b/sf/saxon/xqj/Closable.java
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import javax.xml.xquery.XQException;
+
+/**
+ * This class represents the common ability of many XQJ classes to be closed. Note that closing an object
+ * serves no useful purpose in the Saxon implementation; this complex machinery is provided merely to satisfy
+ * the XQJ interface, which is designed to accommodate a client-server implementation.
+ */
+public abstract class Closable {
+
+ /*@Nullable*/ private Closable container = null;
+ private boolean closed = false;
+
+ /**
+ * Set the container of this closable object. Closing the container causes this object to be
+ * treated as closed itself
+ * @param container the container of this closable object
+ */
+
+ public final void setClosableContainer(Closable container) {
+ this.container = container;
+ }
+
+ /**
+ * Close this object
+ */
+
+ public final void close() {
+ closed = true;
+ }
+
+ /**
+ * Ask whether this object has been closed.
+ * @return true if either the object itself or its container has been closed
+ */
+
+ public final boolean isClosed() {
+ if (container != null && container.isClosed()) {
+ close();
+ }
+ return closed;
+ }
+
+ /**
+ * Check whether this object has been closed (either directly, or by closing its container)
+ * @throws XQException if the object has been closed
+ */
+
+ final void checkNotClosed() throws XQException {
+ if (isClosed()) {
+ throw new XQException("The XQJ object has been closed");
+ }
+ }
+}
+
diff --git a/sf/saxon/xqj/ObjectConverter.java b/sf/saxon/xqj/ObjectConverter.java
new file mode 100644
index 0000000..6112483
--- /dev/null
+++ b/sf/saxon/xqj/ObjectConverter.java
@@ -0,0 +1,54 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.om.Item;
+
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQItemAccessor;
+import javax.xml.xquery.XQItemType;
+
+/**
+ * This interface is based on the "CommonHandler" concept defined in early drafts of XQJ. It defines the data
+ * conversion routines used by the Saxon XQJ implementation to convert between native Java objects and XDM values.
+ * Most applications will use the Saxon-supplied implementation {@link StandardObjectConverter}, but it is possible
+ * to supply an alternative implementation using the method {@link SaxonXQDataFactory#setObjectConverter}
+ */
+public interface ObjectConverter {
+
+ /**
+ * Convert an Item to a Java object
+ * @param xqItemAccessor the XQJ object representing the item to be converted
+ * @return the Java object that results from the conversion
+ * @throws XQException
+ */
+
+ Object toObject(XQItemAccessor xqItemAccessor) throws XQException;
+
+ /**
+ * Convert a Java object to an Item, when no information is available about the required type
+ * @param value the supplied Java object. If null is supplied, null is returned.
+ * @return the Item that results from the conversion
+ * @throws XQException if the Java object cannot be converted to an XQItem
+ */
+
+ Item convertToItem(Object value) throws XQException;
+
+ /**
+ * Convert a Java object to an Item, when a required type has been specified. Note that Saxon only calls
+ * this method when none of the standard conversions defined in the XQJ specification is able to handle
+ * the object.
+ * @param value the supplied Java object. If null is supplied, null is returned.
+ * @param type the required XPath data type
+ * @return the Item that results from the conversion
+ * @throws XQException if the Java object cannot be converted to an XQItem
+ */
+
+ public Item convertToItem(Object value, XQItemType type) throws XQException;
+}
+
diff --git a/sf/saxon/xqj/SaxonDuration.java b/sf/saxon/xqj/SaxonDuration.java
new file mode 100644
index 0000000..45834f0
--- /dev/null
+++ b/sf/saxon/xqj/SaxonDuration.java
@@ -0,0 +1,333 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.*;
+
+import javax.xml.datatype.DatatypeConstants;
+import javax.xml.datatype.Duration;
+import javax.xml.namespace.QName;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Calendar;
+
+/**
+ * Saxon implementation of the JAXP class javax.xml.datatype.Duration. This is currently used only by the XQJ
+ * interface for XQuery: the normal representation of a duration in Saxon is the class {@link DurationValue}.
+ * <p>
+ * The JAXP specification for this class defines it in terms of XML Schema 1.0 semantics. This defines a structure
+ * with six independent components (year, month, day, hour, minute, second). This implementation is more aligned
+ * to the XPath 2.0 semantics of the data type, which essentially defines duration as an integer number of months plus
+ * a decimal number of seconds.
+ */
+public class SaxonDuration extends Duration {
+
+ private DurationValue duration;
+
+ /**
+ * Create a SaxonDuration that wraps a supplied DurationValue
+ * @param duration the value to be wrapped.
+ */
+
+ public SaxonDuration(DurationValue duration) {
+ this.duration = duration;
+ }
+
+ /**
+ * Get the underlying DurationValue
+ * @return the underlying DurationValue
+ */
+
+ public DurationValue getDurationValue() {
+ return duration;
+ }
+
+ /**
+ * Get the type of this duration, as one of the values xs:duration, xs:dayTimeDuration, or
+ * xs:yearMonthDuration. (Note that the XML Schema namespace URI is used, whereas the current
+ * implementation of the superclass uses a provisional URI allocated in a 2003 W3C working draft)
+ * @return the type of this duration, as one of the values xs:duration, xs:dayTimeDuration, or
+ * xs:yearMonthDuration
+ */
+
+ public QName getXMLSchemaType() {
+ if (duration instanceof DayTimeDurationValue) {
+ return new QName(NamespaceConstant.SCHEMA, "dayTimeDuration");
+ } else if (duration instanceof YearMonthDurationValue) {
+ return new QName(NamespaceConstant.SCHEMA, "yearMonthDuration");
+ } else {
+ return new QName(NamespaceConstant.SCHEMA, "duration");
+ }
+ }
+
+ /**
+ * Returns the sign of this duration in -1,0, or 1.
+ *
+ * @return -1 if this duration is negative, 0 if the duration is zero,
+ * and 1 if the duration is positive.
+ */
+ public int getSign() {
+ return duration.signum();
+ }
+
+ /**
+ * Gets the value of a field.
+ * <p/>
+ * Fields of a duration object may contain arbitrary large value.
+ * Therefore this method is designed to return a {@link Number} object.
+ * <p/>
+ * In case of YEARS, MONTHS, DAYS, HOURS, and MINUTES, the returned
+ * number will be a non-negative integer. In case of seconds,
+ * the returned number may be a non-negative decimal value.
+ * <p/>
+ * The Saxon implementation of duration uses normalized values. This means
+ * that the YEARS and DAYS fields may be arbitrarily large, but other
+ * components will be limited in size: for example MINUTES will never
+ * exceed 60 and MONTHS will never exceed 12.
+ *
+ * @param field one of the six Field constants (YEARS, MONTHS, DAYS, HOURS,
+ * MINUTES, or SECONDS.)
+ * @return If the specified field is present, this method returns
+ * a non-null non-negative {@link Number} object that
+ * represents its value. If it is not present, return null.
+ * For YEARS, MONTHS, DAYS, HOURS, and MINUTES, this method
+ * returns a {@link java.math.BigInteger} object. For SECONDS, this
+ * method returns a {@link java.math.BigDecimal}.
+ * @throws NullPointerException If the <code>field</code> is <code>null</code>.
+ */
+ public Number getField(DatatypeConstants.Field field) {
+ try {
+ if (field == DatatypeConstants.YEARS) {
+ return BigInteger.valueOf(((Int64Value)duration.getComponent(Component.YEAR)).longValue());
+ } else if (field == DatatypeConstants.MONTHS) {
+ return BigInteger.valueOf(((Int64Value)duration.getComponent(Component.MONTH)).longValue());
+ } else if (field == DatatypeConstants.DAYS) {
+ return BigInteger.valueOf(((Int64Value)duration.getComponent(Component.DAY)).longValue());
+ } else if (field == DatatypeConstants.HOURS) {
+ return BigInteger.valueOf(((Int64Value)duration.getComponent(Component.HOURS)).longValue());
+ } else if (field == DatatypeConstants.MINUTES) {
+ return BigInteger.valueOf(((Int64Value)duration.getComponent(Component.MINUTES)).longValue());
+ } else if (field == DatatypeConstants.SECONDS) {
+ return (((DecimalValue)duration.getComponent(Component.SECONDS)).getDecimalValue());
+ } else {
+ throw new IllegalArgumentException("Invalid field");
+ }
+ } catch (XPathException e) {
+ throw new AssertionError("Component extraction on duration failed");
+ }
+ }
+
+ /**
+ * Checks if a field is set. In this implementation, all fields are always set.
+ * @param field one of the six Field constants (YEARS, MONTHS, DAYS, HOURS,
+ * MINUTES, or SECONDS.)
+ * @return This implementation always returns true.
+ */
+ public boolean isSet(DatatypeConstants.Field field) {
+ return true;
+ }
+
+ /**
+ * <p>Computes a new duration whose value is <code>this+rhs</code>.</p>
+ * <p>This implementation follows the XPath semantics. This means that the operation will fail
+ * if the duration is not a yearMonthDuration or a dayTimeDuration.
+ * @param rhs <code>Duration</code> to add to this <code>Duration</code>
+ * @return non-null valid Duration object.
+ * @throws NullPointerException If the rhs parameter is null.
+ * @throws IllegalStateException If the durations are not both dayTimeDurations, or
+ * both yearMonthDurations.
+ * @see #subtract(javax.xml.datatype.Duration)
+ */
+ public Duration add(Duration rhs) {
+ try {
+ return new SaxonDuration(duration.add(((SaxonDuration)rhs).duration));
+ } catch (XPathException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * <p>Computes a new duration whose value is <code>this-rhs</code>.</p>
+ * <p>This implementation follows the XPath semantics. This means that the operation will fail
+ * if the duration is not a yearMonthDuration or a dayTimeDuration.
+ * @param rhs <code>Duration</code> to subtract from this <code>Duration</code>
+ * @return non-null valid Duration object.
+ * @throws NullPointerException If the rhs parameter is null.
+ * @throws IllegalStateException If the durations are not both dayTimeDurations, or
+ * both yearMonthDurations.
+ * @see #add(javax.xml.datatype.Duration)
+ */
+ public Duration subtract(Duration rhs) {
+ try {
+ return new SaxonDuration(duration.subtract(((SaxonDuration)rhs).duration));
+ } catch (XPathException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Adds this duration to a {@link java.util.Calendar} object.
+ * <p/>
+ * <p/>
+ * Calls {@link java.util.Calendar#add(int,int)} in the
+ * order of YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, and MILLISECONDS
+ * if those fields are present. Because the {@link java.util.Calendar} class
+ * uses int to hold values, there are cases where this method
+ * won't work correctly (for example if values of fields
+ * exceed the range of int.)
+ * </p>
+ * <p/>
+ * <p/>
+ * Also, since this duration class is a Gregorian duration, this
+ * method will not work correctly if the given {@link java.util.Calendar}
+ * object is based on some other calendar systems.
+ * </p>
+ * <p/>
+ * <p/>
+ * Any fractional parts of this <code>Duration</code> object
+ * beyond milliseconds will be simply ignored. For example, if
+ * this duration is "P1.23456S", then 1 is added to SECONDS,
+ * 234 is added to MILLISECONDS, and the rest will be unused.
+ * </p>
+ * <p/>
+ * <p/>
+ * Note that because {@link java.util.Calendar#add(int, int)} is using
+ * <tt>int</tt>, <code>Duration</code> with values beyond the
+ * range of <tt>int</tt> in its fields
+ * will cause overflow/underflow to the given {@link java.util.Calendar}.
+ * {@link javax.xml.datatype.XMLGregorianCalendar#add(javax.xml.datatype.Duration)} provides the same
+ * basic operation as this method while avoiding
+ * the overflow/underflow issues.
+ *
+ * @param calendar A calendar object whose value will be modified.
+ * @throws NullPointerException if the calendar parameter is null.
+ */
+ public void addTo(Calendar calendar) {
+ int sign = getSign();
+ if (sign == 0) {
+ return;
+ }
+ try {
+ calendar.add(getYears()*sign, Calendar.YEAR);
+ calendar.add(getMonths()*sign, Calendar.MONTH);
+ calendar.add(getDays()*sign, Calendar.DAY_OF_MONTH);
+ calendar.add(getHours()*sign, Calendar.HOUR_OF_DAY);
+ calendar.add(getMinutes()*sign, Calendar.MINUTE);
+ calendar.add((int)((Int64Value)duration.getComponent(Component.WHOLE_SECONDS)).longValue()*sign,
+ Calendar.SECOND);
+ calendar.add((int)((Int64Value)duration.getComponent(Component.MICROSECONDS)).longValue()*sign/1000,
+ Calendar.MILLISECOND);
+ } catch (XPathException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Computes a new duration whose value is <code>factor</code> times
+ * longer than the value of this duration.
+ * <p/>
+ * This implementation follows the XPath semantics. This means that it is defined
+ * only on yearMonthDuration and dayTimeDuration. Other cases produce an IllegalStateException.
+ *
+ * @param factor to multiply by
+ * @return returns a non-null valid <code>Duration</code> object
+ * @throws IllegalStateException if operation produces fraction in
+ * the months field.
+ * @throws NullPointerException if the <code>factor</code> parameter is
+ * <code>null</code>.
+ */
+ public Duration multiply(BigDecimal factor) {
+ try {
+ return new SaxonDuration(duration.multiply(factor.doubleValue()));
+ } catch (XPathException e) {
+ throw new IllegalStateException(e.getMessage());
+ }
+ }
+
+ /**
+ * Returns a new <code>Duration</code> object whose
+ * value is <code>-this</code>.
+ * <p/>
+ * <p/>
+ * Since the <code>Duration</code> class is immutable, this method
+ * doesn't change the value of this object. It simply computes
+ * a new Duration object and returns it.
+ *
+ * @return always return a non-null valid <code>Duration</code> object.
+ */
+ public Duration negate() {
+ return new SaxonDuration(duration.negate());
+ }
+
+ /**
+ * <p>Converts the years and months fields into the days field
+ * by using a specific time instant as the reference point.</p>
+ * <p/>
+ * This implementation does not support this method
+ * @param startTimeInstant <code>Calendar</code> reference point.
+ * @return <code>Duration</code> of years and months of this <code>Duration</code> as days.
+ * @throws NullPointerException If the startTimeInstant parameter is null.
+ * @throws UnsupportedOperationException Always thrown by this implementation.
+ */
+ public Duration normalizeWith(Calendar startTimeInstant) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * <p>Partial order relation comparison with this <code>Duration</code> instance.</p>
+ * <p>This implementation follows the XPath semantics. This means that the result is defined only
+ * for dayTimeDuration and yearMonthDuration values, and the result is never indeterminate.
+ * <p/>
+ * <p>Return:</p>
+ * <ul>
+ * <li>{@link javax.xml.datatype.DatatypeConstants#LESSER}
+ * if this <code>Duration</code> is shorter than <code>duration</code> parameter</li>
+ * <li>{@link javax.xml.datatype.DatatypeConstants#EQUAL}
+ * if this <code>Duration</code> is equal to <code>duration</code> parameter</li>
+ * <li>{@link javax.xml.datatype.DatatypeConstants#GREATER}
+ * if this <code>Duration</code> is longer than <code>duration</code> parameter</li>
+ * <li>{@link javax.xml.datatype.DatatypeConstants#INDETERMINATE}
+ * if a conclusive partial order relation cannot be determined</li>
+ * </ul>
+ *
+ * @param rhs duration to compare
+ * @return the relationship between <code>this</code> <code>Duration</code>and <code>duration</code> parameter as
+ * {@link javax.xml.datatype.DatatypeConstants#LESSER},
+ * {@link javax.xml.datatype.DatatypeConstants#EQUAL},
+ * {@link javax.xml.datatype.DatatypeConstants#GREATER}
+ * or {@link javax.xml.datatype.DatatypeConstants#INDETERMINATE}.
+ * @throws UnsupportedOperationException If the underlying implementation
+ * cannot reasonably process the request, e.g. W3C XML Schema allows for
+ * arbitrarily large/small/precise values, the request may be beyond the
+ * implementations capability.
+ * @throws NullPointerException if <code>duration</code> is <code>null</code>.
+ * @throws IllegalArgumentException if the operands are not dayTimeDuration or yearMonthDuration values.
+ * @see #isShorterThan(javax.xml.datatype.Duration)
+ * @see #isLongerThan(javax.xml.datatype.Duration)
+ */
+ public int compare(/*@NotNull*/ Duration rhs) {
+ if (!(rhs instanceof SaxonDuration)) {
+ throw new IllegalArgumentException("Supplied duration is not a SaxonDuration");
+ }
+ Comparable c0 = duration.getSchemaComparable();
+ Comparable c1 = ((SaxonDuration)rhs).duration.getSchemaComparable();
+ return c0.compareTo(c1);
+ }
+
+ /**
+ * Returns a hash code consistent with the definition of the equals method.
+ *
+ * @see Object#hashCode()
+ */
+ public int hashCode() {
+ return duration.hashCode();
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXMLGregorianCalendar.java b/sf/saxon/xqj/SaxonXMLGregorianCalendar.java
new file mode 100644
index 0000000..6ec3ffb
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXMLGregorianCalendar.java
@@ -0,0 +1,759 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.functions.Component;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.value.*;
+
+import javax.xml.datatype.DatatypeConstants;
+import javax.xml.datatype.Duration;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+/**
+ * Saxon implementation of the JAXP class javax.xml.datatype.XMLGregorianCalendar.
+ * This is currently used only by the XQJ interface for XQuery: the normal representation of a
+ * date, time, or dateTime value in Saxon is with a subclass of {@link CalendarValue}
+ * <p>
+ * The JAXP specification for this class defines it in terms of XML Schema 1.0 semantics.
+ * This implementation is more aligned to the XPath 2.0 semantics of the data types.
+ * <p>
+ * Note that this class, unlike the representations of all other data types, is mutable.
+ */
+public class SaxonXMLGregorianCalendar extends XMLGregorianCalendar {
+
+ /*@Nullable*/ private CalendarValue calendarValue;
+ /*@Nullable*/ private BigInteger year;
+ private int month = DatatypeConstants.FIELD_UNDEFINED;
+ private int day = DatatypeConstants.FIELD_UNDEFINED;
+ private int hour = DatatypeConstants.FIELD_UNDEFINED;
+ private int minute = DatatypeConstants.FIELD_UNDEFINED;
+ private int second = DatatypeConstants.FIELD_UNDEFINED;
+ private int microsecond = DatatypeConstants.FIELD_UNDEFINED;
+ private int tzOffset = DatatypeConstants.FIELD_UNDEFINED;
+
+ /**
+ * Create a SaxonXMLGregorianCalendar from a Saxon CalendarValue object
+ * @param value the CalendarValue
+ */
+
+ public SaxonXMLGregorianCalendar(/*@NotNull*/ CalendarValue value) {
+ clear();
+ setCalendarValue(value);
+ }
+
+ private SaxonXMLGregorianCalendar() {
+ }
+
+ /**
+ * Set the calendar value of this object
+ * @param value the calendar value
+ */
+
+ public void setCalendarValue(/*@NotNull*/ CalendarValue value) {
+ calendarValue = value;
+ try {
+ if (value instanceof GYearValue) {
+ year = BigInteger.valueOf(((Int64Value)value.getComponent(Component.YEAR)).longValue());
+ } else if (value instanceof GYearMonthValue) {
+ year = BigInteger.valueOf(((Int64Value)value.getComponent(Component.YEAR)).longValue());
+ month = (int)((Int64Value)value.getComponent(Component.MONTH)).longValue();
+ } else if (value instanceof GMonthValue) {
+ month = (int)((Int64Value)value.getComponent(Component.MONTH)).longValue();
+ } else if (value instanceof GMonthDayValue) {
+ month = (int)((Int64Value)value.getComponent(Component.MONTH)).longValue();
+ day = (int)((Int64Value)value.getComponent(Component.DAY)).longValue();
+ } else if (value instanceof GDayValue) {
+ day = (int)((Int64Value)value.getComponent(Component.DAY)).longValue();
+ } else if (value instanceof DateValue) {
+ year = BigInteger.valueOf(((Int64Value)value.getComponent(Component.YEAR)).longValue());
+ month = (int)((Int64Value)value.getComponent(Component.MONTH)).longValue();
+ day = (int)((Int64Value)value.getComponent(Component.DAY)).longValue();
+ } else if (value instanceof TimeValue) {
+ hour = (int)((Int64Value)value.getComponent(Component.HOURS)).longValue();
+ minute = (int)((Int64Value)value.getComponent(Component.MINUTES)).longValue();
+ second = (int)((Int64Value)value.getComponent(Component.WHOLE_SECONDS)).longValue();
+ microsecond = (int)((Int64Value)value.getComponent(Component.MICROSECONDS)).longValue();
+ } else {
+ year = BigInteger.valueOf(((Int64Value)value.getComponent(Component.YEAR)).longValue());
+ month = (int)((Int64Value)value.getComponent(Component.MONTH)).longValue();
+ day = (int)((Int64Value)value.getComponent(Component.DAY)).longValue();
+ hour = (int)((Int64Value)value.getComponent(Component.HOURS)).longValue();
+ minute = (int)((Int64Value)value.getComponent(Component.MINUTES)).longValue();
+ second = (int)((Int64Value)value.getComponent(Component.WHOLE_SECONDS)).longValue();
+ microsecond = (int)((Int64Value)value.getComponent(Component.MICROSECONDS)).longValue();
+ }
+ } catch (XPathException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
+ /**
+ * <p>Unset all fields to undefined.</p>
+ * <p/>
+ * <p>Set all int fields to {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED} and reference fields
+ * to null.</p>
+ */
+ public void clear() {
+ year = null;
+ month = DatatypeConstants.FIELD_UNDEFINED;
+ day = DatatypeConstants.FIELD_UNDEFINED;
+ hour = DatatypeConstants.FIELD_UNDEFINED;
+ minute = DatatypeConstants.FIELD_UNDEFINED;
+ second = DatatypeConstants.FIELD_UNDEFINED;
+ microsecond = DatatypeConstants.FIELD_UNDEFINED;
+ tzOffset = DatatypeConstants.FIELD_UNDEFINED;
+ }
+
+ /**
+ * <p>Reset this <code>XMLGregorianCalendar</code> to its original values.</p>
+ *
+ * <p>Saxon does not attempt to reset to the initial value as defined in the specification of
+ * the superclass, because it cannot distinguish the initial setting from subsequent changes.
+ * This method is therefore synonymous with {@link #clear()}</p>
+ */
+ public void reset() {
+ clear();
+ }
+
+ /**
+ * <p>Set low and high order component of XSD <code>dateTime</code> year field.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of <code>null</code>.</p>
+ *
+ * @param year value constraints summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
+ * @throws IllegalArgumentException if <code>year</code> parameter is
+ * outside value constraints for the field as specified in
+ * <a href="#datetimefieldmapping">date/time field mapping table</a>.
+ */
+ public void setYear(BigInteger year) {
+ calendarValue = null;
+ this.year = year;
+ }
+
+ /**
+ * <p>Set year of XSD <code>dateTime</code> year field.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of
+ * {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ * <p/>
+ * <p>Note: if the absolute value of the <code>year</code> parameter
+ * is less than 10^9, the eon component of the XSD year field is set to
+ * <code>null</code> by this method.</p>
+ *
+ * @param year value constraints are summarized in <a href="#datetimefield-year">year field of date/time field mapping table</a>.
+ * If year is {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}, then eon is set to <code>null</code>.
+ */
+ public void setYear(int year) {
+ calendarValue = null;
+ this.year = BigInteger.valueOf(year);
+ }
+
+ /**
+ * <p>Set month.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
+ * @param month value constraints summarized in <a href="#datetimefield-month">month field of date/time field mapping table</a>.
+ * @throws IllegalArgumentException if <code>month</code> parameter is
+ * outside value constraints for the field as specified in
+ * <a href="#datetimefieldmapping">date/time field mapping table</a>.
+ */
+ public void setMonth(int month) {
+ calendarValue = null;
+ this.month = month;
+ }
+
+ /**
+ * <p>Set days in month.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
+ * @param day value constraints summarized in <a href="#datetimefield-day">day field of date/time field mapping table</a>.
+ * @throws IllegalArgumentException if <code>day</code> parameter is
+ * outside value constraints for the field as specified in
+ * <a href="#datetimefieldmapping">date/time field mapping table</a>.
+ */
+ public void setDay(int day) {
+ calendarValue = null;
+ this.day = day;
+ }
+
+ /**
+ * <p>Set the number of minutes in the timezone offset.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
+ * @param offset value constraints summarized in <a href="#datetimefield-timezone">
+ * timezone field of date/time field mapping table</a>.
+ * @throws IllegalArgumentException if <code>offset</code> parameter is
+ * outside value constraints for the field as specified in
+ * <a href="#datetimefieldmapping">date/time field mapping table</a>.
+ */
+ public void setTimezone(int offset) {
+ calendarValue = null;
+ tzOffset = offset;
+ }
+
+ /**
+ * <p>Set hours.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
+ * @param hour value constraints summarized in <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.
+ * @throws IllegalArgumentException if <code>hour</code> parameter is outside value constraints for the field as specified in
+ * <a href="#datetimefieldmapping">date/time field mapping table</a>.
+ */
+ public void setHour(int hour) {
+ calendarValue = null;
+ this.hour = hour;
+ }
+
+ /**
+ * <p>Set minutes.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
+ * @param minute value constraints summarized in <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.
+ * @throws IllegalArgumentException if <code>minute</code> parameter is outside value constraints for the field as specified in
+ * <a href="#datetimefieldmapping">date/time field mapping table</a>.
+ */
+ public void setMinute(int minute) {
+ calendarValue = null;
+ this.minute = minute;
+ }
+
+ /**
+ * <p>Set seconds.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
+ * @param second value constraints summarized in <a href="#datetimefield-second">second field of date/time field mapping table</a>.
+ * @throws IllegalArgumentException if <code>second</code> parameter is outside value constraints for the field as specified in
+ * <a href="#datetimefieldmapping">date/time field mapping table</a>.
+ */
+ public void setSecond(int second) {
+ calendarValue = null;
+ this.second = second;
+ }
+
+ /**
+ * <p>Set milliseconds.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
+ * @param millisecond value constraints summarized in
+ * <a href="#datetimefield-millisecond">millisecond field of date/time field mapping table</a>.
+ * @throws IllegalArgumentException if <code>millisecond</code> parameter is outside value constraints for the field as specified
+ * in <a href="#datetimefieldmapping">date/time field mapping table</a>.
+ */
+ public void setMillisecond(int millisecond) {
+ calendarValue = null;
+ microsecond = millisecond*1000;
+ }
+
+ /**
+ * <p>Set fractional seconds.</p>
+ * <p/>
+ * <p>Unset this field by invoking the setter with a parameter value of <code>null</code>.</p>
+ *
+ * @param fractional value constraints summarized in
+ * <a href="#datetimefield-fractional">fractional field of date/time field mapping table</a>.
+ * @throws IllegalArgumentException if <code>fractional</code> parameter is outside value constraints for the field as specified
+ * in <a href="#datetimefieldmapping">date/time field mapping table</a>.
+ */
+ public void setFractionalSecond(/*@NotNull*/ BigDecimal fractional) {
+ calendarValue = null;
+ second = fractional.intValue();
+ BigInteger micros = fractional.movePointRight(6).toBigInteger();
+ micros = micros.remainder(BigInteger.valueOf(1000000));
+ microsecond = micros.intValue();
+ }
+
+ /**
+ * <p>Return high order component for XML Schema 1.0 dateTime datatype field for
+ * <code>year</code>.
+ * <code>null</code> if this optional part of the year field is not defined.</p>
+ * <p/>
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-year">year field of date/time field mapping table</a>.</p>
+ *
+ * @return eon of this <code>XMLGregorianCalendar</code>. The value
+ * returned is an integer multiple of 10^9.
+ * @see #getYear()
+ * @see #getEonAndYear()
+ */
+ public BigInteger getEon() {
+ return year.divide(BigInteger.valueOf(1000000000));
+ }
+
+ /**
+ * <p>Return low order component for XML Schema 1.0 dateTime datatype field for
+ * <code>year</code> or {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ * <p/>
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-year">year field of date/time field mapping table</a>.</p>
+ *
+ * @return year of this <code>XMLGregorianCalendar</code>.
+ * @see #getEon()
+ * @see #getEonAndYear()
+ */
+ public int getYear() {
+ return year.intValue();
+ }
+
+ /**
+ * <p>Return XML Schema 1.0 dateTime datatype field for
+ * <code>year</code>.</p>
+ * <p/>
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-year">year field of date/time field mapping table</a>.</p>
+ *
+ * @return sum of <code>eon</code> and <code>BigInteger.valueOf(year)</code>
+ * when both fields are defined. When only <code>year</code> is defined,
+ * return it. When both <code>eon</code> and <code>year</code> are not
+ * defined, return <code>null</code>.
+ * @see #getEon()
+ * @see #getYear()
+ */
+ /*@Nullable*/ public BigInteger getEonAndYear() {
+ return year;
+ }
+
+ /**
+ * <p>Return number of month or {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ * <p/>
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-month">month field of date/time field mapping table</a>.</p>
+ *
+ * @return year of this <code>XMLGregorianCalendar</code>.
+ */
+ public int getMonth() {
+ return month;
+ }
+
+ /**
+ * Return day in month or {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ * <p/>
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-day">day field of date/time field mapping table</a>.</p>
+ *
+ * @see #setDay(int)
+ */
+ public int getDay() {
+ return day;
+ }
+
+ /**
+ * Return timezone offset in minutes or
+ * {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED} if this optional field is not defined.
+ * <p/>
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-timezone">timezone field of date/time field mapping table</a>.</p>
+ *
+ * @see #setTimezone(int)
+ */
+ public int getTimezone() {
+ return tzOffset;
+ }
+
+ /**
+ * Return hours or {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.
+ * Returns {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED} if this field is not defined.
+ * <p/>
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-hour">hour field of date/time field mapping table</a>.</p>
+ *
+ * @see #setTime(int, int, int)
+ */
+ public int getHour() {
+ return hour;
+ }
+
+ /**
+ * Return minutes or {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ * Returns {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED} if this field is not defined.
+ * <p/>
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-minute">minute field of date/time field mapping table</a>.</p>
+ *
+ * @see #setTime(int, int, int)
+ */
+ public int getMinute() {
+ return minute;
+ }
+
+ /**
+ * <p>Return seconds or {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ * <p/>
+ * <p>Returns {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED} if this field is not defined.
+ * When this field is not defined, the optional xs:dateTime
+ * fractional seconds field, represented by
+ * {@link #getFractionalSecond()} and {@link #getMillisecond()},
+ * must not be defined.</p>
+ * <p/>
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-second">second field of date/time field mapping table</a>.</p>
+ *
+ * @return Second of this <code>XMLGregorianCalendar</code>.
+ * @see #getFractionalSecond()
+ * @see #getMillisecond()
+ * @see #setTime(int, int, int)
+ */
+ public int getSecond() {
+ return second;
+ }
+
+ /**
+ * <p>Return microsecond precision of {@link #getFractionalSecond()}.</p>
+ *
+ * <p>This method represents a convenience accessor to infinite
+ * precision fractional second value returned by
+ * {@link #getFractionalSecond()}. The returned value is the rounded
+ * down to microseconds value of
+ * {@link #getFractionalSecond()}. When {@link #getFractionalSecond()}
+ * returns <code>null</code>, this method must return
+ * {@link DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
+ * <p>Value constraints for this value are summarized in
+ * <a href="#datetimefield-second">second field of date/time field mapping table</a>.</p>
+ *
+ * @return Millisecond of this <code>XMLGregorianCalendar</code>.
+ *
+ * @see #getFractionalSecond()
+ * @see #setTime(int, int, int)
+ */
+ public int getMicrosecond() {
+
+ BigDecimal fractionalSeconds = getFractionalSecond();
+
+ // is field undefined?
+ if (fractionalSeconds == null) {
+ return DatatypeConstants.FIELD_UNDEFINED;
+ }
+
+ return getFractionalSecond().movePointRight(6).intValue();
+ }
+
+
+ /**
+ * <p>Return fractional seconds.</p>
+ * <p/>
+ * <p><code>null</code> is returned when this optional field is not defined.</p>
+ * <p/>
+ * <p>Value constraints are detailed in
+ * <a href="#datetimefield-second">second field of date/time field mapping table</a>.</p>
+ * <p/>
+ * <p>This optional field can only have a defined value when the
+ * xs:dateTime second field, represented by {@link #getSecond()},
+ * does not return {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.</p>
+ *
+ * @return fractional seconds of this <code>XMLGregorianCalendar</code>.
+ * @see #getSecond()
+ * @see #setTime(int, int, int, java.math.BigDecimal)
+ */
+ /*@Nullable*/ public BigDecimal getFractionalSecond() {
+ if (second == DatatypeConstants.FIELD_UNDEFINED) {
+ return null;
+ }
+ return BigDecimal.valueOf(microsecond).movePointLeft(6);
+ }
+
+ /**
+ * <p>Compare two instances of W3C XML Schema 1.0 date/time datatypes
+ * according to partial order relation defined in
+ * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime-order">W3C XML Schema 1.0 Part 2, Section 3.2.7.3,
+ * <i>Order relation on dateTime</i></a>.</p>
+ * <p/>
+ * <p><code>xsd:dateTime</code> datatype field mapping to accessors of
+ * this class are defined in
+ * <a href="#datetimefieldmapping">date/time field mapping table</a>.</p>
+ *
+ * @param xmlGregorianCalendar Instance of <code>XMLGregorianCalendar</code> to compare
+ * @return The relationship between <code>this</code> <code>XMLGregorianCalendar</code> and
+ * the specified <code>xmlGregorianCalendar</code> as
+ * {@link javax.xml.datatype.DatatypeConstants#LESSER},
+ * {@link javax.xml.datatype.DatatypeConstants#EQUAL},
+ * {@link javax.xml.datatype.DatatypeConstants#GREATER} or
+ * {@link javax.xml.datatype.DatatypeConstants#INDETERMINATE}.
+ * @throws NullPointerException if <code>xmlGregorianCalendar</code> is null.
+ */
+ public int compare(/*@NotNull*/ XMLGregorianCalendar xmlGregorianCalendar) {
+ return toCalendarValue().getSchemaComparable().compareTo(
+ ((SaxonXMLGregorianCalendar)xmlGregorianCalendar).toCalendarValue().getSchemaComparable());
+ }
+
+ /**
+ * <p>Normalize this instance to UTC.</p>
+ * <p/>
+ * <p>2000-03-04T23:00:00+03:00 normalizes to 2000-03-04T20:00:00Z</p>
+ * <p>Implements W3C XML Schema Part 2, Section 3.2.7.3 (A).</p>
+ *
+ * @return a copy of this <code>XMLGregorianCalendar</code> normalized to UTC.
+ */
+ /*@NotNull*/ public XMLGregorianCalendar normalize() {
+ return new SaxonXMLGregorianCalendar(toCalendarValue().adjustTimezone(0));
+ }
+
+ /**
+ * <p>Return the lexical representation of <code>this</code> instance.
+ * The format is specified in
+ * <a href="http://www.w3.org/TR/xmlschema-2/#dateTime-order">XML Schema 1.0 Part 2, Section 3.2.[7-14].1,
+ * <i>Lexical Representation</i>".</a></p>
+ * <p/>
+ * <p>Specific target lexical representation format is determined by
+ * {@link #getXMLSchemaType()}.</p>
+ *
+ * @return XML, as <code>String</code>, representation of this <code>XMLGregorianCalendar</code>
+ * @throws IllegalStateException if the combination of set fields
+ * does not match one of the eight defined XML Schema builtin date/time datatypes.
+ */
+ public String toXMLFormat() {
+ return toCalendarValue().getStringValue();
+ }
+
+ /**
+ * <p>Return the name of the XML Schema date/time type that this instance
+ * maps to. Type is computed based on fields that are set.</p>
+ * @return One of the following class constants:
+ * {@link javax.xml.datatype.DatatypeConstants#DATETIME},
+ * {@link javax.xml.datatype.DatatypeConstants#TIME},
+ * {@link javax.xml.datatype.DatatypeConstants#DATE},
+ * {@link javax.xml.datatype.DatatypeConstants#GYEARMONTH},
+ * {@link javax.xml.datatype.DatatypeConstants#GMONTHDAY},
+ * {@link javax.xml.datatype.DatatypeConstants#GYEAR},
+ * {@link javax.xml.datatype.DatatypeConstants#GMONTH} or
+ * {@link javax.xml.datatype.DatatypeConstants#GDAY}.
+ * @throws IllegalStateException if the combination of set fields
+ * does not match one of the eight defined XML Schema builtin
+ * date/time datatypes.
+ */
+ public QName getXMLSchemaType() {
+ if (second == DatatypeConstants.FIELD_UNDEFINED) {
+ if (year == null) {
+ if (month == DatatypeConstants.FIELD_UNDEFINED) {
+ return DatatypeConstants.GDAY;
+ } else if (day == DatatypeConstants.FIELD_UNDEFINED) {
+ return DatatypeConstants.GMONTH;
+ } else {
+ return DatatypeConstants.GMONTHDAY;
+ }
+ } else if (day == DatatypeConstants.FIELD_UNDEFINED) {
+ if (month == DatatypeConstants.FIELD_UNDEFINED) {
+ return DatatypeConstants.GYEAR;
+ } else {
+ return DatatypeConstants.GYEARMONTH;
+ }
+ }
+ return DatatypeConstants.DATE;
+ } else if (year == null) {
+ return DatatypeConstants.TIME;
+ } else {
+ return DatatypeConstants.DATETIME;
+ }
+ }
+
+ /**
+ * Validate instance by <code>getXMLSchemaType()</code> constraints.
+ *
+ * @return true if data values are valid.
+ */
+ public boolean isValid() {
+ return true;
+ }
+
+ /**
+ * <p>Add <code>duration</code> to this instance.</p>
+ * <p/>
+ * <p>The computation is specified in
+ * <a href="http://www.w3.org/TR/xmlschema-2/#adding-durations-to-dateTimes">XML Schema 1.0 Part 2, Appendix E,
+ * <i>Adding durations to dateTimes</i>></a>.
+ * <a href="#datetimefieldsmapping">date/time field mapping table</a>
+ * defines the mapping from XML Schema 1.0 <code>dateTime</code> fields
+ * to this class' representation of those fields.</p>
+ *
+ * @param duration Duration to add to this <code>XMLGregorianCalendar</code>.
+ * @throws NullPointerException when <code>duration</code> parameter is <code>null</code>.
+ */
+ public void add(/*@NotNull*/ Duration duration) {
+ try {
+ CalendarValue cv = toCalendarValue().add(((SaxonDuration)duration).getDurationValue());
+ setCalendarValue(cv);
+ } catch (XPathException err) {
+ throw new IllegalArgumentException(err.getMessage());
+ }
+ }
+
+ /**
+ * <p>Convert this <code>XMLGregorianCalendar</code> to a {@link java.util.GregorianCalendar}.</p>
+ * <p/>
+ * <p>When <code>this</code> instance has an undefined field, this
+ * conversion relies on the <code>java.util.GregorianCalendar</code> default
+ * for its corresponding field. A notable difference between
+ * XML Schema 1.0 date/time datatypes and <code>java.util.GregorianCalendar</code>
+ * is that Timezone value is optional for date/time datatypes and it is
+ * a required field for <code>java.util.GregorianCalendar</code>. See javadoc
+ * for <code>java.util.TimeZone.getDefault()</code> on how the default
+ * is determined. To explicitly specify the <code>TimeZone</code>
+ * instance, see
+ * {@link #toGregorianCalendar(java.util.TimeZone, Locale, javax.xml.datatype.XMLGregorianCalendar)}.</p>
+ * @see #toGregorianCalendar(java.util.TimeZone, java.util.Locale, javax.xml.datatype.XMLGregorianCalendar)
+ */
+ public GregorianCalendar toGregorianCalendar() {
+ return toCalendarValue().getCalendar();
+ }
+
+ /**
+ * <p>Convert this <code>XMLGregorianCalendar</code> along with provided parameters
+ * to a {@link java.util.GregorianCalendar} instance.</p>
+ * <p/>
+ * <p> Since XML Schema 1.0 date/time datetypes has no concept of
+ * timezone ids or daylight savings timezone ids, this conversion operation
+ * allows the user to explicitly specify one with
+ * <code>timezone</code> parameter.</p>
+ * <p/>
+ * <p>To compute the return value's <code>TimeZone</code> field,
+ * <ul>
+ * <li>when parameter <code>timeZone</code> is non-null,
+ * it is the timezone field.</li>
+ * <li>else when <code>this.getTimezone() != FIELD_UNDEFINED</code>,
+ * create a <code>java.util.TimeZone</code> with a custom timezone id
+ * using the <code>this.getTimezone()</code>.</li>
+ * <li>else when <code>defaults.getTimezone() != FIELD_UNDEFINED</code>,
+ * create a <code>java.util.TimeZone</code> with a custom timezone id
+ * using <code>defaults.getTimezone()</code>.</li>
+ * <li>else use the <code>GregorianCalendar</code> default timezone value
+ * for the host is defined as specified by
+ * <code>java.util.TimeZone.getDefault()</code>.</li></p>
+ * <p/>
+ * <p>To ensure consistency in conversion implementations, the new
+ * <code>GregorianCalendar</code> should be instantiated in following
+ * manner.
+ * <ul>
+ * <li>Create a new <code>java.util.GregorianCalendar(TimeZone,
+ * Locale)</code> with TimeZone set as specified above and the
+ * <code>Locale</code> parameter.
+ * </li>
+ * <li>Initialize all GregorianCalendar fields by calling {@link java.util.GregorianCalendar#clear()}</li>
+ * <li>Obtain a pure Gregorian Calendar by invoking
+ * <code>GregorianCalendar.setGregorianChange(
+ * new Date(Long.MIN_VALUE))</code>.</li>
+ * <li>Its fields ERA, YEAR, MONTH, DAY_OF_MONTH, HOUR_OF_DAY,
+ * MINUTE, SECOND and MILLISECOND are set using the method
+ * <code>Calendar.set(int,int)</code></li>
+ * </ul>
+ *
+ * @param timezone provide Timezone. <code>null</code> is a legal value.
+ * @param aLocale provide explicit Locale. Use default GregorianCalendar locale if
+ * value is <code>null</code>.
+ * @param defaults provide default field values to use when corresponding
+ * field for this instance is FIELD_UNDEFINED or null.
+ * If <code>defaults</code>is <code>null</code> or a field
+ * within the specified <code>defaults</code> is undefined,
+ * just use <code>java.util.GregorianCalendar</code> defaults.
+ * @return a java.util.GregorianCalendar conversion of this instance.
+ */
+ /*@NotNull*/ public GregorianCalendar toGregorianCalendar(TimeZone timezone, Locale aLocale, /*@NotNull*/ XMLGregorianCalendar defaults) {
+ GregorianCalendar gc = new GregorianCalendar(timezone, aLocale);
+ gc.setGregorianChange(new Date(Long.MIN_VALUE));
+ gc.set(Calendar.ERA, (year==null ? (defaults.getYear()>0 ? +1 : -1) : year.signum()));
+ gc.set(Calendar.YEAR, (year==null ? defaults.getYear() : year.abs().intValue()));
+ gc.set(Calendar.MONTH, (month==DatatypeConstants.FIELD_UNDEFINED ? defaults.getMonth() : month));
+ gc.set(Calendar.DAY_OF_MONTH, day==DatatypeConstants.FIELD_UNDEFINED ? defaults.getDay() : day);
+ gc.set(Calendar.HOUR, hour==DatatypeConstants.FIELD_UNDEFINED ? defaults.getHour() : hour);
+ gc.set(Calendar.MINUTE, minute==DatatypeConstants.FIELD_UNDEFINED ? defaults.getMinute() : minute);
+ gc.set(Calendar.SECOND, second==DatatypeConstants.FIELD_UNDEFINED ? defaults.getSecond() : second );
+ gc.set(Calendar.MILLISECOND, microsecond==DatatypeConstants.FIELD_UNDEFINED
+ ? defaults.getMillisecond() : microsecond /1000);
+ return gc;
+ }
+
+ /**
+ * <p>Returns a <code>java.util.TimeZone</code> for this class.</p>
+ * <p/>
+ * <p>If timezone field is defined for this instance,
+ * returns TimeZone initialized with custom timezone id
+ * of zoneoffset. If timezone field is undefined,
+ * try the defaultZoneoffset that was passed in.
+ * If defaultZoneoffset is FIELD_UNDEFINED, return
+ * default timezone for this host.
+ * (Same default as java.util.GregorianCalendar).</p>
+ *
+ * @param defaultZoneoffset default zoneoffset if this zoneoffset is
+ * {@link javax.xml.datatype.DatatypeConstants#FIELD_UNDEFINED}.
+ * @return TimeZone for this.
+ */
+ public TimeZone getTimeZone(int defaultZoneoffset) {
+ if (tzOffset == DatatypeConstants.FIELD_UNDEFINED) {
+ if (defaultZoneoffset == DatatypeConstants.FIELD_UNDEFINED) {
+ return new GregorianCalendar().getTimeZone();
+ } else {
+ return new SimpleTimeZone(defaultZoneoffset*60000, "XXX");
+ }
+ } else {
+ return new SimpleTimeZone(tzOffset*60000, "XXX");
+ }
+ }
+
+ /**
+ * <p>Creates and returns a copy of this object.</p>
+ *
+ * @return copy of this <code>Object</code>
+ */
+ /*@NotNull*/@SuppressWarnings({"CloneDoesntCallSuperClone"})
+ public Object clone() {
+ SaxonXMLGregorianCalendar s = new SaxonXMLGregorianCalendar();
+ s.setYear(year);
+ s.setMonth(month);
+ s.setDay(day);
+ s.setHour(hour);
+ s.setMinute(minute);
+ s.setSecond(second);
+ s.setMillisecond(microsecond/1000);
+ s.setTimezone(tzOffset);
+ return s;
+ }
+
+ /**
+ * Convert this SaxonXMLGregorianCalendar to a Saxon CalendarValue object
+ * @return the corresponding CalendarValue
+ */
+
+ /*@Nullable*/ public CalendarValue toCalendarValue() {
+ if (calendarValue != null) {
+ return calendarValue;
+ }
+ if (second == DatatypeConstants.FIELD_UNDEFINED) {
+ if (year == null) {
+ if (month == DatatypeConstants.FIELD_UNDEFINED) {
+ return new GDayValue((byte)day, tzOffset);
+ } else if (day == DatatypeConstants.FIELD_UNDEFINED) {
+ return new GMonthValue((byte)month, tzOffset);
+ } else {
+ return new GMonthDayValue((byte)month, (byte)day, tzOffset);
+ }
+ } else if (day == DatatypeConstants.FIELD_UNDEFINED) {
+ if (month == DatatypeConstants.FIELD_UNDEFINED) {
+ return new GYearValue(year.intValue(), tzOffset, true);
+ } else {
+ return new GYearMonthValue(year.intValue(), (byte)month, tzOffset, true);
+ }
+ }
+ return new DateValue(year.intValue(), (byte)month, (byte)day, tzOffset,true);
+ } else if (year == null) {
+ return new TimeValue((byte)hour, (byte)minute, (byte)second, getMicrosecond(), tzOffset);
+ } else {
+ return new DateTimeValue(year.intValue(), (byte)month, (byte)day,
+ (byte)hour, (byte)minute, (byte)second, getMicrosecond(), tzOffset, true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXQConnection.java b/sf/saxon/xqj/SaxonXQConnection.java
new file mode 100644
index 0000000..9e87683
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQConnection.java
@@ -0,0 +1,207 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.query.DynamicQueryContext;
+import net.sf.saxon.query.StaticQueryContext;
+import net.sf.saxon.query.XQueryExpression;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.xquery.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Saxon implementation of the XQL interface XQConnection. This interface represents a
+ * "connection" between an XQuery application and an XQuery server. In Saxon the client
+ * and server run in the same process so the concept of a connection is rather notional,
+ * and some of the properties have little meaning. However, the connection is the factory
+ * object used to compile queries.
+ * <p>
+ * For Javadoc descriptions of the public methors, see the XQJ documentation.
+ */
+public class SaxonXQConnection extends SaxonXQDataFactory implements XQConnection {
+
+ private Configuration config;
+ private SaxonXQStaticContext staticContext;
+
+ /**
+ * Create an SaxonXQConnection from a SaxonXQDataSource
+ * @param dataSource the data source.
+ */
+ SaxonXQConnection(SaxonXQDataSource dataSource) {
+ config = dataSource.getConfiguration();
+ staticContext = new SaxonXQStaticContext(config);
+ init();
+ }
+
+ /**
+ * Get the Saxon Configuration in use. Changes made to this Configuration will affect this
+ * data source and XQJ connections created from it (either before or afterwards).
+ * @return the configuration in use.
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ public void commit() throws XQException {
+ checkNotClosed();
+ }
+
+ public XQExpression createExpression() throws XQException {
+ checkNotClosed();
+ return new SaxonXQExpression(this);
+ }
+
+ public XQExpression createExpression(XQStaticContext properties) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(properties, "properties");
+ return new SaxonXQExpression(this, (SaxonXQStaticContext)properties);
+ }
+
+
+ public boolean getAutoCommit() throws XQException {
+ return false;
+ }
+
+ public XQMetaData getMetaData() throws XQException {
+ checkNotClosed();
+ return new SaxonXQMetaData(this);
+ }
+
+
+ public XQStaticContext getStaticContext() throws XQException {
+ checkNotClosed();
+ return new SaxonXQStaticContext(staticContext);
+ }
+
+ public XQPreparedExpression prepareExpression(InputStream xquery) throws XQException {
+ return prepareExpression(xquery, staticContext);
+ }
+
+ public XQPreparedExpression prepareExpression(InputStream xquery, XQStaticContext properties) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(xquery, "xquery");
+ SaxonXQDataSource.checkNotNull(properties, "properties");
+ try {
+ SaxonXQStaticContext xqStaticContext = ((SaxonXQStaticContext)properties);
+ StaticQueryContext sqc = xqStaticContext.getSaxonStaticQueryContext();
+ XQueryExpression exp = sqc.compileQuery(xquery, null);
+ DynamicQueryContext dqc = new DynamicQueryContext(config);
+ dqc.setApplyFunctionConversionRulesToExternalVariables(false);
+ return new SaxonXQPreparedExpression(this, exp, xqStaticContext, dqc);
+ } catch (XPathException e) {
+ throw newXQException(e);
+ } catch (IOException e) {
+ throw newXQException(e);
+ } catch (NullPointerException e) {
+ throw newXQException(e);
+ }
+ }
+
+ public XQPreparedExpression prepareExpression(Reader xquery) throws XQException {
+ return prepareExpression(xquery, staticContext);
+ }
+
+ public XQPreparedExpression prepareExpression(Reader xquery, XQStaticContext properties) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(xquery, "xquery");
+ SaxonXQDataSource.checkNotNull(properties, "properties");
+ try {
+ SaxonXQStaticContext xqStaticContext = ((SaxonXQStaticContext)properties);
+ StaticQueryContext sqc = xqStaticContext.getSaxonStaticQueryContext();
+ XQueryExpression exp = sqc.compileQuery(xquery);
+ DynamicQueryContext dqc = new DynamicQueryContext(config);
+ dqc.setApplyFunctionConversionRulesToExternalVariables(false);
+ return new SaxonXQPreparedExpression(this, exp, xqStaticContext, dqc);
+ } catch (XPathException e) {
+ throw newXQException(e);
+ } catch (IOException e) {
+ throw newXQException(e);
+ } catch (NullPointerException e) {
+ throw newXQException(e);
+ }
+ }
+
+ public XQPreparedExpression prepareExpression(String xquery) throws XQException {
+ return prepareExpression(xquery, staticContext);
+ }
+
+ public XQPreparedExpression prepareExpression(String xquery, XQStaticContext properties) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(xquery, "xquery");
+ SaxonXQDataSource.checkNotNull(properties, "properties");
+ try {
+ SaxonXQStaticContext xqStaticContext = ((SaxonXQStaticContext)properties);
+ StaticQueryContext sqc = xqStaticContext.getSaxonStaticQueryContext();
+ XQueryExpression exp = sqc.compileQuery(xquery);
+ DynamicQueryContext dqc = new DynamicQueryContext(config);
+ dqc.setApplyFunctionConversionRulesToExternalVariables(false);
+ return new SaxonXQPreparedExpression(this, exp, xqStaticContext, dqc);
+ } catch (XPathException e) {
+ throw newXQException(e);
+ } catch (NullPointerException e) {
+ throw newXQException(e);
+ }
+ }
+
+ /**
+ * Copy a prepared expression to create a new prepared expression. The prepared expression to be copied
+ * may belong to a different connection. This method (which is a Saxon extension to the XQJ interface) allows
+ * a query to be compiled once, and reused concurrently under multiple connections in multiple threads. The
+ * compiled code of the existing query and its static context are shared with the original query, but a new
+ * dynamic context is established, so that the two expressions can safely be used in parallel.
+ * @param expression the XQPreparedExpression to be copied. This must have been created using Saxon, and it
+ * must have been created with an XQConnection derived from the same XQDataSource as this connection.
+ * @return a copy of the supplied expression, that can be used in a different connection or thread with its
+ * own dynamic context. The new copy of the expression belongs to this connection, and can be used in the same
+ * way as an expression created using any of the prepareExpression() methods on this class.
+ * @throws XQException, for example if either of the connections has been closed
+ */
+
+ public XQPreparedExpression copyPreparedExpression(XQPreparedExpression expression) throws XQException {
+ checkNotClosed();
+ if (!(expression instanceof SaxonXQPreparedExpression)) {
+ throw new IllegalArgumentException("Supplied expression must be compiled using Saxon");
+ }
+ XQueryExpression xqe = ((SaxonXQPreparedExpression)expression).getXQueryExpression();
+ if (xqe.getExecutable().getConfiguration() != config) {
+ throw new IllegalArgumentException("Supplied expression must derive from the same XQDataSource");
+ }
+ SaxonXQStaticContext sqc = ((SaxonXQPreparedExpression)expression).getSaxonXQStaticContext();
+ DynamicQueryContext dqc = new DynamicQueryContext(config);
+ return new SaxonXQPreparedExpression(this, xqe, sqc, dqc);
+ }
+
+ public void rollback() throws XQException {
+ checkNotClosed();
+ // no-op
+ }
+
+
+ public void setAutoCommit(boolean autoCommit) throws XQException {
+ checkNotClosed();
+ // no-op
+ }
+
+ public void setStaticContext(XQStaticContext properties) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(properties, "properties");
+ staticContext = new SaxonXQStaticContext((SaxonXQStaticContext)properties);
+ }
+
+ /*@NotNull*/ private XQException newXQException(Exception err) {
+ XQException xqe = new XQException(err.getMessage());
+ xqe.initCause(err);
+ return xqe;
+ }
+}
+
diff --git a/sf/saxon/xqj/SaxonXQDataFactory.java b/sf/saxon/xqj/SaxonXQDataFactory.java
new file mode 100644
index 0000000..8c41d07
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQDataFactory.java
@@ -0,0 +1,1042 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.dom.DOMObjectModel;
+import net.sf.saxon.evpull.EventIterator;
+import net.sf.saxon.evpull.EventToStaxBridge;
+import net.sf.saxon.evpull.PullEventSource;
+import net.sf.saxon.evpull.StaxToEventBridge;
+import net.sf.saxon.expr.EarlyEvaluationContext;
+import net.sf.saxon.expr.JPConverter;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.expr.parser.Token;
+import net.sf.saxon.lib.AugmentedSource;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.*;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.*;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+import net.sf.saxon.z.IntToIntHashMap;
+import net.sf.saxon.z.IntToIntMap;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+
+import javax.xml.datatype.Duration;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.Source;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.xquery.*;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.lang.Boolean;
+import java.lang.Byte;
+import java.lang.Double;
+import java.lang.Exception;
+import java.lang.Float;
+import java.lang.Integer;
+import java.lang.Long;
+import java.lang.Object;
+import java.lang.Short;
+import java.lang.String;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Saxon implementation of the XQJ interface XQDataFactory. This is an abstract superclass for SaxonXQDataSource
+ * and SaxonXQConnection, both of which provide the factory methods in this interface.
+ * <p>
+ * For Javadoc specifications of the public methods in this class, see the XQJ documentation.
+ */
+
+public abstract class SaxonXQDataFactory extends Closable implements XQDataFactory {
+
+ private ObjectConverter objectConverter;
+
+ abstract Configuration getConfiguration();
+
+ // Two-way mapping between XQJ integer codes for built-in types and the Saxon equivalents
+
+ /*@NotNull*/ private static IntToIntMap XQJtoSaxonTypeTranslation = new IntToIntHashMap(80);
+ /*@NotNull*/ private static IntToIntMap saxonToXQJTypeTranslation = new IntToIntHashMap(80);
+
+ private static void map(int x, int y) {
+ XQJtoSaxonTypeTranslation.put(x, y);
+ saxonToXQJTypeTranslation.put(y, x);
+ }
+
+ static {
+ map(XQItemType.XQBASETYPE_ANYSIMPLETYPE, StandardNames.XS_ANY_SIMPLE_TYPE);
+ map(XQItemType.XQBASETYPE_ANYTYPE, StandardNames.XS_ANY_TYPE);
+ map(XQItemType.XQBASETYPE_ANYURI, StandardNames.XS_ANY_URI);
+ map(XQItemType.XQBASETYPE_BASE64BINARY, StandardNames.XS_BASE64_BINARY);
+ map(XQItemType.XQBASETYPE_BOOLEAN, StandardNames.XS_BOOLEAN);
+ map(XQItemType.XQBASETYPE_BYTE, StandardNames.XS_BYTE);
+ map(XQItemType.XQBASETYPE_DATE, StandardNames.XS_DATE);
+ map(XQItemType.XQBASETYPE_DATETIME, StandardNames.XS_DATE_TIME);
+ map(XQItemType.XQBASETYPE_DECIMAL, StandardNames.XS_DECIMAL);
+ map(XQItemType.XQBASETYPE_DOUBLE, StandardNames.XS_DOUBLE);
+ map(XQItemType.XQBASETYPE_DURATION, StandardNames.XS_DURATION);
+ map(XQItemType.XQBASETYPE_ENTITIES, StandardNames.XS_ENTITIES);
+ map(XQItemType.XQBASETYPE_ENTITY, StandardNames.XS_ENTITY);
+ map(XQItemType.XQBASETYPE_FLOAT, StandardNames.XS_FLOAT);
+ map(XQItemType.XQBASETYPE_GDAY, StandardNames.XS_G_DAY);
+ map(XQItemType.XQBASETYPE_GMONTH, StandardNames.XS_G_MONTH);
+ map(XQItemType.XQBASETYPE_GMONTHDAY, StandardNames.XS_G_MONTH_DAY);
+ map(XQItemType.XQBASETYPE_GYEAR, StandardNames.XS_G_YEAR);
+ map(XQItemType.XQBASETYPE_GYEARMONTH, StandardNames.XS_G_YEAR_MONTH);
+ map(XQItemType.XQBASETYPE_HEXBINARY, StandardNames.XS_HEX_BINARY);
+ map(XQItemType.XQBASETYPE_ID, StandardNames.XS_ID);
+ map(XQItemType.XQBASETYPE_IDREF, StandardNames.XS_IDREF);
+ map(XQItemType.XQBASETYPE_IDREFS, StandardNames.XS_IDREFS);
+ map(XQItemType.XQBASETYPE_INT, StandardNames.XS_INT);
+ map(XQItemType.XQBASETYPE_INTEGER, StandardNames.XS_INTEGER);
+ map(XQItemType.XQBASETYPE_LANGUAGE, StandardNames.XS_LANGUAGE);
+ map(XQItemType.XQBASETYPE_LONG, StandardNames.XS_LONG);
+ map(XQItemType.XQBASETYPE_NAME, StandardNames.XS_NAME);
+ map(XQItemType.XQBASETYPE_NCNAME, StandardNames.XS_NCNAME);
+ map(XQItemType.XQBASETYPE_NEGATIVE_INTEGER, StandardNames.XS_NEGATIVE_INTEGER);
+ map(XQItemType.XQBASETYPE_NMTOKEN, StandardNames.XS_NMTOKEN);
+ map(XQItemType.XQBASETYPE_NMTOKENS, StandardNames.XS_NMTOKENS);
+ map(XQItemType.XQBASETYPE_NONNEGATIVE_INTEGER, StandardNames.XS_NON_NEGATIVE_INTEGER);
+ map(XQItemType.XQBASETYPE_NONPOSITIVE_INTEGER, StandardNames.XS_NON_POSITIVE_INTEGER);
+ map(XQItemType.XQBASETYPE_NORMALIZED_STRING, StandardNames.XS_NORMALIZED_STRING);
+ map(XQItemType.XQBASETYPE_NOTATION, StandardNames.XS_NOTATION);
+ map(XQItemType.XQBASETYPE_POSITIVE_INTEGER, StandardNames.XS_POSITIVE_INTEGER);
+ map(XQItemType.XQBASETYPE_QNAME, StandardNames.XS_QNAME);
+ map(XQItemType.XQBASETYPE_SHORT, StandardNames.XS_SHORT);
+ map(XQItemType.XQBASETYPE_STRING, StandardNames.XS_STRING);
+ map(XQItemType.XQBASETYPE_TIME, StandardNames.XS_TIME);
+ map(XQItemType.XQBASETYPE_TOKEN, StandardNames.XS_TOKEN);
+ map(XQItemType.XQBASETYPE_UNSIGNED_BYTE, StandardNames.XS_UNSIGNED_BYTE);
+ map(XQItemType.XQBASETYPE_UNSIGNED_INT, StandardNames.XS_UNSIGNED_INT);
+ map(XQItemType.XQBASETYPE_UNSIGNED_LONG, StandardNames.XS_UNSIGNED_LONG);
+ map(XQItemType.XQBASETYPE_UNSIGNED_SHORT, StandardNames.XS_UNSIGNED_SHORT);
+ map(XQItemType.XQBASETYPE_ANYATOMICTYPE, StandardNames.XS_ANY_ATOMIC_TYPE);
+ map(XQItemType.XQBASETYPE_DAYTIMEDURATION, StandardNames.XS_DAY_TIME_DURATION);
+ map(XQItemType.XQBASETYPE_UNTYPED, StandardNames.XS_UNTYPED);
+ map(XQItemType.XQBASETYPE_UNTYPEDATOMIC, StandardNames.XS_UNTYPED_ATOMIC);
+ map(XQItemType.XQBASETYPE_YEARMONTHDURATION, StandardNames.XS_YEAR_MONTH_DURATION);
+ }
+
+ /**
+ * Get the XQJ type code corresponding to a given Saxon type code
+ * @param type the Saxon type code
+ * @return the corresponding XQJ type code
+ */
+
+ static int mapSaxonTypeToXQJ(int type) {
+ return saxonToXQJTypeTranslation.get(type);
+ }
+
+
+ protected void init() {
+ objectConverter = new StandardObjectConverter(this);
+ }
+
+ /**
+ * Set the ObjectConverter to be used. This allows user-defined object conversions to override
+ * or supplement the standard conversions
+ * @param converter the user-supplied ObjectConverter
+ */
+
+ public void setObjectConverter(ObjectConverter converter) {
+ objectConverter = converter;
+ }
+
+ /**
+ * Get the ObjectConverter in use. This will either be the default object converter supplied by Saxon,
+ * or a user-supplied ObjectConverter if one has been set.
+ * @return the ObjectConverter in use.
+ */
+
+ public ObjectConverter getObjectConverter() {
+ return objectConverter;
+ }
+
+ /**
+ * Create an atomic item type object representing a particular built-in atomic type
+ *
+ * @param baseType the built-in atomic type, typically a constant such as
+ * XQItemType.XQBASETYPE_BOOLEAN
+ * @return the corresponding XQItemType
+ * @throws XQException if the supplied baseType parameter is not an atomic type
+ */
+
+ /*@NotNull*/ public XQItemType createAtomicType(int baseType) throws XQException {
+ checkNotClosed();
+ int saxonType = XQJtoSaxonTypeTranslation.get(baseType);
+ if (saxonType == XQJtoSaxonTypeTranslation.getDefaultValue()) {
+ throw new XQException("Unknown base type " + baseType);
+ }
+ SchemaType st = BuiltInType.getSchemaType(saxonType);
+ if (st instanceof AtomicType) {
+ return new SaxonXQItemType((AtomicType)st, getConfiguration());
+ } else {
+ throw new XQException("baseType " + baseType + " is not atomic");
+ }
+ }
+
+ /**
+ * See interface definition, and description of Saxon extensions below.
+ *
+ * <p>In addition to the actions described in the XQJ interface definitions, Saxon allows the
+ * typename to be a name representing a Java external type. In this case the URI part of the QName
+ * must be {@link net.sf.saxon.lib.NamespaceConstant#JAVA_TYPE}, and the local part of the name must be the Java class
+ * name (qualified with its package name)
+ * @param baseType the "baseType" (in XQJ terminology)
+ * @param typename the qualified name of the type
+ * @param schemaURI the location of a schema document in which the type is defined (may be null)
+ * @return the item type definition
+ * @throws XQException
+ */
+
+
+ /*@NotNull*/ public XQItemType createAtomicType(int baseType, /*@Nullable*/ QName typename, URI schemaURI) throws XQException {
+ checkNotClosed();
+ if (typename == null) {
+ return createAtomicType(baseType);
+ }
+ if (typename.getNamespaceURI().equals(NamespaceConstant.JAVA_TYPE)) {
+ String className = typename.getLocalPart();
+ Configuration config = getConfiguration();
+ try {
+ Class javaClass = config.getClass(className, false, null);
+ return new SaxonXQItemType(new ExternalObjectType(javaClass, config), config);
+ } catch (XPathException e) {
+ throw new XQException(e.getMessage());
+ }
+ }
+ SchemaType st = getConfiguration().getSchemaType(getFingerprint(typename));
+ if (st == null) {
+ loadSchema(schemaURI);
+ st = getConfiguration().getSchemaType(getFingerprint(typename));
+ }
+ if (st == null) {
+ throw new XQException("Type " + typename + " not found in schema");
+ } else if (st instanceof AtomicType) {
+ return new SaxonXQItemType((AtomicType)st, getConfiguration());
+ } else {
+ throw new XQException("Type " + typename + " is not atomic");
+ }
+ }
+
+
+ /*@NotNull*/ public XQItemType createAttributeType(/*@Nullable*/ QName nodename, int basetype) throws XQException {
+ checkNotClosed();
+ Configuration config = getConfiguration();
+
+ int saxonType = XQJtoSaxonTypeTranslation.get(basetype);
+ if (saxonType == XQJtoSaxonTypeTranslation.getDefaultValue()) {
+ throw new XQException("Unknown base type " + basetype);
+ }
+ SchemaType st = BuiltInType.getSchemaType(saxonType);
+ if (!(st.isSimpleType())) {
+ throw new XQException("baseType " + basetype + " is not a simple type");
+ }
+ ContentTypeTest contentTest = new ContentTypeTest(Type.ATTRIBUTE, st, config, false);
+ if (nodename == null) {
+ return new SaxonXQItemType(contentTest, config);
+ } else {
+ NameTest nameTest = new NameTest(
+ Type.ATTRIBUTE, nodename.getNamespaceURI(), nodename.getLocalPart(), config.getNamePool());
+ CombinedNodeTest combined = new CombinedNodeTest(nameTest, Token.INTERSECT, contentTest);
+ return new SaxonXQItemType(combined, config);
+ }
+
+ }
+
+ /*@NotNull*/ public XQItemType createAttributeType(/*@Nullable*/ QName nodename, int basetype, /*@Nullable*/ QName typename, URI schemaURI) throws XQException {
+ checkNotClosed();
+ if (typename == null) {
+ return createAttributeType(nodename, basetype);
+ }
+ Configuration config = getConfiguration();
+
+ SchemaType st = BuiltInType.getSchemaType(getFingerprint(typename));
+ if (st == null) {
+ loadSchema(schemaURI);
+ st = getConfiguration().getSchemaType(getFingerprint(typename));
+ }
+ if (st == null) {
+ throw new XQException("Type " + typename + " not found in schema");
+ } else if (!(st.isSimpleType())) {
+ throw new XQException("Type " + typename + " is not a simple type");
+ }
+ ContentTypeTest contentTest = new ContentTypeTest(Type.ATTRIBUTE, st, config, false);
+ if (nodename == null) {
+ return new SaxonXQItemType(contentTest, config);
+ } else {
+ NameTest nameTest = new NameTest(
+ Type.ATTRIBUTE, nodename.getNamespaceURI(), nodename.getLocalPart(), config.getNamePool());
+ CombinedNodeTest combined = new CombinedNodeTest(nameTest, Token.INTERSECT, contentTest);
+ return new SaxonXQItemType(combined, config);
+ }
+ }
+
+ /*@NotNull*/ public XQItemType createCommentType() throws XQException {
+ checkNotClosed();
+ return new SaxonXQItemType(NodeKindTest.COMMENT, getConfiguration());
+ }
+
+ /*@NotNull*/ public XQItemType createDocumentElementType(XQItemType elementType) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(elementType, "elementType");
+ ItemType itemType = ((SaxonXQItemType)elementType).getSaxonItemType();
+ if (itemType instanceof NodeTest && (((NodeTest)itemType).getNodeKindMask() & (1<<Type.ELEMENT)) != 0) {
+ return new SaxonXQItemType(new DocumentNodeTest((NodeTest)itemType), getConfiguration());
+ } else {
+ throw new XQException("elementType is of wrong kind");
+ }
+ }
+
+ /*@NotNull*/ public XQItemType createDocumentSchemaElementType(XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(type, "type");
+ ItemType itemType = ((SaxonXQItemType)type).getSaxonItemType();
+ if (itemType instanceof NodeTest && (((NodeTest)itemType).getNodeKindMask() & (1<<Type.ELEMENT)) != 0) {
+ return new SaxonXQItemType(new DocumentNodeTest((NodeTest)itemType), getConfiguration());
+ } else {
+ throw new XQException("elementType is of wrong kind");
+ }
+ }
+
+ /*@NotNull*/ public XQItemType createDocumentType() throws XQException {
+ checkNotClosed();
+ return new SaxonXQItemType(NodeKindTest.DOCUMENT, getConfiguration());
+ }
+
+ /*@NotNull*/ public XQItemType createElementType(/*@Nullable*/ QName nodename, int basetype) throws XQException {
+ checkNotClosed();
+ Configuration config = getConfiguration();
+
+ if (basetype == XQItemType.XQBASETYPE_ANYTYPE) {
+ if (nodename == null) {
+ return new SaxonXQItemType(NodeKindTest.ELEMENT, config);
+ } else {
+ return new SaxonXQItemType(
+ new NameTest(Type.ELEMENT, getFingerprint(nodename), config.getNamePool()),
+ config);
+ }
+ }
+
+ int saxonType = XQJtoSaxonTypeTranslation.get(basetype);
+ if (saxonType == XQJtoSaxonTypeTranslation.getDefaultValue()) {
+ throw new XQException("Unknown base type " + basetype);
+ }
+ SchemaType st = BuiltInType.getSchemaType(saxonType);
+ ContentTypeTest contentTest = new ContentTypeTest(Type.ELEMENT, st, config, false);
+ if (nodename == null) {
+ return new SaxonXQItemType(contentTest, config);
+ } else {
+ NameTest nameTest = new NameTest(
+ Type.ELEMENT, nodename.getNamespaceURI(), nodename.getLocalPart(), config.getNamePool());
+ CombinedNodeTest combined = new CombinedNodeTest(nameTest, Token.INTERSECT, contentTest);
+ return new SaxonXQItemType(combined, config);
+ }
+
+ }
+
+ /*@NotNull*/ public XQItemType createElementType(/*@Nullable*/ QName nodename, int basetype, /*@Nullable*/ QName typename, URI schemaURI, boolean allowNill)
+ throws XQException {
+ checkNotClosed();
+ if (typename == null) {
+ return createElementType(nodename, basetype);
+ }
+ Configuration config = getConfiguration();
+
+ SchemaType st = BuiltInType.getSchemaType(getFingerprint(typename));
+ if (st == null) {
+ loadSchema(schemaURI);
+ st = getConfiguration().getSchemaType(getFingerprint(typename));
+ }
+ if (st == null) {
+ throw new XQException("Type " + typename + " not found in schema");
+ }
+
+ ContentTypeTest contentTest = new ContentTypeTest(Type.ELEMENT, st, config, allowNill);
+ if (nodename == null) {
+ return new SaxonXQItemType(contentTest, config);
+ } else {
+ NameTest nameTest = new NameTest(
+ Type.ELEMENT, nodename.getNamespaceURI(), nodename.getLocalPart(), config.getNamePool());
+ CombinedNodeTest combined = new CombinedNodeTest(nameTest, Token.INTERSECT, contentTest);
+ return new SaxonXQItemType(combined, config);
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItem(XQItem item) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(item, "item");
+ ((SaxonXQItem)item).checkNotClosed();
+ return new SaxonXQItem(((SaxonXQItem)item).getSaxonItem(), this);
+ }
+
+ /*@NotNull*/ public XQItem createItemFromAtomicValue(String value, XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ SaxonXQDataSource.checkNotNull(type, "type");
+ AtomicType at = testAtomic(type);
+ StringValue sv = new StringValue(value);
+ Converter converter = getConfiguration().getConversionRules().getConverter(BuiltInAtomicType.STRING, at);
+ ConversionResult result = converter.convert(sv);
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ }
+
+ /*@NotNull*/ public XQItem createItemFromBoolean(boolean value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ if (type == null) {
+ return new SaxonXQItem(BooleanValue.get(value), this);
+ } else {
+ AtomicType at = testAtomic(type);
+ if (at.getPrimitiveType() == StandardNames.XS_BOOLEAN) {
+ try {
+ ConversionResult result =
+ Converter.convert(BooleanValue.get(value), at, getConfiguration().getConversionRules());
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } catch (Exception e) {
+ throw new XQException("Failed to convert boolean value to required type: " + e.getMessage());
+ }
+ } else {
+ throw new XQException("Target type for a boolean must be xs:boolean or a subtype");
+ }
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromByte(byte value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ if (type == null) {
+ try {
+ return new SaxonXQItem(new Int64Value(value, BuiltInAtomicType.BYTE, false), this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ } else {
+ return createItemFromLong(value, type);
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromDocument(InputStream value, String baseURI, XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ try {
+ Source ss = new SAXSource(new InputSource(value));
+ ss.setSystemId(baseURI);
+ ss = augmentSource(ss, type);
+ DocumentInfo doc = getConfiguration().buildDocument(ss);
+ checkDocumentType(doc, (SaxonXQItemType)type);
+ return new SaxonXQItem(doc, this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromDocument(Reader value, String baseURI, XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ try {
+ Source ss = new SAXSource(new InputSource(value));
+ ss.setSystemId(baseURI);
+ ss = augmentSource(ss, type);
+ DocumentInfo doc = getConfiguration().buildDocument(ss);
+ checkDocumentType(doc, (SaxonXQItemType)type);
+ return new SaxonXQItem(doc, this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromDocument(Source value, XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ try {
+ Source ss = augmentSource(value, type);
+ DocumentInfo doc = getConfiguration().buildDocument(ss);
+ checkDocumentType(doc, (SaxonXQItemType)type);
+ return new SaxonXQItem(doc, this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromDocument(String value, String baseURI, XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ try {
+ Source ss = new SAXSource(new InputSource(new StringReader(value)));
+ ss.setSystemId(baseURI);
+ ss = augmentSource(ss, type);
+ DocumentInfo doc = getConfiguration().buildDocument(ss);
+ checkDocumentType(doc, (SaxonXQItemType)type);
+ return new SaxonXQItem(doc, this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromDocument(XMLStreamReader value, XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ try {
+ EventIterator provider;
+ if (value instanceof EventToStaxBridge) {
+ // Rather than converting Saxon events to StAX events and back again, use the original
+ // event stream directly
+ provider = ((EventToStaxBridge)value).getProvider();
+ } else {
+ StaxToEventBridge bridge = new StaxToEventBridge();
+ bridge.setXMLStreamReader(value);
+ bridge.setPipelineConfiguration(getConfiguration().makePipelineConfiguration());
+ provider = bridge;
+ }
+ Source ss = new PullEventSource(provider);
+ ss = augmentSource(ss, type);
+ DocumentInfo doc = getConfiguration().buildDocument(ss);
+ checkDocumentType(doc, (SaxonXQItemType)type);
+ return new SaxonXQItem(doc, this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ }
+
+ private Source augmentSource(Source in, /*@Nullable*/ XQItemType required) throws XQException {
+ if (required == null) {
+ return in;
+ } else {
+ int kind = required.getItemKind();
+ switch (kind) {
+ case XQItemType.XQITEMKIND_DOCUMENT:
+ case XQItemType.XQITEMKIND_NODE:
+ case XQItemType.XQITEMKIND_ITEM:
+ // no validation required
+ return in;
+ case XQItemType.XQITEMKIND_DOCUMENT_ELEMENT:
+ // no validation required unless a type is specified
+ ItemType it = ((SaxonXQItemType)required).getSaxonItemType();
+ it = ((DocumentNodeTest)it).getElementTest();
+ SchemaType contentType = ((NodeTest)it).getContentType();
+ int fp = contentType.getFingerprint();
+ if (fp == StandardNames.XS_ANY_TYPE || fp == StandardNames.XS_UNTYPED) {
+ return in;
+ }
+ break;
+ case XQItemType.XQITEMKIND_DOCUMENT_SCHEMA_ELEMENT:
+ break;
+ default:
+ throw new XQException("Required item type for document node is incorrect");
+ }
+ }
+ AugmentedSource out = AugmentedSource.makeAugmentedSource(in);
+ out.setSchemaValidationMode(Validation.STRICT);
+ return out;
+ }
+
+ private void checkDocumentType(DocumentInfo doc, /*@Nullable*/ SaxonXQItemType required) throws XQException {
+ checkNotClosed();
+ if (required != null &&
+ !required.getSaxonItemType().matchesItem(doc, false, getConfiguration())) {
+ throw new XQException("Document was successfully built but has the wrong type");
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromDouble(double value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ if (type == null) {
+ return new SaxonXQItem(new DoubleValue(value), this);
+ } else {
+ AtomicType at = testAtomic(type);
+ if (at.getPrimitiveType() == StandardNames.XS_DOUBLE) {
+ try {
+ ConversionResult result = Converter.convert(new DoubleValue(value), at, getConfiguration().getConversionRules());
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } catch (Exception e) {
+ throw new XQException("Failed to convert double value to required type: " + e.getMessage());
+ }
+ } else {
+ throw new XQException("Target type for a double must be xs:double or a subtype");
+ }
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromFloat(float value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ if (type == null) {
+ return new SaxonXQItem(new FloatValue(value), this);
+ } else {
+ AtomicType at = testAtomic(type);
+ if (at.getPrimitiveType() == StandardNames.XS_FLOAT) {
+ try {
+ ConversionResult result = Converter.convert(new FloatValue(value), at, getConfiguration().getConversionRules());
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } catch (Exception e) {
+ throw new XQException("Failed to convert float value to required type: " + e.getMessage());
+ }
+ } else {
+ throw new XQException("Target type for a float must be xs:float or a subtype");
+ }
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromInt(int value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ if (type == null) {
+ try {
+ return new SaxonXQItem(new Int64Value(value, BuiltInAtomicType.INT, false), this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ } else {
+ return createItemFromLong(value, type);
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromLong(long value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ if (type == null) {
+ try {
+ return new SaxonXQItem(new Int64Value(value, BuiltInAtomicType.LONG, false), this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ } else {
+ AtomicType at = testAtomic(type);
+ int prim = at.getPrimitiveType();
+ if (prim == StandardNames.XS_INTEGER || prim == StandardNames.XS_DECIMAL) {
+ try {
+ ConversionResult result = Converter.convert(Int64Value.makeIntegerValue(value), at, getConfiguration().getConversionRules());
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } catch (Exception e) {
+ throw new XQException("Failed to convert long|int|short|byte value to required type: " + e.getMessage());
+ }
+ } else {
+ throw new XQException("Target type for a long|int|short|byte must be xs:decimal or a subtype");
+ }
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromNode(Node value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ try {
+ JPConverter jp = DOMObjectModel.getInstance().getJPConverter(Node.class, getConfiguration());
+ NodeInfo n = (NodeInfo)jp.convert(value, new EarlyEvaluationContext(getConfiguration(), null));
+ XQItem result = new SaxonXQItem(n, this);
+ if (type != null && !result.instanceOf(type)) {
+ throw new XQException("The node is not a valid instance of the required type");
+ }
+ return result;
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ }
+
+ /*@NotNull*/ public XQItem createItemFromObject(Object value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ if (type == null) {
+ return new SaxonXQItem(getObjectConverter().convertToItem(value), this);
+ } else {
+ return convertToXQItem(value, type);
+ }
+ }
+
+
+ /*@NotNull*/ public XQItem createItemFromString(String value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ if (type == null) {
+ return new SaxonXQItem(new StringValue(value), this);
+ } else {
+ AtomicType at = testAtomic(type);
+ int prim = at.getPrimitiveType();
+ if (prim == StandardNames.XS_STRING) {
+ try {
+ ConversionResult result = Converter.convert(new net.sf.saxon.value.StringValue(value), at, getConfiguration().getConversionRules());
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } catch (Exception e) {
+ throw new XQException("Failed to convert string value to required type: " + e.getMessage());
+ }
+ } else {
+ throw new XQException("Target type for a string must be xs:string or a subtype");
+ }
+ }
+ }
+
+ /*@NotNull*/ public XQItemType createItemType() throws XQException {
+ checkNotClosed();
+ return new SaxonXQItemType(AnyItemType.getInstance(), getConfiguration());
+ }
+
+ /**
+ * Convert a Java object to an XQItem of a given type
+ * @param value the supplied Java object
+ * @param type the required type
+ * @return an XQItem of the required type
+ * @throws XQException if the value cannot be converted
+ */
+
+ /*@NotNull*/ private XQItem convertToXQItem(Object value, /*@NotNull*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(value, "value");
+ if (value instanceof Boolean) {
+ return createItemFromBoolean(((Boolean) value).booleanValue(), type);
+ } else if (value instanceof byte[]) {
+ AtomicType at = testAtomic(type);
+ int prim = at.getPrimitiveType();
+ ConversionResult result;
+ if (prim == StandardNames.XS_HEX_BINARY) {
+ Converter converter = getConfiguration().getConversionRules().getConverter(BuiltInAtomicType.HEX_BINARY, at);
+ result = converter.convert(new HexBinaryValue((byte[]) value));
+ } else if (prim == StandardNames.XS_BASE64_BINARY) {
+ Converter converter = getConfiguration().getConversionRules().getConverter(BuiltInAtomicType.BASE64_BINARY, at);
+ result = converter.convert(new Base64BinaryValue((byte[]) value));
+ } else {
+ throw new XQException("Target type must be xs:hexBinary, xs:base64Binary, or a subtype");
+ }
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } else if (value instanceof Byte) {
+ return createItemFromByte(((Byte) value).byteValue(), type);
+ } else if (value instanceof Float) {
+ return createItemFromFloat(((Float) value).floatValue(), type);
+ } else if (value instanceof Double) {
+ return createItemFromDouble(((Double) value).doubleValue(), type);
+ } else if (value instanceof Integer) {
+ return createItemFromInt(((Integer) value).intValue(), type);
+ } else if (value instanceof Long) {
+ return createItemFromLong(((Long) value).longValue(), type);
+ } else if (value instanceof Short) {
+ return createItemFromShort(((Short) value).shortValue(), type);
+ } else if (value instanceof String) {
+ AtomicType at = testAtomic(type);
+ int prim = at.getPrimitiveType();
+ ConversionResult result;
+ if (prim == StandardNames.XS_UNTYPED_ATOMIC) {
+ result = new UntypedAtomicValue((String) value);
+ } else if (prim == StandardNames.XS_STRING) {
+ Converter converter = getConfiguration().getConversionRules().getConverter(BuiltInAtomicType.STRING, at);
+ result = converter.convert(new StringValue((String)value));
+ } else if (prim == StandardNames.XS_ANY_URI) {
+ Converter converter = getConfiguration().getConversionRules().getConverter(BuiltInAtomicType.ANY_URI, at);
+ result = converter.convert(new StringValue((String)value));
+ } else {
+ // Note: the spec also allow NOTATION, but string->notation conversion doesn't work
+ throw new XQException("Target type must be string, untypedAtomic, or anyURI");
+ }
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } else if (value instanceof BigDecimal) {
+ AtomicType at = testAtomic(type);
+ int prim = at.getPrimitiveType();
+ if (prim == StandardNames.XS_DECIMAL || prim == StandardNames.XS_INTEGER) {
+ Converter converter = getConfiguration().getConversionRules().getConverter(BuiltInAtomicType.DECIMAL, at);
+ ConversionResult result = converter.convert(new DecimalValue((BigDecimal) value));
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } else {
+ throw new XQException("Target type must be xs:decimal or a subtype");
+ }
+ } else if (value instanceof BigInteger) {
+ AtomicType at = testAtomic(type);
+ int prim = at.getPrimitiveType();
+ if (prim == StandardNames.XS_DECIMAL || prim == StandardNames.XS_INTEGER) {
+ Converter converter = getConfiguration().getConversionRules().getConverter(BuiltInAtomicType.DECIMAL, at);
+ ConversionResult result = converter.convert(new DecimalValue(new BigDecimal((BigInteger) value)));
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } else {
+ throw new XQException("Target type must be xs:decimal or a subtype");
+ }
+ } else if (value instanceof Duration) {
+ AtomicType at = testAtomic(type);
+ int prim = at.getPrimitiveType();
+ if (prim == StandardNames.XS_DURATION || prim == StandardNames.XS_DAY_TIME_DURATION || prim == StandardNames.XS_YEAR_MONTH_DURATION) {
+ DurationValue dv = (DurationValue) getObjectConverter().convertToItem(value);
+ Converter converter = getConfiguration().getConversionRules().getConverter(dv.getPrimitiveType(), at);
+ ConversionResult result = converter.convert(dv);
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } else {
+ throw new XQException("Target type must be xs:duration or a subtype");
+ }
+
+ } else if (value instanceof XMLGregorianCalendar) {
+ AtomicType at = testAtomic(type);
+ int prim = at.getPrimitiveType();
+ switch (prim) {
+ case StandardNames.XS_DATE_TIME:
+ case StandardNames.XS_DATE:
+ case StandardNames.XS_TIME:
+ case StandardNames.XS_G_YEAR:
+ case StandardNames.XS_G_YEAR_MONTH:
+ case StandardNames.XS_G_MONTH:
+ case StandardNames.XS_G_MONTH_DAY:
+ case StandardNames.XS_G_DAY:
+ AtomicValue dv = (AtomicValue) getObjectConverter().convertToItem(value);
+ Converter converter = getConfiguration().getConversionRules().getConverter(dv.getPrimitiveType(), at);
+ ConversionResult result = converter.convert(dv);
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ default:
+ throw new XQException("Target type must be a date/time type");
+ }
+ } else if (value instanceof QName) {
+ AtomicType at = testAtomic(type);
+ int prim = at.getPrimitiveType();
+ if (prim == StandardNames.XS_QNAME) {
+ QualifiedNameValue dv = (QualifiedNameValue) getObjectConverter().convertToItem(value);
+ Converter converter = getConfiguration().getConversionRules().getConverter(dv.getPrimitiveType(), at);
+ ConversionResult result = converter.convert(dv);
+ if (result instanceof ValidationFailure) {
+ throw new XQException(((ValidationFailure)result).getMessage());
+ }
+ return new SaxonXQItem((AtomicValue)result, this);
+ } else {
+ throw new XQException("Target type must be xs:QName or a subtype");
+ }
+ } else if (value instanceof Node) {
+ NodeInfo result = (NodeInfo) getObjectConverter().convertToItem(value);
+ XQItem item = new SaxonXQItem(result, this);
+ if (!item.instanceOf(type)) {
+ throw new XQException("Supplied node does not match the requested XQItemType");
+ }
+ return item;
+ } else {
+ return new SaxonXQItem(getObjectConverter().convertToItem(value, type), this);
+ }
+ }
+
+
+ /*@NotNull*/ public XQItem createItemFromShort(short value, /*@Nullable*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ if (type == null) {
+ try {
+ return new SaxonXQItem(new Int64Value(value, BuiltInAtomicType.SHORT, false), this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ } else {
+ return createItemFromLong(value, type);
+ }
+ }
+
+
+
+ /*@NotNull*/ public XQItemType createNodeType() throws XQException {
+ checkNotClosed();
+ return new SaxonXQItemType(AnyNodeTest.getInstance(), getConfiguration());
+ }
+
+ /*@NotNull*/ public XQItemType createProcessingInstructionType(/*@Nullable*/ String piTarget) throws XQException {
+ checkNotClosed();
+ if (piTarget == null) {
+ return new SaxonXQItemType(NodeKindTest.PROCESSING_INSTRUCTION, getConfiguration());
+ } else {
+ return new SaxonXQItemType(
+ new NameTest(Type.PROCESSING_INSTRUCTION, "", piTarget, getConfiguration().getNamePool()),
+ getConfiguration());
+ }
+ }
+
+ /*@NotNull*/ public XQItemType createSchemaAttributeType(/*@NotNull*/ QName nodename, int basetype, /*@Nullable*/ URI schemaURI) throws XQException {
+ checkNotClosed();
+ Configuration config = getConfiguration();
+ int fp = getFingerprint(nodename);
+ SchemaDeclaration attributeDecl = config.getAttributeDeclaration(fp);
+ if (attributeDecl == null && schemaURI != null) {
+ loadSchema(schemaURI);
+ attributeDecl = config.getAttributeDeclaration(fp);
+ }
+ if (attributeDecl == null) {
+ throw new XQException("Attribute declaration " + nodename + " not found in schema");
+ }
+ NodeTest nameTest = attributeDecl.makeSchemaNodeTest();
+ return new SaxonXQItemType(nameTest, config);
+ }
+
+ /*@NotNull*/ public XQItemType createSchemaElementType(/*@NotNull*/ QName nodename, int basetype, /*@Nullable*/ URI schemaURI) throws XQException {
+ checkNotClosed();
+ Configuration config = getConfiguration();
+ int fp = getFingerprint(nodename);
+ SchemaDeclaration elementDecl = config.getElementDeclaration(fp);
+ if (elementDecl == null && schemaURI != null) {
+ loadSchema(schemaURI);
+ elementDecl = config.getElementDeclaration(fp);
+ }
+ if (elementDecl == null) {
+ throw new XQException("Element declaration " + nodename + " not found in schema");
+ }
+ NodeTest nameTest = elementDecl.makeSchemaNodeTest();
+ return new SaxonXQItemType(nameTest, config);
+
+ }
+
+ /*@NotNull*/ public XQSequence createSequence(/*@Nullable*/ Iterator i) throws XQException {
+ checkNotClosed();
+ if (i == null) {
+ throw new XQException("createSequence(): argument is null");
+ }
+ List list = new ArrayList(50);
+ while (i.hasNext()) {
+ Object object = i.next();
+ XQItem item;
+ if (object instanceof XQItem) {
+ item = (XQItem)object;
+ } else {
+ item = createItemFromObject(object, null);
+ }
+ list.add(((SaxonXQItem)item).getSaxonItem());
+ }
+ SequenceExtent extent = new SequenceExtent(list);
+ return new SaxonXQSequence(extent, this);
+ }
+
+ /*@NotNull*/ public XQSequence createSequence(/*@Nullable*/ XQSequence s) throws XQException {
+ checkNotClosed();
+ if (s == null) {
+ throw new XQException("createSequence(): argument is null");
+ }
+ ((Closable)s).checkNotClosed();
+ if (s instanceof SaxonXQSequence) {
+ return new SaxonXQSequence(((SaxonXQSequence) s).getValue(), this);
+ } else if (s instanceof SaxonXQForwardSequence) {
+ try {
+ SequenceExtent extent = new SequenceExtent(((SaxonXQForwardSequence) s).getCleanIterator());
+ return new SaxonXQSequence(extent, this);
+ } catch (XPathException de) {
+ throw newXQException(de);
+ }
+ } else {
+ throw new XQException("Supplied sequence is not a Saxon implementation");
+ }
+ }
+
+ /*@NotNull*/ public XQSequenceType createSequenceType(XQItemType item, int occurrence) throws XQException {
+ checkNotClosed();
+ if (item instanceof SaxonXQItemType) {
+ ItemType itemType = ((SaxonXQItemType) item).getSaxonItemType();
+ int cardinality;
+ switch (occurrence) {
+ case XQSequenceType.OCC_EXACTLY_ONE:
+ cardinality = StaticProperty.EXACTLY_ONE;
+ break;
+ case XQSequenceType.OCC_ONE_OR_MORE:
+ cardinality = StaticProperty.ALLOWS_ONE_OR_MORE;
+ break;
+ case XQSequenceType.OCC_ZERO_OR_ONE:
+ cardinality = StaticProperty.ALLOWS_ZERO_OR_ONE;
+ break;
+ case XQSequenceType.OCC_ZERO_OR_MORE:
+ cardinality = StaticProperty.ALLOWS_ZERO_OR_MORE;
+ break;
+ default:
+ throw new XQException("Invalid occurrence value");
+ }
+ SequenceType st = SequenceType.makeSequenceType(itemType, cardinality);
+ return new SaxonXQSequenceType(st, getConfiguration());
+ } else {
+ throw new XQException("Supplied XQItemType is not a Saxon-created object");
+ }
+ }
+
+ /*@NotNull*/ public XQItemType createTextType() throws XQException {
+ checkNotClosed();
+ return new SaxonXQItemType(NodeKindTest.TEXT, getConfiguration());
+ }
+
+ private AtomicType testAtomic(/*@NotNull*/ XQItemType type) throws XQException {
+ if (type instanceof SaxonXQItemType) {
+ AtomicType at = ((SaxonXQItemType) type).getAtomicType();
+ if (at == null) {
+ throw new XQException("Requested type is not atomic");
+ }
+ return at;
+ } else {
+ throw new XQException("Supplied XQItemType is not a Saxon-created object");
+ }
+ }
+
+ /*@NotNull*/ private static XQException newXQException(/*@NotNull*/ Exception err) {
+ XQException e = new XQException(err.getMessage());
+ e.initCause(err);
+ return e;
+ }
+
+ private int getFingerprint(/*@NotNull*/ QName name) {
+ return getConfiguration().getNamePool().allocate(
+ name.getPrefix(), name.getNamespaceURI(), name.getLocalPart());
+ }
+
+ /**
+ * Attempt to load the schema document at a given location into the Configuration
+ * @param schemaURI the absolute URI of the location of the schema document. If null is supplied,
+ * the method is a no-op.
+ * @throws XQException if the URI is not absolute, or if no schema is found at the location,
+ * or if the schema is invalid, or if it is inconsistent with existing schema components present
+ * in the Configuration.
+ */
+
+ private void loadSchema(/*@Nullable*/ URI schemaURI) throws XQException {
+ if (schemaURI == null) {
+ return;
+ }
+ if (!schemaURI.isAbsolute()) {
+ throw new XQException("Schema URI must be an absolute URI");
+ }
+ try {
+ getConfiguration().loadSchema(schemaURI.toString());
+ } catch (SchemaException err) {
+ throw newXQException(err);
+ }
+ }
+
+}
+
diff --git a/sf/saxon/xqj/SaxonXQDataSource.java b/sf/saxon/xqj/SaxonXQDataSource.java
new file mode 100644
index 0000000..5b6786a
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQDataSource.java
@@ -0,0 +1,539 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.lib.ExtensionFunctionDefinition;
+import net.sf.saxon.lib.FeatureKeys;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.value.BooleanValue;
+import net.sf.saxon.value.Whitespace;
+
+import javax.xml.xquery.XQConnection;
+import javax.xml.xquery.XQDataSource;
+import javax.xml.xquery.XQException;
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * Saxon implementation of the XQJ XQDataSource interface. The first action of a client application
+ * is to instantiate a SaxonXQDataSource. This is done directly: there is no factory class as with JAXP.
+ * An application that does not want compile-time references to the Saxon XQJ implementation can instantiate
+ * this class dynamically using the reflection API (class.newInstance()).
+ * <p>
+ * For full Javadoc descriptions of the public methods, see the XQJ specification.
+ */
+public class SaxonXQDataSource implements XQDataSource {
+
+ private Configuration config;
+ private PrintWriter logger;
+
+ /**
+ * Create a SaxonXQDataSource using a default configuration.
+ * The Configuration will be an EE, PE, or HE configuration depending on the
+ * what is found on the classpath
+ */
+
+ public SaxonXQDataSource() {
+ config = Configuration.newConfiguration();
+ config.setProcessor(this);
+ }
+
+ /**
+ * Create a Saxon XQDataSource with a specific configuration
+ * @param config The Saxon configuration to be used
+ */
+
+ public SaxonXQDataSource(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Get the Saxon Configuration in use. Changes made to this Configuration will affect this
+ * data source and XQJ connections created from it (either before or afterwards). Equally,
+ * changes made to this SaxonXQDataSource are reflected in the Configuration object (which means
+ * they also impact any other SaxonXQDataSource objects that share the same Configuration).
+ * @return the configuration in use.
+ */
+
+ public Configuration getConfiguration() {
+ return config;
+ }
+
+ /*@NotNull*/ public XQConnection getConnection() throws XQException {
+ return new SaxonXQConnection(this);
+ }
+
+ /**
+ * Get a connection based on an underlying JDBC connection
+ * @param con the JDBC connection
+ * @return a connection based on an underlying JDBC connection
+ * @throws XQException The Saxon implementation of this method always throws
+ * an XQException, indicating that Saxon does not support connection to a JDBC data source.
+ */
+
+ /*@NotNull*/ public XQConnection getConnection(Connection con) throws XQException {
+ throw new XQException("Saxon cannot connect to a SQL data source");
+ }
+
+ /**
+ * Get a connection, by supplying a username and password. The Saxon implementation of this is equivalent
+ * to the default constructor: the username and password are ignored.
+ * @param username the user name
+ * @param password the password
+ * @return a connection
+ * @throws XQException
+ */
+
+ /*@NotNull*/ public XQConnection getConnection(String username, String password) throws XQException {
+ return getConnection();
+ }
+
+ public int getLoginTimeout() {
+ return 0;
+ }
+
+ public PrintWriter getLogWriter() {
+ return logger;
+ }
+
+ /**
+ * Get a configuration property setting. The properties that are supported, and their meanings, are the
+ * same as the properties that can be obtained using "get" methods; for example
+ * <code>getProperty("dtdValidation")</code> returns the same result as <code>getDtdValidation()</code>.
+ *
+ * <p>Further Saxon configuration properties are available using the URIs defined as constants in
+ * {@link net.sf.saxon.lib.FeatureKeys}</p>
+ *
+ * @param name the name of the configuration property
+ * @return the value of the configuration property. Note that in the case of on/off properties this
+ * will be returned as the string true/false
+ * @throws XQException
+ */
+
+ /*@Nullable*/ public String getProperty(String name) throws XQException {
+ checkNotNull(name, "name");
+ if ("allowExternalFunctions".equals(name)) {
+ return getAllowExternalFunctions();
+ } else if ("dtdValidation".equals(name)) {
+ return getDtdValidation();
+ } else if ("expandAttributeDefaults".equals(name)) {
+ return getExpandAttributeDefaults();
+ } else if ("expandXInclude".equals(name)) {
+ return getExpandXInclude();
+ } else if ("retainLineNumbers".equals(name)) {
+ return getRetainLineNumbers();
+ } else if ("schemaValidationMode".equals(name)) {
+ return getSchemaValidationMode();
+ } else if ("stripWhitespace".equals(name)) {
+ return getStripWhitespace();
+ } else if ("useXsiSchemaLocation".equals(name)) {
+ return getUseXsiSchemaLocation();
+ } else if ("xmlVersion".equals(name)) {
+ return getXmlVersion();
+ } else if ("xsdVersion".equals(name)) {
+ return getXsdVersion();
+ } else {
+ try {
+ return config.getConfigurationProperty(name).toString();
+ } catch (IllegalArgumentException e) {
+ throw new XQException("Property " + name + " is not recognized");
+ } catch (NullPointerException err) {
+ throw new XQException("Null property name or value");
+ }
+ }
+
+ }
+
+ /*@NotNull*/ private static String[] supportedPropertyNames = {
+ "allowExternalFunctions",
+ "dtdValidation",
+ "expandAttributeDefaults",
+ "expandXInclude",
+ "retainLineNumbers",
+ "schemaValidationMode",
+ "stripWhitespace",
+ "useXsiSchemaLocation",
+ "xmlVersion",
+ "xsdVersion"
+ };
+
+ /*@NotNull*/ public String[] getSupportedPropertyNames() {
+ return supportedPropertyNames;
+ }
+
+ public void setLoginTimeout(int seconds) throws XQException {
+ // no-op
+ }
+
+ public void setLogWriter(PrintWriter out) throws XQException {
+ logger = out;
+ }
+
+ public void setProperties(Properties props) throws XQException {
+ checkNotNull(props, "props");
+ Enumeration iter = props.keys();
+ while (iter.hasMoreElements()) {
+ String name = (String)iter.nextElement();
+ String value = props.getProperty(name);
+ setProperty(name, value);
+ }
+ }
+
+ /**
+ * Set a configuration property. The properties that are supported, and their meanings, are the
+ * same as the properties that can be obtained using "set" methods; for example
+ * <code>setProperty("dtdValidation", "true")</code> has the same effect as
+ * <code>setDtdValidation("true")</code>.
+ *
+ * <p>Further Saxon configuration properties are available using the URIs defined as constants in
+ * {@link net.sf.saxon.lib.FeatureKeys}</p>
+ *
+ * @param name the name of the configuration property
+ * @param value the value of the configuration property
+ * @throws XQException
+ */
+
+ public void setProperty(String name, String value) throws XQException {
+ SaxonXQDataSource.checkNotNull(name, "name");
+ SaxonXQDataSource.checkNotNull(value, "value");
+ if ("allowExternalFunctions".equals(name)) {
+ setAllowExternalFunctions(value);
+ } else if ("dtdValidation".equals(name)) {
+ setDtdValidation(value);
+ } else if ("expandAttributeDefaults".equals(name)) {
+ setExpandAttributeDefaults(value);
+ } else if ("expandXInclude".equals(name)) {
+ setExpandXInclude(value);
+ } else if ("retainLineNumbers".equals(name)) {
+ setRetainLineNumbers(value);
+ } else if ("schemaValidationMode".equals(name)) {
+ setSchemaValidationMode(value);
+ } else if ("stripWhitespace".equals(name)) {
+ setStripWhitespace(value);
+ } else if ("useXsiSchemaLocation".equals(name)) {
+ setUseXsiSchemaLocation(value);
+ } else if ("xmlVersion".equals(name)) {
+ setXmlVersion(value);
+ } else if ("xsdVersion".equals(name)) {
+ setXsdVersion(value);
+ } else {
+ try {
+ config.setConfigurationProperty(name, value);
+ } catch (IllegalArgumentException err) {
+ throw new XQException("Unrecognized property or invalid value for " + name + ": " + err.getMessage());
+ } catch (NullPointerException err) {
+ throw new XQException("Null property name or value");
+ }
+ }
+ }
+
+ static void checkNotNull(/*@Nullable*/ Object arg, String name) throws XQException {
+ if (arg == null) {
+ throw new XQException("Argument " + name + " is null");
+ }
+ }
+
+ /**
+ * Say whether queries are allowed to call external functions.
+ * @param value set to "true" if external function calls are allowed (default) or "false" otherwise
+ */
+
+ public void setAllowExternalFunctions(String value) {
+ if ("true".equals(value)) {
+ config.setBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS, true);
+ } else if ("false".equals(value)) {
+ config.setBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS, false);
+ } else {
+ throw new IllegalArgumentException("allowExternalFunctions");
+ }
+ }
+
+ /**
+ * Ask whether queries are allowed to call external functions.
+ * @return "true" if external function calls are allowed, "false" otherwise
+ */
+
+ /*@NotNull*/ public String getAllowExternalFunctions() {
+ return (config.getBooleanProperty(FeatureKeys.ALLOW_EXTERNAL_FUNCTIONS) ? "true" : "false");
+ }
+
+ /**
+ * Say whether source documents are to be parsed with DTD validation enabled
+ * @param value "true" if DTD validation is to be enabled, otherwise "false". Default is "false".
+ */
+
+ public void setDtdValidation(String value) {
+ if ("true".equals(value)) {
+ config.setValidation(true);
+ } else if ("false".equals(value)) {
+ config.setValidation(false);
+ } else {
+ throw new IllegalArgumentException("dtdValidation");
+ }
+ }
+
+ /**
+ * Ask whether source documents are to be parsed with DTD validation enabled
+ * @return "true" if DTD validation is to be enabled, otherwise "false". Default is "false".
+ */
+
+ /*@NotNull*/ public String getDtdValidation() {
+ return (config.isValidation() ? "true" : "false");
+ }
+
+ /**
+ * Say whether whether fixed and default values defined
+ * in a schema or DTD will be expanded. By default, or if the value is "true" (and for conformance with the
+ * specification) validation against a DTD or schema will cause default values defined in the schema
+ * or DTD to be inserted into the document. Setting this feature to "false" suppresses this behaviour. In
+ * the case of DTD-defined defaults this only works if the XML parser reports whether each attribute was
+ * specified in the source or generated by expanding a default value. Not all XML parsers report this
+ * information.
+ * @param value "true" if default values are to be expanded, otherwise "false". Default is "true".
+ */
+
+ public void setExpandAttributeDefaults(String value) {
+ if ("true".equals(value)) {
+ config.setExpandAttributeDefaults(true);
+ } else if ("false".equals(value)) {
+ config.setExpandAttributeDefaults(false);
+ } else {
+ throw new IllegalArgumentException("expandAttributeDefaults");
+ }
+ }
+
+ /**
+ * Ask whether fixed or default values defined in a schema or DTD will be expanded
+ * @return "true" if such values will be expanded, otherwise "false"
+ */
+
+ /*@NotNull*/ public String getExpandAttributeDefaults() {
+ return (config.isExpandAttributeDefaults() ? "true" : "false");
+ }
+
+ /**
+ * Say whether XInclude processing is to be applied to source documents
+ * @param value "true" if XInclude directives are to expanded, otherwise "false". Default is "false".
+ */
+
+ public void setExpandXInclude(String value) {
+ if ("true".equals(value)) {
+ config.setXIncludeAware(true);
+ } else if ("false".equals(value)) {
+ config.setXIncludeAware(false);
+ } else {
+ throw new IllegalArgumentException("expandXInclude");
+ }
+ }
+
+ /**
+ * Ask whether XInclude processing is to be applied to source documents
+ * @return "true" if XInclude directives are to expanded, otherwise "false". Default is "false".
+ */
+
+ /*@NotNull*/ public String getExpandXInclude() {
+ return (config.isXIncludeAware() ? "true" : "false");
+ }
+
+
+ /**
+ * Say whether source documents should have line and column information retained. This
+ * information is available via extension functions <code>saxon:line-number()</code> and
+ * <code>saxon:column-number()</code>
+ * @param value "true" if line and column information is to be retained, otherwise "false". Default is "false".
+ */
+
+ public void setRetainLineNumbers(String value) {
+ if ("true".equals(value)) {
+ config.setLineNumbering(true);
+ } else if ("false".equals(value)) {
+ config.setLineNumbering(false);
+ } else {
+ throw new IllegalArgumentException("retainLineNumbers");
+ }
+ }
+
+ /**
+ * Ask whether line and column information will be retained for source documents
+ * @return "true" if line and column information is retained, otherwise "false"
+ */
+
+ /*@NotNull*/ public String getRetainLineNumbers() {
+ return (config.isLineNumbering() ? "true" : "false");
+ }
+
+ /**
+ * Say whether source documents should be validated against a schema
+ * @param value set to "strict" if source documents are to be subjected to strict validation,
+ * "lax" if source documents are to be subjected to lax validation, "skip" if source documents
+ * are not to be subjected to schema validation
+ */
+
+ public void setSchemaValidationMode(String value) {
+ if ("strict".equals(value)) {
+ config.setSchemaValidationMode(Validation.STRICT);
+ } else if ("lax".equals(value)) {
+ config.setSchemaValidationMode(Validation.LAX);
+ } else if ("skip".equals(value)) {
+ config.setSchemaValidationMode(Validation.SKIP);
+ } else {
+ throw new IllegalArgumentException("schemaValidationMode");
+ }
+ }
+
+ /**
+ * Ask whether source documents will be validated against a schema
+ * @return "strict" if source documents are to be subjected to strict validation,
+ * "lax" if source documents are to be subjected to lax validation, "skip" if source documents
+ * are not to be subjected to schema validation
+ */
+
+ public String getSchemaValidationMode() {
+ return Validation.toString(config.getSchemaValidationMode());
+ }
+
+ /**
+ * Say whether whitespace should be stripped when loading source documents
+ * @param value "all" if all whitespace text nodes are to be stripped, "ignorable" if
+ * only whitespace text nodes in elements defined in a schema or DTD as having element-only
+ * content are to be stripped, "none" if no whitespace text nodes are to be stripped
+ */
+
+ public void setStripWhitespace(String value) {
+ if ("all".equals(value)) {
+ config.setStripsWhiteSpace(Whitespace.ALL);
+ } else if ("ignorable".equals(value)) {
+ config.setStripsWhiteSpace(Whitespace.IGNORABLE);
+ } else if ("none".equals(value)) {
+ config.setStripsWhiteSpace(Whitespace.NONE);
+ } else {
+ throw new IllegalArgumentException("stripWhitespace");
+ }
+ }
+
+ /**
+ * Ask whether whitespace will be stripped when loading source documents
+ * @return "all" if all whitespace text nodes are to be stripped, "ignorable" if
+ * only whitespace text nodes in elements defined in a schema or DTD as having element-only
+ * content are to be stripped, "none" if no whitespace text nodes are to be stripped
+ */
+
+ /*@NotNull*/ public String getStripWhitespace() {
+ switch (config.getStripsWhiteSpace()) {
+ case Whitespace.ALL: return "all";
+ case Whitespace.IGNORABLE: return "ignorable";
+ default: return "none";
+ }
+ }
+
+ /**
+ * Say whether the schema processor is to take account of xsi:schemaLocation and
+ * xsi:noNamespaceSchemaLocation attributes encountered in an instance document being validated
+ * @param value set to "true" if these attributes are to be recognized (default) or "false" otherwise
+ */
+
+ public void setUseXsiSchemaLocation(String value) {
+ if ("true".equals(value)) {
+ config.setConfigurationProperty(FeatureKeys.USE_XSI_SCHEMA_LOCATION, BooleanValue.TRUE);
+ } else if ("false".equals(value)) {
+ config.setConfigurationProperty(FeatureKeys.USE_XSI_SCHEMA_LOCATION, BooleanValue.FALSE);
+ } else {
+ throw new IllegalArgumentException("useXsiSchemaLocation");
+ }
+ }
+
+ /**
+ * Ask whether the schema processor is to take account of xsi:schemaLocation and
+ * xsi:noNamespaceSchemaLocation attributes encountered in an instance document being validated
+ * @return "true" if these attributes are to be recognized (default) or "false" otherwise
+ */
+
+ /*@NotNull*/ public String getUseXsiSchemaLocation() {
+ Boolean b = (Boolean)config.getConfigurationProperty(FeatureKeys.USE_XSI_SCHEMA_LOCATION);
+ return (b.booleanValue() ? "true" : "false");
+ }
+
+ /**
+ * Say whether XML 1.0 or XML 1.1 rules for XML names are to be followed
+ * @param value "1.0" (default) if XML 1.0 rules are to be used, "1.1" if XML 1.1
+ * rules apply
+ */
+
+ public void setXmlVersion(String value) {
+ if ("1.0".equals(value)) {
+ config.setXMLVersion(Configuration.XML10);
+ } else if ("1.1".equals(value)) {
+ config.setXMLVersion(Configuration.XML11);
+ } else {
+ throw new IllegalArgumentException("xmlVersion");
+ }
+ }
+
+ /**
+ * Ask whether XML 1.0 or XML 1.1 rules for XML names are to be followed
+ * @return "1.0" if XML 1.0 rules are to be used, "1.1" if XML 1.1
+ * rules apply
+ */
+
+ /*@NotNull*/ public String getXmlVersion() {
+ return (config.getXMLVersion() == Configuration.XML10 ? "1.0" : "1.1");
+ }
+
+ /**
+ * Say whether XML Schema 1.0 syntax must be used or whether XML Schema 1.1 features are allowed
+ * @param value "1.0" (default) if XML Schema 1.0 rules are to be followed, "1.1" if XML Schema 1.1
+ * features may be used
+ */
+
+ public void setXsdVersion(String value) {
+ if ("1.0".equals(value)) {
+ config.setConfigurationProperty(FeatureKeys.XSD_VERSION, "1.0");
+ } else if ("1.1".equals(value)) {
+ config.setConfigurationProperty(FeatureKeys.XSD_VERSION, "1.1");
+ } else {
+ throw new IllegalArgumentException("xsdVersion");
+ }
+ }
+
+ /**
+ * Ask whether XML Schema 1.0 syntax must be used or whether XML Schema 1.1 features are allowed
+ * @return "1.0" (default) if XML Schema 1.0 rules are to be followed, "1.1" if XML Schema 1.1
+ * features may be used
+ */
+
+ /*@Nullable*/ public String getXsdVersion() {
+ return (String)config.getConfigurationProperty(FeatureKeys.XSD_VERSION);
+ }
+
+ /**
+ * Register an extension function that is to be made available within any query. This method
+ * allows deep integration of an extension function implemented as an instance of
+ * {@link net.sf.saxon.lib.ExtensionFunctionDefinition}, using an arbitrary name and namespace.
+ * This supplements the ability to call arbitrary Java methods using a namespace and local name
+ * that are related to the Java class and method name.
+ * <p><i>This method is a Saxon extension to the XQJ interface</i></p>
+ * @param function the class that implements the extension function. This must be a class that extends
+ * {@link net.sf.saxon.lib.ExtensionFunctionCall}, and it must have a public zero-argument
+ * constructor
+ * @throws IllegalArgumentException if the class cannot be instantiated or does not extend
+ * {@link net.sf.saxon.lib.ExtensionFunctionCall}
+ * @since 9.2
+ */
+
+ public void registerExtensionFunction(ExtensionFunctionDefinition function) {
+ try {
+ config.registerExtensionFunction(function);
+ } catch (Exception err) {
+ throw new IllegalArgumentException(err);
+ }
+ }
+}
+
diff --git a/sf/saxon/xqj/SaxonXQDynamicContext.java b/sf/saxon/xqj/SaxonXQDynamicContext.java
new file mode 100644
index 0000000..9025df8
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQDynamicContext.java
@@ -0,0 +1,270 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.Sequence;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.query.DynamicQueryContext;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.*;
+import org.w3c.dom.Node;
+
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.Source;
+import javax.xml.xquery.*;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+/**
+ * Saxon implementation of the XQJ DynamicContext interface
+ */
+
+public abstract class SaxonXQDynamicContext extends Closable implements XQDynamicContext {
+
+ protected SaxonXQConnection connection;
+
+ protected abstract DynamicQueryContext getDynamicContext();
+
+ protected final Configuration getConfiguration() {
+ return connection.getConfiguration();
+ }
+
+ protected abstract SaxonXQDataFactory getDataFactory() throws XQException;
+
+ protected abstract boolean externalVariableExists(QName name);
+
+ /*@Nullable*/ private TimeZone implicitTimeZone = null;
+
+ public void bindAtomicValue(QName varname, String value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ checkNotNull(value);
+ checkNotNull(type);
+ SaxonXQItem item = (SaxonXQItem) getDataFactory().createItemFromAtomicValue(value, type);
+ bindExternalVariable(varname, item.getSaxonItem());
+ }
+
+ public void bindBoolean(QName varname, boolean value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ BooleanValue target = BooleanValue.get(value);
+ checkAtomic(type, target);
+ bindExternalVariable(varname, target);
+ }
+
+ public void bindByte(QName varname, byte value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem) getDataFactory().createItemFromByte(value, type);
+ AtomicValue target = (AtomicValue)item.getSaxonItem();
+ checkAtomic(type, target);
+ bindExternalVariable(varname, target);
+ }
+
+ public void bindDocument(QName varname, InputStream value, String baseURI, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem)connection.createItemFromDocument(value, baseURI, type);
+ bindExternalVariable(varname, item.getSaxonItem());
+ }
+
+ public void bindDocument(QName varname, Reader value, String baseURI, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem)connection.createItemFromDocument(value, baseURI, type);
+ bindExternalVariable(varname, item.getSaxonItem());
+ }
+
+ public void bindDocument(QName varname, Source value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem)connection.createItemFromDocument(value, type);
+ bindExternalVariable(varname, item.getSaxonItem());
+ }
+
+ public void bindDocument(QName varname, String value, String baseURI, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem)connection.createItemFromDocument(value, baseURI, type);
+ bindExternalVariable(varname, item.getSaxonItem());
+ }
+
+ public void bindDocument(QName varname, /*@NotNull*/ XMLStreamReader value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem)connection.createItemFromDocument(value, type);
+ bindExternalVariable(varname, item.getSaxonItem());
+ }
+
+ public void bindDouble(QName varname, double value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ AtomicValue target = new DoubleValue(value);
+ checkAtomic(type, target);
+ bindExternalVariable(varname, target);
+ }
+
+ public void bindFloat(QName varname, float value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ AtomicValue target = new FloatValue(value);
+ checkAtomic(type, target);
+ bindExternalVariable(varname, target);
+ }
+
+ public void bindInt(QName varname, int value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem) getDataFactory().createItemFromInt(value, type);
+ AtomicValue target = (AtomicValue)item.getSaxonItem();
+ checkAtomic(type, target);
+ bindExternalVariable(varname, target);
+ }
+
+ public void bindItem(QName varname, /*@NotNull*/ XQItem value) throws XQException {
+ checkNotClosed();
+ ((SaxonXQItem) value).checkNotClosed();
+ checkNotNull(varname);
+ bindExternalVariable(varname, ((SaxonXQItem) value).getSaxonItem());
+ }
+
+ public void bindLong(QName varname, long value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem) getDataFactory().createItemFromLong(value, type);
+ AtomicValue target = (AtomicValue)item.getSaxonItem();
+ checkAtomic(type, target);
+ bindExternalVariable(varname, target);
+ }
+
+ public void bindNode(QName varname, Node value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem) getDataFactory().createItemFromNode(value, type);
+ bindExternalVariable(varname, item.getSaxonItem());
+ }
+
+ public void bindObject(QName varname, /*@NotNull*/ Object value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem) getDataFactory().createItemFromObject(value, type);
+ bindExternalVariable(varname, item.getSaxonItem());
+
+ }
+
+ public void bindSequence(QName varname, /*@NotNull*/ XQSequence value) throws XQException {
+ checkNotClosed();
+ ((Closable)value).checkNotClosed();
+ checkNotNull(varname);
+ try {
+ if (value instanceof SaxonXQForwardSequence) {
+ getDynamicContext().setParameter(getClarkName(varname),
+ ((SaxonXQForwardSequence) value).getCleanIterator());
+ } else if (value instanceof SaxonXQSequence) {
+ bindExternalVariable(varname, ((SaxonXQSequence) value).getValue());
+ } else {
+ throw new XQException("XQSequence value is not a Saxon sequence");
+ }
+ } catch (XPathException de) {
+ XQException err = new XQException(de.getMessage());
+ err.initCause(de);
+ throw err;
+ }
+ }
+
+ public void bindShort(QName varname, short value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem) getDataFactory().createItemFromShort(value, type);
+ AtomicValue target = (AtomicValue)item.getSaxonItem();
+ checkAtomic(type, target);
+ bindExternalVariable(varname, target);
+ }
+
+ public void bindString(QName varname, String value, XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(varname);
+ SaxonXQItem item = (SaxonXQItem) getDataFactory().createItemFromString(value, type);
+ AtomicValue target = (AtomicValue)item.getSaxonItem();
+ checkAtomic(type, target);
+ bindExternalVariable(varname, target);
+ }
+
+ /*@Nullable*/ public TimeZone getImplicitTimeZone() throws XQException {
+ checkNotClosed();
+ if (implicitTimeZone != null) {
+ return implicitTimeZone;
+ } else {
+ return new GregorianCalendar().getTimeZone();
+ }
+ }
+
+ public void setImplicitTimeZone(TimeZone implicitTimeZone) throws XQException {
+ checkNotClosed();
+ GregorianCalendar now = new GregorianCalendar(implicitTimeZone);
+ try {
+ getDynamicContext().setCurrentDateTime(new DateTimeValue(now, true));
+ } catch (XPathException e) {
+ throw new XQException(e.getMessage());
+ }
+ this.implicitTimeZone = implicitTimeZone;
+ }
+
+
+ private void bindExternalVariable(QName varName, /*@NotNull*/ Sequence value) throws XQException {
+ checkNotNull(varName);
+ checkNotNull(value);
+ try {
+ if (varName.equals(XQConstants.CONTEXT_ITEM)) {
+ getDynamicContext().setContextItem(SequenceTool.asItem(value));
+ } else {
+ if (!externalVariableExists(varName)) {
+ throw new XQException("No external variable named " + varName + " exists in the query");
+ }
+ getDynamicContext().setParameterValue(getClarkName(varName), value);
+ }
+ } catch (XPathException e) {
+ XQException err = new XQException(e.getMessage());
+ err.initCause(e);
+ throw err;
+ }
+ }
+
+ private void checkAtomic(/*@Nullable*/ XQItemType type, AtomicValue value) throws XQException {
+ if (type == null) {
+ return;
+ }
+ ItemType itemType = ((SaxonXQItemType)type).getSaxonItemType();
+ if (!itemType.isPlainType()) {
+ throw new XQException("Target type is not atomic");
+ }
+ AtomicType at = (AtomicType)itemType;
+ if (!at.matchesItem(value, true, getConfiguration())) {
+ throw new XQException("value is invalid for specified type");
+ }
+ }
+
+
+ /*@NotNull*/ private static String getClarkName(QName qname) {
+ String uri = qname.getNamespaceURI();
+ return "{" + (uri == null ? "" : uri) + "}" + qname.getLocalPart();
+ }
+
+ private static void checkNotNull(/*@Nullable*/ Object arg) throws XQException {
+ if (arg == null) {
+ throw new XQException("Argument is null");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXQExpression.java b/sf/saxon/xqj/SaxonXQExpression.java
new file mode 100644
index 0000000..94d0414
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQExpression.java
@@ -0,0 +1,132 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.query.DynamicQueryContext;
+import net.sf.saxon.query.StaticQueryContext;
+import net.sf.saxon.query.XQueryExpression;
+import net.sf.saxon.trans.XPathException;
+
+import javax.xml.namespace.QName;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQExpression;
+import javax.xml.xquery.XQResultSequence;
+import javax.xml.xquery.XQStaticContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Saxon implementation of the XQJ XQExpression interface
+ */
+public class SaxonXQExpression extends SaxonXQDynamicContext implements XQExpression {
+
+ private SaxonXQStaticContext sqc;
+ private DynamicQueryContext context;
+
+ SaxonXQExpression(SaxonXQConnection connection) throws XQException {
+ this.connection = connection;
+ context = new DynamicQueryContext(connection.getConfiguration());
+ context.setApplyFunctionConversionRulesToExternalVariables(false);
+ sqc = (SaxonXQStaticContext)connection.getStaticContext(); // this takes a snapshot copy
+ setClosableContainer(connection);
+ }
+
+ SaxonXQExpression(SaxonXQConnection connection, SaxonXQStaticContext staticContext) {
+ this.connection = connection;
+ context = new DynamicQueryContext(connection.getConfiguration());
+ context.setApplyFunctionConversionRulesToExternalVariables(false);
+ sqc = new SaxonXQStaticContext(staticContext); // take a snapshot copy
+ setClosableContainer(connection);
+ }
+
+ protected DynamicQueryContext getDynamicContext() {
+ return context;
+ }
+
+ protected SaxonXQDataFactory getDataFactory() throws XQException {
+ return connection;
+ }
+
+ public void cancel() throws XQException {
+ checkNotClosed();
+ //
+ }
+
+ public void executeCommand(Reader command) throws XQException {
+ checkNotClosed();
+ throw new XQException("Saxon does not recognize any non-XQuery commands");
+ }
+
+ public void executeCommand(String command) throws XQException {
+ checkNotClosed();
+ throw new XQException("Saxon does not recognize any non-XQuery commands");
+ }
+
+ public XQResultSequence executeQuery(InputStream query) throws XQException {
+ checkNotClosed();
+ try {
+ StaticQueryContext env = sqc.getSaxonStaticQueryContext();
+ XQueryExpression exp = env.compileQuery(query, null);
+ SaxonXQPreparedExpression pe = new SaxonXQPreparedExpression(connection, exp, sqc, context);
+ return pe.executeQuery();
+ } catch (XPathException e) {
+ XQException xqe = new XQException(e.getMessage());
+ xqe.initCause(e);
+ throw xqe;
+ } catch (IOException e) {
+ XQException xqe = new XQException(e.getMessage());
+ xqe.initCause(e);
+ throw xqe;
+ }
+ }
+
+ public XQResultSequence executeQuery(Reader query) throws XQException {
+ checkNotClosed();
+ SaxonXQDataSource.checkNotNull(query, "query");
+ try {
+ StaticQueryContext env = sqc.getSaxonStaticQueryContext();
+ XQueryExpression exp = env.compileQuery(query);
+ SaxonXQPreparedExpression pe = new SaxonXQPreparedExpression(connection, exp, sqc, context);
+ return pe.executeQuery();
+ } catch (XPathException e) {
+ XQException xqe = new XQException(e.getMessage());
+ xqe.initCause(e);
+ throw xqe;
+ } catch (IOException e) {
+ XQException xqe = new XQException(e.getMessage());
+ xqe.initCause(e);
+ throw xqe;
+ }
+ }
+
+ public XQResultSequence executeQuery(String query) throws XQException {
+ checkNotClosed();
+ try {
+ StaticQueryContext env = sqc.getSaxonStaticQueryContext();
+ XQueryExpression exp = env.compileQuery(query);
+ SaxonXQPreparedExpression pe = new SaxonXQPreparedExpression(connection, exp, sqc, context);
+ XQResultSequence result = pe.executeQuery();
+ ((Closable)result).setClosableContainer(this);
+ return result;
+ } catch (XPathException e) {
+ XQException xqe = new XQException(e.getMessage());
+ xqe.initCause(e);
+ throw xqe;
+ }
+ }
+
+ public XQStaticContext getStaticContext() throws XQException {
+ checkNotClosed();
+ return sqc;
+ }
+
+ protected boolean externalVariableExists(QName name) {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXQForwardSequence.java b/sf/saxon/xqj/SaxonXQForwardSequence.java
new file mode 100644
index 0000000..baa473c
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQForwardSequence.java
@@ -0,0 +1,359 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.evpull.*;
+import net.sf.saxon.functions.Insert;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.query.QueryResult;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.SingletonIterator;
+import org.w3c.dom.Node;
+import org.xml.sax.ContentHandler;
+
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.Result;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.xquery.*;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.util.Properties;
+
+/**
+ * The class is a Saxon implementation of the XQJ interface XQResultSequence. This
+ * implementation is used to represent a sequence that can only be read in a forwards direction.
+ */
+public class SaxonXQForwardSequence extends Closable implements XQResultSequence {
+
+ private SequenceIterator iterator;
+ SaxonXQPreparedExpression expression;
+ int position = 0; // set to -count when positioned after the end
+ int lastReadPosition = Integer.MIN_VALUE; // used to prevent reading the same item twice, which XQJ doesn't allow
+
+ protected SaxonXQForwardSequence(SequenceIterator iterator, SaxonXQPreparedExpression expression) {
+ this.iterator = iterator;
+ this.expression = expression;
+ setClosableContainer(expression);
+ }
+
+ /*@Nullable*/ SequenceIterator getCleanIterator() throws XPathException {
+ return iterator.getAnother();
+ }
+
+ Configuration getConfiguration() {
+ return expression.getConnection().getConfiguration();
+ }
+
+ public XQConnection getConnection() throws XQException {
+ checkNotClosed();
+ return expression.getConnection();
+ }
+
+ public String getAtomicValue() throws XQException {
+ return getCurrentXQItem(true).getAtomicValue();
+ }
+
+ public boolean getBoolean() throws XQException {
+ return getCurrentXQItem(true).getBoolean();
+ }
+
+ public byte getByte() throws XQException {
+ return getCurrentXQItem(true).getByte();
+ }
+
+ public double getDouble() throws XQException {
+ return getCurrentXQItem(true).getDouble();
+ }
+
+ public float getFloat() throws XQException {
+ return getCurrentXQItem(true).getFloat();
+ }
+
+ public int getInt() throws XQException {
+ return getCurrentXQItem(true).getInt();
+ }
+
+ public XMLStreamReader getItemAsStream() throws XQException {
+ return getCurrentXQItem(true).getItemAsStream();
+ }
+
+ public String getItemAsString(Properties props) throws XQException {
+ return getCurrentXQItem(true).getItemAsString(props);
+ }
+
+ public XQItemType getItemType() throws XQException {
+ return getCurrentXQItem(false).getItemType();
+ }
+
+ public long getLong() throws XQException {
+ return getCurrentXQItem(true).getLong();
+ }
+
+ public Node getNode() throws XQException {
+ return getCurrentXQItem(true).getNode();
+ }
+
+ public URI getNodeUri() throws XQException {
+ return getCurrentXQItem(false).getNodeUri();
+ }
+
+ public Object getObject() throws XQException {
+ return getCurrentXQItem(true).getObject();
+ }
+
+ public short getShort() throws XQException {
+ return getCurrentXQItem(true).getShort();
+ }
+
+ public boolean instanceOf(XQItemType type) throws XQException {
+ return getCurrentXQItem(false).instanceOf(type);
+ }
+
+ public void writeItem(OutputStream os, Properties props) throws XQException {
+ getCurrentXQItem(true).writeItem(os, props);
+ }
+
+ public void writeItem(Writer ow, Properties props) throws XQException {
+ getCurrentXQItem(true).writeItem(ow, props);
+ }
+
+ public void writeItemToResult(Result result) throws XQException {
+ getCurrentXQItem(true).writeItemToResult(result);
+ }
+
+ public void writeItemToSAX(ContentHandler saxHandler) throws XQException {
+ getCurrentXQItem(true).writeItemToSAX(saxHandler);
+ }
+
+ public boolean absolute(int itempos) throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public void afterLast() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public void beforeFirst() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public int count() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public boolean first() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ /*@NotNull*/ public XQItem getItem() throws XQException {
+ return getCurrentXQItem(true);
+ }
+
+ public int getPosition() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ /*@NotNull*/ public XMLStreamReader getSequenceAsStream() throws XQException {
+ checkNotClosed();
+ checkOnlyReadOnce();
+ EventIterator ei = new EventIteratorOverSequence(iterator);
+ ei = new BracketedDocumentIterator(ei);
+ Configuration config = getConfiguration();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ pipe.setHostLanguage(Configuration.XQUERY);
+ ei = new Decomposer(ei, pipe);
+ return new EventToStaxBridge(ei, pipe);
+ }
+
+ public String getSequenceAsString(Properties props) throws XQException {
+ checkNotClosed();
+ StringWriter sw = new StringWriter();
+ writeSequence(sw, props);
+ return sw.toString();
+ }
+
+ public boolean isAfterLast() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public boolean isBeforeFirst() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public boolean isFirst() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public boolean isLast() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public boolean isOnItem() throws XQException {
+ checkNotClosed();
+ return position > 0;
+ }
+
+ public boolean isScrollable() throws XQException {
+ checkNotClosed();
+ return false;
+ }
+
+ public boolean last() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public boolean next() throws XQException {
+ checkNotClosed();
+ if (position < 0) {
+ return false;
+ }
+ try {
+ Item next = iterator.next();
+ if (next == null) {
+ position = -1;
+ return false;
+ } else {
+ position++;
+ return true;
+ }
+ } catch (XPathException e) {
+ throw newXQException(e);
+ }
+ }
+
+ public boolean previous() throws XQException {
+ throw new XQException("Sequence is forwards-only");
+ }
+
+ public boolean relative(int itempos) throws XQException {
+ checkNotClosed();
+// if (itempos < 0) {
+ throw new XQException("Sequence is forwards-only, cannot move backwards");
+// } else {
+// for (int i=0; i<itempos; i++) {
+// if (!next()) {
+// return false;
+// }
+// }
+// return true;
+// }
+ }
+
+ public void writeSequence(OutputStream os, /*@Nullable*/ Properties props) throws XQException {
+ checkNotNull(os);
+ checkNotClosed();
+ checkOnlyReadOnce();
+ if (props == null) {
+ props = new Properties();
+ }
+ props = SaxonXQSequence.setDefaultProperties(props);
+ SequenceIterator iter = iterator;
+ if (isOnItem()) {
+ iter = new Insert.InsertIterator(
+ SingletonIterator.makeIterator(iter.current()),
+ iter, 0);
+ }
+ try {
+ QueryResult.serializeSequence(iter, getConfiguration(), os, props);
+ } catch (XPathException e) {
+ throw newXQException(e);
+ }
+ }
+
+ public void writeSequence(Writer ow, /*@Nullable*/ Properties props) throws XQException {
+ checkNotClosed();
+ checkNotNull(ow);
+ checkOnlyReadOnce();
+ if (props == null) {
+ props = new Properties();
+ } else {
+ SaxonXQItem.validateSerializationProperties(props, expression.getConfiguration());
+ }
+ props = SaxonXQSequence.setDefaultProperties(props);
+ PrintWriter pw;
+ if (ow instanceof PrintWriter) {
+ pw = (PrintWriter)ow;
+ } else {
+ pw = new PrintWriter(ow);
+ }
+ SequenceIterator iter = iterator;
+ if (isOnItem()) {
+ iter = new Insert.InsertIterator(
+ SingletonIterator.makeIterator(iter.current()),
+ iter, 0);
+ }
+ try {
+ QueryResult.serializeSequence(iter, getConfiguration(), pw, props);
+ } catch (XPathException e) {
+ throw newXQException(e);
+ }
+ }
+
+ public void writeSequenceToResult(Result result) throws XQException {
+ checkNotClosed();
+ checkNotNull(result);
+ checkOnlyReadOnce();
+ Properties props = SaxonXQSequence.setDefaultProperties(null);
+ try {
+ QueryResult.serializeSequence(iterator, getConfiguration(), result, props);
+ } catch (XPathException e) {
+ throw newXQException(e);
+ }
+ }
+
+ public void writeSequenceToSAX(ContentHandler saxHandler) throws XQException {
+ checkNotClosed();
+ checkNotNull(saxHandler);
+ writeSequenceToResult(new SAXResult(saxHandler));
+ }
+
+ /*@NotNull*/ private XQItem getCurrentXQItem(boolean onceOnly) throws XQException {
+ checkNotClosed();
+ if (position == 0) {
+ throw new XQException("The XQSequence is positioned before the first item");
+ } else if (position < 0) {
+ throw new XQException("The XQSequence is positioned after the last item");
+ }
+ if (onceOnly) {
+ checkOnlyReadOnce();
+ }
+ SaxonXQItem item = new SaxonXQItem(iterator.current(), expression.getConnection());
+ item.setClosableContainer(this);
+ return item;
+ }
+
+ /*@Nullable*/ Item getSaxonItem() {
+ return iterator.current();
+ }
+
+
+ private void checkNotNull(/*@Nullable*/ Object arg) throws XQException {
+ if (arg == null) {
+ throw new XQException("Argument is null");
+ }
+ }
+
+ private void checkOnlyReadOnce() throws XQException {
+ if (position == lastReadPosition) {
+ throw new XQException("XQJ does not allow the same item to be read more than once");
+ }
+ lastReadPosition = position;
+ }
+
+ /*@NotNull*/ private XQException newXQException(/*@NotNull*/ Exception err) {
+ XQException xqe = new XQException(err.getMessage());
+ xqe.initCause(err);
+ return xqe;
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXQItem.java b/sf/saxon/xqj/SaxonXQItem.java
new file mode 100644
index 0000000..58c134e
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQItem.java
@@ -0,0 +1,327 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.dom.NodeOverNodeInfo;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Receiver;
+import net.sf.saxon.event.TreeReceiver;
+import net.sf.saxon.evpull.*;
+import net.sf.saxon.expr.instruct.ResultDocument;
+import net.sf.saxon.lib.SerializerFactory;
+import net.sf.saxon.om.DocumentInfo;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.wrapper.VirtualNode;
+import net.sf.saxon.value.*;
+import org.w3c.dom.Node;
+import org.xml.sax.ContentHandler;
+
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.Result;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xquery.XQConnection;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQItemType;
+import javax.xml.xquery.XQResultItem;
+import java.io.OutputStream;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ * This Saxon class is used to implement both the XQItem and XQResultItem interfaces in XQJ.
+ * Where the item is not a real XQResultItem, getConnection() will return null.
+ */
+public class SaxonXQItem extends Closable implements XQResultItem, SaxonXQItemAccessor {
+
+ /*@Nullable*/ private Item item;
+ private Configuration config;
+ SaxonXQDataFactory dataFactory;
+
+ public SaxonXQItem(/*@Nullable*/ Item item, /*@NotNull*/ SaxonXQDataFactory factory) {
+ if (item == null) {
+ throw new NullPointerException("item");
+ }
+ this.item = item;
+ dataFactory = factory;
+ config = factory.getConfiguration();
+ setClosableContainer(factory);
+ }
+
+ Configuration getConfiguration() {
+ return config;
+ }
+
+ /*@Nullable*/ public Item getSaxonItem() {
+ return item;
+ }
+
+ /*@Nullable*/ public XQConnection getConnection() throws XQException {
+ checkNotClosed();
+ if (dataFactory instanceof XQConnection) {
+ return (XQConnection)dataFactory;
+ } else {
+ return null;
+ }
+ }
+
+ public String getAtomicValue() throws XQException {
+ checkNotClosed();
+ if (item instanceof AtomicValue) {
+ return item.getStringValue();
+ }
+ throw new XQException("Failed to getAtomicValue: item is a node, or is closed");
+ }
+
+ public boolean getBoolean() throws XQException {
+ checkNotClosed();
+ if (item instanceof BooleanValue) {
+ return ((BooleanValue)item).getBooleanValue();
+ }
+ throw new XQException("Failed in getBoolean: item is not a boolean, or is closed");
+ }
+
+ public byte getByte() throws XQException {
+ checkNotClosed();
+ if (item instanceof AtomicValue) {
+ AtomicValue prim = ((AtomicValue)item);
+ return (byte)longValue(prim, -128, 127);
+ }
+ throw new XQException("Failed in getByte: item is not an atomic value, or is closed");
+ }
+
+ private static long longValue(/*@NotNull*/ AtomicValue value, long min, long max) throws XQException {
+ if (value instanceof NumericValue) {
+ if (value instanceof DoubleValue || value instanceof FloatValue) {
+ throw new XQException("Value is a double or float");
+ }
+ if (!((NumericValue)value).isWholeNumber()) {
+ throw new XQException("Value is not a whole number");
+ }
+ try {
+ long val = ((NumericValue)value).longValue();
+ if (val >= min && val <= max) {
+ return val;
+ } else {
+ throw new XQException("Value is out of range for requested type");
+ }
+ } catch (XPathException err) {
+ XQException xqe = new XQException(err.getMessage());
+ xqe.initCause(err);
+ throw xqe;
+ }
+ }
+ throw new XQException("Value is not numeric");
+ }
+
+ public double getDouble() throws XQException {
+ checkNotClosed();
+ if (item instanceof DoubleValue) {
+ return ((DoubleValue)item).getDoubleValue();
+ }
+ throw new XQException("Failed in getDouble: item is not a double, or is closed");
+ }
+
+ public float getFloat() throws XQException {
+ checkNotClosed();
+ if (item instanceof FloatValue) {
+ return ((FloatValue)item).getFloatValue();
+ }
+ throw new XQException("Failed in getFloat: item is not a float, or is closed");
+ }
+
+ public int getInt() throws XQException {
+ checkNotClosed();
+ if (item instanceof AtomicValue) {
+ AtomicValue prim = ((AtomicValue)item);
+ return (int)longValue(prim, Integer.MIN_VALUE, Integer.MAX_VALUE);
+ }
+ throw new XQException("Failed in getInt: item is not an atomic value, or is closed");
+ }
+
+ /*@NotNull*/ public XMLStreamReader getItemAsStream() throws XQException {
+ // The spec (section 12.1) requires that the item be converted into a document, and we
+ // then read events corresponding to this document
+ checkNotClosed();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ pipe.setHostLanguage(Configuration.XQUERY);
+ if (item instanceof DocumentInfo) {
+ EventIterator eventIterator = new Decomposer((NodeInfo)item, pipe);
+ return new EventToStaxBridge(eventIterator, pipe);
+ } else {
+ EventIterator contentIterator = new SingletonEventIterator(item);
+ EventIterator eventIterator = new BracketedDocumentIterator(contentIterator);
+ eventIterator = new Decomposer(eventIterator, pipe);
+ return new EventToStaxBridge(eventIterator, pipe);
+ }
+ }
+
+ public String getItemAsString(/*@Nullable*/ Properties props) throws XQException {
+ checkNotClosed();
+ if (props == null) {
+ props = new Properties();
+ } else {
+ validateSerializationProperties(props, config);
+ }
+ props = SaxonXQSequence.setDefaultProperties(props);
+ StringWriter writer = new StringWriter();
+ writeItem(writer, props);
+ return writer.toString();
+ }
+
+ protected static void validateSerializationProperties(/*@NotNull*/ Properties props, Configuration config) throws XQException {
+ Properties validatedProps = new Properties();
+ for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
+ String name = (String)e.nextElement();
+ String value = props.getProperty(name);
+ String localName = name;
+ String uri = "";
+ if (name.startsWith("{")) {
+ StructuredQName qName = StructuredQName.fromClarkName(name);
+ localName = qName.getLocalPart();
+ uri = qName.getURI();
+ }
+ try {
+ ResultDocument.setSerializationProperty(validatedProps, uri, localName, value, null, false, config);
+ } catch (XPathException ex) {
+ throw new XQException("Invalid serialization property: " + ex.getMessage());
+ }
+ }
+ }
+
+ /*@NotNull*/ public XQItemType getItemType() throws XQException {
+ checkNotClosed();
+ if (item instanceof AtomicValue) {
+ return new SaxonXQItemType(
+ ((AtomicValue)item).getItemType(),
+ getConfiguration());
+ } else {
+ return new SaxonXQItemType((NodeInfo)item);
+ }
+ }
+
+ public long getLong() throws XQException {
+ checkNotClosed();
+ if (item instanceof AtomicValue) {
+ AtomicValue prim = ((AtomicValue)item);
+ return (byte)longValue(prim, Long.MIN_VALUE, Long.MAX_VALUE);
+ }
+ throw new XQException("Failed in getLong: item is not an atomic value, or is closed");
+ }
+
+ public Node getNode() throws XQException {
+ checkNotClosed();
+ if (!(item instanceof NodeInfo)) {
+ throw new XQException("Failed in getNode: item is an atomic value, or is closed");
+ }
+ if (item instanceof VirtualNode) {
+ Object n = ((VirtualNode)item).getRealNode();
+ if (n instanceof Node) {
+ return (Node)n;
+ }
+ }
+ return NodeOverNodeInfo.wrap((NodeInfo)item);
+ }
+
+ /*@NotNull*/ public URI getNodeUri() throws XQException {
+ checkNotClosed();
+ if (item instanceof NodeInfo) {
+ try {
+ String systemId = ((NodeInfo)item).getSystemId();
+ if (systemId == null) {
+ return new URI("");
+ }
+ return new URI(systemId);
+ } catch (URISyntaxException e) {
+ throw new XQException("System ID of node is not a valid URI");
+ }
+ }
+ throw new XQException("Item is not a node");
+ }
+
+ public Object getObject() throws XQException {
+ checkNotClosed();
+ return dataFactory.getObjectConverter().toObject(this);
+ }
+
+ public short getShort() throws XQException {
+ checkNotClosed();
+ if (item instanceof AtomicValue) {
+ AtomicValue prim = ((AtomicValue)item);
+ return (short)longValue(prim, Short.MIN_VALUE, Short.MAX_VALUE);
+ }
+ throw new XQException("Failed in getShort: item is not an atomic value, or is closed");
+ }
+
+ public boolean instanceOf(/*@NotNull*/ XQItemType type) throws XQException {
+ checkNotClosed();
+ checkNotNull(type);
+ return ((SaxonXQItemType)type).getSaxonItemType().matchesItem(item, false, config);
+ }
+
+ public void writeItem(OutputStream os, Properties props) throws XQException {
+ checkNotClosed();
+ checkNotNull(os);
+ writeItemToResult(new StreamResult(os), props);
+ }
+
+ public void writeItem(Writer ow, Properties props) throws XQException {
+ checkNotClosed();
+ checkNotNull(ow);
+ writeItemToResult(new StreamResult(ow), props);
+ }
+
+ public void writeItemToResult(Result result) throws XQException {
+ checkNotClosed();
+ checkNotNull(result);
+ writeItemToResult(result, new Properties());
+ }
+
+ private void writeItemToResult(Result result, /*@Nullable*/ Properties props) throws XQException {
+ checkNotClosed();
+ checkNotNull(result);
+ if (props == null) {
+ props = new Properties();
+ }
+ props = SaxonXQSequence.setDefaultProperties(props);
+ try {
+ SerializerFactory sf = config.getSerializerFactory();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ Receiver out = sf.getReceiver(result, pipe, props);
+ TreeReceiver tr = new TreeReceiver(out);
+ tr.open();
+ tr.append(item, 0, NodeInfo.ALL_NAMESPACES);
+ tr.close();
+ } catch (XPathException e) {
+ XQException xqe = new XQException(e.getMessage());
+ xqe.initCause(e);
+ throw xqe;
+ }
+ }
+
+ public void writeItemToSAX(ContentHandler saxHandler) throws XQException {
+ checkNotClosed();
+ checkNotNull(saxHandler);
+ writeItemToResult(new SAXResult(saxHandler));
+ }
+
+ private void checkNotNull(/*@Nullable*/ Object arg) throws XQException {
+ if (arg == null) {
+ throw new XQException("Argument is null");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXQItemAccessor.java b/sf/saxon/xqj/SaxonXQItemAccessor.java
new file mode 100644
index 0000000..0d26d05
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQItemAccessor.java
@@ -0,0 +1,28 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.om.Item;
+
+import javax.xml.xquery.XQException;
+
+/**
+ * All Saxon implementations of XQItemAccessor must implement this interface
+ */
+public interface SaxonXQItemAccessor {
+
+ /**
+ * Get the current item, in the form of a Saxon Item object. This allows access to non-XQJ methods
+ * to manipulate the item, which will not necessarily be stable from release to release. The resulting
+ * Item will be an instance of either {@link net.sf.saxon.om.NodeInfo} or {@link net.sf.saxon.value.AtomicValue}.
+ * @return the current item
+ */
+
+ /*@Nullable*/ public Item getSaxonItem() throws XQException;
+}
+
diff --git a/sf/saxon/xqj/SaxonXQItemType.java b/sf/saxon/xqj/SaxonXQItemType.java
new file mode 100644
index 0000000..2c3f958
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQItemType.java
@@ -0,0 +1,260 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.om.NamePool;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.pattern.DocumentNodeTest;
+import net.sf.saxon.pattern.NameTest;
+import net.sf.saxon.pattern.NodeKindTest;
+import net.sf.saxon.pattern.NodeTest;
+import net.sf.saxon.type.*;
+import net.sf.saxon.z.IntIterator;
+import net.sf.saxon.z.IntSet;
+
+import javax.xml.namespace.QName;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQItemType;
+import javax.xml.xquery.XQSequenceType;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Saxon implementation of the XQJ XQItemType interface
+ */
+public class SaxonXQItemType implements XQItemType {
+
+ /*@NotNull*/ private ItemType itemType;
+ /*@NotNull*/ private Configuration config;
+
+ protected SaxonXQItemType(/*@NotNull*/ ItemType itemType, /*@NotNull*/ Configuration config) {
+ this.itemType = itemType;
+ this.config = config;
+ }
+
+ protected SaxonXQItemType(/*@NotNull*/ NodeInfo node) {
+ config = node.getConfiguration();
+ itemType = Type.getItemType(node, config.getTypeHierarchy());
+ }
+
+ public int getBaseType() throws XQException {
+ if (itemType instanceof AtomicType) {
+ AtomicType at = (AtomicType)itemType;
+ while (!at.isBuiltInType()) {
+ at = (AtomicType)at.getBaseType();
+ }
+ return SaxonXQDataFactory.mapSaxonTypeToXQJ(at.getFingerprint());
+ } else if (itemType instanceof NodeTest) {
+ NodeTest it = (NodeTest)itemType;
+ if (it instanceof DocumentNodeTest) {
+ it = ((DocumentNodeTest)it).getElementTest();
+ }
+ if ((it.getNodeKindMask() &
+ (1<<Type.DOCUMENT | 1<<Type.TEXT | 1<<Type.COMMENT | 1<<Type.PROCESSING_INSTRUCTION | 1<<Type.NAMESPACE)) != 0) {
+ throw new XQException("Wrong node kind for getBaseType()");
+ }
+ SchemaType contentType = it.getContentType();
+ if (contentType.isAtomicType()) {
+ AtomicType at = (AtomicType)contentType;
+ while (!at.isBuiltInType()) {
+ at = (AtomicType)at.getBaseType();
+ }
+ return SaxonXQDataFactory.mapSaxonTypeToXQJ(at.getFingerprint());
+ } else if (contentType.isSimpleType()) {
+ if (((SimpleType)contentType).isListType()) {
+ int fp = contentType.getFingerprint();
+ if (fp == StandardNames.XS_NMTOKENS) {
+ return XQBASETYPE_NMTOKENS;
+ } else if (fp == StandardNames.XS_ENTITIES) {
+ return XQBASETYPE_ENTITIES;
+ } else if (fp == StandardNames.XS_IDREFS) {
+ return XQBASETYPE_IDREFS;
+ }
+ }
+ return XQBASETYPE_ANYSIMPLETYPE;
+ } else if (contentType == Untyped.getInstance()) {
+ return XQBASETYPE_UNTYPED;
+ } else {
+ return XQBASETYPE_ANYTYPE;
+ }
+
+ } else {
+ throw new XQException("Wrong item type for getBaseType()");
+ }
+ }
+
+ public int getItemKind() {
+ if (itemType instanceof AtomicType) {
+ return XQITEMKIND_ATOMIC;
+ } else if (itemType instanceof NodeTest) {
+ if (itemType instanceof DocumentNodeTest) {
+ return XQITEMKIND_DOCUMENT_ELEMENT;
+ }
+ int x = itemType.getPrimitiveType();
+ switch (x) {
+ case Type.DOCUMENT:
+ return XQITEMKIND_DOCUMENT;
+ case Type.ELEMENT:
+ return XQITEMKIND_ELEMENT;
+ case Type.ATTRIBUTE:
+ return XQITEMKIND_ATTRIBUTE;
+ case Type.TEXT:
+ return XQITEMKIND_TEXT;
+ case Type.COMMENT:
+ return XQITEMKIND_COMMENT;
+ case Type.PROCESSING_INSTRUCTION:
+ return XQITEMKIND_PI;
+ case Type.NODE:
+ return XQITEMKIND_NODE;
+ }
+ }
+ return XQITEMKIND_ITEM;
+ }
+
+ public int getItemOccurrence() {
+ return XQSequenceType.OCC_EXACTLY_ONE;
+ }
+
+ /*@Nullable*/ public QName getNodeName() throws XQException {
+ ItemType type = itemType;
+ if (type instanceof DocumentNodeTest) {
+ type = ((DocumentNodeTest)type).getElementTest();
+ }
+ if (type instanceof NodeTest) {
+ if ((((NodeTest)type).getNodeKindMask() &
+ (1<<Type.DOCUMENT | 1<<Type.TEXT | 1<<Type.COMMENT | 1<<Type.PROCESSING_INSTRUCTION | 1<<Type.NAMESPACE)) != 0) {
+ throw new XQException("Wrong node kind for getNodeName()");
+ }
+ IntSet set = ((NodeTest)type).getRequiredNodeNames();
+ if (set != null && set.size() == 1) {
+ IntIterator ii = set.iterator();
+ int fp = (ii.hasNext() ? ii.next() : -1);
+ NamePool pool = config.getNamePool();
+ String uri = pool.getURI(fp);
+ String local = pool.getLocalName(fp);
+ return new QName(uri, local);
+ } else {
+ return null;
+ }
+ }
+ throw new XQException("getNodeName() is not defined for this kind of item type");
+ }
+
+ /*@Nullable*/ public String getPIName() throws XQException {
+ if (itemType instanceof NameTest && itemType.getPrimitiveType() == Type.PROCESSING_INSTRUCTION) {
+ NamePool pool = config.getNamePool();
+ return pool.getLocalName(((NameTest)itemType).getFingerprint());
+ } else if (itemType instanceof NodeKindTest && itemType.getPrimitiveType() == Type.PROCESSING_INSTRUCTION) {
+ return null;
+ } else {
+ throw new XQException("Item kind is not a processing instruction");
+ }
+ }
+
+ /*@Nullable*/ public URI getSchemaURI() {
+ try {
+ if (itemType instanceof NodeTest) {
+ SchemaType content = ((NodeTest)itemType).getContentType();
+ if (content == null) {
+ return null;
+ }
+ String systemId = content.getSystemId();
+ if (systemId == null) {
+ return null;
+ }
+ return new URI(systemId);
+ } else if (itemType instanceof AtomicType) {
+ String systemId = ((AtomicType)itemType).getSystemId();
+ return (systemId == null ? null : new URI(systemId));
+ } else {
+ return null;
+ }
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+
+ /*@Nullable*/ public String toString() {
+ return itemType.toString();
+ }
+
+ /*@NotNull*/ public QName getTypeName() throws XQException {
+ ItemType type = itemType;
+ if (type instanceof AtomicType) {
+ int fp = ((AtomicType)type).getFingerprint();
+ NamePool pool = config.getNamePool();
+ String uri = pool.getURI(fp);
+ String local = pool.getLocalName(fp);
+ return new QName(uri, local);
+ }
+ if (type instanceof DocumentNodeTest) {
+ type = ((DocumentNodeTest)type).getElementTest();
+ }
+ if (type instanceof NodeTest) {
+ if ((((NodeTest)type).getNodeKindMask() &
+ (1<<Type.DOCUMENT | 1<<Type.TEXT | 1<<Type.COMMENT | 1<<Type.PROCESSING_INSTRUCTION | 1<<Type.NAMESPACE)) != 0) {
+ throw new XQException("getTypeName() failed: itemType is not a document, element, or attribute test");
+ }
+ SchemaType t = ((NodeTest)type).getContentType();
+ if (t != null) {
+ int fp = ((NodeTest)type).getContentType().getFingerprint();
+ NamePool pool = config.getNamePool();
+ String uri = pool.getURI(fp);
+ String local = pool.getLocalName(fp);
+ return new QName(uri, local);
+ }
+ }
+ throw new XQException("getTypeName() failed: itemType is not a document, element, or attribute test");
+ }
+
+ public boolean isAnonymousType() {
+ ItemType type = itemType;
+ if (type instanceof DocumentNodeTest) {
+ type = ((DocumentNodeTest)type).getElementTest();
+ }
+ if (type instanceof NodeTest) {
+ SchemaType t = ((NodeTest)type).getContentType();
+ if (t != null) {
+ return t.isAnonymousType();
+ }
+ }
+ return false;
+ }
+
+ public boolean isElementNillable() {
+ return (itemType instanceof NodeTest) && ((NodeTest)itemType).getNodeKindMask() == 1<<Type.ELEMENT && ((NodeTest)itemType).isNillable();
+ }
+
+
+ /*@NotNull*/ public XQItemType getItemType() {
+ return this;
+ }
+
+ /*@Nullable*/ AtomicType getAtomicType() {
+ if (itemType instanceof AtomicType) {
+ return (AtomicType)itemType;
+ } else {
+ return null;
+ }
+ }
+
+ /*@Nullable*/ ItemType getSaxonItemType() {
+ return itemType;
+ }
+
+ public boolean equals(/*@NotNull*/ Object obj) {
+ return obj instanceof SaxonXQItemType &&
+ itemType.equals(((SaxonXQItemType)obj).itemType);
+ }
+
+ public int hashCode() {
+ return itemType.hashCode();
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXQMetaData.java b/sf/saxon/xqj/SaxonXQMetaData.java
new file mode 100644
index 0000000..a18a83a
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQMetaData.java
@@ -0,0 +1,163 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.Version;
+
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQMetaData;
+import java.util.Set;
+
+/**
+ * Saxon implementation of the XQMetaData interface
+ */
+public class SaxonXQMetaData implements XQMetaData {
+
+ private SaxonXQConnection connection;
+
+ /**
+ * Create the metadata for a given Saxon configuration
+ * @param connection the Saxon connection
+ */
+
+ public SaxonXQMetaData(SaxonXQConnection connection) {
+ this.connection = connection;
+ }
+
+ public int getMaxExpressionLength() throws XQException {
+ checkNotClosed();
+ checkNotClosed();
+ return Integer.MAX_VALUE;
+ }
+
+ public int getMaxUserNameLength() throws XQException {
+ checkNotClosed();
+ return Integer.MAX_VALUE;
+ }
+
+ public int getProductMajorVersion() throws XQException {
+ checkNotClosed();
+ return Version.getStructuredVersionNumber()[0];
+ }
+
+ public int getProductMinorVersion() throws XQException {
+ checkNotClosed();
+ return Version.getStructuredVersionNumber()[1];
+ }
+
+ /*@NotNull*/ public String getProductName() throws XQException {
+ checkNotClosed();
+ return Version.getProductName();
+ }
+
+ /*@NotNull*/ public String getProductVersion() throws XQException {
+ checkNotClosed();
+ return Version.getProductVersion();
+ }
+
+ public Set getSupportedXQueryEncodings() throws XQException {
+ checkNotClosed();
+ return java.nio.charset.Charset.availableCharsets().keySet();
+ }
+
+ /*@Nullable*/ public String getUserName() throws XQException {
+ checkNotClosed();
+ return null;
+ }
+
+ public int getXQJMajorVersion() throws XQException {
+ checkNotClosed();
+ return 0;
+ }
+
+ public int getXQJMinorVersion() throws XQException {
+ checkNotClosed();
+ return 9;
+ }
+
+ /*@NotNull*/ public String getXQJVersion() throws XQException {
+ checkNotClosed();
+ return "0.9";
+ }
+
+ public boolean isFullAxisFeatureSupported() throws XQException {
+ checkNotClosed();
+ return true;
+ }
+
+ public boolean isModuleFeatureSupported() throws XQException {
+ checkNotClosed();
+ return true;
+ }
+
+ public boolean isReadOnly() throws XQException {
+ checkNotClosed();
+ return true;
+ }
+
+ public boolean isSchemaImportFeatureSupported() throws XQException {
+ checkNotClosed();
+ return connection.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY);
+ }
+
+ public boolean isSchemaValidationFeatureSupported() throws XQException {
+ checkNotClosed();
+ return connection.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY);
+ }
+
+ public boolean isSerializationFeatureSupported() throws XQException {
+ checkNotClosed();
+ return true;
+ }
+
+ public boolean isStaticTypingExtensionsSupported() throws XQException {
+ checkNotClosed();
+ return false;
+ }
+
+ public boolean isStaticTypingFeatureSupported() throws XQException {
+ checkNotClosed();
+ return false;
+ }
+
+ public boolean isTransactionSupported() throws XQException {
+ checkNotClosed();
+ return false;
+ }
+
+ public boolean isUserDefinedXMLSchemaTypeSupported() throws XQException {
+ checkNotClosed();
+ return connection.getConfiguration().isLicensedFeature(Configuration.LicenseFeature.ENTERPRISE_XQUERY);
+ }
+
+ public boolean isXQueryEncodingDeclSupported() throws XQException {
+ checkNotClosed();
+ return true;
+ }
+
+ public boolean isXQueryEncodingSupported(String encoding) throws XQException {
+ checkNotClosed();
+ return getSupportedXQueryEncodings().contains(encoding);
+ }
+
+ public boolean isXQueryXSupported() throws XQException {
+ checkNotClosed();
+ return false;
+ }
+
+ public boolean wasCreatedFromJDBCConnection() throws XQException {
+ checkNotClosed();
+ return false;
+ }
+
+ private void checkNotClosed() throws XQException {
+ connection.checkNotClosed();
+ }
+}
+
diff --git a/sf/saxon/xqj/SaxonXQPreparedExpression.java b/sf/saxon/xqj/SaxonXQPreparedExpression.java
new file mode 100644
index 0000000..e0f6e5a
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQPreparedExpression.java
@@ -0,0 +1,197 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.expr.Expression;
+import net.sf.saxon.expr.instruct.GlobalParam;
+import net.sf.saxon.expr.instruct.GlobalVariable;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.om.StructuredQName;
+import net.sf.saxon.query.DynamicQueryContext;
+import net.sf.saxon.query.XQueryExpression;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.type.ItemType;
+import net.sf.saxon.value.SequenceExtent;
+import net.sf.saxon.value.SequenceType;
+import net.sf.saxon.z.IntHashSet;
+import net.sf.saxon.z.IntIterator;
+
+import javax.xml.namespace.QName;
+import javax.xml.xquery.*;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Saxon implementation of the XQJ interface XQPreparedExpression. This represents a compiled XQuery
+ * expression, together with the dynamic context for its evaluation. Note that this means the object
+ * should not be used in more than one thread concurrently.
+ * <p>
+ * Note that an expression is scrollable or not depending on the scrollability property of the XQConnection
+ * that was used to compile this expression (at the time it was compiled). If the expression is scrollable then
+ * its results are delivered in an XQSequence that supports scrolling backwards as well as forwards.
+ * <p>
+ * For full Javadoc details, see the XQJ interface specification.
+ */
+public class SaxonXQPreparedExpression extends SaxonXQDynamicContext implements XQPreparedExpression {
+
+ private XQueryExpression expression;
+ private SaxonXQStaticContext staticContext;
+ private DynamicQueryContext context;
+ private boolean scrollable;
+
+ protected SaxonXQPreparedExpression(SaxonXQConnection connection,
+ XQueryExpression expression,
+ /*@NotNull*/ SaxonXQStaticContext sqc,
+ DynamicQueryContext context)
+ throws XQException {
+ this.connection = connection;
+ this.expression = expression;
+ this.staticContext = new SaxonXQStaticContext(sqc); // take a snapshot of the supplied static context
+ this.context = context;
+ scrollable = sqc.getScrollability() == XQConstants.SCROLLTYPE_SCROLLABLE;
+ setClosableContainer(connection);
+ }
+
+ protected DynamicQueryContext getDynamicContext() {
+ return context;
+ }
+
+ protected SaxonXQConnection getConnection() {
+ return connection;
+ }
+
+ protected SaxonXQDataFactory getDataFactory() throws XQException {
+ if (connection.isClosed()) {
+ close();
+ }
+ checkNotClosed();
+ return connection;
+ }
+
+ protected XQueryExpression getXQueryExpression() {
+ return expression;
+ }
+
+ protected SaxonXQStaticContext getSaxonXQStaticContext() {
+ return staticContext;
+ }
+
+ public void cancel() throws XQException {
+ checkNotClosed();
+ }
+
+ /*@NotNull*/ public XQResultSequence executeQuery() throws XQException {
+ checkNotClosed();
+ try {
+ SequenceIterator iter = expression.iterator(context);
+ if (scrollable) {
+ SequenceExtent value = new SequenceExtent(iter);
+ return new SaxonXQSequence(value, this);
+ } else {
+ return new SaxonXQForwardSequence(iter, this);
+ }
+ } catch (XPathException de) {
+ XQException xqe = new XQException(de.getMessage());
+ xqe.initCause(de);
+ throw xqe;
+ }
+ }
+
+ /*@NotNull*/ public QName[] getAllExternalVariables() throws XQException {
+ checkNotClosed();
+ HashMap<StructuredQName, GlobalVariable> vars = expression.getExecutable().getCompiledGlobalVariables();
+ if (vars == null || vars.isEmpty()) {
+ return EMPTY_QNAME_ARRAY;
+ } else {
+ HashSet<StructuredQName> params = new HashSet<StructuredQName>(vars.size());
+ for (GlobalVariable var : vars.values()) {
+ if (var instanceof GlobalParam) {
+ StructuredQName q = var.getVariableQName();
+ if (!(q.getURI().equals(NamespaceConstant.SAXON) && q.getLocalPart().equals("context-item"))) {
+ params.add(q);
+ }
+ }
+ }
+ QName[] qnames = new QName[params.size()];
+ int q=0;
+ for (StructuredQName name : params) {
+ qnames[q++] = new QName(name.getURI(), name.getLocalPart(), name.getPrefix());
+ }
+ return qnames;
+ }
+ }
+
+ /*@NotNull*/ private static QName[] EMPTY_QNAME_ARRAY = new QName[0];
+
+ /*@NotNull*/ public QName[] getAllUnboundExternalVariables() throws XQException {
+ checkNotClosed();
+ java.util.Collection<StructuredQName> boundParameters = getDynamicContext().getParameters().getKeys();
+ IntHashSet unbound = new IntHashSet(boundParameters.size());
+ QName[] all = getAllExternalVariables();
+ for (int i=0; i<all.length; i++) {
+ StructuredQName sq = new StructuredQName("", all[i].getNamespaceURI(), all[i].getLocalPart());
+ if (!boundParameters.contains(sq)) {
+ unbound.add(i);
+ }
+ }
+ QName[] unboundq = new QName[unbound.size()];
+ int c = 0;
+ IntIterator iter = unbound.iterator();
+ while (iter.hasNext()) {
+ int x = iter.next();
+ unboundq[c++] = all[x];
+ }
+ return unboundq;
+ }
+
+ public XQStaticContext getStaticContext() throws XQException {
+ checkNotClosed();
+ return staticContext; // a snapshot of the static context at the time the expression was created
+ // old code in Saxon 9.2:
+ // return new SaxonXQExpressionContext(expression);
+ }
+
+ /*@NotNull*/ public XQSequenceType getStaticResultType() throws XQException {
+ checkNotClosed();
+ Expression exp = expression.getExpression(); // unwrap two layers!
+ ItemType itemType = exp.getItemType(connection.getConfiguration().getTypeHierarchy());
+ int cardinality = exp.getCardinality();
+ SequenceType staticType = SequenceType.makeSequenceType(itemType, cardinality);
+ return new SaxonXQSequenceType(staticType, connection.getConfiguration());
+ }
+
+ /*@NotNull*/ public XQSequenceType getStaticVariableType(QName name) throws XQException {
+ checkNotClosed();
+ checkNotNull(name);
+ StructuredQName qn = new StructuredQName(
+ name.getPrefix(), name.getNamespaceURI(), name.getLocalPart());
+ HashMap vars = expression.getExecutable().getCompiledGlobalVariables();
+ GlobalVariable var = (vars == null ? null : (GlobalVariable)vars.get(qn));
+ if (var == null) {
+ throw new XQException("Variable " + name + " is not declared");
+ }
+ return new SaxonXQSequenceType(var.getRequiredType(), connection.getConfiguration());
+ }
+
+
+ protected boolean externalVariableExists(QName name) {
+ StructuredQName qn = new StructuredQName(
+ name.getPrefix(), name.getNamespaceURI(), name.getLocalPart());
+ HashMap vars = expression.getExecutable().getCompiledGlobalVariables();
+ GlobalVariable var = (vars == null ? null : (GlobalVariable)vars.get(qn));
+ return var != null && var instanceof GlobalParam;
+ }
+
+ private void checkNotNull(/*@Nullable*/ Object arg) throws XQException {
+ if (arg == null) {
+ throw new XQException("Argument is null");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXQSequence.java b/sf/saxon/xqj/SaxonXQSequence.java
new file mode 100644
index 0000000..fc8851c
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQSequence.java
@@ -0,0 +1,426 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.evpull.*;
+import net.sf.saxon.expr.TailIterator;
+import net.sf.saxon.om.GroundedValue;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.SequenceIterator;
+import net.sf.saxon.query.QueryResult;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.iter.EmptyIterator;
+import org.w3c.dom.Node;
+import org.xml.sax.ContentHandler;
+
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.xquery.*;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URI;
+import java.util.Properties;
+
+/**
+ * Saxon implementation of the XQSequence interface in XQJ, which represents an XDM sequence together
+ * with a current position. This class is used for a sequence that can be read forwards, backwards,
+ * or by absolute position.
+ */
+public class SaxonXQSequence extends Closable implements XQResultSequence, SaxonXQItemAccessor {
+
+ private GroundedValue value;
+ private int position;
+ private SaxonXQPreparedExpression expression;
+ private SaxonXQDataFactory factory;
+
+ SaxonXQSequence(GroundedValue value, SaxonXQDataFactory factory) {
+ this.value = value;
+ this.factory = factory;
+ setClosableContainer(factory);
+ }
+
+ SaxonXQSequence(GroundedValue value, /*@NotNull*/ SaxonXQPreparedExpression expression) {
+ this.value = value;
+ this.expression = expression;
+ this.factory = expression.getConnection();
+ setClosableContainer(expression);
+ }
+
+ GroundedValue getValue() {
+ return value;
+ }
+
+ Configuration getConfiguration() {
+ return factory.getConfiguration();
+ }
+
+ public boolean absolute(int itempos) throws XQException {
+ checkNotClosed();
+ if (itempos > 0) {
+ if (itempos <= value.getLength()) {
+ position = itempos;
+ return true;
+ } else {
+ position = -1;
+ return false;
+ }
+ } else if (itempos < 0) {
+ if (-itempos <= value.getLength()) {
+ position = value.getLength() + itempos + 1;
+ return true;
+ } else {
+ position = 0;
+ return false;
+ }
+ } else {
+ position = 0;
+ return false;
+ }
+ }
+
+ public void afterLast() throws XQException {
+ checkNotClosed();
+ position = -1;
+ }
+
+ public void beforeFirst() throws XQException {
+ checkNotClosed();
+ position = 0;
+ }
+
+ public int count() throws XQException {
+ checkNotClosed();
+ return value.getLength();
+ }
+
+ public boolean first() throws XQException {
+ checkNotClosed();
+ if (value.getLength() == 0) {
+ position = 0;
+ return false;
+ } else {
+ position = 1;
+ return true;
+ }
+ }
+
+ /*@Nullable*/ public XQItem getItem() throws XQException {
+ checkNotClosed();
+ SaxonXQItem item = new SaxonXQItem(value.itemAt(position - 1), factory);
+ item.setClosableContainer(this);
+ return item;
+ }
+
+ /*@Nullable*/ public Item getSaxonItem() throws XQException {
+ return value.itemAt(position - 1);
+ }
+
+ public int getPosition() throws XQException {
+ checkNotClosed();
+ if (position >= 0) {
+ return position;
+ } else {
+ return value.getLength() + 1;
+ }
+ }
+
+ /*@NotNull*/ public XMLStreamReader getSequenceAsStream() throws XQException {
+ checkNotClosed();
+ EventIterator ei = new EventIteratorOverSequence(iterateRemainder());
+ ei = new BracketedDocumentIterator(ei);
+ Configuration config = getConfiguration();
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ pipe.setHostLanguage(Configuration.XQUERY);
+ ei = new Decomposer(ei, pipe);
+ return new EventToStaxBridge(ei, pipe);
+ }
+
+ public String getSequenceAsString(Properties props) throws XQException {
+ checkNotClosed();
+ StringWriter sw = new StringWriter();
+ writeSequence(sw, props);
+ return sw.toString();
+ }
+
+ public boolean isAfterLast() throws XQException {
+ checkNotClosed();
+ return position < 0;
+ }
+
+ public boolean isBeforeFirst() throws XQException {
+ checkNotClosed();
+ return position == 0 && value.getLength() != 0;
+ }
+
+ public boolean isFirst() throws XQException {
+ checkNotClosed();
+ return position == 1;
+ }
+
+ public boolean isLast() throws XQException {
+ checkNotClosed();
+ return position == value.getLength();
+ }
+
+ public boolean isOnItem() throws XQException {
+ checkNotClosed();
+ return position >= 1;
+ }
+
+ public boolean isScrollable() throws XQException {
+ checkNotClosed();
+ return true;
+ }
+
+ public boolean last() throws XQException {
+ checkNotClosed();
+ int n = value.getLength();
+ if (n == 0) {
+ position = -1;
+ return false;
+ } else {
+ position = n;
+ return true;
+ }
+ }
+
+ public boolean next() throws XQException {
+ checkNotClosed();
+ if (position == value.getLength()) {
+ position = -1;
+ return false;
+ } else {
+ position++;
+ return true;
+ }
+ }
+
+ public boolean previous() throws XQException {
+ checkNotClosed();
+ if (position == -1) {
+ return last();
+ }
+ position--;
+ return (position != 0);
+ }
+
+ public boolean relative(int itempos) throws XQException {
+ checkNotClosed();
+ if (position == -1) {
+ position = value.getLength() + 1;
+ }
+ position += itempos;
+ if (position <= 0) {
+ position = 0;
+ return false;
+ }
+ if (position > value.getLength()) {
+ position = -1;
+ return false;
+ }
+ return true;
+ }
+
+ public void writeSequence(OutputStream os, /*@Nullable*/ Properties props) throws XQException {
+ checkNotClosed();
+ checkNotNull(os);
+ if (props == null) {
+ props = new Properties();
+ }
+ props = SaxonXQSequence.setDefaultProperties(props);
+ try {
+ QueryResult.serializeSequence(iterateRemainder(), getConfiguration(), os, props);
+ position = -1;
+ } catch (XPathException e) {
+ throw newXQException(e);
+ }
+ }
+
+ public void writeSequence(Writer ow, /*@Nullable*/ Properties props) throws XQException {
+ checkNotClosed();
+ checkNotNull(ow);
+ if (props == null) {
+ props = new Properties();
+ }
+ props = setDefaultProperties(props);
+ try {
+ PrintWriter pw;
+ if (ow instanceof PrintWriter) {
+ pw = (PrintWriter)ow;
+ } else {
+ pw = new PrintWriter(ow);
+ }
+ QueryResult.serializeSequence(iterateRemainder(), getConfiguration(), pw, props);
+ position = -1;
+ } catch (XPathException e) {
+ throw newXQException(e);
+ }
+ }
+
+ public void writeSequenceToResult(Result result) throws XQException {
+ checkNotClosed();
+ Properties props = SaxonXQSequence.setDefaultProperties(null);
+ try {
+ QueryResult.serializeSequence(iterateRemainder(), getConfiguration(), result, props);
+ } catch (XPathException e) {
+ throw newXQException(e);
+ }
+ }
+
+ public void writeSequenceToSAX(ContentHandler saxHandler) throws XQException {
+ checkNotClosed();
+ writeSequenceToResult(new SAXResult(saxHandler));
+ }
+
+ public String getAtomicValue() throws XQException {
+ return getCurrentItem().getAtomicValue();
+ }
+
+ public boolean getBoolean() throws XQException {
+ return getCurrentItem().getBoolean();
+ }
+
+ public byte getByte() throws XQException {
+ return getCurrentItem().getByte();
+ }
+
+ public double getDouble() throws XQException {
+ return getCurrentItem().getDouble();
+ }
+
+ public float getFloat() throws XQException {
+ return getCurrentItem().getFloat();
+ }
+
+ public int getInt() throws XQException {
+ return getCurrentItem().getInt();
+ }
+
+ /*@NotNull*/ public XMLStreamReader getItemAsStream() throws XQException {
+ return getCurrentItem().getItemAsStream();
+ }
+
+ public String getItemAsString(Properties props) throws XQException {
+ return getCurrentItem().getItemAsString(props);
+ }
+
+ /*@NotNull*/ public XQItemType getItemType() throws XQException {
+ return getCurrentItem().getItemType();
+ }
+
+ public long getLong() throws XQException {
+ return getCurrentItem().getLong();
+ }
+
+ public Node getNode() throws XQException {
+ return getCurrentItem().getNode();
+ }
+
+ /*@NotNull*/ public URI getNodeUri() throws XQException {
+ return getCurrentItem().getNodeUri();
+ }
+
+ public Object getObject() throws XQException {
+ return getCurrentItem().getObject();
+ }
+
+ public short getShort() throws XQException {
+ return getCurrentItem().getShort();
+ }
+
+ public boolean instanceOf(/*@NotNull*/ XQItemType type) throws XQException {
+ return getCurrentItem().instanceOf(type);
+ }
+
+ public void writeItem(OutputStream os, Properties props) throws XQException {
+ getCurrentItem().writeItem(os, props);
+ }
+
+ public void writeItem(Writer ow, Properties props) throws XQException {
+ getCurrentItem().writeItem(ow, props);
+ }
+
+ public void writeItemToResult(Result result) throws XQException {
+ getCurrentItem().writeItemToResult(result);
+ }
+
+ public void writeItemToSAX(ContentHandler saxHandler) throws XQException {
+ getCurrentItem().writeItemToSAX(saxHandler);
+ }
+
+ /*@Nullable*/ private SaxonXQItem getCurrentItem() throws XQException {
+ checkNotClosed();
+ if (position == 0) {
+ throw new XQException("Sequence is positioned before first item");
+ }
+ if (position < 0) {
+ throw new XQException("Sequence is positioned after last item");
+ }
+ SaxonXQItem item = new SaxonXQItem(value.itemAt(position-1), factory);
+ item.setClosableContainer(this);
+ return item;
+ }
+
+ public XQConnection getConnection() throws XQException {
+ checkNotClosed();
+ if (expression == null) {
+ throw new IllegalStateException("Connection not available");
+ }
+ return expression.getConnection();
+ }
+
+ /*@Nullable*/ private SequenceIterator iterateRemainder() throws XQException {
+ try {
+ if (position == 0) {
+ return value.iterate();
+ } else if (position < 0) {
+ return EmptyIterator.getInstance();
+ } else {
+ return TailIterator.make(value.iterate(), position);
+ }
+ } catch (XPathException e) {
+ throw newXQException(e);
+ }
+ }
+
+ private void checkNotNull(/*@Nullable*/ Object arg) throws XQException {
+ if (arg == null) {
+ throw new XQException("Argument is null");
+ }
+ }
+
+ /*@NotNull*/ private XQException newXQException(/*@NotNull*/ Exception err) {
+ XQException xqe = new XQException(err.getMessage());
+ xqe.initCause(err);
+ return xqe;
+ }
+
+ /*@Nullable*/ static Properties setDefaultProperties(/*@Nullable*/ Properties props) {
+ Properties newProps = (props == null ? new Properties() : new Properties(props));
+ boolean changed = false;
+ if (newProps.getProperty(OutputKeys.METHOD) == null) {
+ newProps.setProperty(OutputKeys.METHOD, "xml");
+ changed = true;
+ }
+ if (newProps.getProperty(OutputKeys.INDENT) == null) {
+ newProps.setProperty(OutputKeys.INDENT, "yes");
+ changed = true;
+ }
+ if (newProps.getProperty(OutputKeys.OMIT_XML_DECLARATION) == null) {
+ newProps.setProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
+ changed = true;
+ }
+ return (changed || props == null ? newProps : props);
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXQSequenceType.java b/sf/saxon/xqj/SaxonXQSequenceType.java
new file mode 100644
index 0000000..d732622
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQSequenceType.java
@@ -0,0 +1,71 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.expr.StaticProperty;
+import net.sf.saxon.value.SequenceType;
+
+import javax.xml.xquery.XQItemType;
+import javax.xml.xquery.XQSequenceType;
+
+/**
+ * Saxon implementation of the XQJ SequenceType interface
+ */
+
+
+public class SaxonXQSequenceType implements XQSequenceType {
+
+ SequenceType sequenceType;
+ Configuration config;
+
+ SaxonXQSequenceType(SequenceType sequenceType, Configuration config) {
+ this.sequenceType = sequenceType;
+ this.config = config;
+ }
+
+ public int getItemOccurrence() {
+ int cardinality = sequenceType.getCardinality();
+ switch (cardinality) {
+ case StaticProperty.EXACTLY_ONE:
+ return XQSequenceType.OCC_EXACTLY_ONE;
+ case StaticProperty.ALLOWS_ZERO_OR_ONE:
+ return XQSequenceType.OCC_ZERO_OR_ONE;
+ case StaticProperty.ALLOWS_ONE_OR_MORE:
+ return XQSequenceType.OCC_ONE_OR_MORE;
+ case StaticProperty.ALLOWS_ZERO_OR_MORE:
+ return XQSequenceType.OCC_ZERO_OR_MORE;
+ default:
+ return XQSequenceType.OCC_ZERO_OR_MORE;
+ }
+ }
+
+ /*@NotNull*/ public XQItemType getItemType() {
+ return new SaxonXQItemType(sequenceType.getPrimaryType(), config);
+ }
+
+ /*@Nullable*/ public String getString() {
+ String s = sequenceType.getPrimaryType().toString();
+ switch (sequenceType.getCardinality()) {
+ case StaticProperty.EXACTLY_ONE:
+ return s;
+ case StaticProperty.ALLOWS_ZERO_OR_ONE:
+ return s + "?";
+ case StaticProperty.ALLOWS_ONE_OR_MORE:
+ return s + "+";
+ case StaticProperty.ALLOWS_ZERO_OR_MORE:
+ return s + "*";
+ default:
+ return s;
+ }
+ }
+
+ /*@Nullable*/ public String toString() {
+ return getString();
+ }
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/SaxonXQStaticContext.java b/sf/saxon/xqj/SaxonXQStaticContext.java
new file mode 100644
index 0000000..3151617
--- /dev/null
+++ b/sf/saxon/xqj/SaxonXQStaticContext.java
@@ -0,0 +1,368 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.lib.NamespaceConstant;
+import net.sf.saxon.lib.Validation;
+import net.sf.saxon.query.StaticQueryContext;
+
+import javax.xml.xquery.XQConstants;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQItemType;
+import javax.xml.xquery.XQStaticContext;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Saxon implementation of the XQJ XQStaticContext interface
+ */
+public class SaxonXQStaticContext implements XQStaticContext {
+
+ private Configuration config;
+ private int bindingMode = XQConstants.BINDING_MODE_IMMEDIATE;
+ private int holdability = XQConstants.HOLDTYPE_HOLD_CURSORS_OVER_COMMIT;
+ private int scrollability = XQConstants.SCROLLTYPE_FORWARD_ONLY;
+ /*@NotNull*/ private Map<String, String> namespaces = new HashMap<String, String>();
+ private String baseURI = "";
+ boolean preserveBoundarySpace = false;
+ boolean constructionModeIsPreserve = false;
+ boolean inheritNamespaces = true;
+ boolean preserveNamespaces = true;
+ boolean emptyLeast = true;
+ boolean isOrdered = true;
+ /*@Nullable*/ SaxonXQItemType contextItemStaticType = null;
+ String defaultCollationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
+ String defaultElementNamespace = "";
+ String defaultFunctionNamespace = NamespaceConstant.FN;
+
+ /**
+ * Create a SaxonXQStaticContext object, the Saxon implementation of XQStaticContext in XQJ
+ * @param config the Saxon configuration
+ */
+
+ public SaxonXQStaticContext(Configuration config) {
+ this.config = config;
+ }
+
+ /**
+ * Create a SaxonXQStaticContext object as a copy of another SaxonXQStaticContext object
+ * @param sc the static context to be copied
+ */
+
+ public SaxonXQStaticContext(/*@NotNull*/ SaxonXQStaticContext sc) {
+ this.config = sc.config;
+ this.bindingMode = sc.bindingMode;
+ this.holdability = sc.holdability;
+ this.scrollability = sc.scrollability;
+ this.namespaces = new HashMap<String, String>(sc.namespaces);
+ this.baseURI = sc.baseURI;
+ this.preserveBoundarySpace = sc.preserveBoundarySpace;
+ this.constructionModeIsPreserve = sc.constructionModeIsPreserve;
+ this.inheritNamespaces = sc.inheritNamespaces;
+ this.preserveNamespaces = sc.preserveNamespaces;
+ this.emptyLeast = sc.emptyLeast;
+ this.isOrdered = sc.isOrdered;
+ this.contextItemStaticType = sc.contextItemStaticType;
+ this.defaultCollationName = sc.defaultCollationName;
+ this.defaultElementNamespace = sc.defaultElementNamespace;
+ this.defaultFunctionNamespace = sc.defaultFunctionNamespace;
+ }
+
+ /**
+ * Get a new Saxon StaticQueryContext object holding the information held in this
+ * XQStaticContext
+ * @return a newly constructed StaticQueryContext object
+ */
+
+ /*@NotNull*/ protected StaticQueryContext getSaxonStaticQueryContext() {
+ StaticQueryContext sqc = config.newStaticQueryContext();
+ sqc.setSchemaAware(config.isLicensedFeature(Configuration.LicenseFeature.SCHEMA_VALIDATION));
+ sqc.setBaseURI(baseURI);
+ sqc.setConstructionMode(constructionModeIsPreserve ? Validation.PRESERVE : Validation.STRIP);
+ sqc.setDefaultElementNamespace(defaultElementNamespace);
+ sqc.setDefaultFunctionNamespace(defaultFunctionNamespace);
+ sqc.setEmptyLeast(emptyLeast);
+ sqc.setInheritNamespaces(inheritNamespaces);
+ sqc.setPreserveBoundarySpace(preserveBoundarySpace);
+ sqc.setPreserveNamespaces(preserveNamespaces);
+ if (contextItemStaticType != null) {
+ sqc.setRequiredContextItemType(contextItemStaticType.getSaxonItemType());
+ }
+ for (Map.Entry<String, String> e : namespaces.entrySet()) {
+ sqc.declareNamespace(e.getKey(), e.getValue());
+ }
+ return sqc;
+ }
+
+
+ public void declareNamespace(String prefix, String uri) throws XQException {
+ checkNotNull(prefix);
+ checkNotNull(uri);
+ if (uri.length() == 0) {
+ namespaces.remove(prefix);
+ } else {
+ namespaces.put(prefix, uri);
+ }
+ }
+
+ public String getBaseURI() {
+ return baseURI;
+ }
+
+
+ public int getBindingMode() {
+ return bindingMode;
+ }
+
+ public int getBoundarySpacePolicy() {
+ return preserveBoundarySpace
+ ? XQConstants.BOUNDARY_SPACE_PRESERVE
+ : XQConstants.BOUNDARY_SPACE_STRIP;
+ }
+
+ public int getConstructionMode() {
+ return constructionModeIsPreserve
+ ? XQConstants.CONSTRUCTION_MODE_PRESERVE
+ : XQConstants.CONSTRUCTION_MODE_STRIP;
+ }
+
+
+ /*@Nullable*/ public XQItemType getContextItemStaticType() {
+ return contextItemStaticType;
+ }
+
+ public int getCopyNamespacesModeInherit() {
+ return inheritNamespaces ?
+ XQConstants.COPY_NAMESPACES_MODE_INHERIT :
+ XQConstants.COPY_NAMESPACES_MODE_NO_INHERIT;
+ }
+
+ public int getCopyNamespacesModePreserve() {
+ return preserveNamespaces ?
+ XQConstants.COPY_NAMESPACES_MODE_PRESERVE :
+ XQConstants.COPY_NAMESPACES_MODE_NO_PRESERVE;
+ }
+
+ public String getDefaultCollation() {
+ return defaultCollationName;
+ }
+
+ public String getDefaultElementTypeNamespace() {
+ return defaultElementNamespace;
+ }
+
+ public String getDefaultFunctionNamespace() {
+ return defaultFunctionNamespace;
+ }
+
+ public int getDefaultOrderForEmptySequences() {
+ return emptyLeast ?
+ XQConstants.DEFAULT_ORDER_FOR_EMPTY_SEQUENCES_LEAST :
+ XQConstants.DEFAULT_ORDER_FOR_EMPTY_SEQUENCES_GREATEST;
+ }
+
+ /*@NotNull*/ public String[] getNamespacePrefixes() {
+ String[] result = new String[namespaces.size()];
+ Iterator iter = namespaces.keySet().iterator();
+ for (int i=0; i<result.length; i++) {
+ iter.hasNext();
+ result[i] = (String)iter.next();
+ }
+ return result;
+ }
+
+ public String getNamespaceURI(String prefix) throws XQException {
+ checkNotNull(prefix);
+ String uri = namespaces.get(prefix);
+ if (uri == null) {
+ throw new XQException("Unknown prefix");
+ }
+ return uri;
+ }
+
+ public int getOrderingMode() {
+ return isOrdered
+ ? XQConstants.ORDERING_MODE_ORDERED
+ : XQConstants.ORDERING_MODE_UNORDERED;
+ }
+
+ public int getHoldability() {
+ return holdability;
+ }
+
+ public int getQueryLanguageTypeAndVersion() {
+ return XQConstants.LANGTYPE_XQUERY;
+ }
+
+ public int getQueryTimeout() {
+ return 0;
+ }
+
+ public int getScrollability() {
+ return scrollability;
+ }
+
+
+ public void setBaseURI(String baseUri) throws XQException {
+ checkNotNull(baseUri);
+ this.baseURI = baseUri;
+ }
+
+ public void setBindingMode(int bindingMode) throws XQException {
+ switch (bindingMode) {
+ case XQConstants.BINDING_MODE_IMMEDIATE:
+ case XQConstants.BINDING_MODE_DEFERRED:
+ this.bindingMode = bindingMode;
+ break;
+ default:
+ throw new XQException("Invalid value for binding mode - " + bindingMode);
+ }
+ }
+
+ public void setBoundarySpacePolicy(int policy) throws XQException {
+ switch (policy) {
+ case XQConstants.BOUNDARY_SPACE_PRESERVE:
+ preserveBoundarySpace = true;
+ break;
+ case XQConstants.BOUNDARY_SPACE_STRIP:
+ preserveBoundarySpace = false;
+ break;
+ default:
+ throw new XQException("Invalid value for boundary space policy - " + policy);
+ }
+ }
+
+ public void setConstructionMode(int mode) throws XQException {
+ switch (mode) {
+ case XQConstants.CONSTRUCTION_MODE_PRESERVE:
+ constructionModeIsPreserve = true;
+ break;
+ case XQConstants.CONSTRUCTION_MODE_STRIP:
+ constructionModeIsPreserve = false;
+ break;
+ default:
+ throw new XQException("Invalid value for construction mode - " + mode);
+ }
+ }
+
+ public void setContextItemStaticType(/*@Nullable*/ XQItemType contextItemType) {
+ this.contextItemStaticType = (SaxonXQItemType)contextItemType;
+ }
+
+ public void setCopyNamespacesModeInherit(int mode) throws XQException {
+ switch (mode) {
+ case XQConstants.COPY_NAMESPACES_MODE_INHERIT:
+ inheritNamespaces = true;
+ break;
+ case XQConstants.COPY_NAMESPACES_MODE_NO_INHERIT:
+ inheritNamespaces = false;
+ break;
+ default:
+ throw new XQException("Invalid value for namespaces inherit mode - " + mode);
+ }
+ }
+
+ public void setCopyNamespacesModePreserve(int mode) throws XQException {
+ switch (mode) {
+ case XQConstants.COPY_NAMESPACES_MODE_PRESERVE:
+ preserveNamespaces = true;
+ break;
+ case XQConstants.COPY_NAMESPACES_MODE_NO_PRESERVE:
+ preserveNamespaces = false;
+ break;
+ default:
+ throw new XQException("Invalid value for namespaces preserve mode - " + mode);
+ }
+ }
+
+ public void setDefaultCollation(String uri) throws XQException {
+ checkNotNull(uri);
+ defaultCollationName = uri;
+ }
+
+ public void setDefaultElementTypeNamespace(String uri) throws XQException {
+ checkNotNull(uri);
+ defaultElementNamespace = uri;
+ }
+
+ public void setDefaultFunctionNamespace(String uri) throws XQException {
+ checkNotNull(uri);
+ defaultFunctionNamespace = uri;
+ }
+
+ public void setDefaultOrderForEmptySequences(int order) throws XQException {
+ switch (order) {
+ case XQConstants.DEFAULT_ORDER_FOR_EMPTY_SEQUENCES_GREATEST:
+ emptyLeast = false;
+ break;
+ case XQConstants.DEFAULT_ORDER_FOR_EMPTY_SEQUENCES_LEAST:
+ emptyLeast = true;
+ break;
+ default:
+ throw new XQException("Invalid value for default order for empty sequences - " + order);
+ }
+ }
+
+ public void setOrderingMode(int mode) throws XQException {
+ switch (mode) {
+ case XQConstants.ORDERING_MODE_ORDERED:
+ isOrdered = true;
+ break;
+ case XQConstants.ORDERING_MODE_UNORDERED:
+ isOrdered = false;
+ break;
+ default:
+ throw new XQException("Invalid ordering mode - " + mode);
+ }
+ }
+
+ public void setQueryTimeout(int seconds) throws XQException {
+ if (seconds < 0) {
+ throw new XQException("Query timeout must not be negative");
+ }
+ // no-op
+ }
+
+ public void setHoldability(int holdability) throws XQException {
+ switch (holdability) {
+ case XQConstants.HOLDTYPE_HOLD_CURSORS_OVER_COMMIT:
+ case XQConstants.HOLDTYPE_CLOSE_CURSORS_AT_COMMIT:
+ this.holdability = holdability;
+ break;
+ default:
+ throw new XQException("Invalid holdability value - " + holdability);
+ }
+ }
+
+ public void setQueryLanguageTypeAndVersion(int langtype) throws XQException {
+ if (langtype != XQConstants.LANGTYPE_XQUERY) {
+ throw new XQException("XQueryX is not supported");
+ }
+ }
+
+ public void setScrollability(int scrollability) throws XQException {
+ switch (scrollability) {
+ case XQConstants.SCROLLTYPE_FORWARD_ONLY:
+ case XQConstants.SCROLLTYPE_SCROLLABLE:
+ this.scrollability = scrollability;
+ break;
+ default:
+ throw new XQException("Invalid scrollability value - " + scrollability);
+ }
+ }
+
+ protected void checkNotNull(/*@Nullable*/ Object arg) throws XQException {
+ if (arg == null) {
+ throw new XQException("Argument is null");
+ }
+ }
+
+}
+
diff --git a/sf/saxon/xqj/StandardObjectConverter.java b/sf/saxon/xqj/StandardObjectConverter.java
new file mode 100644
index 0000000..29912b6
--- /dev/null
+++ b/sf/saxon/xqj/StandardObjectConverter.java
@@ -0,0 +1,284 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.xqj;
+
+import net.sf.saxon.Configuration;
+import net.sf.saxon.dom.DOMObjectModel;
+import net.sf.saxon.dom.NodeOverNodeInfo;
+import net.sf.saxon.event.Builder;
+import net.sf.saxon.event.PipelineConfiguration;
+import net.sf.saxon.event.Sender;
+import net.sf.saxon.evpull.PullEventSource;
+import net.sf.saxon.evpull.StaxToEventBridge;
+import net.sf.saxon.expr.EarlyEvaluationContext;
+import net.sf.saxon.expr.JPConverter;
+import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NodeInfo;
+import net.sf.saxon.om.SequenceTool;
+import net.sf.saxon.om.StandardNames;
+import net.sf.saxon.trans.XPathException;
+import net.sf.saxon.tree.tiny.TinyBuilder;
+import net.sf.saxon.type.AtomicType;
+import net.sf.saxon.type.BuiltInAtomicType;
+import net.sf.saxon.type.ExternalObjectType;
+import net.sf.saxon.value.*;
+import net.sf.saxon.value.StringValue;
+import org.w3c.dom.Node;
+
+import javax.xml.datatype.DatatypeConstants;
+import javax.xml.datatype.Duration;
+import javax.xml.datatype.XMLGregorianCalendar;
+import javax.xml.namespace.QName;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.Source;
+import javax.xml.xquery.XQException;
+import javax.xml.xquery.XQItemAccessor;
+import javax.xml.xquery.XQItemType;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * This class provides all the conversion methods used to convert data between XDM values
+ * and Java values in the XQJ API. At one time the XQJ specification defined such a class,
+ * and it has been retained in the Saxon implementation.
+ * <p>
+ * This handler implements the mappings defined in the XQJ specification. In addition,
+ * it defines the following mappings, which are applied after those defined in XQJ:</p>
+ *
+ * <p>For fromObject:</p>
+ * <ul>
+ * <li>If the supplied object is an instance of javax.xml.transform.Source, a document
+ * node is constructed from the source and the resulting node is returned as the Item</li>
+ * <li>If the supplied object is an instance of javax.xml.stream.XMLStreamReader, a document
+ * node is constructed from the XMLStreamReader and the resulting node is returned as the Item</li>
+ * <li>If the supplied object is
+ * </ul>
+ */
+public class StandardObjectConverter implements ObjectConverter {
+
+ Configuration config;
+
+ /**
+ * CreateCharacter an instance of the class
+ * @param factory the factory object
+ */
+
+ public StandardObjectConverter(/*@NotNull*/ SaxonXQDataFactory factory) {
+ config = factory.getConfiguration();
+ }
+
+ //@SuppressWarnings({"ConstantConditions"})
+ /*@Nullable*/ public Object toObject(/*@NotNull*/ XQItemAccessor xqItemAccessor) throws XQException {
+ Item item = ((SaxonXQItemAccessor)xqItemAccessor).getSaxonItem();
+ if (item instanceof AtomicValue) {
+ AtomicValue p = ((AtomicValue)item);
+ int t = p.getItemType().getPrimitiveType();
+ switch (t) {
+ case StandardNames.XS_ANY_URI:
+ return p.getStringValue();
+ case StandardNames.XS_BASE64_BINARY:
+ return ((Base64BinaryValue)p).getBinaryValue();
+ case StandardNames.XS_BOOLEAN:
+ return Boolean.valueOf(((BooleanValue)p).getBooleanValue());
+ case StandardNames.XS_DATE:
+ return new SaxonXMLGregorianCalendar((CalendarValue)p);
+ case StandardNames.XS_DATE_TIME:
+ return new SaxonXMLGregorianCalendar((CalendarValue)p);
+ case StandardNames.XS_DECIMAL:
+ return ((DecimalValue)p).getDecimalValue();
+ case StandardNames.XS_DOUBLE:
+ return new Double(((DoubleValue)p).getDoubleValue());
+ case StandardNames.XS_DURATION:
+ return new SaxonDuration((DurationValue)p);
+ case StandardNames.XS_FLOAT:
+ return new Float(((FloatValue)p).getFloatValue());
+ case StandardNames.XS_G_DAY:
+ case StandardNames.XS_G_MONTH:
+ case StandardNames.XS_G_MONTH_DAY:
+ case StandardNames.XS_G_YEAR:
+ case StandardNames.XS_G_YEAR_MONTH:
+ return new SaxonXMLGregorianCalendar((CalendarValue)p);
+ case StandardNames.XS_HEX_BINARY:
+ return ((HexBinaryValue)p).getBinaryValue();
+ case StandardNames.XS_INTEGER:
+ if (p instanceof BigIntegerValue) {
+ return ((BigIntegerValue)p).asBigInteger();
+ } else {
+ int sub = ((AtomicType)p.getItemType()).getFingerprint();
+ switch (sub) {
+ case StandardNames.XS_INTEGER:
+ case StandardNames.XS_NEGATIVE_INTEGER:
+ case StandardNames.XS_NON_NEGATIVE_INTEGER:
+ case StandardNames.XS_NON_POSITIVE_INTEGER:
+ case StandardNames.XS_POSITIVE_INTEGER:
+ case StandardNames.XS_UNSIGNED_LONG:
+ return BigInteger.valueOf(((Int64Value)p).longValue());
+ case StandardNames.XS_BYTE:
+ return Byte.valueOf((byte)((Int64Value)p).longValue());
+ case StandardNames.XS_INT:
+ case StandardNames.XS_UNSIGNED_SHORT:
+ return Integer.valueOf((int)((Int64Value)p).longValue());
+ case StandardNames.XS_LONG:
+ case StandardNames.XS_UNSIGNED_INT:
+ return Long.valueOf(((Int64Value)p).longValue());
+ case StandardNames.XS_SHORT:
+ case StandardNames.XS_UNSIGNED_BYTE:
+ return Short.valueOf((short)((Int64Value)p).longValue());
+ default:
+ throw new XQException("Unrecognized integer subtype " + sub);
+ }
+ }
+ case StandardNames.XS_QNAME:
+ return ((QualifiedNameValue)p).toJaxpQName();
+ case StandardNames.XS_STRING:
+ case StandardNames.XS_UNTYPED_ATOMIC:
+ return p.getStringValue();
+ case StandardNames.XS_TIME:
+ return new SaxonXMLGregorianCalendar((CalendarValue)p);
+ case StandardNames.XS_DAY_TIME_DURATION:
+ return new SaxonDuration((DurationValue)p);
+ case StandardNames.XS_YEAR_MONTH_DURATION:
+ return new SaxonDuration((DurationValue)p);
+ default:
+ throw new XQException("unsupported type");
+ }
+ } else {
+ return NodeOverNodeInfo.wrap((NodeInfo)item);
+ }
+ }
+
+ /**
+ * Convert a Java object to a Saxon Item
+ * @param value the Java object. If null is supplied, null is returned.
+ * @return the corresponding Item
+ * @throws XQException
+ */
+
+ /*@Nullable*/ public Item convertToItem(/*@NotNull*/ Object value) throws XQException {
+ if (value == null) {
+ return null;
+ }
+ try {
+ if (value instanceof Boolean) {
+ return BooleanValue.get(((Boolean)value).booleanValue());
+ } else if (value instanceof byte[]) {
+ return new HexBinaryValue((byte[])value);
+ } else if (value instanceof Byte) {
+ return new Int64Value(((Byte)value).byteValue(), BuiltInAtomicType.BYTE, false);
+ } else if (value instanceof Float) {
+ return new FloatValue(((Float)value).floatValue());
+ } else if (value instanceof Double) {
+ return new DoubleValue(((Double)value).doubleValue());
+ } else if (value instanceof Integer) {
+ return new Int64Value(((Integer)value).intValue(), BuiltInAtomicType.INT, false);
+ } else if (value instanceof Long) {
+ return new Int64Value(((Long)value).longValue(), BuiltInAtomicType.LONG, false);
+ } else if (value instanceof Short) {
+ return new Int64Value(((Short)value).shortValue(), BuiltInAtomicType.SHORT, false);
+ } else if (value instanceof String) {
+ return new StringValue((String)value);
+ } else if (value instanceof BigDecimal) {
+ return new DecimalValue((BigDecimal)value);
+ } else if (value instanceof BigInteger) {
+ return new BigIntegerValue((BigInteger)value);
+ } else if (value instanceof SaxonDuration) {
+ return ((SaxonDuration)value).getDurationValue();
+ } else if (value instanceof Duration) {
+ // this is simpler and safer (but perhaps slower) than extracting all the components
+ return DurationValue.makeDuration(value.toString()).asAtomic();
+ } else if (value instanceof SaxonXMLGregorianCalendar) {
+ return ((SaxonXMLGregorianCalendar)value).toCalendarValue();
+ } else if (value instanceof XMLGregorianCalendar) {
+ XMLGregorianCalendar g = (XMLGregorianCalendar)value;
+ QName gtype = g.getXMLSchemaType();
+ if (gtype.equals(DatatypeConstants.DATETIME)) {
+ return DateTimeValue.makeDateTimeValue(value.toString(), config.getConversionRules()).asAtomic();
+ } else if (gtype.equals(DatatypeConstants.DATE)) {
+ return DateValue.makeDateValue(value.toString(), config.getConversionRules()).asAtomic();
+ } else if (gtype.equals(DatatypeConstants.TIME)) {
+ return TimeValue.makeTimeValue(value.toString()).asAtomic();
+ } else if (gtype.equals(DatatypeConstants.GYEAR)) {
+ return GYearValue.makeGYearValue(value.toString(), config.getConversionRules()).asAtomic();
+ } else if (gtype.equals(DatatypeConstants.GYEARMONTH)) {
+ return GYearMonthValue.makeGYearMonthValue(value.toString(), config.getConversionRules()).asAtomic();
+ } else if (gtype.equals(DatatypeConstants.GMONTH)) {
+ return GMonthValue.makeGMonthValue(value.toString(), config.getConversionRules()).asAtomic();
+ } else if (gtype.equals(DatatypeConstants.GMONTHDAY)) {
+ return GMonthDayValue.makeGMonthDayValue(value.toString(), config.getConversionRules()).asAtomic();
+ } else if (gtype.equals(DatatypeConstants.GDAY)) {
+ return GDayValue.makeGDayValue(value.toString(), config.getConversionRules()).asAtomic();
+ } else {
+ throw new AssertionError("Unknown Gregorian date type");
+ }
+ } else if (value instanceof QName) {
+ QName q = (QName)value;
+ return new QNameValue(q.getPrefix(), q.getNamespaceURI(), q.getLocalPart(),
+ BuiltInAtomicType.QNAME, null);
+ } else if (value instanceof Node) {
+ JPConverter jp = DOMObjectModel.getInstance().getJPConverter(Node.class, config);
+ return SequenceTool.asItem(jp.convert(value, new EarlyEvaluationContext(config, null)));
+ //return Value.asItem(DOMObjectModel.getInstance().convertObjectToXPathValue(value, config));
+ } else if (value instanceof Source) {
+ // Saxon extension to the XQJ specification
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ Builder b = new TinyBuilder(pipe);
+ Sender.send((Source)value, b, null);
+ NodeInfo node = b.getCurrentRoot();
+ b.reset();
+ return node;
+ } else if (value instanceof XMLStreamReader) {
+ // Saxon extension to the XQJ specification
+ StaxToEventBridge bridge = new StaxToEventBridge();
+ bridge.setXMLStreamReader((XMLStreamReader)value);
+ PipelineConfiguration pipe = config.makePipelineConfiguration();
+ bridge.setPipelineConfiguration(pipe);
+ Builder b = new TinyBuilder(pipe);
+ Sender.send(new PullEventSource(bridge), b, null);
+ NodeInfo node = b.getCurrentRoot();
+ b.reset();
+ return node;
+ } else {
+ throw new XPathException("Java object cannot be converted to an XQuery value");
+ }
+ } catch (XPathException e) {
+ XQException xqe = new XQException(e.getMessage());
+ xqe.initCause(e);
+ throw xqe;
+ }
+ }
+
+ /**
+ * Convert a Java object to an Item, when a required type has been specified. Note that Saxon only calls
+ * this method when none of the standard conversions defined in the XQJ specification is able to handle
+ * the object.
+ * @param value the supplied Java object. If null is supplied, null is returned.
+ * @param type the required XPath data type
+ * @return the Item that results from the conversion
+ * @throws XQException if the Java object cannot be converted to an XQItem
+ */
+
+ /*@Nullable*/ public Item convertToItem(Object value, /*@NotNull*/ XQItemType type) throws XQException {
+ if (value == null) {
+ return null;
+ }
+ if (((SaxonXQItemType)type).getSaxonItemType() instanceof ExternalObjectType) {
+ Item result = new ObjectValue(value);
+ if (((SaxonXQItemType)type).getSaxonItemType().matches(result, null)) {
+ return result;
+ } else {
+ throw new XQException("The result of wrapping an object of class " + value.getClass().getName() +
+ " does not match the required type " + type.toString());
+ }
+ } else {
+ throw new XQException("Supplied Java object cannot be converted to an XQItem");
+ }
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/sf/saxon/xqj/package.html b/sf/saxon/xqj/package.html
new file mode 100644
index 0000000..8a638c6
--- /dev/null
+++ b/sf/saxon/xqj/package.html
@@ -0,0 +1,38 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+<head>
+<title>Package overview: net.sf.saxon.xqj</title>
+
+</head>
+ <body>
+ <p>This package contains an implementation of the XQJ specification
+ defined in JSR 225. This API is not yet part of the Java Standard Edition platform, but it
+ is widely supported by a variety of Java-based XQuery engines.</p>
+
+ <p>Saxon's implementation of the {@link javax.xml.xquery.XQDataSource} class is
+ {@link net.sf.saxon.xqj.SaxonXQDataSource}. Generally, this is the only class
+ that applications need to access directly (all other access can be done by using
+ standard XQJ interfaces). However, if there is a need to mix XQuery access with other
+ Saxon functionality such as schema processing, then it is often useful to cast the
+ XQJ objects to their underlying Saxon implementation classes, which make additional
+ methods available.</p>
+
+ <p>In general it is not possible to mix Saxon implementation classes of the XQJ interface
+ with implementation classes from other vendors. That is, if an interface specifies that
+ a value of an XQJ type must be passed as an argument, Saxon in general requires the value
+ to belong to a Saxon implementation of that interface.</p>
+
+ <p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+30 July 2010</i></p>
+
+ </body>
+</html>
+
+
diff --git a/sf/saxon/z/AbstractIntSet.java b/sf/saxon/z/AbstractIntSet.java
new file mode 100644
index 0000000..176e19f
--- /dev/null
+++ b/sf/saxon/z/AbstractIntSet.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * Abstract superclass containing helper methods for various implementations of IntSet
+ */
+public abstract class AbstractIntSet implements IntSet {
+
+ /**
+ * Test if this set is a superset of another set
+ * @param other the other set
+ * @return true if every item in the other set is also in this set
+ */
+
+ public boolean containsAll(IntSet other) {
+ if (other == IntUniversalSet.getInstance() || (other instanceof IntComplementSet)) {
+ return false;
+ }
+ IntIterator it = other.iterator();
+ while (it.hasNext()) {
+ if (!contains(it.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Form a new set that is the union of two IntSets.
+ * @param other the second set
+ * @return the union of the two sets
+ */
+
+ public IntSet union(IntSet other) {
+ if (other == IntUniversalSet.getInstance()) {
+ return other;
+ }
+ if (this.isEmpty()) {
+ return other.copy();
+ }
+ if (other.isEmpty()) {
+ return this.copy();
+ }
+ if (other instanceof IntComplementSet) {
+ return other.union(this);
+ }
+ if (other instanceof IntCheckingSet) {
+ return other.union(this);
+ }
+ IntHashSet n = new IntHashSet(this.size() + other.size());
+ IntIterator it = iterator();
+ while (it.hasNext()) {
+ n.add(it.next());
+ }
+ it = other.iterator();
+ while (it.hasNext()) {
+ n.add(it.next());
+ }
+ return n;
+ }
+
+ /**
+ * Form a new set that is the intersection of two IntSets.
+ * @param other the second set
+ * @return the intersection of the two sets
+ */
+
+ public IntSet intersect(IntSet other) {
+ if (this.isEmpty() || other.isEmpty()) {
+ return IntEmptySet.getInstance();
+ }
+ IntHashSet n = new IntHashSet(size());
+ IntIterator it = iterator();
+ while (it.hasNext()) {
+ int v = it.next();
+ if (other.contains(v)) {
+ n.add(v);
+ }
+ }
+ return n;
+ }
+
+ /**
+ * Form a new set that is the difference of this set and another set.
+ * The result will either be an immutable object, or a newly constructed object.
+ * @param other the second set
+ * @return the intersection of the two sets
+ */
+
+
+ public IntSet except(IntSet other) {
+ IntHashSet n = new IntHashSet(size());
+ IntIterator it = iterator();
+ while (it.hasNext()) {
+ int v = it.next();
+ if (!other.contains(v)) {
+ n.add(v);
+ }
+ }
+ return n;
+ }
+}
+
diff --git a/sf/saxon/z/IntArraySet.java b/sf/saxon/z/IntArraySet.java
new file mode 100644
index 0000000..68c4897
--- /dev/null
+++ b/sf/saxon/z/IntArraySet.java
@@ -0,0 +1,355 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Set of int values. This class is modelled on the java.net.Set interface, but it does
+ * not implement this interface, because the set members are int's rather than Objects.
+ * <p/>
+ * This implementation of a set of integers is optimized to use very little storage
+ * and to provide fast comparison of two sets. The equals() method determines whether
+ * two sets contain the same integers.
+ * <p/>
+ * This implementation is not efficient at adding new integers to the set. It creates a new
+ * array each time you do that.
+ * <p/>
+ * Not thread safe.
+ *
+ * @author Michael Kay
+ */
+public class IntArraySet extends AbstractIntSet implements Serializable, IntSet {
+
+ public static final int[] EMPTY_INT_ARRAY = new int[0];
+
+ /**
+ * The array of integers, which will always be sorted
+ */
+
+ private int[] contents;
+
+ /**
+ * Hashcode, evaluated lazily
+ */
+
+ private int hashCode = -1;
+
+ /**
+ * Create an empty set
+ */
+ public IntArraySet() {
+ contents = EMPTY_INT_ARRAY;
+ }
+
+ /**
+ * Create a set containing integers from the specified IntHashSet
+ * @param input the set to be copied
+ */
+
+ public IntArraySet(IntHashSet input) {
+ // exploits the fact that getValues() constructs a new array
+ contents = input.getValues();
+ //System.err.println("new IntArraySet(" + contents.length + ")");
+ Arrays.sort(contents);
+ }
+
+ /**
+ * Create one IntArraySet as a copy of another
+ * @param input the set to be copied
+ */
+
+ public IntArraySet(IntArraySet input) {
+ contents = new int[input.contents.length];
+ System.arraycopy(input.contents, 0, contents, 0, contents.length);
+ }
+
+ public IntSet copy() {
+ IntArraySet i2 = new IntArraySet();
+ i2.contents = new int[contents.length];
+ System.arraycopy(contents, 0, i2.contents, 0, contents.length);
+ //i2.contents = Arrays.copyOf(contents, contents.length);
+ return i2;
+ }
+
+ public IntSet mutableCopy() {
+ return copy();
+ }
+
+ public void clear() {
+ contents = EMPTY_INT_ARRAY;
+ hashCode = -1;
+ }
+
+ public int size() {
+ return contents.length;
+ }
+
+ public boolean isEmpty() {
+ return contents.length == 0;
+ }
+
+ /**
+ * Get the set of integer values as an array
+ * @return a sorted array of integers
+ */
+
+ public int[] getValues() {
+ return contents;
+ }
+
+
+ public boolean contains(int value) {
+ return Arrays.binarySearch(contents, value) >= 0;
+ }
+
+ public boolean remove(int value) {
+ hashCode = -1;
+ int pos = Arrays.binarySearch(contents, value);
+ if (pos < 0) {
+ return false;
+ }
+ int[] newArray = new int[contents.length - 1];
+ if (pos > 0) {
+ // copy the items before the one that's being removed
+ System.arraycopy(contents, 0, newArray, 0, pos);
+ }
+ if (pos < newArray.length) {
+ // copy the items after the one that's being removed
+ System.arraycopy(contents, pos+1, newArray, pos, contents.length - pos);
+ }
+ contents = newArray;
+ return true;
+ }
+
+ /**
+ * Add an integer to the set
+ * @param value the integer to be added
+ * @return true if the integer was added, false if it was already present
+ */
+
+ public boolean add(int value) {
+ hashCode = -1;
+ if (contents.length == 0) {
+ contents = new int[] {value};
+ return true;
+ }
+ int pos = Arrays.binarySearch(contents, value);
+ if (pos >= 0) {
+ return false; // value was already present
+ }
+ pos = -pos - 1; // new insertion point
+ int[] newArray = new int[contents.length + 1];
+ if (pos > 0) {
+ // copy the items before the insertion point
+ System.arraycopy(contents, 0, newArray, 0, pos);
+ }
+ newArray[pos] = value;
+ if (pos < contents.length) {
+ // copy the items after the insertion point
+ System.arraycopy(contents, pos, newArray, pos+1, newArray.length - pos);
+ }
+ contents = newArray;
+ return true;
+ }
+
+ /**
+ * Get the first value in the set.
+ * @return the first value in the set, in sorted order
+ * @throws ArrayIndexOutOfBoundsException if the set is empty
+ */
+
+ public int getFirst() {
+ return contents[0];
+ }
+
+ /**
+ * Get an iterator over the values
+ * @return an iterator over the values, which will be delivered in sorted order
+ */
+
+ public IntIterator iterator() {
+ return new IntArraySetIterator();
+ }
+
+ /**
+ * Form a new set that is the union of this set with another set.
+ * @param other the other set
+ * @return the union of the two sets
+ */
+
+ public IntSet union(IntSet other) {
+ // Look for special cases: one set empty, or both sets equal
+ if (size() == 0) {
+ return other.copy();
+ } else if (other.isEmpty()) {
+ return copy();
+ } else if (other == IntUniversalSet.getInstance()) {
+ return other;
+ } else if (other instanceof IntComplementSet) {
+ return other.union(this);
+ }
+ if (equals(other)) {
+ return copy();
+ }
+ if (other instanceof IntArraySet) {
+ // Form the union by a merge of the two sorted arrays
+ int[] merged = new int[size() + other.size()];
+ int[] a = contents;
+ int[] b = ((IntArraySet)other).contents;
+ int m = a.length, n = b.length;
+ int o=0, i=0, j=0;
+ while (true) {
+ if (a[i] < b[j]) {
+ merged[o++] = a[i++];
+ } else if (b[j] < a[i]) {
+ merged[o++] = b[j++];
+ } else {
+ merged[o++] = a[i++];
+ j++;
+ }
+ if (i == m) {
+ System.arraycopy(b, j, merged, o, n-j);
+ o += (n-j);
+ return make(merged, o);
+ } else if (j == n) {
+ System.arraycopy(a, i, merged, o, m-i);
+ o += (m-i);
+ return make(merged, o);
+ }
+ }
+ } else {
+ return super.union(other);
+ }
+ }
+
+ /**
+ * Factory method to construct a set from an array of integers
+ * @param in the array of integers, which must be in ascending order
+ * @param size the number of elements in the array that are significant
+ * @return the constructed set
+ */
+
+ public static IntArraySet make(int[] in, int size) {
+ int[] out;
+ if (in.length == size) {
+ out = in;
+ } else {
+ out = new int[size];
+ System.arraycopy(in, 0, out, 0, size);
+ }
+ return new IntArraySet(out);
+ }
+
+ private IntArraySet(int[] content) {
+ contents = content;
+ }
+
+ public String toString() {
+ FastStringBuffer sb = new FastStringBuffer(contents.length*4);
+ for (int i=0; i<contents.length; i++) {
+ if (i == contents.length - 1) {
+ sb.append(contents[i] + "");
+ } else if (contents[i]+1 != contents[i+1]) {
+ sb.append(contents[i] + ",");
+ } else {
+ int j = i+1;
+ while (contents[j] == contents[j-1]+1) {
+ j++;
+ if (j == contents.length) {
+ break;
+ }
+ }
+ sb.append(contents[i] + "-" + contents[j-1] + ",");
+ i = j;
+ }
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Test if this set has overlapping membership with another set
+ */
+
+// public boolean containsSome(IntArraySet other) {
+// IntIterator it = other.iterator();
+// while (it.hasNext()) {
+// if (contains(it.next())) {
+// return true;
+// }
+// }
+// return false;
+// }
+
+ /**
+ * Test whether this set has exactly the same members as another set
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof IntArraySet) {
+ IntArraySet s = (IntArraySet)other;
+ return hashCode() == other.hashCode() && Arrays.equals(contents, s.contents);
+ } else
+ return other instanceof IntSet &&
+ contents.length == ((IntSet)other).size() &&
+ containsAll((IntSet)other);
+ }
+
+ /**
+ * Construct a hash key that supports the equals() test
+ */
+
+ public int hashCode() {
+ // Note, hashcodes are the same as those used by IntHashSet
+ if (hashCode == -1) {
+ int h = 936247625;
+ IntIterator it = iterator();
+ while (it.hasNext()) {
+ h += it.next();
+ }
+ hashCode = h;
+ }
+ return hashCode;
+ }
+
+ /**
+ * Iterator class
+ */
+
+ private class IntArraySetIterator implements IntIterator, Serializable {
+
+ private int i = 0;
+
+ public IntArraySetIterator() {
+ i = 0;
+ }
+
+ public boolean hasNext() {
+ return i < contents.length;
+ }
+
+ public int next() {
+ return contents[i++];
+ }
+ }
+
+// public static void main(String[] args) {
+// int[] a = {0,20,21,33,44};
+// int[] b = {1,5,8,12,15};
+// IntArraySet x = new IntArraySet(a).union(new IntArraySet(b));
+// String s = x.toString();
+// System.out.println(s);
+// }
+
+}
+
diff --git a/sf/saxon/z/IntBlockSet.java b/sf/saxon/z/IntBlockSet.java
new file mode 100644
index 0000000..975a8f4
--- /dev/null
+++ b/sf/saxon/z/IntBlockSet.java
@@ -0,0 +1,129 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+import java.io.Serializable;
+
+/**
+ * Set of int values. This implementation of IntSet uses a sorted array
+ * of integer ranges.
+ *
+ * @author Michael Kay
+ */
+public class IntBlockSet extends AbstractIntSet implements Serializable, IntSet {
+
+ private int startPoint;
+ private int endPoint;
+
+ // Hashcode, evaluated lazily
+ private int hashCode = -1;
+
+ /**
+ * Create an IntRangeSet given the start point and end point of the integer range.
+ * @param startPoint the start point of the integer range
+ * @param endPoint the end point of the integer range
+ * @throws IllegalArgumentException if the two arrays are different lengths. Other error conditions
+ * in the input are not currently detected.
+ */
+
+ public IntBlockSet(int startPoint, int endPoint) {
+ this.startPoint = startPoint;
+ this.endPoint = endPoint;
+ }
+
+ public IntSet copy() {
+ return this;
+ }
+
+ public IntSet mutableCopy() {
+ return new IntRangeSet(new int[]{startPoint}, new int[]{endPoint});
+ }
+
+ public int size() {
+ return endPoint - startPoint;
+ }
+
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ public boolean contains(int value) {
+ return value >= startPoint && value <= endPoint;
+ }
+
+ public boolean remove(int value) {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException("clear");
+ }
+
+ /**
+ * Add an integer to the set
+ * @param value the integer to be added
+ * @return true if the integer was added, false if it was already present
+ */
+
+ public boolean add(int value) {
+ throw new UnsupportedOperationException("add");
+ }
+
+ /**
+ * Get an iterator over the values
+ */
+
+ public IntIterator iterator() {
+ return mutableCopy().iterator();
+ }
+
+
+
+ public String toString() {
+ return startPoint + " - " + endPoint;
+ }
+
+ /**
+ * Test whether this set has exactly the same members as another set. Note that
+ * IntBlockSet values are <b>NOT</b> comparable with other implementations of IntSet
+ */
+
+ public boolean equals(Object other) {
+ return mutableCopy().equals(other);
+ }
+
+ /**
+ * Construct a hash key that supports the equals() test
+ */
+
+ public int hashCode() {
+ // Note, hashcodes are NOT the same as those used by IntHashSet and IntArraySet
+ if (hashCode == -1) {
+ hashCode = 0x836a89f1 ^ (startPoint + (endPoint<<3));
+ }
+ return hashCode;
+ }
+
+ /**
+ * Get the start point of the range
+ */
+
+ public int getStartPoint() {
+ return startPoint;
+ }
+
+ /**
+ * Get the end point of the range
+ */
+
+ public int getEndPoint() {
+ return endPoint;
+ }
+
+}
+
diff --git a/sf/saxon/z/IntCheckingSet.java b/sf/saxon/z/IntCheckingSet.java
new file mode 100644
index 0000000..fb8b033
--- /dev/null
+++ b/sf/saxon/z/IntCheckingSet.java
@@ -0,0 +1,89 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An immutable integer set where membership is tested algorithmically
+ */
+public abstract class IntCheckingSet implements IntSet {
+
+ public void clear() {
+ throw new UnsupportedOperationException("IntCheckingSet is immutable");
+ }
+
+ public IntSet copy() {
+ return this;
+ }
+
+ public IntSet mutableCopy() {
+ throw new UnsupportedOperationException("IntCheckingSet cannot be copied");
+ }
+
+ public int size() {
+ return Integer.MAX_VALUE;
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public abstract boolean contains(int value);
+
+ public boolean remove(int value) {
+ throw new UnsupportedOperationException("IntCheckingSet is immutable");
+ }
+
+ public boolean add(int value) {
+ throw new UnsupportedOperationException("IntCheckingSet is immutable");
+ }
+
+ public IntIterator iterator() {
+ throw new UnsupportedOperationException("Cannot iterate over IntCheckingSet");
+ }
+
+ public IntSet union(final IntSet other) {
+ final IntSet is = this;
+ return new IntCheckingSet() {
+ @Override
+ public boolean contains(int value) {
+ return is.contains(value) || other.contains(value);
+ }
+ };
+ }
+
+ public IntSet intersect(final IntSet other) {
+ final IntSet is = this;
+ return new IntCheckingSet() {
+ @Override
+ public boolean contains(int value) {
+ return is.contains(value) && other.contains(value);
+ }
+ };
+ }
+
+ public IntSet except(final IntSet other) {
+ final IntSet is = this;
+ return new IntCheckingSet() {
+ @Override
+ public boolean contains(int value) {
+ return is.contains(value) && !other.contains(value);
+ }
+ };
+ }
+
+ public boolean containsAll(/*@NotNull*/ IntSet other) {
+ IntIterator ii = other.iterator();
+ while (ii.hasNext()) {
+ if (!contains(ii.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
diff --git a/sf/saxon/z/IntComplementPredicate.java b/sf/saxon/z/IntComplementPredicate.java
new file mode 100644
index 0000000..b2b6ed9
--- /dev/null
+++ b/sf/saxon/z/IntComplementPredicate.java
@@ -0,0 +1,42 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An IntPredicate formed as the complement of another predicate;
+ * it matches an integer if the operand does not, and vice versa.
+ */
+
+public class IntComplementPredicate implements IntPredicate {
+
+ private IntPredicate p1;
+
+ public IntComplementPredicate(IntPredicate p1) {
+ this.p1 = p1;
+ }
+
+ /**
+ * Ask whether a given value matches this predicate
+ *
+ * @param value the value to be tested
+ * @return true if the value matches; false if it does not
+ */
+ public boolean matches(int value) {
+ return !p1.matches(value);
+ }
+
+ /**
+ * Get the operand
+ * @return the negated predicate
+ */
+
+ public IntPredicate getOperand() {
+ return p1;
+ }
+}
+
diff --git a/sf/saxon/z/IntComplementSet.java b/sf/saxon/z/IntComplementSet.java
new file mode 100644
index 0000000..93b0c39
--- /dev/null
+++ b/sf/saxon/z/IntComplementSet.java
@@ -0,0 +1,104 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An immutable integer set containing all int values except those in an excluded set
+ */
+public class IntComplementSet implements IntSet {
+
+ private IntSet exclusions;
+
+ public IntComplementSet(IntSet exclusions) {
+ this.exclusions = exclusions.copy();
+ }
+
+ public IntSet getExclusions() {
+ return exclusions;
+ }
+
+ public IntSet copy() {
+ return new IntComplementSet(exclusions.copy());
+ }
+
+ public IntSet mutableCopy() {
+ return copy();
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException("IntComplementSet cannot be emptied");
+ }
+
+ public int size() {
+ return Integer.MAX_VALUE - exclusions.size();
+ }
+
+ public boolean isEmpty() {
+ return size() != 0;
+ }
+
+ public boolean contains(int value) {
+ return !exclusions.contains(value);
+ }
+
+ public boolean remove(int value) {
+ boolean b = contains(value);
+ if (b) {
+ exclusions.add(value);
+ }
+ return b;
+ }
+
+ public boolean add(int value) {
+ boolean b = contains(value);
+ if (!b) {
+ exclusions.remove(value);
+ }
+ return b;
+ }
+
+ public IntIterator iterator() {
+ throw new UnsupportedOperationException("Cannot enumerate an infinite set");
+ }
+
+ public IntSet union(IntSet other) {
+ return new IntComplementSet(exclusions.except(other));
+ }
+
+ public IntSet intersect(IntSet other) {
+ if (other.isEmpty()) {
+ return IntEmptySet.getInstance();
+ } else if (other == IntUniversalSet.getInstance()) {
+ return copy();
+ } else if (other instanceof IntComplementSet) {
+ return new IntComplementSet(exclusions.union(((IntComplementSet)other).exclusions));
+ } else {
+ return other.intersect(this);
+ }
+ }
+
+ public IntSet except(IntSet other) {
+ return new IntComplementSet(exclusions.union(other));
+ }
+
+ public boolean containsAll(/*@NotNull*/ IntSet other) {
+ if (other instanceof IntComplementSet) {
+ return ((IntComplementSet)other).exclusions.containsAll(exclusions);
+ } else if (other instanceof IntUniversalSet) {
+ return (!exclusions.isEmpty());
+ } else {
+ IntIterator ii = other.iterator();
+ while (ii.hasNext()) {
+ if (exclusions.contains(ii.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/sf/saxon/z/IntEmptySet.java b/sf/saxon/z/IntEmptySet.java
new file mode 100644
index 0000000..f9cb7a8
--- /dev/null
+++ b/sf/saxon/z/IntEmptySet.java
@@ -0,0 +1,88 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An immutable integer set containing no integers
+ */
+public class IntEmptySet implements IntSet {
+
+ private static IntEmptySet THE_INSTANCE = new IntEmptySet();
+
+ public static IntEmptySet getInstance() {
+ return THE_INSTANCE;
+ }
+
+
+ private IntEmptySet() {
+ // no action
+ }
+
+ public IntSet copy() {
+ return this;
+ }
+
+ public IntSet mutableCopy() {
+ return new IntHashSet();
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException("IntEmptySet is immutable");
+ }
+
+ public int size() {
+ return 0;
+ }
+
+ public boolean isEmpty() {
+ return true;
+ }
+
+ public boolean contains(int value) {
+ return false;
+ }
+
+ public boolean remove(int value) {
+ throw new UnsupportedOperationException("IntEmptySet is immutable");
+ }
+
+ public boolean add(int value) {
+ throw new UnsupportedOperationException("IntEmptySet is immutable");
+ }
+
+ public IntIterator iterator() {
+ return new IntIterator() {
+ public boolean hasNext() {
+ return false;
+ }
+
+ public int next() {
+ return Integer.MIN_VALUE;
+ }
+ };
+ }
+
+ public IntSet union(IntSet other) {
+ return other.copy();
+ }
+
+ public IntSet intersect(IntSet other) {
+ return this;
+ }
+
+ public IntSet except(IntSet other) {
+ return this;
+ }
+
+ public boolean containsAll(/*@NotNull*/ IntSet other) {
+ return other.isEmpty();
+ }
+}
+
+
+
diff --git a/sf/saxon/z/IntExceptPredicate.java b/sf/saxon/z/IntExceptPredicate.java
new file mode 100644
index 0000000..14970b7
--- /dev/null
+++ b/sf/saxon/z/IntExceptPredicate.java
@@ -0,0 +1,44 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An IntPredicate formed as the difference of two other predicates: it matches
+ * an integer if the first operand matches the integer and the second does not
+ */
+
+public class IntExceptPredicate implements IntPredicate {
+
+ private IntPredicate p1;
+ private IntPredicate p2;
+
+ public IntExceptPredicate(IntPredicate p1, IntPredicate p2) {
+ this.p1 = p1;
+ this.p2 = p2;
+ }
+
+ /**
+ * Ask whether a given value matches this predicate
+ *
+ * @param value the value to be tested
+ * @return true if the value matches; false if it does not
+ */
+ public boolean matches(int value) {
+ return p1.matches(value) && !p2.matches(value);
+ }
+
+ /**
+ * Get the operands
+ * @return an array containing the two operands
+ */
+
+ public IntPredicate[] getOperands() {
+ return new IntPredicate[]{p1, p2};
+ }
+}
+
diff --git a/sf/saxon/z/IntHashMap.java b/sf/saxon/z/IntHashMap.java
new file mode 100644
index 0000000..3c36f5b
--- /dev/null
+++ b/sf/saxon/z/IntHashMap.java
@@ -0,0 +1,427 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * A hash table that maps int keys to Object values.
+ *
+ * @author Dave Hale, Landmark Graphics
+ * @author Dominique Devienne
+ * @author Michael Kay: retrofitted to JDK 1.4, added iterator(), modified to disallow null values
+ * Reverted to generics July 2008.
+ */
+
+public class IntHashMap<T> implements Serializable {
+
+ /**
+ * Initializes a map with a capacity of 8 and a load factor of 0,25.
+ */
+ public IntHashMap() {
+ this(8, 0.25);
+ }
+
+ /**
+ * Initializes a map with the given capacity and a load factor of 0,25.
+ *
+ * @param capacity the initial capacity.
+ */
+ public IntHashMap(int capacity) {
+ this(capacity, 0.25);
+ }
+
+ /**
+ * Constructs a new map with initial capacity, and load factor.
+ * <p/>
+ * The capacity is the number of keys that can be mapped without resizing
+ * the arrays in which keys and values are stored. For efficiency, only
+ * a fraction of the elements in those arrays are used. That fraction is
+ * the specified load factor. The initial length of the arrays equals the
+ * smallest power of two not less than the ratio capacity/factor. The
+ * capacity of the map is increased, as necessary. The maximum number
+ * of keys that can be mapped is 2^30.
+ *
+ * @param capacity the initial capacity.
+ * @param factor the load factor.
+ */
+ public IntHashMap(int capacity, double factor) {
+ _factor = factor;
+ setCapacity(capacity);
+ }
+
+ /**
+ * Clears the map.
+ */
+ public void clear() {
+ _n = 0;
+ for (int i = 0; i < _nmax; ++i) {
+ //_filled[i] = false;
+ _value[i] = null;
+ }
+ }
+
+ /**
+ * Finds a key in the map.
+ *
+ * @param key Key
+ * @return true if the key is mapped
+ */
+// public boolean find(int key) {
+// return _filled[indexOf(key)] ? true : false;
+// }
+
+ /**
+ * Gets the value for this key.
+ *
+ * @param key Key
+ * @return the value, null if not found.
+ */
+ public T get(int key) {
+// int i = indexOf(key);
+// return _filled[i] ? _value[i] : null;
+ return _value[indexOf(key)];
+ }
+
+ /**
+ * Gets the size of the map.
+ *
+ * @return the size (the number of entries in the map)
+ */
+ public int size() {
+ return _n;
+ }
+
+ /**
+ * Removes a key from the map.
+ *
+ * @param key Key to remove
+ * @return true if the value was removed
+ */
+ public boolean remove(int key) {
+ // Knuth, v. 3, 527, Algorithm R.
+ int i = indexOf(key);
+ //if (!_filled[i]) {
+ if (_value[i] == null) {
+ return false;
+ }
+ --_n;
+ for (; ; ) {
+ //_filled[i] = false;
+ _value[i] = null;
+ int j = i;
+ int r;
+ do {
+ i = (i - 1) & _mask;
+ //if (!_filled[i]) {
+ if (_value[i] == null) {
+ return true;
+ }
+ r = hash(_key[i]);
+ } while ((i <= r && r < j) || (r < j && j < i) || (j < i && i <= r));
+ _key[j] = _key[i];
+ _value[j] = _value[i];
+ //_filled[j] = _filled[i];
+ }
+ }
+
+ /**
+ * Adds a key-value pair to the map.
+ *
+ * @param key Key
+ * @param value Value
+ * @return the value that was previously associated with the key, or null if there was no previous value
+ */
+ public T put(int key, /*@Nullable*/ T value) {
+ if (value == null) {
+ throw new NullPointerException("IntHashMap does not allow null values");
+ }
+ int i = indexOf(key);
+ T old = _value[i];
+ if (old != null) {
+ _value[i] = value;
+ } else {
+ _key[i] = key;
+ _value[i] = value;
+ grow();
+ }
+ return old;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // private
+
+ private static final int NBIT = 30; // NMAX = 2^NBIT
+ private static final int NMAX = 1 << NBIT; // maximum number of keys mapped
+ private double _factor; // 0.0 <= _factor <= 1.0
+ private int _nmax; // 0 <= _nmax = 2^nbit <= 2^NBIT = NMAX
+ private int _n; // 0 <= _n <= _nmax <= NMAX
+ private int _nlo; // _nmax*_factor (_n<=_nlo, if possible)
+ private int _nhi; // NMAX*_factor (_n< _nhi, if possible)
+ private int _shift; // _shift = 1 + NBIT - nbit (see function hash() below)
+ private int _mask; // _mask = _nmax - 1
+ private int[] _key; // array[_nmax] of keys
+ //@SuppressWarnings(value = {"unchecked"})
+ private T[] _value; // array[_nmax] of values
+ //private boolean[] _filled; // _filled[i]==true iff _key[i] is mapped
+
+ private int hash(int key) {
+ // Knuth, v. 3, 509-510. Randomize the 31 low-order bits of c*key
+ // and return the highest nbits (where nbits <= 30) bits of these.
+ // The constant c = 1327217885 approximates 2^31 * (sqrt(5)-1)/2.
+ return ((1327217885 * key) >> _shift) & _mask;
+ }
+
+ private int indexOf(int key) {
+ int i = hash(key);
+ //while (_filled[i]) {
+ while (_value[i] != null) {
+ if (_key[i] == key) {
+ return i;
+ }
+ i = (i - 1) & _mask;
+ }
+ return i;
+ }
+
+ private void grow() {
+ ++_n;
+ if (_n > NMAX) {
+ throw new RuntimeException("number of keys mapped exceeds " + NMAX);
+ }
+ if (_nlo < _n && _n <= _nhi) {
+ setCapacity(_n);
+ }
+ }
+
+ private void setCapacity(int capacity) {
+ if (capacity < _n) {
+ capacity = _n;
+ }
+ double factor = (_factor < 0.01) ? 0.01 : (_factor > 0.99) ? 0.99 : _factor;
+ int nbit, nmax;
+ for (nbit = 1, nmax = 2; nmax * factor < capacity && nmax < NMAX; ++nbit, nmax *= 2) {
+ // no-op
+ }
+ int nold = _nmax;
+ if (nmax == nold) {
+ return;
+ }
+ _nmax = nmax;
+ _nlo = (int) (nmax * factor);
+ _nhi = (int) (NMAX * factor);
+ _shift = 1 + NBIT - nbit;
+ _mask = nmax - 1;
+ int[] key = _key;
+ T[] value = _value;
+ //boolean[] filled = _filled;
+ _n = 0;
+ _key = new int[nmax];
+ // semantically equivalent to _value = new V[nmax]
+ _value = (T[]) new Object[nmax];
+ //_filled = new boolean[nmax];
+ if (key != null) {
+ for (int i = 0; i < nold; ++i) {
+ //if (filled[i]) {
+ if (value[i] != null) {
+ put(key[i], value[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get an iterator over the keys
+ *
+ * @return an iterator over the integer keys in the map
+ */
+
+ public IntIterator keyIterator() {
+ return new IntHashMapKeyIterator();
+ }
+
+ /**
+ * Get an iterator over the values
+ *
+ * @return an iterator over the values in the map
+ */
+
+ public Iterator<T> valueIterator() {
+ return new IntHashMapValueIterator();
+ }
+
+ /**
+ * Create a copy of the IntHashMap
+ *
+ * @return a copy of this map
+ */
+
+ public IntHashMap<T> copy() {
+ IntHashMap<T> n = new IntHashMap<T>(size());
+ IntIterator it = keyIterator();
+ while (it.hasNext()) {
+ int k = it.next();
+ n.put(k, get(k));
+ }
+ return n;
+ }
+
+ /**
+ * Diagnostic display of contents
+ */
+
+ public void display() {
+ IntIterator iter = new IntHashMapKeyIterator();
+ while (iter.hasNext()) {
+ int key = iter.next();
+ Object value = get(key);
+ System.err.println(key + " -> " + value.toString());
+ }
+ }
+
+ /**
+ * Iterator over keys
+ */
+ private class IntHashMapKeyIterator implements IntIterator, Serializable {
+
+ private int i = 0;
+
+ public IntHashMapKeyIterator() {
+ i = 0;
+ }
+
+ public boolean hasNext() {
+ while (i < _key.length) {
+ if (_value[i] != null) {
+ return true;
+ } else {
+ i++;
+ }
+ }
+ return false;
+ }
+
+ public int next() {
+ return _key[i++];
+ }
+ }
+
+ /**
+ * Iterator over values
+ */
+ private class IntHashMapValueIterator implements Iterator<T>, Serializable {
+
+ private int i = 0;
+
+ public IntHashMapValueIterator() {
+ i = 0;
+ }
+
+ public boolean hasNext() {
+ while (i < _key.length) {
+ if (_value[i] != null) {
+ return true;
+ } else {
+ i++;
+ }
+ }
+ return false;
+ }
+
+ public T next() {
+ T temp = _value[i++];
+ if (temp == null) {
+ throw new NoSuchElementException();
+ }
+ return temp;
+ }
+
+ /**
+ * Removes from the underlying collection the last element returned by the
+ * iterator (optional operation).
+ *
+ * @throws UnsupportedOperationException if the <tt>remove</tt>
+ * operation is not supported by this Iterator.
+ */
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+ }
+
+ /**
+ * Get the set of integer keys present in this IntHashSet
+ *
+ * @return the set of integer keys present in this IntHashSet
+ */
+
+ public IntSet keySet() {
+ return new IntSet() {
+ public void clear() {
+ throw new UnsupportedOperationException("Immutable set");
+ }
+
+ public IntSet copy() {
+ IntHashSet s = new IntHashSet();
+ IntIterator ii = iterator();
+ while (ii.hasNext()) {
+ s.add(ii.next());
+ }
+ return s;
+ }
+
+ public IntSet mutableCopy() {
+ return copy();
+ }
+
+ public int size() {
+ return _n;
+ }
+
+ public boolean isEmpty() {
+ return _n == 0;
+ }
+
+ public boolean contains(int key) {
+ return _value[indexOf(key)] != null;
+ }
+
+ public boolean remove(int value) {
+ throw new UnsupportedOperationException("Immutable set");
+ }
+
+ public boolean add(int value) {
+ throw new UnsupportedOperationException("Immutable set");
+ }
+
+ public IntIterator iterator() {
+ return new IntHashMapKeyIterator();
+ }
+
+ public IntSet union(IntSet other) {
+ return copy().union(other);
+ }
+
+ public IntSet intersect(IntSet other) {
+ return copy().intersect(other);
+ }
+
+ public IntSet except(IntSet other) {
+ return copy().except(other);
+ }
+
+ public boolean containsAll(IntSet other) {
+ return copy().containsAll(other);
+ }
+ };
+ }
+
+}
+
diff --git a/sf/saxon/z/IntHashSet.java b/sf/saxon/z/IntHashSet.java
new file mode 100644
index 0000000..96c3d82
--- /dev/null
+++ b/sf/saxon/z/IntHashSet.java
@@ -0,0 +1,367 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.io.Serializable;
+
+/**
+ * Set of int values. This class is modelled on the java.net.Set interface, but it does
+ * not implement this interface, because the set members are nint's rather than Objects.
+ * <p/>
+ * Not thread safe.
+ * @author Dominique Devienne
+ * @author Michael Kay: retrofitted to JDK 1.4, added iterator()
+ */
+public class IntHashSet extends AbstractIntSet implements IntSet, Serializable {
+
+ private static final int NBIT = 30; // MAX_SIZE = 2^NBIT
+
+ /**
+ * The maximum number of elements this container can contain.
+ */
+ public static final int MAX_SIZE = 1 << NBIT; // maximum number of keys mapped
+
+ /**
+ * This set's NO-DATA-VALUE.
+ */
+ public final int ndv;
+
+ // private
+
+ //private double _factor; // 0.0 <= _factor <= 1.0 - changed by MHK to assume factor = 0.25
+ private int _nmax; // 0 <= _nmax = 2^nbit <= 2^NBIT = MAX_SIZE
+ private int _size; // 0 <= _size <= _nmax <= MAX_SIZE
+ private int _nlo; // _nmax*_factor (_size<=_nlo, if possible)
+ private int _nhi; // MAX_SIZE*_factor (_size< _nhi, if possible)
+ private int _shift; // _shift = 1 + NBIT - nbit (see function hash() below)
+ private int _mask; // _mask = _nmax - 1
+ private int[] _values; // array[_nmax] of values
+
+
+ /**
+ * Initializes a set with a capacity of 8 and a load factor of 0,25.
+ */
+ public IntHashSet() {
+ this(8, Integer.MIN_VALUE);
+ }
+
+ /**
+ * Initializes a set with the given capacity and a load factor of 0,25.
+ * @param capacity the initial capacity.
+ */
+ public IntHashSet(int capacity) {
+ this(capacity, Integer.MIN_VALUE);
+ }
+
+ /**
+ * Initializes a set with a load factor of 0,25.
+ * @param capacity the initial capacity.
+ * @param noDataValue the value to use for non-values.
+ */
+ public IntHashSet(int capacity, int noDataValue) {
+ ndv = noDataValue;
+ //_factor = 0.25;
+ setCapacity(capacity);
+ }
+
+ public IntSet copy() {
+ if (_size == 0) {
+ return IntEmptySet.getInstance();
+ } else {
+ IntHashSet s = new IntHashSet(_size, ndv);
+ s._nmax = _nmax;
+ s._size = _size;
+ s._nlo = _nlo;
+ s._nhi = _nhi;
+ s._shift = _shift;
+ s._size = _size;
+ s._values = new int[_values.length];
+ System.arraycopy(_values, 0, s._values, 0, _values.length);
+ //s._values = Arrays.copyOf(_values, _values.length);
+ return s;
+ }
+ }
+
+ public IntSet mutableCopy() {
+ return copy();
+ }
+
+ public void clear() {
+ _size = 0;
+ for (int i = 0; i < _nmax; ++i) {
+ _values[i] = ndv;
+ }
+ }
+
+ public int size() {
+ return _size;
+ }
+
+ public boolean isEmpty() {
+ return _size == 0;
+ }
+
+ public int[] getValues() {
+ int index = 0;
+ final int[] values = new int[_size];
+ for (int _value : _values) {
+ if (_value != ndv) {
+ values[index++] = _value;
+ }
+ }
+ return values;
+ }
+
+
+ public boolean contains(int value) {
+ return (_values[indexOf(value)] != ndv);
+ }
+
+
+ public boolean remove(int value) {
+ // Knuth, v. 3, 527, Algorithm R.
+ int i = indexOf(value);
+ if (_values[i] == ndv) {
+ return false;
+ }
+ --_size;
+ for (; ;) {
+ _values[i] = ndv;
+ int j = i;
+ int r;
+ do {
+ i = (i - 1) & _mask;
+ if (_values[i] == ndv) {
+ return true;
+ }
+ r = hash(_values[i]);
+ } while ((i <= r && r < j) || (r < j && j < i) || (j < i && i <= r));
+ _values[j] = _values[i];
+ }
+ }
+
+
+ public boolean add(int value) {
+ if (value == ndv) {
+ throw new IllegalArgumentException("Can't add the 'no data' value");
+ }
+ int i = indexOf(value);
+ if (_values[i] == ndv) {
+ ++_size;
+ _values[i] = value;
+
+ // Check new size
+ if (_size > MAX_SIZE) {
+ throw new RuntimeException("Too many elements (> " + MAX_SIZE + ')');
+ }
+ if (_nlo < _size && _size <= _nhi) {
+ setCapacity(_size);
+ }
+ return true;
+ } else {
+ return false; // leave set unchanged
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+
+ private int hash(int key) {
+ // Knuth, v. 3, 509-510. Randomize the 31 low-order bits of c*key
+ // and return the highest nbits (where nbits <= 30) bits of these.
+ // The constant c = 1327217885 approximates 2^31 * (sqrt(5)-1)/2.
+ return ((1327217885 * key) >> _shift) & _mask;
+ }
+
+ /**
+ * Gets the index of the value, if it exists, or the index at which
+ * this value would be added if it does not exist yet.
+ */
+ private int indexOf(int value) {
+ int i = hash(value);
+ while (_values[i] != ndv) {
+ if (_values[i] == value) {
+ return i;
+ }
+ i = (i - 1) & _mask;
+ }
+ return i;
+ }
+
+ private void setCapacity(int capacity) {
+ // Changed MHK in 8.9 to use a constant factor of 0.25, thus avoiding floating point arithmetic
+ if (capacity < _size) {
+ capacity = _size;
+ }
+ //double factor = 0.25;
+ int nbit, nmax;
+ for (nbit = 1, nmax = 2; nmax < capacity * 4 && nmax < MAX_SIZE; ++nbit, nmax *= 2) {
+ ;
+ }
+ int nold = _nmax;
+ if (nmax == nold) {
+ return;
+ }
+
+ _nmax = nmax;
+ _nlo = (int)(nmax / 4);
+ _nhi = (int)(MAX_SIZE / 4);
+ _shift = 1 + NBIT - nbit;
+ _mask = nmax - 1;
+
+ _size = 0;
+ int[] values = _values;
+ _values = new int[nmax];
+ java.util.Arrays.fill(_values, ndv); // empty all values
+ if (values != null) {
+ for (int i = 0; i < nold; ++i) {
+ int value = values[i];
+ if (value != ndv) {
+ // Don't use add, because the capacity is necessarily large enough,
+ // and the value is necessarily unique (since in this set already)!
+ //add(values[i]);
+ ++_size;
+ _values[indexOf(value)] = value;
+ }
+ }
+ }
+ }
+
+ /**
+ * Get an iterator over the values
+ */
+
+ public IntIterator iterator() {
+ return new IntHashSetIterator();
+ }
+
+
+ /**
+ * Test if one set has overlapping membership with another set
+ */
+
+ public static boolean containsSome(IntSet one, IntSet two) {
+ if (two instanceof IntEmptySet) {
+ return false;
+ }
+ if (two instanceof IntUniversalSet) {
+ return !one.isEmpty();
+ }
+ if (two instanceof IntComplementSet) {
+ return !((IntComplementSet)two).getExclusions().containsAll(one);
+ }
+ IntIterator it = two.iterator();
+ while (it.hasNext()) {
+ if (one.contains(it.next())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Test whether this set has exactly the same members as another set
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof IntSet) {
+ IntHashSet s = (IntHashSet)other;
+ return (size() == s.size() && containsAll(s));
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Construct a hash key that supports the equals() test
+ */
+
+ public int hashCode() {
+ // Note, hashcodes are the same as those used by IntArraySet
+ int h = 936247625;
+ IntIterator it = iterator();
+ while (it.hasNext()) {
+ h += it.next();
+ }
+ return h;
+ }
+
+ /**
+ * Diagnostic output
+ */
+
+ public void diagnosticDump() {
+ System.err.println("Contents of IntHashSet");
+ FastStringBuffer sb = new FastStringBuffer(100);
+ for (int i = 0; i < _values.length; i++) {
+ if (i % 10 == 0) {
+ System.err.println(sb.toString());
+ sb.setLength(0);
+ }
+ if (_values[i] == ndv) {
+ sb.append("*, ");
+ } else {
+ sb.append(_values[i] + ", ");
+ }
+ }
+ System.err.println(sb.toString());
+ sb.setLength(0);
+ System.err.println("size: " + _size);
+ System.err.println("ndv: " + ndv);
+ System.err.println("nlo: " + _nlo);
+ System.err.println("nhi: " + _nhi);
+ System.err.println("nmax: " + _nmax);
+ System.err.println("shift: " + _shift);
+ System.err.println("mask: " + _mask);
+ System.err.println("Result of iterator:");
+ IntIterator iter = iterator();
+ int i = 0;
+ while (iter.hasNext()) {
+ if (i++ % 10 == 0) {
+ System.err.println(sb.toString());
+ sb.setLength(0);
+ }
+ sb.append(iter.next() + ", ");
+ }
+ System.err.println(sb.toString());
+ System.err.println("=====================");
+ }
+
+ /**
+ * Iterator class
+ * @author Saxonica Limited
+ */
+
+ private class IntHashSetIterator implements IntIterator, Serializable {
+
+ private int i = 0;
+
+ public IntHashSetIterator() {
+ i = 0;
+ }
+
+ public boolean hasNext() {
+ while (i < _values.length) {
+ if (_values[i] != ndv) {
+ return true;
+ } else {
+ i++;
+ }
+ }
+ return false;
+ }
+
+ public int next() {
+ return _values[i++];
+ }
+ }
+
+}
+
diff --git a/sf/saxon/z/IntIntersectionPredicate.java b/sf/saxon/z/IntIntersectionPredicate.java
new file mode 100644
index 0000000..e2b20af
--- /dev/null
+++ b/sf/saxon/z/IntIntersectionPredicate.java
@@ -0,0 +1,44 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An IntPredicate formed as the intersection of two other predicates: it matches
+ * an integer if both of the operands matches the integer
+ */
+
+public class IntIntersectionPredicate implements IntPredicate {
+
+ private IntPredicate p1;
+ private IntPredicate p2;
+
+ public IntIntersectionPredicate(IntPredicate p1, IntPredicate p2) {
+ this.p1 = p1;
+ this.p2 = p2;
+ }
+
+ /**
+ * Ask whether a given value matches this predicate
+ *
+ * @param value the value to be tested
+ * @return true if the value matches; false if it does not
+ */
+ public boolean matches(int value) {
+ return p1.matches(value) && p2.matches(value);
+ }
+
+ /**
+ * Get the operands
+ * @return an array containing the two operands
+ */
+
+ public IntPredicate[] getOperands() {
+ return new IntPredicate[]{p1, p2};
+ }
+}
+
diff --git a/sf/saxon/z/IntIterator.java b/sf/saxon/z/IntIterator.java
new file mode 100644
index 0000000..1586c1e
--- /dev/null
+++ b/sf/saxon/z/IntIterator.java
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An iterator over a sequence of unboxed int values
+ */
+public interface IntIterator {
+
+ /**
+ * Test whether there are any more integers in the sequence
+ * @return true if there are more integers to come
+ */
+
+ public boolean hasNext();
+
+ /**
+ * Return the next integer in the sequence. The result is undefined unless hasNext() has been called
+ * and has returned true.
+ * @return the next integer in the sequence
+ */
+
+ public int next();
+}
diff --git a/sf/saxon/z/IntPredicate.java b/sf/saxon/z/IntPredicate.java
new file mode 100644
index 0000000..53ac147
--- /dev/null
+++ b/sf/saxon/z/IntPredicate.java
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * Interface defining a predicate that can be tested to determine whether an integer
+ * satisfies, or does not satisfy, some condition.
+ */
+public interface IntPredicate {
+
+ /**
+ * Ask whether a given value matches this predicate
+ * @param value the value to be tested
+ * @return true if the value matches; false if it does not
+ */
+ public boolean matches(int value);
+}
diff --git a/sf/saxon/z/IntRangeSet.java b/sf/saxon/z/IntRangeSet.java
new file mode 100644
index 0000000..6eef655
--- /dev/null
+++ b/sf/saxon/z/IntRangeSet.java
@@ -0,0 +1,425 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+
+import net.sf.saxon.tree.util.FastStringBuffer;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * Set of int values. This implementation of IntSet uses a sorted array
+ * of integer ranges.
+ *
+ * @author Michael Kay
+ */
+public class IntRangeSet extends AbstractIntSet implements Serializable, IntSet {
+
+ // The array of start points, which will always be sorted
+ private int[] startPoints;
+
+ // The array of end points, which will always be sorted
+ private int[] endPoints;
+
+ // The number of elements of the above two arrays that are actually in use
+ private int used = 0;
+
+ // Hashcode, evaluated lazily
+ private int hashCode = -1;
+
+ // The number of items in the set
+ private int size = 0;
+
+ /**
+ * Create an empty set
+ */
+ public IntRangeSet() {
+ startPoints = new int[4];
+ endPoints = new int[4];
+ used = 0;
+ size = 0;
+ hashCode = -1;
+ }
+
+ /**
+ * Create one IntRangeSet as a copy of another
+ * @param input the IntRangeSet to be copied
+ */
+
+ public IntRangeSet(IntRangeSet input) {
+ startPoints = new int[input.used];
+ endPoints = new int[input.used];
+ used = input.used;
+ System.arraycopy(input.startPoints, 0, startPoints, 0, used);
+ System.arraycopy(input.endPoints, 0, endPoints, 0, used);
+ hashCode = input.hashCode;
+ }
+
+ /**
+ * Create an IntRangeSet given the start points and end points of the integer ranges.
+ * The two arrays must be the same length; each must be in ascending order; and the n'th end point
+ * must be greater than the n'th start point, and less than the n+1'th start point, for all n.
+ * @param startPoints the start points of the integer ranges
+ * @param endPoints the end points of the integer ranges
+ * @throws IllegalArgumentException if the two arrays are different lengths. Other error conditions
+ * in the input are not currently detected.
+ */
+
+ public IntRangeSet(int[] startPoints, int[] endPoints) {
+ if (startPoints.length != endPoints.length) {
+ throw new IllegalArgumentException("Array lengths differ");
+ }
+ this.startPoints = startPoints;
+ this.endPoints = endPoints;
+ used = startPoints.length;
+ for (int i=0; i<used; i++) {
+ size += (endPoints[i] - startPoints[i] + 1);
+ }
+ }
+
+ public void clear() {
+ startPoints = new int[4];
+ endPoints = new int[4];
+ used = 0;
+ hashCode = -1;
+ }
+
+ public IntSet copy() {
+ IntRangeSet s = new IntRangeSet();
+ s.startPoints = new int[startPoints.length];
+ System.arraycopy(startPoints, 0, s.startPoints, 0, startPoints.length);
+ //s.startPoints = Arrays.copyOf(startPoints, startPoints.length);
+ s.endPoints = new int[endPoints.length];
+ System.arraycopy(endPoints, 0, s.endPoints, 0, endPoints.length);
+ //s.endPoints = Arrays.copyOf(endPoints, endPoints.length);
+ s.used = used;
+ s.size = size;
+ return s;
+ }
+
+ public IntSet mutableCopy() {
+ return copy();
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ public boolean contains(int value) {
+ if (used == 0) {
+ return false;
+ }
+ if (value > endPoints[used-1]) {
+ return false;
+ }
+ if (value < startPoints[0]) {
+ return false;
+ }
+ int i = 0;
+ int j = used;
+ do {
+ int mid = i + (j-i)/2;
+ if (endPoints[mid] < value) {
+ i = Math.max(mid, i+1);
+ } else if (startPoints[mid] > value) {
+ j = Math.min(mid, j-1);
+ } else {
+ return true;
+ }
+ } while (i != j);
+ return false;
+ }
+
+ public boolean remove(int value) {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ /**
+ * Add an integer to the set
+ * @param value the integer to be added
+ * @return true if the integer was added, false if it was already present
+ */
+
+ public boolean add(int value) {
+ hashCode = -1;
+ if (used == 0) {
+ ensureCapacity(1);
+ startPoints[used-1] = value;
+ endPoints[used-1] = value;
+ size++;
+ return true;
+ }
+ if (value > endPoints[used-1]) {
+ if (value == endPoints[used-1] + 1) {
+ endPoints[used-1]++;
+ } else {
+ ensureCapacity(used+1);
+ startPoints[used-1] = value;
+ endPoints[used-1] = value;
+ }
+ size++;
+ return true;
+ }
+ if (value < startPoints[0]) {
+ if (value == startPoints[0] - 1) {
+ startPoints[0]--;
+ } else {
+ ensureCapacity(used+1);
+ System.arraycopy(startPoints, 0, startPoints, 1, used-1);
+ System.arraycopy(endPoints, 0, endPoints, 1, used-1);
+ startPoints[0] = value;
+ endPoints[0] = value;
+ }
+ size++;
+ return true;
+ }
+ int i = 0;
+ int j = used;
+ do {
+ int mid = i + (j-i)/2;
+ if (endPoints[mid] < value) {
+ i = Math.max(mid, i+1);
+ } else if (startPoints[mid] > value) {
+ j = Math.min(mid, j-1);
+ } else {
+ return false; // value is already present
+ }
+ } while (i != j);
+ if (i > 0 && endPoints[i-1]+1 == value) {
+ i--;
+ } else if (i < used-1 && startPoints[i+1]-1 == value) {
+ i++;
+ }
+ if (endPoints[i]+1 == value) {
+ if (value == startPoints[i+1]-1) {
+ // merge the two ranges
+ endPoints[i] = endPoints[i+1];
+ System.arraycopy(startPoints, i+2, startPoints, i+1, used-i-2);
+ System.arraycopy(endPoints, i+2, endPoints, i+1, used-i-2);
+ used--;
+ } else {
+ endPoints[i]++;
+ }
+ size++;
+ return true;
+ } else if (startPoints[i]-1 == value) {
+ if (value == endPoints[i-1]+1) {
+ // merge the two ranges
+ endPoints[i-1] = endPoints[i];
+ System.arraycopy(startPoints, i+1, startPoints, i, used-i-1);
+ System.arraycopy(endPoints, i+1, endPoints, i, used-i-1);
+ used--;
+ } else {
+ startPoints[i]--;
+ }
+ size++;
+ return true;
+ } else {
+ if (value > endPoints[i]) {
+ i++;
+ }
+ ensureCapacity(used+1);
+ try {
+ System.arraycopy(startPoints, i, startPoints, i+1, used-i-1);
+ System.arraycopy(endPoints, i, endPoints, i+1, used-i-1);
+ } catch (Exception err) {
+ err.printStackTrace();
+ }
+ startPoints[i] = value;
+ endPoints[i] = value;
+ size++;
+ return true;
+ }
+ }
+
+ private void ensureCapacity(int n) {
+ if (startPoints.length < n) {
+ int[] s = new int[startPoints.length * 2];
+ int[] e = new int[startPoints.length * 2];
+ System.arraycopy(startPoints, 0, s, 0, used);
+ System.arraycopy(endPoints, 0, e, 0, used);
+ startPoints = s;
+ endPoints = e;
+ }
+ used = n;
+ }
+
+
+ /**
+ * Get an iterator over the values
+ */
+
+ public IntIterator iterator() {
+ return new IntRangeSetIterator();
+ }
+
+
+
+ public String toString() {
+ FastStringBuffer sb = new FastStringBuffer(used*8);
+ for (int i=0; i<used; i++) {
+ sb.append(startPoints[i] + "-" + endPoints[i] + ",");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Test whether this set has exactly the same members as another set. Note that
+ * IntRangeSet values are <b>NOT</b> comparable with other implementations of IntSet
+ */
+
+ public boolean equals(Object other) {
+ if (other instanceof IntSet) {
+ if (other instanceof IntRangeSet) {
+ return used == ((IntRangeSet)other).used &&
+ Arrays.equals(startPoints, ((IntRangeSet)other).startPoints) &&
+ Arrays.equals(endPoints, ((IntRangeSet)other).endPoints) ;
+ } else {
+ return containsAll((IntSet)other);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Construct a hash key that supports the equals() test
+ */
+
+ public int hashCode() {
+ // Note, hashcodes are NOT the same as those used by IntHashSet and IntArraySet
+ if (hashCode == -1) {
+ int h = 0x836a89f1;
+ for (int i=0; i<used; i++) {
+ h ^= startPoints[i] + (endPoints[i]<<3);
+ }
+ hashCode = h;
+ }
+ return hashCode;
+ }
+
+ /**
+ * Add a range of integers to the set.
+ * This is optimized for the case where these are all greater than any existing integer
+ * in the set.
+ * @param low the low end of the new range
+ * @param high the high end of the new range
+ */
+
+ public void addRange(int low, int high) {
+ if (low == high) {
+ add(low);
+ return;
+ }
+ hashCode = -1;
+ if (used == 0) {
+ ensureCapacity(1);
+ startPoints[used-1] = low;
+ endPoints[used-1] = high;
+ size += (high - low + 1);
+ } else if (low > endPoints[used-1]) {
+ if (low == endPoints[used-1] + 1) {
+ endPoints[used-1] = high;
+ } else {
+ ensureCapacity(used+1);
+ startPoints[used-1] = low;
+ endPoints[used-1] = high;
+ }
+ size += (high - low + 1);
+ } else if (high < startPoints[0]) {
+ ensureCapacity(used+1);
+ System.arraycopy(startPoints, 0, startPoints, 1, used-1);
+ System.arraycopy(endPoints, 0, endPoints, 1, used-1);
+ startPoints[0] = low;
+ endPoints[0] = high;
+ } else {
+ for (int i=1; i<used; i++) {
+ if (startPoints[i] > high && endPoints[i-1] < low) {
+ ensureCapacity(used+1);
+ System.arraycopy(startPoints, i, startPoints, i+1, used-i-1);
+ System.arraycopy(endPoints, i, endPoints, i+1, used-i-1);
+ startPoints[i] = low;
+ endPoints[i] = high;
+ return;
+ }
+ }
+ // otherwise do it the hard way
+ for (int i=low; i<=high; i++) {
+ add(i);
+ }
+ }
+ }
+
+ /**
+ * Get the start points of the ranges
+ */
+
+ public int[] getStartPoints() {
+ return startPoints;
+ }
+
+ /**
+ * Get the end points of the ranges
+ */
+
+ public int[] getEndPoints() {
+ return endPoints;
+ }
+
+ /**
+ * Get the number of ranges actually in use
+ */
+
+ public int getNumberOfRanges() {
+ return used;
+ }
+
+ /**
+ * Iterator class
+ */
+
+ private class IntRangeSetIterator implements IntIterator, Serializable {
+
+ private int i = 0;
+ private int current = 0;
+
+ public IntRangeSetIterator() {
+ i = -1;
+ current = Integer.MIN_VALUE;
+ }
+
+ public boolean hasNext() {
+ if (i<0) {
+ return size > 0;
+ } else {
+ return current < endPoints[used-1];
+ }
+ }
+
+ public int next() {
+ if (i < 0) {
+ i = 0;
+ current = startPoints[0];
+ return current;
+ }
+ if (current == endPoints[i]) {
+ current = startPoints[++i];
+ return current;
+ } else {
+ return ++current;
+ }
+ }
+ }
+
+}
+
diff --git a/sf/saxon/z/IntSet.java b/sf/saxon/z/IntSet.java
new file mode 100644
index 0000000..ce619b9
--- /dev/null
+++ b/sf/saxon/z/IntSet.java
@@ -0,0 +1,119 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * A set of integers represented as int values
+ */
+public interface IntSet {
+
+ /**
+ * Create a copy of this IntSet that leaves the original unchanged.
+ * @return an IntSet containing the same integers. The result will not necessarily be the
+ * same class as the original. It will either be an immutable object, or a newly constructed
+ * object.
+ */
+
+ IntSet copy();
+
+ /**
+ * Create a copy of this IntSet that contains the same set of integers.
+ * @return an IntSet containing the same integers. The result will not necessarily be the
+ * same class as the original. It will always be a mutable object
+ */
+
+ IntSet mutableCopy();
+
+ /**
+ * Clear the contents of the IntSet (making it an empty set)
+ */
+ void clear();
+
+ /**
+ * Get the number of integers in the set
+ * @return the size of the set
+ */
+
+ int size();
+
+ /**
+ * Determine if the set is empty
+ * @return true if the set is empty, false if not
+ */
+
+ boolean isEmpty();
+
+ /**
+ * Determine whether a particular integer is present in the set
+ * @param value the integer under test
+ * @return true if value is present in the set, false if not
+ */
+
+ boolean contains(int value);
+
+ /**
+ * Remove an integer from the set
+ * @param value the integer to be removed
+ * @return true if the integer was present in the set, false if it was not present
+ */
+
+ boolean remove(int value);
+
+ /**
+ * Add an integer to the set
+ * @param value the integer to be added
+ * @return true if the integer was added, false if it was already present
+ */
+
+ boolean add(int value);
+
+ /**
+ * Get an iterator over the values
+ * @return an iterator over the integers in the set
+ */
+
+ IntIterator iterator();
+
+ /**
+ * Form a new set that is the union of this IntSet and another.
+ * The result will either be an immutable object, or a newly constructed object.
+ * @param other the second set
+ * @return the union of the two sets
+ */
+
+ IntSet union(IntSet other);
+
+ /**
+ * Form a new set that is the intersection of this IntSet and another.
+ * The result will either be an immutable object, or a newly constructed object.
+ * @param other the second set
+ * @return the intersection of the two sets
+ */
+
+ IntSet intersect(IntSet other);
+
+ /**
+ * Form a new set that is the difference of this set and another set.
+ * The result will either be an immutable object, or a newly constructed object.
+ * @param other the second set
+ * @return the intersection of the two sets
+ */
+
+ IntSet except(IntSet other);
+
+
+ /**
+ * Test if this set is a superset of another set
+ * @param other the other set
+ * @return true if every integer in the other set is also present in this set
+ */
+
+ public boolean containsAll(IntSet other);
+
+
+}
diff --git a/sf/saxon/z/IntSetPredicate.java b/sf/saxon/z/IntSetPredicate.java
new file mode 100644
index 0000000..9f83135
--- /dev/null
+++ b/sf/saxon/z/IntSetPredicate.java
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An implementation of IntPredicate that tests whether a given integer is a member
+ * of some IntSet
+ */
+public class IntSetPredicate implements IntPredicate {
+
+ private IntSet set;
+
+ public IntSetPredicate(IntSet set) {
+ if (set==null) {
+ throw new NullPointerException();
+ }
+ this.set = set;
+ }
+
+ /**
+ * Ask whether a given value matches this predicate
+ *
+ * @param value the value to be tested
+ * @return true if the value matches; false if it does not
+ */
+ public boolean matches(int value) {
+ return set.contains(value);
+ }
+
+ /**
+ * Get the underlying IntSet
+ * @return the underlying IntSet
+ */
+
+ public IntSet getIntSet() {
+ return set;
+ }
+}
diff --git a/sf/saxon/z/IntSingletonSet.java b/sf/saxon/z/IntSingletonSet.java
new file mode 100644
index 0000000..5b7ba7a
--- /dev/null
+++ b/sf/saxon/z/IntSingletonSet.java
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An immutable integer set containing a single integer
+ */
+public class IntSingletonSet implements IntSet {
+
+ private int value;
+
+ public IntSingletonSet(int value) {
+ this.value = value;
+ }
+
+ public int getMember() {
+ return value;
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException("IntSingletonSet is immutable");
+ }
+
+ public IntSet copy() {
+ return this;
+ }
+
+ public IntSet mutableCopy() {
+ IntHashSet intHashSet = new IntHashSet();
+ intHashSet.add(value);
+ return intHashSet;
+ }
+
+ public int size() {
+ return 1;
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public boolean contains(int value) {
+ return this.value == value;
+ }
+
+ public boolean remove(int value) {
+ throw new UnsupportedOperationException("IntSingletonSet is immutable");
+ }
+
+ public boolean add(int value) {
+ throw new UnsupportedOperationException("IntSingletonSet is immutable");
+ }
+
+ public IntIterator iterator() {
+ return new IntIterator() {
+
+ boolean gone = false;
+ public boolean hasNext() {
+ return !gone;
+ }
+
+ public int next() {
+ gone = true;
+ return value;
+ }
+ };
+ }
+
+ public IntSet union(IntSet other) {
+ IntSet n = other.mutableCopy();
+ n.add(value);
+ return n;
+ }
+
+ public IntSet intersect(IntSet other) {
+ if (other.contains(value)) {
+ return this;
+ } else {
+ return IntEmptySet.getInstance();
+ }
+ }
+
+ public IntSet except(IntSet other) {
+ if (other.contains(value)) {
+ return IntEmptySet.getInstance();
+ } else {
+ return this;
+ }
+ }
+
+ public boolean containsAll(/*@NotNull*/ IntSet other) {
+ if (other.size() > 1) {
+ return false;
+ }
+ IntIterator ii = other.iterator();
+ while (ii.hasNext()) {
+ if (value != ii.next()) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
diff --git a/sf/saxon/z/IntToIntArrayMap.java b/sf/saxon/z/IntToIntArrayMap.java
new file mode 100644
index 0000000..f8fb198
--- /dev/null
+++ b/sf/saxon/z/IntToIntArrayMap.java
@@ -0,0 +1,182 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+import java.io.Serializable;
+
+/**
+ * An implementation of {@link IntToIntMap} that relies on serial searching, and
+ * is therefore optimized for very small map sizes
+ */
+public class IntToIntArrayMap implements IntToIntMap, Serializable {
+
+ private int[] keys;
+ private int[] values;
+ private int used = 0;
+ private int defaultValue = Integer.MIN_VALUE;
+
+ /**
+ * Create an initial empty map with default space allocation
+ */
+
+ public IntToIntArrayMap() {
+ keys = new int[8];
+ values = new int[8];
+ }
+
+ /**
+ * Create an initial empty map with a specified initial capacity
+ * @param capacity the initial capacity (the number of entries that can be held
+ * before more space is allocated)
+ */
+
+ public IntToIntArrayMap(int capacity) {
+ if (capacity <= 0) {
+ throw new IllegalArgumentException("capacity <= 0");
+ }
+ keys = new int[capacity];
+ values = new int[capacity];
+ }
+
+ /**
+ * Clear the map.
+ */
+ public void clear() {
+ used = 0;
+ }
+
+ /**
+ * Finds a key in the map.
+ *
+ * @param key Key
+ * @return true if the key is mapped
+ */
+ public boolean find(int key) {
+ for (int i=0; i<used; i++) {
+ if (keys[i] == key) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the value for this key.
+ *
+ * @param key Key
+ * @return the value, or the default value if not found.
+ */
+ public int get(int key) {
+ for (int i=0; i<used; i++) {
+ if (keys[i] == key) {
+ return values[i];
+ }
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Get the default value used to indicate an unused entry
+ *
+ * @return the value to be returned by {@link #get(int)} if no entry
+ * exists for the supplied key
+ */
+
+ public int getDefaultValue() {
+ return defaultValue;
+ }
+
+ /**
+ * Get an iterator over the integer key values held in the hash map.
+ * <p>The contents of the hash map must not be modified while this iterator remains in use</p>
+ * @return an iterator whose next() call returns the key values (in arbitrary order)
+ */
+
+ /*@NotNull*/ public IntIterator keyIterator() {
+ return new KeyIterator();
+ }
+
+ /**
+ * Adds a key-value pair to the map.
+ *
+ * @param key Key
+ * @param value Value
+ */
+ public void put(int key, int value) {
+ for (int i=0; i<used; i++) {
+ if (keys[i] == key) {
+ values[i] = value;
+ return;
+ }
+ }
+ if (used >= keys.length) {
+ int[] k2 = new int[used*2];
+ System.arraycopy(keys, 0, k2, 0, used);
+ keys = k2;
+ int[] v2 = new int[used*2];
+ System.arraycopy(values, 0, v2, 0, used);
+ values = v2;
+ }
+ keys[used] = key;
+ values[used++] = value;
+ }
+
+ /**
+ * Removes a key from the map.
+ *
+ * @param key Key to remove
+ * @return true if the value was removed
+ */
+ public boolean remove(int key) {
+ for (int i=0; i<used; i++) {
+ if (keys[i] == key) {
+ values[i] = defaultValue;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Set the value to be returned to indicate an unused entry
+ *
+ * @param defaultValue the value to be returned by {@link #get(int)} if no entry
+ * exists for the supplied key
+ */
+ public void setDefaultValue(int defaultValue) {
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Gets the size of the map.
+ *
+ * @return the size
+ */
+ public int size() {
+ return used;
+ }
+
+ private class KeyIterator implements IntIterator, Serializable {
+
+ private int i = 0;
+ private static final long serialVersionUID = 1720894017771245276L;
+
+ public KeyIterator() {
+ i = 0;
+ }
+
+ public boolean hasNext() {
+ return i < used;
+ }
+
+ public int next() {
+ return keys[i++];
+ }
+ }
+}
+
diff --git a/sf/saxon/z/IntToIntHashMap.java b/sf/saxon/z/IntToIntHashMap.java
new file mode 100644
index 0000000..ba6366a
--- /dev/null
+++ b/sf/saxon/z/IntToIntHashMap.java
@@ -0,0 +1,283 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+import java.io.Serializable;
+
+/**
+ * A hash table that maps int keys to int values.
+ *
+ * @author Dave Hale, Landmark Graphics
+ * @author Dominique Devienne
+ * @author Michael Kay: created this class based on IntHashMap
+ */
+public class IntToIntHashMap implements Serializable, IntToIntMap {
+
+ /**
+ * Initializes a map with a capacity of 8 and a load factor of 0,25.
+ */
+ public IntToIntHashMap() {
+ this(8, 0.25);
+ }
+
+ /**
+ * Initializes a map with the given capacity and a load factor of 0,25.
+ *
+ * @param capacity the initial capacity.
+ */
+ public IntToIntHashMap(int capacity) {
+ this(capacity, 0.25);
+ }
+
+ /**
+ * Constructs a new map with initial capacity, and load factor.
+ * <p/>
+ * The capacity is the number of keys that can be mapped without resizing
+ * the arrays in which keys and values are stored. For efficiency, only
+ * a fraction of the elements in those arrays are used. That fraction is
+ * the specified load factor. The initial length of the arrays equals the
+ * smallest power of two not less than the ratio capacity/factor. The
+ * capacity of the map is increased, as necessary. The maximum number
+ * of keys that can be mapped is 2^30.
+ *
+ * @param capacity the initial capacity.
+ * @param factor the load factor.
+ */
+ public IntToIntHashMap(int capacity, double factor) {
+ _factor = factor;
+ setCapacity(capacity);
+ }
+
+ /**
+ * Set the value to be returned to indicate an unused entry
+ * @param defaultValue the value to be returned by {@link #get(int)} if no entry
+ * exists for the supplied key
+ */
+ public void setDefaultValue(int defaultValue) {
+ _defaultValue = defaultValue;
+ }
+
+ /**
+ * Get the default value used to indicate an unused entry
+ * @return the value to be returned by {@link #get(int)} if no entry
+ * exists for the supplied key
+ */
+
+ public int getDefaultValue() {
+ return _defaultValue;
+ }
+
+ /**
+ * Clears the map.
+ */
+ public void clear() {
+ _n = 0;
+ for (int i = 0; i < _nmax; ++i) {
+ _filled[i] = false;
+ }
+ }
+
+ /**
+ * Finds a key in the map.
+ *
+ * @param key Key
+ * @return true if the key is mapped
+ */
+ public boolean find(int key) {
+ return _filled[indexOf(key)];
+ }
+
+ /**
+ * Gets the value for this key.
+ *
+ * @param key Key
+ * @return the value, or the default value if not found.
+ */
+ public int get(int key) {
+ int i = indexOf(key);
+ return _filled[i] ? _value[i] : _defaultValue;
+ }
+
+ /**
+ * Gets the size of the map.
+ *
+ * @return the size
+ */
+ public int size() {
+ return _n;
+ }
+
+ /**
+ * Removes a key from the map.
+ *
+ * @param key Key to remove
+ * @return true if the value was removed
+ */
+ public boolean remove(int key) {
+ // Knuth, v. 3, 527, Algorithm R.
+ int i = indexOf(key);
+ if (!_filled[i]) {
+ return false;
+ }
+ --_n;
+ for (; ;) {
+ _filled[i] = false;
+ int j = i;
+ int r;
+ do {
+ i = (i - 1) & _mask;
+ if (!_filled[i]) {
+ return true;
+ }
+ r = hash(_key[i]);
+ } while ((i <= r && r < j) || (r < j && j < i) || (j < i && i <= r));
+ _key[j] = _key[i];
+ _value[j] = _value[i];
+ _filled[j] = _filled[i];
+ }
+ }
+
+ /**
+ * Adds a key-value pair to the map.
+ *
+ * @param key Key
+ * @param value Value
+ */
+ public void put(int key, int value) {
+ int i = indexOf(key);
+ if (_filled[i]) {
+ _value[i] = value;
+ } else {
+ _key[i] = key;
+ _value[i] = value;
+ _filled[i] = true;
+ grow();
+ }
+ }
+
+ /**
+ * Get an iterator over the integer key values held in the hash map
+ * @return an iterator whose next() call returns the key values (in arbitrary order)
+ */
+
+ /*@NotNull*/ public IntIterator keyIterator() {
+ return new IntToIntHashMapKeyIterator();
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // private
+
+ private static final int NBIT = 30; // NMAX = 2^NBIT
+ private static final int NMAX = 1 << NBIT; // maximum number of keys mapped
+ private double _factor; // 0.0 <= _factor <= 1.0
+ private int _defaultValue = Integer.MAX_VALUE;
+ private int _nmax; // 0 <= _nmax = 2^nbit <= 2^NBIT = NMAX
+ private int _n; // 0 <= _n <= _nmax <= NMAX
+ private int _nlo; // _nmax*_factor (_n<=_nlo, if possible)
+ private int _nhi; // NMAX*_factor (_n< _nhi, if possible)
+ private int _shift; // _shift = 1 + NBIT - nbit (see function hash() below)
+ private int _mask; // _mask = _nmax - 1
+ private int[] _key; // array[_nmax] of keys
+ //@SuppressWarnings(value = {"unchecked"})
+ private int[] _value; // array[_nmax] of values
+ private boolean[] _filled; // _filled[i]==true iff _key[i] is mapped
+
+ private int hash(int key) {
+ // Knuth, v. 3, 509-510. Randomize the 31 low-order bits of c*key
+ // and return the highest nbits (where nbits <= 30) bits of these.
+ // The constant c = 1327217885 approximates 2^31 * (sqrt(5)-1)/2.
+ return ((1327217885 * key) >> _shift) & _mask;
+ }
+
+ private int indexOf(int key) {
+ int i = hash(key);
+ while (_filled[i]) {
+ if (_key[i] == key) {
+ return i;
+ }
+ i = (i - 1) & _mask;
+ }
+ return i;
+ }
+
+ private void grow() {
+ ++_n;
+ if (_n > NMAX) {
+ throw new RuntimeException("number of keys mapped exceeds " + NMAX);
+ }
+ if (_nlo < _n && _n <= _nhi) {
+ setCapacity(_n);
+ }
+ }
+
+ private void setCapacity(int capacity) {
+ if (capacity < _n) {
+ capacity = _n;
+ }
+ double factor = (_factor < 0.01) ? 0.01 : (_factor > 0.99) ? 0.99 : _factor;
+ int nbit, nmax;
+ for (nbit = 1, nmax = 2; nmax * factor < capacity && nmax < NMAX; ++nbit, nmax *= 2) {
+ // no-op
+ }
+ int nold = _nmax;
+ if (nmax == nold) {
+ return;
+ }
+ _nmax = nmax;
+ _nlo = (int)(nmax * factor);
+ _nhi = (int)(NMAX * factor);
+ _shift = 1 + NBIT - nbit;
+ _mask = nmax - 1;
+ int[] key = _key;
+ int[] value = _value;
+ boolean[] filled = _filled;
+ _n = 0;
+ _key = new int[nmax];
+ // semantically equivalent to _value = new V[nmax]
+ _value = new int[nmax];
+ _filled = new boolean[nmax];
+ if (key != null) {
+ for (int i = 0; i < nold; ++i) {
+ if (filled[i]) {
+ put(key[i], value[i]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Iterator over keys
+ */
+ private class IntToIntHashMapKeyIterator implements IntIterator, Serializable {
+
+ private int i = 0;
+ private static final long serialVersionUID = -5978261613309710617L;
+
+ public IntToIntHashMapKeyIterator() {
+ i = 0;
+ }
+
+ public boolean hasNext() {
+ while (i < _key.length) {
+ if (_filled[i]) {
+ return true;
+ } else {
+ i++;
+ }
+ }
+ return false;
+ }
+
+ public int next() {
+ return _key[i++];
+ }
+ }
+
+}
+
diff --git a/sf/saxon/z/IntToIntMap.java b/sf/saxon/z/IntToIntMap.java
new file mode 100644
index 0000000..f485a66
--- /dev/null
+++ b/sf/saxon/z/IntToIntMap.java
@@ -0,0 +1,81 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+import java.io.Serializable;
+
+/**
+ * Interface defining a map from integers to integers
+ */
+public interface IntToIntMap extends Serializable {
+ /**
+ * Set the value to be returned to indicate an unused entry
+ * @param defaultValue the value to be returned by {@link #get(int)} if no entry
+ * exists for the supplied key
+ */
+ void setDefaultValue(int defaultValue);
+
+ /**
+ * Get the default value used to indicate an unused entry
+ * @return the value to be returned by {@link #get(int)} if no entry
+ * exists for the supplied key
+ */
+
+ int getDefaultValue();
+
+ /**
+ * Clear the map.
+ */
+ void clear();
+
+ /**
+ * Finds a key in the map.
+ *
+ * @param key Key
+ * @return true if the key is mapped
+ */
+ boolean find(int key);
+
+ /**
+ * Gets the value for this key.
+ *
+ * @param key Key
+ * @return the value, or the default value if not found.
+ */
+ int get(int key);
+
+ /**
+ * Gets the size of the map.
+ *
+ * @return the size
+ */
+ int size();
+
+ /**
+ * Removes a key from the map.
+ *
+ * @param key Key to remove
+ * @return true if the value was removed
+ */
+ boolean remove(int key);
+
+ /**
+ * Adds a key-value pair to the map.
+ *
+ * @param key Key
+ * @param value Value
+ */
+ void put(int key, int value);
+
+ /**
+ * Get an iterator over the integer key values held in the hash map
+ * @return an iterator whose next() call returns the key values (in arbitrary order)
+ */
+
+ /*@NotNull*/ IntIterator keyIterator();
+}
\ No newline at end of file
diff --git a/sf/saxon/z/IntUnionPredicate.java b/sf/saxon/z/IntUnionPredicate.java
new file mode 100644
index 0000000..5ebcf7f
--- /dev/null
+++ b/sf/saxon/z/IntUnionPredicate.java
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An IntPredicate formed as the union of two other predicates: it matches
+ * an integer if either of the operands matches the integer
+ */
+
+public class IntUnionPredicate implements IntPredicate {
+
+ private IntPredicate p1;
+ private IntPredicate p2;
+
+ public IntUnionPredicate(IntPredicate p1, IntPredicate p2) {
+ this.p1 = p1;
+ this.p2 = p2;
+ }
+
+ /**
+ * Ask whether a given value matches this predicate
+ *
+ * @param value the value to be tested
+ * @return true if the value matches; false if it does not
+ */
+ public boolean matches(int value) {
+ return p1.matches(value) || p2.matches(value);
+ }
+
+ /**
+ * Get the operands
+ * @return an array containing the two operands
+ */
+
+ public IntPredicate[] getOperands() {
+ return new IntPredicate[]{p1, p2};
+ }
+}
diff --git a/sf/saxon/z/IntUniversalSet.java b/sf/saxon/z/IntUniversalSet.java
new file mode 100644
index 0000000..d78e071
--- /dev/null
+++ b/sf/saxon/z/IntUniversalSet.java
@@ -0,0 +1,83 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An immutable integer set containing every integer
+ */
+public class IntUniversalSet implements IntSet {
+
+ private static IntUniversalSet THE_INSTANCE = new IntUniversalSet();
+
+ public static IntUniversalSet getInstance() {
+ return THE_INSTANCE;
+ }
+
+
+ private IntUniversalSet() {
+ // no action
+ }
+
+ public IntSet copy() {
+ return this;
+ }
+
+ public IntSet mutableCopy() {
+ return new IntComplementSet(new IntHashSet());
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException("IntUniversalSet is immutable");
+ }
+
+ public int size() {
+ return Integer.MAX_VALUE;
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+
+ public boolean contains(int value) {
+ return true;
+ }
+
+ public boolean remove(int value) {
+ throw new UnsupportedOperationException("IntUniversalSet is immutable");
+ }
+
+ public boolean add(int value) {
+ throw new UnsupportedOperationException("IntUniversalSet is immutable");
+ }
+
+ public IntIterator iterator() {
+ throw new UnsupportedOperationException("Cannot enumerate an infinite set");
+ }
+
+ public IntSet union(IntSet other) {
+ return this;
+ }
+
+ public IntSet intersect(IntSet other) {
+ return other.copy();
+ }
+
+ public IntSet except(IntSet other) {
+ if (other instanceof IntUniversalSet) {
+ return IntEmptySet.getInstance();
+ } else {
+ return new IntComplementSet(other.copy());
+ }
+ }
+
+ public boolean containsAll(/*@NotNull*/ IntSet other) {
+ return true;
+ }
+}
+
+
diff --git a/sf/saxon/z/IntValuePredicate.java b/sf/saxon/z/IntValuePredicate.java
new file mode 100644
index 0000000..6e72373
--- /dev/null
+++ b/sf/saxon/z/IntValuePredicate.java
@@ -0,0 +1,40 @@
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 2013 Saxonica Limited.
+// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
+// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+package net.sf.saxon.z;
+
+/**
+ * An IntPredicate that matches a single specific integer
+ */
+
+public class IntValuePredicate implements IntPredicate {
+
+ private int target;
+
+ public IntValuePredicate(int target) {
+ this.target = target;
+ }
+
+ /**
+ * Ask whether a given value matches this predicate
+ *
+ * @param value the value to be tested
+ * @return true if the value matches; false if it does not
+ */
+ public boolean matches(int value) {
+ return value == target;
+ }
+
+ /**
+ * Get the value matched by this predicate
+ * @return the value that this predicate matches
+ */
+
+ public int getTarget() {
+ return target;
+ }
+}
diff --git a/sf/saxon/z/package.html b/sf/saxon/z/package.html
new file mode 100644
index 0000000..cbddc8e
--- /dev/null
+++ b/sf/saxon/z/package.html
@@ -0,0 +1,30 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<!-- Copyright (c) 2013 Saxonica Limited. -->
+<!-- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. -->
+<!-- If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!-- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. -->
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+
+<html>
+
+<head>
+<title>Package overview for net.sf.saxon.z</title>
+</head>
+
+<body>
+
+<p>This package provides utility routines for handling integer sets.</p>
+
+<p>The package name is a pun on the conventional use by mathematicians
+of the symbol <b>Z</b> to denote the set of all natural integers.</p>
+
+
+<hr>
+
+
+
+<p align="center"><i>Michael H. Kay<br/>
+Saxonica Limited<br/>
+5 January 2012</i></p>
+</body>
+</html>
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/saxonhe.git
More information about the pkg-java-commits
mailing list